diff --git a/.github/workflow_data/hotfix.py b/.github/workflow_data/hotfix.py index b35cf7a073..5d09d9f9cd 100644 --- a/.github/workflow_data/hotfix.py +++ b/.github/workflow_data/hotfix.py @@ -82,6 +82,6 @@ print(f"{req.url = }\n{req.status_code = }\n{req.content = }") sys.exit(1) - changelog = body.split("## 🚀 Changelog", 1)[1].rsplit("## ❤️ Support", 1)[0] + changelog = body.split("## 🚀 Changelog", 1)[1] with open(os.environ["ARTIFACT_TGZ"].removesuffix(".tgz") + ".md", "w") as f: f.write(changelog.strip() + "\n\n") diff --git a/.github/workflow_data/release.md b/.github/workflow_data/release.md index 0e716526af..7128cb4549 100644 --- a/.github/workflow_data/release.md +++ b/.github/workflow_data/release.md @@ -7,6 +7,17 @@ **Check the [install guide](https://github.com/ClaraCrazy/Flipper-Xtreme#install) if you're not sure, or [join our Discord](https://discord.gg/flipper-xtreme) if you have questions or encounter issues!** +## ❤️ Support +If you like what you're seeing, **please consider donating to us**. We won't ever put this behind a paywall, but we'd still appreciate a few bucks! + +- **[Patreon](https://patreon.com/CynthiaLabs)**: ❤️ Account needed, subscription with perks across my entire org. +- **[Wire-transfer](https://bunq.me/ClaraK)**: No account needed, one-time +- **[Paypal](https://paypal.me/RdX2020)**: Account needed, one-time +- **[ko-fi](https://ko-fi.com/cynthialabs)**: No account needed, one-time +- **Monero**: `41kyWeeoVdK4quzQ4M9ikVGs6tCQCLfdx8jLExTNsAu2SF1QAyDqRdjfGM6EL8L9NpXwt89HJeAoGf1aoArk7nDr4AMMV4T` + +**Thanks for all your support <3** + ## 🚀 Changelog {CHANGELOG} @@ -17,13 +28,3 @@ **If you have any of the above issues, please re-download and re-install!** --> - -## ❤️ Support -If you like what you're seeing, **please consider donating to us**. We won't ever put this behind a paywall, but we'd still appreciate a few bucks! - -- **[Direct Wire-transfer](https://bunq.me/ClaraK)**: No account needed, just specify amount and hit send -- **[Patreon](https://patreon.com/CynthiaLabs)** -- **[Paypal](https://paypal.me/RdX2020)** -- **Monero**: `41kyWeeoVdK4quzQ4M9ikVGs6tCQCLfdx8jLExTNsAu2SF1QAyDqRdjfGM6EL8L9NpXwt89HJeAoGf1aoArk7nDr4AMMV4T` - -**Thanks for all your support <3** diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 37ec991e69..0389542568 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -28,8 +28,8 @@ jobs: run: | set -e for TARGET in ${TARGETS}; do - TARGET="$(echo "${TARGET}" | sed 's/f//')"; \ - ./fbt TARGET_HW=$TARGET updater_package + TARGET_HW="$(echo "${TARGET}" | sed 's/f//')"; \ + ./fbt TARGET_HW=$TARGET_HW updater_package done - name: "Check for uncommitted changes" diff --git a/.github/workflows/hotfix.yml b/.github/workflows/hotfix.yml index 9b78d6e2b7..d8430d396a 100644 --- a/.github/workflows/hotfix.yml +++ b/.github/workflows/hotfix.yml @@ -35,8 +35,8 @@ jobs: run: | set -e for TARGET in ${TARGETS}; do - TARGET="$(echo "${TARGET}" | sed 's/f//')"; \ - ./fbt TARGET_HW=$TARGET updater_package + TARGET_HW="$(echo "${TARGET}" | sed 's/f//')"; \ + ./fbt TARGET_HW=$TARGET_HW FORCE_NO_DIRTY=1 updater_package done - name: "Check for uncommitted changes" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b74a7e3157..0fce2a0191 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -35,8 +35,8 @@ jobs: run: | set -e for TARGET in ${TARGETS}; do - TARGET="$(echo "${TARGET}" | sed 's/f//')"; \ - ./fbt TARGET_HW=$TARGET updater_package + TARGET_HW="$(echo "${TARGET}" | sed 's/f//')"; \ + ./fbt TARGET_HW=$TARGET_HW FORCE_NO_DIRTY=1 updater_package done - name: "Check for uncommitted changes" diff --git a/.github/workflows/sonarcloud.yaml b/.github/workflows/sonarcloud.yaml index e763aaad7b..58a6568a27 100644 --- a/.github/workflows/sonarcloud.yaml +++ b/.github/workflows/sonarcloud.yaml @@ -53,8 +53,8 @@ jobs: mkdir ${{ env.BUILD_WRAPPER_OUT_DIR }} set -e for TARGET in ${TARGETS}; do - TARGET="$(echo "${TARGET}" | sed 's/f//')"; \ - build-wrapper-linux-x86-64 --out-dir ${{ env.BUILD_WRAPPER_OUT_DIR }} ./sonar-build "./fbt TARGET_HW=$TARGET updater_package" + TARGET_HW="$(echo "${TARGET}" | sed 's/f//')"; \ + build-wrapper-linux-x86-64 --out-dir ${{ env.BUILD_WRAPPER_OUT_DIR }} ./sonar-build "./fbt TARGET_HW=$TARGET_HW updater_package" done - name: Run sonar-scanner diff --git a/CODING_STYLE.md b/CODING_STYLE.md index c62009eff5..002c67f246 100644 --- a/CODING_STYLE.md +++ b/CODING_STYLE.md @@ -48,7 +48,7 @@ Almost everything in flipper firmware is built around this concept. # C coding style - Tab is 4 spaces -- Use `fbt format` to reformat source code and check style guide before commit +- Use `./fbt format` to reformat source code and check style guide before commit ## Naming diff --git a/ReadMe.md b/ReadMe.md index 82cbfd7a0a..2da02b5308 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -183,13 +183,13 @@ There are 3 methods to install Xtreme, we recommend you use the **Web Updater**,
-**If you have issues or crashes with the install process, you can try to use `Settings > Storage > Factory Reset` then retry the install.** -**Doing that will NOT remove your saved files, it will only forget some settings and paired devices.** - ----

Build it yourself:

+> **Warning** +> We will not give basic support for compiling in our server. This is intended for people that already *know* what they are doing! + ```bash To download the needed tools: $ git clone --recursive https://github.com/ClaraCrazy/Flipper-Xtreme.git @@ -222,12 +222,13 @@ $ ./fbt resources icons dolphin_ext ## ❤️ Support If you like what you're seeing, **please consider donating to us**. We won't ever put this behind a paywall, but we'd still appreciate a few bucks! -- **[Direct Wire-transfer](https://bunq.me/ClaraK)**: No account needed, just specify amount and hit send -- **[Patreon](https://patreon.com/CynthiaLabs)** -- **[Paypal](https://paypal.me/RdX2020)** +- **[Patreon](https://patreon.com/CynthiaLabs)**: ❤️ Account needed, subscription with perks across my entire org. +- **[Wire-transfer](https://bunq.me/ClaraK)**: No account needed, one-time +- **[Paypal](https://paypal.me/RdX2020)**: Account needed, one-time +- **[ko-fi](https://ko-fi.com/cynthialabs)**: No account needed, one-time - **Monero**: `41kyWeeoVdK4quzQ4M9ikVGs6tCQCLfdx8jLExTNsAu2SF1QAyDqRdjfGM6EL8L9NpXwt89HJeAoGf1aoArk7nDr4AMMV4T` **Thanks for all your support <3** ---- -

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

\ No newline at end of file +

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

diff --git a/SConstruct b/SConstruct index 85e0241c67..3ea360979a 100644 --- a/SConstruct +++ b/SConstruct @@ -171,7 +171,7 @@ distenv.Depends(firmware_env["FW_RESOURCES"], external_apps_artifacts.resources_ fap_deploy = distenv.PhonyTarget( "fap_deploy", - "${PYTHON3} ${ROOT_DIR}/scripts/storage.py send ${SOURCE} /ext/apps", + "${PYTHON3} ${FBT_SCRIPT_DIR}/storage.py -p ${FLIP_PORT} send ${SOURCE} /ext/apps", source=Dir("#/assets/resources/apps"), ) @@ -323,7 +323,9 @@ distenv.PhonyTarget( ) # Start Flipper CLI via PySerial's miniterm -distenv.PhonyTarget("cli", "${PYTHON3} ${FBT_SCRIPT_DIR}/serial_cli.py") +distenv.PhonyTarget( + "cli", "${PYTHON3} ${FBT_SCRIPT_DIR}/serial_cli.py -p ${FLIP_PORT}" +) # Find blackmagic probe diff --git a/applications/ReadMe.md b/applications/ReadMe.md index 598c8b2ada..2a373509c2 100644 --- a/applications/ReadMe.md +++ b/applications/ReadMe.md @@ -26,7 +26,6 @@ Applications for main Flipper menu. - `archive` - Archive and file manager - `bad_kb` - Bad KB application -- `fap_loader` - External applications loader - `gpio` - GPIO application: includes USART bridge and GPIO control - `ibutton` - iButton application, onewire keys and more - `infrared` - Infrared application, controls your IR devices diff --git a/applications/debug/application.fam b/applications/debug/application.fam index a33b3693df..cdbf8fe18b 100644 --- a/applications/debug/application.fam +++ b/applications/debug/application.fam @@ -12,5 +12,6 @@ App( "display_test", "text_box_test", "file_browser_test", + "speaker_debug", ], ) diff --git a/applications/debug/battery_test_app/application.fam b/applications/debug/battery_test_app/application.fam index f97d102791..b388445cc3 100644 --- a/applications/debug/battery_test_app/application.fam +++ b/applications/debug/battery_test_app/application.fam @@ -11,5 +11,4 @@ App( stack_size=1 * 1024, order=130, fap_category="Debug", - fap_libs=["assets"], ) diff --git a/applications/debug/crash_test/application.fam b/applications/debug/crash_test/application.fam new file mode 100644 index 0000000000..55f62f86d8 --- /dev/null +++ b/applications/debug/crash_test/application.fam @@ -0,0 +1,10 @@ +App( + appid="crash_test", + name="Crash Test", + apptype=FlipperAppType.DEBUG, + entry_point="crash_test_app", + cdefines=["APP_CRASH_TEST"], + requires=["gui"], + stack_size=1 * 1024, + fap_category="Debug", +) diff --git a/applications/debug/crash_test/crash_test.c b/applications/debug/crash_test/crash_test.c new file mode 100644 index 0000000000..92f1668be9 --- /dev/null +++ b/applications/debug/crash_test/crash_test.c @@ -0,0 +1,128 @@ +#include +#include + +#include +#include +#include + +#define TAG "CrashTest" + +typedef struct { + Gui* gui; + ViewDispatcher* view_dispatcher; + Submenu* submenu; +} CrashTest; + +typedef enum { + CrashTestViewSubmenu, +} CrashTestView; + +typedef enum { + CrashTestSubmenuCheck, + CrashTestSubmenuCheckMessage, + CrashTestSubmenuAssert, + CrashTestSubmenuAssertMessage, + CrashTestSubmenuCrash, + CrashTestSubmenuHalt, +} CrashTestSubmenu; + +static void crash_test_submenu_callback(void* context, uint32_t index) { + CrashTest* instance = (CrashTest*)context; + UNUSED(instance); + + switch(index) { + case CrashTestSubmenuCheck: + furi_check(false); + break; + case CrashTestSubmenuCheckMessage: + furi_check(false, "Crash test: furi_check with message"); + break; + case CrashTestSubmenuAssert: + furi_assert(false); + break; + case CrashTestSubmenuAssertMessage: + furi_assert(false, "Crash test: furi_assert with message"); + break; + case CrashTestSubmenuCrash: + furi_crash("Crash test: furi_crash"); + break; + case CrashTestSubmenuHalt: + furi_halt("Crash test: furi_halt"); + break; + default: + furi_crash("Programming error"); + } +} + +static uint32_t crash_test_exit_callback(void* context) { + UNUSED(context); + return VIEW_NONE; +} + +CrashTest* crash_test_alloc() { + CrashTest* instance = malloc(sizeof(CrashTest)); + + View* view = NULL; + + instance->gui = furi_record_open(RECORD_GUI); + instance->view_dispatcher = view_dispatcher_alloc(); + view_dispatcher_enable_queue(instance->view_dispatcher); + view_dispatcher_attach_to_gui( + instance->view_dispatcher, instance->gui, ViewDispatcherTypeFullscreen); + + // Menu + instance->submenu = submenu_alloc(); + view = submenu_get_view(instance->submenu); + view_set_previous_callback(view, crash_test_exit_callback); + view_dispatcher_add_view(instance->view_dispatcher, CrashTestViewSubmenu, view); + submenu_add_item( + instance->submenu, "Check", CrashTestSubmenuCheck, crash_test_submenu_callback, instance); + submenu_add_item( + instance->submenu, + "Check with message", + CrashTestSubmenuCheckMessage, + crash_test_submenu_callback, + instance); + submenu_add_item( + instance->submenu, "Assert", CrashTestSubmenuAssert, crash_test_submenu_callback, instance); + submenu_add_item( + instance->submenu, + "Assert with message", + CrashTestSubmenuAssertMessage, + crash_test_submenu_callback, + instance); + submenu_add_item( + instance->submenu, "Crash", CrashTestSubmenuCrash, crash_test_submenu_callback, instance); + submenu_add_item( + instance->submenu, "Halt", CrashTestSubmenuHalt, crash_test_submenu_callback, instance); + + return instance; +} + +void crash_test_free(CrashTest* instance) { + view_dispatcher_remove_view(instance->view_dispatcher, CrashTestViewSubmenu); + submenu_free(instance->submenu); + + view_dispatcher_free(instance->view_dispatcher); + furi_record_close(RECORD_GUI); + + free(instance); +} + +int32_t crash_test_run(CrashTest* instance) { + view_dispatcher_switch_to_view(instance->view_dispatcher, CrashTestViewSubmenu); + view_dispatcher_run(instance->view_dispatcher); + return 0; +} + +int32_t crash_test_app(void* p) { + UNUSED(p); + + CrashTest* instance = crash_test_alloc(); + + int32_t ret = crash_test_run(instance); + + crash_test_free(instance); + + return ret; +} diff --git a/applications/debug/lfrfid_debug/scenes/lfrfid_debug_app_scene_tune.c b/applications/debug/lfrfid_debug/scenes/lfrfid_debug_app_scene_tune.c index 74c53ae6d4..ac2e2b8061 100644 --- a/applications/debug/lfrfid_debug/scenes/lfrfid_debug_app_scene_tune.c +++ b/applications/debug/lfrfid_debug/scenes/lfrfid_debug_app_scene_tune.c @@ -6,6 +6,11 @@ static void comparator_trigger_callback(bool level, void* comp_ctx) { furi_hal_gpio_write(&gpio_ext_pa7, !level); } +void lfrfid_debug_view_tune_callback(void* context) { + LfRfidDebug* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, 0xBA); +} + void lfrfid_debug_scene_tune_on_enter(void* context) { LfRfidDebug* app = context; @@ -16,6 +21,8 @@ void lfrfid_debug_scene_tune_on_enter(void* context) { furi_hal_rfid_tim_read_start(125000, 0.5); + lfrfid_debug_view_tune_set_callback(app->tune_view, lfrfid_debug_view_tune_callback, app); + view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidDebugViewTune); } diff --git a/applications/debug/lfrfid_debug/views/lfrfid_debug_view_tune.c b/applications/debug/lfrfid_debug/views/lfrfid_debug_view_tune.c index fd221c4e9d..9e48a7e27f 100644 --- a/applications/debug/lfrfid_debug/views/lfrfid_debug_view_tune.c +++ b/applications/debug/lfrfid_debug/views/lfrfid_debug_view_tune.c @@ -13,6 +13,8 @@ typedef struct { uint32_t ARR; uint32_t CCR; int pos; + void (*update_callback)(void* context); + void* update_context; } LfRfidTuneViewModel; static void lfrfid_debug_view_tune_draw_callback(Canvas* canvas, void* _model) { @@ -151,6 +153,18 @@ static bool lfrfid_debug_view_tune_input_callback(InputEvent* event, void* conte consumed = false; break; } + + if(event->key == InputKeyLeft || event->key == InputKeyRight) { + with_view_model( + tune_view->view, + LfRfidTuneViewModel * model, + { + if(model->update_callback) { + model->update_callback(model->update_context); + } + }, + false); + } } return consumed; @@ -161,19 +175,7 @@ LfRfidTuneView* lfrfid_debug_view_tune_alloc() { tune_view->view = view_alloc(); view_set_context(tune_view->view, tune_view); view_allocate_model(tune_view->view, ViewModelTypeLocking, sizeof(LfRfidTuneViewModel)); - - with_view_model( - tune_view->view, - LfRfidTuneViewModel * model, - { - model->dirty = true; - model->fine = false; - model->ARR = 511; - model->CCR = 255; - model->pos = 0; - }, - true); - + lfrfid_debug_view_tune_clean(tune_view); view_set_draw_callback(tune_view->view, lfrfid_debug_view_tune_draw_callback); view_set_input_callback(tune_view->view, lfrfid_debug_view_tune_input_callback); @@ -199,6 +201,8 @@ void lfrfid_debug_view_tune_clean(LfRfidTuneView* tune_view) { model->ARR = 511; model->CCR = 255; model->pos = 0; + model->update_callback = NULL; + model->update_context = NULL; }, true); } @@ -232,3 +236,17 @@ uint32_t lfrfid_debug_view_tune_get_ccr(LfRfidTuneView* tune_view) { return result; } + +void lfrfid_debug_view_tune_set_callback( + LfRfidTuneView* tune_view, + void (*callback)(void* context), + void* context) { + with_view_model( + tune_view->view, + LfRfidTuneViewModel * model, + { + model->update_callback = callback; + model->update_context = context; + }, + false); +} diff --git a/applications/debug/lfrfid_debug/views/lfrfid_debug_view_tune.h b/applications/debug/lfrfid_debug/views/lfrfid_debug_view_tune.h index fd6d0b1fe9..be54b63f9a 100644 --- a/applications/debug/lfrfid_debug/views/lfrfid_debug_view_tune.h +++ b/applications/debug/lfrfid_debug/views/lfrfid_debug_view_tune.h @@ -16,3 +16,8 @@ bool lfrfid_debug_view_tune_is_dirty(LfRfidTuneView* tune_view); uint32_t lfrfid_debug_view_tune_get_arr(LfRfidTuneView* tune_view); uint32_t lfrfid_debug_view_tune_get_ccr(LfRfidTuneView* tune_view); + +void lfrfid_debug_view_tune_set_callback( + LfRfidTuneView* tune_view, + void (*callback)(void* context), + void* context); diff --git a/applications/debug/speaker_debug/application.fam b/applications/debug/speaker_debug/application.fam new file mode 100644 index 0000000000..68d8b188bd --- /dev/null +++ b/applications/debug/speaker_debug/application.fam @@ -0,0 +1,11 @@ +App( + appid="speaker_debug", + name="Speaker Debug", + apptype=FlipperAppType.DEBUG, + entry_point="speaker_debug_app", + requires=["gui", "notification"], + stack_size=2 * 1024, + order=10, + fap_category="Debug", + fap_libs=["music_worker"], +) diff --git a/applications/debug/speaker_debug/speaker_debug.c b/applications/debug/speaker_debug/speaker_debug.c new file mode 100644 index 0000000000..e01d5b8ec4 --- /dev/null +++ b/applications/debug/speaker_debug/speaker_debug.c @@ -0,0 +1,120 @@ +#include +#include +#include +#include +#include + +#define TAG "SpeakerDebug" +#define CLI_COMMAND "speaker_debug" + +typedef enum { + SpeakerDebugAppMessageTypeStop, +} SpeakerDebugAppMessageType; + +typedef struct { + SpeakerDebugAppMessageType type; +} SpeakerDebugAppMessage; + +typedef struct { + MusicWorker* music_worker; + FuriMessageQueue* message_queue; + Cli* cli; +} SpeakerDebugApp; + +static SpeakerDebugApp* speaker_app_alloc() { + SpeakerDebugApp* app = (SpeakerDebugApp*)malloc(sizeof(SpeakerDebugApp)); + app->music_worker = music_worker_alloc(); + app->message_queue = furi_message_queue_alloc(8, sizeof(SpeakerDebugAppMessage)); + app->cli = furi_record_open(RECORD_CLI); + return app; +} + +static void speaker_app_free(SpeakerDebugApp* app) { + music_worker_free(app->music_worker); + furi_message_queue_free(app->message_queue); + furi_record_close(RECORD_CLI); + free(app); +} + +static void speaker_app_cli(Cli* cli, FuriString* args, void* context) { + UNUSED(cli); + + SpeakerDebugApp* app = (SpeakerDebugApp*)context; + SpeakerDebugAppMessage message; + FuriString* cmd = furi_string_alloc(); + + if(!args_read_string_and_trim(args, cmd)) { + furi_string_free(cmd); + printf("Usage:\r\n"); + printf("\t" CLI_COMMAND " stop\r\n"); + return; + } + + if(furi_string_cmp(cmd, "stop") == 0) { + message.type = SpeakerDebugAppMessageTypeStop; + FuriStatus status = furi_message_queue_put(app->message_queue, &message, 100); + if(status != FuriStatusOk) { + printf("Failed to send message\r\n"); + } else { + printf("Stopping\r\n"); + } + } else { + printf("Usage:\r\n"); + printf("\t" CLI_COMMAND " stop\r\n"); + } + + furi_string_free(cmd); +} + +static bool speaker_app_music_play(SpeakerDebugApp* app, const char* rtttl) { + if(music_worker_is_playing(app->music_worker)) { + music_worker_stop(app->music_worker); + } + + if(!music_worker_load_rtttl_from_string(app->music_worker, rtttl)) { + FURI_LOG_E(TAG, "Failed to load RTTTL"); + return false; + } + + music_worker_set_volume(app->music_worker, 1.0f); + music_worker_start(app->music_worker); + + return true; +} + +static void speaker_app_music_stop(SpeakerDebugApp* app) { + if(music_worker_is_playing(app->music_worker)) { + music_worker_stop(app->music_worker); + } +} + +static void speaker_app_run(SpeakerDebugApp* app, const char* arg) { + if(!arg || !speaker_app_music_play(app, arg)) { + FURI_LOG_E(TAG, "Provided RTTTL is invalid"); + return; + } + + cli_add_command(app->cli, CLI_COMMAND, CliCommandFlagParallelSafe, speaker_app_cli, app); + + SpeakerDebugAppMessage message; + FuriStatus status; + while(true) { + status = furi_message_queue_get(app->message_queue, &message, FuriWaitForever); + + if(status == FuriStatusOk) { + if(message.type == SpeakerDebugAppMessageTypeStop) { + speaker_app_music_stop(app); + break; + } + } + } + + cli_delete_command(app->cli, CLI_COMMAND); +} + +int32_t speaker_debug_app(void* arg) { + SpeakerDebugApp* app = speaker_app_alloc(); + speaker_app_run(app, arg); + speaker_app_free(app); + return 0; +} diff --git a/applications/debug/uart_echo/application.fam b/applications/debug/uart_echo/application.fam index ecdc847cd1..3e262a09df 100644 --- a/applications/debug/uart_echo/application.fam +++ b/applications/debug/uart_echo/application.fam @@ -1,5 +1,5 @@ App( - appid="UART_Echo", + appid="uart_echo", name="[GPIO] UART Echo", apptype=FlipperAppType.DEBUG, entry_point="uart_echo_app", diff --git a/applications/debug/uart_echo/uart_echo.c b/applications/debug/uart_echo/uart_echo.c index dc13275292..4bede9ab45 100644 --- a/applications/debug/uart_echo/uart_echo.c +++ b/applications/debug/uart_echo/uart_echo.c @@ -10,6 +10,8 @@ #define LINES_ON_SCREEN 6 #define COLUMNS_ON_SCREEN 21 +#define TAG "UartEcho" +#define DEFAULT_BAUD_RATE 230400 typedef struct UartDumpModel UartDumpModel; @@ -179,7 +181,7 @@ static int32_t uart_echo_worker(void* context) { return 0; } -static UartEchoApp* uart_echo_app_alloc() { +static UartEchoApp* uart_echo_app_alloc(uint32_t baudrate) { UartEchoApp* app = malloc(sizeof(UartEchoApp)); app->rx_stream = furi_stream_buffer_alloc(2048, 1); @@ -220,7 +222,7 @@ static UartEchoApp* uart_echo_app_alloc() { // Enable uart listener furi_hal_console_disable(); - furi_hal_uart_set_br(FuriHalUartIdUSART1, 115200); + furi_hal_uart_set_br(FuriHalUartIdUSART1, baudrate); furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, uart_echo_on_irq_cb, app); return app; @@ -263,8 +265,18 @@ static void uart_echo_app_free(UartEchoApp* app) { } int32_t uart_echo_app(void* p) { - UNUSED(p); - UartEchoApp* app = uart_echo_app_alloc(); + uint32_t baudrate = DEFAULT_BAUD_RATE; + if(p) { + const char* baudrate_str = p; + if(sscanf(baudrate_str, "%lu", &baudrate) != 1) { + FURI_LOG_E(TAG, "Invalid baudrate: %s", baudrate_str); + baudrate = DEFAULT_BAUD_RATE; + } + } + + FURI_LOG_I(TAG, "Using baudrate: %lu", baudrate); + + UartEchoApp* app = uart_echo_app_alloc(baudrate); view_dispatcher_run(app->view_dispatcher); uart_echo_app_free(app); return 0; diff --git a/applications/debug/unit_tests/dialogs/dialogs_file_browser_options.c b/applications/debug/unit_tests/dialogs/dialogs_file_browser_options.c new file mode 100644 index 0000000000..2d5bad4c8a --- /dev/null +++ b/applications/debug/unit_tests/dialogs/dialogs_file_browser_options.c @@ -0,0 +1,32 @@ +#include + +#include "../minunit.h" + +MU_TEST(test_dialog_file_browser_set_basic_options_should_init_all_fields) { + mu_assert( + sizeof(DialogsFileBrowserOptions) == 28, + "Changes to `DialogsFileBrowserOptions` should also be reflected in `dialog_file_browser_set_basic_options`"); + + DialogsFileBrowserOptions options; + dialog_file_browser_set_basic_options(&options, ".fap", NULL); + // note: this assertions can safely be changed, their primary purpose is to remind the maintainer + // to update `dialog_file_browser_set_basic_options` by including all structure fields in it + mu_assert_string_eq(".fap", options.extension); + mu_assert_null(options.base_path); + mu_assert(options.skip_assets, "`skip_assets` should default to `true"); + mu_assert(options.hide_dot_files, "`hide_dot_files` should default to `true"); + mu_assert_null(options.icon); + mu_assert(options.hide_ext, "`hide_ext` should default to `true"); + mu_assert_null(options.item_loader_callback); + mu_assert_null(options.item_loader_context); +} + +MU_TEST_SUITE(dialogs_file_browser_options) { + MU_RUN_TEST(test_dialog_file_browser_set_basic_options_should_init_all_fields); +} + +int run_minunit_test_dialogs_file_browser_options() { + MU_RUN_SUITE(dialogs_file_browser_options); + + return MU_EXIT_CODE; +} diff --git a/applications/debug/unit_tests/rpc/rpc_test.c b/applications/debug/unit_tests/rpc/rpc_test.c index 167266a843..e01f56be10 100644 --- a/applications/debug/unit_tests/rpc/rpc_test.c +++ b/applications/debug/unit_tests/rpc/rpc_test.c @@ -43,7 +43,7 @@ static RpcSessionContext rpc_session[TEST_RPC_SESSIONS]; #define TAG "UnitTestsRpc" #define MAX_RECEIVE_OUTPUT_TIMEOUT 3000 -#define MAX_NAME_LENGTH 255 +#define MAX_NAME_LENGTH 254 #define MAX_DATA_SIZE 512u // have to be exact as in rpc_storage.c #define TEST_DIR TEST_DIR_NAME "/" #define TEST_DIR_NAME EXT_PATH("unit_tests_tmp") @@ -186,7 +186,7 @@ static void clean_directory(Storage* fs_api, const char* clean_dir) { File* dir = storage_file_alloc(fs_api); if(storage_dir_open(dir, clean_dir)) { FileInfo fileinfo; - char* name = malloc(MAX_NAME_LENGTH + 1); + char* name = malloc(MAX_NAME_LENGTH); while(storage_dir_read(dir, &fileinfo, name, MAX_NAME_LENGTH)) { size_t size = strlen(clean_dir) + strlen(name) + 1 + 1; char* fullname = malloc(size); @@ -598,7 +598,7 @@ static void test_rpc_storage_list_create_expected_list( while(!finish) { FileInfo fileinfo; - char* name = malloc(MAX_NAME_LENGTH + 1); + char* name = malloc(MAX_NAME_LENGTH); if(storage_dir_read(dir, &fileinfo, name, MAX_NAME_LENGTH)) { if(i == COUNT_OF(list->file)) { list->file_count = i; diff --git a/applications/debug/unit_tests/test_index.c b/applications/debug/unit_tests/test_index.c index ac71ca397e..9d7631bfee 100644 --- a/applications/debug/unit_tests/test_index.c +++ b/applications/debug/unit_tests/test_index.c @@ -27,6 +27,7 @@ int run_minunit_test_nfc(); int run_minunit_test_bit_lib(); int run_minunit_test_float_tools(); int run_minunit_test_bt(); +int run_minunit_test_dialogs_file_browser_options(); typedef int (*UnitTestEntry)(); @@ -55,6 +56,8 @@ const UnitTest unit_tests[] = { {.name = "bit_lib", .entry = run_minunit_test_bit_lib}, {.name = "float_tools", .entry = run_minunit_test_float_tools}, {.name = "bt", .entry = run_minunit_test_bt}, + {.name = "dialogs_file_browser_options", + .entry = run_minunit_test_dialogs_file_browser_options}, }; void minunit_print_progress() { diff --git a/applications/debug/usb_mouse/application.fam b/applications/debug/usb_mouse/application.fam index ab91e7aa26..55ba7d8698 100644 --- a/applications/debug/usb_mouse/application.fam +++ b/applications/debug/usb_mouse/application.fam @@ -7,6 +7,6 @@ App( requires=["gui"], stack_size=1 * 1024, order=60, - fap_icon="../../external/mousejacker/mouse_10px.png", + fap_icon="mouse_10px.png", fap_category="Debug", ) diff --git a/applications/external/mousejacker/mouse_10px.png b/applications/debug/usb_mouse/mouse_10px.png similarity index 100% rename from applications/external/mousejacker/mouse_10px.png rename to applications/debug/usb_mouse/mouse_10px.png diff --git a/applications/external/.mass_storage/application.fam b/applications/external/.mass_storage/application.fam new file mode 100644 index 0000000000..5cc57b2b50 --- /dev/null +++ b/applications/external/.mass_storage/application.fam @@ -0,0 +1,12 @@ +App( + appid="mass_storage", + name="USB Mass Storage", + apptype=FlipperAppType.EXTERNAL, + entry_point="mass_storage_app", + cdefines=["APP_MASS_STORAGE"], + requires=["gui"], + stack_size=1 * 1024, + order=2, + # fap_icon="", + fap_category="Misc", +) diff --git a/applications/external/.mass_storage/helpers/mass_storage_scsi.c b/applications/external/.mass_storage/helpers/mass_storage_scsi.c new file mode 100644 index 0000000000..995f050765 --- /dev/null +++ b/applications/external/.mass_storage/helpers/mass_storage_scsi.c @@ -0,0 +1,220 @@ +#include "mass_storage_scsi.h" + +#define TAG "MassStorageSCSI" + +#define SCSI_TEST_UNIT_READY (0x00) +#define SCSI_REQUEST_SENSE (0x03) +#define SCSI_INQUIRY (0x12) +#define SCSI_READ_CAPACITY_6 (0x25) +#define SCSI_MODE_SENSE_6 (0x1A) +#define SCSI_READ_10 (0x28) +#define SCSI_PREVENT_MEDIUM_REMOVAL (0x1E) +#define SCSI_START_STOP_UNIT (0x1B) +#define SCSI_WRITE_10 (0x2A) + +bool scsi_cmd_start(SCSISession* scsi, uint8_t* cmd, uint8_t len) { + if(!len) { + scsi->sk = SCSI_SK_ILLEGAL_REQUEST; + scsi->asc = SCSI_ASC_INVALID_COMMAND_OPERATION_CODE; + return false; + } + // FURI_LOG_I(TAG, "START %02x", cmd[0]); + scsi->cmd = cmd; + scsi->cmd_len = len; + scsi->rx_done = false; + scsi->tx_done = false; + switch(cmd[0]) { + case SCSI_WRITE_10: { + if(len < 10) return false; + scsi->write_10.lba = cmd[2] << 24 | cmd[3] << 16 | cmd[4] << 8 | cmd[5]; + scsi->write_10.count = cmd[7] << 8 | cmd[8]; + FURI_LOG_I(TAG, "SCSI_WRITE_10 %08lx %04x", scsi->write_10.lba, scsi->write_10.count); + return true; + }; break; + case SCSI_READ_10: { + if(len < 10) return false; + scsi->read_10.lba = cmd[2] << 24 | cmd[3] << 16 | cmd[4] << 8 | cmd[5]; + scsi->read_10.count = cmd[7] << 8 | cmd[8]; + FURI_LOG_I(TAG, "SCSI_READ_10 %08lx %04x", scsi->read_10.lba, scsi->read_10.count); + return true; + }; break; + } + return true; +} + +bool scsi_cmd_rx_data(SCSISession* scsi, uint8_t* data, uint32_t len) { + // FURI_LOG_I(TAG, "RX %02x len %d", scsi->cmd[0], len); + if(scsi->rx_done) return false; + switch(scsi->cmd[0]) { + case SCSI_WRITE_10: { + uint32_t block_size = SCSI_BLOCK_SIZE; + uint16_t blocks = len / block_size; + bool result = + scsi->fn.write(scsi->fn.ctx, scsi->write_10.lba, blocks, data, blocks * block_size); + scsi->write_10.lba += blocks; + scsi->write_10.count -= blocks; + if(!scsi->write_10.count) { + scsi->rx_done = true; + } + return result; + }; break; + default: { + FURI_LOG_W(TAG, "unexpected scsi rx data cmd=%02x", scsi->cmd[0]); + scsi->sk = SCSI_SK_ILLEGAL_REQUEST; + scsi->asc = SCSI_ASC_INVALID_COMMAND_OPERATION_CODE; + return false; + }; break; + } +} + +bool scsi_cmd_tx_data(SCSISession* scsi, uint8_t* data, uint32_t* len, uint32_t cap) { + // FURI_LOG_I(TAG, "TX %02x cap %d", scsi->cmd[0], cap); + if(scsi->tx_done) return false; + switch(scsi->cmd[0]) { + case SCSI_REQUEST_SENSE: { + FURI_LOG_I(TAG, "SCSI_REQUEST_SENSE"); + if(cap < 18) return false; + memset(data, 0, cap); + data[0] = 0x70; // fixed format sense data + data[1] = 0; // obsolete + data[2] = scsi->sk; // sense key + data[3] = 0; // information + data[4] = 0; // information + data[5] = 0; // information + data[6] = 0; // information + data[7] = 10; // additional sense length (len-8) + data[8] = 0; // command specific information + data[9] = 0; // command specific information + data[10] = 0; // command specific information + data[11] = 0; // command specific information + data[12] = scsi->asc; // additional sense code + data[13] = 0; // additional sense code qualifier + data[14] = 0; // field replaceable unit code + data[15] = 0; // sense key specific information + data[16] = 0; // sense key specific information + data[17] = 0; // sense key specific information + *len = 18; + scsi->sk = 0; + scsi->asc = 0; + scsi->tx_done = true; + return true; + }; break; + case SCSI_INQUIRY: { + FURI_LOG_I(TAG, "SCSI_INQUIRY"); + if(scsi->cmd_len < 5) return false; + if(cap < 36) return false; + bool evpd = scsi->cmd[1] & 1; + uint8_t page_code = scsi->cmd[2]; + // uint16_t alloc_len = scsi->cmd[3] << 8 | scsi->cmd[4]; + if(evpd) return false; + if(page_code) return false; + data[0] = 0x00; // device type: direct access block device + data[1] = 0x80; // removable: true + data[2] = 0x04; // version + data[3] = 0x02; // response data format + data[4] = 31; // additional length (len - 5) + data[5] = 0; // flags + data[6] = 0; // flags + data[7] = 0; // flags + memcpy(data + 8, "Flipper ", 8); // vendor id + memcpy(data + 16, "Mass Storage ", 16); // product id + memcpy(data + 32, "0001", 4); // product revision level + *len = 36; + scsi->tx_done = true; + return true; + }; break; + case SCSI_READ_CAPACITY_6: { + FURI_LOG_I(TAG, "SCSI_READ_CAPACITY_6"); + if(cap < 8) return false; + uint32_t n_blocks = scsi->fn.num_blocks(scsi->fn.ctx); + uint32_t block_size = SCSI_BLOCK_SIZE; + data[0] = (n_blocks - 1) >> 24; + data[1] = (n_blocks - 1) >> 16; + data[2] = (n_blocks - 1) >> 8; + data[3] = (n_blocks - 1); + data[4] = block_size >> 24; + data[5] = block_size >> 16; + data[6] = block_size >> 8; + data[7] = block_size; + *len = 8; + scsi->tx_done = true; + return true; + }; break; + case SCSI_MODE_SENSE_6: { + FURI_LOG_I(TAG, "SCSI_MODE_SENSE_6 %ld", cap); + if(cap < 4) return false; + data[0] = 3; // mode data length (len - 1) + data[1] = 0; // medium type + data[2] = 0; // device-specific parameter + data[3] = 0; // block descriptor length + *len = 4; + scsi->tx_done = true; + return true; + }; break; + case SCSI_READ_10: { + uint32_t block_size = SCSI_BLOCK_SIZE; + bool result = + scsi->fn.read(scsi->fn.ctx, scsi->read_10.lba, scsi->read_10.count, data, len, cap); + *len -= *len % block_size; + uint16_t blocks = *len / block_size; + scsi->read_10.lba += blocks; + scsi->read_10.count -= blocks; + if(!scsi->read_10.count) { + scsi->tx_done = true; + } + return result; + }; break; + default: { + FURI_LOG_W(TAG, "unexpected scsi tx data cmd=%02x", scsi->cmd[0]); + scsi->sk = SCSI_SK_ILLEGAL_REQUEST; + scsi->asc = SCSI_ASC_INVALID_COMMAND_OPERATION_CODE; + return false; + }; break; + } +} + +bool scsi_cmd_end(SCSISession* scsi) { + // FURI_LOG_I(TAG, "END %02x", scsi->cmd[0]); + uint8_t* cmd = scsi->cmd; + uint8_t len = scsi->cmd_len; + scsi->cmd = NULL; + scsi->cmd_len = 0; + switch(cmd[0]) { + case SCSI_WRITE_10: + return scsi->rx_done; + + case SCSI_REQUEST_SENSE: + case SCSI_INQUIRY: + case SCSI_READ_CAPACITY_6: + case SCSI_MODE_SENSE_6: + case SCSI_READ_10: + return scsi->tx_done; + + case SCSI_TEST_UNIT_READY: { + FURI_LOG_I(TAG, "SCSI_TEST_UNIT_READY"); + return true; + }; break; + case SCSI_PREVENT_MEDIUM_REMOVAL: { + if(len < 6) return false; + bool prevent = cmd[5]; + FURI_LOG_I(TAG, "SCSI_PREVENT_MEDIUM_REMOVAL prevent=%d", prevent); + return !prevent; + }; break; + case SCSI_START_STOP_UNIT: { + if(len < 6) return false; + bool eject = (cmd[4] & 2) != 0; + bool start = (cmd[4] & 1) != 0; + FURI_LOG_I(TAG, "SCSI_START_STOP_UNIT eject=%d start=%d", eject, start); + if(eject) { + scsi->fn.eject(scsi->fn.ctx); + } + return true; + }; break; + default: { + FURI_LOG_W(TAG, "unexpected scsi cmd=%02x", cmd[0]); + scsi->sk = SCSI_SK_ILLEGAL_REQUEST; + scsi->asc = SCSI_ASC_INVALID_COMMAND_OPERATION_CODE; + return false; + }; break; + } +} diff --git a/applications/external/.mass_storage/helpers/mass_storage_scsi.h b/applications/external/.mass_storage/helpers/mass_storage_scsi.h new file mode 100644 index 0000000000..27a6f6ebb7 --- /dev/null +++ b/applications/external/.mass_storage/helpers/mass_storage_scsi.h @@ -0,0 +1,56 @@ +#pragma once + +#include + +#define SCSI_BLOCK_SIZE (0x200u) + +#define SCSI_SK_ILLEGAL_REQUEST (5) + +#define SCSI_ASC_INVALID_COMMAND_OPERATION_CODE (0x20) +#define SCSI_ASC_LBA_OOB (0x21) +#define SCSI_ASC_INVALID_FIELD_IN_CDB (0x24) + +typedef struct { + void* ctx; + bool (*read)( + void* ctx, + uint32_t lba, + uint16_t count, + uint8_t* out, + uint32_t* out_len, + uint32_t out_cap); + bool (*write)(void* ctx, uint32_t lba, uint16_t count, uint8_t* buf, uint32_t len); + uint32_t (*num_blocks)(void* ctx); + void (*eject)(void* ctx); +} SCSIDeviceFunc; + +typedef struct { + SCSIDeviceFunc fn; + + uint8_t* cmd; + uint8_t cmd_len; + bool rx_done; + bool tx_done; + + uint8_t sk; // sense key + uint8_t asc; // additional sense code + + // command-specific data + // valid from cmd_start to cmd_end + union { + struct { + uint16_t count; + uint32_t lba; + } read_10; // SCSI_READ_10 + + struct { + uint16_t count; + uint32_t lba; + } write_10; // SCSI_WRITE_10 + }; +} SCSISession; + +bool scsi_cmd_start(SCSISession* scsi, uint8_t* cmd, uint8_t len); +bool scsi_cmd_rx_data(SCSISession* scsi, uint8_t* data, uint32_t len); +bool scsi_cmd_tx_data(SCSISession* scsi, uint8_t* data, uint32_t* len, uint32_t cap); +bool scsi_cmd_end(SCSISession* scsi); diff --git a/applications/external/.mass_storage/helpers/mass_storage_usb.c b/applications/external/.mass_storage/helpers/mass_storage_usb.c new file mode 100644 index 0000000000..5a24b58981 --- /dev/null +++ b/applications/external/.mass_storage/helpers/mass_storage_usb.c @@ -0,0 +1,473 @@ +#include "mass_storage_usb.h" + +#include + +#define TAG "MassStorageUsb" + +#define USB_MSC_RX_EP (0x01) +#define USB_MSC_TX_EP (0x82) + +#define USB_MSC_RX_EP_SIZE (64) +#define USB_MSC_TX_EP_SIZE (64u) + +#define USB_MSC_BOT_GET_MAX_LUN (0xFE) +#define USB_MSC_BOT_RESET (0xFF) + +#define CBW_SIG (0x43425355) +#define CBW_FLAGS_DEVICE_TO_HOST (0x80) + +#define CSW_SIG (0x53425355) +#define CSW_STATUS_OK (0) +#define CSW_STATUS_NOK (1) +#define CSW_STATUS_PHASE_ERROR (2) + +// must be SCSI_BLOCK_SIZE aligned +// larger than 0x10000 exceeds size_t, storage_file_* ops fail +#define USB_MSC_BUF_MAX (0x10000u - SCSI_BLOCK_SIZE) + +static usbd_respond usb_ep_config(usbd_device* dev, uint8_t cfg); +static usbd_respond usb_control(usbd_device* dev, usbd_ctlreq* req, usbd_rqc_callback* callback); + +typedef enum { + EventExit = 1 << 0, + EventReset = 1 << 1, + EventRxTx = 1 << 2, + + EventAll = EventExit | EventReset | EventRxTx, +} MassStorageEvent; + +typedef struct { + uint32_t sig; + uint32_t tag; + uint32_t len; + uint8_t flags; + uint8_t lun; + uint8_t cmd_len; + uint8_t cmd[16]; +} __attribute__((packed)) CBW; +typedef struct { + uint32_t sig; + uint32_t tag; + uint32_t residue; + uint8_t status; +} __attribute__((packed)) CSW; + +struct MassStorageUsb { + FuriHalUsbInterface usb; + FuriHalUsbInterface* usb_prev; + + FuriThread* thread; + usbd_device* dev; + SCSIDeviceFunc fn; +}; + +static int32_t mass_thread_worker(void* context) { + MassStorageUsb* mass = context; + usbd_device* dev = mass->dev; + SCSISession scsi = { + .fn = mass->fn, + }; + CBW cbw = {0}; + CSW csw = {0}; + uint8_t* buf = NULL; + uint32_t buf_len = 0, buf_cap = 0, buf_sent = 0; + enum { + StateReadCBW, + StateReadData, + StateWriteData, + StateBuildCSW, + StateWriteCSW, + } state = StateReadCBW; + while(true) { + uint32_t flags = furi_thread_flags_wait(EventAll, FuriFlagWaitAny, FuriFlagWaitAny); + if(flags & EventExit) { + FURI_LOG_I(TAG, "exit"); + free(buf); + return 0; + } + if(flags & EventReset) { + FURI_LOG_I(TAG, "reset"); + scsi.sk = 0; + scsi.asc = 0; + memset(&cbw, 0, sizeof(cbw)); + memset(&csw, 0, sizeof(csw)); + free(buf); + buf = NULL; + buf_len = buf_cap = buf_sent = 0; + state = StateReadCBW; + } + if(flags & EventRxTx) do { + switch(state) { + case StateReadCBW: { + // FURI_LOG_I(TAG, "StateReadCBW"); + int32_t len = usbd_ep_read(dev, USB_MSC_RX_EP, &cbw, sizeof(cbw)); + if(len <= 0) { + // FURI_LOG_I(TAG, "cbw not ready"); + break; + } + if(len != sizeof(cbw) || cbw.sig != CBW_SIG) { + FURI_LOG_W(TAG, "bad cbw sig=%08lx", cbw.sig); + usbd_ep_stall(dev, USB_MSC_TX_EP); + usbd_ep_stall(dev, USB_MSC_RX_EP); + continue; + } + if(!scsi_cmd_start(&scsi, cbw.cmd, cbw.cmd_len)) { + FURI_LOG_W(TAG, "bad cmd"); + usbd_ep_stall(dev, USB_MSC_RX_EP); + csw.sig = CSW_SIG; + csw.tag = cbw.tag; + csw.status = CSW_STATUS_NOK; + state = StateWriteCSW; + continue; + } + if(cbw.flags & CBW_FLAGS_DEVICE_TO_HOST) { + buf_len = 0; + buf_sent = 0; + state = StateWriteData; + } else { + buf_len = 0; + state = StateReadData; + } + continue; + }; break; + case StateReadData: { + // FURI_LOG_I(TAG, "StateReadData %d/%d", buf_len, cbw.len); + if(!cbw.len) { + state = StateBuildCSW; + continue; + } + uint32_t buf_clamp = MIN(cbw.len, USB_MSC_BUF_MAX); + if(buf_clamp > buf_cap) { + // FURI_LOG_I(TAG, "growing buf %d -> %d", buf_cap, buf_clamp); + free(buf); + buf_cap = buf_clamp; + buf = malloc(buf_cap); + } + if(buf_len < buf_clamp) { + int32_t len = + usbd_ep_read(dev, USB_MSC_RX_EP, buf + buf_len, buf_clamp - buf_len); + if(len < 0) { + // FURI_LOG_I(TAG, "rx not ready %d", len); + break; + } + // FURI_LOG_I(TAG, "clamp %ld len %d", buf_clamp, len); + buf_len += len; + } + if(buf_len == buf_clamp) { + if(!scsi_cmd_rx_data(&scsi, buf, buf_len)) { + FURI_LOG_W(TAG, "short rx"); + usbd_ep_stall(dev, USB_MSC_RX_EP); + csw.sig = CSW_SIG; + csw.tag = cbw.tag; + csw.status = CSW_STATUS_NOK; + csw.residue = cbw.len; + state = StateWriteCSW; + continue; + } + cbw.len -= buf_len; + buf_len = 0; + } + continue; + }; break; + case StateWriteData: { + // FURI_LOG_I(TAG, "StateWriteData %d", cbw.len); + if(!cbw.len) { + state = StateBuildCSW; + continue; + } + uint32_t buf_clamp = MIN(cbw.len, USB_MSC_BUF_MAX); + if(buf_clamp > buf_cap) { + // FURI_LOG_I(TAG, "growing buf %d -> %d", buf_cap, buf_clamp); + free(buf); + buf_cap = buf_clamp; + buf = malloc(buf_cap); + } + if(!buf_len && !scsi_cmd_tx_data(&scsi, buf, &buf_len, buf_clamp)) { + FURI_LOG_W(TAG, "short tx"); + // usbd_ep_stall(dev, USB_MSC_TX_EP); + state = StateBuildCSW; + continue; + } + int32_t len = usbd_ep_write( + dev, + USB_MSC_TX_EP, + buf + buf_sent, + MIN(USB_MSC_TX_EP_SIZE, buf_len - buf_sent)); + if(len < 0) { + // FURI_LOG_I(TAG, "tx not ready %d", len); + break; + } + buf_sent += len; + if(buf_sent == buf_len) { + cbw.len -= buf_len; + buf_len = 0; + buf_sent = 0; + } + continue; + }; break; + case StateBuildCSW: { + // FURI_LOG_I(TAG, "StateBuildCSW"); + csw.sig = CSW_SIG; + csw.tag = cbw.tag; + if(scsi_cmd_end(&scsi)) { + csw.status = CSW_STATUS_OK; + } else { + csw.status = CSW_STATUS_NOK; + } + csw.residue = cbw.len; + state = StateWriteCSW; + continue; + }; break; + case StateWriteCSW: { + // FURI_LOG_I(TAG, "StateWriteCSW"); + if(csw.status) { + FURI_LOG_W( + TAG, + "csw sig=%08lx tag=%08lx residue=%08lx status=%02x", + csw.sig, + csw.tag, + csw.residue, + csw.status); + } + int32_t len = usbd_ep_write(dev, USB_MSC_TX_EP, &csw, sizeof(csw)); + if(len < 0) { + // FURI_LOG_I(TAG, "csw not ready"); + break; + } + if(len != sizeof(csw)) { + FURI_LOG_W(TAG, "bad csw write %ld", len); + usbd_ep_stall(dev, USB_MSC_TX_EP); + break; + } + memset(&cbw, 0, sizeof(cbw)); + memset(&csw, 0, sizeof(csw)); + state = StateReadCBW; + continue; + }; break; + } + break; + } while(true); + } +} + +// needed in usb_deinit, usb_suspend, usb_rxtx_ep_callback, usb_control, +// where if_ctx isn't passed +static MassStorageUsb* mass_cur = NULL; + +static void usb_init(usbd_device* dev, FuriHalUsbInterface* intf, void* ctx) { + UNUSED(intf); + MassStorageUsb* mass = ctx; + mass_cur = mass; + mass->dev = dev; + + usbd_reg_config(dev, usb_ep_config); + usbd_reg_control(dev, usb_control); + usbd_connect(dev, true); + + mass->thread = furi_thread_alloc(); + furi_thread_set_name(mass->thread, "MassStorageUsb"); + furi_thread_set_stack_size(mass->thread, 1024); + furi_thread_set_context(mass->thread, ctx); + furi_thread_set_callback(mass->thread, mass_thread_worker); + furi_thread_start(mass->thread); +} + +static void usb_deinit(usbd_device* dev) { + usbd_reg_config(dev, NULL); + usbd_reg_control(dev, NULL); + + MassStorageUsb* mass = mass_cur; + if(!mass || mass->dev != dev) { + FURI_LOG_E(TAG, "deinit mass_cur leak"); + return; + } + mass_cur = NULL; + + furi_assert(mass->thread); + furi_thread_flags_set(furi_thread_get_id(mass->thread), EventExit); + furi_thread_join(mass->thread); + furi_thread_free(mass->thread); + mass->thread = NULL; + + free(mass->usb.str_prod_descr); + mass->usb.str_prod_descr = NULL; + free(mass->usb.str_serial_descr); + mass->usb.str_serial_descr = NULL; + free(mass); +} + +static void usb_wakeup(usbd_device* dev) { + UNUSED(dev); +} + +static void usb_suspend(usbd_device* dev) { + MassStorageUsb* mass = mass_cur; + if(!mass || mass->dev != dev) return; + furi_thread_flags_set(furi_thread_get_id(mass->thread), EventReset); +} + +static void usb_rxtx_ep_callback(usbd_device* dev, uint8_t event, uint8_t ep) { + UNUSED(event); + UNUSED(ep); + MassStorageUsb* mass = mass_cur; + if(!mass || mass->dev != dev) return; + furi_thread_flags_set(furi_thread_get_id(mass->thread), EventRxTx); +} + +static usbd_respond usb_ep_config(usbd_device* dev, uint8_t cfg) { + switch(cfg) { + case 0: // deconfig + usbd_ep_deconfig(dev, USB_MSC_RX_EP); + usbd_ep_deconfig(dev, USB_MSC_TX_EP); + usbd_reg_endpoint(dev, USB_MSC_RX_EP, NULL); + usbd_reg_endpoint(dev, USB_MSC_TX_EP, NULL); + return usbd_ack; + case 1: // config + usbd_ep_config( + dev, USB_MSC_RX_EP, USB_EPTYPE_BULK /* | USB_EPTYPE_DBLBUF*/, USB_MSC_RX_EP_SIZE); + usbd_ep_config( + dev, USB_MSC_TX_EP, USB_EPTYPE_BULK /* | USB_EPTYPE_DBLBUF*/, USB_MSC_TX_EP_SIZE); + usbd_reg_endpoint(dev, USB_MSC_RX_EP, usb_rxtx_ep_callback); + usbd_reg_endpoint(dev, USB_MSC_TX_EP, usb_rxtx_ep_callback); + return usbd_ack; + } + return usbd_fail; +} + +static usbd_respond usb_control(usbd_device* dev, usbd_ctlreq* req, usbd_rqc_callback* callback) { + UNUSED(callback); + if(((USB_REQ_RECIPIENT | USB_REQ_TYPE) & req->bmRequestType) != + (USB_REQ_INTERFACE | USB_REQ_CLASS)) { + return usbd_fail; + } + switch(req->bRequest) { + case USB_MSC_BOT_GET_MAX_LUN: { + static uint8_t max_lun = 0; + dev->status.data_ptr = &max_lun; + dev->status.data_count = 1; + return usbd_ack; + }; break; + case USB_MSC_BOT_RESET: { + MassStorageUsb* mass = mass_cur; + if(!mass || mass->dev != dev) return usbd_fail; + furi_thread_flags_set(furi_thread_get_id(mass->thread), EventReset); + return usbd_ack; + }; break; + } + return usbd_fail; +} + +static const struct usb_string_descriptor dev_manuf_desc = USB_STRING_DESC("Flipper Devices Inc."); + +struct MassStorageDescriptor { + struct usb_config_descriptor config; + struct usb_interface_descriptor intf; + struct usb_endpoint_descriptor ep_rx; + struct usb_endpoint_descriptor ep_tx; +} __attribute__((packed)); + +static const struct usb_device_descriptor usb_mass_dev_descr = { + .bLength = sizeof(struct usb_device_descriptor), + .bDescriptorType = USB_DTYPE_DEVICE, + .bcdUSB = VERSION_BCD(2, 0, 0), + .bDeviceClass = USB_CLASS_PER_INTERFACE, + .bDeviceSubClass = USB_SUBCLASS_NONE, + .bDeviceProtocol = USB_PROTO_NONE, + .bMaxPacketSize0 = 8, // USB_EP0_SIZE + .idVendor = 0x0483, + .idProduct = 0x5720, + .bcdDevice = VERSION_BCD(1, 0, 0), + .iManufacturer = 1, // UsbDevManuf + .iProduct = 2, // UsbDevProduct + .iSerialNumber = 3, // UsbDevSerial + .bNumConfigurations = 1, +}; + +static const struct MassStorageDescriptor usb_mass_cfg_descr = { + .config = + { + .bLength = sizeof(struct usb_config_descriptor), + .bDescriptorType = USB_DTYPE_CONFIGURATION, + .wTotalLength = sizeof(struct MassStorageDescriptor), + .bNumInterfaces = 1, + .bConfigurationValue = 1, + .iConfiguration = NO_DESCRIPTOR, + .bmAttributes = USB_CFG_ATTR_RESERVED | USB_CFG_ATTR_SELFPOWERED, + .bMaxPower = USB_CFG_POWER_MA(100), + }, + .intf = + { + .bLength = sizeof(struct usb_interface_descriptor), + .bDescriptorType = USB_DTYPE_INTERFACE, + .bInterfaceNumber = 0, + .bAlternateSetting = 0, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_MASS_STORAGE, + .bInterfaceSubClass = 0x06, // scsi transparent + .bInterfaceProtocol = 0x50, // bulk only + .iInterface = NO_DESCRIPTOR, + }, + .ep_rx = + { + .bLength = sizeof(struct usb_endpoint_descriptor), + .bDescriptorType = USB_DTYPE_ENDPOINT, + .bEndpointAddress = USB_MSC_RX_EP, + .bmAttributes = USB_EPTYPE_BULK, + .wMaxPacketSize = USB_MSC_RX_EP_SIZE, + .bInterval = 0, + }, + .ep_tx = + { + .bLength = sizeof(struct usb_endpoint_descriptor), + .bDescriptorType = USB_DTYPE_ENDPOINT, + .bEndpointAddress = USB_MSC_TX_EP, + .bmAttributes = USB_EPTYPE_BULK, + .wMaxPacketSize = USB_MSC_TX_EP_SIZE, + .bInterval = 0, + }, +}; + +MassStorageUsb* mass_storage_usb_start(const char* filename, SCSIDeviceFunc fn) { + MassStorageUsb* mass = malloc(sizeof(MassStorageUsb)); + mass->usb_prev = furi_hal_usb_get_config(); + mass->usb.init = usb_init; + mass->usb.deinit = usb_deinit; + mass->usb.wakeup = usb_wakeup; + mass->usb.suspend = usb_suspend; + mass->usb.dev_descr = (struct usb_device_descriptor*)&usb_mass_dev_descr; + mass->usb.str_manuf_descr = (void*)&dev_manuf_desc; + mass->usb.str_prod_descr = NULL; + mass->usb.str_serial_descr = NULL; + mass->usb.cfg_descr = (void*)&usb_mass_cfg_descr; + + const char* name = furi_hal_version_get_device_name_ptr(); + if(!name) name = "Flipper Zero"; + size_t len = strlen(name); + struct usb_string_descriptor* str_prod_descr = malloc(len * 2 + 2); + str_prod_descr->bLength = len * 2 + 2; + str_prod_descr->bDescriptorType = USB_DTYPE_STRING; + for(uint8_t i = 0; i < len; i++) str_prod_descr->wString[i] = name[i]; + mass->usb.str_prod_descr = str_prod_descr; + + len = strlen(filename); + struct usb_string_descriptor* str_serial_descr = malloc(len * 2 + 2); + str_serial_descr->bLength = len * 2 + 2; + str_serial_descr->bDescriptorType = USB_DTYPE_STRING; + for(uint8_t i = 0; i < len; i++) str_serial_descr->wString[i] = filename[i]; + mass->usb.str_serial_descr = str_serial_descr; + + mass->fn = fn; + if(!furi_hal_usb_set_config(&mass->usb, mass)) { + FURI_LOG_E(TAG, "USB locked, cannot start Mass Storage"); + free(mass->usb.str_prod_descr); + free(mass->usb.str_serial_descr); + free(mass); + return NULL; + } + return mass; +} + +void mass_storage_usb_stop(MassStorageUsb* mass) { + furi_hal_usb_set_config(mass->usb_prev, NULL); + // freed by usb_deinit asynchronously from usb thread +} diff --git a/applications/external/.mass_storage/helpers/mass_storage_usb.h b/applications/external/.mass_storage/helpers/mass_storage_usb.h new file mode 100644 index 0000000000..0f370f98ed --- /dev/null +++ b/applications/external/.mass_storage/helpers/mass_storage_usb.h @@ -0,0 +1,9 @@ +#pragma once + +#include +#include "mass_storage_scsi.h" + +typedef struct MassStorageUsb MassStorageUsb; + +MassStorageUsb* mass_storage_usb_start(const char* filename, SCSIDeviceFunc fn); +void mass_storage_usb_stop(MassStorageUsb* mass); diff --git a/applications/external/.mass_storage/mass_storage_app.c b/applications/external/.mass_storage/mass_storage_app.c new file mode 100644 index 0000000000..f07c8aa6c9 --- /dev/null +++ b/applications/external/.mass_storage/mass_storage_app.c @@ -0,0 +1,124 @@ +#include "mass_storage_app_i.h" +#include +#include +#include + +static bool mass_storage_app_custom_event_callback(void* context, uint32_t event) { + furi_assert(context); + MassStorageApp* app = context; + return scene_manager_handle_custom_event(app->scene_manager, event); +} + +static bool mass_storage_app_back_event_callback(void* context) { + furi_assert(context); + MassStorageApp* app = context; + return scene_manager_handle_back_event(app->scene_manager); +} + +static void mass_storage_app_tick_event_callback(void* context) { + furi_assert(context); + MassStorageApp* app = context; + scene_manager_handle_tick_event(app->scene_manager); +} + +static bool mass_storage_check_assets(Storage* fs_api) { + File* dir = storage_file_alloc(fs_api); + bool ret = false; + + if(storage_dir_open(dir, MASS_STORAGE_APP_PATH_FOLDER)) { + ret = true; + } + + storage_dir_close(dir); + storage_file_free(dir); + + return ret; +} + +MassStorageApp* mass_storage_app_alloc(char* arg) { + MassStorageApp* app = malloc(sizeof(MassStorageApp)); + memset(app, 0, sizeof(MassStorageApp)); + + if(arg != NULL) { + FuriString* filename = furi_string_alloc_set(arg); + if(furi_string_start_with_str(filename, MASS_STORAGE_APP_PATH_FOLDER)) { + furi_string_right(filename, strlen(MASS_STORAGE_APP_PATH_FOLDER) + 1); + } + strlcpy(app->file_name, furi_string_get_cstr(filename), MASS_STORAGE_FILE_NAME_LEN); + furi_string_free(filename); + } + + app->gui = furi_record_open(RECORD_GUI); + app->fs_api = furi_record_open(RECORD_STORAGE); + app->notifications = furi_record_open(RECORD_NOTIFICATION); + app->dialogs = furi_record_open(RECORD_DIALOGS); + + storage_simply_mkdir(app->fs_api, MASS_STORAGE_APP_PATH_FOLDER); + + app->view_dispatcher = view_dispatcher_alloc(); + view_dispatcher_enable_queue(app->view_dispatcher); + + app->scene_manager = scene_manager_alloc(&mass_storage_scene_handlers, app); + + view_dispatcher_set_event_callback_context(app->view_dispatcher, app); + view_dispatcher_set_tick_event_callback( + app->view_dispatcher, mass_storage_app_tick_event_callback, 500); + view_dispatcher_set_custom_event_callback( + app->view_dispatcher, mass_storage_app_custom_event_callback); + view_dispatcher_set_navigation_event_callback( + app->view_dispatcher, mass_storage_app_back_event_callback); + + // Custom Widget + app->widget = widget_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, MassStorageAppViewError, widget_get_view(app->widget)); + + app->mass_storage_view = mass_storage_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, + MassStorageAppViewWork, + mass_storage_get_view(app->mass_storage_view)); + + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + + if(*app->file_name != '\0') { + scene_manager_next_scene(app->scene_manager, MassStorageSceneWork); + } else if(mass_storage_check_assets(app->fs_api)) { + scene_manager_next_scene(app->scene_manager, MassStorageSceneFileSelect); + } else { + scene_manager_next_scene(app->scene_manager, MassStorageSceneError); + } + + return app; +} + +void mass_storage_app_free(MassStorageApp* app) { + furi_assert(app); + + // Views + view_dispatcher_remove_view(app->view_dispatcher, MassStorageAppViewWork); + mass_storage_free(app->mass_storage_view); + + // Custom Widget + view_dispatcher_remove_view(app->view_dispatcher, MassStorageAppViewError); + widget_free(app->widget); + + // View dispatcher + view_dispatcher_free(app->view_dispatcher); + scene_manager_free(app->scene_manager); + + // Close records + furi_record_close(RECORD_GUI); + furi_record_close(RECORD_STORAGE); + furi_record_close(RECORD_NOTIFICATION); + furi_record_close(RECORD_DIALOGS); + + free(app); +} + +int32_t mass_storage_app(void* p) { + MassStorageApp* mass_storage_app = mass_storage_app_alloc((char*)p); + view_dispatcher_run(mass_storage_app->view_dispatcher); + mass_storage_app_free(mass_storage_app); + return 0; +} diff --git a/applications/external/.mass_storage/mass_storage_app.h b/applications/external/.mass_storage/mass_storage_app.h new file mode 100644 index 0000000000..820ad2b6c7 --- /dev/null +++ b/applications/external/.mass_storage/mass_storage_app.h @@ -0,0 +1,11 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct MassStorageApp MassStorageApp; + +#ifdef __cplusplus +} +#endif diff --git a/applications/external/.mass_storage/mass_storage_app_i.h b/applications/external/.mass_storage/mass_storage_app_i.h new file mode 100644 index 0000000000..4db7a40f3a --- /dev/null +++ b/applications/external/.mass_storage/mass_storage_app_i.h @@ -0,0 +1,44 @@ +#pragma once + +#include "mass_storage_app.h" +#include "scenes/mass_storage_scene.h" +#include "helpers/mass_storage_usb.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "views/mass_storage_view.h" + +#define MASS_STORAGE_APP_PATH_FOLDER STORAGE_APP_DATA_PATH_PREFIX +// #define MASS_STORAGE_APP_EXTENSION ".iso" +#define MASS_STORAGE_FILE_NAME_LEN 40 + +struct MassStorageApp { + Gui* gui; + Storage* fs_api; + ViewDispatcher* view_dispatcher; + SceneManager* scene_manager; + NotificationApp* notifications; + DialogsApp* dialogs; + Widget* widget; + + char file_name[MASS_STORAGE_FILE_NAME_LEN]; + File* file; + MassStorage* mass_storage_view; + + FuriMutex* usb_mutex; + MassStorageUsb* usb; +}; + +typedef enum { + MassStorageAppViewError, + MassStorageAppViewFileSelect, + MassStorageAppViewWork, +} MassStorageAppView; diff --git a/applications/external/.mass_storage/scenes/mass_storage_scene.c b/applications/external/.mass_storage/scenes/mass_storage_scene.c new file mode 100644 index 0000000000..bab24ca400 --- /dev/null +++ b/applications/external/.mass_storage/scenes/mass_storage_scene.c @@ -0,0 +1,30 @@ +#include "mass_storage_scene.h" + +// Generate scene on_enter handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, +void (*const mass_storage_scene_on_enter_handlers[])(void*) = { +#include "mass_storage_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_event handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event, +bool (*const mass_storage_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = { +#include "mass_storage_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_exit handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit, +void (*const mass_storage_scene_on_exit_handlers[])(void* context) = { +#include "mass_storage_scene_config.h" +}; +#undef ADD_SCENE + +// Initialize scene handlers configuration structure +const SceneManagerHandlers mass_storage_scene_handlers = { + .on_enter_handlers = mass_storage_scene_on_enter_handlers, + .on_event_handlers = mass_storage_scene_on_event_handlers, + .on_exit_handlers = mass_storage_scene_on_exit_handlers, + .scene_num = MassStorageSceneNum, +}; diff --git a/applications/external/.mass_storage/scenes/mass_storage_scene.h b/applications/external/.mass_storage/scenes/mass_storage_scene.h new file mode 100644 index 0000000000..d43bb4c978 --- /dev/null +++ b/applications/external/.mass_storage/scenes/mass_storage_scene.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +// Generate scene id and total number +#define ADD_SCENE(prefix, name, id) MassStorageScene##id, +typedef enum { +#include "mass_storage_scene_config.h" + MassStorageSceneNum, +} MassStorageScene; +#undef ADD_SCENE + +extern const SceneManagerHandlers mass_storage_scene_handlers; + +// Generate scene on_enter handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); +#include "mass_storage_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_event handlers declaration +#define ADD_SCENE(prefix, name, id) \ + bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event); +#include "mass_storage_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_exit handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context); +#include "mass_storage_scene_config.h" +#undef ADD_SCENE diff --git a/applications/external/.mass_storage/scenes/mass_storage_scene_config.h b/applications/external/.mass_storage/scenes/mass_storage_scene_config.h new file mode 100644 index 0000000000..7104fd852c --- /dev/null +++ b/applications/external/.mass_storage/scenes/mass_storage_scene_config.h @@ -0,0 +1,3 @@ +ADD_SCENE(mass_storage, file_select, FileSelect) +ADD_SCENE(mass_storage, work, Work) +ADD_SCENE(mass_storage, error, Error) diff --git a/applications/external/.mass_storage/scenes/mass_storage_scene_error.c b/applications/external/.mass_storage/scenes/mass_storage_scene_error.c new file mode 100644 index 0000000000..daef7ccd19 --- /dev/null +++ b/applications/external/.mass_storage/scenes/mass_storage_scene_error.c @@ -0,0 +1,54 @@ +#include "../mass_storage_app_i.h" +#include + +typedef enum { + SubghzCustomEventErrorBack, +} MassStorageCustomEvent; + +static void + mass_storage_scene_error_event_callback(GuiButtonType result, InputType type, void* context) { + furi_assert(context); + MassStorageApp* app = context; + + if((result == GuiButtonTypeLeft) && (type == InputTypeShort)) { + view_dispatcher_send_custom_event(app->view_dispatcher, SubghzCustomEventErrorBack); + } +} + +void mass_storage_scene_error_on_enter(void* context) { + MassStorageApp* app = context; + + widget_add_icon_element(app->widget, 0, 0, &I_SDQuestion_35x43); + + widget_add_string_multiline_element( + app->widget, + 81, + 4, + AlignCenter, + AlignTop, + FontSecondary, + "No SD card or\napp data found.\nThis app requires a\nmass_storage/\ndirectory."); + + widget_add_button_element( + app->widget, GuiButtonTypeLeft, "Back", mass_storage_scene_error_event_callback, app); + + view_dispatcher_switch_to_view(app->view_dispatcher, MassStorageAppViewError); +} + +bool mass_storage_scene_error_on_event(void* context, SceneManagerEvent event) { + MassStorageApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubghzCustomEventErrorBack) { + view_dispatcher_stop(app->view_dispatcher); + consumed = true; + } + } + return consumed; +} + +void mass_storage_scene_error_on_exit(void* context) { + MassStorageApp* app = context; + widget_reset(app->widget); +} diff --git a/applications/external/.mass_storage/scenes/mass_storage_scene_file_select.c b/applications/external/.mass_storage/scenes/mass_storage_scene_file_select.c new file mode 100644 index 0000000000..36c8c4692b --- /dev/null +++ b/applications/external/.mass_storage/scenes/mass_storage_scene_file_select.c @@ -0,0 +1,47 @@ +#include "../mass_storage_app_i.h" +#include "furi_hal_power.h" + +static bool mass_storage_file_select(MassStorageApp* mass_storage) { + furi_assert(mass_storage); + + FuriString* file_path = furi_string_alloc(); + DialogsFileBrowserOptions browser_options; + dialog_file_browser_set_basic_options(&browser_options, "*", NULL); + browser_options.base_path = MASS_STORAGE_APP_PATH_FOLDER; + furi_string_set(file_path, MASS_STORAGE_APP_PATH_FOLDER); + + bool res = + dialog_file_browser_show(mass_storage->dialogs, file_path, file_path, &browser_options); + + if(res) { + strlcpy( + mass_storage->file_name, + furi_string_get_cstr(file_path), + sizeof(mass_storage->file_name)); + } + furi_string_free(file_path); + return res; +} + +void mass_storage_scene_file_select_on_enter(void* context) { + MassStorageApp* mass_storage = context; + + if(mass_storage_file_select(mass_storage)) { + scene_manager_next_scene(mass_storage->scene_manager, MassStorageSceneWork); + } else { + scene_manager_previous_scene(mass_storage->scene_manager); + view_dispatcher_stop(mass_storage->view_dispatcher); + } +} + +bool mass_storage_scene_file_select_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + // MassStorageApp* mass_storage = context; + return false; +} + +void mass_storage_scene_file_select_on_exit(void* context) { + UNUSED(context); + // MassStorageApp* mass_storage = context; +} diff --git a/applications/external/.mass_storage/scenes/mass_storage_scene_work.c b/applications/external/.mass_storage/scenes/mass_storage_scene_work.c new file mode 100644 index 0000000000..4112931ead --- /dev/null +++ b/applications/external/.mass_storage/scenes/mass_storage_scene_work.c @@ -0,0 +1,104 @@ +#include "../mass_storage_app_i.h" +#include "../views/mass_storage_view.h" +#include "../helpers/mass_storage_usb.h" + +#define TAG "MassStorageSceneWork" + +static bool file_read( + void* ctx, + uint32_t lba, + uint16_t count, + uint8_t* out, + uint32_t* out_len, + uint32_t out_cap) { + MassStorageApp* app = ctx; + // FURI_LOG_I(TAG, "file_read lba=%08lx count=%04x out_cap=%04x", lba, count, out_cap); + if(!storage_file_seek(app->file, lba * SCSI_BLOCK_SIZE, true)) { + FURI_LOG_W(TAG, "seek failed"); + return false; + } + uint16_t clamp = MIN(out_cap, count * SCSI_BLOCK_SIZE); + *out_len = storage_file_read(app->file, out, clamp); + // FURI_LOG_I(TAG, "%d/%d", *out_len, count * SCSI_BLOCK_SIZE); + return *out_len == clamp; +} + +static bool file_write(void* ctx, uint32_t lba, uint16_t count, uint8_t* buf, uint32_t len) { + MassStorageApp* app = ctx; + // FURI_LOG_I(TAG, "file_write lba=%08lx count=%04x len=%04x", lba, count, len); + if(len != count * SCSI_BLOCK_SIZE) { + FURI_LOG_W(TAG, "bad write params count=%d len=%ld", count, len); + return false; + } + if(!storage_file_seek(app->file, lba * SCSI_BLOCK_SIZE, true)) { + FURI_LOG_W(TAG, "seek failed"); + return false; + } + return storage_file_write(app->file, buf, len) == len; +} + +static uint32_t file_num_blocks(void* ctx) { + MassStorageApp* app = ctx; + return storage_file_size(app->file) / SCSI_BLOCK_SIZE; +} + +static void file_eject(void* ctx) { + MassStorageApp* app = ctx; + FURI_LOG_I(TAG, "EJECT"); + furi_check(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); +} + +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); + } + return false; +} + +void mass_storage_scene_work_on_enter(void* context) { + MassStorageApp* app = context; + + app->usb_mutex = furi_mutex_alloc(FuriMutexTypeNormal); + + FuriString* file_name = furi_string_alloc(); + + mass_storage_set_file_name(app->mass_storage_view, app->file_name); + furi_string_printf(file_name, "%s/%s", MASS_STORAGE_APP_PATH_FOLDER, app->file_name); + app->file = storage_file_alloc(app->fs_api); + furi_assert(storage_file_open( + app->file, furi_string_get_cstr(file_name), FSAM_READ | FSAM_WRITE, FSOM_OPEN_EXISTING)); + furi_string_free(file_name); + + SCSIDeviceFunc fn = { + .ctx = app, + .read = file_read, + .write = file_write, + .num_blocks = file_num_blocks, + .eject = file_eject, + }; + app->usb = mass_storage_usb_start(app->file_name, fn); + + view_dispatcher_switch_to_view(app->view_dispatcher, MassStorageAppViewWork); +} + +void mass_storage_scene_work_on_exit(void* context) { + MassStorageApp* app = context; + + furi_mutex_free(app->usb_mutex); + if(app->usb) { + mass_storage_usb_stop(app->usb); + app->usb = NULL; + } + if(app->file) { + storage_file_free(app->file); + app->file = NULL; + } +} diff --git a/applications/external/.mass_storage/views/mass_storage_view.c b/applications/external/.mass_storage/views/mass_storage_view.c new file mode 100644 index 0000000000..7cf0fa66c4 --- /dev/null +++ b/applications/external/.mass_storage/views/mass_storage_view.c @@ -0,0 +1,53 @@ +#include "mass_storage_view.h" +#include +#include + +struct MassStorage { + View* view; +}; + +typedef struct { + char* file_name; +} MassStorageModel; + +static void mass_storage_draw_callback(Canvas* canvas, void* _model) { + MassStorageModel* model = _model; + + FuriString* disp_str = furi_string_alloc_set(model->file_name); + elements_string_fit_width(canvas, disp_str, 128 - 2); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 2, 8, furi_string_get_cstr(disp_str)); + furi_string_reset(disp_str); + + canvas_draw_icon(canvas, 40, 20, &I_UsbTree_48x22); + + furi_string_free(disp_str); +} + +MassStorage* mass_storage_alloc() { + MassStorage* mass_storage = malloc(sizeof(MassStorage)); + + mass_storage->view = view_alloc(); + view_allocate_model(mass_storage->view, ViewModelTypeLocking, sizeof(MassStorageModel)); + view_set_context(mass_storage->view, mass_storage); + view_set_draw_callback(mass_storage->view, mass_storage_draw_callback); + + return mass_storage; +} + +void mass_storage_free(MassStorage* mass_storage) { + furi_assert(mass_storage); + view_free(mass_storage->view); + free(mass_storage); +} + +View* mass_storage_get_view(MassStorage* mass_storage) { + furi_assert(mass_storage); + return mass_storage->view; +} + +void mass_storage_set_file_name(MassStorage* mass_storage, char* name) { + furi_assert(name); + with_view_model( + mass_storage->view, MassStorageModel * model, { model->file_name = name; }, true); +} diff --git a/applications/external/.mass_storage/views/mass_storage_view.h b/applications/external/.mass_storage/views/mass_storage_view.h new file mode 100644 index 0000000000..af7b1fb37c --- /dev/null +++ b/applications/external/.mass_storage/views/mass_storage_view.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +typedef struct MassStorage MassStorage; + +MassStorage* mass_storage_alloc(); + +void mass_storage_free(MassStorage* mass_storage); + +View* mass_storage_get_view(MassStorage* mass_storage); + +void mass_storage_set_file_name(MassStorage* mass_storage, char* name); diff --git a/applications/external/4inrow/4inrow.c b/applications/external/4inrow/4inrow.c new file mode 100644 index 0000000000..e9b7b6c698 --- /dev/null +++ b/applications/external/4inrow/4inrow.c @@ -0,0 +1,320 @@ +#include +#include +#include +#include +#include +#include + +static int matrix[6][7] = {0}; +static int cursorx = 3; +static int cursory = 5; +static int player = 1; +static int scoreX = 0; +static int scoreO = 0; + +typedef struct { + FuriMutex* mutex; +} FourInRowState; + +void init() { + for(size_t i = 0; i < 6; i++) { + for(size_t j = 0; j < 7; j++) { + matrix[i][j] = 0; + } + } + cursorx = 3; + cursory = 5; + player = 1; +} + +const NotificationSequence end = { + &message_vibro_on, + + &message_note_ds4, + &message_delay_10, + &message_sound_off, + &message_delay_10, + + &message_note_ds4, + &message_delay_10, + &message_sound_off, + &message_delay_10, + + &message_note_ds4, + &message_delay_10, + &message_sound_off, + &message_delay_10, + + &message_vibro_off, + NULL, +}; + +void intToStr(int num, char* str) { + int i = 0, sign = 0; + + if(num < 0) { + num = -num; + sign = 1; + } + + do { + str[i++] = num % 10 + '0'; + num /= 10; + } while(num > 0); + + if(sign) { + str[i++] = '-'; + } + + str[i] = '\0'; + + // Reverse the string + int j, len = i; + char temp; + for(j = 0; j < len / 2; j++) { + temp = str[j]; + str[j] = str[len - j - 1]; + str[len - j - 1] = temp; + } +} + +int next_height(int x) { + if(matrix[0][x] != 0) { + return -1; + } + for(size_t y = 1; y < 6; y++) { + if(matrix[y][x] != 0) { + return y - 1; + } + } + return 5; +} + +int wincheck() { + for(size_t y = 0; y <= 2; y++) { + for(size_t x = 0; x <= 6; x++) { + if(matrix[y][x] != 0 && matrix[y][x] == matrix[y + 1][x] && + matrix[y][x] == matrix[y + 2][x] && matrix[y][x] == matrix[y + 3][x]) { + return matrix[y][x]; + } + } + } + + for(size_t y = 0; y <= 5; y++) { + for(size_t x = 0; x <= 3; x++) { + if(matrix[y][x] != 0 && matrix[y][x] == matrix[y][x + 1] && + matrix[y][x] == matrix[y][x + 2] && matrix[y][x] == matrix[y][x + 3]) { + return matrix[y][x]; + } + } + } + + for(size_t y = 0; y <= 2; y++) { + for(size_t x = 0; x <= 3; x++) { + if(matrix[y][x] != 0 && matrix[y][x] == matrix[y + 1][x + 1] && + matrix[y][x] == matrix[y + 2][x + 2] && matrix[y][x] == matrix[y + 3][x + 3]) { + return matrix[y][x]; + } + } + } + + for(size_t y = 3; y <= 5; y++) { + for(size_t x = 0; x <= 3; x++) { + if(matrix[y][x] != 0 && matrix[y][x] == matrix[y - 1][x + 1] && + matrix[y][x] == matrix[y - 2][x + 2] && matrix[y][x] == matrix[y - 3][x + 3]) { + return matrix[y][x]; + } + } + } + + bool tf = true; + for(size_t y = 0; y < 6; y++) { + for(size_t x = 0; x < 7; x++) { + if(matrix[y][x] == 0) { + tf = false; + } + } + } + if(tf) { + return 0; + } + + return -1; +} + +static void draw_callback(Canvas* canvas, void* ctx) { + furi_assert(ctx); + const FourInRowState* fourinrow_state = ctx; + + furi_mutex_acquire(fourinrow_state->mutex, FuriWaitForever); + canvas_clear(canvas); + + if(wincheck() != -1) { + canvas_set_font(canvas, FontPrimary); + + if(wincheck() == 0) { + canvas_draw_str(canvas, 30, 35, "Draw! O_o"); + } + if(wincheck() == 1) { + canvas_draw_str(canvas, 30, 35, "Player X win!"); + } + if(wincheck() == 2) { + canvas_draw_str(canvas, 30, 35, "Player O win!"); + } + + furi_mutex_release(fourinrow_state->mutex); + + return; + } + + for(size_t i = 0; i < 6; i++) { + for(size_t j = 0; j < 7; j++) { + char el[2]; + switch(matrix[i][j]) { + case 0: + strcpy(el, "_\0"); + break; + + case 1: + strcpy(el, "X\0"); + break; + + case 2: + strcpy(el, "O\0"); + break; + } + canvas_draw_str(canvas, j * 10 + 10, i * 10 + 10, el); + } + } + canvas_draw_str(canvas, cursorx * 10 + 8, cursory * 10 + 10, "[ ]"); + + if(player == 1) { + canvas_draw_str(canvas, 80, 10, "Turn: X"); + } + if(player == 2) { + canvas_draw_str(canvas, 80, 10, "Turn: O"); + } + char scX[1]; + intToStr(scoreX, scX); + char scO[1]; + intToStr(scoreO, scO); + + canvas_draw_str(canvas, 80, 20, "X:"); + canvas_draw_str(canvas, 90, 20, scX); + + canvas_draw_str(canvas, 80, 30, "O:"); + canvas_draw_str(canvas, 90, 30, scO); + + furi_mutex_release(fourinrow_state->mutex); +} + +static void input_callback(InputEvent* input_event, void* ctx) { + // Проверяем, что контекст не нулевой + furi_assert(ctx); + FuriMessageQueue* event_queue = ctx; + + furi_message_queue_put(event_queue, input_event, FuriWaitForever); +} + +int32_t four_in_row_app(void* p) { + UNUSED(p); + + // Текущее событие типа InputEvent + InputEvent event; + // Очередь событий на 8 элементов размера InputEvent + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); + + FourInRowState* fourinrow_state = malloc(sizeof(FourInRowState)); + + fourinrow_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); // Alloc Mutex + if(!fourinrow_state->mutex) { + FURI_LOG_E("4inRow", "cannot create mutex\r\n"); + furi_message_queue_free(event_queue); + free(fourinrow_state); + return 255; + } + + // dolphin_deed(DolphinDeedPluginGameStart); + + // Создаем новый view port + ViewPort* view_port = view_port_alloc(); + // Создаем callback отрисовки, без контекста + view_port_draw_callback_set(view_port, draw_callback, fourinrow_state); + // Создаем callback нажатий на клавиши, в качестве контекста передаем + // нашу очередь сообщений, чтоб запихивать в неё эти события + view_port_input_callback_set(view_port, input_callback, event_queue); + + // Создаем GUI приложения + Gui* gui = furi_record_open(RECORD_GUI); + // Подключаем view port к GUI в полноэкранном режиме + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); + notification_message_block(notification, &sequence_display_backlight_enforce_on); + + // Бесконечный цикл обработки очереди событий + while(1) { + // Выбираем событие из очереди в переменную event (ждем бесконечно долго, если очередь пуста) + // и проверяем, что у нас получилось это сделать + furi_check(furi_message_queue_get(event_queue, &event, FuriWaitForever) == FuriStatusOk); + furi_mutex_acquire(fourinrow_state->mutex, FuriWaitForever); + // Если нажата кнопка "назад", то выходим из цикла, а следовательно и из приложения + if(wincheck() != -1) { + notification_message(notification, &end); + furi_delay_ms(1000); + if(wincheck() == 1) { + scoreX++; + } + if(wincheck() == 2) { + scoreO++; + } + init(); + } + + if(event.type == InputTypePress) { + if(event.key == InputKeyOk) { + int nh = next_height(cursorx); + if(nh != -1) { + matrix[nh][cursorx] = player; + player = 3 - player; + } + } + if(event.key == InputKeyUp) { + //cursory--; + } + if(event.key == InputKeyDown) { + //cursory++; + } + if(event.key == InputKeyLeft) { + if(cursorx > 0) { + cursorx--; + } + } + if(event.key == InputKeyRight) { + if(cursorx < 6) { + cursorx++; + } + } + if(event.key == InputKeyBack) { + break; + } + } + view_port_update(view_port); + furi_mutex_release(fourinrow_state->mutex); + } + + // Clear notification + notification_message_block(notification, &sequence_display_backlight_enforce_auto); + furi_record_close(RECORD_NOTIFICATION); + + // Специальная очистка памяти, занимаемой очередью + furi_message_queue_free(event_queue); + + // Чистим созданные объекты, связанные с интерфейсом + gui_remove_view_port(gui, view_port); + view_port_free(view_port); + furi_mutex_free(fourinrow_state->mutex); + furi_record_close(RECORD_GUI); + free(fourinrow_state); + + return 0; +} diff --git a/applications/external/4inrow/4inrow_10px.png b/applications/external/4inrow/4inrow_10px.png new file mode 100644 index 0000000000..ef3e18f20d Binary files /dev/null and b/applications/external/4inrow/4inrow_10px.png differ diff --git a/applications/external/4inrow/application.fam b/applications/external/4inrow/application.fam new file mode 100644 index 0000000000..9d0db09ca2 --- /dev/null +++ b/applications/external/4inrow/application.fam @@ -0,0 +1,17 @@ +App( + appid="4inrow", + name="4 in a Row", + apptype=FlipperAppType.EXTERNAL, + entry_point="four_in_row_app", + requires=[ + "gui", + ], + stack_size=1 * 1024, + order=90, + fap_icon="4inrow_10px.png", + fap_category="Games", + fap_author="leo-need-more-coffee", + fap_weburl="https://github.com/leo-need-more-coffee/flipperzero-4inrow", + fap_version="1.0", + fap_description="4 in row Game", +) diff --git a/applications/external/airmouse/air_mouse.c b/applications/external/airmouse/air_mouse.c index 3bb7253b56..5ca5df21c1 100644 --- a/applications/external/airmouse/air_mouse.c +++ b/applications/external/airmouse/air_mouse.c @@ -1,4 +1,5 @@ #include "air_mouse.h" +#include #include @@ -52,6 +53,11 @@ uint32_t air_mouse_exit(void* context) { AirMouse* air_mouse_app_alloc() { AirMouse* app = malloc(sizeof(AirMouse)); + Storage* storage = furi_record_open(RECORD_STORAGE); + storage_common_migrate( + storage, EXT_PATH(".calibration.data"), APP_DATA_PATH("calibration.data")); + furi_record_close(RECORD_STORAGE); + // Gui app->gui = furi_record_open(RECORD_GUI); diff --git a/applications/external/airmouse/application.fam b/applications/external/airmouse/application.fam index 7bdba948a7..1164b66de1 100644 --- a/applications/external/airmouse/application.fam +++ b/applications/external/airmouse/application.fam @@ -1,5 +1,5 @@ App( - appid="Air_Mouse", + appid="air_mouse", name="[BMI160] Air Mouse", apptype=FlipperAppType.EXTERNAL, entry_point="air_mouse_app", diff --git a/applications/external/airmouse/tracking/calibration_data.h b/applications/external/airmouse/tracking/calibration_data.h index 7d240775ae..522a09a9a9 100644 --- a/applications/external/airmouse/tracking/calibration_data.h +++ b/applications/external/airmouse/tracking/calibration_data.h @@ -7,8 +7,7 @@ #include "util/vector.h" #define CALIBRATION_DATA_VER (1) -#define CALIBRATION_DATA_FILE_NAME ".calibration.data" -#define CALIBRATION_DATA_PATH EXT_PATH(CALIBRATION_DATA_FILE_NAME) +#define CALIBRATION_DATA_PATH APP_DATA_PATH("calibration.data") #define CALIBRATION_DATA_MAGIC (0x23) #define CALIBRATION_DATA_SAVE(x) \ diff --git a/applications/external/arkanoid/application.fam b/applications/external/arkanoid/application.fam index 56038c36ac..b33b9acae7 100644 --- a/applications/external/arkanoid/application.fam +++ b/applications/external/arkanoid/application.fam @@ -8,4 +8,7 @@ App( order=20, fap_icon="arkanoid_10px.png", fap_category="Games", + fap_author="@xMasterX & @gotnull", + fap_version="1.0", + fap_description="Arkanoid Game", ) diff --git a/applications/external/arkanoid/arkanoid_game.c b/applications/external/arkanoid/arkanoid_game.c index f6d8c1a3d0..4fba7766e8 100644 --- a/applications/external/arkanoid/arkanoid_game.c +++ b/applications/external/arkanoid/arkanoid_game.c @@ -397,6 +397,9 @@ int32_t arkanoid_game_app(void* p) { Gui* gui = furi_record_open(RECORD_GUI); gui_add_view_port(gui, view_port, GuiLayerFullscreen); + // Call dolphin deed on game start + // dolphin_deed(DolphinDeedPluginGameStart); + GameEvent event; for(bool processing = true; processing;) { FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); diff --git a/applications/external/asteroids/app.c b/applications/external/asteroids/app.c index 495542a249..cd2f50357f 100644 --- a/applications/external/asteroids/app.c +++ b/applications/external/asteroids/app.c @@ -28,7 +28,8 @@ #define MAXPOWERUPS 3 /* Max powerups allowed on screen */ #define POWERUPSTTL 400 /* Max powerup time to live, in ticks. */ #define SHIP_HIT_ANIMATION_LEN 15 -#define SAVING_DIRECTORY "/ext/apps/Games" +// Tick runs in thread, cant use APP_DATA_PATH() +#define SAVING_DIRECTORY EXT_PATH("apps_data/asteroids") #define SAVING_FILENAME SAVING_DIRECTORY "/game_asteroids.save" #ifndef PI #define PI 3.14159265358979f @@ -1146,6 +1147,8 @@ void game_tick(void* ctx) { bool load_game(AsteroidsApp* app) { Storage* storage = furi_record_open(RECORD_STORAGE); + storage_common_mkdir(storage, SAVING_DIRECTORY); + storage_common_migrate(storage, EXT_PATH("apps/Games/game_asteroids.save"), SAVING_FILENAME); File* file = storage_file_alloc(storage); uint16_t bytes_readed = 0; @@ -1163,12 +1166,6 @@ bool load_game(AsteroidsApp* app) { void save_game(AsteroidsApp* app) { Storage* storage = furi_record_open(RECORD_STORAGE); - if(storage_common_stat(storage, SAVING_DIRECTORY, NULL) == FSE_NOT_EXIST) { - if(!storage_simply_mkdir(storage, SAVING_DIRECTORY)) { - return; - } - } - File* file = storage_file_alloc(storage); if(storage_file_open(file, SAVING_FILENAME, FSAM_WRITE, FSOM_CREATE_ALWAYS)) { storage_file_write(file, app, sizeof(AsteroidsApp)); diff --git a/applications/external/asteroids/application.fam b/applications/external/asteroids/application.fam index 5eb43a6e52..ca5f5d1759 100644 --- a/applications/external/asteroids/application.fam +++ b/applications/external/asteroids/application.fam @@ -8,9 +8,10 @@ App( stack_size=8 * 1024, order=50, fap_icon="appicon.png", + fap_icon_assets="assets", fap_category="Games", - fap_icon_assets="assets", # Image assets to compile for this application - fap_description="An implementation of the classic arcade game Asteroids", - fap_author="antirez, SimplyMinimal", - fap_weburl="https://github.com/SimplyMinimal/FlipperZero-Asteroids", + fap_author="@antirez & @SimplyMinimal", + fap_weburl="https://github.com/antirez/flipper-asteroids", + fap_version="1.0", + fap_description="Asteroids game", ) diff --git a/applications/external/avr_isp_programmer/avr_isp_app_i.h b/applications/external/avr_isp_programmer/avr_isp_app_i.h index 17c69f8f2e..819e3f45f0 100644 --- a/applications/external/avr_isp_programmer/avr_isp_app_i.h +++ b/applications/external/avr_isp_programmer/avr_isp_app_i.h @@ -41,4 +41,4 @@ typedef struct { AvrIspError error; } AvrIspApp; -bool avr_isp_load_from_file(AvrIspApp* app); \ No newline at end of file +bool avr_isp_load_from_file(AvrIspApp* app); diff --git a/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.c b/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.c index 209551a47c..121f085650 100644 --- a/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.c +++ b/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.c @@ -60,7 +60,9 @@ static int32_t avr_isp_worker_rw_thread(void* context) { AvrIspWorkerRW* instance = context; /* start PWM on &gpio_ext_pa4 */ - furi_hal_pwm_start(FuriHalPwmOutputIdLptim2PA4, 4000000, 50); + if(!furi_hal_pwm_is_running(FuriHalPwmOutputIdLptim2PA4)) { + furi_hal_pwm_start(FuriHalPwmOutputIdLptim2PA4, 4000000, 50); + } FURI_LOG_D(TAG, "Start"); @@ -122,7 +124,9 @@ static int32_t avr_isp_worker_rw_thread(void* context) { } FURI_LOG_D(TAG, "Stop"); - furi_hal_pwm_stop(FuriHalPwmOutputIdLptim2PA4); + if(furi_hal_pwm_is_running(FuriHalPwmOutputIdLptim2PA4)) { + furi_hal_pwm_stop(FuriHalPwmOutputIdLptim2PA4); + } return 0; } @@ -136,7 +140,12 @@ bool avr_isp_worker_rw_detect_chip(AvrIspWorkerRW* instance) { instance->chip_arr_ind = avr_isp_chip_arr_size + 1; /* start PWM on &gpio_ext_pa4 */ - furi_hal_pwm_start(FuriHalPwmOutputIdLptim2PA4, 4000000, 50); + bool was_pwm_enabled = false; + if(!furi_hal_pwm_is_running(FuriHalPwmOutputIdLptim2PA4)) { + furi_hal_pwm_start(FuriHalPwmOutputIdLptim2PA4, 4000000, 50); + } else { + was_pwm_enabled = true; + } do { if(!avr_isp_auto_set_spi_speed_start_pmode(instance->avr_isp)) { @@ -200,7 +209,9 @@ bool avr_isp_worker_rw_detect_chip(AvrIspWorkerRW* instance) { } while(0); - furi_hal_pwm_stop(FuriHalPwmOutputIdLptim2PA4); + if(furi_hal_pwm_is_running(FuriHalPwmOutputIdLptim2PA4) && !was_pwm_enabled) { + furi_hal_pwm_stop(FuriHalPwmOutputIdLptim2PA4); + } if(instance->callback) { if(instance->chip_arr_ind > avr_isp_chip_arr_size) { diff --git a/applications/external/avr_isp_programmer/scenes/avr_isp_scene_input_name.c b/applications/external/avr_isp_programmer/scenes/avr_isp_scene_input_name.c index 3394f4362a..950d564ca7 100644 --- a/applications/external/avr_isp_programmer/scenes/avr_isp_scene_input_name.c +++ b/applications/external/avr_isp_programmer/scenes/avr_isp_scene_input_name.c @@ -1,8 +1,6 @@ #include "../avr_isp_app_i.h" #include -#define MAX_TEXT_INPUT_LEN 22 - void avr_isp_scene_input_name_text_callback(void* context) { furi_assert(context); @@ -46,7 +44,7 @@ void avr_isp_scene_input_name_on_enter(void* context) { avr_isp_scene_input_name_text_callback, app, app->file_name_tmp, - MAX_TEXT_INPUT_LEN, // buffer size + AVR_ISP_MAX_LEN_NAME, // buffer size dev_name_empty); ValidatorIsFile* validator_is_file = diff --git a/applications/external/barcode_gen/application.fam b/applications/external/barcode_gen/application.fam index 72c8aa114d..9c0299617f 100644 --- a/applications/external/barcode_gen/application.fam +++ b/applications/external/barcode_gen/application.fam @@ -8,4 +8,9 @@ App( fap_category="Misc", fap_icon="images/barcode_10.png", fap_icon_assets="images", + fap_icon_assets_symbol="barcode_app", + fap_author="@Kingal1337", + fap_weburl="https://github.com/Kingal1337/flipper-barcode-generator", + fap_version="1.0", + fap_description="App allows you to display various barcodes on flipper screen", ) diff --git a/applications/external/barcode_gen/barcode_app.c b/applications/external/barcode_gen/barcode_app.c index 581c92fdaf..99e5769d2a 100644 --- a/applications/external/barcode_gen/barcode_app.c +++ b/applications/external/barcode_gen/barcode_app.c @@ -13,7 +13,7 @@ static bool select_file(const char* folder, FuriString* file_path) { DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); DialogsFileBrowserOptions browser_options; - dialog_file_browser_set_basic_options(&browser_options, BARCODE_EXTENSION, &I_barcode_10); + dialog_file_browser_set_basic_options(&browser_options, "", &I_barcode_10); browser_options.base_path = DEFAULT_USER_BARCODES; furi_string_set(file_path, folder); @@ -65,7 +65,7 @@ bool get_file_name_from_path(FuriString* file_path, FuriString* file_name, bool if(file_path == NULL || file_name == NULL) { return false; } - uint slash_index = furi_string_search_rchar(file_path, '/', 0); + uint32_t slash_index = furi_string_search_rchar(file_path, '/', 0); if(slash_index == FURI_STRING_FAILURE || slash_index >= (furi_string_size(file_path) - 1)) { return false; } @@ -73,7 +73,7 @@ bool get_file_name_from_path(FuriString* file_path, FuriString* file_name, bool furi_string_set(file_name, file_path); furi_string_right(file_name, slash_index + 1); if(remove_extension) { - uint ext_index = furi_string_search_rchar(file_name, '.', 0); + uint32_t ext_index = furi_string_search_rchar(file_name, '.', 0); if(ext_index != FURI_STRING_FAILURE && ext_index < (furi_string_size(file_path))) { furi_string_left(file_name, ext_index); } @@ -239,6 +239,11 @@ void submenu_callback(void* context, uint32_t index) { } } +uint32_t create_view_callback(void* context) { + UNUSED(context); + return CreateBarcodeView; +} + uint32_t main_menu_callback(void* context) { UNUSED(context); return MainMenuView; @@ -305,6 +310,7 @@ int32_t barcode_main(void* p) { * Creating Text Input View ******************************/ app->text_input = text_input_alloc(); + view_set_previous_callback(text_input_get_view(app->text_input), create_view_callback); view_dispatcher_add_view( app->view_dispatcher, TextInputView, text_input_get_view(app->text_input)); diff --git a/applications/external/barcode_gen/barcode_app.h b/applications/external/barcode_gen/barcode_app.h index 31c805a69c..bbc787f1ee 100644 --- a/applications/external/barcode_gen/barcode_app.h +++ b/applications/external/barcode_gen/barcode_app.h @@ -15,7 +15,7 @@ #include "barcode_utils.h" #define TAG "BARCODE" -#define VERSION "1.0" +#define VERSION "1.1" #define FILE_VERSION "1" #define TEXT_BUFFER_SIZE 128 @@ -23,10 +23,11 @@ #define BARCODE_HEIGHT 50 #define BARCODE_Y_START 3 -#define APPS_DATA EXT_PATH("apps_data") - //the folder where the encodings are located -#define BARCODE_DATA_FILE_DIR_PATH APPS_DATA "/barcode_data" +#define BARCODE_DATA_FILE_DIR_PATH EXT_PATH("apps_data/barcode_data") + +//the folder where the codabar encoding table is located +#define CODABAR_DICT_FILE_PATH BARCODE_DATA_FILE_DIR_PATH "/codabar_encodings.txt" //the folder where the code 39 encoding table is located #define CODE39_DICT_FILE_PATH BARCODE_DATA_FILE_DIR_PATH "/code39_encodings.txt" @@ -35,11 +36,11 @@ #define CODE128_DICT_FILE_PATH BARCODE_DATA_FILE_DIR_PATH "/code128_encodings.txt" //the folder where the user stores their barcodes -#define DEFAULT_USER_BARCODES EXT_PATH("barcodes") +#define DEFAULT_USER_BARCODES EXT_PATH("apps_data/barcodes") //The extension barcode files use -#define BARCODE_EXTENSION ".barcode" -#define BARCODE_EXTENSION_LENGTH 8 +#define BARCODE_EXTENSION ".txt" +#define BARCODE_EXTENSION_LENGTH 4 #include "views/barcode_view.h" #include "views/create_view.h" diff --git a/applications/external/barcode_gen/barcode_utils.c b/applications/external/barcode_gen/barcode_utils.c index 502014d852..d7bb126892 100644 --- a/applications/external/barcode_gen/barcode_utils.c +++ b/applications/external/barcode_gen/barcode_utils.c @@ -43,6 +43,14 @@ void init_types() { code_128->start_pos = 0; barcode_type_objs[CODE128] = code_128; + BarcodeTypeObj* codabar = malloc(sizeof(BarcodeTypeObj)); + codabar->name = "Codabar"; + codabar->type = CODABAR; + codabar->min_digits = 1; + codabar->max_digits = -1; + codabar->start_pos = 0; + barcode_type_objs[CODABAR] = codabar; + BarcodeTypeObj* unknown = malloc(sizeof(BarcodeTypeObj)); unknown->name = "Unknown"; unknown->type = UNKNOWN; @@ -74,6 +82,9 @@ BarcodeTypeObj* get_type(FuriString* type_string) { if(furi_string_cmp_str(type_string, "CODE-128") == 0) { return barcode_type_objs[CODE128]; } + if(furi_string_cmp_str(type_string, "Codabar") == 0) { + return barcode_type_objs[CODABAR]; + } return barcode_type_objs[UNKNOWN]; } @@ -98,7 +109,7 @@ const char* get_error_code_name(ErrorCode error_code) { return "OK"; default: return "Unknown Code"; - } + }; } const char* get_error_code_message(ErrorCode error_code) { @@ -121,5 +132,5 @@ const char* get_error_code_message(ErrorCode error_code) { return "OK"; default: return "Could not read barcode data"; - } + }; } \ No newline at end of file diff --git a/applications/external/barcode_gen/barcode_utils.h b/applications/external/barcode_gen/barcode_utils.h index 212923a899..7bc565cee4 100644 --- a/applications/external/barcode_gen/barcode_utils.h +++ b/applications/external/barcode_gen/barcode_utils.h @@ -3,7 +3,7 @@ #include #include -#define NUMBER_OF_BARCODE_TYPES 6 +#define NUMBER_OF_BARCODE_TYPES 7 typedef enum { WrongNumberOfDigits, //There is too many or too few digits in the barcode @@ -22,6 +22,7 @@ typedef enum { EAN13, CODE39, CODE128, + CODABAR, UNKNOWN } BarcodeType; diff --git a/applications/external/barcode_gen/barcode_validator.c b/applications/external/barcode_gen/barcode_validator.c index 6cb3eec4c2..51e71ae2ec 100644 --- a/applications/external/barcode_gen/barcode_validator.c +++ b/applications/external/barcode_gen/barcode_validator.c @@ -13,6 +13,9 @@ void barcode_loader(BarcodeData* barcode_data) { case CODE128: code_128_loader(barcode_data); break; + case CODABAR: + codabar_loader(barcode_data); + break; case UNKNOWN: barcode_data->reason = UnsupportedType; barcode_data->valid = false; @@ -36,6 +39,7 @@ int calculate_check_digit(BarcodeData* barcode_data) { break; case CODE39: case CODE128: + case CODABAR: case UNKNOWN: default: break; @@ -130,7 +134,6 @@ void code_39_loader(BarcodeData* barcode_data) { int barcode_length = furi_string_size(barcode_data->raw_data); int min_digits = barcode_data->type_obj->min_digits; - // int max_digit = barcode_data->type_obj->max_digits; //check the length of the barcode, must contain atleast a character, //this can have as many characters as it wants, it might not fit on the screen @@ -215,7 +218,6 @@ void code_128_loader(BarcodeData* barcode_data) { const char* stop_code_bits = "1100011101011"; int min_digits = barcode_data->type_obj->min_digits; - // int max_digit = barcode_data->type_obj->max_digits; /** * A sum of all of the characters values @@ -342,3 +344,60 @@ void code_128_loader(BarcodeData* barcode_data) { furi_string_cat(barcode_data->correct_data, barcode_bits); furi_string_free(barcode_bits); } + +void codabar_loader(BarcodeData* barcode_data) { + int barcode_length = furi_string_size(barcode_data->raw_data); + + int min_digits = barcode_data->type_obj->min_digits; + + //check the length of the barcode, must contain atleast a character, + //this can have as many characters as it wants, it might not fit on the screen + if(barcode_length < min_digits) { + barcode_data->reason = WrongNumberOfDigits; + barcode_data->valid = false; + return; + } + + FuriString* barcode_bits = furi_string_alloc(); + + barcode_length = furi_string_size(barcode_data->raw_data); + + //Open Storage + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* ff = flipper_format_file_alloc(storage); + + if(!flipper_format_file_open_existing(ff, CODABAR_DICT_FILE_PATH)) { + FURI_LOG_E(TAG, "Could not open file %s", CODABAR_DICT_FILE_PATH); + barcode_data->reason = MissingEncodingTable; + barcode_data->valid = false; + } else { + FuriString* char_bits = furi_string_alloc(); + for(int i = 0; i < barcode_length; i++) { + char barcode_char = toupper(furi_string_get_char(barcode_data->raw_data, i)); + + //convert a char into a string so it used in flipper_format_read_string + char current_character[2]; + snprintf(current_character, 2, "%c", barcode_char); + + if(!flipper_format_read_string(ff, current_character, char_bits)) { + FURI_LOG_E(TAG, "Could not read \"%c\" string", barcode_char); + barcode_data->reason = InvalidCharacters; + barcode_data->valid = false; + break; + } else { + FURI_LOG_I( + TAG, "\"%c\" string: %s", barcode_char, furi_string_get_cstr(char_bits)); + furi_string_cat(barcode_bits, char_bits); + } + flipper_format_rewind(ff); + } + furi_string_free(char_bits); + } + + //Close Storage + flipper_format_free(ff); + furi_record_close(RECORD_STORAGE); + + furi_string_cat(barcode_data->correct_data, barcode_bits); + furi_string_free(barcode_bits); +} \ No newline at end of file diff --git a/applications/external/barcode_gen/barcode_validator.h b/applications/external/barcode_gen/barcode_validator.h index 962d147290..739c80ddf2 100644 --- a/applications/external/barcode_gen/barcode_validator.h +++ b/applications/external/barcode_gen/barcode_validator.h @@ -10,4 +10,5 @@ void ean_8_loader(BarcodeData* barcode_data); void ean_13_loader(BarcodeData* barcode_data); void code_39_loader(BarcodeData* barcode_data); void code_128_loader(BarcodeData* barcode_data); +void codabar_loader(BarcodeData* barcode_data); void barcode_loader(BarcodeData* barcode_data); \ No newline at end of file diff --git a/applications/external/barcode_gen/views/barcode_view.c b/applications/external/barcode_gen/views/barcode_view.c index afd727b635..994053801d 100644 --- a/applications/external/barcode_gen/views/barcode_view.c +++ b/applications/external/barcode_gen/views/barcode_view.c @@ -322,6 +322,68 @@ static void draw_code_128(Canvas* canvas, BarcodeData* barcode_data) { canvas, 62, y + height + 8, AlignCenter, AlignBottom, furi_string_get_cstr(raw_data)); } +static void draw_codabar(Canvas* canvas, BarcodeData* barcode_data) { + FuriString* raw_data = barcode_data->raw_data; + FuriString* barcode_digits = barcode_data->correct_data; + //BarcodeTypeObj* type_obj = barcode_data->type_obj; + + int barcode_length = furi_string_size(barcode_digits); + int total_pixels = 0; + + for(int i = 0; i < barcode_length; i++) { + //1 for wide, 0 for narrow + char wide_or_narrow = furi_string_get_char(barcode_digits, i); + int wn_digit = wide_or_narrow - '0'; //wide(1) or narrow(0) digit + + if(wn_digit == 1) { + total_pixels += 3; + } else { + total_pixels += 1; + } + if((i + 1) % 7 == 0) { + total_pixels += 1; + } + } + + int x = (128 - total_pixels) / 2; + int y = BARCODE_Y_START; + int width = 1; + int height = BARCODE_HEIGHT; + bool filled_in = true; + + //set the canvas color to black to print the digit + canvas_set_color(canvas, ColorBlack); + // canvas_draw_str_aligned(canvas, 62, 30, AlignCenter, AlignCenter, error); + canvas_draw_str_aligned( + canvas, 62, y + height + 8, AlignCenter, AlignBottom, furi_string_get_cstr(raw_data)); + + for(int i = 0; i < barcode_length; i++) { + //1 for wide, 0 for narrow + char wide_or_narrow = furi_string_get_char(barcode_digits, i); + int wn_digit = wide_or_narrow - '0'; //wide(1) or narrow(0) digit + + if(filled_in) { + if(wn_digit == 1) { + x = draw_bits(canvas, "111", x, y, width, height); + } else { + x = draw_bits(canvas, "1", x, y, width, height); + } + filled_in = false; + } else { + if(wn_digit == 1) { + x = draw_bits(canvas, "000", x, y, width, height); + } else { + x = draw_bits(canvas, "0", x, y, width, height); + } + filled_in = true; + } + if((i + 1) % 7 == 0) { + x = draw_bits(canvas, "0", x, y, width, height); + filled_in = true; + } + } +} + static void barcode_draw_callback(Canvas* canvas, void* ctx) { furi_assert(ctx); BarcodeModel* barcode_model = ctx; @@ -346,6 +408,9 @@ static void barcode_draw_callback(Canvas* canvas, void* ctx) { case CODE128: draw_code_128(canvas, data); break; + case CODABAR: + draw_codabar(canvas, data); + break; case UNKNOWN: default: break; diff --git a/applications/external/barcode_gen/views/create_view.c b/applications/external/barcode_gen/views/create_view.c index 23a5fa4097..e4e4891138 100644 --- a/applications/external/barcode_gen/views/create_view.c +++ b/applications/external/barcode_gen/views/create_view.c @@ -448,7 +448,8 @@ void save_barcode(CreateView* create_view_object) { flipper_format_write_string_cstr(ff, "Version", FILE_VERSION); - flipper_format_write_comment_cstr(ff, "Types - UPC-A, EAN-8, EAN-13, CODE-39, CODE-128"); + flipper_format_write_comment_cstr( + ff, "Types - UPC-A, EAN-8, EAN-13, CODE-39, CODE-128, Codabar"); flipper_format_write_string_cstr(ff, "Type", barcode_type->name); diff --git a/applications/external/barcode_gen/views/message_view.c b/applications/external/barcode_gen/views/message_view.c index 13e2383812..3a9aa90b32 100644 --- a/applications/external/barcode_gen/views/message_view.c +++ b/applications/external/barcode_gen/views/message_view.c @@ -53,15 +53,9 @@ MessageView* message_view_allocate(BarcodeApp* barcode_app) { return message_view_object; } -void message_view_free_model(MessageView* message_view_object) { - with_view_model( - message_view_object->view, MessageViewModel * model, { UNUSED(model); }, true); -} - void message_view_free(MessageView* message_view_object) { furi_assert(message_view_object); - message_view_free_model(message_view_object); view_free(message_view_object->view); free(message_view_object); } @@ -69,4 +63,4 @@ void message_view_free(MessageView* message_view_object) { View* message_get_view(MessageView* message_view_object) { furi_assert(message_view_object); return message_view_object->view; -} +} \ No newline at end of file diff --git a/applications/external/blackjack/application.fam b/applications/external/blackjack/application.fam index d07d7510bd..83b3c83a88 100644 --- a/applications/external/blackjack/application.fam +++ b/applications/external/blackjack/application.fam @@ -9,4 +9,7 @@ App( fap_icon="blackjack_10px.png", fap_category="Games", fap_icon_assets="assets", + fap_author="@teeebor", + fap_version="1.0", + fap_description="Blackjack Game", ) diff --git a/applications/external/blackjack/blackjack.c b/applications/external/blackjack/blackjack.c index ee5e354151..91a493228e 100644 --- a/applications/external/blackjack/blackjack.c +++ b/applications/external/blackjack/blackjack.c @@ -275,6 +275,7 @@ void dealer_tick(GameState* game_state) { if(dealer_score >= DEALER_MAX) { if(dealer_score > 21 || dealer_score < player_score) { + // dolphin_deed(DolphinDeedPluginGameWin); enqueue( &(game_state->queue_state), game_state, @@ -568,6 +569,9 @@ int32_t blackjack_app(void* p) { 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, 100); furi_mutex_acquire(game_state->mutex, FuriWaitForever); diff --git a/applications/external/blackjack/util.c b/applications/external/blackjack/util.c index 8e88c2231c..c1cb01c132 100644 --- a/applications/external/blackjack/util.c +++ b/applications/external/blackjack/util.c @@ -1,7 +1,7 @@ #include #include "util.h" -const char* CONFIG_FILE_PATH = EXT_PATH(".blackjack.settings"); +const char* CONFIG_FILE_PATH = APP_DATA_PATH("blackjack.settings"); void save_settings(Settings settings) { Storage* storage = furi_record_open(RECORD_STORAGE); @@ -59,6 +59,7 @@ Settings load_settings() { FURI_LOG_D(APP_NAME, "Opening storage"); Storage* storage = furi_record_open(RECORD_STORAGE); + storage_common_migrate(storage, EXT_PATH(".blackjack.settings"), CONFIG_FILE_PATH); FURI_LOG_D(APP_NAME, "Allocating file"); FlipperFormat* file = flipper_format_file_alloc(storage); @@ -120,4 +121,4 @@ Settings load_settings() { flipper_format_free(file); furi_record_close(RECORD_STORAGE); return settings; -} \ No newline at end of file +} diff --git a/applications/external/bomberduck/application.fam b/applications/external/bomberduck/application.fam index d2ee5564bb..39c680693a 100644 --- a/applications/external/bomberduck/application.fam +++ b/applications/external/bomberduck/application.fam @@ -11,4 +11,7 @@ App( fap_icon="bomb.png", fap_category="Games", fap_icon_assets="assets", + fap_author="@leo-need-more-coffee & @xMasterX", + fap_version="1.0", + fap_description="Bomberduck(Bomberman) Game", ) diff --git a/applications/external/bomberduck/bomberduck.c b/applications/external/bomberduck/bomberduck.c index 8c38b1a9c1..29bd15abb9 100644 --- a/applications/external/bomberduck/bomberduck.c +++ b/applications/external/bomberduck/bomberduck.c @@ -382,6 +382,7 @@ int32_t bomberduck_app(void* p) { return 255; } + // dolphin_deed(DolphinDeedPluginGameStart); // Создаем новый view port ViewPort* view_port = view_port_alloc(); // Создаем callback отрисовки, без контекста @@ -455,6 +456,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); + // } } 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 93c4179c59..5e824813e5 100644 --- a/applications/external/bpmtapper/application.fam +++ b/applications/external/bpmtapper/application.fam @@ -1,5 +1,5 @@ App( - appid="BPM_Tapper", + appid="bpm_tapper", name="BPM Tapper", apptype=FlipperAppType.EXTERNAL, entry_point="bpm_tapper_app", @@ -7,7 +7,10 @@ App( requires=["gui"], stack_size=2 * 1024, fap_icon="bpm_10px.png", - fap_category="Music", - fap_icon_assets="icons", + fap_category="Media", order=15, + fap_author="@panki27", + fap_weburl="https://github.com/panki27/bpm-tapper", + fap_version="1.0", + fap_description="Tap center button to measure BPM", ) diff --git a/applications/external/bpmtapper/bpm.c b/applications/external/bpmtapper/bpm.c index 5720ac4d2f..8353019df8 100644 --- a/applications/external/bpmtapper/bpm.c +++ b/applications/external/bpmtapper/bpm.c @@ -4,7 +4,7 @@ #include #include #include -#include "BPM_Tapper_icons.h" +#include "assets_icons.h" typedef enum { EventTypeTick, diff --git a/applications/external/brainfuck/application.fam b/applications/external/brainfuck/application.fam index 716c44a6a9..9ee518fbee 100644 --- a/applications/external/brainfuck/application.fam +++ b/applications/external/brainfuck/application.fam @@ -1,5 +1,5 @@ App( - appid="Brainfuck", + appid="brainfuck", name="Brainfuck", apptype=FlipperAppType.EXTERNAL, entry_point="brainfuck_app", @@ -11,4 +11,8 @@ App( fap_icon="bfico.png", fap_category="Misc", fap_icon_assets="icons", + fap_author="@nymda", + fap_weburl="https://github.com/nymda/FlipperZeroBrainfuck", + fap_version="1.0", + fap_description="Brainfuck language interpreter", ) diff --git a/applications/external/brainfuck/brainfuck.c b/applications/external/brainfuck/brainfuck.c index 4577de68b5..84a810eaf9 100644 --- a/applications/external/brainfuck/brainfuck.c +++ b/applications/external/brainfuck/brainfuck.c @@ -137,7 +137,7 @@ int32_t brainfuck_app(void* p) { } Storage* storage = furi_record_open(RECORD_STORAGE); - storage_simply_mkdir(storage, "/ext/brainfuck"); + storage_common_migrate(storage, EXT_PATH("brainfuck"), STORAGE_APP_DATA_PATH_PREFIX); scene_manager_next_scene(brainfuck->scene_manager, brainfuckSceneStart); @@ -146,4 +146,4 @@ int32_t brainfuck_app(void* p) { brainfuck_free(brainfuck); return 0; -} \ No newline at end of file +} diff --git a/applications/external/brainfuck/brainfuck_i.h b/applications/external/brainfuck/brainfuck_i.h index 3940cecade..293654c3e5 100644 --- a/applications/external/brainfuck/brainfuck_i.h +++ b/applications/external/brainfuck/brainfuck_i.h @@ -29,7 +29,8 @@ typedef unsigned char byte; #include #include -#include +#include +#include #include #include diff --git a/applications/external/brainfuck/icons/ButtonRightSmall_3x5.png b/applications/external/brainfuck/icons/ButtonRightSmall_3x5.png deleted file mode 100644 index b9d5f87db1..0000000000 Binary files a/applications/external/brainfuck/icons/ButtonRightSmall_3x5.png and /dev/null differ diff --git a/applications/external/brainfuck/icons/KeySaveSelected_24x11.png b/applications/external/brainfuck/icons/KeySaveSelected_24x11.png deleted file mode 100644 index eeb3569d3a..0000000000 Binary files a/applications/external/brainfuck/icons/KeySaveSelected_24x11.png and /dev/null differ diff --git a/applications/external/brainfuck/icons/KeySave_24x11.png b/applications/external/brainfuck/icons/KeySave_24x11.png deleted file mode 100644 index e7dba987a0..0000000000 Binary files a/applications/external/brainfuck/icons/KeySave_24x11.png and /dev/null differ diff --git a/applications/external/brainfuck/scenes/brainfuck_scene_file_create.c b/applications/external/brainfuck/scenes/brainfuck_scene_file_create.c index 9f88859779..96df831c9f 100644 --- a/applications/external/brainfuck/scenes/brainfuck_scene_file_create.c +++ b/applications/external/brainfuck/scenes/brainfuck_scene_file_create.c @@ -25,7 +25,7 @@ bool brainfuck_scene_file_create_on_event(void* context, SceneManagerEvent event bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { if(event.event == brainfuckCustomEventTextInputDone) { - furi_string_cat_printf(app->BF_file_path, "/ext/brainfuck/%s.b", tmpName); + furi_string_cat_printf(app->BF_file_path, APP_DATA_PATH("%s.b"), tmpName); //remove old file Storage* storage = furi_record_open(RECORD_STORAGE); diff --git a/applications/external/brainfuck/scenes/brainfuck_scene_file_select.c b/applications/external/brainfuck/scenes/brainfuck_scene_file_select.c index 33c06ee810..10692b32f3 100644 --- a/applications/external/brainfuck/scenes/brainfuck_scene_file_select.c +++ b/applications/external/brainfuck/scenes/brainfuck_scene_file_select.c @@ -6,11 +6,11 @@ void brainfuck_scene_file_select_on_enter(void* context) { DialogsApp* dialogs = furi_record_open("dialogs"); FuriString* path; path = furi_string_alloc(); - furi_string_set(path, "/ext/brainfuck"); + furi_string_set(path, STORAGE_APP_DATA_PATH_PREFIX); DialogsFileBrowserOptions browser_options; dialog_file_browser_set_basic_options(&browser_options, ".b", &I_bfico); - browser_options.base_path = "/ext/brainfuck"; + browser_options.base_path = STORAGE_APP_DATA_PATH_PREFIX; browser_options.hide_ext = false; bool selected = dialog_file_browser_show(dialogs, path, path, &browser_options); diff --git a/applications/external/caesarcipher/application.fam b/applications/external/caesarcipher/application.fam index 4f438d2b34..7f973d54af 100644 --- a/applications/external/caesarcipher/application.fam +++ b/applications/external/caesarcipher/application.fam @@ -1,5 +1,5 @@ App( - appid="Caesar_Cipher", + appid="caesar_cipher", name="Caesar Cipher", apptype=FlipperAppType.EXTERNAL, entry_point="caesar_cipher_app", @@ -11,4 +11,8 @@ App( fap_icon="caesar_cipher_icon.png", fap_category="Misc", order=20, + fap_author="@panki27", + fap_weburl="https://github.com/panki27/caesar-cipher", + fap_version="1.0", + fap_description="Encrypt and decrypt text using Caesar Cipher", ) diff --git a/applications/external/calculator/application.fam b/applications/external/calculator/application.fam index ac78af7a5d..1913e55036 100644 --- a/applications/external/calculator/application.fam +++ b/applications/external/calculator/application.fam @@ -1,5 +1,5 @@ App( - appid="Calculator", + appid="calculator", name="Calculator", apptype=FlipperAppType.EXTERNAL, entry_point="calculator_app", @@ -9,4 +9,8 @@ App( order=45, fap_icon="calcIcon.png", fap_category="Misc", + fap_author="@n-o-T-I-n-s-a-n-e", + fap_weburl="https://github.com/n-o-T-I-n-s-a-n-e", + fap_version="1.0", + fap_description="Calculator, that can calculate simple expressions", ) diff --git a/applications/external/cntdown_timer/application.fam b/applications/external/cntdown_timer/application.fam index ba22fd4bdb..10d610d0f8 100644 --- a/applications/external/cntdown_timer/application.fam +++ b/applications/external/cntdown_timer/application.fam @@ -13,4 +13,8 @@ App( order=20, fap_icon="cntdown_timer.png", fap_category="Misc", + fap_author="@0w0mewo", + fap_weburl="https://github.com/0w0mewo/fpz_cntdown_timer", + fap_version="1.0", + fap_description="Simple count down timer", ) diff --git a/applications/external/counter/application.fam b/applications/external/counter/application.fam index b287965bfa..3678c644da 100644 --- a/applications/external/counter/application.fam +++ b/applications/external/counter/application.fam @@ -7,6 +7,9 @@ App( "gui", ], fap_category="Misc", - fap_icon="icons/counter_icon.png", - fap_icon_assets="icons", + fap_icon="counter_icon.png", + fap_author="@Krulknul", + fap_weburl="https://github.com/Krulknul/dolphin-counter", + fap_version="1.0", + fap_description="Simple counter", ) diff --git a/applications/external/counter/counter.c b/applications/external/counter/counter.c index 22fa8cd80a..67b45170b8 100644 --- a/applications/external/counter/counter.c +++ b/applications/external/counter/counter.c @@ -2,7 +2,6 @@ #include #include #include -#include #define MAX_COUNT 99 #define BOXTIME 2 diff --git a/applications/external/counter/icons/counter_icon.png b/applications/external/counter/counter_icon.png similarity index 100% rename from applications/external/counter/icons/counter_icon.png rename to applications/external/counter/counter_icon.png diff --git a/applications/external/dap_link/application.fam b/applications/external/dap_link/application.fam index 321b9e1024..e05a7e01f2 100644 --- a/applications/external/dap_link/application.fam +++ b/applications/external/dap_link/application.fam @@ -1,6 +1,6 @@ App( appid="dap_link", - name="[GPIO] DAP Link", + name="[SWD-JTAG] DAP Link", apptype=FlipperAppType.EXTERNAL, entry_point="dap_link_app", requires=[ diff --git a/applications/external/dap_link/dap_link.c b/applications/external/dap_link/dap_link.c index eafb435e7b..52d2221386 100644 --- a/applications/external/dap_link/dap_link.c +++ b/applications/external/dap_link/dap_link.c @@ -15,6 +15,7 @@ #include "usb/dap_v2_usb.h" #include #include "dap_link_icons.h" +#include "assets_icons.h" /***************************************************************************/ /****************************** DAP COMMON *********************************/ @@ -524,4 +525,4 @@ int32_t dap_link_app(void* p) { dap_app_free(app); return 0; -} \ No newline at end of file +} diff --git a/applications/external/dap_link/icons/ActiveConnection_50x64.png b/applications/external/dap_link/icons/ActiveConnection_50x64.png deleted file mode 100644 index 1d7686dddf..0000000000 Binary files a/applications/external/dap_link/icons/ActiveConnection_50x64.png and /dev/null differ diff --git a/applications/external/doom/doom.c b/applications/external/doom/doom.c index b6d08536db..5821e8622e 100644 --- a/applications/external/doom/doom.c +++ b/applications/external/doom/doom.c @@ -995,6 +995,9 @@ int32_t doom_app() { music_player_worker_load_rtttl_from_string(plugin_state->music_instance->worker, dsintro); music_player_worker_start(plugin_state->music_instance->worker); #endif + // Call dolphin deed on game start + // dolphin_deed(DolphinDeedPluginGameStart); + for(bool processing = true; processing;) { FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); furi_mutex_acquire(plugin_state->mutex, FuriWaitForever); diff --git a/applications/external/dtmf_dolphin/application.fam b/applications/external/dtmf_dolphin/application.fam index 460f6ded68..5f01bc9a07 100644 --- a/applications/external/dtmf_dolphin/application.fam +++ b/applications/external/dtmf_dolphin/application.fam @@ -12,4 +12,7 @@ App( stack_size=8 * 1024, order=20, fap_category="Tools", + fap_author="@litui & @xMasterX", + fap_version="1.0", + fap_description="DTMF (Dual-Tone Multi-Frequency) dialer, Bluebox, and Redbox.", ) diff --git a/applications/external/dtmf_dolphin/dtmf_dolphin.c b/applications/external/dtmf_dolphin/dtmf_dolphin.c index 732b22ef84..4447fb2780 100644 --- a/applications/external/dtmf_dolphin/dtmf_dolphin.c +++ b/applications/external/dtmf_dolphin/dtmf_dolphin.c @@ -83,7 +83,7 @@ int32_t dtmf_dolphin_app(void* p) { UNUSED(p); DTMFDolphinApp* app = app_alloc(); - DOLPHIN_DEED(DolphinDeedPluginStart); + dolphin_deed(DolphinDeedPluginStart); view_dispatcher_run(app->view_dispatcher); app_free(app); diff --git a/applications/external/esp32cam_camera/application.fam b/applications/external/esp32cam_camera/application.fam index 459413817c..a0e0ede4c4 100644 --- a/applications/external/esp32cam_camera/application.fam +++ b/applications/external/esp32cam_camera/application.fam @@ -1,5 +1,5 @@ App( - appid="MAYHEM_Camera", + appid="mayhem_camera", name="[MAYHEM] Camera", apptype=FlipperAppType.EXTERNAL, entry_point="camera_app", diff --git a/applications/external/esp32cam_marauder_companion/application.fam b/applications/external/esp32cam_marauder_companion/application.fam index 430d641052..800cb92be3 100644 --- a/applications/external/esp32cam_marauder_companion/application.fam +++ b/applications/external/esp32cam_marauder_companion/application.fam @@ -1,5 +1,5 @@ App( - appid="MAYHEM_Marauder", + appid="mayhem_marauder", name="[MAYHEM] Marauder", apptype=FlipperAppType.EXTERNAL, entry_point="wifi_marauder_app", diff --git a/applications/external/esp32cam_marauder_companion/wifi_marauder_app.c b/applications/external/esp32cam_marauder_companion/wifi_marauder_app.c index cb41468fd2..0f3d4e00a6 100644 --- a/applications/external/esp32cam_marauder_companion/wifi_marauder_app.c +++ b/applications/external/esp32cam_marauder_companion/wifi_marauder_app.c @@ -158,11 +158,12 @@ void wifi_marauder_app_free(WifiMarauderApp* app) { int32_t wifi_marauder_app(void* p) { UNUSED(p); - furi_hal_power_disable_external_3_3v(); - furi_hal_power_disable_otg(); + uint8_t attempts = 0; + while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) { + furi_hal_power_enable_otg(); + furi_delay_ms(10); + } furi_delay_ms(200); - furi_hal_power_enable_external_3_3v(); - furi_hal_power_enable_otg(); for(int i = 0; i < 2; i++) { furi_delay_ms(500); furi_hal_uart_tx(FuriHalUartIdUSART1, (uint8_t[1]){'w'}, 1); @@ -181,7 +182,9 @@ int32_t wifi_marauder_app(void* p) { wifi_marauder_app_free(wifi_marauder_app); - furi_hal_power_disable_otg(); + if(furi_hal_power_is_otg_enabled()) { + furi_hal_power_disable_otg(); + } return 0; } diff --git a/applications/external/esp32cam_marauder_companion/wifi_marauder_app_i.h b/applications/external/esp32cam_marauder_companion/wifi_marauder_app_i.h index 62b858ceaa..bd1fb3071a 100644 --- a/applications/external/esp32cam_marauder_companion/wifi_marauder_app_i.h +++ b/applications/external/esp32cam_marauder_companion/wifi_marauder_app_i.h @@ -25,7 +25,7 @@ #define WIFI_MARAUDER_TEXT_INPUT_STORE_SIZE (512) #define MARAUDER_APP_FOLDER_USER "apps_data/marauder" -#define MARAUDER_APP_FOLDER ANY_PATH(MARAUDER_APP_FOLDER_USER) +#define MARAUDER_APP_FOLDER EXT_PATH(MARAUDER_APP_FOLDER_USER) #define MARAUDER_APP_FOLDER_PCAPS MARAUDER_APP_FOLDER "/pcaps" #define MARAUDER_APP_FOLDER_LOGS MARAUDER_APP_FOLDER "/logs" #define MARAUDER_APP_FOLDER_USER_PCAPS MARAUDER_APP_FOLDER_USER "/pcaps" diff --git a/applications/external/esp32cam_marauder_companion/wifi_marauder_uart.c b/applications/external/esp32cam_marauder_companion/wifi_marauder_uart.c index 37304f06a5..77ad239b9e 100644 --- a/applications/external/esp32cam_marauder_companion/wifi_marauder_uart.c +++ b/applications/external/esp32cam_marauder_companion/wifi_marauder_uart.c @@ -106,6 +106,9 @@ void wifi_marauder_uart_free(WifiMarauderUart* uart) { furi_thread_free(uart->rx_thread); furi_hal_uart_set_irq_cb(uart->channel, NULL, NULL); + if(uart->channel == FuriHalUartIdLPUART1) { + furi_hal_uart_deinit(uart->channel); + } furi_hal_console_enable(); free(uart); diff --git a/applications/external/esp32cam_morseflasher/application.fam b/applications/external/esp32cam_morseflasher/application.fam index c15207015c..af60d8de79 100644 --- a/applications/external/esp32cam_morseflasher/application.fam +++ b/applications/external/esp32cam_morseflasher/application.fam @@ -1,5 +1,5 @@ App( - appid="MAYHEM_MorseFlash", + appid="mayhem_morseflash", name="[MAYHEM] Morse Flasher", apptype=FlipperAppType.EXTERNAL, entry_point="uart_terminal_app", @@ -7,7 +7,6 @@ App( requires=["gui"], stack_size=1 * 1024, order=90, - fap_icon_assets="assets", fap_icon="icon.png", fap_category="GPIO", fap_description="ESP32-CAM app to stream a message in morse using the powerful flashlight. [Unplug the USB cable to test with Mayhem]", diff --git a/applications/external/esp32cam_morseflasher/assets/KeyBackspaceSelected_16x9.png b/applications/external/esp32cam_morseflasher/assets/KeyBackspaceSelected_16x9.png deleted file mode 100644 index 7cc0759a8c..0000000000 Binary files a/applications/external/esp32cam_morseflasher/assets/KeyBackspaceSelected_16x9.png and /dev/null differ diff --git a/applications/external/esp32cam_morseflasher/assets/KeyBackspace_16x9.png b/applications/external/esp32cam_morseflasher/assets/KeyBackspace_16x9.png deleted file mode 100644 index 9946232d95..0000000000 Binary files a/applications/external/esp32cam_morseflasher/assets/KeyBackspace_16x9.png and /dev/null differ diff --git a/applications/external/esp32cam_morseflasher/assets/KeySaveSelected_24x11.png b/applications/external/esp32cam_morseflasher/assets/KeySaveSelected_24x11.png deleted file mode 100644 index eeb3569d3a..0000000000 Binary files a/applications/external/esp32cam_morseflasher/assets/KeySaveSelected_24x11.png and /dev/null differ diff --git a/applications/external/esp32cam_morseflasher/assets/KeySave_24x11.png b/applications/external/esp32cam_morseflasher/assets/KeySave_24x11.png deleted file mode 100644 index e7dba987a0..0000000000 Binary files a/applications/external/esp32cam_morseflasher/assets/KeySave_24x11.png and /dev/null differ diff --git a/applications/external/esp32cam_morseflasher/assets/WarningDolphin_45x42.png b/applications/external/esp32cam_morseflasher/assets/WarningDolphin_45x42.png deleted file mode 100644 index d766ffbb44..0000000000 Binary files a/applications/external/esp32cam_morseflasher/assets/WarningDolphin_45x42.png and /dev/null differ diff --git a/applications/external/esp32cam_morseflasher/uart_text_input.c b/applications/external/esp32cam_morseflasher/uart_text_input.c index 4e23362543..9a10994681 100644 --- a/applications/external/esp32cam_morseflasher/uart_text_input.c +++ b/applications/external/esp32cam_morseflasher/uart_text_input.c @@ -1,6 +1,6 @@ #include "uart_text_input.h" #include -#include "MAYHEM_MorseFlash_icons.h" +#include "assets_icons.h" #include struct UART_TextInput { diff --git a/applications/external/esp32cam_motion_detection/application.fam b/applications/external/esp32cam_motion_detection/application.fam index 9a9b4fcc97..57a5396c5b 100644 --- a/applications/external/esp32cam_motion_detection/application.fam +++ b/applications/external/esp32cam_motion_detection/application.fam @@ -1,5 +1,5 @@ App( - appid="MAYHEM_Motion", + appid="mayhem_motion", name="[MAYHEM] Motion detection", apptype=FlipperAppType.EXTERNAL, entry_point="uart_echo_app", diff --git a/applications/external/esp32cam_nannycam/application.fam b/applications/external/esp32cam_nannycam/application.fam index 1c4831d7c8..75f2be7838 100644 --- a/applications/external/esp32cam_nannycam/application.fam +++ b/applications/external/esp32cam_nannycam/application.fam @@ -1,5 +1,5 @@ App( - appid="MAYHEM_NannyCam", + appid="mayhem_nannycam", name="[MAYHEM] Nanny Cam", apptype=FlipperAppType.EXTERNAL, entry_point="uart_echo_app", diff --git a/applications/external/esp32cam_qrcode/application.fam b/applications/external/esp32cam_qrcode/application.fam index bd74f26b2f..73c418510c 100644 --- a/applications/external/esp32cam_qrcode/application.fam +++ b/applications/external/esp32cam_qrcode/application.fam @@ -1,5 +1,5 @@ App( - appid="MAYHEM_QRcode", + appid="mayhem_qrcode", name="[MAYHEM] QR Code", apptype=FlipperAppType.EXTERNAL, entry_point="uart_echo_app", diff --git a/applications/external/esp8266_deauth/application.fam b/applications/external/esp8266_deauth/application.fam index cd322dbe86..23ae94dce5 100644 --- a/applications/external/esp8266_deauth/application.fam +++ b/applications/external/esp8266_deauth/application.fam @@ -8,4 +8,7 @@ App( order=20, fap_icon="wifi_10px.png", fap_category="WiFi", + fap_author="@SequoiaSan & @xMasterX", + fap_version="1.0", + fap_description="DSTIKE Deauther module interface, based on ESP8266", ) diff --git a/applications/external/esp8266_deauth/esp8266_deauth.c b/applications/external/esp8266_deauth/esp8266_deauth.c index 35b9a850b0..76551f60c3 100644 --- a/applications/external/esp8266_deauth/esp8266_deauth.c +++ b/applications/external/esp8266_deauth/esp8266_deauth.c @@ -321,7 +321,7 @@ int32_t esp8266_deauth_app(void* p) { SWiFiDeauthApp* app = malloc(sizeof(SWiFiDeauthApp)); - DOLPHIN_DEED(DolphinDeedPluginStart); + dolphin_deed(DolphinDeedPluginStart); esp8266_deauth_app_init(app); furi_hal_gpio_init_simple(app->m_GpioButtons.pinButtonUp, GpioModeOutputPushPull); diff --git a/applications/external/etch_a_sketch/LICENSE b/applications/external/etch_a_sketch/LICENSE new file mode 100644 index 0000000000..f288702d2f --- /dev/null +++ b/applications/external/etch_a_sketch/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/applications/external/etch_a_sketch/application.fam b/applications/external/etch_a_sketch/application.fam new file mode 100644 index 0000000000..22144a6854 --- /dev/null +++ b/applications/external/etch_a_sketch/application.fam @@ -0,0 +1,16 @@ +App( + appid="etch", + name="Etch a Sketch", + apptype=FlipperAppType.EXTERNAL, + entry_point="etch_a_sketch_app", + cdefines=["APP_ETCH_A_SKETCH"], + requires=["gui"], + stack_size=2 * 1024, + order=175, + fap_icon="etch-a-sketch-icon.png", + fap_category="Media", + fap_author="@SimplyMinimal", + fap_weburl="https://github.com/SimplyMinimal/FlipperZero-Etch-A-Sketch", + fap_version="1.0", + fap_description="Turn the Flipper Zero into an Etch A Sketch", +) diff --git a/applications/external/etch_a_sketch/etch-a-sketch-icon.png b/applications/external/etch_a_sketch/etch-a-sketch-icon.png new file mode 100644 index 0000000000..567c16900e Binary files /dev/null and b/applications/external/etch_a_sketch/etch-a-sketch-icon.png differ diff --git a/applications/external/etch_a_sketch/etch_a_sketch.c b/applications/external/etch_a_sketch/etch_a_sketch.c new file mode 100644 index 0000000000..6fd6d3596b --- /dev/null +++ b/applications/external/etch_a_sketch/etch_a_sketch.c @@ -0,0 +1,274 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // Header-file for boolean data-type. +#include +#include + +#define WIDTH 64 +#define HEIGHT 32 + +const int brush_size = 2; + +typedef struct selected_position { + int x; + int y; +} selected_position; + +typedef struct { + FuriMutex* mutex; + selected_position selected; + bool board[64][32]; + bool isDrawing; + bool showWelcome; +} EtchData; + +// Sequence to indicate that drawing is enabled. +const NotificationSequence sequence_begin_draw = { + &message_display_backlight_on, + + // Vibrate to indicate that drawing is enabled. + &message_vibro_on, + &message_note_g5, + &message_delay_50, + &message_note_c6, + &message_delay_50, + &message_note_e5, + &message_vibro_off, + &message_sound_off, + NULL, +}; + +// sequence to indicate that drawing is disabled +const NotificationSequence sequence_end_draw = { + &message_red_0, + // Indicate that drawing is disabled. + &message_vibro_on, + &message_note_g5, + &message_delay_50, + &message_note_e5, + &message_delay_50, + &message_vibro_off, + &message_sound_off, + &message_do_not_reset, + NULL, +}; + +// Indicate that drawing is enabled. +const NotificationSequence sequence_draw_enabled = { + &message_red_255, + &message_do_not_reset, + NULL, +}; + +// Indicate that drawing is disabled. +const NotificationSequence sequence_draw_disabled = { + &message_red_0, + &message_do_not_reset, + NULL, +}; + +const NotificationSequence sequence_cleanup = { + &message_red_0, + &message_green_0, + &message_blue_0, + &message_sound_off, + &message_vibro_off, + NULL, +}; + +void etch_draw_callback(Canvas* canvas, void* ctx) { + furi_assert(ctx); + const EtchData* etch_state = ctx; + furi_mutex_acquire(etch_state->mutex, FuriWaitForever); + + canvas_clear(canvas); + + // Show Welcome Message + if(etch_state->showWelcome) { + // Draw Etch A Sketch frame + canvas_draw_frame(canvas, 5, 3, 119, 55); // Border + canvas_draw_icon(canvas, 8, 50, &I_Ok_btn_pressed_13x13); // Left Knob + canvas_draw_icon(canvas, 107, 50, &I_Ok_btn_pressed_13x13); // Right Knob + + // Draw Etch A Sketch text banner + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 36, 15, "Etch A Sketch"); + + // Draw Etch A Sketch instructions "Hold Back to clear" + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 31, 26, "* Hold "); + canvas_draw_icon(canvas, 59, 18, &I_Pin_back_arrow_10x8); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 72, 26, "to clear"); + + // Draw Etch A Sketch instructions "Hold OK button to draw" + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 31, 37, "* Hold"); + canvas_draw_icon(canvas, 61, 30, &I_ButtonCenter_7x7); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 72, 37, "to draw"); + } + + canvas_set_color(canvas, ColorBlack); + //draw the canvas(64x32) on screen(144x64) using brush_size*brush_size tiles + for(int y = 0; y < 32; y++) { + for(int x = 0; x < 64; x++) { + if(etch_state->board[x][y]) { + canvas_draw_box(canvas, x * brush_size, y * brush_size, 2, 2); + } + } + } + + //draw cursor as a brush_size by brush_size black box + canvas_set_color(canvas, ColorBlack); + canvas_draw_box( + canvas, + etch_state->selected.x * brush_size, + etch_state->selected.y * brush_size, + brush_size, + brush_size); + + //release the mutex + furi_mutex_release(etch_state->mutex); +} + +void etch_input_callback(InputEvent* input_event, void* ctx) { + furi_assert(ctx); + FuriMessageQueue* event_queue = ctx; + furi_message_queue_put(event_queue, input_event, FuriWaitForever); +} + +int32_t etch_a_sketch_app(void* p) { + UNUSED(p); + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); + + EtchData* etch_state = malloc(sizeof(EtchData)); + etch_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + if(!etch_state->mutex) { + FURI_LOG_E("etch", "cannot create mutex\r\n"); + free(etch_state); + return -1; + } + + // Configure view port + ViewPort* view_port = view_port_alloc(); + view_port_draw_callback_set(view_port, etch_draw_callback, etch_state); + view_port_input_callback_set(view_port, etch_input_callback, event_queue); + + // Register view port in GUI + Gui* gui = furi_record_open(RECORD_GUI); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); + + InputEvent event; + + // Show Welcome Banner + etch_state->showWelcome = true; + + while(furi_message_queue_get(event_queue, &event, FuriWaitForever) == FuriStatusOk) { + //break out of the loop if the back key is pressed + if(event.key == InputKeyBack && event.type == InputTypeShort) { + break; + } + + // Clear + // TODO: Do animation of shaking board + if(event.key == InputKeyBack && event.type == InputTypeLong) { + etch_state->showWelcome = false; + etch_state->board[1][1] = true; + for(int y = 0; y < 32; y++) { + for(int x = 0; x < 64; x++) { + etch_state->board[x][y] = false; + } + } + view_port_update(view_port); + } + + // Keep LED on while drawing + if(etch_state->isDrawing) { + notification_message(notification, &sequence_draw_enabled); + } else { + notification_message(notification, &sequence_draw_disabled); + } + + // Single Dot Select + if(event.key == InputKeyOk && event.type == InputTypeShort) { + etch_state->board[etch_state->selected.x][etch_state->selected.y] = + !etch_state->board[etch_state->selected.x][etch_state->selected.y]; + } + + // Start Drawing + if(event.key == InputKeyOk && event.type == InputTypeLong) { + // notification_message(furi_record_open(RECORD_NOTIFICATION), &sequence_begin_draw); + notification_message(notification, &sequence_begin_draw); + + if(etch_state->isDrawing) { + // We're ending the drawing + notification_message(notification, &sequence_end_draw); + } + + etch_state->isDrawing = !etch_state->isDrawing; + etch_state->board[etch_state->selected.x][etch_state->selected.y] = true; + + view_port_update(view_port); + } + + //check the key pressed and change x and y accordingly + if(event.type == InputTypeShort || event.type == InputTypeRepeat || + event.type == InputTypeLong) { + switch(event.key) { + case InputKeyUp: + etch_state->selected.y -= 1; + break; + case InputKeyDown: + etch_state->selected.y += 1; + break; + case InputKeyLeft: + etch_state->selected.x -= 1; + break; + case InputKeyRight: + etch_state->selected.x += 1; + break; + default: + break; + } + + //check if cursor position is out of bounds and reset it to the closest position + if(etch_state->selected.x < 0) { + etch_state->selected.x = 0; + } + if(etch_state->selected.x > 61) { + etch_state->selected.x = 61; + } + if(etch_state->selected.y < 0) { + etch_state->selected.y = 0; + } + if(etch_state->selected.y > 31) { + etch_state->selected.y = 31; + } + if(etch_state->isDrawing == true) { + etch_state->board[etch_state->selected.x][etch_state->selected.y] = true; + } + view_port_update(view_port); + } + } + + notification_message(notification, &sequence_cleanup); + gui_remove_view_port(gui, view_port); + view_port_free(view_port); + furi_mutex_free(etch_state->mutex); + furi_message_queue_free(event_queue); + furi_record_close(RECORD_NOTIFICATION); + furi_record_close(RECORD_GUI); + free(etch_state); + + return 0; +} diff --git a/applications/external/flappy_bird/application.fam b/applications/external/flappy_bird/application.fam index 9bfab454f4..ad18e57e10 100644 --- a/applications/external/flappy_bird/application.fam +++ b/applications/external/flappy_bird/application.fam @@ -9,4 +9,7 @@ App( fap_icon="flappy_10px.png", fap_category="Games", fap_icon_assets="assets", + fap_author="@DroomOne & @xMasterX", + fap_version="1.0", + fap_description="Flappy Bird Game", ) diff --git a/applications/external/flappy_bird/flappy_bird.c b/applications/external/flappy_bird/flappy_bird.c index 9685f942c5..4dd85b7513 100644 --- a/applications/external/flappy_bird/flappy_bird.c +++ b/applications/external/flappy_bird/flappy_bird.c @@ -307,6 +307,9 @@ int32_t flappy_game_app(void* p) { Gui* gui = furi_record_open(RECORD_GUI); gui_add_view_port(gui, view_port, GuiLayerFullscreen); + // Call dolphin deed on game start + // dolphin_deed(DolphinDeedPluginGameStart); + GameEvent event; for(bool processing = true; processing;) { FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); diff --git a/applications/external/flashlight/application.fam b/applications/external/flashlight/application.fam index ea8eb03d17..030c529905 100644 --- a/applications/external/flashlight/application.fam +++ b/applications/external/flashlight/application.fam @@ -11,4 +11,8 @@ App( order=20, fap_icon="flash10px.png", fap_category="GPIO", + fap_author="@xMasterX", + fap_weburl="https://github.com/xMasterX/flipper-flashlight", + fap_version="1.0", + fap_description="Enables 3.3v on pin 7/C3 when you press Ok and leaves it on when you exit app", ) diff --git a/applications/external/flipbip/LICENSE b/applications/external/flipbip/LICENSE new file mode 100644 index 0000000000..61361ecc7a --- /dev/null +++ b/applications/external/flipbip/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 Struan Clark + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/applications/external/flipbip/application.fam b/applications/external/flipbip/application.fam new file mode 100644 index 0000000000..e8c660a89b --- /dev/null +++ b/applications/external/flipbip/application.fam @@ -0,0 +1,22 @@ +App( + appid="flipbip", + name="FlipBIP Crypto Wallet", + apptype=FlipperAppType.EXTERNAL, + entry_point="flipbip_app", + requires=[ + "gui", + ], + stack_size=3 * 1024, + order=10, + fap_icon="flipbip_10px.png", + fap_icon_assets="icons", + fap_private_libs=[ + Lib( + name="crypto", + ), + ], + fap_category="Misc", + fap_description="Crypto toolkit for Flipper", + fap_author="Struan Clark (xtruan)", + fap_weburl="https://github.com/xtruan/FlipBIP", +) diff --git a/applications/external/flipbip/flipbip.c b/applications/external/flipbip/flipbip.c new file mode 100644 index 0000000000..1a5e5a983b --- /dev/null +++ b/applications/external/flipbip/flipbip.c @@ -0,0 +1,205 @@ +#pragma GCC optimize("-Os") + +#include "flipbip.h" +#include "helpers/flipbip_file.h" +#include "helpers/flipbip_haptic.h" +// From: lib/crypto +#include +#include + +bool flipbip_custom_event_callback(void* context, uint32_t event) { + furi_assert(context); + FlipBip* app = context; + return scene_manager_handle_custom_event(app->scene_manager, event); +} + +void flipbip_tick_event_callback(void* context) { + furi_assert(context); + FlipBip* app = context; + scene_manager_handle_tick_event(app->scene_manager); +} + +//leave app if back button pressed +bool flipbip_navigation_event_callback(void* context) { + furi_assert(context); + FlipBip* app = context; + return scene_manager_handle_back_event(app->scene_manager); +} + +static void text_input_callback(void* context) { + furi_assert(context); + FlipBip* app = context; + bool handled = false; + + // check that there is text in the input + if(strlen(app->input_text) > 0) { + if(app->input_state == FlipBipTextInputPassphrase) { + if(app->passphrase == FlipBipPassphraseOn) { + strcpy(app->passphrase_text, app->input_text); + } + // clear input text + memzero(app->input_text, TEXT_BUFFER_SIZE); + // reset input state + app->input_state = FlipBipTextInputDefault; + handled = true; + view_dispatcher_switch_to_view(app->view_dispatcher, FlipBipViewIdSettings); + } else if(app->input_state == FlipBipTextInputMnemonic) { + if(app->import_from_mnemonic == 1) { + strcpy(app->import_mnemonic_text, app->input_text); + + int status = FlipBipStatusSuccess; + // Check if the mnemonic is valid + if(mnemonic_check(app->import_mnemonic_text) == 0) + status = FlipBipStatusMnemonicCheckError; // 13 = mnemonic check error + // Save the mnemonic to persistent storage + else if(!flipbip_save_file_secure(app->import_mnemonic_text)) + status = FlipBipStatusSaveError; // 12 = save error + + if(status == FlipBipStatusSuccess) { + //notification_message(app->notification, &sequence_blink_cyan_100); + flipbip_play_happy_bump(app); + } else { + //notification_message(app->notification, &sequence_blink_red_100); + flipbip_play_long_bump(app); + } + + memzero(app->import_mnemonic_text, TEXT_BUFFER_SIZE); + } + // clear input text + memzero(app->input_text, TEXT_BUFFER_SIZE); + // reset input state + app->input_state = FlipBipTextInputDefault; + handled = true; + view_dispatcher_switch_to_view(app->view_dispatcher, FlipBipViewIdMenu); + } + } + + if(!handled) { + // clear input text + memzero(app->input_text, TEXT_BUFFER_SIZE); + // reset input state + app->input_state = FlipBipTextInputDefault; + view_dispatcher_switch_to_view(app->view_dispatcher, FlipBipViewIdMenu); + } +} + +FlipBip* flipbip_app_alloc() { + FlipBip* app = malloc(sizeof(FlipBip)); + app->gui = furi_record_open(RECORD_GUI); + app->notification = furi_record_open(RECORD_NOTIFICATION); + + //Turn backlight on, believe me this makes testing your app easier + notification_message(app->notification, &sequence_display_backlight_on); + + //Scene additions + app->view_dispatcher = view_dispatcher_alloc(); + view_dispatcher_enable_queue(app->view_dispatcher); + + app->scene_manager = scene_manager_alloc(&flipbip_scene_handlers, app); + view_dispatcher_set_event_callback_context(app->view_dispatcher, app); + view_dispatcher_set_navigation_event_callback( + app->view_dispatcher, flipbip_navigation_event_callback); + view_dispatcher_set_tick_event_callback( + app->view_dispatcher, flipbip_tick_event_callback, 100); + view_dispatcher_set_custom_event_callback(app->view_dispatcher, flipbip_custom_event_callback); + app->submenu = submenu_alloc(); + + // Settings + app->haptic = FlipBipHapticOn; + app->led = FlipBipLedOn; + app->bip39_strength = FlipBipStrength256; // 256 bits (24 words) + app->passphrase = FlipBipPassphraseOff; + + // Main menu + app->bip44_coin = FlipBipCoinBTC0; // 0 (BTC) + app->overwrite_saved_seed = 0; + app->import_from_mnemonic = 0; + + // Text input + app->input_state = FlipBipTextInputDefault; + + view_dispatcher_add_view( + app->view_dispatcher, FlipBipViewIdMenu, submenu_get_view(app->submenu)); + app->flipbip_startscreen = flipbip_startscreen_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, + FlipBipViewIdStartscreen, + flipbip_startscreen_get_view(app->flipbip_startscreen)); + app->flipbip_scene_1 = flipbip_scene_1_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, FlipBipViewIdScene1, flipbip_scene_1_get_view(app->flipbip_scene_1)); + app->variable_item_list = variable_item_list_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, + FlipBipViewIdSettings, + variable_item_list_get_view(app->variable_item_list)); + + app->text_input = text_input_alloc(); + text_input_set_result_callback( + app->text_input, + text_input_callback, + (void*)app, + app->input_text, + TEXT_BUFFER_SIZE, + //clear default text + true); + text_input_set_header_text(app->text_input, "Input"); + view_dispatcher_add_view( + app->view_dispatcher, FlipBipViewIdTextInput, text_input_get_view(app->text_input)); + + //End Scene Additions + + return app; +} + +void flipbip_app_free(FlipBip* app) { + furi_assert(app); + + // Scene manager + scene_manager_free(app->scene_manager); + + text_input_free(app->text_input); + + // View Dispatcher + view_dispatcher_remove_view(app->view_dispatcher, FlipBipViewIdMenu); + view_dispatcher_remove_view(app->view_dispatcher, FlipBipViewIdScene1); + view_dispatcher_remove_view(app->view_dispatcher, FlipBipViewIdSettings); + view_dispatcher_remove_view(app->view_dispatcher, FlipBipViewIdTextInput); + submenu_free(app->submenu); + + view_dispatcher_free(app->view_dispatcher); + furi_record_close(RECORD_GUI); + + app->gui = NULL; + app->notification = NULL; + + //Remove whatever is left + memzero(app, sizeof(FlipBip)); + free(app); +} + +int32_t flipbip_app(void* p) { + UNUSED(p); + FlipBip* app = flipbip_app_alloc(); + + // Disabled because causes exit on custom firmwares such as RM + /*if(!furi_hal_region_is_provisioned()) { + flipbip_app_free(app); + return 1; + }*/ + + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + + scene_manager_next_scene( + app->scene_manager, FlipBipSceneStartscreen); //Start with start screen + //scene_manager_next_scene(app->scene_manager, FlipBipSceneMenu); //if you want to directly start with Menu + + furi_hal_power_suppress_charge_enter(); + + view_dispatcher_run(app->view_dispatcher); + + furi_hal_power_suppress_charge_exit(); + flipbip_app_free(app); + + return 0; +} diff --git a/applications/external/flipbip/flipbip.h b/applications/external/flipbip/flipbip.h new file mode 100644 index 0000000000..6f84a17363 --- /dev/null +++ b/applications/external/flipbip/flipbip.h @@ -0,0 +1,99 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "scenes/flipbip_scene.h" +#include "views/flipbip_startscreen.h" +#include "views/flipbip_scene_1.h" + +#define FLIPBIP_VERSION "v0.0.9" + +#define COIN_BTC 0 +#define COIN_DOGE 3 +#define COIN_ETH 60 + +#define TEXT_BUFFER_SIZE 256 + +typedef struct { + Gui* gui; + NotificationApp* notification; + ViewDispatcher* view_dispatcher; + Submenu* submenu; + SceneManager* scene_manager; + VariableItemList* variable_item_list; + TextInput* text_input; + FlipBipStartscreen* flipbip_startscreen; + FlipBipScene1* flipbip_scene_1; + // Settings options + int haptic; + int led; + int bip39_strength; + int passphrase; + // Main menu options + int bip44_coin; + int overwrite_saved_seed; + int import_from_mnemonic; + // Text input + int input_state; + char passphrase_text[TEXT_BUFFER_SIZE]; + char import_mnemonic_text[TEXT_BUFFER_SIZE]; + char input_text[TEXT_BUFFER_SIZE]; +} FlipBip; + +typedef enum { + FlipBipViewIdStartscreen, + FlipBipViewIdMenu, + FlipBipViewIdScene1, + FlipBipViewIdSettings, + FlipBipViewIdTextInput, +} FlipBipViewId; + +typedef enum { + FlipBipHapticOff, + FlipBipHapticOn, +} FlipBipHapticState; + +typedef enum { + FlipBipLedOff, + FlipBipLedOn, +} FlipBipLedState; + +typedef enum { + FlipBipStrength128, + FlipBipStrength192, + FlipBipStrength256, +} FlipBipStrengthState; + +typedef enum { + FlipBipPassphraseOff, + FlipBipPassphraseOn, +} FlipBipPassphraseState; + +typedef enum { + FlipBipCoinBTC0, + FlipBipCoinETH60, + FlipBipCoinDOGE3, +} FlipBipCoin; + +typedef enum { + FlipBipTextInputDefault, + FlipBipTextInputPassphrase, + FlipBipTextInputMnemonic +} FlipBipTextInputState; + +typedef enum { + FlipBipStatusSuccess = 0, + FlipBipStatusReturn = 10, + FlipBipStatusLoadError = 11, + FlipBipStatusSaveError = 12, + FlipBipStatusMnemonicCheckError = 13, +} FlipBipStatus; diff --git a/applications/external/flipbip/flipbip_10px.png b/applications/external/flipbip/flipbip_10px.png new file mode 100644 index 0000000000..1730790465 Binary files /dev/null and b/applications/external/flipbip/flipbip_10px.png differ diff --git a/applications/external/flipbip/helpers/flipbip_custom_event.h b/applications/external/flipbip/helpers/flipbip_custom_event.h new file mode 100644 index 0000000000..2dbaf51129 --- /dev/null +++ b/applications/external/flipbip/helpers/flipbip_custom_event.h @@ -0,0 +1,16 @@ +#pragma once + +typedef enum { + FlipBipCustomEventStartscreenUp, + FlipBipCustomEventStartscreenDown, + FlipBipCustomEventStartscreenLeft, + FlipBipCustomEventStartscreenRight, + FlipBipCustomEventStartscreenOk, + FlipBipCustomEventStartscreenBack, + FlipBipCustomEventScene1Up, + FlipBipCustomEventScene1Down, + FlipBipCustomEventScene1Left, + FlipBipCustomEventScene1Right, + FlipBipCustomEventScene1Ok, + FlipBipCustomEventScene1Back, +} FlipBipCustomEvent; \ No newline at end of file diff --git a/applications/external/flipbip/helpers/flipbip_file.c b/applications/external/flipbip/helpers/flipbip_file.c new file mode 100644 index 0000000000..3b61b6b958 --- /dev/null +++ b/applications/external/flipbip/helpers/flipbip_file.c @@ -0,0 +1,309 @@ +#include "flipbip_file.h" +#include +#include +#include +#include "../helpers/flipbip_string.h" +// From: lib/crypto +#include +#include + +// #define FLIPBIP_APP_BASE_FOLDER APP_DATA_PATH("flipbip") +#define FLIPBIP_APP_BASE_FOLDER EXT_PATH("apps_data/flipbip") +#define FLIPBIP_APP_BASE_FOLDER_PATH(path) FLIPBIP_APP_BASE_FOLDER "/" path +#define FLIPBIP_DAT_FILE_NAME ".flipbip.dat" +// #define FLIPBIP_DAT_FILE_NAME ".flipbip.dat.txt" +#define FLIPBIP_DAT_FILE_NAME_BAK ".flipbip.dat.bak" +#define FLIPBIP_KEY_FILE_NAME ".flipbip.key" +// #define FLIPBIP_KEY_FILE_NAME ".flipbip.key.txt" +#define FLIPBIP_KEY_FILE_NAME_BAK ".flipbip.key.bak" +#define FLIPBIP_DAT_PATH FLIPBIP_APP_BASE_FOLDER_PATH(FLIPBIP_DAT_FILE_NAME) +#define FLIPBIP_DAT_PATH_BAK FLIPBIP_APP_BASE_FOLDER_PATH(FLIPBIP_DAT_FILE_NAME_BAK) +#define FLIPBIP_KEY_PATH FLIPBIP_APP_BASE_FOLDER_PATH(FLIPBIP_KEY_FILE_NAME) +#define FLIPBIP_KEY_PATH_BAK FLIPBIP_APP_BASE_FOLDER_PATH(FLIPBIP_KEY_FILE_NAME_BAK) + +const char* TEXT_QRFILE = "Filetype: QRCode\n" + "Version: 0\n" + "Message: "; // 37 chars + 1 null +#define FILE_HLEN 4 +#define FILE_KLEN 256 +#define FILE_SLEN 512 +#define FILE_MAX_PATH_LEN 48 +#define FILE_MAX_QRFILE_CONTENT 90 +const char* FILE_HSTR = "fb01"; +const char* FILE_K1 = "fb0131d5cf688221c109163908ebe51debb46227c6cc8b37641910833222772a" + "baefe6d9ceb651842260e0d1e05e3b90d15e7d5ffaaabc0207bf200a117793a2"; + +bool flipbip_load_file(char* settings, const FlipBipFile file_type, const char* file_name) { + bool ret = false; + const char* path; + if(file_type == FlipBipFileKey) { + path = FLIPBIP_KEY_PATH; + } else if(file_type == FlipBipFileDat) { + path = FLIPBIP_DAT_PATH; + } else { + char path_buf[FILE_MAX_PATH_LEN] = {0}; + strcpy(path_buf, FLIPBIP_APP_BASE_FOLDER); // 22 + strcpy(path_buf + strlen(path_buf), "/"); + strcpy(path_buf + strlen(path_buf), file_name); + path = path_buf; + } + + Storage* fs_api = furi_record_open(RECORD_STORAGE); + + File* settings_file = storage_file_alloc(fs_api); + if(storage_file_open(settings_file, path, FSAM_READ, FSOM_OPEN_EXISTING)) { + char chr; + int i = 0; + while((storage_file_read(settings_file, &chr, 1) == 1) && + !storage_file_eof(settings_file) && !isspace(chr)) { + settings[i] = chr; + i++; + } + ret = true; + } else { + memzero(settings, strlen(settings)); + settings[0] = '\0'; + ret = false; + } + storage_file_close(settings_file); + storage_file_free(settings_file); + furi_record_close(RECORD_STORAGE); + + if(strlen(settings) > 0) { + Storage* fs_api = furi_record_open(RECORD_STORAGE); + FileInfo layout_file_info; + FS_Error file_check_err = storage_common_stat(fs_api, path, &layout_file_info); + furi_record_close(RECORD_STORAGE); + if(file_check_err != FSE_OK) { + memzero(settings, strlen(settings)); + settings[0] = '\0'; + ret = false; + } + // if(layout_file_info.size != 256) { + // memzero(settings, strlen(settings)); + // settings[0] = '\0'; + // } + } + + return ret; +} + +bool flipbip_has_file(const FlipBipFile file_type, const char* file_name, const bool remove) { + bool ret = false; + const char* path; + if(file_type == FlipBipFileKey) { + path = FLIPBIP_KEY_PATH; + } else if(file_type == FlipBipFileDat) { + path = FLIPBIP_DAT_PATH; + } else { + char path_buf[FILE_MAX_PATH_LEN] = {0}; + strcpy(path_buf, FLIPBIP_APP_BASE_FOLDER); // 22 + strcpy(path_buf + strlen(path_buf), "/"); + strcpy(path_buf + strlen(path_buf), file_name); + path = path_buf; + } + + Storage* fs_api = furi_record_open(RECORD_STORAGE); + if(remove) { + ret = storage_simply_remove(fs_api, path); + } else { + ret = storage_file_exists(fs_api, path); + } + furi_record_close(RECORD_STORAGE); + + return ret; +} + +bool flipbip_save_file( + const char* settings, + const FlipBipFile file_type, + const char* file_name, + const bool append) { + bool ret = false; + const char* path; + const char* path_bak; + if(file_type == FlipBipFileKey) { + path = FLIPBIP_KEY_PATH; + path_bak = FLIPBIP_KEY_PATH_BAK; + } else if(file_type == FlipBipFileDat) { + path = FLIPBIP_DAT_PATH; + path_bak = FLIPBIP_DAT_PATH_BAK; + } else { + char path_buf[FILE_MAX_PATH_LEN] = {0}; + strcpy(path_buf, FLIPBIP_APP_BASE_FOLDER); // 22 + strcpy(path_buf + strlen(path_buf), "/"); + strcpy(path_buf + strlen(path_buf), file_name); + path = path_buf; + path_bak = NULL; + } + int open_mode = FSOM_OPEN_ALWAYS; + if(append) { + open_mode = FSOM_OPEN_APPEND; + } + + Storage* fs_api = furi_record_open(RECORD_STORAGE); + // // if the key file exists, we don't want to overwrite it + // if (key_file && storage_file_exists(fs_api, path)) { + // furi_record_close(RECORD_STORAGE); + // ret = true; + // return ret; + // } + // try to create the folder + storage_simply_mkdir(fs_api, FLIPBIP_APP_BASE_FOLDER); + + File* settings_file = storage_file_alloc(fs_api); + if(storage_file_open(settings_file, path, FSAM_WRITE, open_mode)) { + storage_file_write(settings_file, settings, strlen(settings)); + storage_file_write(settings_file, "\n", 1); + ret = true; + } + storage_file_close(settings_file); + storage_file_free(settings_file); + + if(path_bak != NULL) { + File* settings_file_bak = storage_file_alloc(fs_api); + if(storage_file_open(settings_file_bak, path_bak, FSAM_WRITE, open_mode)) { + storage_file_write(settings_file_bak, settings, strlen(settings)); + storage_file_write(settings_file_bak, "\n", 1); + } + storage_file_close(settings_file_bak); + storage_file_free(settings_file_bak); + } + + furi_record_close(RECORD_STORAGE); + + return ret; +} + +bool flipbip_save_qrfile( + const char* qr_msg_prefix, + const char* qr_msg_content, + const char* file_name) { + char qr_buf[FILE_MAX_QRFILE_CONTENT] = {0}; + strcpy(qr_buf, TEXT_QRFILE); + strcpy(qr_buf + strlen(qr_buf), qr_msg_prefix); + strcpy(qr_buf + strlen(qr_buf), qr_msg_content); + return flipbip_save_file(qr_buf, FlipBipFileOther, file_name, false); +} + +bool flipbip_load_file_secure(char* settings) { + const size_t dlen = FILE_HLEN + FILE_SLEN + 1; + + // allocate memory for key/data + char* data = malloc(dlen); + memzero(data, dlen); + + // load k2 from file + if(!flipbip_load_file(data, FlipBipFileKey, NULL)) return false; + + // check header + if(data[0] != FILE_HSTR[0] || data[1] != FILE_HSTR[1] || data[2] != FILE_HSTR[2] || + data[3] != FILE_HSTR[3]) { + memzero(data, dlen); + free(data); + return false; + } + // seek --> header + data += FILE_HLEN; + + // prepare k1 + uint8_t k1[64]; + flipbip_xtob(FILE_K1, k1, strlen(FILE_K1) / 2); + + // load k2 from file buffer (secured by k1) + flipbip_cipher(k1, strlen(FILE_K1) / 2, data, data, FILE_KLEN); + uint8_t k2[128]; + flipbip_xtob(data, k2, FILE_KLEN / 2); + // zero k2 buffer + memzero(data, FILE_KLEN); + // seek <-- header + data -= FILE_HLEN; + + // load data from file + if(!flipbip_load_file(data, FlipBipFileDat, NULL)) return false; + + // check header + if(data[0] != FILE_HSTR[0] || data[1] != FILE_HSTR[1] || data[2] != FILE_HSTR[2] || + data[3] != FILE_HSTR[3]) { + memzero(data, dlen); + free(data); + memzero(k1, strlen(FILE_K1) / 2); + memzero(k2, FILE_KLEN / 2); + return false; + } + // seek --> header + data += FILE_HLEN; + + // load settings from file buffer (secured by k2) + flipbip_cipher(k2, FILE_KLEN / 2, data, data, FILE_SLEN); + flipbip_xtob(data, (unsigned char*)data, FILE_SLEN / 2); + + // copy to output + strcpy(settings, data); + + // seek <-- header + data -= FILE_HLEN; + + // clear memory + memzero(data, dlen); + free(data); + memzero(k1, strlen(FILE_K1) / 2); + memzero(k2, FILE_KLEN / 2); + + return true; +} + +bool flipbip_save_file_secure(const char* settings) { + const size_t dlen = FILE_HLEN + FILE_SLEN + 1; + + // cap settings to 256 bytes + size_t len = strlen(settings); + if(len > (FILE_SLEN / 2)) len = FILE_SLEN / 2; + + // allocate memory for key/data + char* data = malloc(dlen); + memzero(data, dlen); + + // write header + strncpy(data, FILE_HSTR, FILE_HLEN); + // seek --> header + data += FILE_HLEN; + + // prepare k1 + uint8_t k1[64]; + flipbip_xtob(FILE_K1, k1, strlen(FILE_K1) / 2); + + // generate k2 + uint8_t k2[128]; + random_buffer(k2, FILE_KLEN / 2); + + // write k2 to file buffer (secured by k1) + flipbip_btox(k2, FILE_KLEN / 2, data); + flipbip_cipher(k1, strlen(FILE_K1) / 2, data, data, FILE_KLEN); + + // seek <-- header + data -= FILE_HLEN; + // save k2 to file + flipbip_save_file(data, FlipBipFileKey, NULL, false); + // seek --> header + data += FILE_HLEN; + // zero k2 memory + memzero(data, FILE_KLEN); + + // write settings to file buffer (secured by k2) + flipbip_btox((uint8_t*)settings, len, data); + flipbip_cipher(k2, FILE_KLEN / 2, data, data, FILE_SLEN); + + // seek <-- header + data -= FILE_HLEN; + // save data to file + flipbip_save_file(data, FlipBipFileDat, NULL, false); + + // clear memory + memzero(data, dlen); + free(data); + memzero(k1, strlen(FILE_K1) / 2); + memzero(k2, FILE_KLEN / 2); + + return true; +} diff --git a/applications/external/flipbip/helpers/flipbip_file.h b/applications/external/flipbip/helpers/flipbip_file.h new file mode 100644 index 0000000000..c19f70dc51 --- /dev/null +++ b/applications/external/flipbip/helpers/flipbip_file.h @@ -0,0 +1,23 @@ +#include + +typedef enum { + FlipBipFileDat, + FlipBipFileKey, + FlipBipFileOther, +} FlipBipFile; + +bool flipbip_has_file(const FlipBipFile file_type, const char* file_name, const bool remove); +bool flipbip_load_file(char* settings, const FlipBipFile file_type, const char* file_name); +bool flipbip_save_file( + const char* settings, + const FlipBipFile file_type, + const char* file_name, + const bool append); + +bool flipbip_save_qrfile( + const char* qr_msg_prefix, + const char* qr_msg_content, + const char* file_name); + +bool flipbip_load_file_secure(char* settings); +bool flipbip_save_file_secure(const char* settings); diff --git a/applications/external/flipbip/helpers/flipbip_haptic.c b/applications/external/flipbip/helpers/flipbip_haptic.c new file mode 100644 index 0000000000..c5608efa56 --- /dev/null +++ b/applications/external/flipbip/helpers/flipbip_haptic.c @@ -0,0 +1,35 @@ +#include "flipbip_haptic.h" +#include "../flipbip.h" + +void flipbip_play_happy_bump(void* context) { + FlipBip* app = context; + if(app->haptic != 1) { + return; + } + notification_message(app->notification, &sequence_set_vibro_on); + furi_thread_flags_wait(0, FuriFlagWaitAny, 20); + notification_message(app->notification, &sequence_reset_vibro); +} + +void flipbip_play_bad_bump(void* context) { + FlipBip* app = context; + if(app->haptic != 1) { + return; + } + notification_message(app->notification, &sequence_set_vibro_on); + furi_thread_flags_wait(0, FuriFlagWaitAny, 100); + notification_message(app->notification, &sequence_reset_vibro); +} + +void flipbip_play_long_bump(void* context) { + FlipBip* app = context; + if(app->haptic != 1) { + return; + } + for(int i = 0; i < 4; i++) { + notification_message(app->notification, &sequence_set_vibro_on); + furi_thread_flags_wait(0, FuriFlagWaitAny, 50); + notification_message(app->notification, &sequence_reset_vibro); + furi_thread_flags_wait(0, FuriFlagWaitAny, 100); + } +} diff --git a/applications/external/flipbip/helpers/flipbip_haptic.h b/applications/external/flipbip/helpers/flipbip_haptic.h new file mode 100644 index 0000000000..cab1d3a632 --- /dev/null +++ b/applications/external/flipbip/helpers/flipbip_haptic.h @@ -0,0 +1,7 @@ +#include + +void flipbip_play_happy_bump(void* context); + +void flipbip_play_bad_bump(void* context); + +void flipbip_play_long_bump(void* context); diff --git a/applications/external/flipbip/helpers/flipbip_led.c b/applications/external/flipbip/helpers/flipbip_led.c new file mode 100644 index 0000000000..7a6fd1778a --- /dev/null +++ b/applications/external/flipbip/helpers/flipbip_led.c @@ -0,0 +1,39 @@ +#include "flipbip_led.h" +#include "../flipbip.h" + +void flipbip_led_set_rgb(void* context, int red, int green, int blue) { + FlipBip* app = context; + if(app->led != 1) { + return; + } + NotificationMessage notification_led_message_1; + notification_led_message_1.type = NotificationMessageTypeLedRed; + NotificationMessage notification_led_message_2; + notification_led_message_2.type = NotificationMessageTypeLedGreen; + NotificationMessage notification_led_message_3; + notification_led_message_3.type = NotificationMessageTypeLedBlue; + + notification_led_message_1.data.led.value = red; + notification_led_message_2.data.led.value = green; + notification_led_message_3.data.led.value = blue; + const NotificationSequence notification_sequence = { + ¬ification_led_message_1, + ¬ification_led_message_2, + ¬ification_led_message_3, + &message_do_not_reset, + NULL, + }; + notification_message(app->notification, ¬ification_sequence); + furi_thread_flags_wait( + 0, FuriFlagWaitAny, 10); //Delay, prevent removal from RAM before LED value set +} + +void flipbip_led_reset(void* context) { + FlipBip* app = context; + notification_message(app->notification, &sequence_reset_red); + notification_message(app->notification, &sequence_reset_green); + notification_message(app->notification, &sequence_reset_blue); + + furi_thread_flags_wait( + 0, FuriFlagWaitAny, 300); //Delay, prevent removal from RAM before LED value set +} diff --git a/applications/external/flipbip/helpers/flipbip_led.h b/applications/external/flipbip/helpers/flipbip_led.h new file mode 100644 index 0000000000..bbacc976b6 --- /dev/null +++ b/applications/external/flipbip/helpers/flipbip_led.h @@ -0,0 +1,2 @@ +void flipbip_led_set_rgb(void* context, int red, int green, int blue); +void flipbip_led_reset(void* context); diff --git a/applications/external/flipbip/helpers/flipbip_speaker.c b/applications/external/flipbip/helpers/flipbip_speaker.c new file mode 100644 index 0000000000..f7ae2193b9 --- /dev/null +++ b/applications/external/flipbip/helpers/flipbip_speaker.c @@ -0,0 +1,27 @@ +// #include "flipbip_speaker.h" +// #include "../flipbip.h" + +// #define NOTE_INPUT 587.33f + +// void flipbip_play_input_sound(void* context) { +// FlipBip* app = context; +// if (app->speaker != 1) { +// return; +// } +// float volume = 1.0f; +// if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(30)) { +// furi_hal_speaker_start(NOTE_INPUT, volume); +// } + +// } + +// void flipbip_stop_all_sound(void* context) { +// FlipBip* app = context; +// if (app->speaker != 1) { +// return; +// } +// if(furi_hal_speaker_is_mine()) { +// furi_hal_speaker_stop(); +// furi_hal_speaker_release(); +// } +// } diff --git a/applications/external/flipbip/helpers/flipbip_speaker.h b/applications/external/flipbip/helpers/flipbip_speaker.h new file mode 100644 index 0000000000..150ba91297 --- /dev/null +++ b/applications/external/flipbip/helpers/flipbip_speaker.h @@ -0,0 +1,4 @@ +// #define NOTE_INPUT 587.33f + +// void flipbip_play_input_sound(void* context); +// void flipbip_stop_all_sound(void* context); diff --git a/applications/external/flipbip/helpers/flipbip_string.c b/applications/external/flipbip/helpers/flipbip_string.c new file mode 100644 index 0000000000..9998206469 --- /dev/null +++ b/applications/external/flipbip/helpers/flipbip_string.c @@ -0,0 +1,129 @@ +/* + * Copyright (c) 1988 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include "flipbip_string.h" +#include +#include +#include +// From: lib/crypto +#include +#include + +char* flipbip_strtok(char* s, const char* delim) { + static char* last; + return flipbip_strtok_r(s, delim, &last); +} +char* flipbip_strtok_r(char* s, const char* delim, char** last) { + char* spanp; + int c, sc; + char* tok; + if(s == NULL && (s = *last) == NULL) return (NULL); + /* + * Skip (span) leading delimiters (s += strspn(s, delim), sort of). + */ +cont: + c = *s++; + for(spanp = (char*)delim; (sc = *spanp++) != 0;) { + if(c == sc) goto cont; + } + if(c == 0) { /* no non-delimiter characters */ + *last = NULL; + return (NULL); + } + tok = s - 1; + /* + * Scan token (scan for delimiters: s += strcspn(s, delim), sort of). + * Note that delim must have one NUL; we stop if we see that, too. + */ + for(;;) { + c = *s++; + spanp = (char*)delim; + do { + if((sc = *spanp++) == c) { + if(c == 0) + s = NULL; + else + s[-1] = 0; + *last = s; + return (tok); + } + } while(sc != 0); + } + /* NOTREACHED */ +} + +void flipbip_btox(const unsigned char* in, int in_len, char* str) { + for(int i = 0; i < in_len; i++) { + unsigned char n; + unsigned char x = in[i]; + + str += 2; + *(str + (i * 2)) = '\0'; + + for(n = 2; n != 0; --n) { + *(--str + (i * 2)) = "0123456789abcdef"[x & 0x0F]; + x >>= 4; + } + } +} +void flipbip_xtob(const char* str, unsigned char* out, int out_len) { + int len = strlen(str) / 2; + if(len > out_len) len = out_len; + for(int i = 0; i < len; i++) { + char c = 0; + if(str[i * 2] >= '0' && str[i * 2] <= '9') c += (str[i * 2] - '0') << 4; + if((str[i * 2] & ~0x20) >= 'A' && (str[i * 2] & ~0x20) <= 'F') + c += (10 + (str[i * 2] & ~0x20) - 'A') << 4; + if(str[i * 2 + 1] >= '0' && str[i * 2 + 1] <= '9') c += (str[i * 2 + 1] - '0'); + if((str[i * 2 + 1] & ~0x20) >= 'A' && (str[i * 2 + 1] & ~0x20) <= 'F') + c += (10 + (str[i * 2 + 1] & ~0x20) - 'A'); + out[i] = c; + } +} + +void flipbip_cipher( + const unsigned char* key_in, + const unsigned int key_len, + const char* in, + char* out, + const unsigned int io_len) { + if(io_len > 512) return; + + RC4_CTX ctx; + uint8_t buf[256]; + memzero(buf, 256); + + flipbip_xtob(in, buf, io_len / 2); + + rc4_init(&ctx, key_in, key_len); + rc4_encrypt(&ctx, buf, 256); + + flipbip_btox(buf, io_len / 2, out); + + memzero(buf, 256); +} \ No newline at end of file diff --git a/applications/external/flipbip/helpers/flipbip_string.h b/applications/external/flipbip/helpers/flipbip_string.h new file mode 100644 index 0000000000..a66bf7572b --- /dev/null +++ b/applications/external/flipbip/helpers/flipbip_string.h @@ -0,0 +1,12 @@ +char* flipbip_strtok(char* s, const char* delim); +char* flipbip_strtok_r(char* s, const char* delim, char** last); + +void flipbip_btox(const unsigned char* in, int in_len, char* str); +void flipbip_xtob(const char* str, unsigned char* out, int out_len); + +void flipbip_cipher( + const unsigned char* key_in, + const unsigned int key_len, + const char* in, + char* out, + const unsigned int io_len); \ No newline at end of file diff --git a/applications/external/flipbip/icons/ButtonDown_10x5.png b/applications/external/flipbip/icons/ButtonDown_10x5.png new file mode 100644 index 0000000000..b492b926c4 Binary files /dev/null and b/applications/external/flipbip/icons/ButtonDown_10x5.png differ diff --git a/applications/external/flipbip/icons/ButtonUp_10x5.png b/applications/external/flipbip/icons/ButtonUp_10x5.png new file mode 100644 index 0000000000..5da99d01ee Binary files /dev/null and b/applications/external/flipbip/icons/ButtonUp_10x5.png differ diff --git a/applications/external/flipbip/lib/crypto/AUTHORS b/applications/external/flipbip/lib/crypto/AUTHORS new file mode 100644 index 0000000000..51c9bdab08 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/AUTHORS @@ -0,0 +1,2 @@ +Tomas Dzetkulic +Pavol Rusnak diff --git a/applications/external/flipbip/lib/crypto/CONTRIBUTORS b/applications/external/flipbip/lib/crypto/CONTRIBUTORS new file mode 100644 index 0000000000..300b9788d2 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/CONTRIBUTORS @@ -0,0 +1,16 @@ +Tomas Dzetkulic +Pavol Rusnak +Jochen Hoenicke +Dustin Laurence +Ondrej Mikle +Roman Zeyde +Alex Beregszaszi +netanelkl +Jan Pochyla +Ondrej Mikle +Josh Billings +Adam Mackler +Oleg Andreev +mog +John Dvorak +Christian Reitter diff --git a/applications/external/flipbip/lib/crypto/LICENSE b/applications/external/flipbip/lib/crypto/LICENSE new file mode 100644 index 0000000000..1ea1df7037 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2013 Tomas Dzetkulic +Copyright (c) 2013 Pavol Rusnak + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/applications/external/flipbip/lib/crypto/Makefile b/applications/external/flipbip/lib/crypto/Makefile new file mode 100644 index 0000000000..2bdfc94172 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/Makefile @@ -0,0 +1,187 @@ +# CLANG_VERSION is empty if the compiler is not clang-based +CLANG_VERSION = $(shell $(CC) --version | sed -nr 's/^.*clang version ([0-9.]+).*$$/\1/p') +CLANG_VERSION_MAJOR = $(shell echo $(CLANG_VERSION) | cut -f1 -d.) + +# determine specific version ranges +ifneq ($(CLANG_VERSION),) +$(if $(shell [ $(CLANG_VERSION_MAJOR) -ge 13 ] && echo "OK"), \ + $(eval CLANG_AT_LEAST_13 := true), \ + $(eval CLANG_AT_LEAST_13 := false)) +endif + +ifeq ($(FUZZER),1) +CC ?= clang +LD ?= $(CC) +SANFLAGS += -fsanitize=fuzzer + +# only clang versions >= 13 support this feature +ifeq ($(CLANG_AT_LEAST_13),true) +$(info "info: using -fsanitize-ignorelist") +SANFLAGS += -fsanitize-ignorelist=fuzzer/sanitizer_ignorelist.txt +else +$(info "info: not using -fsanitize-ignorelist") +endif + +# TODO is there a better solution, for example by disabling a specific optimization technique? +# there is a clang optimization issue in relation with the blake2 code at -fsanitize=undefined +$(warning "warning: disabling optimization on blake2 code as workaround") +blake2b.o: OPTFLAGS += -O0 +blake2s.o: OPTFLAGS += -O0 + +else ifeq ($(ADDRESS_SANITIZER),1) +SANFLAGS += -fsanitize=address,undefined +endif + +CC ?= cc + +OPTFLAGS ?= -O3 -g + +CFLAGS += $(OPTFLAGS) \ + $(SANFLAGS) \ + -std=gnu99 \ + -W \ + -Wall \ + -Wextra \ + -Wimplicit-function-declaration \ + -Wredundant-decls \ + -Wstrict-prototypes \ + -Wundef \ + -Wshadow \ + -Wpointer-arith \ + -Wformat \ + -Wreturn-type \ + -Wsign-compare \ + -Wmultichar \ + -Wformat-nonliteral \ + -Winit-self \ + -Wuninitialized \ + -Wformat-security \ + -Wno-missing-braces \ + -Werror + +ZKP_CFLAGS = \ + -DECMULT_GEN_PREC_BITS=4 \ + -DECMULT_WINDOW_SIZE=8 \ + -DENABLE_MODULE_GENERATOR \ + -DENABLE_MODULE_RECOVERY \ + -DENABLE_MODULE_SCHNORRSIG \ + -DENABLE_MODULE_EXTRAKEYS +ZKP_PATH = ../vendor/secp256k1-zkp +# this is specific for 64-bit builds +CFLAGS += -DSECP256K1_CONTEXT_SIZE=208 + +VALGRIND ?= 1 +ifeq ($(VALGRIND),1) +CFLAGS += -DVALGRIND +endif + +CFLAGS += -I. +CFLAGS += -I.. +CFLAGS += -DUSE_ETHEREUM=1 +CFLAGS += -DUSE_KECCAK=1 +CFLAGS += -DUSE_MONERO=1 +CFLAGS += -DUSE_NEM=1 +CFLAGS += -DUSE_CARDANO=1 +CFLAGS += $(shell pkg-config --cflags openssl) + +# disable certain optimizations and features when small footprint is required +ifdef SMALL +CFLAGS += -DUSE_PRECOMPUTED_CP=0 +endif + +SRCS = bignum.c ecdsa.c curves.c secp256k1.c nist256p1.c rand.c hmac.c bip32.c bip39.c bip39_english.c pbkdf2.c base58.c base32.c +SRCS += address.c +SRCS += script.c +SRCS += ripemd160.c +SRCS += sha2.c +SRCS += sha3.c +SRCS += hasher.c +SRCS += aes/aescrypt.c aes/aeskey.c aes/aestab.c aes/aes_modes.c +SRCS += ed25519_donna/curve25519_donna_32bit.c ed25519_donna/curve25519_donna_helpers.c ed25519_donna/modm_donna_32bit.c +SRCS += ed25519_donna/ed25519_donna_basepoint_table.c ed25519_donna/ed25519_donna_32bit_tables.c ed25519_donna/ed25519_donna_impl_base.c +SRCS += ed25519_donna/ed25519.c ed25519_donna/curve25519_donna_scalarmult_base.c ed25519_donna/ed25519_sha3.c ed25519_donna/ed25519_keccak.c +SRCS += monero/base58.c +SRCS += monero/serialize.c +SRCS += monero/xmr.c +SRCS += blake256.c +SRCS += blake2b.c blake2s.c +SRCS += chacha_drbg.c +SRCS += groestl.c +SRCS += chacha20poly1305/chacha20poly1305.c chacha20poly1305/chacha_merged.c chacha20poly1305/poly1305_donna.c chacha20poly1305/rfc7539.c +SRCS += rc4.c +SRCS += nem.c +SRCS += segwit_addr.c cash_addr.c +SRCS += memzero.c +SRCS += shamir.c +SRCS += hmac_drbg.c +SRCS += rfc6979.c +SRCS += slip39.c +SRCS += zkp_context.c +SRCS += zkp_ecdsa.c +SRCS += zkp_bip340.c +SRCS += cardano.c + +OBJS = $(SRCS:.c=.o) +OBJS += secp256k1-zkp.o +OBJS += precomputed_ecmult.o +OBJS += precomputed_ecmult_gen.o + +TESTLIBS = $(shell pkg-config --libs check) -lpthread -lm +TESTSSLLIBS = $(shell pkg-config --libs openssl) + +all: tools tests + +%.o: %.c %.h options.h + $(CC) $(CFLAGS) -o $@ -c $< + +tests: tests/test_check tests/test_openssl tests/test_speed tests/libtrezor-crypto.so tests/aestst + +tests/aestst: aes/aestst.o aes/aescrypt.o aes/aeskey.o aes/aestab.o + $(CC) $(CFLAGS) $^ -o $@ + +tests/test_check.o: tests/test_check_cardano.h tests/test_check_monero.h tests/test_check_cashaddr.h tests/test_check_segwit.h + +tests/test_check: tests/test_check.o $(OBJS) + $(CC) $(CFLAGS) tests/test_check.o $(OBJS) $(TESTLIBS) -o tests/test_check + +tests/test_speed: tests/test_speed.o $(OBJS) + $(CC) $(CFLAGS) tests/test_speed.o $(OBJS) -o tests/test_speed + +tests/test_openssl: tests/test_openssl.o $(OBJS) + $(CC) $(CFLAGS) tests/test_openssl.o $(OBJS) $(TESTSSLLIBS) -o tests/test_openssl + +tests/libtrezor-crypto.so: $(SRCS) secp256k1-zkp.o precomputed_ecmult.o precomputed_ecmult_gen.o + $(CC) $(CFLAGS) -DAES_128 -DAES_192 -fPIC -shared $(SRCS) secp256k1-zkp.o precomputed_ecmult.o precomputed_ecmult_gen.o -o tests/libtrezor-crypto.so + +tools: tools/xpubaddrgen tools/mktable tools/bip39bruteforce + +tools/xpubaddrgen: tools/xpubaddrgen.o $(OBJS) + $(CC) $(CFLAGS) tools/xpubaddrgen.o $(OBJS) -o tools/xpubaddrgen + +tools/mktable: tools/mktable.o $(OBJS) + $(CC) $(CFLAGS) tools/mktable.o $(OBJS) -o tools/mktable + +tools/bip39bruteforce: tools/bip39bruteforce.o $(OBJS) + $(CC) $(CFLAGS) tools/bip39bruteforce.o $(OBJS) -o tools/bip39bruteforce + +fuzzer: fuzzer/fuzzer.o $(OBJS) + $(CC) $(CFLAGS) fuzzer/fuzzer.o $(OBJS) -o fuzzer/fuzzer + +precomputed_ecmult.o: + $(CC) $(CFLAGS) -Wno-unused-function $(ZKP_CFLAGS) -fPIC -c $(ZKP_PATH)/src/precomputed_ecmult.c -o precomputed_ecmult.o + +precomputed_ecmult_gen.o: + $(CC) $(CFLAGS) -Wno-unused-function $(ZKP_CFLAGS) -fPIC -c $(ZKP_PATH)/src/precomputed_ecmult_gen.c -o precomputed_ecmult_gen.o + +secp256k1-zkp.o: + $(CC) $(CFLAGS) -Wno-unused-function $(ZKP_CFLAGS) -fPIC -I$(ZKP_PATH) -I$(ZKP_PATH)/src -c $(ZKP_PATH)/src/secp256k1.c -o secp256k1-zkp.o + +clean: + rm -f *.o aes/*.o chacha20poly1305/*.o ed25519_donna/*.o monero/*.o + rm -f tests/*.o tests/test_check tests/test_speed tests/test_openssl tests/libtrezor-crypto.so tests/aestst + rm -f tools/*.o tools/xpubaddrgen tools/mktable tools/bip39bruteforce + rm -f fuzzer/*.o fuzzer/fuzzer + rm -f secp256k1-zkp.o precomputed_ecmult.o precomputed_ecmult_gen.o + +clean-fuzzer: clean + rm -f crash-* fuzz-*.log slow-unit-* timeout-* diff --git a/applications/external/flipbip/lib/crypto/address.c b/applications/external/flipbip/lib/crypto/address.c new file mode 100644 index 0000000000..2e791d05f3 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/address.c @@ -0,0 +1,90 @@ +/** + * Copyright (c) 2016 Daira Hopwood + * Copyright (c) 2016 Pavol Rusnak + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "address.h" +#include "bignum.h" + +size_t address_prefix_bytes_len(uint32_t address_type) { + if(address_type <= 0xFF) return 1; + if(address_type <= 0xFFFF) return 2; + if(address_type <= 0xFFFFFF) return 3; + return 4; +} + +void address_write_prefix_bytes(uint32_t address_type, uint8_t* out) { + if(address_type > 0xFFFFFF) *(out++) = address_type >> 24; + if(address_type > 0xFFFF) *(out++) = (address_type >> 16) & 0xFF; + if(address_type > 0xFF) *(out++) = (address_type >> 8) & 0xFF; + *(out++) = address_type & 0xFF; +} + +bool address_check_prefix(const uint8_t* addr, uint32_t address_type) { + if(address_type <= 0xFF) { + return address_type == (uint32_t)(addr[0]); + } + if(address_type <= 0xFFFF) { + return address_type == (((uint32_t)addr[0] << 8) | ((uint32_t)addr[1])); + } + if(address_type <= 0xFFFFFF) { + return address_type == + (((uint32_t)addr[0] << 16) | ((uint32_t)addr[1] << 8) | ((uint32_t)addr[2])); + } + return address_type == (((uint32_t)addr[0] << 24) | ((uint32_t)addr[1] << 16) | + ((uint32_t)addr[2] << 8) | ((uint32_t)addr[3])); +} + +#if USE_ETHEREUM +#include "sha3.h" + +void ethereum_address_checksum(const uint8_t* addr, char* address, bool rskip60, uint64_t chain_id) { + const char* hex = "0123456789abcdef"; + address[0] = '0'; + address[1] = 'x'; + for(int i = 0; i < 20; i++) { + address[2 + i * 2] = hex[(addr[i] >> 4) & 0xF]; + address[2 + i * 2 + 1] = hex[addr[i] & 0xF]; + } + address[42] = 0; + + SHA3_CTX ctx = {0}; + uint8_t hash[32] = {0}; + keccak_256_Init(&ctx); + if(rskip60) { + char prefix[16] = {0}; + int prefix_size = + bn_format_uint64(chain_id, NULL, "0x", 0, 0, false, 0, prefix, sizeof(prefix)); + keccak_Update(&ctx, (const uint8_t*)prefix, prefix_size); + } + keccak_Update(&ctx, (const uint8_t*)(address + 2), 40); + keccak_Final(&ctx, hash); + + for(int i = 0; i < 20; i++) { + if((hash[i] & 0x80) && address[2 + i * 2] >= 'a' && address[2 + i * 2] <= 'f') { + address[2 + i * 2] -= 0x20; + } + if((hash[i] & 0x08) && address[2 + i * 2 + 1] >= 'a' && address[2 + i * 2 + 1] <= 'f') { + address[2 + i * 2 + 1] -= 0x20; + } + } +} +#endif diff --git a/applications/external/flipbip/lib/crypto/address.h b/applications/external/flipbip/lib/crypto/address.h new file mode 100644 index 0000000000..e2767d6b0b --- /dev/null +++ b/applications/external/flipbip/lib/crypto/address.h @@ -0,0 +1,39 @@ +/** + * Copyright (c) 2016 Daira Hopwood + * Copyright (c) 2016 Pavol Rusnak + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __ADDRESS_H__ +#define __ADDRESS_H__ + +#include +#include +#include +#include "options.h" + +size_t address_prefix_bytes_len(uint32_t address_type); +void address_write_prefix_bytes(uint32_t address_type, uint8_t* out); +bool address_check_prefix(const uint8_t* addr, uint32_t address_type); +#if USE_ETHEREUM +void ethereum_address_checksum(const uint8_t* addr, char* address, bool rskip60, uint64_t chain_id); +#endif + +#endif diff --git a/applications/external/flipbip/lib/crypto/aes/aes.h b/applications/external/flipbip/lib/crypto/aes/aes.h new file mode 100644 index 0000000000..b277c390d2 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/aes/aes.h @@ -0,0 +1,256 @@ +/* +--------------------------------------------------------------------------- +Copyright (c) 1998-2013, Brian Gladman, Worcester, UK. All rights reserved. + +The redistribution and use of this software (with or without changes) +is allowed without the payment of fees or royalties provided that: + + source code distributions include the above copyright notice, this + list of conditions and the following disclaimer; + + binary distributions include the above copyright notice, this list + of conditions and the following disclaimer in their documentation. + +This software is provided 'as is' with no explicit or implied warranties +in respect of its operation, including, but not limited to, correctness +and fitness for purpose. +--------------------------------------------------------------------------- +Issue Date: 02/08/2018 + + This file contains the definitions required to use AES in C. See aesopt.h + for optimisation details. +*/ + +#ifndef _AES_H +#define _AES_H + +#include +#include + +#define VOID_RETURN void +#define INT_RETURN int +#define ALIGN_OFFSET(x, n) (((intptr_t)(x)) & ((n)-1)) +#define ALIGN_FLOOR(x, n) ((uint8_t*)(x) - (((intptr_t)(x)) & ((n)-1))) +#define ALIGN_CEIL(x, n) ((uint8_t*)(x) + (-((intptr_t)(x)) & ((n)-1))) + +#if defined(__cplusplus) +extern "C" { +#endif + +// #define AES_128 /* if a fast 128 bit key scheduler is needed */ +// #define AES_192 /* if a fast 192 bit key scheduler is needed */ +#define AES_256 /* if a fast 256 bit key scheduler is needed */ +// #define AES_VAR /* if variable key size scheduler is needed */ +#if 1 +#define AES_MODES /* if support is needed for modes in the C code */ +#endif /* (these will use AES_NI if it is present) */ +#if 0 /* add this to make direct calls to the AES_NI */ +#/* implemented CBC and CTR modes available */ +#define ADD_AESNI_MODE_CALLS +#endif + +/* The following must also be set in assembler files if being used */ + +#define AES_ENCRYPT /* if support for encryption is needed */ +#define AES_DECRYPT /* if support for decryption is needed */ + +#define AES_BLOCK_SIZE_P2 4 /* AES block size as a power of 2 */ +#define AES_BLOCK_SIZE (1 << AES_BLOCK_SIZE_P2) /* AES block size */ +#define N_COLS 4 /* the number of columns in the state */ + +/* The key schedule length is 11, 13 or 15 16-byte blocks for 128, */ +/* 192 or 256-bit keys respectively. That is 176, 208 or 240 bytes */ +/* or 44, 52 or 60 32-bit words. */ + +#if defined(AES_VAR) || defined(AES_256) +#define KS_LENGTH 60 +#elif defined(AES_192) +#define KS_LENGTH 52 +#else +#define KS_LENGTH 44 +#endif + +#define AES_RETURN INT_RETURN + +/* the character array 'inf' in the following structures is used */ +/* to hold AES context information. This AES code uses cx->inf.b[0] */ +/* to hold the number of rounds multiplied by 16. The other three */ +/* elements can be used by code that implements additional modes */ + +typedef union { + uint32_t l; + uint8_t b[4]; +} aes_inf; + +#ifdef _MSC_VER +#pragma warning(disable : 4324) +#endif + +#if defined(_MSC_VER) && defined(_WIN64) +#define ALIGNED_(x) __declspec(align(x)) +#elif defined(__GNUC__) && defined(__x86_64__) +#define ALIGNED_(x) __attribute__((aligned(x))) +#else +#define ALIGNED_(x) +#endif + +typedef struct ALIGNED_(16) { + uint32_t ks[KS_LENGTH]; + aes_inf inf; +} aes_encrypt_ctx; + +typedef struct ALIGNED_(16) { + uint32_t ks[KS_LENGTH]; + aes_inf inf; +} aes_decrypt_ctx; + +#ifdef _MSC_VER +#pragma warning(default : 4324) +#endif + +/* This routine must be called before first use if non-static */ +/* tables are being used */ + +AES_RETURN aes_init(void); + +/* Key lengths in the range 16 <= key_len <= 32 are given in bytes, */ +/* those in the range 128 <= key_len <= 256 are given in bits */ + +#if defined(AES_ENCRYPT) + +#if defined(AES_128) || defined(AES_VAR) +AES_RETURN aes_encrypt_key128(const unsigned char* key, aes_encrypt_ctx cx[1]); +#endif + +#if defined(AES_192) || defined(AES_VAR) +AES_RETURN aes_encrypt_key192(const unsigned char* key, aes_encrypt_ctx cx[1]); +#endif + +#if defined(AES_256) || defined(AES_VAR) +AES_RETURN aes_encrypt_key256(const unsigned char* key, aes_encrypt_ctx cx[1]); +#endif + +#if defined(AES_VAR) +AES_RETURN aes_encrypt_key(const unsigned char* key, int key_len, aes_encrypt_ctx cx[1]); +#endif + +AES_RETURN aes_encrypt(const unsigned char* in, unsigned char* out, const aes_encrypt_ctx cx[1]); + +#endif + +#if defined(AES_DECRYPT) + +#if defined(AES_128) || defined(AES_VAR) +AES_RETURN aes_decrypt_key128(const unsigned char* key, aes_decrypt_ctx cx[1]); +#endif + +#if defined(AES_192) || defined(AES_VAR) +AES_RETURN aes_decrypt_key192(const unsigned char* key, aes_decrypt_ctx cx[1]); +#endif + +#if defined(AES_256) || defined(AES_VAR) +AES_RETURN aes_decrypt_key256(const unsigned char* key, aes_decrypt_ctx cx[1]); +#endif + +#if defined(AES_VAR) +AES_RETURN aes_decrypt_key(const unsigned char* key, int key_len, aes_decrypt_ctx cx[1]); +#endif + +AES_RETURN aes_decrypt(const unsigned char* in, unsigned char* out, const aes_decrypt_ctx cx[1]); + +#endif + +#if defined(AES_MODES) + +/* Multiple calls to the following subroutines for multiple block */ +/* ECB, CBC, CFB, OFB and CTR mode encryption can be used to handle */ +/* long messages incrementally provided that the context AND the iv */ +/* are preserved between all such calls. For the ECB and CBC modes */ +/* each individual call within a series of incremental calls must */ +/* process only full blocks (i.e. len must be a multiple of 16) but */ +/* the CFB, OFB and CTR mode calls can handle multiple incremental */ +/* calls of any length. Each mode is reset when a new AES key is */ +/* set but ECB needs no reset and CBC can be reset without setting */ +/* a new key by setting a new IV value. To reset CFB, OFB and CTR */ +/* without setting the key, aes_mode_reset() must be called and the */ +/* IV must be set. NOTE: All these calls update the IV on exit so */ +/* this has to be reset if a new operation with the same IV as the */ +/* previous one is required (or decryption follows encryption with */ +/* the same IV array). */ + +AES_RETURN aes_test_alignment_detection(unsigned int n); + +AES_RETURN aes_ecb_encrypt( + const unsigned char* ibuf, + unsigned char* obuf, + int len, + const aes_encrypt_ctx cx[1]); + +AES_RETURN aes_ecb_decrypt( + const unsigned char* ibuf, + unsigned char* obuf, + int len, + const aes_decrypt_ctx cx[1]); + +AES_RETURN aes_cbc_encrypt( + const unsigned char* ibuf, + unsigned char* obuf, + int len, + unsigned char* iv, + const aes_encrypt_ctx cx[1]); + +AES_RETURN aes_cbc_decrypt( + const unsigned char* ibuf, + unsigned char* obuf, + int len, + unsigned char* iv, + const aes_decrypt_ctx cx[1]); + +AES_RETURN aes_mode_reset(aes_encrypt_ctx cx[1]); + +AES_RETURN aes_cfb_encrypt( + const unsigned char* ibuf, + unsigned char* obuf, + int len, + unsigned char* iv, + aes_encrypt_ctx cx[1]); + +AES_RETURN aes_cfb_decrypt( + const unsigned char* ibuf, + unsigned char* obuf, + int len, + unsigned char* iv, + aes_encrypt_ctx cx[1]); + +#define aes_ofb_encrypt aes_ofb_crypt +#define aes_ofb_decrypt aes_ofb_crypt + +AES_RETURN aes_ofb_crypt( + const unsigned char* ibuf, + unsigned char* obuf, + int len, + unsigned char* iv, + aes_encrypt_ctx cx[1]); + +typedef void cbuf_inc(unsigned char* cbuf); + +#define aes_ctr_encrypt aes_ctr_crypt +#define aes_ctr_decrypt aes_ctr_crypt + +AES_RETURN aes_ctr_crypt( + const unsigned char* ibuf, + unsigned char* obuf, + int len, + unsigned char* cbuf, + cbuf_inc ctr_inc, + aes_encrypt_ctx cx[1]); + +void aes_ctr_cbuf_inc(unsigned char* cbuf); + +#endif + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/applications/external/flipbip/lib/crypto/aes/aes_modes.c b/applications/external/flipbip/lib/crypto/aes/aes_modes.c new file mode 100644 index 0000000000..7e6b7eb34a --- /dev/null +++ b/applications/external/flipbip/lib/crypto/aes/aes_modes.c @@ -0,0 +1,932 @@ +/* +--------------------------------------------------------------------------- +Copyright (c) 1998-2013, Brian Gladman, Worcester, UK. All rights reserved. + +The redistribution and use of this software (with or without changes) +is allowed without the payment of fees or royalties provided that: + + source code distributions include the above copyright notice, this + list of conditions and the following disclaimer; + + binary distributions include the above copyright notice, this list + of conditions and the following disclaimer in their documentation. + +This software is provided 'as is' with no explicit or implied warranties +in respect of its operation, including, but not limited to, correctness +and fitness for purpose. +--------------------------------------------------------------------------- +Issue Date: 20/12/2007 + + These subroutines implement multiple block AES modes for ECB, CBC, CFB, + OFB and CTR encryption, The code provides support for the VIA Advanced + Cryptography Engine (ACE). + + NOTE: In the following subroutines, the AES contexts (ctx) must be + 16 byte aligned if VIA ACE is being used +*/ + +#include +#include +#include + +#include "aesopt.h" + +#if defined(AES_MODES) +#if defined(__cplusplus) +extern "C" { +#endif + +#if defined(_MSC_VER) && (_MSC_VER > 800) +#pragma intrinsic(memcpy) +#endif + +#define BFR_BLOCKS 8 + +/* These values are used to detect long word alignment in order to */ +/* speed up some buffer operations. This facility may not work on */ +/* some machines so this define can be commented out if necessary */ + +#define FAST_BUFFER_OPERATIONS + +#define lp32(x) ((uint32_t*)(x)) + +#if defined(USE_VIA_ACE_IF_PRESENT) + +#include "aes_via_ace.h" + +#pragma pack(16) + +aligned_array(unsigned long, enc_gen_table, 12, 16) = NEH_ENC_GEN_DATA; +aligned_array(unsigned long, enc_load_table, 12, 16) = NEH_ENC_LOAD_DATA; +aligned_array(unsigned long, enc_hybrid_table, 12, 16) = NEH_ENC_HYBRID_DATA; +aligned_array(unsigned long, dec_gen_table, 12, 16) = NEH_DEC_GEN_DATA; +aligned_array(unsigned long, dec_load_table, 12, 16) = NEH_DEC_LOAD_DATA; +aligned_array(unsigned long, dec_hybrid_table, 12, 16) = NEH_DEC_HYBRID_DATA; + +/* NOTE: These control word macros must only be used after */ +/* a key has been set up because they depend on key size */ +/* See the VIA ACE documentation for key type information */ +/* and aes_via_ace.h for non-default NEH_KEY_TYPE values */ + +#ifndef NEH_KEY_TYPE +#define NEH_KEY_TYPE NEH_HYBRID +#endif + +#if NEH_KEY_TYPE == NEH_LOAD +#define kd_adr(c) ((uint8_t*)(c)->ks) +#elif NEH_KEY_TYPE == NEH_GENERATE +#define kd_adr(c) ((uint8_t*)(c)->ks + (c)->inf.b[0]) +#elif NEH_KEY_TYPE == NEH_HYBRID +#define kd_adr(c) ((uint8_t*)(c)->ks + ((c)->inf.b[0] == 160 ? 160 : 0)) +#else +#error no key type defined for VIA ACE +#endif + +#else + +#define aligned_array(type, name, no, stride) type name[no] +#define aligned_auto(type, name, no, stride) type name[no] + +#endif + +#if defined(_MSC_VER) && _MSC_VER > 1200 + +#define via_cwd(cwd, ty, dir, len) unsigned long* cwd = (dir##_##ty##_table + ((len - 128) >> 4)) + +#else + +#define via_cwd(cwd, ty, dir, len) \ + aligned_auto(unsigned long, cwd, 4, 16); \ + cwd[1] = cwd[2] = cwd[3] = 0; \ + cwd[0] = neh_##dir##_##ty##_key(len) + +#endif + +/* test the code for detecting and setting pointer alignment */ + +AES_RETURN aes_test_alignment_detection(unsigned int n) /* 4 <= n <= 16 */ +{ + uint8_t p[16]; + uint32_t i = 0, count_eq = 0, count_neq = 0; + + if(n < 4 || n > 16) return EXIT_FAILURE; + + for(i = 0; i < n; ++i) { + uint8_t *qf = ALIGN_FLOOR(p + i, n), *qh = ALIGN_CEIL(p + i, n); + + if(qh == qf) + ++count_eq; + else if(qh == qf + n) + ++count_neq; + else + return EXIT_FAILURE; + } + return (count_eq != 1 || count_neq != n - 1 ? EXIT_FAILURE : EXIT_SUCCESS); +} + +AES_RETURN aes_mode_reset(aes_encrypt_ctx ctx[1]) { + ctx->inf.b[2] = 0; + return EXIT_SUCCESS; +} + +AES_RETURN aes_ecb_encrypt( + const unsigned char* ibuf, + unsigned char* obuf, + int len, + const aes_encrypt_ctx ctx[1]) { + int nb = len >> AES_BLOCK_SIZE_P2; + + if(len & (AES_BLOCK_SIZE - 1)) return EXIT_FAILURE; + +#if defined(USE_VIA_ACE_IF_PRESENT) + + if(ctx->inf.b[1] == 0xff) { + uint8_t* ksp = (uint8_t*)(ctx->ks); + via_cwd(cwd, hybrid, enc, 2 * ctx->inf.b[0] - 192); + + if(ALIGN_OFFSET(ctx, 16)) return EXIT_FAILURE; + + if(!ALIGN_OFFSET(ibuf, 16) && !ALIGN_OFFSET(obuf, 16)) { + via_ecb_op5(ksp, cwd, ibuf, obuf, nb); + } else { + aligned_auto(uint8_t, buf, BFR_BLOCKS * AES_BLOCK_SIZE, 16); + uint8_t *ip = NULL, *op = NULL; + + while(nb) { + int m = (nb > BFR_BLOCKS ? BFR_BLOCKS : nb); + + ip = (ALIGN_OFFSET(ibuf, 16) ? buf : ibuf); + op = (ALIGN_OFFSET(obuf, 16) ? buf : obuf); + + if(ip != ibuf) memcpy(buf, ibuf, m * AES_BLOCK_SIZE); + + via_ecb_op5(ksp, cwd, ip, op, m); + + if(op != obuf) memcpy(obuf, buf, m * AES_BLOCK_SIZE); + + ibuf += m * AES_BLOCK_SIZE; + obuf += m * AES_BLOCK_SIZE; + nb -= m; + } + } + + return EXIT_SUCCESS; + } + +#endif + +#if !defined(ASSUME_VIA_ACE_PRESENT) + while(nb--) { + if(aes_encrypt(ibuf, obuf, ctx) != EXIT_SUCCESS) return EXIT_FAILURE; + ibuf += AES_BLOCK_SIZE; + obuf += AES_BLOCK_SIZE; + } +#endif + return EXIT_SUCCESS; +} + +AES_RETURN aes_ecb_decrypt( + const unsigned char* ibuf, + unsigned char* obuf, + int len, + const aes_decrypt_ctx ctx[1]) { + int nb = len >> AES_BLOCK_SIZE_P2; + + if(len & (AES_BLOCK_SIZE - 1)) return EXIT_FAILURE; + +#if defined(USE_VIA_ACE_IF_PRESENT) + + if(ctx->inf.b[1] == 0xff) { + uint8_t* ksp = kd_adr(ctx); + via_cwd(cwd, hybrid, dec, 2 * ctx->inf.b[0] - 192); + + if(ALIGN_OFFSET(ctx, 16)) return EXIT_FAILURE; + + if(!ALIGN_OFFSET(ibuf, 16) && !ALIGN_OFFSET(obuf, 16)) { + via_ecb_op5(ksp, cwd, ibuf, obuf, nb); + } else { + aligned_auto(uint8_t, buf, BFR_BLOCKS * AES_BLOCK_SIZE, 16); + uint8_t *ip = NULL, *op = NULL; + + while(nb) { + int m = (nb > BFR_BLOCKS ? BFR_BLOCKS : nb); + + ip = (ALIGN_OFFSET(ibuf, 16) ? buf : ibuf); + op = (ALIGN_OFFSET(obuf, 16) ? buf : obuf); + + if(ip != ibuf) memcpy(buf, ibuf, m * AES_BLOCK_SIZE); + + via_ecb_op5(ksp, cwd, ip, op, m); + + if(op != obuf) memcpy(obuf, buf, m * AES_BLOCK_SIZE); + + ibuf += m * AES_BLOCK_SIZE; + obuf += m * AES_BLOCK_SIZE; + nb -= m; + } + } + + return EXIT_SUCCESS; + } + +#endif + +#if !defined(ASSUME_VIA_ACE_PRESENT) + while(nb--) { + if(aes_decrypt(ibuf, obuf, ctx) != EXIT_SUCCESS) return EXIT_FAILURE; + ibuf += AES_BLOCK_SIZE; + obuf += AES_BLOCK_SIZE; + } +#endif + return EXIT_SUCCESS; +} + +AES_RETURN aes_cbc_encrypt( + const unsigned char* ibuf, + unsigned char* obuf, + int len, + unsigned char* iv, + const aes_encrypt_ctx ctx[1]) { + int nb = len >> AES_BLOCK_SIZE_P2; + + if(len & (AES_BLOCK_SIZE - 1)) return EXIT_FAILURE; + +#if defined(USE_VIA_ACE_IF_PRESENT) + + if(ctx->inf.b[1] == 0xff) { + uint8_t *ksp = (uint8_t*)(ctx->ks), *ivp = iv; + aligned_auto(uint8_t, liv, AES_BLOCK_SIZE, 16); + via_cwd(cwd, hybrid, enc, 2 * ctx->inf.b[0] - 192); + + if(ALIGN_OFFSET(ctx, 16)) return EXIT_FAILURE; + + if(ALIGN_OFFSET(iv, 16)) /* ensure an aligned iv */ + { + ivp = liv; + memcpy(liv, iv, AES_BLOCK_SIZE); + } + + if(!ALIGN_OFFSET(ibuf, 16) && !ALIGN_OFFSET(obuf, 16) && !ALIGN_OFFSET(iv, 16)) { + via_cbc_op7(ksp, cwd, ibuf, obuf, nb, ivp, ivp); + } else { + aligned_auto(uint8_t, buf, BFR_BLOCKS * AES_BLOCK_SIZE, 16); + uint8_t *ip = NULL, *op = NULL; + + while(nb) { + int m = (nb > BFR_BLOCKS ? BFR_BLOCKS : nb); + + ip = (ALIGN_OFFSET(ibuf, 16) ? buf : ibuf); + op = (ALIGN_OFFSET(obuf, 16) ? buf : obuf); + + if(ip != ibuf) memcpy(buf, ibuf, m * AES_BLOCK_SIZE); + + via_cbc_op7(ksp, cwd, ip, op, m, ivp, ivp); + + if(op != obuf) memcpy(obuf, buf, m * AES_BLOCK_SIZE); + + ibuf += m * AES_BLOCK_SIZE; + obuf += m * AES_BLOCK_SIZE; + nb -= m; + } + } + + if(iv != ivp) memcpy(iv, ivp, AES_BLOCK_SIZE); + + return EXIT_SUCCESS; + } + +#endif + +#if !defined(ASSUME_VIA_ACE_PRESENT) +#ifdef FAST_BUFFER_OPERATIONS + if(!ALIGN_OFFSET(ibuf, 4) && !ALIGN_OFFSET(iv, 4)) + while(nb--) { + lp32(iv)[0] ^= lp32(ibuf)[0]; + lp32(iv)[1] ^= lp32(ibuf)[1]; + lp32(iv)[2] ^= lp32(ibuf)[2]; + lp32(iv)[3] ^= lp32(ibuf)[3]; + if(aes_encrypt(iv, iv, ctx) != EXIT_SUCCESS) return EXIT_FAILURE; + memcpy(obuf, iv, AES_BLOCK_SIZE); + ibuf += AES_BLOCK_SIZE; + obuf += AES_BLOCK_SIZE; + } + else +#endif + while(nb--) { + iv[0] ^= ibuf[0]; + iv[1] ^= ibuf[1]; + iv[2] ^= ibuf[2]; + iv[3] ^= ibuf[3]; + iv[4] ^= ibuf[4]; + iv[5] ^= ibuf[5]; + iv[6] ^= ibuf[6]; + iv[7] ^= ibuf[7]; + iv[8] ^= ibuf[8]; + iv[9] ^= ibuf[9]; + iv[10] ^= ibuf[10]; + iv[11] ^= ibuf[11]; + iv[12] ^= ibuf[12]; + iv[13] ^= ibuf[13]; + iv[14] ^= ibuf[14]; + iv[15] ^= ibuf[15]; + if(aes_encrypt(iv, iv, ctx) != EXIT_SUCCESS) return EXIT_FAILURE; + memcpy(obuf, iv, AES_BLOCK_SIZE); + ibuf += AES_BLOCK_SIZE; + obuf += AES_BLOCK_SIZE; + } +#endif + return EXIT_SUCCESS; +} + +AES_RETURN aes_cbc_decrypt( + const unsigned char* ibuf, + unsigned char* obuf, + int len, + unsigned char* iv, + const aes_decrypt_ctx ctx[1]) { + unsigned char tmp[AES_BLOCK_SIZE]; + int nb = len >> AES_BLOCK_SIZE_P2; + + if(len & (AES_BLOCK_SIZE - 1)) return EXIT_FAILURE; + +#if defined(USE_VIA_ACE_IF_PRESENT) + + if(ctx->inf.b[1] == 0xff) { + uint8_t *ksp = kd_adr(ctx), *ivp = iv; + aligned_auto(uint8_t, liv, AES_BLOCK_SIZE, 16); + via_cwd(cwd, hybrid, dec, 2 * ctx->inf.b[0] - 192); + + if(ALIGN_OFFSET(ctx, 16)) return EXIT_FAILURE; + + if(ALIGN_OFFSET(iv, 16)) /* ensure an aligned iv */ + { + ivp = liv; + memcpy(liv, iv, AES_BLOCK_SIZE); + } + + if(!ALIGN_OFFSET(ibuf, 16) && !ALIGN_OFFSET(obuf, 16) && !ALIGN_OFFSET(iv, 16)) { + via_cbc_op6(ksp, cwd, ibuf, obuf, nb, ivp); + } else { + aligned_auto(uint8_t, buf, BFR_BLOCKS * AES_BLOCK_SIZE, 16); + uint8_t *ip = NULL, *op = NULL; + + while(nb) { + int m = (nb > BFR_BLOCKS ? BFR_BLOCKS : nb); + + ip = (ALIGN_OFFSET(ibuf, 16) ? buf : ibuf); + op = (ALIGN_OFFSET(obuf, 16) ? buf : obuf); + + if(ip != ibuf) memcpy(buf, ibuf, m * AES_BLOCK_SIZE); + + via_cbc_op6(ksp, cwd, ip, op, m, ivp); + + if(op != obuf) memcpy(obuf, buf, m * AES_BLOCK_SIZE); + + ibuf += m * AES_BLOCK_SIZE; + obuf += m * AES_BLOCK_SIZE; + nb -= m; + } + } + + if(iv != ivp) memcpy(iv, ivp, AES_BLOCK_SIZE); + + return EXIT_SUCCESS; + } +#endif + +#if !defined(ASSUME_VIA_ACE_PRESENT) +#ifdef FAST_BUFFER_OPERATIONS + if(!ALIGN_OFFSET(obuf, 4) && !ALIGN_OFFSET(iv, 4)) + while(nb--) { + memcpy(tmp, ibuf, AES_BLOCK_SIZE); + if(aes_decrypt(ibuf, obuf, ctx) != EXIT_SUCCESS) return EXIT_FAILURE; + lp32(obuf)[0] ^= lp32(iv)[0]; + lp32(obuf)[1] ^= lp32(iv)[1]; + lp32(obuf)[2] ^= lp32(iv)[2]; + lp32(obuf)[3] ^= lp32(iv)[3]; + memcpy(iv, tmp, AES_BLOCK_SIZE); + ibuf += AES_BLOCK_SIZE; + obuf += AES_BLOCK_SIZE; + } + else +#endif + while(nb--) { + memcpy(tmp, ibuf, AES_BLOCK_SIZE); + if(aes_decrypt(ibuf, obuf, ctx) != EXIT_SUCCESS) return EXIT_FAILURE; + obuf[0] ^= iv[0]; + obuf[1] ^= iv[1]; + obuf[2] ^= iv[2]; + obuf[3] ^= iv[3]; + obuf[4] ^= iv[4]; + obuf[5] ^= iv[5]; + obuf[6] ^= iv[6]; + obuf[7] ^= iv[7]; + obuf[8] ^= iv[8]; + obuf[9] ^= iv[9]; + obuf[10] ^= iv[10]; + obuf[11] ^= iv[11]; + obuf[12] ^= iv[12]; + obuf[13] ^= iv[13]; + obuf[14] ^= iv[14]; + obuf[15] ^= iv[15]; + memcpy(iv, tmp, AES_BLOCK_SIZE); + ibuf += AES_BLOCK_SIZE; + obuf += AES_BLOCK_SIZE; + } +#endif + return EXIT_SUCCESS; +} + +AES_RETURN aes_cfb_encrypt( + const unsigned char* ibuf, + unsigned char* obuf, + int len, + unsigned char* iv, + aes_encrypt_ctx ctx[1]) { + int cnt = 0, b_pos = (int)ctx->inf.b[2], nb; + + if(b_pos) /* complete any partial block */ + { + while(b_pos < AES_BLOCK_SIZE && cnt < len) { + *obuf++ = (iv[b_pos++] ^= *ibuf++); + cnt++; + } + + b_pos = (b_pos == AES_BLOCK_SIZE ? 0 : b_pos); + } + + if((nb = (len - cnt) >> AES_BLOCK_SIZE_P2) != 0) /* process whole blocks */ + { +#if defined(USE_VIA_ACE_IF_PRESENT) + + if(ctx->inf.b[1] == 0xff) { + int m; + uint8_t *ksp = (uint8_t*)(ctx->ks), *ivp = iv; + aligned_auto(uint8_t, liv, AES_BLOCK_SIZE, 16); + via_cwd(cwd, hybrid, enc, 2 * ctx->inf.b[0] - 192); + + if(ALIGN_OFFSET(ctx, 16)) return EXIT_FAILURE; + + if(ALIGN_OFFSET(iv, 16)) /* ensure an aligned iv */ + { + ivp = liv; + memcpy(liv, iv, AES_BLOCK_SIZE); + } + + if(!ALIGN_OFFSET(ibuf, 16) && !ALIGN_OFFSET(obuf, 16)) { + via_cfb_op7(ksp, cwd, ibuf, obuf, nb, ivp, ivp); + ibuf += nb * AES_BLOCK_SIZE; + obuf += nb * AES_BLOCK_SIZE; + cnt += nb * AES_BLOCK_SIZE; + } else /* input, output or both are unaligned */ + { + aligned_auto(uint8_t, buf, BFR_BLOCKS * AES_BLOCK_SIZE, 16); + uint8_t *ip = NULL, *op = NULL; + + while(nb) { + m = (nb > BFR_BLOCKS ? BFR_BLOCKS : nb), nb -= m; + + ip = (ALIGN_OFFSET(ibuf, 16) ? buf : ibuf); + op = (ALIGN_OFFSET(obuf, 16) ? buf : obuf); + + if(ip != ibuf) memcpy(buf, ibuf, m * AES_BLOCK_SIZE); + + via_cfb_op7(ksp, cwd, ip, op, m, ivp, ivp); + + if(op != obuf) memcpy(obuf, buf, m * AES_BLOCK_SIZE); + + ibuf += m * AES_BLOCK_SIZE; + obuf += m * AES_BLOCK_SIZE; + cnt += m * AES_BLOCK_SIZE; + } + } + + if(ivp != iv) memcpy(iv, ivp, AES_BLOCK_SIZE); + } +#else +#ifdef FAST_BUFFER_OPERATIONS + if(!ALIGN_OFFSET(ibuf, 4) && !ALIGN_OFFSET(obuf, 4) && !ALIGN_OFFSET(iv, 4)) + while(cnt + AES_BLOCK_SIZE <= len) { + assert(b_pos == 0); + if(aes_encrypt(iv, iv, ctx) != EXIT_SUCCESS) return EXIT_FAILURE; + lp32(obuf)[0] = lp32(iv)[0] ^= lp32(ibuf)[0]; + lp32(obuf)[1] = lp32(iv)[1] ^= lp32(ibuf)[1]; + lp32(obuf)[2] = lp32(iv)[2] ^= lp32(ibuf)[2]; + lp32(obuf)[3] = lp32(iv)[3] ^= lp32(ibuf)[3]; + ibuf += AES_BLOCK_SIZE; + obuf += AES_BLOCK_SIZE; + cnt += AES_BLOCK_SIZE; + } + else +#endif + while(cnt + AES_BLOCK_SIZE <= len) { + assert(b_pos == 0); + if(aes_encrypt(iv, iv, ctx) != EXIT_SUCCESS) return EXIT_FAILURE; + obuf[0] = iv[0] ^= ibuf[0]; + obuf[1] = iv[1] ^= ibuf[1]; + obuf[2] = iv[2] ^= ibuf[2]; + obuf[3] = iv[3] ^= ibuf[3]; + obuf[4] = iv[4] ^= ibuf[4]; + obuf[5] = iv[5] ^= ibuf[5]; + obuf[6] = iv[6] ^= ibuf[6]; + obuf[7] = iv[7] ^= ibuf[7]; + obuf[8] = iv[8] ^= ibuf[8]; + obuf[9] = iv[9] ^= ibuf[9]; + obuf[10] = iv[10] ^= ibuf[10]; + obuf[11] = iv[11] ^= ibuf[11]; + obuf[12] = iv[12] ^= ibuf[12]; + obuf[13] = iv[13] ^= ibuf[13]; + obuf[14] = iv[14] ^= ibuf[14]; + obuf[15] = iv[15] ^= ibuf[15]; + ibuf += AES_BLOCK_SIZE; + obuf += AES_BLOCK_SIZE; + cnt += AES_BLOCK_SIZE; + } +#endif + } + + while(cnt < len) { + if(!b_pos && aes_encrypt(iv, iv, ctx) != EXIT_SUCCESS) return EXIT_FAILURE; + + while(cnt < len && b_pos < AES_BLOCK_SIZE) { + *obuf++ = (iv[b_pos++] ^= *ibuf++); + cnt++; + } + + b_pos = (b_pos == AES_BLOCK_SIZE ? 0 : b_pos); + } + + ctx->inf.b[2] = (uint8_t)b_pos; + return EXIT_SUCCESS; +} + +AES_RETURN aes_cfb_decrypt( + const unsigned char* ibuf, + unsigned char* obuf, + int len, + unsigned char* iv, + aes_encrypt_ctx ctx[1]) { + int cnt = 0, b_pos = (int)ctx->inf.b[2], nb; + + if(b_pos) /* complete any partial block */ + { + uint8_t t; + + while(b_pos < AES_BLOCK_SIZE && cnt < len) { + t = *ibuf++; + *obuf++ = t ^ iv[b_pos]; + iv[b_pos++] = t; + cnt++; + } + + b_pos = (b_pos == AES_BLOCK_SIZE ? 0 : b_pos); + } + + if((nb = (len - cnt) >> AES_BLOCK_SIZE_P2) != 0) /* process whole blocks */ + { +#if defined(USE_VIA_ACE_IF_PRESENT) + + if(ctx->inf.b[1] == 0xff) { + int m; + uint8_t *ksp = (uint8_t*)(ctx->ks), *ivp = iv; + aligned_auto(uint8_t, liv, AES_BLOCK_SIZE, 16); + via_cwd(cwd, hybrid, dec, 2 * ctx->inf.b[0] - 192); + + if(ALIGN_OFFSET(ctx, 16)) return EXIT_FAILURE; + + if(ALIGN_OFFSET(iv, 16)) /* ensure an aligned iv */ + { + ivp = liv; + memcpy(liv, iv, AES_BLOCK_SIZE); + } + + if(!ALIGN_OFFSET(ibuf, 16) && !ALIGN_OFFSET(obuf, 16)) { + via_cfb_op6(ksp, cwd, ibuf, obuf, nb, ivp); + ibuf += nb * AES_BLOCK_SIZE; + obuf += nb * AES_BLOCK_SIZE; + cnt += nb * AES_BLOCK_SIZE; + } else /* input, output or both are unaligned */ + { + aligned_auto(uint8_t, buf, BFR_BLOCKS * AES_BLOCK_SIZE, 16); + uint8_t *ip = NULL, *op = NULL; + + while(nb) { + m = (nb > BFR_BLOCKS ? BFR_BLOCKS : nb), nb -= m; + + ip = (ALIGN_OFFSET(ibuf, 16) ? buf : ibuf); + op = (ALIGN_OFFSET(obuf, 16) ? buf : obuf); + + if(ip != ibuf) /* input buffer is not aligned */ + memcpy(buf, ibuf, m * AES_BLOCK_SIZE); + + via_cfb_op6(ksp, cwd, ip, op, m, ivp); + + if(op != obuf) /* output buffer is not aligned */ + memcpy(obuf, buf, m * AES_BLOCK_SIZE); + + ibuf += m * AES_BLOCK_SIZE; + obuf += m * AES_BLOCK_SIZE; + cnt += m * AES_BLOCK_SIZE; + } + } + + if(ivp != iv) memcpy(iv, ivp, AES_BLOCK_SIZE); + } +#else +#ifdef FAST_BUFFER_OPERATIONS + if(!ALIGN_OFFSET(ibuf, 4) && !ALIGN_OFFSET(obuf, 4) && !ALIGN_OFFSET(iv, 4)) + while(cnt + AES_BLOCK_SIZE <= len) { + uint32_t t; + + assert(b_pos == 0); + if(aes_encrypt(iv, iv, ctx) != EXIT_SUCCESS) return EXIT_FAILURE; + t = lp32(ibuf)[0], lp32(obuf)[0] = t ^ lp32(iv)[0], lp32(iv)[0] = t; + t = lp32(ibuf)[1], lp32(obuf)[1] = t ^ lp32(iv)[1], lp32(iv)[1] = t; + t = lp32(ibuf)[2], lp32(obuf)[2] = t ^ lp32(iv)[2], lp32(iv)[2] = t; + t = lp32(ibuf)[3], lp32(obuf)[3] = t ^ lp32(iv)[3], lp32(iv)[3] = t; + ibuf += AES_BLOCK_SIZE; + obuf += AES_BLOCK_SIZE; + cnt += AES_BLOCK_SIZE; + } + else +#endif + while(cnt + AES_BLOCK_SIZE <= len) { + uint8_t t; + + assert(b_pos == 0); + if(aes_encrypt(iv, iv, ctx) != EXIT_SUCCESS) return EXIT_FAILURE; + t = ibuf[0], obuf[0] = t ^ iv[0], iv[0] = t; + t = ibuf[1], obuf[1] = t ^ iv[1], iv[1] = t; + t = ibuf[2], obuf[2] = t ^ iv[2], iv[2] = t; + t = ibuf[3], obuf[3] = t ^ iv[3], iv[3] = t; + t = ibuf[4], obuf[4] = t ^ iv[4], iv[4] = t; + t = ibuf[5], obuf[5] = t ^ iv[5], iv[5] = t; + t = ibuf[6], obuf[6] = t ^ iv[6], iv[6] = t; + t = ibuf[7], obuf[7] = t ^ iv[7], iv[7] = t; + t = ibuf[8], obuf[8] = t ^ iv[8], iv[8] = t; + t = ibuf[9], obuf[9] = t ^ iv[9], iv[9] = t; + t = ibuf[10], obuf[10] = t ^ iv[10], iv[10] = t; + t = ibuf[11], obuf[11] = t ^ iv[11], iv[11] = t; + t = ibuf[12], obuf[12] = t ^ iv[12], iv[12] = t; + t = ibuf[13], obuf[13] = t ^ iv[13], iv[13] = t; + t = ibuf[14], obuf[14] = t ^ iv[14], iv[14] = t; + t = ibuf[15], obuf[15] = t ^ iv[15], iv[15] = t; + ibuf += AES_BLOCK_SIZE; + obuf += AES_BLOCK_SIZE; + cnt += AES_BLOCK_SIZE; + } +#endif + } + + while(cnt < len) { + uint8_t t; + + if(!b_pos && aes_encrypt(iv, iv, ctx) != EXIT_SUCCESS) return EXIT_FAILURE; + + while(cnt < len && b_pos < AES_BLOCK_SIZE) { + t = *ibuf++; + *obuf++ = t ^ iv[b_pos]; + iv[b_pos++] = t; + cnt++; + } + + b_pos = (b_pos == AES_BLOCK_SIZE ? 0 : b_pos); + } + + ctx->inf.b[2] = (uint8_t)b_pos; + return EXIT_SUCCESS; +} + +AES_RETURN aes_ofb_crypt( + const unsigned char* ibuf, + unsigned char* obuf, + int len, + unsigned char* iv, + aes_encrypt_ctx ctx[1]) { + int cnt = 0, b_pos = (int)ctx->inf.b[2], nb; + + if(b_pos) /* complete any partial block */ + { + while(b_pos < AES_BLOCK_SIZE && cnt < len) { + *obuf++ = iv[b_pos++] ^ *ibuf++; + cnt++; + } + + b_pos = (b_pos == AES_BLOCK_SIZE ? 0 : b_pos); + } + + if((nb = (len - cnt) >> AES_BLOCK_SIZE_P2) != 0) /* process whole blocks */ + { +#if defined(USE_VIA_ACE_IF_PRESENT) + + if(ctx->inf.b[1] == 0xff) { + int m; + uint8_t *ksp = (uint8_t*)(ctx->ks), *ivp = iv; + aligned_auto(uint8_t, liv, AES_BLOCK_SIZE, 16); + via_cwd(cwd, hybrid, enc, 2 * ctx->inf.b[0] - 192); + + if(ALIGN_OFFSET(ctx, 16)) return EXIT_FAILURE; + + if(ALIGN_OFFSET(iv, 16)) /* ensure an aligned iv */ + { + ivp = liv; + memcpy(liv, iv, AES_BLOCK_SIZE); + } + + if(!ALIGN_OFFSET(ibuf, 16) && !ALIGN_OFFSET(obuf, 16)) { + via_ofb_op6(ksp, cwd, ibuf, obuf, nb, ivp); + ibuf += nb * AES_BLOCK_SIZE; + obuf += nb * AES_BLOCK_SIZE; + cnt += nb * AES_BLOCK_SIZE; + } else /* input, output or both are unaligned */ + { + aligned_auto(uint8_t, buf, BFR_BLOCKS * AES_BLOCK_SIZE, 16); + uint8_t *ip = NULL, *op = NULL; + + while(nb) { + m = (nb > BFR_BLOCKS ? BFR_BLOCKS : nb), nb -= m; + + ip = (ALIGN_OFFSET(ibuf, 16) ? buf : ibuf); + op = (ALIGN_OFFSET(obuf, 16) ? buf : obuf); + + if(ip != ibuf) memcpy(buf, ibuf, m * AES_BLOCK_SIZE); + + via_ofb_op6(ksp, cwd, ip, op, m, ivp); + + if(op != obuf) memcpy(obuf, buf, m * AES_BLOCK_SIZE); + + ibuf += m * AES_BLOCK_SIZE; + obuf += m * AES_BLOCK_SIZE; + cnt += m * AES_BLOCK_SIZE; + } + } + + if(ivp != iv) memcpy(iv, ivp, AES_BLOCK_SIZE); + } +#else +#ifdef FAST_BUFFER_OPERATIONS + if(!ALIGN_OFFSET(ibuf, 4) && !ALIGN_OFFSET(obuf, 4) && !ALIGN_OFFSET(iv, 4)) + while(cnt + AES_BLOCK_SIZE <= len) { + assert(b_pos == 0); + if(aes_encrypt(iv, iv, ctx) != EXIT_SUCCESS) return EXIT_FAILURE; + lp32(obuf)[0] = lp32(iv)[0] ^ lp32(ibuf)[0]; + lp32(obuf)[1] = lp32(iv)[1] ^ lp32(ibuf)[1]; + lp32(obuf)[2] = lp32(iv)[2] ^ lp32(ibuf)[2]; + lp32(obuf)[3] = lp32(iv)[3] ^ lp32(ibuf)[3]; + ibuf += AES_BLOCK_SIZE; + obuf += AES_BLOCK_SIZE; + cnt += AES_BLOCK_SIZE; + } + else +#endif + while(cnt + AES_BLOCK_SIZE <= len) { + assert(b_pos == 0); + if(aes_encrypt(iv, iv, ctx) != EXIT_SUCCESS) return EXIT_FAILURE; + obuf[0] = iv[0] ^ ibuf[0]; + obuf[1] = iv[1] ^ ibuf[1]; + obuf[2] = iv[2] ^ ibuf[2]; + obuf[3] = iv[3] ^ ibuf[3]; + obuf[4] = iv[4] ^ ibuf[4]; + obuf[5] = iv[5] ^ ibuf[5]; + obuf[6] = iv[6] ^ ibuf[6]; + obuf[7] = iv[7] ^ ibuf[7]; + obuf[8] = iv[8] ^ ibuf[8]; + obuf[9] = iv[9] ^ ibuf[9]; + obuf[10] = iv[10] ^ ibuf[10]; + obuf[11] = iv[11] ^ ibuf[11]; + obuf[12] = iv[12] ^ ibuf[12]; + obuf[13] = iv[13] ^ ibuf[13]; + obuf[14] = iv[14] ^ ibuf[14]; + obuf[15] = iv[15] ^ ibuf[15]; + ibuf += AES_BLOCK_SIZE; + obuf += AES_BLOCK_SIZE; + cnt += AES_BLOCK_SIZE; + } +#endif + } + + while(cnt < len) { + if(!b_pos && aes_encrypt(iv, iv, ctx) != EXIT_SUCCESS) return EXIT_FAILURE; + + while(cnt < len && b_pos < AES_BLOCK_SIZE) { + *obuf++ = iv[b_pos++] ^ *ibuf++; + cnt++; + } + + b_pos = (b_pos == AES_BLOCK_SIZE ? 0 : b_pos); + } + + ctx->inf.b[2] = (uint8_t)b_pos; + return EXIT_SUCCESS; +} + +#define BFR_LENGTH (BFR_BLOCKS * AES_BLOCK_SIZE) + +AES_RETURN aes_ctr_crypt( + const unsigned char* ibuf, + unsigned char* obuf, + int len, + unsigned char* cbuf, + cbuf_inc ctr_inc, + aes_encrypt_ctx ctx[1]) { + unsigned char* ip; + int i = 0, blen = 0, b_pos = (int)(ctx->inf.b[2]); + +#if defined(USE_VIA_ACE_IF_PRESENT) + aligned_auto(uint8_t, buf, BFR_LENGTH, 16); + if(ctx->inf.b[1] == 0xff && ALIGN_OFFSET(ctx, 16)) return EXIT_FAILURE; +#else + uint8_t buf[BFR_LENGTH] = {0}; +#endif + + if(b_pos) { + memcpy(buf, cbuf, AES_BLOCK_SIZE); + if(aes_ecb_encrypt(buf, buf, AES_BLOCK_SIZE, ctx) != EXIT_SUCCESS) return EXIT_FAILURE; + + while(b_pos < AES_BLOCK_SIZE && len) { + *obuf++ = *ibuf++ ^ buf[b_pos++]; + --len; + } + + if(len) ctr_inc(cbuf), b_pos = 0; + } + + while(len) { + blen = (len > BFR_LENGTH ? BFR_LENGTH : len), len -= blen; + + for(i = 0, ip = buf; i < (blen >> AES_BLOCK_SIZE_P2); ++i) { + memcpy(ip, cbuf, AES_BLOCK_SIZE); + ctr_inc(cbuf); + ip += AES_BLOCK_SIZE; + } + + if(blen & (AES_BLOCK_SIZE - 1)) memcpy(ip, cbuf, AES_BLOCK_SIZE), i++; + +#if defined(USE_VIA_ACE_IF_PRESENT) + if(ctx->inf.b[1] == 0xff) { + via_cwd(cwd, hybrid, enc, 2 * ctx->inf.b[0] - 192); + via_ecb_op5((ctx->ks), cwd, buf, buf, i); + } else +#endif + if(aes_ecb_encrypt(buf, buf, i * AES_BLOCK_SIZE, ctx) != EXIT_SUCCESS) + return EXIT_FAILURE; + + i = 0; + ip = buf; +#ifdef FAST_BUFFER_OPERATIONS + if(!ALIGN_OFFSET(ibuf, 4) && !ALIGN_OFFSET(obuf, 4) && !ALIGN_OFFSET(ip, 4)) + while(i + AES_BLOCK_SIZE <= blen) { + lp32(obuf)[0] = lp32(ibuf)[0] ^ lp32(ip)[0]; + lp32(obuf)[1] = lp32(ibuf)[1] ^ lp32(ip)[1]; + lp32(obuf)[2] = lp32(ibuf)[2] ^ lp32(ip)[2]; + lp32(obuf)[3] = lp32(ibuf)[3] ^ lp32(ip)[3]; + i += AES_BLOCK_SIZE; + ip += AES_BLOCK_SIZE; + ibuf += AES_BLOCK_SIZE; + obuf += AES_BLOCK_SIZE; + } + else +#endif + while(i + AES_BLOCK_SIZE <= blen) { + obuf[0] = ibuf[0] ^ ip[0]; + obuf[1] = ibuf[1] ^ ip[1]; + obuf[2] = ibuf[2] ^ ip[2]; + obuf[3] = ibuf[3] ^ ip[3]; + obuf[4] = ibuf[4] ^ ip[4]; + obuf[5] = ibuf[5] ^ ip[5]; + obuf[6] = ibuf[6] ^ ip[6]; + obuf[7] = ibuf[7] ^ ip[7]; + obuf[8] = ibuf[8] ^ ip[8]; + obuf[9] = ibuf[9] ^ ip[9]; + obuf[10] = ibuf[10] ^ ip[10]; + obuf[11] = ibuf[11] ^ ip[11]; + obuf[12] = ibuf[12] ^ ip[12]; + obuf[13] = ibuf[13] ^ ip[13]; + obuf[14] = ibuf[14] ^ ip[14]; + obuf[15] = ibuf[15] ^ ip[15]; + i += AES_BLOCK_SIZE; + ip += AES_BLOCK_SIZE; + ibuf += AES_BLOCK_SIZE; + obuf += AES_BLOCK_SIZE; + } + + while(i++ < blen) *obuf++ = *ibuf++ ^ ip[b_pos++]; + } + + ctx->inf.b[2] = (uint8_t)b_pos; + return EXIT_SUCCESS; +} + +void aes_ctr_cbuf_inc(unsigned char* cbuf) { + int i = AES_BLOCK_SIZE - 1; + while(i >= 0) { + cbuf[i]++; + if(cbuf[i]) return; // if there was no overflow + i--; + } +} + +#if defined(__cplusplus) +} +#endif +#endif diff --git a/applications/external/flipbip/lib/crypto/aes/aescrypt.c b/applications/external/flipbip/lib/crypto/aes/aescrypt.c new file mode 100644 index 0000000000..a160b6f951 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/aes/aescrypt.c @@ -0,0 +1,349 @@ +/* +--------------------------------------------------------------------------- +Copyright (c) 1998-2013, Brian Gladman, Worcester, UK. All rights reserved. + +The redistribution and use of this software (with or without changes) +is allowed without the payment of fees or royalties provided that: + + source code distributions include the above copyright notice, this + list of conditions and the following disclaimer; + + binary distributions include the above copyright notice, this list + of conditions and the following disclaimer in their documentation. + +This software is provided 'as is' with no explicit or implied warranties +in respect of its operation, including, but not limited to, correctness +and fitness for purpose. +--------------------------------------------------------------------------- +Issue Date: 20/12/2007 +*/ + +#include "aesopt.h" +#include "aestab.h" + +#if defined(USE_INTEL_AES_IF_PRESENT) +#include "aes_ni.h" +#else +/* map names here to provide the external API ('name' -> 'aes_name') */ +#define aes_xi(x) aes_##x +#endif + +#if defined(__cplusplus) +extern "C" { +#endif + +#define si(y, x, k, c) (s(y, c) = word_in(x, c) ^ (k)[c]) +#define so(y, x, c) word_out(y, c, s(x, c)) + +#if defined(ARRAYS) +#define locals(y, x) x[4], y[4] +#else +#define locals(y, x) x##0, x##1, x##2, x##3, y##0, y##1, y##2, y##3 +#endif + +#define l_copy(y, x) \ + s(y, 0) = s(x, 0); \ + s(y, 1) = s(x, 1); \ + s(y, 2) = s(x, 2); \ + s(y, 3) = s(x, 3); +#define state_in(y, x, k) \ + si(y, x, k, 0); \ + si(y, x, k, 1); \ + si(y, x, k, 2); \ + si(y, x, k, 3) +#define state_out(y, x) \ + so(y, x, 0); \ + so(y, x, 1); \ + so(y, x, 2); \ + so(y, x, 3) +#define round(rm, y, x, k) \ + rm(y, x, k, 0); \ + rm(y, x, k, 1); \ + rm(y, x, k, 2); \ + rm(y, x, k, 3) + +#if(FUNCS_IN_C & ENCRYPTION_IN_C) + +/* Visual C++ .Net v7.1 provides the fastest encryption code when using + Pentium optimiation with small code but this is poor for decryption + so we need to control this with the following VC++ pragmas +*/ + +#if defined(_MSC_VER) && !defined(_WIN64) && !defined(__clang__) +#pragma optimize("s", on) +#endif + +/* Given the column (c) of the output state variable, the following + macros give the input state variables which are needed in its + computation for each row (r) of the state. All the alternative + macros give the same end values but expand into different ways + of calculating these values. In particular the complex macro + used for dynamically variable block sizes is designed to expand + to a compile time constant whenever possible but will expand to + conditional clauses on some branches (I am grateful to Frank + Yellin for this construction) +*/ + +#define fwd_var(x, r, c) \ + (r == 0 ? (c == 0 ? s(x, 0) : \ + c == 1 ? s(x, 1) : \ + c == 2 ? s(x, 2) : \ + s(x, 3)) : \ + r == 1 ? (c == 0 ? s(x, 1) : \ + c == 1 ? s(x, 2) : \ + c == 2 ? s(x, 3) : \ + s(x, 0)) : \ + r == 2 ? (c == 0 ? s(x, 2) : \ + c == 1 ? s(x, 3) : \ + c == 2 ? s(x, 0) : \ + s(x, 1)) : \ + (c == 0 ? s(x, 3) : \ + c == 1 ? s(x, 0) : \ + c == 2 ? s(x, 1) : \ + s(x, 2))) + +#if defined(FT4_SET) +#undef dec_fmvars +#define fwd_rnd(y, x, k, c) (s(y, c) = (k)[c] ^ four_tables(x, t_use(f, n), fwd_var, rf1, c)) +#elif defined(FT1_SET) +#undef dec_fmvars +#define fwd_rnd(y, x, k, c) (s(y, c) = (k)[c] ^ one_table(x, upr, t_use(f, n), fwd_var, rf1, c)) +#else +#define fwd_rnd(y, x, k, c) \ + (s(y, c) = (k)[c] ^ fwd_mcol(no_table(x, t_use(s, box), fwd_var, rf1, c))) +#endif + +#if defined(FL4_SET) +#define fwd_lrnd(y, x, k, c) (s(y, c) = (k)[c] ^ four_tables(x, t_use(f, l), fwd_var, rf1, c)) +#elif defined(FL1_SET) +#define fwd_lrnd(y, x, k, c) (s(y, c) = (k)[c] ^ one_table(x, ups, t_use(f, l), fwd_var, rf1, c)) +#else +#define fwd_lrnd(y, x, k, c) (s(y, c) = (k)[c] ^ no_table(x, t_use(s, box), fwd_var, rf1, c)) +#endif + +AES_RETURN +aes_xi(encrypt)(const unsigned char* in, unsigned char* out, const aes_encrypt_ctx cx[1]) { + uint32_t locals(b0, b1); + const uint32_t* kp = NULL; +#if defined(dec_fmvars) + dec_fmvars; /* declare variables for fwd_mcol() if needed */ +#endif + + if(cx->inf.b[0] != 10 * AES_BLOCK_SIZE && cx->inf.b[0] != 12 * AES_BLOCK_SIZE && + cx->inf.b[0] != 14 * AES_BLOCK_SIZE) + return EXIT_FAILURE; + + kp = cx->ks; + state_in(b0, in, kp); + +#if(ENC_UNROLL == FULL) + + switch(cx->inf.b[0]) { + case 14 * AES_BLOCK_SIZE: + round(fwd_rnd, b1, b0, kp + 1 * N_COLS); + round(fwd_rnd, b0, b1, kp + 2 * N_COLS); + kp += 2 * N_COLS; + //-fallthrough + case 12 * AES_BLOCK_SIZE: + round(fwd_rnd, b1, b0, kp + 1 * N_COLS); + round(fwd_rnd, b0, b1, kp + 2 * N_COLS); + kp += 2 * N_COLS; + //-fallthrough + case 10 * AES_BLOCK_SIZE: + round(fwd_rnd, b1, b0, kp + 1 * N_COLS); + round(fwd_rnd, b0, b1, kp + 2 * N_COLS); + round(fwd_rnd, b1, b0, kp + 3 * N_COLS); + round(fwd_rnd, b0, b1, kp + 4 * N_COLS); + round(fwd_rnd, b1, b0, kp + 5 * N_COLS); + round(fwd_rnd, b0, b1, kp + 6 * N_COLS); + round(fwd_rnd, b1, b0, kp + 7 * N_COLS); + round(fwd_rnd, b0, b1, kp + 8 * N_COLS); + round(fwd_rnd, b1, b0, kp + 9 * N_COLS); + round(fwd_lrnd, b0, b1, kp + 10 * N_COLS); + //-fallthrough + } + +#else + +#if(ENC_UNROLL == PARTIAL) + { + uint32_t rnd; + for(rnd = 0; rnd < (cx->inf.b[0] >> 5) - 1; ++rnd) { + kp += N_COLS; + round(fwd_rnd, b1, b0, kp); + kp += N_COLS; + round(fwd_rnd, b0, b1, kp); + } + kp += N_COLS; + round(fwd_rnd, b1, b0, kp); +#else + { + uint32_t rnd; + for(rnd = 0; rnd < (cx->inf.b[0] >> 4) - 1; ++rnd) { + kp += N_COLS; + round(fwd_rnd, b1, b0, kp); + l_copy(b0, b1); + } +#endif + kp += N_COLS; + round(fwd_lrnd, b0, b1, kp); + } +#endif + + state_out(out, b0); + return EXIT_SUCCESS; +} + +#endif + +#if(FUNCS_IN_C & DECRYPTION_IN_C) + +/* Visual C++ .Net v7.1 provides the fastest encryption code when using + Pentium optimiation with small code but this is poor for decryption + so we need to control this with the following VC++ pragmas +*/ + +#if defined(_MSC_VER) && !defined(_WIN64) && !defined(__clang__) +#pragma optimize("t", on) +#endif + +/* Given the column (c) of the output state variable, the following + macros give the input state variables which are needed in its + computation for each row (r) of the state. All the alternative + macros give the same end values but expand into different ways + of calculating these values. In particular the complex macro + used for dynamically variable block sizes is designed to expand + to a compile time constant whenever possible but will expand to + conditional clauses on some branches (I am grateful to Frank + Yellin for this construction) +*/ + +#define inv_var(x, r, c) \ + (r == 0 ? (c == 0 ? s(x, 0) : \ + c == 1 ? s(x, 1) : \ + c == 2 ? s(x, 2) : \ + s(x, 3)) : \ + r == 1 ? (c == 0 ? s(x, 3) : \ + c == 1 ? s(x, 0) : \ + c == 2 ? s(x, 1) : \ + s(x, 2)) : \ + r == 2 ? (c == 0 ? s(x, 2) : \ + c == 1 ? s(x, 3) : \ + c == 2 ? s(x, 0) : \ + s(x, 1)) : \ + (c == 0 ? s(x, 1) : \ + c == 1 ? s(x, 2) : \ + c == 2 ? s(x, 3) : \ + s(x, 0))) + +#if defined(IT4_SET) +#undef dec_imvars +#define inv_rnd(y, x, k, c) (s(y, c) = (k)[c] ^ four_tables(x, t_use(i, n), inv_var, rf1, c)) +#elif defined(IT1_SET) +#undef dec_imvars +#define inv_rnd(y, x, k, c) (s(y, c) = (k)[c] ^ one_table(x, upr, t_use(i, n), inv_var, rf1, c)) +#else +#define inv_rnd(y, x, k, c) \ + (s(y, c) = inv_mcol((k)[c] ^ no_table(x, t_use(i, box), inv_var, rf1, c))) +#endif + +#if defined(IL4_SET) +#define inv_lrnd(y, x, k, c) (s(y, c) = (k)[c] ^ four_tables(x, t_use(i, l), inv_var, rf1, c)) +#elif defined(IL1_SET) +#define inv_lrnd(y, x, k, c) (s(y, c) = (k)[c] ^ one_table(x, ups, t_use(i, l), inv_var, rf1, c)) +#else +#define inv_lrnd(y, x, k, c) (s(y, c) = (k)[c] ^ no_table(x, t_use(i, box), inv_var, rf1, c)) +#endif + +/* This code can work with the decryption key schedule in the */ +/* order that is used for encrytpion (where the 1st decryption */ +/* round key is at the high end ot the schedule) or with a key */ +/* schedule that has been reversed to put the 1st decryption */ +/* round key at the low end of the schedule in memory (when */ +/* AES_REV_DKS is defined) */ + +#ifdef AES_REV_DKS +#define key_ofs 0 +#define rnd_key(n) (kp + n * N_COLS) +#else +#define key_ofs 1 +#define rnd_key(n) (kp - n * N_COLS) +#endif + +AES_RETURN +aes_xi(decrypt)(const unsigned char* in, unsigned char* out, const aes_decrypt_ctx cx[1]) { + uint32_t locals(b0, b1); +#if defined(dec_imvars) + dec_imvars; /* declare variables for inv_mcol() if needed */ +#endif + const uint32_t* kp = NULL; + + if(cx->inf.b[0] != 10 * AES_BLOCK_SIZE && cx->inf.b[0] != 12 * AES_BLOCK_SIZE && + cx->inf.b[0] != 14 * AES_BLOCK_SIZE) + return EXIT_FAILURE; + + kp = cx->ks + (key_ofs ? (cx->inf.b[0] >> 2) : 0); + state_in(b0, in, kp); + +#if(DEC_UNROLL == FULL) + + kp = cx->ks + (key_ofs ? 0 : (cx->inf.b[0] >> 2)); + switch(cx->inf.b[0]) { + case 14 * AES_BLOCK_SIZE: + round(inv_rnd, b1, b0, rnd_key(-13)); + round(inv_rnd, b0, b1, rnd_key(-12)); + //-fallthrough + case 12 * AES_BLOCK_SIZE: + round(inv_rnd, b1, b0, rnd_key(-11)); + round(inv_rnd, b0, b1, rnd_key(-10)); + //-fallthrough + case 10 * AES_BLOCK_SIZE: + round(inv_rnd, b1, b0, rnd_key(-9)); + round(inv_rnd, b0, b1, rnd_key(-8)); + round(inv_rnd, b1, b0, rnd_key(-7)); + round(inv_rnd, b0, b1, rnd_key(-6)); + round(inv_rnd, b1, b0, rnd_key(-5)); + round(inv_rnd, b0, b1, rnd_key(-4)); + round(inv_rnd, b1, b0, rnd_key(-3)); + round(inv_rnd, b0, b1, rnd_key(-2)); + round(inv_rnd, b1, b0, rnd_key(-1)); + round(inv_lrnd, b0, b1, rnd_key(0)); + //-fallthrough + } + +#else + +#if(DEC_UNROLL == PARTIAL) + { + uint32_t rnd; + for(rnd = 0; rnd < (cx->inf.b[0] >> 5) - 1; ++rnd) { + kp = rnd_key(1); + round(inv_rnd, b1, b0, kp); + kp = rnd_key(1); + round(inv_rnd, b0, b1, kp); + } + kp = rnd_key(1); + round(inv_rnd, b1, b0, kp); +#else + { + uint32_t rnd; + for(rnd = 0; rnd < (cx->inf.b[0] >> 4) - 1; ++rnd) { + kp = rnd_key(1); + round(inv_rnd, b1, b0, kp); + l_copy(b0, b1); + } +#endif + kp = rnd_key(1); + round(inv_lrnd, b0, b1, kp); + } +#endif + + state_out(out, b0); + return EXIT_SUCCESS; +} + +#endif + +#if defined(__cplusplus) +} +#endif diff --git a/applications/external/flipbip/lib/crypto/aes/aeskey.c b/applications/external/flipbip/lib/crypto/aes/aeskey.c new file mode 100644 index 0000000000..0092be4396 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/aes/aeskey.c @@ -0,0 +1,662 @@ +/* +--------------------------------------------------------------------------- +Copyright (c) 1998-2013, Brian Gladman, Worcester, UK. All rights reserved. + +The redistribution and use of this software (with or without changes) +is allowed without the payment of fees or royalties provided that: + + source code distributions include the above copyright notice, this + list of conditions and the following disclaimer; + + binary distributions include the above copyright notice, this list + of conditions and the following disclaimer in their documentation. + +This software is provided 'as is' with no explicit or implied warranties +in respect of its operation, including, but not limited to, correctness +and fitness for purpose. +--------------------------------------------------------------------------- +Issue Date: 20/12/2007 +*/ + +#include "aesopt.h" +#include "aestab.h" + +#if defined(USE_INTEL_AES_IF_PRESENT) +#include "aes_ni.h" +#else +/* map names here to provide the external API ('name' -> 'aes_name') */ +#define aes_xi(x) aes_##x +#endif + +#ifdef USE_VIA_ACE_IF_PRESENT +#include "aes_via_ace.h" +#endif + +#if defined(__cplusplus) +extern "C" { +#endif + +/* Initialise the key schedule from the user supplied key. The key + length can be specified in bytes, with legal values of 16, 24 + and 32, or in bits, with legal values of 128, 192 and 256. These + values correspond with Nk values of 4, 6 and 8 respectively. + + The following macros implement a single cycle in the key + schedule generation process. The number of cycles needed + for each cx->n_col and nk value is: + + nk = 4 5 6 7 8 + ------------------------------ + cx->n_col = 4 10 9 8 7 7 + cx->n_col = 5 14 11 10 9 9 + cx->n_col = 6 19 15 12 11 11 + cx->n_col = 7 21 19 16 13 14 + cx->n_col = 8 29 23 19 17 14 +*/ + +#if defined(REDUCE_CODE_SIZE) +#define ls_box ls_sub +uint32_t ls_sub(const uint32_t t, const uint32_t n); +#define inv_mcol im_sub +uint32_t im_sub(const uint32_t x); +#ifdef ENC_KS_UNROLL +#undef ENC_KS_UNROLL +#endif +#ifdef DEC_KS_UNROLL +#undef DEC_KS_UNROLL +#endif +#endif + +#if(FUNCS_IN_C & ENC_KEYING_IN_C) + +#if defined(AES_128) || defined(AES_VAR) + +#define ke4(k, i) \ + { \ + k[4 * (i) + 4] = ss[0] ^= ls_box(ss[3], 3) ^ t_use(r, c)[i]; \ + k[4 * (i) + 5] = ss[1] ^= ss[0]; \ + k[4 * (i) + 6] = ss[2] ^= ss[1]; \ + k[4 * (i) + 7] = ss[3] ^= ss[2]; \ + } + +AES_RETURN aes_xi(encrypt_key128)(const unsigned char* key, aes_encrypt_ctx cx[1]) { + uint32_t ss[4]; + + cx->ks[0] = ss[0] = word_in(key, 0); + cx->ks[1] = ss[1] = word_in(key, 1); + cx->ks[2] = ss[2] = word_in(key, 2); + cx->ks[3] = ss[3] = word_in(key, 3); + +#ifdef ENC_KS_UNROLL + ke4(cx->ks, 0); + ke4(cx->ks, 1); + ke4(cx->ks, 2); + ke4(cx->ks, 3); + ke4(cx->ks, 4); + ke4(cx->ks, 5); + ke4(cx->ks, 6); + ke4(cx->ks, 7); + ke4(cx->ks, 8); +#else + { + uint32_t i; + for(i = 0; i < 9; ++i) ke4(cx->ks, i); + } +#endif + ke4(cx->ks, 9); + cx->inf.l = 0; + cx->inf.b[0] = 10 * AES_BLOCK_SIZE; + +#ifdef USE_VIA_ACE_IF_PRESENT + if(VIA_ACE_AVAILABLE) cx->inf.b[1] = 0xff; +#endif + return EXIT_SUCCESS; +} + +#endif + +#if defined(AES_192) || defined(AES_VAR) + +#define kef6(k, i) \ + { \ + k[6 * (i) + 6] = ss[0] ^= ls_box(ss[5], 3) ^ t_use(r, c)[i]; \ + k[6 * (i) + 7] = ss[1] ^= ss[0]; \ + k[6 * (i) + 8] = ss[2] ^= ss[1]; \ + k[6 * (i) + 9] = ss[3] ^= ss[2]; \ + } + +#define ke6(k, i) \ + { \ + kef6(k, i); \ + k[6 * (i) + 10] = ss[4] ^= ss[3]; \ + k[6 * (i) + 11] = ss[5] ^= ss[4]; \ + } + +AES_RETURN aes_xi(encrypt_key192)(const unsigned char* key, aes_encrypt_ctx cx[1]) { + uint32_t ss[6]; + + cx->ks[0] = ss[0] = word_in(key, 0); + cx->ks[1] = ss[1] = word_in(key, 1); + cx->ks[2] = ss[2] = word_in(key, 2); + cx->ks[3] = ss[3] = word_in(key, 3); + cx->ks[4] = ss[4] = word_in(key, 4); + cx->ks[5] = ss[5] = word_in(key, 5); + +#ifdef ENC_KS_UNROLL + ke6(cx->ks, 0); + ke6(cx->ks, 1); + ke6(cx->ks, 2); + ke6(cx->ks, 3); + ke6(cx->ks, 4); + ke6(cx->ks, 5); + ke6(cx->ks, 6); +#else + { + uint32_t i; + for(i = 0; i < 7; ++i) ke6(cx->ks, i); + } +#endif + kef6(cx->ks, 7); + cx->inf.l = 0; + cx->inf.b[0] = 12 * AES_BLOCK_SIZE; + +#ifdef USE_VIA_ACE_IF_PRESENT + if(VIA_ACE_AVAILABLE) cx->inf.b[1] = 0xff; +#endif + return EXIT_SUCCESS; +} + +#endif + +#if defined(AES_256) || defined(AES_VAR) + +#define kef8(k, i) \ + { \ + k[8 * (i) + 8] = ss[0] ^= ls_box(ss[7], 3) ^ t_use(r, c)[i]; \ + k[8 * (i) + 9] = ss[1] ^= ss[0]; \ + k[8 * (i) + 10] = ss[2] ^= ss[1]; \ + k[8 * (i) + 11] = ss[3] ^= ss[2]; \ + } + +#define ke8(k, i) \ + { \ + kef8(k, i); \ + k[8 * (i) + 12] = ss[4] ^= ls_box(ss[3], 0); \ + k[8 * (i) + 13] = ss[5] ^= ss[4]; \ + k[8 * (i) + 14] = ss[6] ^= ss[5]; \ + k[8 * (i) + 15] = ss[7] ^= ss[6]; \ + } + +AES_RETURN aes_xi(encrypt_key256)(const unsigned char* key, aes_encrypt_ctx cx[1]) { + uint32_t ss[8]; + + cx->ks[0] = ss[0] = word_in(key, 0); + cx->ks[1] = ss[1] = word_in(key, 1); + cx->ks[2] = ss[2] = word_in(key, 2); + cx->ks[3] = ss[3] = word_in(key, 3); + cx->ks[4] = ss[4] = word_in(key, 4); + cx->ks[5] = ss[5] = word_in(key, 5); + cx->ks[6] = ss[6] = word_in(key, 6); + cx->ks[7] = ss[7] = word_in(key, 7); + +#ifdef ENC_KS_UNROLL + ke8(cx->ks, 0); + ke8(cx->ks, 1); + ke8(cx->ks, 2); + ke8(cx->ks, 3); + ke8(cx->ks, 4); + ke8(cx->ks, 5); +#else + { + uint32_t i; + for(i = 0; i < 6; ++i) ke8(cx->ks, i); + } +#endif + kef8(cx->ks, 6); + cx->inf.l = 0; + cx->inf.b[0] = 14 * AES_BLOCK_SIZE; + +#ifdef USE_VIA_ACE_IF_PRESENT + if(VIA_ACE_AVAILABLE) cx->inf.b[1] = 0xff; +#endif + return EXIT_SUCCESS; +} + +#endif + +#endif + +#if(FUNCS_IN_C & DEC_KEYING_IN_C) + +/* this is used to store the decryption round keys */ +/* in forward or reverse order */ + +#ifdef AES_REV_DKS +#define v(n, i) ((n) - (i) + 2 * ((i)&3)) +#else +#define v(n, i) (i) +#endif + +#if DEC_ROUND == NO_TABLES +#define ff(x) (x) +#else +#define ff(x) inv_mcol(x) +#if defined(dec_imvars) +#define d_vars dec_imvars +#endif +#endif + +#if defined(AES_128) || defined(AES_VAR) + +#define k4e(k, i) \ + { \ + k[v(40, (4 * (i)) + 4)] = ss[0] ^= ls_box(ss[3], 3) ^ t_use(r, c)[i]; \ + k[v(40, (4 * (i)) + 5)] = ss[1] ^= ss[0]; \ + k[v(40, (4 * (i)) + 6)] = ss[2] ^= ss[1]; \ + k[v(40, (4 * (i)) + 7)] = ss[3] ^= ss[2]; \ + } + +#if 1 + +#define kdf4(k, i) \ + { \ + ss[0] = ss[0] ^ ss[2] ^ ss[1] ^ ss[3]; \ + ss[1] = ss[1] ^ ss[3]; \ + ss[2] = ss[2] ^ ss[3]; \ + ss[4] = ls_box(ss[(i + 3) % 4], 3) ^ t_use(r, c)[i]; \ + ss[i % 4] ^= ss[4]; \ + ss[4] ^= k[v(40, (4 * (i)))]; \ + k[v(40, (4 * (i)) + 4)] = ff(ss[4]); \ + ss[4] ^= k[v(40, (4 * (i)) + 1)]; \ + k[v(40, (4 * (i)) + 5)] = ff(ss[4]); \ + ss[4] ^= k[v(40, (4 * (i)) + 2)]; \ + k[v(40, (4 * (i)) + 6)] = ff(ss[4]); \ + ss[4] ^= k[v(40, (4 * (i)) + 3)]; \ + k[v(40, (4 * (i)) + 7)] = ff(ss[4]); \ + } + +#define kd4(k, i) \ + { \ + ss[4] = ls_box(ss[(i + 3) % 4], 3) ^ t_use(r, c)[i]; \ + ss[i % 4] ^= ss[4]; \ + ss[4] = ff(ss[4]); \ + k[v(40, (4 * (i)) + 4)] = ss[4] ^= k[v(40, (4 * (i)))]; \ + k[v(40, (4 * (i)) + 5)] = ss[4] ^= k[v(40, (4 * (i)) + 1)]; \ + k[v(40, (4 * (i)) + 6)] = ss[4] ^= k[v(40, (4 * (i)) + 2)]; \ + k[v(40, (4 * (i)) + 7)] = ss[4] ^= k[v(40, (4 * (i)) + 3)]; \ + } + +#define kdl4(k, i) \ + { \ + ss[4] = ls_box(ss[(i + 3) % 4], 3) ^ t_use(r, c)[i]; \ + ss[i % 4] ^= ss[4]; \ + k[v(40, (4 * (i)) + 4)] = (ss[0] ^= ss[1]) ^ ss[2] ^ ss[3]; \ + k[v(40, (4 * (i)) + 5)] = ss[1] ^ ss[3]; \ + k[v(40, (4 * (i)) + 6)] = ss[0]; \ + k[v(40, (4 * (i)) + 7)] = ss[1]; \ + } + +#else + +#define kdf4(k, i) \ + { \ + ss[0] ^= ls_box(ss[3], 3) ^ t_use(r, c)[i]; \ + k[v(40, (4 * (i)) + 4)] = ff(ss[0]); \ + ss[1] ^= ss[0]; \ + k[v(40, (4 * (i)) + 5)] = ff(ss[1]); \ + ss[2] ^= ss[1]; \ + k[v(40, (4 * (i)) + 6)] = ff(ss[2]); \ + ss[3] ^= ss[2]; \ + k[v(40, (4 * (i)) + 7)] = ff(ss[3]); \ + } + +#define kd4(k, i) \ + { \ + ss[4] = ls_box(ss[3], 3) ^ t_use(r, c)[i]; \ + ss[0] ^= ss[4]; \ + ss[4] = ff(ss[4]); \ + k[v(40, (4 * (i)) + 4)] = ss[4] ^= k[v(40, (4 * (i)))]; \ + ss[1] ^= ss[0]; \ + k[v(40, (4 * (i)) + 5)] = ss[4] ^= k[v(40, (4 * (i)) + 1)]; \ + ss[2] ^= ss[1]; \ + k[v(40, (4 * (i)) + 6)] = ss[4] ^= k[v(40, (4 * (i)) + 2)]; \ + ss[3] ^= ss[2]; \ + k[v(40, (4 * (i)) + 7)] = ss[4] ^= k[v(40, (4 * (i)) + 3)]; \ + } + +#define kdl4(k, i) \ + { \ + ss[0] ^= ls_box(ss[3], 3) ^ t_use(r, c)[i]; \ + k[v(40, (4 * (i)) + 4)] = ss[0]; \ + ss[1] ^= ss[0]; \ + k[v(40, (4 * (i)) + 5)] = ss[1]; \ + ss[2] ^= ss[1]; \ + k[v(40, (4 * (i)) + 6)] = ss[2]; \ + ss[3] ^= ss[2]; \ + k[v(40, (4 * (i)) + 7)] = ss[3]; \ + } + +#endif + +AES_RETURN aes_xi(decrypt_key128)(const unsigned char* key, aes_decrypt_ctx cx[1]) { + uint32_t ss[5]; +#if defined(d_vars) + d_vars; +#endif + + cx->ks[v(40, (0))] = ss[0] = word_in(key, 0); + cx->ks[v(40, (1))] = ss[1] = word_in(key, 1); + cx->ks[v(40, (2))] = ss[2] = word_in(key, 2); + cx->ks[v(40, (3))] = ss[3] = word_in(key, 3); + +#ifdef DEC_KS_UNROLL + kdf4(cx->ks, 0); + kd4(cx->ks, 1); + kd4(cx->ks, 2); + kd4(cx->ks, 3); + kd4(cx->ks, 4); + kd4(cx->ks, 5); + kd4(cx->ks, 6); + kd4(cx->ks, 7); + kd4(cx->ks, 8); + kdl4(cx->ks, 9); +#else + { + uint32_t i; + for(i = 0; i < 10; ++i) k4e(cx->ks, i); +#if !(DEC_ROUND == NO_TABLES) + for(i = N_COLS; i < 10 * N_COLS; ++i) cx->ks[i] = inv_mcol(cx->ks[i]); +#endif + } +#endif + cx->inf.l = 0; + cx->inf.b[0] = 10 * AES_BLOCK_SIZE; + +#ifdef USE_VIA_ACE_IF_PRESENT + if(VIA_ACE_AVAILABLE) cx->inf.b[1] = 0xff; +#endif + return EXIT_SUCCESS; +} + +#endif + +#if defined(AES_192) || defined(AES_VAR) + +#define k6ef(k, i) \ + { \ + k[v(48, (6 * (i)) + 6)] = ss[0] ^= ls_box(ss[5], 3) ^ t_use(r, c)[i]; \ + k[v(48, (6 * (i)) + 7)] = ss[1] ^= ss[0]; \ + k[v(48, (6 * (i)) + 8)] = ss[2] ^= ss[1]; \ + k[v(48, (6 * (i)) + 9)] = ss[3] ^= ss[2]; \ + } + +#define k6e(k, i) \ + { \ + k6ef(k, i); \ + k[v(48, (6 * (i)) + 10)] = ss[4] ^= ss[3]; \ + k[v(48, (6 * (i)) + 11)] = ss[5] ^= ss[4]; \ + } + +#define kdf6(k, i) \ + { \ + ss[0] ^= ls_box(ss[5], 3) ^ t_use(r, c)[i]; \ + k[v(48, (6 * (i)) + 6)] = ff(ss[0]); \ + ss[1] ^= ss[0]; \ + k[v(48, (6 * (i)) + 7)] = ff(ss[1]); \ + ss[2] ^= ss[1]; \ + k[v(48, (6 * (i)) + 8)] = ff(ss[2]); \ + ss[3] ^= ss[2]; \ + k[v(48, (6 * (i)) + 9)] = ff(ss[3]); \ + ss[4] ^= ss[3]; \ + k[v(48, (6 * (i)) + 10)] = ff(ss[4]); \ + ss[5] ^= ss[4]; \ + k[v(48, (6 * (i)) + 11)] = ff(ss[5]); \ + } + +#define kd6(k, i) \ + { \ + ss[6] = ls_box(ss[5], 3) ^ t_use(r, c)[i]; \ + ss[0] ^= ss[6]; \ + ss[6] = ff(ss[6]); \ + k[v(48, (6 * (i)) + 6)] = ss[6] ^= k[v(48, (6 * (i)))]; \ + ss[1] ^= ss[0]; \ + k[v(48, (6 * (i)) + 7)] = ss[6] ^= k[v(48, (6 * (i)) + 1)]; \ + ss[2] ^= ss[1]; \ + k[v(48, (6 * (i)) + 8)] = ss[6] ^= k[v(48, (6 * (i)) + 2)]; \ + ss[3] ^= ss[2]; \ + k[v(48, (6 * (i)) + 9)] = ss[6] ^= k[v(48, (6 * (i)) + 3)]; \ + ss[4] ^= ss[3]; \ + k[v(48, (6 * (i)) + 10)] = ss[6] ^= k[v(48, (6 * (i)) + 4)]; \ + ss[5] ^= ss[4]; \ + k[v(48, (6 * (i)) + 11)] = ss[6] ^= k[v(48, (6 * (i)) + 5)]; \ + } + +#define kdl6(k, i) \ + { \ + ss[0] ^= ls_box(ss[5], 3) ^ t_use(r, c)[i]; \ + k[v(48, (6 * (i)) + 6)] = ss[0]; \ + ss[1] ^= ss[0]; \ + k[v(48, (6 * (i)) + 7)] = ss[1]; \ + ss[2] ^= ss[1]; \ + k[v(48, (6 * (i)) + 8)] = ss[2]; \ + ss[3] ^= ss[2]; \ + k[v(48, (6 * (i)) + 9)] = ss[3]; \ + } + +AES_RETURN aes_xi(decrypt_key192)(const unsigned char* key, aes_decrypt_ctx cx[1]) { + uint32_t ss[7]; +#if defined(d_vars) + d_vars; +#endif + + cx->ks[v(48, (0))] = ss[0] = word_in(key, 0); + cx->ks[v(48, (1))] = ss[1] = word_in(key, 1); + cx->ks[v(48, (2))] = ss[2] = word_in(key, 2); + cx->ks[v(48, (3))] = ss[3] = word_in(key, 3); + +#ifdef DEC_KS_UNROLL + ss[4] = word_in(key, 4); + ss[5] = word_in(key, 5); + cx->ks[v(48, (4))] = ff(ss[4]); + cx->ks[v(48, (5))] = ff(ss[5]); + kdf6(cx->ks, 0); + kd6(cx->ks, 1); + kd6(cx->ks, 2); + kd6(cx->ks, 3); + kd6(cx->ks, 4); + kd6(cx->ks, 5); + kd6(cx->ks, 6); + kdl6(cx->ks, 7); +#else + cx->ks[v(48, (4))] = ss[4] = word_in(key, 4); + cx->ks[v(48, (5))] = ss[5] = word_in(key, 5); + { + uint32_t i; + + for(i = 0; i < 7; ++i) k6e(cx->ks, i); + k6ef(cx->ks, 7); +#if !(DEC_ROUND == NO_TABLES) + for(i = N_COLS; i < 12 * N_COLS; ++i) cx->ks[i] = inv_mcol(cx->ks[i]); +#endif + } +#endif + cx->inf.l = 0; + cx->inf.b[0] = 12 * AES_BLOCK_SIZE; + +#ifdef USE_VIA_ACE_IF_PRESENT + if(VIA_ACE_AVAILABLE) cx->inf.b[1] = 0xff; +#endif + return EXIT_SUCCESS; +} + +#endif + +#if defined(AES_256) || defined(AES_VAR) + +#define k8ef(k, i) \ + { \ + k[v(56, (8 * (i)) + 8)] = ss[0] ^= ls_box(ss[7], 3) ^ t_use(r, c)[i]; \ + k[v(56, (8 * (i)) + 9)] = ss[1] ^= ss[0]; \ + k[v(56, (8 * (i)) + 10)] = ss[2] ^= ss[1]; \ + k[v(56, (8 * (i)) + 11)] = ss[3] ^= ss[2]; \ + } + +#define k8e(k, i) \ + { \ + k8ef(k, i); \ + k[v(56, (8 * (i)) + 12)] = ss[4] ^= ls_box(ss[3], 0); \ + k[v(56, (8 * (i)) + 13)] = ss[5] ^= ss[4]; \ + k[v(56, (8 * (i)) + 14)] = ss[6] ^= ss[5]; \ + k[v(56, (8 * (i)) + 15)] = ss[7] ^= ss[6]; \ + } + +#define kdf8(k, i) \ + { \ + ss[0] ^= ls_box(ss[7], 3) ^ t_use(r, c)[i]; \ + k[v(56, (8 * (i)) + 8)] = ff(ss[0]); \ + ss[1] ^= ss[0]; \ + k[v(56, (8 * (i)) + 9)] = ff(ss[1]); \ + ss[2] ^= ss[1]; \ + k[v(56, (8 * (i)) + 10)] = ff(ss[2]); \ + ss[3] ^= ss[2]; \ + k[v(56, (8 * (i)) + 11)] = ff(ss[3]); \ + ss[4] ^= ls_box(ss[3], 0); \ + k[v(56, (8 * (i)) + 12)] = ff(ss[4]); \ + ss[5] ^= ss[4]; \ + k[v(56, (8 * (i)) + 13)] = ff(ss[5]); \ + ss[6] ^= ss[5]; \ + k[v(56, (8 * (i)) + 14)] = ff(ss[6]); \ + ss[7] ^= ss[6]; \ + k[v(56, (8 * (i)) + 15)] = ff(ss[7]); \ + } + +#define kd8(k, i) \ + { \ + ss[8] = ls_box(ss[7], 3) ^ t_use(r, c)[i]; \ + ss[0] ^= ss[8]; \ + ss[8] = ff(ss[8]); \ + k[v(56, (8 * (i)) + 8)] = ss[8] ^= k[v(56, (8 * (i)))]; \ + ss[1] ^= ss[0]; \ + k[v(56, (8 * (i)) + 9)] = ss[8] ^= k[v(56, (8 * (i)) + 1)]; \ + ss[2] ^= ss[1]; \ + k[v(56, (8 * (i)) + 10)] = ss[8] ^= k[v(56, (8 * (i)) + 2)]; \ + ss[3] ^= ss[2]; \ + k[v(56, (8 * (i)) + 11)] = ss[8] ^= k[v(56, (8 * (i)) + 3)]; \ + ss[8] = ls_box(ss[3], 0); \ + ss[4] ^= ss[8]; \ + ss[8] = ff(ss[8]); \ + k[v(56, (8 * (i)) + 12)] = ss[8] ^= k[v(56, (8 * (i)) + 4)]; \ + ss[5] ^= ss[4]; \ + k[v(56, (8 * (i)) + 13)] = ss[8] ^= k[v(56, (8 * (i)) + 5)]; \ + ss[6] ^= ss[5]; \ + k[v(56, (8 * (i)) + 14)] = ss[8] ^= k[v(56, (8 * (i)) + 6)]; \ + ss[7] ^= ss[6]; \ + k[v(56, (8 * (i)) + 15)] = ss[8] ^= k[v(56, (8 * (i)) + 7)]; \ + } + +#define kdl8(k, i) \ + { \ + ss[0] ^= ls_box(ss[7], 3) ^ t_use(r, c)[i]; \ + k[v(56, (8 * (i)) + 8)] = ss[0]; \ + ss[1] ^= ss[0]; \ + k[v(56, (8 * (i)) + 9)] = ss[1]; \ + ss[2] ^= ss[1]; \ + k[v(56, (8 * (i)) + 10)] = ss[2]; \ + ss[3] ^= ss[2]; \ + k[v(56, (8 * (i)) + 11)] = ss[3]; \ + } + +AES_RETURN aes_xi(decrypt_key256)(const unsigned char* key, aes_decrypt_ctx cx[1]) { + uint32_t ss[9]; +#if defined(d_vars) + d_vars; +#endif + + cx->ks[v(56, (0))] = ss[0] = word_in(key, 0); + cx->ks[v(56, (1))] = ss[1] = word_in(key, 1); + cx->ks[v(56, (2))] = ss[2] = word_in(key, 2); + cx->ks[v(56, (3))] = ss[3] = word_in(key, 3); + +#ifdef DEC_KS_UNROLL + ss[4] = word_in(key, 4); + ss[5] = word_in(key, 5); + ss[6] = word_in(key, 6); + ss[7] = word_in(key, 7); + cx->ks[v(56, (4))] = ff(ss[4]); + cx->ks[v(56, (5))] = ff(ss[5]); + cx->ks[v(56, (6))] = ff(ss[6]); + cx->ks[v(56, (7))] = ff(ss[7]); + kdf8(cx->ks, 0); + kd8(cx->ks, 1); + kd8(cx->ks, 2); + kd8(cx->ks, 3); + kd8(cx->ks, 4); + kd8(cx->ks, 5); + kdl8(cx->ks, 6); +#else + cx->ks[v(56, (4))] = ss[4] = word_in(key, 4); + cx->ks[v(56, (5))] = ss[5] = word_in(key, 5); + cx->ks[v(56, (6))] = ss[6] = word_in(key, 6); + cx->ks[v(56, (7))] = ss[7] = word_in(key, 7); + { + uint32_t i; + + for(i = 0; i < 6; ++i) k8e(cx->ks, i); + k8ef(cx->ks, 6); +#if !(DEC_ROUND == NO_TABLES) + for(i = N_COLS; i < 14 * N_COLS; ++i) cx->ks[i] = inv_mcol(cx->ks[i]); +#endif + } +#endif + cx->inf.l = 0; + cx->inf.b[0] = 14 * AES_BLOCK_SIZE; + +#ifdef USE_VIA_ACE_IF_PRESENT + if(VIA_ACE_AVAILABLE) cx->inf.b[1] = 0xff; +#endif + return EXIT_SUCCESS; +} + +#endif + +#endif + +#if defined(AES_VAR) + +AES_RETURN aes_encrypt_key(const unsigned char* key, int key_len, aes_encrypt_ctx cx[1]) { + switch(key_len) { + case 16: + case 128: + return aes_encrypt_key128(key, cx); + case 24: + case 192: + return aes_encrypt_key192(key, cx); + case 32: + case 256: + return aes_encrypt_key256(key, cx); + default: + return EXIT_FAILURE; + } +} + +AES_RETURN aes_decrypt_key(const unsigned char* key, int key_len, aes_decrypt_ctx cx[1]) { + switch(key_len) { + case 16: + case 128: + return aes_decrypt_key128(key, cx); + case 24: + case 192: + return aes_decrypt_key192(key, cx); + case 32: + case 256: + return aes_decrypt_key256(key, cx); + default: + return EXIT_FAILURE; + } +} + +#endif + +#if defined(__cplusplus) +} +#endif diff --git a/applications/external/flipbip/lib/crypto/aes/aesopt.h b/applications/external/flipbip/lib/crypto/aes/aesopt.h new file mode 100644 index 0000000000..6d4de004fd --- /dev/null +++ b/applications/external/flipbip/lib/crypto/aes/aesopt.h @@ -0,0 +1,793 @@ +/* +--------------------------------------------------------------------------- +Copyright (c) 1998-2013, Brian Gladman, Worcester, UK. All rights reserved. + +The redistribution and use of this software (with or without changes) +is allowed without the payment of fees or royalties provided that: + + source code distributions include the above copyright notice, this + list of conditions and the following disclaimer; + + binary distributions include the above copyright notice, this list + of conditions and the following disclaimer in their documentation. + +This software is provided 'as is' with no explicit or implied warranties +in respect of its operation, including, but not limited to, correctness +and fitness for purpose. +--------------------------------------------------------------------------- +Issue Date: 20/12/2007 + + This file contains the compilation options for AES (Rijndael) and code + that is common across encryption, key scheduling and table generation. + + OPERATION + + These source code files implement the AES algorithm Rijndael designed by + Joan Daemen and Vincent Rijmen. This version is designed for the standard + block size of 16 bytes and for key sizes of 128, 192 and 256 bits (16, 24 + and 32 bytes). + + This version is designed for flexibility and speed using operations on + 32-bit words rather than operations on bytes. It can be compiled with + either big or little endian internal byte order but is faster when the + native byte order for the processor is used. + + THE CIPHER INTERFACE + + The cipher interface is implemented as an array of bytes in which lower + AES bit sequence indexes map to higher numeric significance within bytes. + + uint8_t (an unsigned 8-bit type) + uint32_t (an unsigned 32-bit type) + struct aes_encrypt_ctx (structure for the cipher encryption context) + struct aes_decrypt_ctx (structure for the cipher decryption context) + AES_RETURN the function return type + + C subroutine calls: + + AES_RETURN aes_encrypt_key128(const unsigned char *key, aes_encrypt_ctx cx[1]); + AES_RETURN aes_encrypt_key192(const unsigned char *key, aes_encrypt_ctx cx[1]); + AES_RETURN aes_encrypt_key256(const unsigned char *key, aes_encrypt_ctx cx[1]); + AES_RETURN aes_encrypt(const unsigned char *in, unsigned char *out, + const aes_encrypt_ctx cx[1]); + + AES_RETURN aes_decrypt_key128(const unsigned char *key, aes_decrypt_ctx cx[1]); + AES_RETURN aes_decrypt_key192(const unsigned char *key, aes_decrypt_ctx cx[1]); + AES_RETURN aes_decrypt_key256(const unsigned char *key, aes_decrypt_ctx cx[1]); + AES_RETURN aes_decrypt(const unsigned char *in, unsigned char *out, + const aes_decrypt_ctx cx[1]); + + IMPORTANT NOTE: If you are using this C interface with dynamic tables make sure that + you call aes_init() before AES is used so that the tables are initialised. + + C++ aes class subroutines: + + Class AESencrypt for encryption + + Constructors: + AESencrypt(void) + AESencrypt(const unsigned char *key) - 128 bit key + Members: + AES_RETURN key128(const unsigned char *key) + AES_RETURN key192(const unsigned char *key) + AES_RETURN key256(const unsigned char *key) + AES_RETURN encrypt(const unsigned char *in, unsigned char *out) const + + Class AESdecrypt for encryption + Constructors: + AESdecrypt(void) + AESdecrypt(const unsigned char *key) - 128 bit key + Members: + AES_RETURN key128(const unsigned char *key) + AES_RETURN key192(const unsigned char *key) + AES_RETURN key256(const unsigned char *key) + AES_RETURN decrypt(const unsigned char *in, unsigned char *out) const +*/ + +#if !defined(_AESOPT_H) +#define _AESOPT_H + +#if defined(__cplusplus) +#include "aescpp.h" +#else +#include "aes.h" +#endif + +/* PLATFORM SPECIFIC INCLUDES */ + +#define IS_BIG_ENDIAN 4321 +#define IS_LITTLE_ENDIAN 1234 +#define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN + +/* CONFIGURATION - THE USE OF DEFINES + + Later in this section there are a number of defines that control the + operation of the code. In each section, the purpose of each define is + explained so that the relevant form can be included or excluded by + setting either 1's or 0's respectively on the branches of the related + #if clauses. The following local defines should not be changed. +*/ + +#define ENCRYPTION_IN_C 1 +#define DECRYPTION_IN_C 2 +#define ENC_KEYING_IN_C 4 +#define DEC_KEYING_IN_C 8 + +#define NO_TABLES 0 +#define ONE_TABLE 1 +#define FOUR_TABLES 4 +#define NONE 0 +#define PARTIAL 1 +#define FULL 2 + +/* --- START OF USER CONFIGURED OPTIONS --- */ + +/* 1. BYTE ORDER WITHIN 32 BIT WORDS + + The fundamental data processing units in Rijndael are 8-bit bytes. The + input, output and key input are all enumerated arrays of bytes in which + bytes are numbered starting at zero and increasing to one less than the + number of bytes in the array in question. This enumeration is only used + for naming bytes and does not imply any adjacency or order relationship + from one byte to another. When these inputs and outputs are considered + as bit sequences, bits 8*n to 8*n+7 of the bit sequence are mapped to + byte[n] with bit 8n+i in the sequence mapped to bit 7-i within the byte. + In this implementation bits are numbered from 0 to 7 starting at the + numerically least significant end of each byte (bit n represents 2^n). + + However, Rijndael can be implemented more efficiently using 32-bit + words by packing bytes into words so that bytes 4*n to 4*n+3 are placed + into word[n]. While in principle these bytes can be assembled into words + in any positions, this implementation only supports the two formats in + which bytes in adjacent positions within words also have adjacent byte + numbers. This order is called big-endian if the lowest numbered bytes + in words have the highest numeric significance and little-endian if the + opposite applies. + + This code can work in either order irrespective of the order used by the + machine on which it runs. Normally the internal byte order will be set + to the order of the processor on which the code is to be run but this + define can be used to reverse this in special situations + + WARNING: Assembler code versions rely on PLATFORM_BYTE_ORDER being set. + This define will hence be redefined later (in section 4) if necessary +*/ + +#if 1 +#define ALGORITHM_BYTE_ORDER PLATFORM_BYTE_ORDER +#elif 0 +#define ALGORITHM_BYTE_ORDER IS_LITTLE_ENDIAN +#elif 0 +#define ALGORITHM_BYTE_ORDER IS_BIG_ENDIAN +#else +#error The algorithm byte order is not defined +#endif + +/* 2. Intel AES AND VIA ACE SUPPORT */ + +#if defined(__GNUC__) && defined(__i386__) && !defined(__BEOS__) || \ + defined(_WIN32) && defined(_M_IX86) && \ + !(defined(_WIN64) || defined(_WIN32_WCE) || defined(_MSC_VER) && (_MSC_VER <= 800)) +#define VIA_ACE_POSSIBLE +#endif + +/* AESNI is supported by all Windows x64 compilers, but for Linux/GCC + we have to test for SSE 2, SSE 3, and AES to before enabling it; */ +#if !defined(INTEL_AES_POSSIBLE) +#if defined(_WIN64) && defined(_MSC_VER) || defined(__GNUC__) && defined(__x86_64__) && \ + defined(__SSE2__) && defined(__SSE3__) && \ + defined(__AES__) +#define INTEL_AES_POSSIBLE +#endif +#endif + +/* Define this option if support for the Intel AESNI is required + If USE_INTEL_AES_IF_PRESENT is defined then AESNI will be used + if it is detected (both present and enabled). + + AESNI uses a decryption key schedule with the first decryption + round key at the high end of the key scedule with the following + round keys at lower positions in memory. So AES_REV_DKS must NOT + be defined when AESNI will be used. Although it is unlikely that + assembler code will be used with an AESNI build, if it is then + AES_REV_DKS must NOT be defined when the assembler files are + built (the definition of USE_INTEL_AES_IF_PRESENT in the assembler + code files must match that here if they are used). +*/ + +#if 0 && defined(INTEL_AES_POSSIBLE) && !defined(USE_INTEL_AES_IF_PRESENT) +#define USE_INTEL_AES_IF_PRESENT +#endif + +/* Define this option if support for the VIA ACE is required. This uses + inline assembler instructions and is only implemented for the Microsoft, + Intel and GCC compilers. If VIA ACE is known to be present, then defining + ASSUME_VIA_ACE_PRESENT will remove the ordinary encryption/decryption + code. If USE_VIA_ACE_IF_PRESENT is defined then VIA ACE will be used if + it is detected (both present and enabled) but the normal AES code will + also be present. + + When VIA ACE is to be used, all AES encryption contexts MUST be 16 byte + aligned; other input/output buffers do not need to be 16 byte aligned + but there are very large performance gains if this can be arranged. + VIA ACE also requires the decryption key schedule to be in reverse + order (which later checks below ensure). + + AES_REV_DKS must be set for assembler code used with a VIA ACE build +*/ + +#if 0 && defined(VIA_ACE_POSSIBLE) && !defined(USE_VIA_ACE_IF_PRESENT) +#define USE_VIA_ACE_IF_PRESENT +#endif + +#if 0 && defined(VIA_ACE_POSSIBLE) && !defined(ASSUME_VIA_ACE_PRESENT) +#define ASSUME_VIA_ACE_PRESENT +#endif + +/* 3. ASSEMBLER SUPPORT + + This define (which can be on the command line) enables the use of the + assembler code routines for encryption, decryption and key scheduling + as follows: + + ASM_X86_V1C uses the assembler (aes_x86_v1.asm) with large tables for + encryption and decryption and but with key scheduling in C + ASM_X86_V2 uses assembler (aes_x86_v2.asm) with compressed tables for + encryption, decryption and key scheduling + ASM_X86_V2C uses assembler (aes_x86_v2.asm) with compressed tables for + encryption and decryption and but with key scheduling in C + ASM_AMD64_C uses assembler (aes_amd64.asm) with compressed tables for + encryption and decryption and but with key scheduling in C + + Change one 'if 0' below to 'if 1' to select the version or define + as a compilation option. +*/ + +#if 0 && !defined(ASM_X86_V1C) +#define ASM_X86_V1C +#elif 0 && !defined(ASM_X86_V2) +#define ASM_X86_V2 +#elif 0 && !defined(ASM_X86_V2C) +#define ASM_X86_V2C +#elif 0 && !defined(ASM_AMD64_C) +#define ASM_AMD64_C +#endif + +#if defined(__i386) || defined(_M_IX86) +#define A32_ +#elif defined(__x86_64__) || defined(_M_X64) +#define A64_ +#endif + +#if(defined(ASM_X86_V1C) || defined(ASM_X86_V2) || defined(ASM_X86_V2C)) && !defined(A32_) || \ + defined(ASM_AMD64_C) && !defined(A64_) +#error Assembler code is only available for x86 and AMD64 systems +#endif + +/* 4. FAST INPUT/OUTPUT OPERATIONS. + + On some machines it is possible to improve speed by transferring the + bytes in the input and output arrays to and from the internal 32-bit + variables by addressing these arrays as if they are arrays of 32-bit + words. On some machines this will always be possible but there may + be a large performance penalty if the byte arrays are not aligned on + the normal word boundaries. On other machines this technique will + lead to memory access errors when such 32-bit word accesses are not + properly aligned. The option SAFE_IO avoids such problems but will + often be slower on those machines that support misaligned access + (especially so if care is taken to align the input and output byte + arrays on 32-bit word boundaries). If SAFE_IO is not defined it is + assumed that access to byte arrays as if they are arrays of 32-bit + words will not cause problems when such accesses are misaligned. +*/ +#if 1 && !defined(_MSC_VER) +#define SAFE_IO +#endif + +/* 5. LOOP UNROLLING + + The code for encryption and decrytpion cycles through a number of rounds + that can be implemented either in a loop or by expanding the code into a + long sequence of instructions, the latter producing a larger program but + one that will often be much faster. The latter is called loop unrolling. + There are also potential speed advantages in expanding two iterations in + a loop with half the number of iterations, which is called partial loop + unrolling. The following options allow partial or full loop unrolling + to be set independently for encryption and decryption +*/ +#if 1 +#define ENC_UNROLL FULL +#elif 0 +#define ENC_UNROLL PARTIAL +#else +#define ENC_UNROLL NONE +#endif + +#if 1 +#define DEC_UNROLL FULL +#elif 0 +#define DEC_UNROLL PARTIAL +#else +#define DEC_UNROLL NONE +#endif + +#if 1 +#define ENC_KS_UNROLL +#endif + +#if 1 +#define DEC_KS_UNROLL +#endif + +/* 6. FAST FINITE FIELD OPERATIONS + + If this section is included, tables are used to provide faster finite + field arithmetic (this has no effect if STATIC_TABLES is defined). +*/ +#if 1 +#define FF_TABLES +#endif + +/* 7. INTERNAL STATE VARIABLE FORMAT + + The internal state of Rijndael is stored in a number of local 32-bit + word varaibles which can be defined either as an array or as individual + names variables. Include this section if you want to store these local + varaibles in arrays. Otherwise individual local variables will be used. +*/ +#if 1 +#define ARRAYS +#endif + +/* 8. FIXED OR DYNAMIC TABLES + + When this section is included the tables used by the code are compiled + statically into the binary file. Otherwise the subroutine aes_init() + must be called to compute them before the code is first used. +*/ +#if 1 && !(defined(_MSC_VER) && (_MSC_VER <= 800)) +#define STATIC_TABLES +#endif + +/* 9. MASKING OR CASTING FROM LONGER VALUES TO BYTES + + In some systems it is better to mask longer values to extract bytes + rather than using a cast. This option allows this choice. +*/ +#if 0 +#define to_byte(x) ((uint8_t)(x)) +#else +#define to_byte(x) ((x)&0xff) +#endif + +/* 10. TABLE ALIGNMENT + + On some sytsems speed will be improved by aligning the AES large lookup + tables on particular boundaries. This define should be set to a power of + two giving the desired alignment. It can be left undefined if alignment + is not needed. This option is specific to the Microsft VC++ compiler - + it seems to sometimes cause trouble for the VC++ version 6 compiler. +*/ + +#if 1 && defined(_MSC_VER) && (_MSC_VER >= 1300) +#define TABLE_ALIGN 32 +#endif + +/* 11. REDUCE CODE AND TABLE SIZE + + This replaces some expanded macros with function calls if AES_ASM_V2 or + AES_ASM_V2C are defined +*/ + +#if 1 && (defined(ASM_X86_V2) || defined(ASM_X86_V2C)) +#define REDUCE_CODE_SIZE +#endif + +/* 12. TABLE OPTIONS + + This cipher proceeds by repeating in a number of cycles known as 'rounds' + which are implemented by a round function which can optionally be speeded + up using tables. The basic tables are each 256 32-bit words, with either + one or four tables being required for each round function depending on + how much speed is required. The encryption and decryption round functions + are different and the last encryption and decrytpion round functions are + different again making four different round functions in all. + + This means that: + 1. Normal encryption and decryption rounds can each use either 0, 1 + or 4 tables and table spaces of 0, 1024 or 4096 bytes each. + 2. The last encryption and decryption rounds can also use either 0, 1 + or 4 tables and table spaces of 0, 1024 or 4096 bytes each. + + Include or exclude the appropriate definitions below to set the number + of tables used by this implementation. +*/ + +#if 1 /* set tables for the normal encryption round */ +#define ENC_ROUND FOUR_TABLES +#elif 0 +#define ENC_ROUND ONE_TABLE +#else +#define ENC_ROUND NO_TABLES +#endif + +#if 1 /* set tables for the last encryption round */ +#define LAST_ENC_ROUND FOUR_TABLES +#elif 0 +#define LAST_ENC_ROUND ONE_TABLE +#else +#define LAST_ENC_ROUND NO_TABLES +#endif + +#if 1 /* set tables for the normal decryption round */ +#define DEC_ROUND FOUR_TABLES +#elif 0 +#define DEC_ROUND ONE_TABLE +#else +#define DEC_ROUND NO_TABLES +#endif + +#if 1 /* set tables for the last decryption round */ +#define LAST_DEC_ROUND FOUR_TABLES +#elif 0 +#define LAST_DEC_ROUND ONE_TABLE +#else +#define LAST_DEC_ROUND NO_TABLES +#endif + +/* The decryption key schedule can be speeded up with tables in the same + way that the round functions can. Include or exclude the following + defines to set this requirement. +*/ +#if 1 +#define KEY_SCHED FOUR_TABLES +#elif 0 +#define KEY_SCHED ONE_TABLE +#else +#define KEY_SCHED NO_TABLES +#endif + +/* ---- END OF USER CONFIGURED OPTIONS ---- */ + +/* VIA ACE support is only available for VC++ and GCC */ + +#if !defined(_MSC_VER) && !defined(__GNUC__) +#if defined(ASSUME_VIA_ACE_PRESENT) +#undef ASSUME_VIA_ACE_PRESENT +#endif +#if defined(USE_VIA_ACE_IF_PRESENT) +#undef USE_VIA_ACE_IF_PRESENT +#endif +#endif + +#if defined(ASSUME_VIA_ACE_PRESENT) && !defined(USE_VIA_ACE_IF_PRESENT) +#define USE_VIA_ACE_IF_PRESENT +#endif + +/* define to reverse decryption key schedule */ +#if 1 || defined(USE_VIA_ACE_IF_PRESENT) && !defined(AES_REV_DKS) +#define AES_REV_DKS +#endif + +/* Intel AESNI uses a decryption key schedule in the encryption order */ +#if defined(USE_INTEL_AES_IF_PRESENT) && defined(AES_REV_DKS) +#undef AES_REV_DKS +#endif + +/* Assembler support requires the use of platform byte order */ + +#if(defined(ASM_X86_V1C) || defined(ASM_X86_V2C) || defined(ASM_AMD64_C)) && \ + (ALGORITHM_BYTE_ORDER != PLATFORM_BYTE_ORDER) +#undef ALGORITHM_BYTE_ORDER +#define ALGORITHM_BYTE_ORDER PLATFORM_BYTE_ORDER +#endif + +/* In this implementation the columns of the state array are each held in + 32-bit words. The state array can be held in various ways: in an array + of words, in a number of individual word variables or in a number of + processor registers. The following define maps a variable name x and + a column number c to the way the state array variable is to be held. + The first define below maps the state into an array x[c] whereas the + second form maps the state into a number of individual variables x0, + x1, etc. Another form could map individual state colums to machine + register names. +*/ + +#if defined(ARRAYS) +#define s(x, c) x[c] +#else +#define s(x, c) x##c +#endif + +/* This implementation provides subroutines for encryption, decryption + and for setting the three key lengths (separately) for encryption + and decryption. Since not all functions are needed, masks are set + up here to determine which will be implemented in C +*/ + +#if !defined(AES_ENCRYPT) +#define EFUNCS_IN_C 0 +#elif defined(ASSUME_VIA_ACE_PRESENT) || defined(ASM_X86_V1C) || defined(ASM_X86_V2C) || \ + defined(ASM_AMD64_C) +#define EFUNCS_IN_C ENC_KEYING_IN_C +#elif !defined(ASM_X86_V2) +#define EFUNCS_IN_C (ENCRYPTION_IN_C | ENC_KEYING_IN_C) +#else +#define EFUNCS_IN_C 0 +#endif + +#if !defined(AES_DECRYPT) +#define DFUNCS_IN_C 0 +#elif defined(ASSUME_VIA_ACE_PRESENT) || defined(ASM_X86_V1C) || defined(ASM_X86_V2C) || \ + defined(ASM_AMD64_C) +#define DFUNCS_IN_C DEC_KEYING_IN_C +#elif !defined(ASM_X86_V2) +#define DFUNCS_IN_C (DECRYPTION_IN_C | DEC_KEYING_IN_C) +#else +#define DFUNCS_IN_C 0 +#endif + +#define FUNCS_IN_C (EFUNCS_IN_C | DFUNCS_IN_C) + +/* END OF CONFIGURATION OPTIONS */ + +#define RC_LENGTH (5 * (AES_BLOCK_SIZE / 4 - 2)) + +/* Disable or report errors on some combinations of options */ + +#if ENC_ROUND == NO_TABLES && LAST_ENC_ROUND != NO_TABLES +#undef LAST_ENC_ROUND +#define LAST_ENC_ROUND NO_TABLES +#elif ENC_ROUND == ONE_TABLE && LAST_ENC_ROUND == FOUR_TABLES +#undef LAST_ENC_ROUND +#define LAST_ENC_ROUND ONE_TABLE +#endif + +#if ENC_ROUND == NO_TABLES && ENC_UNROLL != NONE +#undef ENC_UNROLL +#define ENC_UNROLL NONE +#endif + +#if DEC_ROUND == NO_TABLES && LAST_DEC_ROUND != NO_TABLES +#undef LAST_DEC_ROUND +#define LAST_DEC_ROUND NO_TABLES +#elif DEC_ROUND == ONE_TABLE && LAST_DEC_ROUND == FOUR_TABLES +#undef LAST_DEC_ROUND +#define LAST_DEC_ROUND ONE_TABLE +#endif + +#if DEC_ROUND == NO_TABLES && DEC_UNROLL != NONE +#undef DEC_UNROLL +#define DEC_UNROLL NONE +#endif + +#if defined(bswap32) +#define aes_sw32 bswap32 +#elif defined(bswap_32) +#define aes_sw32 bswap_32 +#else +#define brot(x, n) (((uint32_t)(x) << n) | ((uint32_t)(x) >> (32 - n))) +#define aes_sw32(x) ((brot((x), 8) & 0x00ff00ff) | (brot((x), 24) & 0xff00ff00)) +#endif + +/* upr(x,n): rotates bytes within words by n positions, moving bytes to + higher index positions with wrap around into low positions + ups(x,n): moves bytes by n positions to higher index positions in + words but without wrap around + bval(x,n): extracts a byte from a word + + WARNING: The definitions given here are intended only for use with + unsigned variables and with shift counts that are compile + time constants +*/ + +#if(ALGORITHM_BYTE_ORDER == IS_LITTLE_ENDIAN) +#define upr(x, n) (((uint32_t)(x) << (8 * (n))) | ((uint32_t)(x) >> (32 - 8 * (n)))) +#define ups(x, n) ((uint32_t)(x) << (8 * (n))) +#define bval(x, n) to_byte((x) >> (8 * (n))) +#define bytes2word(b0, b1, b2, b3) \ + (((uint32_t)(b3) << 24) | ((uint32_t)(b2) << 16) | ((uint32_t)(b1) << 8) | (b0)) +#endif + +#if(ALGORITHM_BYTE_ORDER == IS_BIG_ENDIAN) +#define upr(x, n) (((uint32_t)(x) >> (8 * (n))) | ((uint32_t)(x) << (32 - 8 * (n)))) +#define ups(x, n) ((uint32_t)(x) >> (8 * (n))) +#define bval(x, n) to_byte((x) >> (24 - 8 * (n))) +#define bytes2word(b0, b1, b2, b3) \ + (((uint32_t)(b0) << 24) | ((uint32_t)(b1) << 16) | ((uint32_t)(b2) << 8) | (b3)) +#endif + +#if defined(SAFE_IO) +#define word_in(x, c) \ + bytes2word( \ + ((const uint8_t*)(x) + 4 * c)[0], \ + ((const uint8_t*)(x) + 4 * c)[1], \ + ((const uint8_t*)(x) + 4 * c)[2], \ + ((const uint8_t*)(x) + 4 * c)[3]) +#define word_out(x, c, v) \ + { \ + ((uint8_t*)(x) + 4 * c)[0] = bval(v, 0); \ + ((uint8_t*)(x) + 4 * c)[1] = bval(v, 1); \ + ((uint8_t*)(x) + 4 * c)[2] = bval(v, 2); \ + ((uint8_t*)(x) + 4 * c)[3] = bval(v, 3); \ + } +#elif(ALGORITHM_BYTE_ORDER == PLATFORM_BYTE_ORDER) +#define word_in(x, c) (*((uint32_t*)(x) + (c))) +#define word_out(x, c, v) (*((uint32_t*)(x) + (c)) = (v)) +#else +#define word_in(x, c) aes_sw32(*((uint32_t*)(x) + (c))) +#define word_out(x, c, v) (*((uint32_t*)(x) + (c)) = aes_sw32(v)) +#endif + +/* the finite field modular polynomial and elements */ + +#define WPOLY 0x011b +#define BPOLY 0x1b + +/* multiply four bytes in GF(2^8) by 'x' {02} in parallel */ + +#define gf_c1 0x80808080 +#define gf_c2 0x7f7f7f7f +#define gf_mulx(x) ((((x)&gf_c2) << 1) ^ ((((x)&gf_c1) >> 7) * BPOLY)) + +/* The following defines provide alternative definitions of gf_mulx that might + give improved performance if a fast 32-bit multiply is not available. Note + that a temporary variable u needs to be defined where gf_mulx is used. + +#define gf_mulx(x) (u = (x) & gf_c1, u |= (u >> 1), ((x) & gf_c2) << 1) ^ ((u >> 3) | (u >> 6)) +#define gf_c4 (0x01010101 * BPOLY) +#define gf_mulx(x) (u = (x) & gf_c1, ((x) & gf_c2) << 1) ^ ((u - (u >> 7)) & gf_c4) +*/ + +/* Work out which tables are needed for the different options */ + +#if defined(ASM_X86_V1C) +#if defined(ENC_ROUND) +#undef ENC_ROUND +#endif +#define ENC_ROUND FOUR_TABLES +#if defined(LAST_ENC_ROUND) +#undef LAST_ENC_ROUND +#endif +#define LAST_ENC_ROUND FOUR_TABLES +#if defined(DEC_ROUND) +#undef DEC_ROUND +#endif +#define DEC_ROUND FOUR_TABLES +#if defined(LAST_DEC_ROUND) +#undef LAST_DEC_ROUND +#endif +#define LAST_DEC_ROUND FOUR_TABLES +#if defined(KEY_SCHED) +#undef KEY_SCHED +#define KEY_SCHED FOUR_TABLES +#endif +#endif + +#if(FUNCS_IN_C & ENCRYPTION_IN_C) || defined(ASM_X86_V1C) +#if ENC_ROUND == ONE_TABLE +#define FT1_SET +#elif ENC_ROUND == FOUR_TABLES +#define FT4_SET +#else +#define SBX_SET +#endif +#if LAST_ENC_ROUND == ONE_TABLE +#define FL1_SET +#elif LAST_ENC_ROUND == FOUR_TABLES +#define FL4_SET +#elif !defined(SBX_SET) +#define SBX_SET +#endif +#endif + +#if(FUNCS_IN_C & DECRYPTION_IN_C) || defined(ASM_X86_V1C) +#if DEC_ROUND == ONE_TABLE +#define IT1_SET +#elif DEC_ROUND == FOUR_TABLES +#define IT4_SET +#else +#define ISB_SET +#endif +#if LAST_DEC_ROUND == ONE_TABLE +#define IL1_SET +#elif LAST_DEC_ROUND == FOUR_TABLES +#define IL4_SET +#elif !defined(ISB_SET) +#define ISB_SET +#endif +#endif + +#if !(defined(REDUCE_CODE_SIZE) && (defined(ASM_X86_V2) || defined(ASM_X86_V2C))) +#if((FUNCS_IN_C & ENC_KEYING_IN_C) || (FUNCS_IN_C & DEC_KEYING_IN_C)) +#if KEY_SCHED == ONE_TABLE +#if !defined(FL1_SET) && !defined(FL4_SET) +#define LS1_SET +#endif +#elif KEY_SCHED == FOUR_TABLES +#if !defined(FL4_SET) +#define LS4_SET +#endif +#elif !defined(SBX_SET) +#define SBX_SET +#endif +#endif +#if(FUNCS_IN_C & DEC_KEYING_IN_C) +#if KEY_SCHED == ONE_TABLE +#define IM1_SET +#elif KEY_SCHED == FOUR_TABLES +#define IM4_SET +#elif !defined(SBX_SET) +#define SBX_SET +#endif +#endif +#endif + +/* generic definitions of Rijndael macros that use tables */ + +#define no_table(x, box, vf, rf, c) \ + bytes2word( \ + box[bval(vf(x, 0, c), rf(0, c))], \ + box[bval(vf(x, 1, c), rf(1, c))], \ + box[bval(vf(x, 2, c), rf(2, c))], \ + box[bval(vf(x, 3, c), rf(3, c))]) + +#define one_table(x, op, tab, vf, rf, c) \ + (tab[bval(vf(x, 0, c), rf(0, c))] ^ op(tab[bval(vf(x, 1, c), rf(1, c))], 1) ^ \ + op(tab[bval(vf(x, 2, c), rf(2, c))], 2) ^ op(tab[bval(vf(x, 3, c), rf(3, c))], 3)) + +#define four_tables(x, tab, vf, rf, c) \ + (tab[0][bval(vf(x, 0, c), rf(0, c))] ^ tab[1][bval(vf(x, 1, c), rf(1, c))] ^ \ + tab[2][bval(vf(x, 2, c), rf(2, c))] ^ tab[3][bval(vf(x, 3, c), rf(3, c))]) + +#define vf1(x, r, c) (x) +#define rf1(r, c) (r) +#define rf2(r, c) ((8 + r - c) & 3) + +/* perform forward and inverse column mix operation on four bytes in long word x in */ +/* parallel. NOTE: x must be a simple variable, NOT an expression in these macros. */ + +#if !(defined(REDUCE_CODE_SIZE) && (defined(ASM_X86_V2) || defined(ASM_X86_V2C))) + +#if defined(FM4_SET) /* not currently used */ +#define fwd_mcol(x) four_tables(x, t_use(f, m), vf1, rf1, 0) +#elif defined(FM1_SET) /* not currently used */ +#define fwd_mcol(x) one_table(x, upr, t_use(f, m), vf1, rf1, 0) +#else +#define dec_fmvars uint32_t g2 +#define fwd_mcol(x) (g2 = gf_mulx(x), g2 ^ upr((x) ^ g2, 3) ^ upr((x), 2) ^ upr((x), 1)) +#endif + +#if defined(IM4_SET) +#define inv_mcol(x) four_tables(x, t_use(i, m), vf1, rf1, 0) +#elif defined(IM1_SET) +#define inv_mcol(x) one_table(x, upr, t_use(i, m), vf1, rf1, 0) +#else +#define dec_imvars uint32_t g2, g4, g9 +#define inv_mcol(x) \ + (g2 = gf_mulx(x), \ + g4 = gf_mulx(g2), \ + g9 = (x) ^ gf_mulx(g4), \ + g4 ^= g9, \ + (x) ^ g2 ^ g4 ^ upr(g2 ^ g9, 3) ^ upr(g4, 2) ^ upr(g9, 1)) +#endif + +#if defined(FL4_SET) +#define ls_box(x, c) four_tables(x, t_use(f, l), vf1, rf2, c) +#elif defined(LS4_SET) +#define ls_box(x, c) four_tables(x, t_use(l, s), vf1, rf2, c) +#elif defined(FL1_SET) +#define ls_box(x, c) one_table(x, upr, t_use(f, l), vf1, rf2, c) +#elif defined(LS1_SET) +#define ls_box(x, c) one_table(x, upr, t_use(l, s), vf1, rf2, c) +#else +#define ls_box(x, c) no_table(x, t_use(s, box), vf1, rf2, c) +#endif + +#endif + +#if defined(ASM_X86_V1C) && defined(AES_DECRYPT) && !defined(ISB_SET) +#define ISB_SET +#endif + +#endif diff --git a/applications/external/flipbip/lib/crypto/aes/aestab.c b/applications/external/flipbip/lib/crypto/aes/aestab.c new file mode 100644 index 0000000000..0bfb7808fe --- /dev/null +++ b/applications/external/flipbip/lib/crypto/aes/aestab.c @@ -0,0 +1,403 @@ +/* +--------------------------------------------------------------------------- +Copyright (c) 1998-2013, Brian Gladman, Worcester, UK. All rights reserved. + +The redistribution and use of this software (with or without changes) +is allowed without the payment of fees or royalties provided that: + + source code distributions include the above copyright notice, this + list of conditions and the following disclaimer; + + binary distributions include the above copyright notice, this list + of conditions and the following disclaimer in their documentation. + +This software is provided 'as is' with no explicit or implied warranties +in respect of its operation, including, but not limited to, correctness +and fitness for purpose. +--------------------------------------------------------------------------- +Issue Date: 20/12/2007 +*/ + +#define DO_TABLES + +#include "aes.h" +#include "aesopt.h" + +#if defined(STATIC_TABLES) + +#define sb_data(w) \ + { \ + w(0x63), w(0x7c), w(0x77), w(0x7b), w(0xf2), w(0x6b), w(0x6f), w(0xc5), w(0x30), w(0x01), \ + w(0x67), w(0x2b), w(0xfe), w(0xd7), w(0xab), w(0x76), w(0xca), w(0x82), w(0xc9), \ + w(0x7d), w(0xfa), w(0x59), w(0x47), w(0xf0), w(0xad), w(0xd4), w(0xa2), w(0xaf), \ + w(0x9c), w(0xa4), w(0x72), w(0xc0), w(0xb7), w(0xfd), w(0x93), w(0x26), w(0x36), \ + w(0x3f), w(0xf7), w(0xcc), w(0x34), w(0xa5), w(0xe5), w(0xf1), w(0x71), w(0xd8), \ + w(0x31), w(0x15), w(0x04), w(0xc7), w(0x23), w(0xc3), w(0x18), w(0x96), w(0x05), \ + w(0x9a), w(0x07), w(0x12), w(0x80), w(0xe2), w(0xeb), w(0x27), w(0xb2), w(0x75), \ + w(0x09), w(0x83), w(0x2c), w(0x1a), w(0x1b), w(0x6e), w(0x5a), w(0xa0), w(0x52), \ + w(0x3b), w(0xd6), w(0xb3), w(0x29), w(0xe3), w(0x2f), w(0x84), w(0x53), w(0xd1), \ + w(0x00), w(0xed), w(0x20), w(0xfc), w(0xb1), w(0x5b), w(0x6a), w(0xcb), w(0xbe), \ + w(0x39), w(0x4a), w(0x4c), w(0x58), w(0xcf), w(0xd0), w(0xef), w(0xaa), w(0xfb), \ + w(0x43), w(0x4d), w(0x33), w(0x85), w(0x45), w(0xf9), w(0x02), w(0x7f), w(0x50), \ + w(0x3c), w(0x9f), w(0xa8), w(0x51), w(0xa3), w(0x40), w(0x8f), w(0x92), w(0x9d), \ + w(0x38), w(0xf5), w(0xbc), w(0xb6), w(0xda), w(0x21), w(0x10), w(0xff), w(0xf3), \ + w(0xd2), w(0xcd), w(0x0c), w(0x13), w(0xec), w(0x5f), w(0x97), w(0x44), w(0x17), \ + w(0xc4), w(0xa7), w(0x7e), w(0x3d), w(0x64), w(0x5d), w(0x19), w(0x73), w(0x60), \ + w(0x81), w(0x4f), w(0xdc), w(0x22), w(0x2a), w(0x90), w(0x88), w(0x46), w(0xee), \ + w(0xb8), w(0x14), w(0xde), w(0x5e), w(0x0b), w(0xdb), w(0xe0), w(0x32), w(0x3a), \ + w(0x0a), w(0x49), w(0x06), w(0x24), w(0x5c), w(0xc2), w(0xd3), w(0xac), w(0x62), \ + w(0x91), w(0x95), w(0xe4), w(0x79), w(0xe7), w(0xc8), w(0x37), w(0x6d), w(0x8d), \ + w(0xd5), w(0x4e), w(0xa9), w(0x6c), w(0x56), w(0xf4), w(0xea), w(0x65), w(0x7a), \ + w(0xae), w(0x08), w(0xba), w(0x78), w(0x25), w(0x2e), w(0x1c), w(0xa6), w(0xb4), \ + w(0xc6), w(0xe8), w(0xdd), w(0x74), w(0x1f), w(0x4b), w(0xbd), w(0x8b), w(0x8a), \ + w(0x70), w(0x3e), w(0xb5), w(0x66), w(0x48), w(0x03), w(0xf6), w(0x0e), w(0x61), \ + w(0x35), w(0x57), w(0xb9), w(0x86), w(0xc1), w(0x1d), w(0x9e), w(0xe1), w(0xf8), \ + w(0x98), w(0x11), w(0x69), w(0xd9), w(0x8e), w(0x94), w(0x9b), w(0x1e), w(0x87), \ + w(0xe9), w(0xce), w(0x55), w(0x28), w(0xdf), w(0x8c), w(0xa1), w(0x89), w(0x0d), \ + w(0xbf), w(0xe6), w(0x42), w(0x68), w(0x41), w(0x99), w(0x2d), w(0x0f), w(0xb0), \ + w(0x54), w(0xbb), w(0x16) \ + } + +#define isb_data(w) \ + { \ + w(0x52), w(0x09), w(0x6a), w(0xd5), w(0x30), w(0x36), w(0xa5), w(0x38), w(0xbf), w(0x40), \ + w(0xa3), w(0x9e), w(0x81), w(0xf3), w(0xd7), w(0xfb), w(0x7c), w(0xe3), w(0x39), \ + w(0x82), w(0x9b), w(0x2f), w(0xff), w(0x87), w(0x34), w(0x8e), w(0x43), w(0x44), \ + w(0xc4), w(0xde), w(0xe9), w(0xcb), w(0x54), w(0x7b), w(0x94), w(0x32), w(0xa6), \ + w(0xc2), w(0x23), w(0x3d), w(0xee), w(0x4c), w(0x95), w(0x0b), w(0x42), w(0xfa), \ + w(0xc3), w(0x4e), w(0x08), w(0x2e), w(0xa1), w(0x66), w(0x28), w(0xd9), w(0x24), \ + w(0xb2), w(0x76), w(0x5b), w(0xa2), w(0x49), w(0x6d), w(0x8b), w(0xd1), w(0x25), \ + w(0x72), w(0xf8), w(0xf6), w(0x64), w(0x86), w(0x68), w(0x98), w(0x16), w(0xd4), \ + w(0xa4), w(0x5c), w(0xcc), w(0x5d), w(0x65), w(0xb6), w(0x92), w(0x6c), w(0x70), \ + w(0x48), w(0x50), w(0xfd), w(0xed), w(0xb9), w(0xda), w(0x5e), w(0x15), w(0x46), \ + w(0x57), w(0xa7), w(0x8d), w(0x9d), w(0x84), w(0x90), w(0xd8), w(0xab), w(0x00), \ + w(0x8c), w(0xbc), w(0xd3), w(0x0a), w(0xf7), w(0xe4), w(0x58), w(0x05), w(0xb8), \ + w(0xb3), w(0x45), w(0x06), w(0xd0), w(0x2c), w(0x1e), w(0x8f), w(0xca), w(0x3f), \ + w(0x0f), w(0x02), w(0xc1), w(0xaf), w(0xbd), w(0x03), w(0x01), w(0x13), w(0x8a), \ + w(0x6b), w(0x3a), w(0x91), w(0x11), w(0x41), w(0x4f), w(0x67), w(0xdc), w(0xea), \ + w(0x97), w(0xf2), w(0xcf), w(0xce), w(0xf0), w(0xb4), w(0xe6), w(0x73), w(0x96), \ + w(0xac), w(0x74), w(0x22), w(0xe7), w(0xad), w(0x35), w(0x85), w(0xe2), w(0xf9), \ + w(0x37), w(0xe8), w(0x1c), w(0x75), w(0xdf), w(0x6e), w(0x47), w(0xf1), w(0x1a), \ + w(0x71), w(0x1d), w(0x29), w(0xc5), w(0x89), w(0x6f), w(0xb7), w(0x62), w(0x0e), \ + w(0xaa), w(0x18), w(0xbe), w(0x1b), w(0xfc), w(0x56), w(0x3e), w(0x4b), w(0xc6), \ + w(0xd2), w(0x79), w(0x20), w(0x9a), w(0xdb), w(0xc0), w(0xfe), w(0x78), w(0xcd), \ + w(0x5a), w(0xf4), w(0x1f), w(0xdd), w(0xa8), w(0x33), w(0x88), w(0x07), w(0xc7), \ + w(0x31), w(0xb1), w(0x12), w(0x10), w(0x59), w(0x27), w(0x80), w(0xec), w(0x5f), \ + w(0x60), w(0x51), w(0x7f), w(0xa9), w(0x19), w(0xb5), w(0x4a), w(0x0d), w(0x2d), \ + w(0xe5), w(0x7a), w(0x9f), w(0x93), w(0xc9), w(0x9c), w(0xef), w(0xa0), w(0xe0), \ + w(0x3b), w(0x4d), w(0xae), w(0x2a), w(0xf5), w(0xb0), w(0xc8), w(0xeb), w(0xbb), \ + w(0x3c), w(0x83), w(0x53), w(0x99), w(0x61), w(0x17), w(0x2b), w(0x04), w(0x7e), \ + w(0xba), w(0x77), w(0xd6), w(0x26), w(0xe1), w(0x69), w(0x14), w(0x63), w(0x55), \ + w(0x21), w(0x0c), w(0x7d) \ + } + +#define mm_data(w) \ + { \ + w(0x00), w(0x01), w(0x02), w(0x03), w(0x04), w(0x05), w(0x06), w(0x07), w(0x08), w(0x09), \ + w(0x0a), w(0x0b), w(0x0c), w(0x0d), w(0x0e), w(0x0f), w(0x10), w(0x11), w(0x12), \ + w(0x13), w(0x14), w(0x15), w(0x16), w(0x17), w(0x18), w(0x19), w(0x1a), w(0x1b), \ + w(0x1c), w(0x1d), w(0x1e), w(0x1f), w(0x20), w(0x21), w(0x22), w(0x23), w(0x24), \ + w(0x25), w(0x26), w(0x27), w(0x28), w(0x29), w(0x2a), w(0x2b), w(0x2c), w(0x2d), \ + w(0x2e), w(0x2f), w(0x30), w(0x31), w(0x32), w(0x33), w(0x34), w(0x35), w(0x36), \ + w(0x37), w(0x38), w(0x39), w(0x3a), w(0x3b), w(0x3c), w(0x3d), w(0x3e), w(0x3f), \ + w(0x40), w(0x41), w(0x42), w(0x43), w(0x44), w(0x45), w(0x46), w(0x47), w(0x48), \ + w(0x49), w(0x4a), w(0x4b), w(0x4c), w(0x4d), w(0x4e), w(0x4f), w(0x50), w(0x51), \ + w(0x52), w(0x53), w(0x54), w(0x55), w(0x56), w(0x57), w(0x58), w(0x59), w(0x5a), \ + w(0x5b), w(0x5c), w(0x5d), w(0x5e), w(0x5f), w(0x60), w(0x61), w(0x62), w(0x63), \ + w(0x64), w(0x65), w(0x66), w(0x67), w(0x68), w(0x69), w(0x6a), w(0x6b), w(0x6c), \ + w(0x6d), w(0x6e), w(0x6f), w(0x70), w(0x71), w(0x72), w(0x73), w(0x74), w(0x75), \ + w(0x76), w(0x77), w(0x78), w(0x79), w(0x7a), w(0x7b), w(0x7c), w(0x7d), w(0x7e), \ + w(0x7f), w(0x80), w(0x81), w(0x82), w(0x83), w(0x84), w(0x85), w(0x86), w(0x87), \ + w(0x88), w(0x89), w(0x8a), w(0x8b), w(0x8c), w(0x8d), w(0x8e), w(0x8f), w(0x90), \ + w(0x91), w(0x92), w(0x93), w(0x94), w(0x95), w(0x96), w(0x97), w(0x98), w(0x99), \ + w(0x9a), w(0x9b), w(0x9c), w(0x9d), w(0x9e), w(0x9f), w(0xa0), w(0xa1), w(0xa2), \ + w(0xa3), w(0xa4), w(0xa5), w(0xa6), w(0xa7), w(0xa8), w(0xa9), w(0xaa), w(0xab), \ + w(0xac), w(0xad), w(0xae), w(0xaf), w(0xb0), w(0xb1), w(0xb2), w(0xb3), w(0xb4), \ + w(0xb5), w(0xb6), w(0xb7), w(0xb8), w(0xb9), w(0xba), w(0xbb), w(0xbc), w(0xbd), \ + w(0xbe), w(0xbf), w(0xc0), w(0xc1), w(0xc2), w(0xc3), w(0xc4), w(0xc5), w(0xc6), \ + w(0xc7), w(0xc8), w(0xc9), w(0xca), w(0xcb), w(0xcc), w(0xcd), w(0xce), w(0xcf), \ + w(0xd0), w(0xd1), w(0xd2), w(0xd3), w(0xd4), w(0xd5), w(0xd6), w(0xd7), w(0xd8), \ + w(0xd9), w(0xda), w(0xdb), w(0xdc), w(0xdd), w(0xde), w(0xdf), w(0xe0), w(0xe1), \ + w(0xe2), w(0xe3), w(0xe4), w(0xe5), w(0xe6), w(0xe7), w(0xe8), w(0xe9), w(0xea), \ + w(0xeb), w(0xec), w(0xed), w(0xee), w(0xef), w(0xf0), w(0xf1), w(0xf2), w(0xf3), \ + w(0xf4), w(0xf5), w(0xf6), w(0xf7), w(0xf8), w(0xf9), w(0xfa), w(0xfb), w(0xfc), \ + w(0xfd), w(0xfe), w(0xff) \ + } + +#define rc_data(w) \ + { w(0x01), w(0x02), w(0x04), w(0x08), w(0x10), w(0x20), w(0x40), w(0x80), w(0x1b), w(0x36) } + +#define h0(x) (x) + +#define w0(p) bytes2word(p, 0, 0, 0) +#define w1(p) bytes2word(0, p, 0, 0) +#define w2(p) bytes2word(0, 0, p, 0) +#define w3(p) bytes2word(0, 0, 0, p) + +#define u0(p) bytes2word(f2(p), p, p, f3(p)) +#define u1(p) bytes2word(f3(p), f2(p), p, p) +#define u2(p) bytes2word(p, f3(p), f2(p), p) +#define u3(p) bytes2word(p, p, f3(p), f2(p)) + +#define v0(p) bytes2word(fe(p), f9(p), fd(p), fb(p)) +#define v1(p) bytes2word(fb(p), fe(p), f9(p), fd(p)) +#define v2(p) bytes2word(fd(p), fb(p), fe(p), f9(p)) +#define v3(p) bytes2word(f9(p), fd(p), fb(p), fe(p)) + +#endif + +#if defined(STATIC_TABLES) || !defined(FF_TABLES) + +#define f2(x) ((x << 1) ^ (((x >> 7) & 1) * WPOLY)) +#define f4(x) ((x << 2) ^ (((x >> 6) & 1) * WPOLY) ^ (((x >> 6) & 2) * WPOLY)) +#define f8(x) \ + ((x << 3) ^ (((x >> 5) & 1) * WPOLY) ^ (((x >> 5) & 2) * WPOLY) ^ (((x >> 5) & 4) * WPOLY)) +#define f3(x) (f2(x) ^ x) +#define f9(x) (f8(x) ^ x) +#define fb(x) (f8(x) ^ f2(x) ^ x) +#define fd(x) (f8(x) ^ f4(x) ^ x) +#define fe(x) (f8(x) ^ f4(x) ^ f2(x)) + +#else + +#define f2(x) ((x) ? pow[log[x] + 0x19] : 0) +#define f3(x) ((x) ? pow[log[x] + 0x01] : 0) +#define f9(x) ((x) ? pow[log[x] + 0xc7] : 0) +#define fb(x) ((x) ? pow[log[x] + 0x68] : 0) +#define fd(x) ((x) ? pow[log[x] + 0xee] : 0) +#define fe(x) ((x) ? pow[log[x] + 0xdf] : 0) + +#endif + +#include "aestab.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +#if defined(STATIC_TABLES) + +/* implemented in case of wrong call for fixed tables */ + +AES_RETURN aes_init(void) { + return EXIT_SUCCESS; +} + +#else /* Generate the tables for the dynamic table option */ + +#if defined(FF_TABLES) + +#define gf_inv(x) ((x) ? pow[255 - log[x]] : 0) + +#else + +/* It will generally be sensible to use tables to compute finite + field multiplies and inverses but where memory is scarse this + code might sometimes be better. But it only has effect during + initialisation so its pretty unimportant in overall terms. +*/ + +/* return 2 ^ (n - 1) where n is the bit number of the highest bit + set in x with x in the range 1 < x < 0x00000200. This form is + used so that locals within fi can be bytes rather than words +*/ + +static uint8_t hibit(const uint32_t x) { + uint8_t r = (uint8_t)((x >> 1) | (x >> 2)); + + r |= (r >> 2); + r |= (r >> 4); + return (r + 1) >> 1; +} + +/* return the inverse of the finite field element x */ + +static uint8_t gf_inv(const uint8_t x) { + uint8_t p1 = x, p2 = BPOLY, n1 = hibit(x), n2 = 0x80, v1 = 1, v2 = 0; + + if(x < 2) return x; + + for(;;) { + if(n1) + while(n2 >= n1) /* divide polynomial p2 by p1 */ + { + n2 /= n1; /* shift smaller polynomial left */ + p2 ^= (p1 * n2) & 0xff; /* and remove from larger one */ + v2 ^= v1 * n2; /* shift accumulated value and */ + n2 = hibit(p2); /* add into result */ + } + else + return v1; + + if(n2) /* repeat with values swapped */ + while(n1 >= n2) { + n1 /= n2; + p1 ^= p2 * n1; + v1 ^= v2 * n1; + n1 = hibit(p1); + } + else + return v2; + } +} + +#endif + +/* The forward and inverse affine transformations used in the S-box */ +uint8_t fwd_affine(const uint8_t x) { + uint32_t w = x; + w ^= (w << 1) ^ (w << 2) ^ (w << 3) ^ (w << 4); + return 0x63 ^ ((w ^ (w >> 8)) & 0xff); +} + +uint8_t inv_affine(const uint8_t x) { + uint32_t w = x; + w = (w << 1) ^ (w << 3) ^ (w << 6); + return 0x05 ^ ((w ^ (w >> 8)) & 0xff); +} + +static int init = 0; + +AES_RETURN aes_init(void) { + uint32_t i, w; + +#if defined(FF_TABLES) + + uint8_t pow[512] = {0}, log[256] = {0}; + + if(init) return EXIT_SUCCESS; + /* log and power tables for GF(2^8) finite field with + WPOLY as modular polynomial - the simplest primitive + root is 0x03, used here to generate the tables + */ + + i = 0; + w = 1; + do { + pow[i] = (uint8_t)w; + pow[i + 255] = (uint8_t)w; + log[w] = (uint8_t)i++; + w ^= (w << 1) ^ (w & 0x80 ? WPOLY : 0); + } while(w != 1); + +#else + if(init) return EXIT_SUCCESS; +#endif + + for(i = 0, w = 1; i < RC_LENGTH; ++i) { + t_set(r, c)[i] = bytes2word(w, 0, 0, 0); + w = f2(w); + } + + for(i = 0; i < 256; ++i) { + uint8_t b; + + b = fwd_affine(gf_inv((uint8_t)i)); + w = bytes2word(f2(b), b, b, f3(b)); + +#if defined(SBX_SET) + t_set(s, box)[i] = b; +#endif + +#if defined(FT1_SET) /* tables for a normal encryption round */ + t_set(f, n)[i] = w; +#endif +#if defined(FT4_SET) + t_set(f, n)[0][i] = w; + t_set(f, n)[1][i] = upr(w, 1); + t_set(f, n)[2][i] = upr(w, 2); + t_set(f, n)[3][i] = upr(w, 3); +#endif + w = bytes2word(b, 0, 0, 0); + +#if defined(FL1_SET) /* tables for last encryption round (may also */ + t_set(f, l)[i] = w; /* be used in the key schedule) */ +#endif +#if defined(FL4_SET) + t_set(f, l)[0][i] = w; + t_set(f, l)[1][i] = upr(w, 1); + t_set(f, l)[2][i] = upr(w, 2); + t_set(f, l)[3][i] = upr(w, 3); +#endif + +#if defined(LS1_SET) /* table for key schedule if t_set(f,l) above is*/ + t_set(l, s)[i] = w; /* not of the required form */ +#endif +#if defined(LS4_SET) + t_set(l, s)[0][i] = w; + t_set(l, s)[1][i] = upr(w, 1); + t_set(l, s)[2][i] = upr(w, 2); + t_set(l, s)[3][i] = upr(w, 3); +#endif + + b = gf_inv(inv_affine((uint8_t)i)); + w = bytes2word(fe(b), f9(b), fd(b), fb(b)); + +#if defined(IM1_SET) /* tables for the inverse mix column operation */ + t_set(i, m)[b] = w; +#endif +#if defined(IM4_SET) + t_set(i, m)[0][b] = w; + t_set(i, m)[1][b] = upr(w, 1); + t_set(i, m)[2][b] = upr(w, 2); + t_set(i, m)[3][b] = upr(w, 3); +#endif + +#if defined(ISB_SET) + t_set(i, box)[i] = b; +#endif +#if defined(IT1_SET) /* tables for a normal decryption round */ + t_set(i, n)[i] = w; +#endif +#if defined(IT4_SET) + t_set(i, n)[0][i] = w; + t_set(i, n)[1][i] = upr(w, 1); + t_set(i, n)[2][i] = upr(w, 2); + t_set(i, n)[3][i] = upr(w, 3); +#endif + w = bytes2word(b, 0, 0, 0); +#if defined(IL1_SET) /* tables for last decryption round */ + t_set(i, l)[i] = w; +#endif +#if defined(IL4_SET) + t_set(i, l)[0][i] = w; + t_set(i, l)[1][i] = upr(w, 1); + t_set(i, l)[2][i] = upr(w, 2); + t_set(i, l)[3][i] = upr(w, 3); +#endif + } + init = 1; + return EXIT_SUCCESS; +} + +/* + Automatic code initialisation (suggested by by Henrik S. Gaßmann) + based on code provided by Joe Lowe and placed in the public domain at: + http://stackoverflow.com/questions/1113409/attribute-constructor-equivalent-in-vc +*/ + +#ifdef _MSC_VER + +#pragma section(".CRT$XCU", read) + +__declspec(allocate(".CRT$XCU")) void(__cdecl* aes_startup)(void) = aes_init; + +#elif defined(__GNUC__) + +static void aes_startup(void) __attribute__((constructor)); + +static void aes_startup(void) { + aes_init(); +} + +#else + +#pragma message("dynamic tables must be initialised manually on your system") + +#endif + +#endif + +#if defined(__cplusplus) +} +#endif diff --git a/applications/external/flipbip/lib/crypto/aes/aestab.h b/applications/external/flipbip/lib/crypto/aes/aestab.h new file mode 100644 index 0000000000..9dfcded1ae --- /dev/null +++ b/applications/external/flipbip/lib/crypto/aes/aestab.h @@ -0,0 +1,173 @@ +/* +--------------------------------------------------------------------------- +Copyright (c) 1998-2013, Brian Gladman, Worcester, UK. All rights reserved. + +The redistribution and use of this software (with or without changes) +is allowed without the payment of fees or royalties provided that: + + source code distributions include the above copyright notice, this + list of conditions and the following disclaimer; + + binary distributions include the above copyright notice, this list + of conditions and the following disclaimer in their documentation. + +This software is provided 'as is' with no explicit or implied warranties +in respect of its operation, including, but not limited to, correctness +and fitness for purpose. +--------------------------------------------------------------------------- +Issue Date: 20/12/2007 + + This file contains the code for declaring the tables needed to implement + AES. The file aesopt.h is assumed to be included before this header file. + If there are no global variables, the definitions here can be used to put + the AES tables in a structure so that a pointer can then be added to the + AES context to pass them to the AES routines that need them. If this + facility is used, the calling program has to ensure that this pointer is + managed appropriately. In particular, the value of the t_dec(in,it) item + in the table structure must be set to zero in order to ensure that the + tables are initialised. In practice the three code sequences in aeskey.c + that control the calls to aes_init() and the aes_init() routine itself will + have to be changed for a specific implementation. If global variables are + available it will generally be preferable to use them with the precomputed + STATIC_TABLES option that uses static global tables. + + The following defines can be used to control the way the tables + are defined, initialised and used in embedded environments that + require special features for these purposes + + the 't_dec' construction is used to declare fixed table arrays + the 't_set' construction is used to set fixed table values + the 't_use' construction is used to access fixed table values + + 256 byte tables: + + t_xxx(s,box) => forward S box + t_xxx(i,box) => inverse S box + + 256 32-bit word OR 4 x 256 32-bit word tables: + + t_xxx(f,n) => forward normal round + t_xxx(f,l) => forward last round + t_xxx(i,n) => inverse normal round + t_xxx(i,l) => inverse last round + t_xxx(l,s) => key schedule table + t_xxx(i,m) => key schedule table + + Other variables and tables: + + t_xxx(r,c) => the rcon table +*/ + +#if !defined(_AESTAB_H) +#define _AESTAB_H + +#if defined(__cplusplus) +extern "C" { +#endif + +#define t_dec(m, n) t_##m##n +#define t_set(m, n) t_##m##n +#define t_use(m, n) t_##m##n + +#if defined(STATIC_TABLES) +#if !defined(__GNUC__) && (defined(__MSDOS__) || defined(__WIN16__)) +/* make tables far data to avoid using too much DGROUP space (PG) */ +#define CONST const far +#else +#define CONST const +#endif +#else +#define CONST +#endif + +#if defined(DO_TABLES) +#define EXTERN +#else +#define EXTERN extern +#endif + +#if defined(_MSC_VER) && defined(TABLE_ALIGN) +#define ALIGN __declspec(align(TABLE_ALIGN)) +#else +#define ALIGN +#endif + +#if defined(__WATCOMC__) && (__WATCOMC__ >= 1100) +#define XP_DIR __cdecl +#else +#define XP_DIR +#endif + +#if defined(DO_TABLES) && defined(STATIC_TABLES) +#define d_1(t, n, b, e) EXTERN ALIGN CONST XP_DIR t n[256] = b(e) +#define d_4(t, n, b, e, f, g, h) EXTERN ALIGN CONST XP_DIR t n[4][256] = {b(e), b(f), b(g), b(h)} +EXTERN ALIGN CONST uint32_t t_dec(r, c)[RC_LENGTH] = rc_data(w0); +#else +#define d_1(t, n, b, e) EXTERN ALIGN CONST XP_DIR t n[256] +#define d_4(t, n, b, e, f, g, h) EXTERN ALIGN CONST XP_DIR t n[4][256] +EXTERN ALIGN CONST uint32_t t_dec(r, c)[RC_LENGTH]; +#endif + +#if defined(SBX_SET) +d_1(uint8_t, t_dec(s, box), sb_data, h0); +#endif +#if defined(ISB_SET) +d_1(uint8_t, t_dec(i, box), isb_data, h0); +#endif + +#if defined(FT1_SET) +d_1(uint32_t, t_dec(f, n), sb_data, u0); +#endif +#if defined(FT4_SET) +d_4(uint32_t, t_dec(f, n), sb_data, u0, u1, u2, u3); +#endif + +#if defined(FL1_SET) +d_1(uint32_t, t_dec(f, l), sb_data, w0); +#endif +#if defined(FL4_SET) +d_4(uint32_t, t_dec(f, l), sb_data, w0, w1, w2, w3); +#endif + +#if defined(IT1_SET) +d_1(uint32_t, t_dec(i, n), isb_data, v0); +#endif +#if defined(IT4_SET) +d_4(uint32_t, t_dec(i, n), isb_data, v0, v1, v2, v3); +#endif + +#if defined(IL1_SET) +d_1(uint32_t, t_dec(i, l), isb_data, w0); +#endif +#if defined(IL4_SET) +d_4(uint32_t, t_dec(i, l), isb_data, w0, w1, w2, w3); +#endif + +#if defined(LS1_SET) +#if defined(FL1_SET) +#undef LS1_SET +#else +d_1(uint32_t, t_dec(l, s), sb_data, w0); +#endif +#endif + +#if defined(LS4_SET) +#if defined(FL4_SET) +#undef LS4_SET +#else +d_4(uint32_t, t_dec(l, s), sb_data, w0, w1, w2, w3); +#endif +#endif + +#if defined(IM1_SET) +d_1(uint32_t, t_dec(i, m), mm_data, v0); +#endif +#if defined(IM4_SET) +d_4(uint32_t, t_dec(i, m), mm_data, v0, v1, v2, v3); +#endif + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/applications/external/flipbip/lib/crypto/aes/aestst.c b/applications/external/flipbip/lib/crypto/aes/aestst.c new file mode 100644 index 0000000000..f9d6465dd3 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/aes/aestst.c @@ -0,0 +1,189 @@ +/* + --------------------------------------------------------------------------- + Copyright (c) 1998-2008, Brian Gladman, Worcester, UK. All rights reserved. + + LICENSE TERMS + + The redistribution and use of this software (with or without changes) + is allowed without the payment of fees or royalties provided that: + + 1. source code distributions include the above copyright notice, this + list of conditions and the following disclaimer; + + 2. binary distributions include the above copyright notice, this list + of conditions and the following disclaimer in their documentation; + + 3. the name of the copyright holder is not used to endorse products + built using this software without specific written permission. + + DISCLAIMER + + This software is provided 'as is' with no explicit or implied warranties + in respect of its properties, including, but not limited to, correctness + and/or fitness for purpose. + --------------------------------------------------------------------------- + Issue Date: 20/12/2007 +*/ + +// Correct Output (for variable block size - AES_BLOCK_SIZE undefined): + +// lengths: block = 16 bytes, key = 16 bytes +// key = 2b7e151628aed2a6abf7158809cf4f3c +// input = 3243f6a8885a308d313198a2e0370734 +// encrypt = 3925841d02dc09fbdc118597196a0b32 +// decrypt = 3243f6a8885a308d313198a2e0370734 + +// lengths: block = 16 bytes, key = 24 bytes +// key = 2b7e151628aed2a6abf7158809cf4f3c762e7160f38b4da5 +// input = 3243f6a8885a308d313198a2e0370734 +// encrypt = f9fb29aefc384a250340d833b87ebc00 +// decrypt = 3243f6a8885a308d313198a2e0370734 + +// lengths: block = 16 bytes, key = 32 bytes +// key = 2b7e151628aed2a6abf7158809cf4f3c762e7160f38b4da56a784d9045190cfe +// input = 3243f6a8885a308d313198a2e0370734 +// encrypt = 1a6e6c2c662e7da6501ffb62bc9e93f3 +// decrypt = 3243f6a8885a308d313198a2e0370734 + +#include +#include + +#include "aes.h" +#include "aestst.h" + +void out_state(long s0, long s1, long s2, long s3) { + printf("\n%08lx%08lx%08lx%08lx", s0, s1, s2, s3); +} + +void oblk(char m[], unsigned char v[], unsigned long n) { + unsigned long i; + + printf("\n%s", m); + + for(i = 0; i < n; ++i) printf("%02x", v[i]); +} + +void message(const char* s) { + printf("%s", s); +} + +unsigned char pih[32] = // hex digits of pi + {0x32, 0x43, 0xf6, 0xa8, 0x88, 0x5a, 0x30, 0x8d, 0x31, 0x31, 0x98, + 0xa2, 0xe0, 0x37, 0x07, 0x34, 0x4a, 0x40, 0x93, 0x82, 0x22, 0x99, + 0xf3, 0x1d, 0x00, 0x82, 0xef, 0xa9, 0x8e, 0xc4, 0xe6, 0xc8}; + +unsigned char exh[32] = // hex digits of e + {0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, + 0x88, 0x09, 0xcf, 0x4f, 0x3c, 0x76, 0x2e, 0x71, 0x60, 0xf3, 0x8b, + 0x4d, 0xa5, 0x6a, 0x78, 0x4d, 0x90, 0x45, 0x19, 0x0c, 0xfe}; + +unsigned char res[3][32] = { + {0x39, 0x25, 0x84, 0x1d, 0x02, 0xdc, 0x09, 0xfb, 0xdc, 0x11, 0x85, 0x97, 0x19, 0x6a, 0x0b, 0x32}, + {0xf9, 0xfb, 0x29, 0xae, 0xfc, 0x38, 0x4a, 0x25, 0x03, 0x40, 0xd8, 0x33, 0xb8, 0x7e, 0xbc, 0x00}, + {0x1a, 0x6e, 0x6c, 0x2c, 0x66, 0x2e, 0x7d, 0xa6, 0x50, 0x1f, 0xfb, 0x62, 0xbc, 0x9e, 0x93, 0xf3}}; + +// void cycles(volatile uint64_t *rtn) +// { +// #if defined( _MSCVER ) +// __asm // read the Pentium Time Stamp Counter +// { cpuid +// rdtsc +// mov ecx,rtn +// mov [ecx],eax +// mov [ecx+4],edx +// cpuid +// } +// #elif defined( __GNUC__ ) +// #if defined(__aarch64__) +// __asm__ __volatile__("mrs %0, cntvct_el0": "=r" (*rtn)); +// #else +// __asm__ __volatile__("rdtsc": "=A" (*rtn)); +// #endif +// #endif +// } + +int main(void) { + unsigned char out[32], ret[32], err = 0; + f_ectx alge[1]; + f_dctx algd[1]; + + aes_init(); + + message("\nRun tests for the AES algorithm"); + + memset(&alge, 0, sizeof(aes_encrypt_ctx)); + memset(&algd, 0, sizeof(aes_decrypt_ctx)); + +#if defined(AES_128) + memset(out, 0xcc, 16); + memset(ret, 0xcc, 16); + printf("\n\n// lengths: block = 16, bytes, key = 16 bytes"); + f_enc_key128(alge, exh); + oblk("// key = ", exh, 16); + oblk("// input = ", pih, 16); + do_enc(alge, pih, out, 1); + oblk("// encrypt = ", out, 16); + if(memcmp(out, res[0], 16)) { + message(" error"); + err += 1; + } + f_dec_key128(algd, exh); + do_dec(algd, out, ret, 1); + oblk("// decrypt = ", ret, 16); + if(memcmp(ret, pih, 16)) { + message(" error"); + err += 2; + } +#endif + +#if defined(AES_192) + memset(out, 0xcc, 16); + memset(ret, 0xcc, 16); + printf("\n\n// lengths: block = 16, bytes, key = 24 bytes"); + f_enc_key192(alge, exh); + oblk("// key = ", exh, 24); + oblk("// input = ", pih, 16); + do_enc(alge, pih, out, 1); + oblk("// encrypt = ", out, 16); + if(memcmp(out, res[1], 16)) { + message(" error"); + err += 4; + } + f_dec_key192(algd, exh); + do_dec(algd, out, ret, 1); + oblk("// decrypt = ", ret, 16); + if(memcmp(ret, pih, 16)) { + message(" error"); + err += 8; + } +#endif + +#if defined(AES_256) + memset(out, 0xcc, 16); + memset(ret, 0xcc, 16); + printf("\n\n// lengths: block = 16, bytes, key = 32 bytes"); + f_enc_key256(alge, exh); + oblk("// key = ", exh, 32); + oblk("// input = ", pih, 16); + do_enc(alge, pih, out, 1); + oblk("// encrypt = ", out, 16); + if(memcmp(out, res[2], 16)) { + message(" error"); + err += 16; + } + f_dec_key256(algd, exh); + do_dec(algd, out, ret, 1); + oblk("// decrypt = ", ret, 16); + if(memcmp(ret, pih, 16)) { + message(" error"); + err += 32; + } +#endif + + if(!err) + message("\n\nThese values are all correct\n\n"); + else + message("\n\nSome values are in error\n\n"); + + return 0; +} diff --git a/applications/external/flipbip/lib/crypto/aes/aestst.h b/applications/external/flipbip/lib/crypto/aes/aestst.h new file mode 100644 index 0000000000..bb38b04349 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/aes/aestst.h @@ -0,0 +1,85 @@ +/* +--------------------------------------------------------------------------- +Copyright (c) 1998-2013, Brian Gladman, Worcester, UK. All rights reserved. + +The redistribution and use of this software (with or without changes) +is allowed without the payment of fees or royalties provided that: + + source code distributions include the above copyright notice, this + list of conditions and the following disclaimer; + + binary distributions include the above copyright notice, this list + of conditions and the following disclaimer in their documentation. + +This software is provided 'as is' with no explicit or implied warranties +in respect of its operation, including, but not limited to, correctness +and fitness for purpose. +--------------------------------------------------------------------------- +Issue Date: 20/12/2007 +*/ + +// The following definitions are required for testing only, They are not needed +// for AES (Rijndael) implementation. They are used to allow C, C++ and DLL +// data access and subroutine calls to be expressed in the same form in the +// testing code. + +#ifndef AESTST_H +#define AESTST_H + +#define f_info(x) (x)->inf.b[2] +#define f_ectx aes_encrypt_ctx +#define f_enc_key128(a, b) aes_encrypt_key128((b), (a)) +#define f_enc_key192(a, b) aes_encrypt_key192((b), (a)) +#define f_enc_key256(a, b) aes_encrypt_key256((b), (a)) +#define f_enc_key(a, b, c) aes_encrypt_key((b), (c), (a)) +#define f_enc_blk(a, b, c) aes_encrypt((b), (c), (a)) + +#define f_dctx aes_decrypt_ctx +#define f_dec_key128(a, b) aes_decrypt_key128((b), (a)) +#define f_dec_key192(a, b) aes_decrypt_key192((b), (a)) +#define f_dec_key256(a, b) aes_decrypt_key256((b), (a)) +#define f_dec_key(a, b, c) aes_decrypt_key((b), (c), (a)) +#define f_dec_blk(a, b, c) aes_decrypt((b), (c), (a)) + +#define f_talign(a, b) aes_test_alignment_detection(b) +#define f_mode_reset(a) aes_mode_reset(a) +#define f_ecb_enc(a, b, c, d) aes_ecb_encrypt((b), (c), (d), (a)) +#define f_ecb_dec(a, b, c, d) aes_ecb_decrypt((b), (c), (d), (a)) +#define f_cbc_enc(a, b, c, d, e) aes_cbc_encrypt((b), (c), (d), (e), (a)) +#define f_cbc_dec(a, b, c, d, e) aes_cbc_decrypt((b), (c), (d), (e), (a)) +#define f_cfb_enc(a, b, c, d, e) aes_cfb_encrypt((b), (c), (d), (e), (a)) +#define f_cfb_dec(a, b, c, d, e) aes_cfb_decrypt((b), (c), (d), (e), (a)) +#define f_ofb_cry(a, b, c, d, e) aes_ofb_crypt((b), (c), (d), (e), (a)) +#define f_ctr_cry(a, b, c, d, e, f) aes_ctr_crypt((b), (c), (d), (e), (f), (a)) + +#define ek_name128 "aes_encrypt_key128" +#define ek_name192 "aes_encrypt_key192" +#define ek_name256 "aes_encrypt_key256" +#define ek_name "aes_encrypt_key" +#define eb_name "aes_encrypt" + +#define dk_name128 "aes_decrypt_key128" +#define dk_name192 "aes_decrypt_key192" +#define dk_name256 "aes_decrypt_key256" +#define dk_name "aes_decrypt_key" +#define db_name "aes_decrypt" + +#define eres_name "aes_mode_reset" +#define ecbe_name "aes_ecb_encrypt" +#define ecbd_name "aes_ecb_decrypt" +#define cbce_name "aes_cbc_encrypt" +#define cbcd_name "aes_cbc_decrypt" +#define cfbe_name "aes_cfb_encrypt" +#define cfbd_name "aes_cfb_decrypt" +#define ofb_name "aes_ofb_crypt" +#define ctr_name "aes_ctr_crypt" + +#ifndef AES_N_BLOCK +#define do_enc(a, b, c, d) f_enc_blk(a, b, c) +#define do_dec(a, b, c, d) f_dec_blk(a, b, c) +#else +#define do_enc(a, b, c, d) f_ecb_enc(a, b, c, 1) +#define do_dec(a, b, c, d) f_ecb_dec(a, b, c, 1) +#endif + +#endif diff --git a/applications/external/flipbip/lib/crypto/base32.c b/applications/external/flipbip/lib/crypto/base32.c new file mode 100644 index 0000000000..e211bb308f --- /dev/null +++ b/applications/external/flipbip/lib/crypto/base32.c @@ -0,0 +1,241 @@ +/** + * Copyright (c) 2017 Saleem Rashid + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, E1PRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "base32.h" + +#include + +const char* BASE32_ALPHABET_RFC4648 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ23456789"; + +static inline void base32_5to8(const uint8_t* in, uint8_t length, uint8_t* out); +static inline bool + base32_8to5(const uint8_t* in, uint8_t length, uint8_t* out, const char* alphabet); +static inline void base32_8to5_raw(const uint8_t* in, uint8_t length, uint8_t* out); + +static inline int base32_encode_character(uint8_t decoded, const char* alphabet); +static inline int base32_decode_character(char encoded, const char* alphabet); + +char* base32_encode( + const uint8_t* in, + size_t inlen, + char* out, + size_t outlen, + const char* alphabet) { + size_t length = base32_encoded_length(inlen); + if(outlen <= length) { + return NULL; + } + + base32_encode_unsafe(in, inlen, (uint8_t*)out); + + for(size_t i = 0; i < length; i++) { + int ret = base32_encode_character(out[i], alphabet); + + if(ret == -1) { + return false; + } else { + out[i] = ret; + } + } + + out[length] = '\0'; + return &out[length]; +} + +uint8_t* + base32_decode(const char* in, size_t inlen, uint8_t* out, size_t outlen, const char* alphabet) { + size_t length = base32_decoded_length(inlen); + if(outlen < length) { + return NULL; + } + + if(!base32_decode_unsafe((uint8_t*)in, inlen, (uint8_t*)out, alphabet)) { + return NULL; + } + + return &out[length]; +} + +void base32_encode_unsafe(const uint8_t* in, size_t inlen, uint8_t* out) { + uint8_t remainder = inlen % 5; + size_t limit = inlen - remainder; + + size_t i = 0, j = 0; + for(i = 0, j = 0; i < limit; i += 5, j += 8) { + base32_5to8(&in[i], 5, &out[j]); + } + + if(remainder) base32_5to8(&in[i], remainder, &out[j]); +} + +bool base32_decode_unsafe(const uint8_t* in, size_t inlen, uint8_t* out, const char* alphabet) { + uint8_t remainder = inlen % 8; + size_t limit = inlen - remainder; + + size_t i = 0, j = 0; + for(i = 0, j = 0; i < limit; i += 8, j += 5) { + if(!base32_8to5(&in[i], 8, &out[j], alphabet)) { + return false; + } + } + + if(remainder && !base32_8to5(&in[i], remainder, &out[j], alphabet)) { + return false; + } + + return true; +} + +size_t base32_encoded_length(size_t inlen) { + uint8_t remainder = inlen % 5; + + return (inlen / 5) * 8 + (remainder * 8 + 4) / 5; +} + +size_t base32_decoded_length(size_t inlen) { + uint8_t remainder = inlen % 8; + + return (inlen / 8) * 5 + (remainder * 5) / 8; +} + +void base32_5to8(const uint8_t* in, uint8_t length, uint8_t* out) { + if(length >= 1) { + out[0] = (in[0] >> 3); + out[1] = (in[0] & 7) << 2; + } + + if(length >= 2) { + out[1] |= (in[1] >> 6); + out[2] = (in[1] >> 1) & 31; + out[3] = (in[1] & 1) << 4; + } + + if(length >= 3) { + out[3] |= (in[2] >> 4); + out[4] = (in[2] & 15) << 1; + } + + if(length >= 4) { + out[4] |= (in[3] >> 7); + out[5] = (in[3] >> 2) & 31; + out[6] = (in[3] & 3) << 3; + } + + if(length >= 5) { + out[6] |= (in[4] >> 5); + out[7] = (in[4] & 31); + } +} + +bool base32_8to5(const uint8_t* in, uint8_t length, uint8_t* out, const char* alphabet) { + if(length == 1 || length == 3 || length == 6 || length > 8) { + return false; + } + + if(alphabet) { + uint8_t decoded[length]; + memset(decoded, 0, sizeof(decoded)); + + for(size_t i = 0; i < length; i++) { + int ret = base32_decode_character(in[i], alphabet); + + if(ret == -1) { + return false; + } else { + decoded[i] = ret; + } + } + + base32_8to5_raw(decoded, length, out); + } else { + base32_8to5_raw(in, length, out); + } + + return true; +} + +void base32_8to5_raw(const uint8_t* in, uint8_t length, uint8_t* out) { + if(length >= 2) { + out[0] = (in[0] << 3); + out[0] |= (in[1] >> 2); + } + + if(length >= 4) { + out[1] = (in[1] & 3) << 6; + out[1] |= (in[2] << 1); + out[1] |= (in[3] >> 4); + } + + if(length >= 5) { + out[2] = (in[3] & 15) << 4; + out[2] |= (in[4] >> 1); + } + + if(length >= 7) { + out[3] = (in[4] & 1) << 7; + out[3] |= (in[5] << 2); + out[3] |= (in[6] >> 3); + } + + if(length >= 8) { + out[4] = (in[6] & 7) << 5; + out[4] |= (in[7] & 31); + } +} + +int base32_encode_character(uint8_t decoded, const char* alphabet) { + if(decoded >> 5) { + return -1; + } + + if(alphabet == BASE32_ALPHABET_RFC4648) { + if(decoded < 26) { + return 'A' + decoded; + } else { + return '2' - 26 + decoded; + } + } + + return alphabet[decoded]; +} + +int base32_decode_character(char encoded, const char* alphabet) { + if(alphabet == BASE32_ALPHABET_RFC4648) { + if(encoded >= 'A' && encoded <= 'Z') { + return encoded - 'A'; + } else if(encoded >= 'a' && encoded <= 'z') { + return encoded - 'a'; + } else if(encoded >= '2' && encoded <= '7') { + return encoded - '2' + 26; + } else { + return -1; + } + } + + const char* occurrence = strchr(alphabet, encoded); + + if(occurrence) { + return occurrence - alphabet; + } else { + return -1; + } +} diff --git a/applications/external/flipbip/lib/crypto/base32.h b/applications/external/flipbip/lib/crypto/base32.h new file mode 100644 index 0000000000..5e214aabb9 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/base32.h @@ -0,0 +1,42 @@ +/** + * Copyright (c) 2017 Saleem Rashid + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __BASE32_H__ +#define __BASE32_H__ + +#include +#include +#include + +extern const char* BASE32_ALPHABET_RFC4648; + +char* base32_encode(const uint8_t* in, size_t inlen, char* out, size_t outlen, const char* alphabet); +void base32_encode_unsafe(const uint8_t* in, size_t inlen, uint8_t* out); + +uint8_t* + base32_decode(const char* in, size_t inlen, uint8_t* out, size_t outlen, const char* alphabet); +bool base32_decode_unsafe(const uint8_t* in, size_t inlen, uint8_t* out, const char* alphabet); + +size_t base32_encoded_length(size_t inlen); +size_t base32_decoded_length(size_t inlen); + +#endif diff --git a/applications/external/flipbip/lib/crypto/base58.c b/applications/external/flipbip/lib/crypto/base58.c new file mode 100644 index 0000000000..dbcd3832f6 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/base58.c @@ -0,0 +1,219 @@ +/** + * Copyright (c) 2012-2014 Luke Dashjr + * Copyright (c) 2013-2014 Pavol Rusnak + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "base58.h" +#include +#include +#include "memzero.h" +#include "ripemd160.h" +#include "sha2.h" + +const char b58digits_ordered[] = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; +const int8_t b58digits_map[] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, -1, -1, -1, -1, -1, -1, -1, 9, + 10, 11, 12, 13, 14, 15, 16, -1, 17, 18, 19, 20, 21, -1, 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32, -1, -1, -1, -1, -1, -1, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, -1, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, -1, -1, -1, -1, -1, +}; + +typedef uint64_t b58_maxint_t; +typedef uint32_t b58_almostmaxint_t; +#define b58_almostmaxint_bits (sizeof(b58_almostmaxint_t) * 8) +static const b58_almostmaxint_t b58_almostmaxint_mask = + ((((b58_maxint_t)1) << b58_almostmaxint_bits) - 1); + +// Decodes a null-terminated Base58 string `b58` to binary and writes the result +// at the end of the buffer `bin` of size `*binszp`. On success `*binszp` is set +// to the number of valid bytes at the end of the buffer. +bool b58tobin(void* bin, size_t* binszp, const char* b58) { + size_t binsz = *binszp; + + if(binsz == 0) { + return false; + } + + const unsigned char* b58u = (const unsigned char*)b58; + unsigned char* binu = bin; + size_t outisz = (binsz + sizeof(b58_almostmaxint_t) - 1) / sizeof(b58_almostmaxint_t); + b58_almostmaxint_t outi[outisz]; + b58_maxint_t t = 0; + b58_almostmaxint_t c = 0; + size_t i = 0, j = 0; + uint8_t bytesleft = binsz % sizeof(b58_almostmaxint_t); + b58_almostmaxint_t zeromask = bytesleft ? (b58_almostmaxint_mask << (bytesleft * 8)) : 0; + unsigned zerocount = 0; + + size_t b58sz = strlen(b58); + + memzero(outi, sizeof(outi)); + + // Leading zeros, just count + for(i = 0; i < b58sz && b58u[i] == '1'; ++i) ++zerocount; + + for(; i < b58sz; ++i) { + if(b58u[i] & 0x80) + // High-bit set on invalid digit + return false; + if(b58digits_map[b58u[i]] == -1) + // Invalid base58 digit + return false; + c = (unsigned)b58digits_map[b58u[i]]; + for(j = outisz; j--;) { + t = ((b58_maxint_t)outi[j]) * 58 + c; + c = t >> b58_almostmaxint_bits; + outi[j] = t & b58_almostmaxint_mask; + } + if(c) + // Output number too big (carry to the next int32) + return false; + if(outi[0] & zeromask) + // Output number too big (last int32 filled too far) + return false; + } + + j = 0; + if(bytesleft) { + for(i = bytesleft; i > 0; --i) { + *(binu++) = (outi[0] >> (8 * (i - 1))) & 0xff; + } + ++j; + } + + for(; j < outisz; ++j) { + for(i = sizeof(*outi); i > 0; --i) { + *(binu++) = (outi[j] >> (8 * (i - 1))) & 0xff; + } + } + + // locate the most significant byte + binu = bin; + for(i = 0; i < binsz; ++i) { + if(binu[i]) break; + } + + // prepend the correct number of null-bytes + if(zerocount > i) { + /* result too large */ + return false; + } + *binszp = binsz - i + zerocount; + + return true; +} + +int b58check(const void* bin, size_t binsz, HasherType hasher_type, const char* base58str) { + unsigned char buf[32] = {0}; + const uint8_t* binc = bin; + unsigned i = 0; + if(binsz < 4) return -4; + hasher_Raw(hasher_type, bin, binsz - 4, buf); + if(memcmp(&binc[binsz - 4], buf, 4)) return -1; + + // Check number of zeros is correct AFTER verifying checksum (to avoid + // possibility of accessing base58str beyond the end) + for(i = 0; binc[i] == '\0' && base58str[i] == '1'; ++i) { + } // Just finding the end of zeros, nothing to do in loop + if(binc[i] == '\0' || base58str[i] == '1') return -3; + + return binc[0]; +} + +bool b58enc(char* b58, size_t* b58sz, const void* data, size_t binsz) { + const uint8_t* bin = data; + int carry = 0; + size_t i = 0, j = 0, high = 0, zcount = 0; + size_t size = 0; + + while(zcount < binsz && !bin[zcount]) ++zcount; + + size = (binsz - zcount) * 138 / 100 + 1; + uint8_t buf[size]; + memzero(buf, size); + + for(i = zcount, high = size - 1; i < binsz; ++i, high = j) { + for(carry = bin[i], j = size - 1; (j > high) || carry; --j) { + carry += 256 * buf[j]; + buf[j] = carry % 58; + carry /= 58; + if(!j) { + // Otherwise j wraps to maxint which is > high + break; + } + } + } + + for(j = 0; j < size && !buf[j]; ++j) + ; + + if(*b58sz <= zcount + size - j) { + *b58sz = zcount + size - j + 1; + return false; + } + + if(zcount) memset(b58, '1', zcount); + for(i = zcount; j < size; ++i, ++j) b58[i] = b58digits_ordered[buf[j]]; + b58[i] = '\0'; + *b58sz = i + 1; + + return true; +} + +int base58_encode_check( + const uint8_t* data, + int datalen, + HasherType hasher_type, + char* str, + int strsize) { + if(datalen > 128) { + return 0; + } + uint8_t buf[datalen + 32]; + memset(buf, 0, sizeof(buf)); + uint8_t* hash = buf + datalen; + memcpy(buf, data, datalen); + hasher_Raw(hasher_type, data, datalen, hash); + size_t res = strsize; + bool success = b58enc(str, &res, buf, datalen + 4); + memzero(buf, sizeof(buf)); + return success ? res : 0; +} + +int base58_decode_check(const char* str, HasherType hasher_type, uint8_t* data, int datalen) { + if(datalen > 128) { + return 0; + } + uint8_t d[datalen + 4]; + memset(d, 0, sizeof(d)); + size_t res = datalen + 4; + if(b58tobin(d, &res, str) != true) { + return 0; + } + uint8_t* nd = d + datalen + 4 - res; + if(b58check(nd, res, hasher_type, str) < 0) { + return 0; + } + memcpy(data, nd, res - 4); + return res - 4; +} diff --git a/applications/external/flipbip/lib/crypto/base58.h b/applications/external/flipbip/lib/crypto/base58.h new file mode 100644 index 0000000000..0f7fa66d3a --- /dev/null +++ b/applications/external/flipbip/lib/crypto/base58.h @@ -0,0 +1,48 @@ +/** + * Copyright (c) 2013-2014 Tomas Dzetkulic + * Copyright (c) 2013-2014 Pavol Rusnak + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __BASE58_H__ +#define __BASE58_H__ + +#include +#include +#include "hasher.h" +#include "options.h" + +extern const char b58digits_ordered[]; +extern const int8_t b58digits_map[]; + +int base58_encode_check( + const uint8_t* data, + int len, + HasherType hasher_type, + char* str, + int strsize); +int base58_decode_check(const char* str, HasherType hasher_type, uint8_t* data, int datalen); + +// Private +bool b58tobin(void* bin, size_t* binszp, const char* b58); +int b58check(const void* bin, size_t binsz, HasherType hasher_type, const char* base58str); +bool b58enc(char* b58, size_t* b58sz, const void* data, size_t binsz); + +#endif diff --git a/applications/external/flipbip/lib/crypto/bignum.c b/applications/external/flipbip/lib/crypto/bignum.c new file mode 100644 index 0000000000..f0c7d6e43b --- /dev/null +++ b/applications/external/flipbip/lib/crypto/bignum.c @@ -0,0 +1,1834 @@ +/** + * Copyright (c) 2013-2014 Tomas Dzetkulic + * Copyright (c) 2013-2014 Pavol Rusnak + * Copyright (c) 2015 Jochen Hoenicke + * Copyright (c) 2016 Alex Beregszaszi + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "bignum.h" + +#include +#include +#include +#include + +#include "memzero.h" +#include "script.h" + +/* + This library implements 256-bit numbers arithmetic. + + An unsigned 256-bit number is represented by a bignum256 structure, that is an + array of nine 32-bit values called limbs. Limbs are digits of the number in + the base 2**29 representation in the little endian order. This means that + bignum256 x; + represents the value + sum([x[i] * 2**(29*i) for i in range(9)). + + A limb of a bignum256 is *normalized* iff it's less than 2**29. + A bignum256 is *normalized* iff every its limb is normalized. + A number is *fully reduced modulo p* iff it is less than p. + A number is *partly reduced modulo p* iff is is less than 2*p. + The number p is usually a prime number such that 2^256 - 2^224 <= p <= 2^256. + + All functions except bn_fast_mod expect that all their bignum256 inputs are + normalized. (The function bn_fast_mod allows the input number to have the + most significant limb unnormalized). All bignum256 outputs of all functions + are guaranteed to be normalized. + + A number can be partly reduced with bn_fast_mod, a partly reduced number can + be fully reduced with bn_mod. + + A function has *constant control flow with regard to its argument* iff the + order in which instructions of the function are executed doesn't depend on the + value of the argument. + A function has *constant memory access flow with regard to its argument* iff + the memory addresses that are acessed and the order in which they are accessed + don't depend on the value of the argument. + A function *has contant control (memory access) flow* iff it has constant + control (memory access) flow with regard to all its arguments. + + The following function has contant control flow with regard to its arugment + n, however is doesn't have constant memory access flow with regard to it: + void (int n, int *a) } + a[0] = 0; + a[n] = 0; // memory address reveals the value of n + } + + Unless stated otherwise all functions are supposed to have both constant + control flow and constant memory access flow. + */ + +#define BN_MAX_DECIMAL_DIGITS 79 // floor(log(2**(LIMBS * BITS_PER_LIMB), 10)) + 1 + +// out_number = (bignum256) in_number +// Assumes in_number is a raw bigendian 256-bit number +// Guarantees out_number is normalized +void bn_read_be(const uint8_t* in_number, bignum256* out_number) { + uint32_t temp = 0; + + for(int i = 0; i < BN_LIMBS - 1; i++) { + uint32_t limb = read_be(in_number + (BN_LIMBS - 2 - i) * 4); + + temp |= limb << (BN_EXTRA_BITS * i); + out_number->val[i] = temp & BN_LIMB_MASK; + + temp = limb >> (32 - BN_EXTRA_BITS * (i + 1)); + } + + out_number->val[BN_LIMBS - 1] = temp; +} + +// out_number = (256BE) in_number +// Assumes in_number < 2**256 +// Guarantess out_number is a raw bigendian 256-bit number +void bn_write_be(const bignum256* in_number, uint8_t* out_number) { + uint32_t temp = in_number->val[BN_LIMBS - 1]; + for(int i = BN_LIMBS - 2; i >= 0; i--) { + uint32_t limb = in_number->val[i]; + + temp = (temp << (BN_BITS_PER_LIMB - BN_EXTRA_BITS * i)) | (limb >> (BN_EXTRA_BITS * i)); + write_be(out_number + (BN_LIMBS - 2 - i) * 4, temp); + + temp = limb; + } +} + +// out_number = (bignum256) in_number +// Assumes in_number is a raw little endian 256-bit number +// Guarantees out_number is normalized +void bn_read_le(const uint8_t* in_number, bignum256* out_number) { + uint32_t temp = 0; + for(int i = 0; i < BN_LIMBS - 1; i++) { + uint32_t limb = read_le(in_number + i * 4); + + temp |= limb << (BN_EXTRA_BITS * i); + out_number->val[i] = temp & BN_LIMB_MASK; + temp = limb >> (32 - BN_EXTRA_BITS * (i + 1)); + } + + out_number->val[BN_LIMBS - 1] = temp; +} + +// out_number = (256LE) in_number +// Assumes in_number < 2**256 +// Guarantess out_number is a raw little endian 256-bit number +void bn_write_le(const bignum256* in_number, uint8_t* out_number) { + uint32_t temp = in_number->val[BN_LIMBS - 1]; + + for(int i = BN_LIMBS - 2; i >= 0; i--) { + uint32_t limb = in_number->val[i]; + temp = (temp << (BN_BITS_PER_LIMB - BN_EXTRA_BITS * i)) | (limb >> (BN_EXTRA_BITS * i)); + write_le(out_number + i * 4, temp); + temp = limb; + } +} + +// out_number = (bignum256) in_number +// Guarantees out_number is normalized +void bn_read_uint32(uint32_t in_number, bignum256* out_number) { + out_number->val[0] = in_number & BN_LIMB_MASK; + out_number->val[1] = in_number >> BN_BITS_PER_LIMB; + for(uint32_t i = 2; i < BN_LIMBS; i++) out_number->val[i] = 0; +} + +// out_number = (bignum256) in_number +// Guarantees out_number is normalized +void bn_read_uint64(uint64_t in_number, bignum256* out_number) { + out_number->val[0] = in_number & BN_LIMB_MASK; + out_number->val[1] = (in_number >>= BN_BITS_PER_LIMB) & BN_LIMB_MASK; + out_number->val[2] = in_number >> BN_BITS_PER_LIMB; + for(uint32_t i = 3; i < BN_LIMBS; i++) out_number->val[i] = 0; +} + +// Returns the bitsize of x +// Assumes x is normalized +// The function doesn't have neither constant control flow nor constant memory +// access flow +int bn_bitcount(const bignum256* x) { + for(int i = BN_LIMBS - 1; i >= 0; i--) { + uint32_t limb = x->val[i]; + if(limb != 0) { + // __builtin_clz returns the number of leading zero bits starting at the + // most significant bit position + return i * BN_BITS_PER_LIMB + (32 - __builtin_clz(limb)); + } + } + return 0; +} + +// Returns the number of decimal digits of x; if x is 0, returns 1 +// Assumes x is normalized +// The function doesn't have neither constant control flow nor constant memory +// access flow +unsigned int bn_digitcount(const bignum256* x) { + bignum256 val = {0}; + bn_copy(x, &val); + + unsigned int digits = 1; + for(unsigned int i = 0; i < BN_MAX_DECIMAL_DIGITS; i += 3) { + uint32_t limb = 0; + + bn_divmod1000(&val, &limb); + + if(limb >= 100) { + digits = i + 3; + } else if(limb >= 10) { + digits = i + 2; + } else if(limb >= 1) { + digits = i + 1; + } + } + + memzero(&val, sizeof(val)); + + return digits; +} + +// x = 0 +// Guarantees x is normalized +void bn_zero(bignum256* x) { + for(int i = 0; i < BN_LIMBS; i++) { + x->val[i] = 0; + } +} + +// x = 1 +// Guarantees x is normalized +void bn_one(bignum256* x) { + x->val[0] = 1; + for(int i = 1; i < BN_LIMBS; i++) { + x->val[i] = 0; + } +} + +// Returns x == 0 +// Assumes x is normalized +int bn_is_zero(const bignum256* x) { + uint32_t result = 0; + for(int i = 0; i < BN_LIMBS; i++) { + result |= x->val[i]; + } + return !result; +} + +// Returns x == 1 +// Assumes x is normalized +int bn_is_one(const bignum256* x) { + uint32_t result = x->val[0] ^ 1; + for(int i = 1; i < BN_LIMBS; i++) { + result |= x->val[i]; + } + return !result; +} + +// Returns x < y +// Assumes x, y are normalized +int bn_is_less(const bignum256* x, const bignum256* y) { + uint32_t res1 = 0; + uint32_t res2 = 0; + for(int i = BN_LIMBS - 1; i >= 0; i--) { + res1 = (res1 << 1) | (x->val[i] < y->val[i]); + res2 = (res2 << 1) | (x->val[i] > y->val[i]); + } + return res1 > res2; +} + +// Returns x == y +// Assumes x, y are normalized +int bn_is_equal(const bignum256* x, const bignum256* y) { + uint32_t result = 0; + for(int i = 0; i < BN_LIMBS; i++) { + result |= x->val[i] ^ y->val[i]; + } + return !result; +} + +// res = cond if truecase else falsecase +// Assumes cond is either 0 or 1 +// Works properly even if &res == &truecase or &res == &falsecase or +// &truecase == &falsecase or &res == &truecase == &falsecase +void bn_cmov( + bignum256* res, + volatile uint32_t cond, + const bignum256* truecase, + const bignum256* falsecase) { + // Intentional use of bitwise OR operator to ensure constant-time + assert((int)(cond == 1) | (int)(cond == 0)); + + uint32_t tmask = -cond; // tmask = 0xFFFFFFFF if cond else 0x00000000 + uint32_t fmask = ~tmask; // fmask = 0x00000000 if cond else 0xFFFFFFFF + + for(int i = 0; i < BN_LIMBS; i++) { + res->val[i] = (truecase->val[i] & tmask) | (falsecase->val[i] & fmask); + } +} + +// x = -x % prime if cond else x, +// Explicitly x = (3 * prime - x if x > prime else 2 * prime - x) if cond else +// else (x if x > prime else x + prime) +// Assumes x is normalized and partly reduced +// Assumes cond is either 1 or 0 +// Guarantees x is normalized +// Assumes prime is normalized and +// 0 < prime < 2**260 == 2**(BITS_PER_LIMB * LIMBS - 1) +void bn_cnegate(volatile uint32_t cond, bignum256* x, const bignum256* prime) { + // Intentional use of bitwise OR operator to ensure constant time + assert((int)(cond == 1) | (int)(cond == 0)); + + uint32_t tmask = -cond; // tmask = 0xFFFFFFFF if cond else 0x00000000 + uint32_t fmask = ~tmask; // fmask = 0x00000000 if cond else 0xFFFFFFFF + + bn_mod(x, prime); + // x < prime + + uint32_t acc1 = 1; + uint32_t acc2 = 0; + + for(int i = 0; i < BN_LIMBS; i++) { + acc1 += (BN_BASE - 1) + 2 * prime->val[i] - x->val[i]; + // acc1 neither overflows 32 bits nor underflows 0 + // Proof: + // acc1 + (BASE - 1) + 2 * prime[i] - x[i] + // >= (BASE - 1) - x >= (2**BITS_PER_LIMB - 1) - (2**BITS_PER_LIMB - 1) + // == 0 + // acc1 + (BASE - 1) + 2 * prime[i] - x[i] + // <= acc1 + (BASE - 1) + 2 * prime[i] + // <= (2**(32 - BITS_PER_LIMB) - 1) + 2 * (2**BITS_PER_LIMB - 1) + + // (2**BITS_PER_LIMB - 1) + // == 7 + 3 * 2**29 < 2**32 + + acc2 += prime->val[i] + x->val[i]; + // acc2 doesn't overflow 32 bits + // Proof: + // acc2 + prime[i] + x[i] + // <= 2**(32 - BITS_PER_LIMB) - 1 + 2 * (2**BITS_PER_LIMB - 1) + // == 2**(32 - BITS_PER_LIMB) + 2**(BITS_PER_LIMB + 1) - 2 + // == 2**30 + 5 < 2**32 + + // x = acc1 & LIMB_MASK if cond else acc2 & LIMB_MASK + x->val[i] = ((acc1 & tmask) | (acc2 & fmask)) & BN_LIMB_MASK; + + acc1 >>= BN_BITS_PER_LIMB; + // acc1 <= 7 == 2**(32 - BITS_PER_LIMB) - 1 + // acc1 == 2**(BITS_PER_LIMB * (i + 1)) + 2 * prime[:i + 1] - x[:i + 1] + // >> BITS_PER_LIMB * (i + 1) + + acc2 >>= BN_BITS_PER_LIMB; + // acc2 <= 7 == 2**(32 - BITS_PER_LIMB) - 1 + // acc2 == prime[:i + 1] + x[:i + 1] >> BITS_PER_LIMB * (i + 1) + } + + // assert(acc1 == 1); // assert prime <= 2**260 + // assert(acc2 == 0); + + // clang-format off + // acc1 == 1 + // Proof: + // acc1 == 2**(BITS_PER_LIMB * LIMBS) + 2 * prime[:LIMBS] - x[:LIMBS] >> BITS_PER_LIMB * LIMBS + // == 2**(BITS_PER_LIMB * LIMBS) + 2 * prime - x >> BITS_PER_LIMB * LIMBS + // <= 2**(BITS_PER_LIMB * LIMBS) + 2 * prime >> BITS_PER_LIMB * LIMBS + // <= 2**(BITS_PER_LIMB * LIMBS) + 2 * (2**(BITS_PER_LIMB * LIMBS - 1) - 1) >> BITS_PER_LIMB * LIMBS + // <= 2**(BITS_PER_LIMB * LIMBS) + 2**(BITS_PER_LIMB * LIMBS) - 2 >> BITS_PER_LIMB * LIMBS + // == 1 + + // acc1 == 2**(BITS_PER_LIMB * LIMBS) + 2 * prime[:LIMBS] - x[:LIMBS] >> BITS_PER_LIMB * LIMBS + // == 2**(BITS_PER_LIMB * LIMBS) + 2 * prime - x >> BITS_PER_LIMB * LIMBS + // >= 2**(BITS_PER_LIMB * LIMBS) + 0 >> BITS_PER_LIMB * LIMBS + // == 1 + + // acc2 == 0 + // Proof: + // acc2 == prime[:LIMBS] + x[:LIMBS] >> BITS_PER_LIMB * LIMBS + // == prime + x >> BITS_PER_LIMB * LIMBS + // <= 2 * prime - 1 >> BITS_PER_LIMB * LIMBS + // <= 2 * (2**(BITS_PER_LIMB * LIMBS - 1) - 1) - 1 >> 261 + // == 2**(BITS_PER_LIMB * LIMBS) - 3 >> BITS_PER_LIMB * LIMBS + // == 0 + // clang-format on +} + +// x <<= 1 +// Assumes x is normalized, x < 2**260 == 2**(LIMBS*BITS_PER_LIMB - 1) +// Guarantees x is normalized +void bn_lshift(bignum256* x) { + for(int i = BN_LIMBS - 1; i > 0; i--) { + x->val[i] = ((x->val[i] << 1) & BN_LIMB_MASK) | (x->val[i - 1] >> (BN_BITS_PER_LIMB - 1)); + } + x->val[0] = (x->val[0] << 1) & BN_LIMB_MASK; +} + +// x >>= 1, i.e. x = floor(x/2) +// Assumes x is normalized +// Guarantees x is normalized +// If x is partly reduced (fully reduced) modulo prime, +// guarantess x will be partly reduced (fully reduced) modulo prime +void bn_rshift(bignum256* x) { + for(int i = 0; i < BN_LIMBS - 1; i++) { + x->val[i] = (x->val[i] >> 1) | ((x->val[i + 1] & 1) << (BN_BITS_PER_LIMB - 1)); + } + x->val[BN_LIMBS - 1] >>= 1; +} + +// Sets i-th least significant bit (counting from zero) +// Assumes x is normalized and 0 <= i < 261 == LIMBS*BITS_PER_LIMB +// Guarantees x is normalized +// The function has constant control flow but not constant memory access flow +// with regard to i +void bn_setbit(bignum256* x, uint16_t i) { + assert(i < BN_LIMBS * BN_BITS_PER_LIMB); + x->val[i / BN_BITS_PER_LIMB] |= (1u << (i % BN_BITS_PER_LIMB)); +} + +// clears i-th least significant bit (counting from zero) +// Assumes x is normalized and 0 <= i < 261 == LIMBS*BITS_PER_LIMB +// Guarantees x is normalized +// The function has constant control flow but not constant memory access flow +// with regard to i +void bn_clearbit(bignum256* x, uint16_t i) { + assert(i < BN_LIMBS * BN_BITS_PER_LIMB); + x->val[i / BN_BITS_PER_LIMB] &= ~(1u << (i % BN_BITS_PER_LIMB)); +} + +// returns i-th least significant bit (counting from zero) +// Assumes x is normalized and 0 <= i < 261 == LIMBS*BITS_PER_LIMB +// The function has constant control flow but not constant memory access flow +// with regard to i +uint32_t bn_testbit(const bignum256* x, uint16_t i) { + assert(i < BN_LIMBS * BN_BITS_PER_LIMB); + return (x->val[i / BN_BITS_PER_LIMB] >> (i % BN_BITS_PER_LIMB)) & 1; +} + +// res = x ^ y +// Assumes x, y are normalized +// Guarantees res is normalized +// Works properly even if &res == &x or &res == &y or &res == &x == &y +void bn_xor(bignum256* res, const bignum256* x, const bignum256* y) { + for(int i = 0; i < BN_LIMBS; i++) { + res->val[i] = x->val[i] ^ y->val[i]; + } +} + +// x = x / 2 % prime +// Explicitly x = x / 2 if is_even(x) else (x + prime) / 2 +// Assumes x is normalized, x + prime < 261 == LIMBS * BITS_PER_LIMB +// Guarantees x is normalized +// If x is partly reduced (fully reduced) modulo prime, +// guarantess x will be partly reduced (fully reduced) modulo prime +// Assumes prime is an odd number and normalized +void bn_mult_half(bignum256* x, const bignum256* prime) { + // x = x / 2 if is_even(x) else (x + prime) / 2 + + uint32_t x_is_odd_mask = -(x->val[0] & 1); // x_is_odd_mask = 0xFFFFFFFF if is_odd(x) else 0 + + uint32_t acc = (x->val[0] + (prime->val[0] & x_is_odd_mask)) >> 1; + // acc < 2**BITS_PER_LIMB + // Proof: + // acc == x[0] + prime[0] & x_is_odd_mask >> 1 + // <= (2**(BITS_PER_LIMB) - 1) + (2**(BITS_PER_LIMB) - 1) >> 1 + // == 2**(BITS_PER_LIMB + 1) - 2 >> 1 + // < 2**(BITS_PER_LIMB) + + for(int i = 0; i < BN_LIMBS - 1; i++) { + uint32_t temp = (x->val[i + 1] + (prime->val[i + 1] & x_is_odd_mask)); + // temp < 2**(BITS_PER_LIMB + 1) + // Proof: + // temp == x[i + 1] + val[i + 1] & x_is_odd_mask + // <= (2**(BITS_PER_LIMB) - 1) + (2**(BITS_PER_LIMB) - 1) + // < 2**(BITS_PER_LIMB + 1) + + acc += (temp & 1) << (BN_BITS_PER_LIMB - 1); + // acc doesn't overflow 32 bits + // Proof: + // acc + (temp & 1 << BITS_PER_LIMB - 1) + // <= 2**(BITS_PER_LIMB + 1) + 2**(BITS_PER_LIMB - 1) + // <= 2**30 + 2**28 < 2**32 + + x->val[i] = acc & BN_LIMB_MASK; + acc >>= BN_BITS_PER_LIMB; + acc += temp >> 1; + // acc < 2**(BITS_PER_LIMB + 1) + // Proof: + // acc + (temp >> 1) + // <= (2**(32 - BITS_PER_LIMB) - 1) + (2**(BITS_PER_LIMB + 1) - 1 >> 1) + // == 7 + 2**(BITS_PER_LIMB) - 1 < 2**(BITS_PER_LIMB + 1) + + // acc == x[:i+2]+(prime[:i+2] & x_is_odd_mask) >> BITS_PER_LIMB * (i+1) + } + x->val[BN_LIMBS - 1] = acc; + + // assert(acc >> BITS_PER_LIMB == 0); + // acc >> BITS_PER_LIMB == 0 + // Proof: + // acc + // == x[:LIMBS] + (prime[:LIMBS] & x_is_odd_mask) >> BITS_PER_LIMB*LIMBS + // == x + (prime & x_is_odd_mask) >> BITS_PER_LIMB * LIMBS + // <= x + prime >> BITS_PER_LIMB * LIMBS + // <= 2**(BITS_PER_LIMB * LIMBS) - 1 >> BITS_PER_LIMB * LIMBS + // == 0 +} + +// x = x * k % prime +// Assumes x is normalized, 0 <= k <= 8 = 2**(32 - BITS_PER_LIMB) +// Assumes prime is normalized and 2^256 - 2^224 <= prime <= 2^256 +// Guarantees x is normalized and partly reduced modulo prime +void bn_mult_k(bignum256* x, uint8_t k, const bignum256* prime) { + assert(k <= 8); + + for(int i = 0; i < BN_LIMBS; i++) { + x->val[i] = k * x->val[i]; + // x[i] doesn't overflow 32 bits + // k * x[i] <= 2**(32 - BITS_PER_LIMB) * (2**BITS_PER_LIMB - 1) + // < 2**(32 - BITS_PER_LIMB) * 2**BITS_PER_LIMB == 2**32 + } + + bn_fast_mod(x, prime); +} + +// Reduces partly reduced x modulo prime +// Explicitly x = x if x < prime else x - prime +// Assumes x is partly reduced modulo prime +// Guarantees x is fully reduced modulo prime +// Assumes prime is nonzero and normalized +void bn_mod(bignum256* x, const bignum256* prime) { + uint32_t x_less_prime = bn_is_less(x, prime); + + bignum256 temp = {0}; + bn_subtract(x, prime, &temp); + bn_cmov(x, x_less_prime, x, &temp); + + memzero(&temp, sizeof(temp)); +} + +// Auxiliary function for bn_multiply +// res = k * x +// Assumes k and x are normalized +// Guarantees res is normalized 18 digit little endian number in base 2**29 +void bn_multiply_long(const bignum256* k, const bignum256* x, uint32_t res[2 * BN_LIMBS]) { + // Uses long multiplication in base 2**29, see + // https://en.wikipedia.org/wiki/Multiplication_algorithm#Long_multiplication + + uint64_t acc = 0; + + // compute lower half + for(int i = 0; i < BN_LIMBS; i++) { + for(int j = 0; j <= i; j++) { + acc += k->val[j] * (uint64_t)x->val[i - j]; + // acc doesn't overflow 64 bits + // Proof: + // acc <= acc + sum([k[j] * x[i-j] for j in range(i)]) + // <= (2**(64 - BITS_PER_LIMB) - 1) + + // LIMBS * (2**BITS_PER_LIMB - 1) * (2**BITS_PER_LIMB - 1) + // == (2**35 - 1) + 9 * (2**29 - 1) * (2**29 - 1) + // <= 2**35 + 9 * 2**58 < 2**64 + } + + res[i] = acc & BN_LIMB_MASK; + acc >>= BN_BITS_PER_LIMB; + // acc <= 2**35 - 1 == 2**(64 - BITS_PER_LIMB) - 1 + } + + // compute upper half + for(int i = BN_LIMBS; i < 2 * BN_LIMBS - 1; i++) { + for(int j = i - BN_LIMBS + 1; j < BN_LIMBS; j++) { + acc += k->val[j] * (uint64_t)x->val[i - j]; + // acc doesn't overflow 64 bits + // Proof: + // acc <= acc + sum([k[j] * x[i-j] for j in range(i)]) + // <= (2**(64 - BITS_PER_LIMB) - 1) + // LIMBS * (2**BITS_PER_LIMB - 1) * (2**BITS_PER_LIMB - 1) + // == (2**35 - 1) + 9 * (2**29 - 1) * (2**29 - 1) + // <= 2**35 + 9 * 2**58 < 2**64 + } + + res[i] = acc & (BN_BASE - 1); + acc >>= BN_BITS_PER_LIMB; + // acc < 2**35 == 2**(64 - BITS_PER_LIMB) + } + + res[2 * BN_LIMBS - 1] = acc; +} + +// Auxiliary function for bn_multiply +// Assumes 0 <= d <= 8 == LIMBS - 1 +// Assumes res is normalized and res < 2**(256 + 29*d + 31) +// Guarantess res in normalized and res < 2 * prime * 2**(29*d) +// Assumes prime is normalized, 2**256 - 2**224 <= prime <= 2**256 +void bn_multiply_reduce_step(uint32_t res[2 * BN_LIMBS], const bignum256* prime, uint32_t d) { + // clang-format off + // Computes res = res - (res // 2**(256 + BITS_PER_LIMB * d)) * prime * 2**(BITS_PER_LIMB * d) + + // res - (res // 2**(256 + BITS_PER_LIMB * d)) * prime * 2**(BITS_PER_LIMB * d) < 2 * prime * 2**(BITS_PER_LIMB * d) + // Proof: + // res - res // (2**(256 + BITS_PER_LIMB * d)) * 2**(BITS_PER_LIMB * d) * prime + // == res - res // (2**(256 + BITS_PER_LIMB * d)) * 2**(BITS_PER_LIMB * d) * (2**256 - (2**256 - prime)) + // == res - res // (2**(256 + BITS_PER_LIMB * d)) * 2**(BITS_PER_LIMB * d) * 2**256 + res // (2**(256 + BITS_PER_LIMB * d)) * 2**(BITS_PER_LIMB * d) * (2**256 - prime) + // == (res % 2**(256 + BITS_PER_LIMB * d)) + res // (2**256 + BITS_PER_LIMB * d) * 2**(BITS_PER_LIMB * d) * (2**256 - prime) + // <= (2**(256 + 29*d + 31) % 2**(256 + 29*d)) + (2**(256 + 29*d + 31) - 1) / (2**256 + 29*d) * 2**(29*d) * (2**256 - prime) + // <= 2**(256 + 29*d) + 2**(256 + 29*d + 31) / (2**256 + 29*d) * 2**(29*d) * (2**256 - prime) + // == 2**(256 + 29*d) + 2**31 * 2**(29*d) * (2**256 - prime) + // == 2**(29*d) * (2**256 + 2**31 * (2*256 - prime)) + // <= 2**(29*d) * (2**256 + 2**31 * 2*224) + // <= 2**(29*d) * (2**256 + 2**255) + // <= 2**(29*d) * 2 * (2**256 - 2**224) + // <= 2 * prime * 2**(29*d) + // clang-format on + + uint32_t coef = (res[d + BN_LIMBS - 1] >> (256 - (BN_LIMBS - 1) * BN_BITS_PER_LIMB)) + + (res[d + BN_LIMBS] << ((BN_LIMBS * BN_BITS_PER_LIMB) - 256)); + + // coef == res // 2**(256 + BITS_PER_LIMB * d) + + // coef < 2**31 + // Proof: + // coef == res // 2**(256 + BITS_PER_LIMB * d) + // < 2**(256 + 29 * d + 31) // 2**(256 + 29 * d) + // == 2**31 + + const int shift = 31; + uint64_t acc = 1ull << shift; + + for(int i = 0; i < BN_LIMBS; i++) { + acc += (((uint64_t)(BN_BASE - 1)) << shift) + res[d + i] - prime->val[i] * (uint64_t)coef; + // acc neither overflow 64 bits nor underflow zero + // Proof: + // acc + ((BASE - 1) << shift) + res[d + i] - prime[i] * coef + // >= ((BASE - 1) << shift) - prime[i] * coef + // == 2**shift * (2**BITS_PER_LIMB - 1) - (2**BITS_PER_LIMB - 1) * + // (2**31 - 1) + // == (2**shift - 2**31 + 1) * (2**BITS_PER_LIMB - 1) + // == (2**31 - 2**31 + 1) * (2**29 - 1) + // == 2**29 - 1 > 0 + // acc + ((BASE - 1) << shift) + res[d + i] - prime[i] * coef + // <= acc + ((BASE - 1) << shift) + res[d+i] + // <= (2**(64 - BITS_PER_LIMB) - 1) + 2**shift * (2**BITS_PER_LIMB - 1) + // + (2*BITS_PER_LIMB - 1) + // == (2**(64 - BITS_PER_LIMB) - 1) + (2**shift + 1) * + // (2**BITS_PER_LIMB - 1) + // == (2**35 - 1) + (2**31 + 1) * (2**29 - 1) + // <= 2**35 + 2**60 + 2**29 < 2**64 + + res[d + i] = acc & BN_LIMB_MASK; + acc >>= BN_BITS_PER_LIMB; + // acc <= 2**(64 - BITS_PER_LIMB) - 1 == 2**35 - 1 + + // acc == (1 << BITS_PER_LIMB * (i + 1) + shift) + res[d : d + i + 1] + // - coef * prime[:i + 1] >> BITS_PER_LIMB * (i + 1) + } + + // acc += (((uint64_t)(BASE - 1)) << shift) + res[d + LIMBS]; + // acc >>= BITS_PER_LIMB; + // assert(acc <= 1ul << shift); + + // clang-format off + // acc == 1 << shift + // Proof: + // acc + // == (1 << BITS_PER_LIMB * (LIMBS + 1) + shift) + res[d : d + LIMBS + 1] - coef * prime[:LIMBS] >> BITS_PER_LIMB * (LIMBS + 1) + // == (1 << BITS_PER_LIMB * (LIMBS + 1) + shift) + res[d : d + LIMBS + 1] - coef * prime >> BITS_PER_LIMB * (LIMBS + 1) + + // == (1 << BITS_PER_LIMB * (LIMBS + 1) + shift) + (res[d : d + LIMBS + 1] - coef * prime) >> BITS_PER_LIMB * (LIMBS + 1) + // <= (1 << BITS_PER_LIMB * (LIMBS + 1) + shift) + (res[:d] + BASE**d * res[d : d + LIMBS + 1] - BASE**d * coef * prime)//BASE**d >> BITS_PER_LIMB * (LIMBS + 1) + // <= (1 << BITS_PER_LIMB * (LIMBS + 1) + shift) + (res - BASE**d * coef * prime) // BASE**d >> BITS_PER_LIMB * (LIMBS + 1) + // == (1 << BITS_PER_LIMB * (LIMBS + 1) + shift) + (2 * prime * BASE**d) // BASE**d >> BITS_PER_LIMB * (LIMBS + 1) + // <= (1 << 321) + 2 * 2**256 >> 290 + // == 1 << 31 == 1 << shift + + // == (1 << BITS_PER_LIMB * (LIMBS + 1) + shift) + res[d : d + LIMBS + 1] - coef * prime[:LIMBS + 1] >> BITS_PER_LIMB * (LIMBS + 1) + // >= (1 << BITS_PER_LIMB * (LIMBS + 1) + shift) + 0 >> BITS_PER_LIMB * (LIMBS + 1) + // == 1 << shift + // clang-format on + + res[d + BN_LIMBS] = 0; +} + +// Auxiliary function for bn_multiply +// Partly reduces res and stores both in x and res +// Assumes res in normalized and res < 2**519 +// Guarantees x is normalized and partly reduced modulo prime +// Assumes prime is normalized, 2**256 - 2**224 <= prime <= 2**256 +void bn_multiply_reduce(bignum256* x, uint32_t res[2 * BN_LIMBS], const bignum256* prime) { + for(int i = BN_LIMBS - 1; i >= 0; i--) { + // res < 2**(256 + 29*i + 31) + // Proof: + // if i == LIMBS - 1: + // res < 2**519 + // == 2**(256 + 29 * 8 + 31) + // == 2**(256 + 29 * (LIMBS - 1) + 31) + // else: + // res < 2 * prime * 2**(29 * (i + 1)) + // <= 2**256 * 2**(29*i + 29) < 2**(256 + 29*i + 31) + bn_multiply_reduce_step(res, prime, i); + } + + for(int i = 0; i < BN_LIMBS; i++) { + x->val[i] = res[i]; + } +} + +// x = k * x % prime +// Assumes k, x are normalized, k * x < 2**519 +// Guarantees x is normalized and partly reduced modulo prime +// Assumes prime is normalized, 2**256 - 2**224 <= prime <= 2**256 +void bn_multiply(const bignum256* k, bignum256* x, const bignum256* prime) { + uint32_t res[2 * BN_LIMBS] = {0}; + + bn_multiply_long(k, x, res); + bn_multiply_reduce(x, res, prime); + + memzero(res, sizeof(res)); +} + +// Partly reduces x modulo prime +// Assumes limbs of x except the last (the most significant) one are normalized +// Assumes prime is normalized and 2^256 - 2^224 <= prime <= 2^256 +// Guarantees x is normalized and partly reduced modulo prime +void bn_fast_mod(bignum256* x, const bignum256* prime) { + // Computes x = x - (x // 2**256) * prime + + // x < 2**((LIMBS - 1) * BITS_PER_LIMB + 32) == 2**264 + + // x - (x // 2**256) * prime < 2 * prime + // Proof: + // x - (x // 2**256) * prime + // == x - (x // 2**256) * (2**256 - (2**256 - prime)) + // == x - ((x // 2**256) * 2**256) + (x // 2**256) * (2**256 - prime) + // == (x % prime) + (x // 2**256) * (2**256 - prime) + // <= prime - 1 + (2**264 // 2**256) * (2**256 - prime) + // <= 2**256 + 2**8 * 2**224 == 2**256 + 2**232 + // < 2 * (2**256 - 2**224) + // <= 2 * prime + + // x - (x // 2**256 - 1) * prime < 2 * prime + // Proof: + // x - (x // 2**256) * prime + prime + // == x - (x // 2**256) * (2**256 - (2**256 - prime)) + prime + // == x - ((x//2**256) * 2**256) + (x//2**256) * (2**256 - prime) + prime + // == (x % prime) + (x // 2**256) * (2**256 - prime) + prime + // <= 2 * prime - 1 + (2**264 // 2**256) * (2**256 - prime) + // <= 2 * prime + 2**8 * 2**224 == 2**256 + 2**232 + 2**256 - 2**224 + // < 2 * (2**256 - 2**224) + // <= 2 * prime + + uint32_t coef = x->val[BN_LIMBS - 1] >> (256 - ((BN_LIMBS - 1) * BN_BITS_PER_LIMB)); + + // clang-format off + // coef == x // 2**256 + // 0 <= coef < 2**((LIMBS - 1) * BITS_PER_LIMB + 32 - 256) == 256 + // Proof: + //* Let x[[a : b] be the number consisting of a-th to (b-1)-th bit of the number x. + // x[LIMBS - 1] >> (256 - ((LIMBS - 1) * BITS_PER_LIMB)) + // == x[[(LIMBS - 1) * BITS_PER_LIMB : (LIMBS - 1) * BITS_PER_LIMB + 32]] >> (256 - ((LIMBS - 1) * BITS_PER_LIMB)) + // == x[[256 - ((LIMBS - 1) * BITS_PER_LIMB) + (LIMBS - 1) * BITS_PER_LIMB : (LIMBS - 1) * BITS_PER_LIMB + 32]] + // == x[[256 : (LIMBS - 1) * BITS_PER_LIMB + 32]] + // == x[[256 : 264]] == x // 2**256 + // clang-format on + + const int shift = 8; + uint64_t acc = 1ull << shift; + + for(int i = 0; i < BN_LIMBS; i++) { + acc += (((uint64_t)(BN_BASE - 1)) << shift) + x->val[i] - prime->val[i] * (uint64_t)coef; + // acc neither overflows 64 bits nor underflows 0 + // Proof: + // acc + (BASE - 1 << shift) + x[i] - prime[i] * coef + // >= (BASE - 1 << shift) - prime[i] * coef + // >= 2**shift * (2**BITS_PER_LIMB - 1) - (2**BITS_PER_LIMB - 1) * 255 + // == (2**shift - 255) * (2**BITS_PER_LIMB - 1) + // == (2**8 - 255) * (2**29 - 1) == 2**29 - 1 >= 0 + // acc + (BASE - 1 << shift) + x[i] - prime[i] * coef + // <= acc + ((BASE - 1) << shift) + x[i] + // <= (2**(64 - BITS_PER_LIMB) - 1) + 2**shift * (2**BITS_PER_LIMB - 1) + // + (2**32 - 1) + // == (2**35 - 1) + 2**8 * (2**29 - 1) + 2**32 + // < 2**35 + 2**37 + 2**32 < 2**64 + + x->val[i] = acc & BN_LIMB_MASK; + acc >>= BN_BITS_PER_LIMB; + // acc <= 2**(64 - BITS_PER_LIMB) - 1 == 2**35 - 1 + + // acc == (1 << BITS_PER_LIMB * (i + 1) + shift) + x[:i + 1] + // - coef * prime[:i + 1] >> BITS_PER_LIMB * (i + 1) + } + + // assert(acc == 1 << shift); + + // clang-format off + // acc == 1 << shift + // Proof: + // acc + // == (1 << BITS_PER_LIMB * LIMBS + shift) + x[:LIMBS] - coef * prime[:LIMBS] >> BITS_PER_LIMB * LIMBS + // == (1 << BITS_PER_LIMB * LIMBS + shift) + (x - coef * prime) >> BITS_PER_LIMB * LIMBS + // <= (1 << BITS_PER_LIMB * LIMBS + shift) + (2 * prime) >> BITS_PER_LIMB * LIMBS + // <= (1 << BITS_PER_LIMB * LIMBS + shift) + 2 * 2**256 >> BITS_PER_LIMB * LIMBS + // <= 2**269 + 2**257 >> 2**261 + // <= 1 << 8 == 1 << shift + + // acc + // == (1 << BITS_PER_LIMB * LIMBS + shift) + x[:LIMBS] - coef * prime[:LIMBS] >> BITS_PER_LIMB * LIMBS + // >= (1 << BITS_PER_LIMB * LIMBS + shift) + 0 >> BITS_PER_LIMB * LIMBS + // == (1 << BITS_PER_LIMB * LIMBS + shift) + 0 >> BITS_PER_LIMB * LIMBS + // <= 1 << 8 == 1 << shift + // clang-format on +} + +// res = x**e % prime +// Assumes both x and e are normalized, x < 2**259 +// Guarantees res is normalized and partly reduced modulo prime +// Works properly even if &x == &res +// Assumes prime is normalized, 2**256 - 2**224 <= prime <= 2**256 +// The function doesn't have neither constant control flow nor constant memory +// access flow with regard to e +void bn_power_mod(const bignum256* x, const bignum256* e, const bignum256* prime, bignum256* res) { + // Uses iterative right-to-left exponentiation by squaring, see + // https://en.wikipedia.org/wiki/Modular_exponentiation#Right-to-left_binary_method + + bignum256 acc = {0}; + bn_copy(x, &acc); + + bn_one(res); + for(int i = 0; i < BN_LIMBS; i++) { + uint32_t limb = e->val[i]; + + for(int j = 0; j < BN_BITS_PER_LIMB; j++) { + // Break if the following bits of the last limb are zero + if(i == BN_LIMBS - 1 && limb == 0) break; + + if(limb & 1) + // acc * res < 2**519 + // Proof: + // acc * res <= max(2**259 - 1, 2 * prime) * (2 * prime) + // == max(2**259 - 1, 2**257) * 2**257 < 2**259 * 2**257 + // == 2**516 < 2**519 + bn_multiply(&acc, res, prime); + + limb >>= 1; + // acc * acc < 2**519 + // Proof: + // acc * acc <= max(2**259 - 1, 2 * prime)**2 + // <= (2**259)**2 == 2**518 < 2**519 + bn_multiply(&acc, &acc, prime); + } + // acc == x**(e[:i + 1]) % prime + } + + memzero(&acc, sizeof(acc)); +} + +// x = sqrt(x) % prime +// Explicitly x = x**((prime+1)/4) % prime +// The other root is -sqrt(x) +// Assumes x is normalized, x < 2**259 and quadratic residuum mod prime +// Assumes prime is a prime number, prime % 4 == 3, it is normalized and +// 2**256 - 2**224 <= prime <= 2**256 +// Guarantees x is normalized and fully reduced modulo prime +// The function doesn't have neither constant control flow nor constant memory +// access flow with regard to prime +void bn_sqrt(bignum256* x, const bignum256* prime) { + // Uses the Lagrange formula for the primes of the special form, see + // http://en.wikipedia.org/wiki/Quadratic_residue#Prime_or_prime_power_modulus + // If prime % 4 == 3, then sqrt(x) % prime == x**((prime+1)//4) % prime + + assert(prime->val[BN_LIMBS - 1] % 4 == 3); + + // e = (prime + 1) // 4 + bignum256 e = {0}; + bn_copy(prime, &e); + bn_addi(&e, 1); + bn_rshift(&e); + bn_rshift(&e); + + bn_power_mod(x, &e, prime, x); + bn_mod(x, prime); + + memzero(&e, sizeof(e)); +} + +// a = 1/a % 2**n +// Assumes a is odd, 1 <= n <= 32 +// The function doesn't have neither constant control flow nor constant memory +// access flow with regard to n +uint32_t inverse_mod_power_two(uint32_t a, uint32_t n) { + // Uses "Explicit Quadratic Modular inverse modulo 2" from section 3.3 of "On + // Newton-Raphson iteration for multiplicative inverses modulo prime powers" + // by Jean-Guillaume Dumas, see + // https://arxiv.org/pdf/1209.6626.pdf + + // 1/a % 2**n + // = (2-a) * product([1 + (a-1)**(2**i) for i in range(1, floor(log2(n)))]) + + uint32_t acc = 2 - a; + uint32_t f = a - 1; + + // mask = (1 << n) - 1 + uint32_t mask = n == 32 ? 0xFFFFFFFF : (1u << n) - 1; + + for(uint32_t i = 1; i < n; i <<= 1) { + f = (f * f) & mask; + acc = (acc * (1 + f)) & mask; + } + + return acc; +} + +// x = (x / 2**BITS_PER_LIMB) % prime +// Assumes both x and prime are normalized +// Assumes prime is an odd number and normalized +// Guarantees x is normalized +// If x is partly reduced (fully reduced) modulo prime, +// guarantess x will be partly reduced (fully reduced) modulo prime +void bn_divide_base(bignum256* x, const bignum256* prime) { + // Uses an explicit formula for the modular inverse of power of two + // (x / 2**n) % prime == (x + ((-x / prime) % 2**n) * prime) // 2**n + // Proof: + // (x + ((-x / prime) % 2**n) * prime) % 2**n + // == (x - x / prime * prime) % 2**n + // == 0 + // (x + ((-1 / prime) % 2**n) * prime) % prime + // == x + // if x < prime: + // (x + ((-x / prime) % 2**n) * prime) // 2**n + // <= ((prime - 1) + (2**n - 1) * prime) / 2**n + // == (2**n * prime - 1) / 2**n == prime - 1 / 2**n < prime + // if x < 2 * prime: + // (x + ((-x / prime) % 2**n) * prime) // 2**n + // <= ((2 * prime - 1) + (2**n - 1) * prime) / 2**n + // == (2**n * prime + prime - 1) / 2**n + // == prime + (prime - 1) / 2**n < 2 * prime + + // m = (-x / prime) % 2**BITS_PER_LIMB + uint32_t m = (x->val[0] * (BN_BASE - inverse_mod_power_two(prime->val[0], BN_BITS_PER_LIMB))) & + BN_LIMB_MASK; + // m < 2**BITS_PER_LIMB + + uint64_t acc = x->val[0] + (uint64_t)m * prime->val[0]; + acc >>= BN_BITS_PER_LIMB; + + for(int i = 1; i < BN_LIMBS; i++) { + acc = acc + x->val[i] + (uint64_t)m * prime->val[i]; + // acc does not overflow 64 bits + // acc == acc + x + m * prime + // <= 2**(64 - BITS_PER_LIMB) + 2**(BITS_PER_LIMB) + // 2**(BITS_PER_LIMB) * 2**(BITS_PER_LIMB) + // <= 2**(2 * BITS_PER_LIMB) + 2**(64 - BITS_PER_LIMB) + + // 2**(BITS_PER_LIMB) + // <= 2**58 + 2**35 + 2**29 < 2**64 + + x->val[i - 1] = acc & BN_LIMB_MASK; + acc >>= BN_BITS_PER_LIMB; + // acc < 2**35 == 2**(64 - BITS_PER_LIMB) + + // acc == x[:i + 1] + m * prime[:i + 1] >> BITS_PER_LIMB * (i + 1) + } + + x->val[BN_LIMBS - 1] = acc; + + assert(acc >> BN_BITS_PER_LIMB == 0); + + // clang-format off + // acc >> BITS_PER_LIMB == 0 + // Proof: + // acc >> BITS_PER_LIMB + // == (x[:LIMB] + m * prime[:LIMB] >> BITS_PER_LIMB * LIMBS) >> BITS_PER_LIMB * (LIMBS + 1) + // == x + m * prime >> BITS_PER_LIMB * (LIMBS + 1) + // <= (2**(BITS_PER_LIMB * LIMBS) - 1) + (2**BITS_PER_LIMB - 1) * (2**(BITS_PER_LIMB * LIMBS) - 1) >> BITS_PER_LIMB * (LIMBS + 1) + // == 2**(BITS_PER_LIMB * LIMBS) - 1 + 2**(BITS_PER_LIMB * (LIMBS + 1)) - 2**(BITS_PER_LIMB * LIMBS) - 2**BITS_PER_LIMB + 1 >> BITS_PER_LIMB * (LIMBS + 1) + // == 2**(BITS_PER_LIMB * (LIMBS + 1)) - 2**BITS_PER_LIMB >> BITS_PER_LIMB * (LIMBS + 1) + // == 0 + // clang-format on +} + +#if !USE_INVERSE_FAST +// x = 1/x % prime if x != 0 else 0 +// Assumes x is normalized +// Assumes prime is a prime number +// Guarantees x is normalized and fully reduced modulo prime +// Assumes prime is normalized, 2**256 - 2**224 <= prime <= 2**256 +// The function doesn't have neither constant control flow nor constant memory +// access flow with regard to prime +static void bn_inverse_slow(bignum256* x, const bignum256* prime) { + // Uses formula 1/x % prime == x**(prime - 2) % prime + // See https://en.wikipedia.org/wiki/Fermat%27s_little_theorem + + bn_fast_mod(x, prime); + + // e = prime - 2 + bignum256 e = {0}; + bn_read_uint32(2, &e); + bn_subtract(prime, &e, &e); + + bn_power_mod(x, &e, prime, x); + bn_mod(x, prime); + + memzero(&e, sizeof(e)); +} +#endif + +#if false +// x = 1/x % prime if x != 0 else 0 +// Assumes x is is_normalized +// Assumes GCD(x, prime) = 1 +// Guarantees x is normalized and fully reduced modulo prime +// Assumes prime is odd, normalized, 2**256 - 2**224 <= prime <= 2**256 +// The function doesn't have neither constant control flow nor constant memory +// access flow with regard to prime and x +static void bn_inverse_fast(bignum256 *x, const bignum256 *prime) { + // "The Almost Montgomery Inverse" from the section 3 of "Constant Time + // Modular Inversion" by Joppe W. Bos + // See http://www.joppebos.com/files/CTInversion.pdf + + /* + u = prime + v = x & prime + s = 1 + r = 0 + + k = 0 + while v != 1: + k += 1 + if is_even(u): + u = u // 2 + s = 2 * s + elif is_even(v): + v = v // 2 + r = 2 * r + elif v < u: + u = (u - v) // 2 + r = r + s + s = 2 * s + else: + v = (v - u) // 2 + s = r + s + r = 2 * r + + s = (s / 2**k) % prime + return s + */ + + if (bn_is_zero(x)) return; + + bn_fast_mod(x, prime); + bn_mod(x, prime); + + bignum256 u = {0}, v = {0}, r = {0}, s = {0}; + bn_copy(prime, &u); + bn_copy(x, &v); + bn_one(&s); + bn_zero(&r); + + int k = 0; + while (!bn_is_one(&v)) { + if ((u.val[0] & 1) == 0) { + bn_rshift(&u); + bn_lshift(&s); + } else if ((v.val[0] & 1) == 0) { + bn_rshift(&v); + bn_lshift(&r); + } else if (bn_is_less(&v, &u)) { + bn_subtract(&u, &v, &u); + bn_rshift(&u); + bn_add(&r, &s); + bn_lshift(&s); + } else { + bn_subtract(&v, &u, &v); + bn_rshift(&v); + bn_add(&s, &r); + bn_lshift(&r); + } + k += 1; + assert(!bn_is_zero(&v)); // assert GCD(x, prime) == 1 + } + + // s = s / 2**(k // BITS_PER_LIMB * BITS_PER_LIMB) + for (int i = 0; i < k / BITS_PER_LIMB; i++) { + bn_divide_base(&s, prime); + } + + // s = s / 2**(k % BITS_PER_LIMB) + for (int i = 0; i < k % BN_BITS_PER_LIMB; i++) { + bn_mult_half(&s, prime); + } + + bn_copy(&s, x); + + memzero(&u, sizeof(u)); + memzero(&v, sizeof(v)); + memzero(&r, sizeof(r)); + memzero(&s, sizeof(s)); +} +#endif + +#if USE_INVERSE_FAST +// x = 1/x % prime if x != 0 else 0 +// Assumes x is is_normalized +// Assumes GCD(x, prime) = 1 +// Guarantees x is normalized and fully reduced modulo prime +// Assumes prime is odd, normalized, 2**256 - 2**224 <= prime <= 2**256 +// The function has constant control flow but not constant memory access flow +// with regard to prime and x +static void bn_inverse_fast(bignum256* x, const bignum256* prime) { + // Custom constant time version of "The Almost Montgomery Inverse" from the + // section 3 of "Constant Time Modular Inversion" by Joppe W. Bos + // See http://www.joppebos.com/files/CTInversion.pdf + + /* + u = prime + v = x % prime + s = 1 + r = 0 + + k = 0 + while v != 1: + k += 1 + if is_even(u): # b1 + u = u // 2 + s = 2 * s + elif is_even(v): # b2 + v = v // 2 + r = 2 * r + elif v < u: # b3 + u = (u - v) // 2 + r = r + s + s = 2 * s + else: # b4 + v = (v - u) // 2 + s = r + s + r = 2 * r + + s = (s / 2**k) % prime + return s + */ + + bn_fast_mod(x, prime); + bn_mod(x, prime); + + bignum256 u = {0}, v = {0}, r = {0}, s = {0}; + bn_copy(prime, &u); + bn_copy(x, &v); + bn_one(&s); + bn_zero(&r); + + bignum256 zero = {0}; + bn_zero(&zero); + + int k = 0; + + int finished = 0, u_even = 0, v_even = 0, v_less_u = 0, b1 = 0, b2 = 0, b3 = 0, b4 = 0; + finished = 0; + + for(int i = 0; i < 2 * BN_LIMBS * BN_BITS_PER_LIMB; i++) { + finished = finished | -bn_is_one(&v); + u_even = -bn_is_even(&u); + v_even = -bn_is_even(&v); + v_less_u = -bn_is_less(&v, &u); + + b1 = ~finished & u_even; + b2 = ~finished & ~b1 & v_even; + b3 = ~finished & ~b1 & ~b2 & v_less_u; + b4 = ~finished & ~b1 & ~b2 & ~b3; + +// The ternary operator for pointers with constant control flow +// BN_INVERSE_FAST_TERNARY(c, t, f) = t if c else f +// Very nasty hack, sorry for that +#define BN_INVERSE_FAST_TERNARY(c, t, f) \ + ((void*)(((c) & (uintptr_t)(t)) | (~(c) & (uintptr_t)(f)))) + + bn_subtract( + BN_INVERSE_FAST_TERNARY(b3, &u, &v), + BN_INVERSE_FAST_TERNARY(b3 | b4, BN_INVERSE_FAST_TERNARY(b3, &v, &u), &zero), + BN_INVERSE_FAST_TERNARY(b3, &u, &v)); + + bn_add( + BN_INVERSE_FAST_TERNARY(b3, &r, &s), + BN_INVERSE_FAST_TERNARY(b3 | b4, BN_INVERSE_FAST_TERNARY(b3, &s, &r), &zero)); + bn_rshift(BN_INVERSE_FAST_TERNARY(b1 | b3, &u, &v)); + bn_lshift(BN_INVERSE_FAST_TERNARY(b1 | b3, &s, &r)); + + k = k - ~finished; + } + + // s = s / 2**(k // BITS_PER_LIMB * BITS_PER_LIMB) + for(int i = 0; i < 2 * BN_LIMBS; i++) { + // s = s / 2**BITS_PER_LIMB % prime if i < k // BITS_PER_LIMB else s + bn_copy(&s, &r); + bn_divide_base(&r, prime); + bn_cmov(&s, i < k / BN_BITS_PER_LIMB, &r, &s); + } + + // s = s / 2**(k % BITS_PER_LIMB) + for(int i = 0; i < BN_BITS_PER_LIMB; i++) { + // s = s / 2 % prime if i < k % BITS_PER_LIMB else s + bn_copy(&s, &r); + bn_mult_half(&r, prime); + bn_cmov(&s, i < k % BN_BITS_PER_LIMB, &r, &s); + } + + bn_cmov(x, bn_is_zero(x), x, &s); + + memzero(&u, sizeof(u)); + memzero(&v, sizeof(v)); + memzero(&r, sizeof(s)); + memzero(&s, sizeof(s)); +} +#endif + +#if false +// x = 1/x % prime if x != 0 else 0 +// Assumes x is is_normalized +// Assumes GCD(x, prime) = 1 +// Guarantees x is normalized and fully reduced modulo prime +// Assumes prime is odd, normalized, 2**256 - 2**224 <= prime <= 2**256 +static void bn_inverse_fast(bignum256 *x, const bignum256 *prime) { + // Custom constant time version of "The Almost Montgomery Inverse" from the + // section 3 of "Constant Time Modular Inversion" by Joppe W. Bos + // See http://www.joppebos.com/files/CTInversion.pdf + + /* + u = prime + v = x % prime + s = 1 + r = 0 + + k = 0 + while v != 1: + k += 1 + if is_even(u): # b1 + u = u // 2 + s = 2 * s + elif is_even(v): # b2 + v = v // 2 + r = 2 * r + elif v < u: # b3 + u = (u - v) // 2 + r = r + s + s = 2 * s + else: # b4 + v = (v - u) // 2 + s = r + s + r = 2 * r + + s = (s / 2**k) % prime + return s + */ + + bn_fast_mod(x, prime); + bn_mod(x, prime); + + bignum256 u = {0}, v = {0}, r = {0}, s = {0}; + bn_copy(prime, &u); + bn_copy(x, &v); + bn_one(&s); + bn_zero(&r); + + bignum256 zero = {0}; + bn_zero(&zero); + + int k = 0; + + uint32_t finished = 0, u_even = 0, v_even = 0, v_less_u = 0, b1 = 0, b2 = 0, + b3 = 0, b4 = 0; + finished = 0; + + bignum256 u_half = {0}, v_half = {0}, u_minus_v_half = {0}, v_minus_u_half = {0}, r_plus_s = {0}, r_twice = {0}, s_twice = {0}; + for (int i = 0; i < 2 * BN_LIMBS * BN_BITS_PER_LIMB; i++) { + finished = finished | bn_is_one(&v); + u_even = bn_is_even(&u); + v_even = bn_is_even(&v); + v_less_u = bn_is_less(&v, &u); + + b1 = (finished ^ 1) & u_even; + b2 = (finished ^ 1) & (b1 ^ 1) & v_even; + b3 = (finished ^ 1) & (b1 ^ 1) & (b2 ^ 1) & v_less_u; + b4 = (finished ^ 1) & (b1 ^ 1) & (b2 ^ 1) & (b3 ^ 1); + + // u_half = u // 2 + bn_copy(&u, &u_half); + bn_rshift(&u_half); + + // v_half = v // 2 + bn_copy(&v, &v_half); + bn_rshift(&v_half); + + // u_minus_v_half = (u - v) // 2 + bn_subtract(&u, &v, &u_minus_v_half); + bn_rshift(&u_minus_v_half); + + // v_minus_u_half = (v - u) // 2 + bn_subtract(&v, &u, &v_minus_u_half); + bn_rshift(&v_minus_u_half); + + // r_plus_s = r + s + bn_copy(&r, &r_plus_s); + bn_add(&r_plus_s, &s); + + // r_twice = 2 * r + bn_copy(&r, &r_twice); + bn_lshift(&r_twice); + + // s_twice = 2 * s + bn_copy(&s, &s_twice); + bn_lshift(&s_twice); + + bn_cmov(&u, b1, &u_half, &u); + bn_cmov(&u, b3, &u_minus_v_half, &u); + + bn_cmov(&v, b2, &v_half, &v); + bn_cmov(&v, b4, &v_minus_u_half, &v); + + bn_cmov(&r, b2 | b4, &r_twice, &r); + bn_cmov(&r, b3, &r_plus_s, &r); + + bn_cmov(&s, b1 | b3, &s_twice, &s); + bn_cmov(&s, b4, &r_plus_s, &s); + + k = k + (finished ^ 1); + } + + // s = s / 2**(k // BITS_PER_LIMB * BITS_PER_LIMB) + for (int i = 0; i < 2 * BN_LIMBS; i++) { + // s = s / 2**BITS_PER_LIMB % prime if i < k // BITS_PER_LIMB else s + bn_copy(&s, &r); + bn_divide_base(&r, prime); + bn_cmov(&s, i < k / BITS_PER_LIMB, &r, &s); + } + + // s = s / 2**(k % BITS_PER_LIMB) + for (int i = 0; i < BN_BITS_PER_LIMB; i++) { + // s = s / 2 % prime if i < k % BITS_PER_LIMB else s + bn_copy(&s, &r); + bn_mult_half(&r, prime); + bn_cmov(&s, i < k % BN_BITS_PER_LIMB, &r, &s); + } + + bn_cmov(x, bn_is_zero(x), x, &s); + + memzero(&u, sizeof(u)); + memzero(&v, sizeof(v)); + memzero(&r, sizeof(r)); + memzero(&s, sizeof(s)); + memzero(&u_half, sizeof(u_half)); + memzero(&v_half, sizeof(v_half)); + memzero(&u_minus_v_half, sizeof(u_minus_v_half)); + memzero(&v_minus_u_half, sizeof(v_minus_u_half)); + memzero(&r_twice, sizeof(r_twice)); + memzero(&s_twice, sizeof(s_twice)); + memzero(&r_plus_s, sizeof(r_plus_s)); +} +#endif + +// Normalizes x +// Assumes x < 2**261 == 2**(LIMBS * BITS_PER_LIMB) +// Guarantees x is normalized +void bn_normalize(bignum256* x) { + uint32_t acc = 0; + + for(int i = 0; i < BN_LIMBS; i++) { + acc += x->val[i]; + // acc doesn't overflow 32 bits + // Proof: + // acc + x[i] + // <= (2**(32 - BITS_PER_LIMB) - 1) + (2**BITS_PER_LIMB - 1) + // == 7 + 2**29 - 1 < 2**32 + + x->val[i] = acc & BN_LIMB_MASK; + acc >>= (BN_BITS_PER_LIMB); + // acc <= 7 == 2**(32 - BITS_PER_LIMB) - 1 + } +} + +// x = x + y +// Assumes x, y are normalized, x + y < 2**(LIMBS*BITS_PER_LIMB) == 2**261 +// Guarantees x is normalized +// Works properly even if &x == &y +void bn_add(bignum256* x, const bignum256* y) { + uint32_t acc = 0; + for(int i = 0; i < BN_LIMBS; i++) { + acc += x->val[i] + y->val[i]; + // acc doesn't overflow 32 bits + // Proof: + // acc + x[i] + y[i] + // <= (2**(32 - BITS_PER_LIMB) - 1) + 2 * (2**BITS_PER_LIMB - 1) + // == (2**(32 - BITS_PER_LIMB) - 1) + 2**(BITS_PER_LIMB + 1) - 2 + // == 7 + 2**30 - 2 < 2**32 + + x->val[i] = acc & BN_LIMB_MASK; + acc >>= BN_BITS_PER_LIMB; + // acc <= 7 == 2**(32 - BITS_PER_LIMB) - 1 + + // acc == x[:i + 1] + y[:i + 1] >> BITS_PER_LIMB * (i + 1) + } + + // assert(acc == 0); // assert x + y < 2**261 + // acc == 0 + // Proof: + // acc == x[:LIMBS] + y[:LIMBS] >> LIMBS * BITS_PER_LIMB + // == x + y >> LIMBS * BITS_PER_LIMB + // <= 2**(LIMBS * BITS_PER_LIMB) - 1 >> LIMBS * BITS_PER_LIMB == 0 +} + +// x = x + y % prime +// Assumes x, y are normalized +// Guarantees x is normalized and partly reduced modulo prime +// Assumes prime is normalized and 2^256 - 2^224 <= prime <= 2^256 +void bn_addmod(bignum256* x, const bignum256* y, const bignum256* prime) { + for(int i = 0; i < BN_LIMBS; i++) { + x->val[i] += y->val[i]; + // x[i] doesn't overflow 32 bits + // Proof: + // x[i] + y[i] + // <= 2 * (2**BITS_PER_LIMB - 1) + // == 2**30 - 2 < 2**32 + } + + bn_fast_mod(x, prime); +} + +// x = x + y +// Assumes x is normalized +// Assumes y <= 2**32 - 2**29 == 2**32 - 2**BITS_PER_LIMB and +// x + y < 2**261 == 2**(LIMBS * BITS_PER_LIMB) +// Guarantees x is normalized +void bn_addi(bignum256* x, uint32_t y) { + // assert(y <= 3758096384); // assert y <= 2**32 - 2**29 + uint32_t acc = y; + + for(int i = 0; i < BN_LIMBS; i++) { + acc += x->val[i]; + // acc doesn't overflow 32 bits + // Proof: + // if i == 0: + // acc + x[i] == y + x[0] + // <= (2**32 - 2**BITS_PER_LIMB) + (2**BITS_PER_LIMB - 1) + // == 2**32 - 1 < 2**32 + // else: + // acc + x[i] + // <= (2**(32 - BITS_PER_LIMB) - 1) + (2**BITS_PER_LIMB - 1) + // == 7 + 2**29 - 1 < 2**32 + + x->val[i] = acc & BN_LIMB_MASK; + acc >>= (BN_BITS_PER_LIMB); + // acc <= 7 == 2**(32 - BITS_PER_LIMB) - 1 + + // acc == x[:i + 1] + y >> BITS_PER_LIMB * (i + 1) + } + + // assert(acc == 0); // assert x + y < 2**261 + // acc == 0 + // Proof: + // acc == x[:LIMBS] + y << LIMBS * BITS_PER_LIMB + // == x + y << LIMBS * BITS_PER_LIMB + // <= 2**(LIMBS + BITS_PER_LIMB) - 1 << LIMBS * BITS_PER_LIMB + // == 0 +} + +// x = x - y % prime +// Explicitly x = x + prime - y +// Assumes x, y are normalized +// Assumes y < prime[0], x + prime - y < 2**261 == 2**(LIMBS * BITS_PER_LIMB) +// Guarantees x is normalized +// If x is fully reduced modulo prime, +// guarantess x will be partly reduced modulo prime +// Assumes prime is nonzero and normalized +void bn_subi(bignum256* x, uint32_t y, const bignum256* prime) { + assert(y < prime->val[0]); + + // x = x + prime - y + + uint32_t acc = -y; + for(int i = 0; i < BN_LIMBS; i++) { + acc += x->val[i] + prime->val[i]; + // acc neither overflows 32 bits nor underflows 0 + // Proof: + // acc + x[i] + prime[i] + // <= (2**(32 - BITS_PER_LIMB) - 1) + 2 * (2**BITS_PER_LIMB - 1) + // <= 7 + 2**30 - 2 < 2**32 + // acc + x[i] + prime[i] + // >= -y + prime[0] >= 0 + + x->val[i] = acc & BN_LIMB_MASK; + acc >>= BN_BITS_PER_LIMB; + // acc <= 7 == 2**(32 - BITS_PER_LIMB) - 1 + + // acc == x[:i + 1] + prime[:i + 1] - y >> BITS_PER_LIMB * (i + 1) + } + + // assert(acc == 0); // assert x + prime - y < 2**261 + // acc == 0 + // Proof: + // acc == x[:LIMBS] + prime[:LIMBS] - y >> BITS_PER_LIMB * LIMBS + // == x + prime - y >> BITS_PER_LIMB * LIMBS + // <= 2**(LIMBS * BITS_PER_LIMB) - 1 >> BITS_PER_LIMB * LIMBS == 0 +} + +// res = x - y % prime +// Explicitly res = x + (2 * prime - y) +// Assumes x, y are normalized, y is partly reduced +// Assumes x + 2 * prime - y < 2**261 == 2**(BITS_PER_LIMB * LIMBS) +// Guarantees res is normalized +// Assumes prime is nonzero and normalized +void bn_subtractmod(const bignum256* x, const bignum256* y, bignum256* res, const bignum256* prime) { + // res = x + (2 * prime - y) + + uint32_t acc = 1; + + for(int i = 0; i < BN_LIMBS; i++) { + acc += (BN_BASE - 1) + x->val[i] + 2 * prime->val[i] - y->val[i]; + // acc neither overflows 32 bits nor underflows 0 + // Proof: + // acc + (BASE - 1) + x[i] + 2 * prime[i] - y[i] + // >= (BASE - 1) - y[i] + // == (2**BITS_PER_LIMB - 1) - (2**BITS_PER_LIMB - 1) == 0 + // acc + (BASE - 1) + x[i] + 2 * prime[i] - y[i] + // <= acc + (BASE - 1) + x[i] + 2 * prime[i] + // <= (2**(32 - BITS_PER_LIMB) - 1) + (2**BITS_PER_LIMB - 1) + + // (2**BITS_PER_LIMB - 1) + 2 * (2**BITS_PER_LIMB - 1) + // <= (2**(32 - BITS_PER_LIMB) - 1) + 4 * (2**BITS_PER_LIMB - 1) + // == 7 + 4 * 2**29 - 4 == 2**31 + 3 < 2**32 + + res->val[i] = acc & (BN_BASE - 1); + acc >>= BN_BITS_PER_LIMB; + // acc <= 7 == 2**(32 - BITS_PER_LIMB) - 1 + + // acc == 2**(BITS_PER_LIMB * (i + 1)) + x[:i+1] - y[:i+1] + 2*prime[:i+1] + // >> BITS_PER_LIMB * (i+1) + } + + // assert(acc == 1); // assert x + 2 * prime - y < 2**261 + + // clang-format off + // acc == 1 + // Proof: + // acc == 2**(BITS_PER_LIMB * LIMBS) + x[:LIMBS] - y[:LIMBS] + 2 * prime[:LIMBS] >> BITS_PER_LIMB * LIMBS + // == 2**(BITS_PER_LIMB * LIMBS) + x - y + 2 * prime >> BITS_PER_LIMB * LIMBS + // == 2**(BITS_PER_LIMB * LIMBS) + x + (2 * prime - y) >> BITS_PER_LIMB * LIMBS + // <= 2**(BITS_PER_LIMB * LIMBS) + 2**(BITS_PER_LIMB * LIMBS) - 1 >> BITS_PER_LIMB * LIMBS + // <= 2 * 2**(BITS_PER_LIMB * LIMBS) - 1 >> BITS_PER_LIMB * LIMBS + // == 1 + + // acc == 2**(BITS_PER_LIMB * LIMBS) + x[:LIMBS] - y[:LIMBS] + 2 * prime[:LIMBS] >> BITS_PER_LIMB * LIMBS + // == 2**(BITS_PER_LIMB * LIMBS) + x - y + 2 * prime >> BITS_PER_LIMB * LIMBS + // == 2**(BITS_PER_LIMB * LIMBS) + x + (2 * prime - y) >> BITS_PER_LIMB * LIMBS + // >= 2**(BITS_PER_LIMB * LIMBS) + 0 + 1 >> BITS_PER_LIMB * LIMBS + // == 1 + // clang-format on +} + +// res = x - y +// Assumes x, y are normalized and x >= y +// Guarantees res is normalized +// Works properly even if &x == &y or &x == &res or &y == &res or +// &x == &y == &res +void bn_subtract(const bignum256* x, const bignum256* y, bignum256* res) { + uint32_t acc = 1; + for(int i = 0; i < BN_LIMBS; i++) { + acc += (BN_BASE - 1) + x->val[i] - y->val[i]; + // acc neither overflows 32 bits nor underflows 0 + // Proof: + // acc + (BASE - 1) + x[i] - y[i] + // >= (BASE - 1) - y == (2**BITS_PER_LIMB - 1) - (2**BITS_PER_LIMB - 1) + // == 0 + // acc + (BASE - 1) + x[i] - y[i] + // <= acc + (BASE - 1) + x[i] + // <= (2**(32 - BITS_PER_LIMB) - 1) + (2**BITS_PER_LIMB - 1) + + // (2**BITS_PER_LIMB - 1) + // == 7 + 2 * 2**29 < 2 **32 + + res->val[i] = acc & BN_LIMB_MASK; + acc >>= BN_BITS_PER_LIMB; + // acc <= 7 == 2**(32 - BITS_PER_LIMB) - 1 + + // acc == 2**(BITS_PER_LIMB * (i + 1)) + x[:i + 1] - y[:i + 1] + // >> BITS_PER_LIMB * (i + 1) + } + + // assert(acc == 1); // assert x >= y + + // clang-format off + // acc == 1 + // Proof: + // acc == 2**(BITS_PER_LIMB * LIMBS) + x[:LIMBS] - y[:LIMBS] >> BITS_PER_LIMB * LIMBS + // == 2**(BITS_PER_LIMB * LIMBS) + x - y >> BITS_PER_LIMB * LIMBS + // == 2**(BITS_PER_LIMB * LIMBS) + x >> BITS_PER_LIMB * LIMBS + // <= 2**(BITS_PER_LIMB * LIMBS) + 2**(BITS_PER_LIMB * LIMBS) - 1 >> BITS_PER_LIMB * LIMBS + // <= 2 * 2**(BITS_PER_LIMB * LIMBS) - 1 >> BITS_PER_LIMB * LIMBS + // == 1 + + // acc == 2**(BITS_PER_LIMB * LIMBS) + x[:LIMBS] - y[:LIMBS] >> BITS_PER_LIMB * LIMBS + // == 2**(BITS_PER_LIMB * LIMBS) + x - y >> BITS_PER_LIMB * LIMBS + // >= 2**(BITS_PER_LIMB * LIMBS) >> BITS_PER_LIMB * LIMBS + // == 1 +} + +// q = x // d, r = x % d +// Assumes x is normalized, 1 <= d <= 61304 +// Guarantees q is normalized +void bn_long_division(bignum256 *x, uint32_t d, bignum256 *q, uint32_t *r) { + assert(1 <= d && d < 61304); + + uint32_t acc = 0; + + *r = x->val[BN_LIMBS - 1] % d; + q->val[BN_LIMBS - 1] = x->val[BN_LIMBS - 1] / d; + + for (int i = BN_LIMBS - 2; i >= 0; i--) { + acc = *r * (BN_BASE % d) + x->val[i]; + // acc doesn't overflow 32 bits + // Proof: + // r * (BASE % d) + x[i] + // <= (d - 1) * (d - 1) + (2**BITS_PER_LIMB - 1) + // == d**2 - 2*d + 2**BITS_PER_LIMB + // == 61304**2 - 2 * 61304 + 2**29 + // == 3758057808 + 2**29 < 2**32 + + q->val[i] = *r * (BN_BASE / d) + (acc / d); + // q[i] doesn't overflow 32 bits + // Proof: + // r * (BASE // d) + (acc // d) + // <= (d - 1) * (2**BITS_PER_LIMB / d) + + // ((d**2 - 2*d + 2**BITS_PER_LIMB) / d) + // <= (d - 1) * (2**BITS_PER_LIMB / d) + (d - 2 + 2**BITS_PER_LIMB / d) + // == (d - 1 + 1) * (2**BITS_PER_LIMB / d) + d - 2 + // == 2**BITS_PER_LIMB + d - 2 <= 2**29 + 61304 < 2**32 + + // q[i] == (r * BASE + x[i]) // d + // Proof: + // q[i] == r * (BASE // d) + (acc // d) + // == r * (BASE // d) + (r * (BASE % d) + x[i]) // d + // == (r * d * (BASE // d) + r * (BASE % d) + x[i]) // d + // == (r * (d * (BASE // d) + (BASE % d)) + x[i]) // d + // == (r * BASE + x[i]) // d + + // q[i] < 2**BITS_PER_LIMB + // Proof: + // q[i] == (r * BASE + x[i]) // d + // <= ((d - 1) * 2**BITS_PER_LIMB + (2**BITS_PER_LIMB - 1)) / d + // == (d * 2**BITS_PER_LIMB - 1) / d == 2**BITS_PER_LIMB - 1 / d + // < 2**BITS_PER_LIMB + + *r = acc % d; + // r == (r * BASE + x[i]) % d + // Proof: + // r == acc % d == (r * (BASE % d) + x[i]) % d + // == (r * BASE + x[i]) % d + + // x[:i] == q[:i] * d + r + } +} + +// x = x // 58, r = x % 58 +// Assumes x is normalized +// Guarantees x is normalized +void bn_divmod58(bignum256 *x, uint32_t *r) { bn_long_division(x, 58, x, r); } + +// x = x // 1000, r = x % 1000 +// Assumes x is normalized +// Guarantees x is normalized +void bn_divmod1000(bignum256 *x, uint32_t *r) { + bn_long_division(x, 1000, x, r); +} + +// x = x // 10, r = x % 10 +// Assumes x is normalized +// Guarantees x is normalized +void bn_divmod10(bignum256 *x, uint32_t *r) { bn_long_division(x, 10, x, r); } + +// Formats amount +// Assumes amount is normalized +// Assumes prefix and suffix are null-terminated strings +// Assumes output is an array of length output_length +// The function doesn't have neither constant control flow nor constant memory +// access flow with regard to any its argument +size_t bn_format(const bignum256 *amount, const char *prefix, const char *suffix, unsigned int decimals, int exponent, bool trailing, char thousands, char *output, size_t output_length) { + +/* + Python prototype of the function: + + def format(amount, prefix, suffix, decimals, exponent, trailing, thousands): + if exponent >= 0: + amount *= 10**exponent + else: + amount //= 10 ** (-exponent) + + d = pow(10, decimals) + + integer_part = amount // d + integer_str = f"{integer_part:,}".replace(",", thousands or "") + + if decimals: + decimal_part = amount % d + decimal_str = f".{decimal_part:0{decimals}d}" + if not trailing: + decimal_str = decimal_str.rstrip("0").rstrip(".") + else: + decimal_str = "" + + return prefix + integer_str + decimal_str + suffix +*/ + +// Auxiliary macro for bn_format +// If enough space adds one character to output starting from the end +#define BN_FORMAT_ADD_OUTPUT_CHAR(c) \ + { \ + --position; \ + if (output <= position && position < output + output_length) { \ + *position = (c); \ + } else { \ + memset(output, '\0', output_length); \ + return 0; \ + } \ + } + + bignum256 temp = {0}; + bn_copy(amount, &temp); + uint32_t digit = 0; + + char *position = output + output_length; + + // Add string ending character + BN_FORMAT_ADD_OUTPUT_CHAR('\0'); + + // Add suffix + size_t suffix_length = suffix ? strlen(suffix) : 0; + for (int i = suffix_length - 1; i >= 0; --i) + BN_FORMAT_ADD_OUTPUT_CHAR(suffix[i]) + + // amount //= 10**exponent + for (; exponent < 0; ++exponent) { + // if temp == 0, there is no need to divide it by 10 anymore + if (bn_is_zero(&temp)) { + exponent = 0; + break; + } + bn_divmod10(&temp, &digit); + } + + // exponent >= 0 && decimals >= 0 + + bool fractional_part = false; // is fractional-part of amount present + + { // Add fractional-part digits of amount + // Add trailing zeroes + unsigned int trailing_zeros = decimals < (unsigned int) exponent ? decimals : (unsigned int) exponent; + // When casting a negative int to unsigned int, UINT_MAX is added to the int before + // Since exponent >= 0, the value remains unchanged + decimals -= trailing_zeros; + exponent -= trailing_zeros; + + if (trailing && trailing_zeros) { + fractional_part = true; + for (; trailing_zeros > 0; --trailing_zeros) + BN_FORMAT_ADD_OUTPUT_CHAR('0') + } + + // exponent == 0 || decimals == 0 + + // Add significant digits and leading zeroes + for (; decimals > 0; --decimals) { + bn_divmod10(&temp, &digit); + + if (fractional_part || digit || trailing) { + fractional_part = true; + BN_FORMAT_ADD_OUTPUT_CHAR('0' + digit) + } + else if (bn_is_zero(&temp)) { + // We break since the remaining digits are zeroes and fractional_part == trailing == false + decimals = 0; + break; + } + } + // decimals == 0 + } + + if (fractional_part) { + BN_FORMAT_ADD_OUTPUT_CHAR('.') + } + + { // Add integer-part digits of amount + // Add trailing zeroes + int digits = 0; + if (!bn_is_zero(&temp)) { + for (; exponent > 0; --exponent) { + ++digits; + BN_FORMAT_ADD_OUTPUT_CHAR('0') + if (thousands != 0 && digits % 3 == 0) { + BN_FORMAT_ADD_OUTPUT_CHAR(thousands) + } + } + } + // decimals == 0 && exponent == 0 + + // Add significant digits + bool is_zero = false; + do { + ++digits; + bn_divmod10(&temp, &digit); + is_zero = bn_is_zero(&temp); + BN_FORMAT_ADD_OUTPUT_CHAR('0' + digit) + if (thousands != 0 && !is_zero && digits % 3 == 0) { + BN_FORMAT_ADD_OUTPUT_CHAR(thousands) + } + } while (!is_zero); + } + + // Add prefix + size_t prefix_length = prefix ? strlen(prefix) : 0; + for (int i = prefix_length - 1; i >= 0; --i) + BN_FORMAT_ADD_OUTPUT_CHAR(prefix[i]) + + // Move formatted amount to the start of output + int length = output - position + output_length; + memmove(output, position, length); + return length - 1; +} + +#if USE_BN_PRINT +// Prints x in hexadecimal +// Assumes x is normalized and x < 2**256 +void bn_print(const bignum256 *x) { + printf("%06x", x->val[8]); + printf("%08x", ((x->val[7] << 3) | (x->val[6] >> 26))); + printf("%07x", ((x->val[6] << 2) | (x->val[5] >> 27)) & 0x0FFFFFFF); + printf("%07x", ((x->val[5] << 1) | (x->val[4] >> 28)) & 0x0FFFFFFF); + printf("%07x", x->val[4] & 0x0FFFFFFF); + printf("%08x", ((x->val[3] << 3) | (x->val[2] >> 26))); + printf("%07x", ((x->val[2] << 2) | (x->val[1] >> 27)) & 0x0FFFFFFF); + printf("%07x", ((x->val[1] << 1) | (x->val[0] >> 28)) & 0x0FFFFFFF); + printf("%07x", x->val[0] & 0x0FFFFFFF); +} + +// Prints comma separated list of limbs of x +void bn_print_raw(const bignum256 *x) { + for (int i = 0; i < BN_LIMBS - 1; i++) { + printf("0x%08x, ", x->val[i]); + } + printf("0x%08x", x->val[BN_LIMBS - 1]); +} +#endif + +#if USE_INVERSE_FAST +void bn_inverse(bignum256 *x, const bignum256 *prime) { + bn_inverse_fast(x, prime); +} +#else +void bn_inverse(bignum256 *x, const bignum256 *prime) { + bn_inverse_slow(x, prime); +} +#endif diff --git a/applications/external/flipbip/lib/crypto/bignum.h b/applications/external/flipbip/lib/crypto/bignum.h new file mode 100644 index 0000000000..858613401d --- /dev/null +++ b/applications/external/flipbip/lib/crypto/bignum.h @@ -0,0 +1,195 @@ +/** + * Copyright (c) 2013-2014 Tomas Dzetkulic + * Copyright (c) 2013-2014 Pavol Rusnak + * Copyright (c) 2016 Alex Beregszaszi + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __BIGNUM_H__ +#define __BIGNUM_H__ + +#include +#include +#include + +#include "options.h" + +#define BN_LIMBS 9 +#define BN_BITS_PER_LIMB 29 +#define BN_BASE (1u << BN_BITS_PER_LIMB) +#define BN_LIMB_MASK ((1u << BN_BITS_PER_LIMB) - 1) +#define BN_EXTRA_BITS (32 - BN_BITS_PER_LIMB) +#define BN_BITS_LAST_LIMB (256 - (BN_LIMBS - 1) * BN_BITS_PER_LIMB) + +// Represents the number sum([val[i] * 2**(29*i) for i in range(9)) +typedef struct { + uint32_t val[BN_LIMBS]; +} bignum256; + +static inline uint32_t read_be(const uint8_t* data) { + return (((uint32_t)data[0]) << 24) | (((uint32_t)data[1]) << 16) | (((uint32_t)data[2]) << 8) | + (((uint32_t)data[3])); +} + +static inline void write_be(uint8_t* data, uint32_t x) { + data[0] = x >> 24; + data[1] = x >> 16; + data[2] = x >> 8; + data[3] = x; +} + +static inline uint32_t read_le(const uint8_t* data) { + return (((uint32_t)data[3]) << 24) | (((uint32_t)data[2]) << 16) | (((uint32_t)data[1]) << 8) | + (((uint32_t)data[0])); +} + +static inline void write_le(uint8_t* data, uint32_t x) { + data[3] = x >> 24; + data[2] = x >> 16; + data[1] = x >> 8; + data[0] = x; +} + +void bn_read_be(const uint8_t* in_number, bignum256* out_number); +void bn_write_be(const bignum256* in_number, uint8_t* out_number); +void bn_read_le(const uint8_t* in_number, bignum256* out_number); +void bn_write_le(const bignum256* in_number, uint8_t* out_number); +void bn_read_uint32(uint32_t in_number, bignum256* out_number); +void bn_read_uint64(uint64_t in_number, bignum256* out_number); +int bn_bitcount(const bignum256* x); +unsigned int bn_digitcount(const bignum256* x); +void bn_zero(bignum256* x); +void bn_one(bignum256* x); +int bn_is_zero(const bignum256* x); +int bn_is_one(const bignum256* x); +int bn_is_less(const bignum256* x, const bignum256* y); +int bn_is_equal(const bignum256* x, const bignum256* y); +void bn_cmov( + bignum256* res, + volatile uint32_t cond, + const bignum256* truecase, + const bignum256* falsecase); +void bn_cnegate(volatile uint32_t cond, bignum256* x, const bignum256* prime); +void bn_lshift(bignum256* x); +void bn_rshift(bignum256* x); +void bn_setbit(bignum256* x, uint16_t i); +void bn_clearbit(bignum256* x, uint16_t i); +uint32_t bn_testbit(const bignum256* x, uint16_t i); +void bn_xor(bignum256* res, const bignum256* x, const bignum256* y); +void bn_mult_half(bignum256* x, const bignum256* prime); +void bn_mult_k(bignum256* x, uint8_t k, const bignum256* prime); +void bn_mod(bignum256* x, const bignum256* prime); +void bn_multiply(const bignum256* k, bignum256* x, const bignum256* prime); +void bn_fast_mod(bignum256* x, const bignum256* prime); +void bn_power_mod(const bignum256* x, const bignum256* e, const bignum256* prime, bignum256* res); +void bn_sqrt(bignum256* x, const bignum256* prime); +uint32_t inverse_mod_power_two(uint32_t a, uint32_t n); +void bn_divide_base(bignum256* x, const bignum256* prime); +void bn_normalize(bignum256* x); +void bn_add(bignum256* x, const bignum256* y); +void bn_addmod(bignum256* x, const bignum256* y, const bignum256* prime); +void bn_addi(bignum256* x, uint32_t y); +void bn_subi(bignum256* x, uint32_t y, const bignum256* prime); +void bn_subtractmod(const bignum256* x, const bignum256* y, bignum256* res, const bignum256* prime); +void bn_subtract(const bignum256* x, const bignum256* y, bignum256* res); +void bn_long_division(bignum256* x, uint32_t d, bignum256* q, uint32_t* r); +void bn_divmod58(bignum256* x, uint32_t* r); +void bn_divmod1000(bignum256* x, uint32_t* r); +void bn_inverse(bignum256* x, const bignum256* prime); +size_t bn_format( + const bignum256* amount, + const char* prefix, + const char* suffix, + unsigned int decimals, + int exponent, + bool trailing, + char thousands, + char* output, + size_t output_length); + +// Returns (uint32_t) in_number +// Assumes in_number < 2**32 +// Assumes in_number is normalized +static inline uint32_t bn_write_uint32(const bignum256* in_number) { + return in_number->val[0] | (in_number->val[1] << BN_BITS_PER_LIMB); +} + +// Returns (uint64_t) in_number +// Assumes in_number < 2**64 +// Assumes in_number is normalized +static inline uint64_t bn_write_uint64(const bignum256* in_number) { + uint64_t acc; + acc = in_number->val[2]; + acc <<= BN_BITS_PER_LIMB; + acc |= in_number->val[1]; + acc <<= BN_BITS_PER_LIMB; + acc |= in_number->val[0]; + return acc; +} + +// y = x +static inline void bn_copy(const bignum256* x, bignum256* y) { + *y = *x; +} + +// Returns x % 2 == 0 +static inline int bn_is_even(const bignum256* x) { + return (x->val[0] & 1) == 0; +} + +// Returns x % 2 == 0 +static inline int bn_is_odd(const bignum256* x) { + return (x->val[0] & 1) == 1; +} + +static inline size_t bn_format_uint64( + uint64_t amount, + const char* prefix, + const char* suffix, + unsigned int decimals, + int exponent, + bool trailing, + char thousands, + char* output, + size_t output_length) { + bignum256 bn_amount; + bn_read_uint64(amount, &bn_amount); + + return bn_format( + &bn_amount, prefix, suffix, decimals, exponent, trailing, thousands, output, output_length); +} + +static inline size_t bn_format_amount( + uint64_t amount, + const char* prefix, + const char* suffix, + unsigned int decimals, + char* output, + size_t output_length) { + return bn_format_uint64( + amount, prefix, suffix, decimals, 0, false, ',', output, output_length); +} + +#if USE_BN_PRINT +void bn_print(const bignum256* x); +void bn_print_raw(const bignum256* x); +#endif + +#endif diff --git a/applications/external/flipbip/lib/crypto/bip32.c b/applications/external/flipbip/lib/crypto/bip32.c new file mode 100644 index 0000000000..09f00d60e0 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/bip32.c @@ -0,0 +1,885 @@ +/** + * Copyright (c) 2013-2016 Tomas Dzetkulic + * Copyright (c) 2013-2016 Pavol Rusnak + * Copyright (c) 2015-2016 Jochen Hoenicke + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include + +#include "address.h" +#include "aes/aes.h" +#include "base58.h" +#include "bignum.h" +#include "bip32.h" +#include "cardano.h" +#include "curves.h" +#include "ecdsa.h" +#include "ed25519_donna/ed25519_sha3.h" +#include "ed25519_donna/ed25519.h" +#include "hmac.h" +#include "nist256p1.h" +#include "secp256k1.h" +#include "sha2.h" +#include "sha3.h" +#if USE_KECCAK +#include "ed25519_donna/ed25519_keccak.h" +#endif +#if USE_NEM +#include "nem.h" +#endif +#include "memzero.h" + +const curve_info ed25519_info = { + .bip32_name = ED25519_SEED_NAME, + .params = NULL, + .hasher_base58 = HASHER_SHA2D, + .hasher_sign = HASHER_SHA2D, + .hasher_pubkey = HASHER_SHA2_RIPEMD, + .hasher_script = HASHER_SHA2, +}; + +const curve_info ed25519_sha3_info = { + .bip32_name = "ed25519-sha3 seed", + .params = NULL, + .hasher_base58 = HASHER_SHA2D, + .hasher_sign = HASHER_SHA2D, + .hasher_pubkey = HASHER_SHA2_RIPEMD, + .hasher_script = HASHER_SHA2, +}; + +#if USE_KECCAK +const curve_info ed25519_keccak_info = { + .bip32_name = "ed25519-keccak seed", + .params = NULL, + .hasher_base58 = HASHER_SHA2D, + .hasher_sign = HASHER_SHA2D, + .hasher_pubkey = HASHER_SHA2_RIPEMD, + .hasher_script = HASHER_SHA2, +}; +#endif + +const curve_info curve25519_info = { + .bip32_name = "curve25519 seed", + .params = NULL, + .hasher_base58 = HASHER_SHA2D, + .hasher_sign = HASHER_SHA2D, + .hasher_pubkey = HASHER_SHA2_RIPEMD, + .hasher_script = HASHER_SHA2, +}; + +int hdnode_from_xpub( + uint32_t depth, + uint32_t child_num, + const uint8_t* chain_code, + const uint8_t* public_key, + const char* curve, + HDNode* out) { + const curve_info* info = get_curve_by_name(curve); + if(info == 0) { + return 0; + } + if(public_key[0] != 0x02 && public_key[0] != 0x03) { // invalid pubkey + return 0; + } + out->curve = info; + out->depth = depth; + out->child_num = child_num; + memcpy(out->chain_code, chain_code, 32); + memzero(out->private_key, 32); + memzero(out->private_key_extension, 32); + memcpy(out->public_key, public_key, 33); + return 1; +} + +int hdnode_from_xprv( + uint32_t depth, + uint32_t child_num, + const uint8_t* chain_code, + const uint8_t* private_key, + const char* curve, + HDNode* out) { + bool failed = false; + const curve_info* info = get_curve_by_name(curve); + if(info == 0) { + failed = true; + } else if(info->params) { + bignum256 a = {0}; + bn_read_be(private_key, &a); + if(bn_is_zero(&a)) { // == 0 + failed = true; + } else { + if(!bn_is_less(&a, &info->params->order)) { // >= order + failed = true; + } + } + memzero(&a, sizeof(a)); + } + + if(failed) { + return 0; + } + + out->curve = info; + out->depth = depth; + out->child_num = child_num; + memcpy(out->chain_code, chain_code, 32); + memcpy(out->private_key, private_key, 32); + memzero(out->public_key, sizeof(out->public_key)); + memzero(out->private_key_extension, sizeof(out->private_key_extension)); + return 1; +} + +int hdnode_from_seed(const uint8_t* seed, int seed_len, const char* curve, HDNode* out) { + static CONFIDENTIAL uint8_t I[32 + 32]; + memzero(out, sizeof(HDNode)); + out->depth = 0; + out->child_num = 0; + out->curve = get_curve_by_name(curve); + if(out->curve == 0) { + return 0; + } + static CONFIDENTIAL HMAC_SHA512_CTX ctx; + hmac_sha512_Init(&ctx, (const uint8_t*)out->curve->bip32_name, strlen(out->curve->bip32_name)); + hmac_sha512_Update(&ctx, seed, seed_len); + hmac_sha512_Final(&ctx, I); + + if(out->curve->params) { + bignum256 a = {0}; + while(true) { + bn_read_be(I, &a); + if(!bn_is_zero(&a) // != 0 + && bn_is_less(&a, &out->curve->params->order)) { // < order + break; + } + hmac_sha512_Init( + &ctx, (const uint8_t*)out->curve->bip32_name, strlen(out->curve->bip32_name)); + hmac_sha512_Update(&ctx, I, sizeof(I)); + hmac_sha512_Final(&ctx, I); + } + memzero(&a, sizeof(a)); + } + memcpy(out->private_key, I, 32); + memcpy(out->chain_code, I + 32, 32); + memzero(out->public_key, sizeof(out->public_key)); + memzero(I, sizeof(I)); + return 1; +} + +uint32_t hdnode_fingerprint(HDNode* node) { + uint8_t digest[32] = {0}; + uint32_t fingerprint = 0; + + hdnode_fill_public_key(node); + hasher_Raw(node->curve->hasher_pubkey, node->public_key, 33, digest); + fingerprint = ((uint32_t)digest[0] << 24) + (digest[1] << 16) + (digest[2] << 8) + digest[3]; + memzero(digest, sizeof(digest)); + return fingerprint; +} + +int hdnode_private_ckd_bip32(HDNode* inout, uint32_t i) { + static CONFIDENTIAL uint8_t data[1 + 32 + 4]; + static CONFIDENTIAL uint8_t I[32 + 32]; + static CONFIDENTIAL bignum256 a, b; + +#if USE_CARDANO + if(inout->curve == &ed25519_cardano_info) { + return 0; + } +#endif + + if(i & 0x80000000) { // private derivation + data[0] = 0; + memcpy(data + 1, inout->private_key, 32); + } else { // public derivation + if(!inout->curve->params) { + return 0; + } + if(hdnode_fill_public_key(inout) != 0) { + return 0; + } + memcpy(data, inout->public_key, 33); + } + write_be(data + 33, i); + + bn_read_be(inout->private_key, &a); + + static CONFIDENTIAL HMAC_SHA512_CTX ctx; + hmac_sha512_Init(&ctx, inout->chain_code, 32); + hmac_sha512_Update(&ctx, data, sizeof(data)); + hmac_sha512_Final(&ctx, I); + + if(inout->curve->params) { + while(true) { + bool failed = false; + bn_read_be(I, &b); + if(!bn_is_less(&b, &inout->curve->params->order)) { // >= order + failed = true; + } else { + bn_add(&b, &a); + bn_mod(&b, &inout->curve->params->order); + if(bn_is_zero(&b)) { + failed = true; + } + } + + if(!failed) { + bn_write_be(&b, inout->private_key); + break; + } + + data[0] = 1; + memcpy(data + 1, I + 32, 32); + hmac_sha512_Init(&ctx, inout->chain_code, 32); + hmac_sha512_Update(&ctx, data, sizeof(data)); + hmac_sha512_Final(&ctx, I); + } + } else { + memcpy(inout->private_key, I, 32); + } + + memcpy(inout->chain_code, I + 32, 32); + inout->depth++; + inout->child_num = i; + memzero(inout->public_key, sizeof(inout->public_key)); + + // making sure to wipe our memory + memzero(&a, sizeof(a)); + memzero(&b, sizeof(b)); + memzero(I, sizeof(I)); + memzero(data, sizeof(data)); + return 1; +} + +int hdnode_private_ckd(HDNode* inout, uint32_t i) { +#if USE_CARDANO + if(inout->curve == &ed25519_cardano_info) { + return hdnode_private_ckd_cardano(inout, i); + } else +#endif + { + return hdnode_private_ckd_bip32(inout, i); + } +} + +int hdnode_public_ckd_cp( + const ecdsa_curve* curve, + const curve_point* parent, + const uint8_t* parent_chain_code, + uint32_t i, + curve_point* child, + uint8_t* child_chain_code) { + uint8_t data[(1 + 32) + 4] = {0}; + uint8_t I[32 + 32] = {0}; + bignum256 c = {0}; + + if(i & 0x80000000) { // private derivation + return 0; + } + + data[0] = 0x02 | (parent->y.val[0] & 0x01); + bn_write_be(&parent->x, data + 1); + write_be(data + 33, i); + + while(true) { + hmac_sha512(parent_chain_code, 32, data, sizeof(data), I); + bn_read_be(I, &c); + if(bn_is_less(&c, &curve->order)) { // < order + scalar_multiply(curve, &c, child); // b = c * G + point_add(curve, parent, child); // b = a + b + if(!point_is_infinity(child)) { + if(child_chain_code) { + memcpy(child_chain_code, I + 32, 32); + } + + // Wipe all stack data. + memzero(data, sizeof(data)); + memzero(I, sizeof(I)); + memzero(&c, sizeof(c)); + return 1; + } + } + + data[0] = 1; + memcpy(data + 1, I + 32, 32); + } +} + +int hdnode_public_ckd(HDNode* inout, uint32_t i) { + curve_point parent = {0}, child = {0}; + + if(!ecdsa_read_pubkey(inout->curve->params, inout->public_key, &parent)) { + return 0; + } + if(!hdnode_public_ckd_cp( + inout->curve->params, &parent, inout->chain_code, i, &child, inout->chain_code)) { + return 0; + } + memzero(inout->private_key, 32); + inout->depth++; + inout->child_num = i; + inout->public_key[0] = 0x02 | (child.y.val[0] & 0x01); + bn_write_be(&child.x, inout->public_key + 1); + + // Wipe all stack data. + memzero(&parent, sizeof(parent)); + memzero(&child, sizeof(child)); + + return 1; +} + +void hdnode_public_ckd_address_optimized( + const curve_point* pub, + const uint8_t* chain_code, + uint32_t i, + uint32_t version, + HasherType hasher_pubkey, + HasherType hasher_base58, + char* addr, + int addrsize, + int addrformat) { + uint8_t child_pubkey[33] = {0}; + curve_point b = {0}; + + hdnode_public_ckd_cp(&secp256k1, pub, chain_code, i, &b, NULL); + child_pubkey[0] = 0x02 | (b.y.val[0] & 0x01); + bn_write_be(&b.x, child_pubkey + 1); + + switch(addrformat) { + case 1: // Segwit-in-P2SH + ecdsa_get_address_segwit_p2sh( + child_pubkey, version, hasher_pubkey, hasher_base58, addr, addrsize); + break; + default: // normal address + ecdsa_get_address(child_pubkey, version, hasher_pubkey, hasher_base58, addr, addrsize); + break; + } +} + +#if USE_BIP32_CACHE +static bool private_ckd_cache_root_set = false; +static CONFIDENTIAL HDNode private_ckd_cache_root; +static int private_ckd_cache_index = 0; + +static CONFIDENTIAL struct { + bool set; + size_t depth; + uint32_t i[BIP32_CACHE_MAXDEPTH]; + HDNode node; +} private_ckd_cache[BIP32_CACHE_SIZE]; + +void bip32_cache_clear(void) { + private_ckd_cache_root_set = false; + private_ckd_cache_index = 0; + memzero(&private_ckd_cache_root, sizeof(private_ckd_cache_root)); + memzero(private_ckd_cache, sizeof(private_ckd_cache)); +} + +int hdnode_private_ckd_cached( + HDNode* inout, + const uint32_t* i, + size_t i_count, + uint32_t* fingerprint) { + if(i_count == 0) { + // no way how to compute parent fingerprint + return 1; + } + if(i_count == 1) { + if(fingerprint) { + *fingerprint = hdnode_fingerprint(inout); + } + if(hdnode_private_ckd(inout, i[0]) == 0) return 0; + return 1; + } + + bool found = false; + // if root is not set or not the same + if(!private_ckd_cache_root_set || + memcmp(&private_ckd_cache_root, inout, sizeof(HDNode)) != 0) { + // clear the cache + private_ckd_cache_index = 0; + memzero(private_ckd_cache, sizeof(private_ckd_cache)); + // setup new root + memcpy(&private_ckd_cache_root, inout, sizeof(HDNode)); + private_ckd_cache_root_set = true; + } else { + // try to find parent + int j = 0; + for(j = 0; j < BIP32_CACHE_SIZE; j++) { + if(private_ckd_cache[j].set && private_ckd_cache[j].depth == i_count - 1 && + memcmp(private_ckd_cache[j].i, i, (i_count - 1) * sizeof(uint32_t)) == 0 && + private_ckd_cache[j].node.curve == inout->curve) { + memcpy(inout, &(private_ckd_cache[j].node), sizeof(HDNode)); + found = true; + break; + } + } + } + + // else derive parent + if(!found) { + size_t k = 0; + for(k = 0; k < i_count - 1; k++) { + if(hdnode_private_ckd(inout, i[k]) == 0) return 0; + } + // and save it + memzero( + &(private_ckd_cache[private_ckd_cache_index]), + sizeof(private_ckd_cache[private_ckd_cache_index])); + private_ckd_cache[private_ckd_cache_index].set = true; + private_ckd_cache[private_ckd_cache_index].depth = i_count - 1; + memcpy(private_ckd_cache[private_ckd_cache_index].i, i, (i_count - 1) * sizeof(uint32_t)); + memcpy(&(private_ckd_cache[private_ckd_cache_index].node), inout, sizeof(HDNode)); + private_ckd_cache_index = (private_ckd_cache_index + 1) % BIP32_CACHE_SIZE; + } + + if(fingerprint) { + *fingerprint = hdnode_fingerprint(inout); + } + if(hdnode_private_ckd(inout, i[i_count - 1]) == 0) return 0; + + return 1; +} +#endif + +int hdnode_get_address_raw(HDNode* node, uint32_t version, uint8_t* addr_raw) { + if(hdnode_fill_public_key(node) != 0) { + return 1; + } + ecdsa_get_address_raw(node->public_key, version, node->curve->hasher_pubkey, addr_raw); + return 0; +} + +int hdnode_get_address(HDNode* node, uint32_t version, char* addr, int addrsize) { + if(hdnode_fill_public_key(node) != 0) { + return 1; + } + ecdsa_get_address( + node->public_key, + version, + node->curve->hasher_pubkey, + node->curve->hasher_base58, + addr, + addrsize); + return 0; +} + +int hdnode_fill_public_key(HDNode* node) { + if(node->public_key[0] != 0) return 0; + +#if USE_BIP32_25519_CURVES + if(node->curve->params) { + if(ecdsa_get_public_key33(node->curve->params, node->private_key, node->public_key) != 0) { + return 1; + } + } else { + node->public_key[0] = 1; + if(node->curve == &ed25519_info) { + ed25519_publickey(node->private_key, node->public_key + 1); + } else if(node->curve == &ed25519_sha3_info) { + ed25519_publickey_sha3(node->private_key, node->public_key + 1); +#if USE_KECCAK + } else if(node->curve == &ed25519_keccak_info) { + ed25519_publickey_keccak(node->private_key, node->public_key + 1); +#endif + } else if(node->curve == &curve25519_info) { + curve25519_scalarmult_basepoint(node->public_key + 1, node->private_key); +#if USE_CARDANO + } else if(node->curve == &ed25519_cardano_info) { + ed25519_publickey_ext(node->private_key, node->public_key + 1); +#endif + } + } +#else + + if(ecdsa_get_public_key33(node->curve->params, node->private_key, node->public_key) != 0) { + return 1; + } +#endif + return 0; +} + +#if USE_ETHEREUM +int hdnode_get_ethereum_pubkeyhash(const HDNode* node, uint8_t* pubkeyhash) { + uint8_t buf[65] = {0}; + //SHA3_CTX ctx = {0}; + SHA3_CTX* ctx = malloc(sizeof(SHA3_CTX)); + memzero(ctx, sizeof(SHA3_CTX)); + + /* get uncompressed public key */ + if(ecdsa_get_public_key65(node->curve->params, node->private_key, buf) != 0) { + memzero(ctx, sizeof(SHA3_CTX)); + free(ctx); + return 0; + } + + /* compute sha3 of x and y coordinate without 04 prefix */ + sha3_256_Init(ctx); + sha3_Update(ctx, buf + 1, 64); + keccak_Final(ctx, buf); + + memzero(ctx, sizeof(SHA3_CTX)); + free(ctx); + + /* result are the least significant 160 bits */ + memcpy(pubkeyhash, buf + 12, 20); + + return 1; +} +#endif + +#if USE_NEM +int hdnode_get_nem_address(HDNode* node, uint8_t version, char* address) { + if(node->curve != &ed25519_keccak_info) { + return 0; + } + + if(hdnode_fill_public_key(node) != 0) { + return 0; + } + + return nem_get_address(&node->public_key[1], version, address); +} + +int hdnode_get_nem_shared_key( + const HDNode* node, + const ed25519_public_key peer_public_key, + const uint8_t* salt, + ed25519_public_key mul, + uint8_t* shared_key) { + if(node->curve != &ed25519_keccak_info) { + return 0; + } + + // sizeof(ed25519_public_key) == SHA3_256_DIGEST_LENGTH + if(mul == NULL) mul = shared_key; + + if(ed25519_scalarmult_keccak(mul, node->private_key, peer_public_key)) { + return 0; + } + + for(size_t i = 0; i < 32; i++) { + shared_key[i] = mul[i] ^ salt[i]; + } + + keccak_256(shared_key, 32, shared_key); + return 1; +} + +int hdnode_nem_encrypt( + const HDNode* node, + const ed25519_public_key public_key, + const uint8_t* iv_immut, + const uint8_t* salt, + const uint8_t* payload, + size_t size, + uint8_t* buffer) { + uint8_t last_block[AES_BLOCK_SIZE] = {0}; + uint8_t remainder = size % AES_BLOCK_SIZE; + + // Round down to last whole block + size -= remainder; + // Copy old last block + memcpy(last_block, &payload[size], remainder); + // Pad new last block with number of missing bytes + memset(&last_block[remainder], AES_BLOCK_SIZE - remainder, AES_BLOCK_SIZE - remainder); + + // the IV gets mutated, so we make a copy not to touch the original + uint8_t iv[AES_BLOCK_SIZE] = {0}; + memcpy(iv, iv_immut, AES_BLOCK_SIZE); + + uint8_t shared_key[SHA3_256_DIGEST_LENGTH] = {0}; + if(!hdnode_get_nem_shared_key(node, public_key, salt, NULL, shared_key)) { + return 0; + } + + aes_encrypt_ctx ctx = {0}; + + int ret = aes_encrypt_key256(shared_key, &ctx); + memzero(shared_key, sizeof(shared_key)); + + if(ret != EXIT_SUCCESS) { + return 0; + } + + if(aes_cbc_encrypt(payload, buffer, size, iv, &ctx) != EXIT_SUCCESS) { + return 0; + } + + if(aes_cbc_encrypt(last_block, &buffer[size], sizeof(last_block), iv, &ctx) != EXIT_SUCCESS) { + return 0; + } + + return 1; +} + +int hdnode_nem_decrypt( + const HDNode* node, + const ed25519_public_key public_key, + uint8_t* iv, + const uint8_t* salt, + const uint8_t* payload, + size_t size, + uint8_t* buffer) { + uint8_t shared_key[SHA3_256_DIGEST_LENGTH] = {0}; + + if(!hdnode_get_nem_shared_key(node, public_key, salt, NULL, shared_key)) { + return 0; + } + + aes_decrypt_ctx ctx = {0}; + + int ret = aes_decrypt_key256(shared_key, &ctx); + memzero(shared_key, sizeof(shared_key)); + + if(ret != EXIT_SUCCESS) { + return 0; + } + + if(aes_cbc_decrypt(payload, buffer, size, iv, &ctx) != EXIT_SUCCESS) { + return 0; + } + + return 1; +} +#endif + +// msg is a data to be signed +// msg_len is the message length +int hdnode_sign( + HDNode* node, + const uint8_t* msg, + uint32_t msg_len, + HasherType hasher_sign, + uint8_t* sig, + uint8_t* pby, + int (*is_canonical)(uint8_t by, uint8_t sig[64])) { + if(node->curve->params) { + return ecdsa_sign( + node->curve->params, + hasher_sign, + node->private_key, + msg, + msg_len, + sig, + pby, + is_canonical); + } else if(node->curve == &curve25519_info) { + return 1; // signatures are not supported + } else { + if(node->curve == &ed25519_info) { + ed25519_sign(msg, msg_len, node->private_key, sig); + } else if(node->curve == &ed25519_sha3_info) { + ed25519_sign_sha3(msg, msg_len, node->private_key, sig); +#if USE_KECCAK + } else if(node->curve == &ed25519_keccak_info) { + ed25519_sign_keccak(msg, msg_len, node->private_key, sig); +#endif + } else { + return 1; // unknown or unsupported curve + } + return 0; + } +} + +int hdnode_sign_digest( + HDNode* node, + const uint8_t* digest, + uint8_t* sig, + uint8_t* pby, + int (*is_canonical)(uint8_t by, uint8_t sig[64])) { + if(node->curve->params) { + return ecdsa_sign_digest( + node->curve->params, node->private_key, digest, sig, pby, is_canonical); + } else if(node->curve == &curve25519_info) { + return 1; // signatures are not supported + } else { + return hdnode_sign(node, digest, 32, 0, sig, pby, is_canonical); + } +} + +int hdnode_get_shared_key( + const HDNode* node, + const uint8_t* peer_public_key, + uint8_t* session_key, + int* result_size) { + // Use elliptic curve Diffie-Helman to compute shared session key + if(node->curve->params) { + if(ecdh_multiply(node->curve->params, node->private_key, peer_public_key, session_key) != + 0) { + return 1; + } + *result_size = 65; + return 0; + } else if(node->curve == &curve25519_info) { + session_key[0] = 0x04; + if(peer_public_key[0] != 0x40) { + return 1; // Curve25519 public key should start with 0x40 byte. + } + curve25519_scalarmult(session_key + 1, node->private_key, peer_public_key + 1); + *result_size = 33; + return 0; + } else { + *result_size = 0; + return 1; // ECDH is not supported + } +} + +static int hdnode_serialize( + const HDNode* node, + uint32_t fingerprint, + uint32_t version, + bool use_private, + char* str, + int strsize) { + uint8_t node_data[78] = {0}; + write_be(node_data, version); + node_data[4] = node->depth; + write_be(node_data + 5, fingerprint); + write_be(node_data + 9, node->child_num); + memcpy(node_data + 13, node->chain_code, 32); + if(use_private) { + node_data[45] = 0; + memcpy(node_data + 46, node->private_key, 32); + } else { + memcpy(node_data + 45, node->public_key, 33); + } + int ret = base58_encode_check( + node_data, sizeof(node_data), node->curve->hasher_base58, str, strsize); + memzero(node_data, sizeof(node_data)); + return ret; +} + +int hdnode_serialize_public( + const HDNode* node, + uint32_t fingerprint, + uint32_t version, + char* str, + int strsize) { + return hdnode_serialize(node, fingerprint, version, false, str, strsize); +} + +int hdnode_serialize_private( + const HDNode* node, + uint32_t fingerprint, + uint32_t version, + char* str, + int strsize) { + return hdnode_serialize(node, fingerprint, version, true, str, strsize); +} + +// check for validity of curve point in case of public data not performed +static int hdnode_deserialize( + const char* str, + uint32_t version, + bool use_private, + const char* curve, + HDNode* node, + uint32_t* fingerprint) { + uint8_t node_data[78] = {0}; + memzero(node, sizeof(HDNode)); + node->curve = get_curve_by_name(curve); + if(base58_decode_check(str, node->curve->hasher_base58, node_data, sizeof(node_data)) != + sizeof(node_data)) { + return -1; + } + uint32_t ver = read_be(node_data); + if(ver != version) { + return -3; // invalid version + } + if(use_private) { + // invalid data + if(node_data[45]) { + return -2; + } + memcpy(node->private_key, node_data + 46, 32); + memzero(node->public_key, sizeof(node->public_key)); + } else { + memzero(node->private_key, sizeof(node->private_key)); + memcpy(node->public_key, node_data + 45, 33); + } + node->depth = node_data[4]; + if(fingerprint) { + *fingerprint = read_be(node_data + 5); + } + node->child_num = read_be(node_data + 9); + memcpy(node->chain_code, node_data + 13, 32); + return 0; +} + +int hdnode_deserialize_public( + const char* str, + uint32_t version, + const char* curve, + HDNode* node, + uint32_t* fingerprint) { + return hdnode_deserialize(str, version, false, curve, node, fingerprint); +} + +int hdnode_deserialize_private( + const char* str, + uint32_t version, + const char* curve, + HDNode* node, + uint32_t* fingerprint) { + return hdnode_deserialize(str, version, true, curve, node, fingerprint); +} + +const curve_info* get_curve_by_name(const char* curve_name) { + if(curve_name == 0) { + return 0; + } + if(strcmp(curve_name, SECP256K1_NAME) == 0) { + return &secp256k1_info; + } + if(strcmp(curve_name, SECP256K1_DECRED_NAME) == 0) { + return &secp256k1_decred_info; + } + if(strcmp(curve_name, SECP256K1_GROESTL_NAME) == 0) { + return &secp256k1_groestl_info; + } + if(strcmp(curve_name, SECP256K1_SMART_NAME) == 0) { + return &secp256k1_smart_info; + } + if(strcmp(curve_name, NIST256P1_NAME) == 0) { + return &nist256p1_info; + } + if(strcmp(curve_name, ED25519_NAME) == 0) { + return &ed25519_info; + } +#if USE_CARDANO + if(strcmp(curve_name, ED25519_CARDANO_NAME) == 0) { + return &ed25519_cardano_info; + } +#endif + if(strcmp(curve_name, ED25519_SHA3_NAME) == 0) { + return &ed25519_sha3_info; + } +#if USE_KECCAK + if(strcmp(curve_name, ED25519_KECCAK_NAME) == 0) { + return &ed25519_keccak_info; + } +#endif + if(strcmp(curve_name, CURVE25519_NAME) == 0) { + return &curve25519_info; + } + return 0; +} diff --git a/applications/external/flipbip/lib/crypto/bip32.h b/applications/external/flipbip/lib/crypto/bip32.h new file mode 100644 index 0000000000..3e31148483 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/bip32.h @@ -0,0 +1,202 @@ +/** + * Copyright (c) 2013-2014 Tomas Dzetkulic + * Copyright (c) 2013-2014 Pavol Rusnak + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __BIP32_H__ +#define __BIP32_H__ + +#include +#include +#include +#include "ecdsa.h" +#include "ed25519_donna/ed25519.h" +#include "options.h" + +// Maximum length of a Base58Check-encoded extended public or private key. +#define XPUB_MAXLEN 112 + +// Maximum length of a Base58Check-encoded address. +#define ADDRESS_MAXLEN 39 + +typedef struct { + const char* bip32_name; // string for generating BIP32 xprv from seed + const ecdsa_curve* params; // ecdsa curve parameters, null for ed25519 + + HasherType hasher_base58; + HasherType hasher_sign; + HasherType hasher_pubkey; + HasherType hasher_script; +} curve_info; + +typedef struct { + uint32_t depth; + uint32_t child_num; + uint8_t chain_code[32]; + + uint8_t private_key[32]; + uint8_t private_key_extension[32]; + + uint8_t public_key[33]; + const curve_info* curve; +} HDNode; + +int hdnode_from_xpub( + uint32_t depth, + uint32_t child_num, + const uint8_t* chain_code, + const uint8_t* public_key, + const char* curve, + HDNode* out); + +int hdnode_from_xprv( + uint32_t depth, + uint32_t child_num, + const uint8_t* chain_code, + const uint8_t* private_key, + const char* curve, + HDNode* out); + +int hdnode_from_seed(const uint8_t* seed, int seed_len, const char* curve, HDNode* out); + +#define hdnode_private_ckd_prime(X, I) hdnode_private_ckd((X), ((I) | 0x80000000)) + +int hdnode_private_ckd(HDNode* inout, uint32_t i); + +int hdnode_public_ckd_cp( + const ecdsa_curve* curve, + const curve_point* parent, + const uint8_t* parent_chain_code, + uint32_t i, + curve_point* child, + uint8_t* child_chain_code); + +int hdnode_public_ckd(HDNode* inout, uint32_t i); + +void hdnode_public_ckd_address_optimized( + const curve_point* pub, + const uint8_t* chain_code, + uint32_t i, + uint32_t version, + HasherType hasher_pubkey, + HasherType hasher_base58, + char* addr, + int addrsize, + int addrformat); + +#if USE_BIP32_CACHE +void bip32_cache_clear(void); +int hdnode_private_ckd_cached( + HDNode* inout, + const uint32_t* i, + size_t i_count, + uint32_t* fingerprint); +#endif + +uint32_t hdnode_fingerprint(HDNode* node); + +int hdnode_fill_public_key(HDNode* node); + +#if USE_ETHEREUM +int hdnode_get_ethereum_pubkeyhash(const HDNode* node, uint8_t* pubkeyhash); +#endif + +#if USE_NEM +int hdnode_get_nem_address(HDNode* node, uint8_t version, char* address); +int hdnode_get_nem_shared_key( + const HDNode* node, + const ed25519_public_key peer_public_key, + const uint8_t* salt, + ed25519_public_key mul, + uint8_t* shared_key); +int hdnode_nem_encrypt( + const HDNode* node, + const ed25519_public_key public_key, + const uint8_t* iv, + const uint8_t* salt, + const uint8_t* payload, + size_t size, + uint8_t* buffer); +int hdnode_nem_decrypt( + const HDNode* node, + const ed25519_public_key public_key, + uint8_t* iv, + const uint8_t* salt, + const uint8_t* payload, + size_t size, + uint8_t* buffer); +#endif + +int hdnode_sign( + HDNode* node, + const uint8_t* msg, + uint32_t msg_len, + HasherType hasher_sign, + uint8_t* sig, + uint8_t* pby, + int (*is_canonical)(uint8_t by, uint8_t sig[64])); +int hdnode_sign_digest( + HDNode* node, + const uint8_t* digest, + uint8_t* sig, + uint8_t* pby, + int (*is_canonical)(uint8_t by, uint8_t sig[64])); + +int hdnode_get_shared_key( + const HDNode* node, + const uint8_t* peer_public_key, + uint8_t* session_key, + int* result_size); + +int hdnode_serialize_public( + const HDNode* node, + uint32_t fingerprint, + uint32_t version, + char* str, + int strsize); + +int hdnode_serialize_private( + const HDNode* node, + uint32_t fingerprint, + uint32_t version, + char* str, + int strsize); + +int hdnode_deserialize_public( + const char* str, + uint32_t version, + const char* curve, + HDNode* node, + uint32_t* fingerprint); + +int hdnode_deserialize_private( + const char* str, + uint32_t version, + const char* curve, + HDNode* node, + uint32_t* fingerprint); + +int hdnode_get_address_raw(HDNode* node, uint32_t version, uint8_t* addr_raw); +int hdnode_get_address(HDNode* node, uint32_t version, char* addr, int addrsize); + +const curve_info* get_curve_by_name(const char* curve_name); + +#endif diff --git a/applications/external/flipbip/lib/crypto/bip39.c b/applications/external/flipbip/lib/crypto/bip39.c new file mode 100644 index 0000000000..c99fe76c90 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/bip39.c @@ -0,0 +1,288 @@ +/** + * Copyright (c) 2013-2014 Tomas Dzetkulic + * Copyright (c) 2013-2014 Pavol Rusnak + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include + +#include "bip39.h" +#include "hmac.h" +#include "memzero.h" +#include "options.h" +#include "pbkdf2.h" +#include "rand.h" +#include "sha2.h" + +#if USE_BIP39_CACHE + +static int bip39_cache_index = 0; + +static CONFIDENTIAL struct { + bool set; + char mnemonic[256]; + char passphrase[64]; + uint8_t seed[512 / 8]; +} bip39_cache[BIP39_CACHE_SIZE]; + +void bip39_cache_clear(void) { + memzero(bip39_cache, sizeof(bip39_cache)); + bip39_cache_index = 0; +} + +#endif + +const char* mnemonic_generate(int strength) { + if(strength % 32 || strength < 128 || strength > 256) { + return 0; + } + uint8_t data[32] = {0}; + random_buffer(data, 32); + const char* r = mnemonic_from_data(data, strength / 8); + memzero(data, sizeof(data)); + return r; +} + +static CONFIDENTIAL char mnemo[24 * 10]; + +const char* mnemonic_from_data(const uint8_t* data, int len) { + if(len % 4 || len < 16 || len > 32) { + return 0; + } + + uint8_t bits[32 + 1] = {0}; + + sha256_Raw(data, len, bits); + // checksum + bits[len] = bits[0]; + // data + memcpy(bits, data, len); + + int mlen = len * 3 / 4; + + int i = 0, j = 0, idx = 0; + char* p = mnemo; + for(i = 0; i < mlen; i++) { + idx = 0; + for(j = 0; j < 11; j++) { + idx <<= 1; + idx += (bits[(i * 11 + j) / 8] & (1 << (7 - ((i * 11 + j) % 8)))) > 0; + } + strcpy(p, BIP39_WORDLIST_ENGLISH[idx]); + p += strlen(BIP39_WORDLIST_ENGLISH[idx]); + *p = (i < mlen - 1) ? ' ' : 0; + p++; + } + memzero(bits, sizeof(bits)); + + return mnemo; +} + +void mnemonic_clear(void) { + memzero(mnemo, sizeof(mnemo)); +} + +int mnemonic_to_bits(const char* mnemonic, uint8_t* bits) { + if(!mnemonic) { + return 0; + } + + uint32_t i = 0, n = 0; + + while(mnemonic[i]) { + if(mnemonic[i] == ' ') { + n++; + } + i++; + } + n++; + + // check that number of words is valid for BIP-39: + // (a) between 128 and 256 bits of initial entropy (12 - 24 words) + // (b) number of bits divisible by 33 (1 checksum bit per 32 input bits) + // - that is, (n * 11) % 33 == 0, so n % 3 == 0 + if(n < 12 || n > 24 || (n % 3)) { + return 0; + } + + char current_word[10] = {0}; + uint32_t j = 0, ki = 0, bi = 0; + uint8_t result[32 + 1] = {0}; + + memzero(result, sizeof(result)); + i = 0; + while(mnemonic[i]) { + j = 0; + while(mnemonic[i] != ' ' && mnemonic[i] != 0) { + if(j >= sizeof(current_word) - 1) { + return 0; + } + current_word[j] = mnemonic[i]; + i++; + j++; + } + current_word[j] = 0; + if(mnemonic[i] != 0) { + i++; + } + int k = mnemonic_find_word(current_word); + if(k < 0) { // word not found + return 0; + } + for(ki = 0; ki < 11; ki++) { + if(k & (1 << (10 - ki))) { + result[bi / 8] |= 1 << (7 - (bi % 8)); + } + bi++; + } + } + if(bi != n * 11) { + return 0; + } + memcpy(bits, result, sizeof(result)); + memzero(result, sizeof(result)); + + // returns amount of entropy + checksum BITS + return n * 11; +} + +int mnemonic_check(const char* mnemonic) { + uint8_t bits[32 + 1] = {0}; + int mnemonic_bits_len = mnemonic_to_bits(mnemonic, bits); + if(mnemonic_bits_len != (12 * 11) && mnemonic_bits_len != (18 * 11) && + mnemonic_bits_len != (24 * 11)) { + return 0; + } + int words = mnemonic_bits_len / 11; + + uint8_t checksum = bits[words * 4 / 3]; + sha256_Raw(bits, words * 4 / 3, bits); + if(words == 12) { + return (bits[0] & 0xF0) == (checksum & 0xF0); // compare first 4 bits + } else if(words == 18) { + return (bits[0] & 0xFC) == (checksum & 0xFC); // compare first 6 bits + } else if(words == 24) { + return bits[0] == checksum; // compare 8 bits + } + return 0; +} + +// passphrase must be at most 256 characters otherwise it would be truncated +void mnemonic_to_seed( + const char* mnemonic, + const char* passphrase, + uint8_t seed[512 / 8], + void (*progress_callback)(uint32_t current, uint32_t total)) { + int mnemoniclen = strlen(mnemonic); + int passphraselen = strlen(passphrase); + if(passphraselen > 256) passphraselen = 256; +#if USE_BIP39_CACHE + // check cache + if(mnemoniclen < 256 && passphraselen < 64) { + for(int i = 0; i < BIP39_CACHE_SIZE; i++) { + if(!bip39_cache[i].set) continue; + if(strcmp(bip39_cache[i].mnemonic, mnemonic) != 0) continue; + if(strcmp(bip39_cache[i].passphrase, passphrase) != 0) continue; + // found the correct entry + memcpy(seed, bip39_cache[i].seed, 512 / 8); + return; + } + } +#endif + uint8_t salt[8 + 256] = {0}; + memcpy(salt, "mnemonic", 8); + memcpy(salt + 8, passphrase, passphraselen); + static CONFIDENTIAL PBKDF2_HMAC_SHA512_CTX pctx; + pbkdf2_hmac_sha512_Init( + &pctx, (const uint8_t*)mnemonic, mnemoniclen, salt, passphraselen + 8, 1); + if(progress_callback) { + progress_callback(0, BIP39_PBKDF2_ROUNDS); + } + for(int i = 0; i < 16; i++) { + pbkdf2_hmac_sha512_Update(&pctx, BIP39_PBKDF2_ROUNDS / 16); + if(progress_callback) { + progress_callback((i + 1) * BIP39_PBKDF2_ROUNDS / 16, BIP39_PBKDF2_ROUNDS); + } + } + pbkdf2_hmac_sha512_Final(&pctx, seed); + memzero(salt, sizeof(salt)); +#if USE_BIP39_CACHE + // store to cache + if(mnemoniclen < 256 && passphraselen < 64) { + bip39_cache[bip39_cache_index].set = true; + strcpy(bip39_cache[bip39_cache_index].mnemonic, mnemonic); + strcpy(bip39_cache[bip39_cache_index].passphrase, passphrase); + memcpy(bip39_cache[bip39_cache_index].seed, seed, 512 / 8); + bip39_cache_index = (bip39_cache_index + 1) % BIP39_CACHE_SIZE; + } +#endif +} + +// binary search for finding the word in the wordlist +int mnemonic_find_word(const char* word) { + int lo = 0, hi = BIP39_WORD_COUNT - 1; + while(lo <= hi) { + int mid = lo + (hi - lo) / 2; + int cmp = strcmp(word, BIP39_WORDLIST_ENGLISH[mid]); + if(cmp == 0) { + return mid; + } + if(cmp > 0) { + lo = mid + 1; + } else { + hi = mid - 1; + } + } + return -1; +} + +const char* mnemonic_complete_word(const char* prefix, int len) { + // we need to perform linear search, + // because we want to return the first match + for(int i = 0; i < BIP39_WORD_COUNT; i++) { + if(strncmp(BIP39_WORDLIST_ENGLISH[i], prefix, len) == 0) { + return BIP39_WORDLIST_ENGLISH[i]; + } + } + return NULL; +} + +const char* mnemonic_get_word(int index) { + if(index >= 0 && index < BIP39_WORD_COUNT) { + return BIP39_WORDLIST_ENGLISH[index]; + } else { + return NULL; + } +} + +uint32_t mnemonic_word_completion_mask(const char* prefix, int len) { + if(len <= 0) { + return 0x3ffffff; // all letters (bits 1-26 set) + } + uint32_t res = 0; + for(int i = 0; i < BIP39_WORD_COUNT; i++) { + const char* word = BIP39_WORDLIST_ENGLISH[i]; + if(strncmp(word, prefix, len) == 0 && word[len] >= 'a' && word[len] <= 'z') { + res |= 1 << (word[len] - 'a'); + } + } + return res; +} diff --git a/applications/external/flipbip/lib/crypto/bip39.h b/applications/external/flipbip/lib/crypto/bip39.h new file mode 100644 index 0000000000..5e29c83364 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/bip39.h @@ -0,0 +1,61 @@ +/** + * Copyright (c) 2013-2014 Tomas Dzetkulic + * Copyright (c) 2013-2014 Pavol Rusnak + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __BIP39_H__ +#define __BIP39_H__ + +#include +#include + +#include "options.h" + +#define BIP39_WORD_COUNT 2048 +#define BIP39_PBKDF2_ROUNDS 2048 + +#if USE_BIP39_CACHE +void bip39_cache_clear(void); +#endif + +extern const char* const BIP39_WORDLIST_ENGLISH[BIP39_WORD_COUNT]; + +const char* mnemonic_generate(int strength); // strength in bits +const char* mnemonic_from_data(const uint8_t* data, int len); +void mnemonic_clear(void); + +int mnemonic_check(const char* mnemonic); + +int mnemonic_to_bits(const char* mnemonic, uint8_t* bits); + +// passphrase must be at most 256 characters otherwise it would be truncated +void mnemonic_to_seed( + const char* mnemonic, + const char* passphrase, + uint8_t seed[512 / 8], + void (*progress_callback)(uint32_t current, uint32_t total)); + +int mnemonic_find_word(const char* word); +const char* mnemonic_complete_word(const char* prefix, int len); +const char* mnemonic_get_word(int index); +uint32_t mnemonic_word_completion_mask(const char* prefix, int len); + +#endif diff --git a/applications/external/flipbip/lib/crypto/bip39_english.c b/applications/external/flipbip/lib/crypto/bip39_english.c new file mode 100644 index 0000000000..4b92b4c7f1 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/bip39_english.c @@ -0,0 +1,283 @@ +/** + * Copyright (c) 2013-2014 Tomas Dzetkulic + * Copyright (c) 2013-2014 Pavol Rusnak + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "bip39.h" + +const char* const BIP39_WORDLIST_ENGLISH[BIP39_WORD_COUNT] = { + "abandon", "ability", "able", "about", "above", "absent", "absorb", "abstract", + "absurd", "abuse", "access", "accident", "account", "accuse", "achieve", "acid", + "acoustic", "acquire", "across", "act", "action", "actor", "actress", "actual", + "adapt", "add", "addict", "address", "adjust", "admit", "adult", "advance", + "advice", "aerobic", "affair", "afford", "afraid", "again", "age", "agent", + "agree", "ahead", "aim", "air", "airport", "aisle", "alarm", "album", + "alcohol", "alert", "alien", "all", "alley", "allow", "almost", "alone", + "alpha", "already", "also", "alter", "always", "amateur", "amazing", "among", + "amount", "amused", "analyst", "anchor", "ancient", "anger", "angle", "angry", + "animal", "ankle", "announce", "annual", "another", "answer", "antenna", "antique", + "anxiety", "any", "apart", "apology", "appear", "apple", "approve", "april", + "arch", "arctic", "area", "arena", "argue", "arm", "armed", "armor", + "army", "around", "arrange", "arrest", "arrive", "arrow", "art", "artefact", + "artist", "artwork", "ask", "aspect", "assault", "asset", "assist", "assume", + "asthma", "athlete", "atom", "attack", "attend", "attitude", "attract", "auction", + "audit", "august", "aunt", "author", "auto", "autumn", "average", "avocado", + "avoid", "awake", "aware", "away", "awesome", "awful", "awkward", "axis", + "baby", "bachelor", "bacon", "badge", "bag", "balance", "balcony", "ball", + "bamboo", "banana", "banner", "bar", "barely", "bargain", "barrel", "base", + "basic", "basket", "battle", "beach", "bean", "beauty", "because", "become", + "beef", "before", "begin", "behave", "behind", "believe", "below", "belt", + "bench", "benefit", "best", "betray", "better", "between", "beyond", "bicycle", + "bid", "bike", "bind", "biology", "bird", "birth", "bitter", "black", + "blade", "blame", "blanket", "blast", "bleak", "bless", "blind", "blood", + "blossom", "blouse", "blue", "blur", "blush", "board", "boat", "body", + "boil", "bomb", "bone", "bonus", "book", "boost", "border", "boring", + "borrow", "boss", "bottom", "bounce", "box", "boy", "bracket", "brain", + "brand", "brass", "brave", "bread", "breeze", "brick", "bridge", "brief", + "bright", "bring", "brisk", "broccoli", "broken", "bronze", "broom", "brother", + "brown", "brush", "bubble", "buddy", "budget", "buffalo", "build", "bulb", + "bulk", "bullet", "bundle", "bunker", "burden", "burger", "burst", "bus", + "business", "busy", "butter", "buyer", "buzz", "cabbage", "cabin", "cable", + "cactus", "cage", "cake", "call", "calm", "camera", "camp", "can", + "canal", "cancel", "candy", "cannon", "canoe", "canvas", "canyon", "capable", + "capital", "captain", "car", "carbon", "card", "cargo", "carpet", "carry", + "cart", "case", "cash", "casino", "castle", "casual", "cat", "catalog", + "catch", "category", "cattle", "caught", "cause", "caution", "cave", "ceiling", + "celery", "cement", "census", "century", "cereal", "certain", "chair", "chalk", + "champion", "change", "chaos", "chapter", "charge", "chase", "chat", "cheap", + "check", "cheese", "chef", "cherry", "chest", "chicken", "chief", "child", + "chimney", "choice", "choose", "chronic", "chuckle", "chunk", "churn", "cigar", + "cinnamon", "circle", "citizen", "city", "civil", "claim", "clap", "clarify", + "claw", "clay", "clean", "clerk", "clever", "click", "client", "cliff", + "climb", "clinic", "clip", "clock", "clog", "close", "cloth", "cloud", + "clown", "club", "clump", "cluster", "clutch", "coach", "coast", "coconut", + "code", "coffee", "coil", "coin", "collect", "color", "column", "combine", + "come", "comfort", "comic", "common", "company", "concert", "conduct", "confirm", + "congress", "connect", "consider", "control", "convince", "cook", "cool", "copper", + "copy", "coral", "core", "corn", "correct", "cost", "cotton", "couch", + "country", "couple", "course", "cousin", "cover", "coyote", "crack", "cradle", + "craft", "cram", "crane", "crash", "crater", "crawl", "crazy", "cream", + "credit", "creek", "crew", "cricket", "crime", "crisp", "critic", "crop", + "cross", "crouch", "crowd", "crucial", "cruel", "cruise", "crumble", "crunch", + "crush", "cry", "crystal", "cube", "culture", "cup", "cupboard", "curious", + "current", "curtain", "curve", "cushion", "custom", "cute", "cycle", "dad", + "damage", "damp", "dance", "danger", "daring", "dash", "daughter", "dawn", + "day", "deal", "debate", "debris", "decade", "december", "decide", "decline", + "decorate", "decrease", "deer", "defense", "define", "defy", "degree", "delay", + "deliver", "demand", "demise", "denial", "dentist", "deny", "depart", "depend", + "deposit", "depth", "deputy", "derive", "describe", "desert", "design", "desk", + "despair", "destroy", "detail", "detect", "develop", "device", "devote", "diagram", + "dial", "diamond", "diary", "dice", "diesel", "diet", "differ", "digital", + "dignity", "dilemma", "dinner", "dinosaur", "direct", "dirt", "disagree", "discover", + "disease", "dish", "dismiss", "disorder", "display", "distance", "divert", "divide", + "divorce", "dizzy", "doctor", "document", "dog", "doll", "dolphin", "domain", + "donate", "donkey", "donor", "door", "dose", "double", "dove", "draft", + "dragon", "drama", "drastic", "draw", "dream", "dress", "drift", "drill", + "drink", "drip", "drive", "drop", "drum", "dry", "duck", "dumb", + "dune", "during", "dust", "dutch", "duty", "dwarf", "dynamic", "eager", + "eagle", "early", "earn", "earth", "easily", "east", "easy", "echo", + "ecology", "economy", "edge", "edit", "educate", "effort", "egg", "eight", + "either", "elbow", "elder", "electric", "elegant", "element", "elephant", "elevator", + "elite", "else", "embark", "embody", "embrace", "emerge", "emotion", "employ", + "empower", "empty", "enable", "enact", "end", "endless", "endorse", "enemy", + "energy", "enforce", "engage", "engine", "enhance", "enjoy", "enlist", "enough", + "enrich", "enroll", "ensure", "enter", "entire", "entry", "envelope", "episode", + "equal", "equip", "era", "erase", "erode", "erosion", "error", "erupt", + "escape", "essay", "essence", "estate", "eternal", "ethics", "evidence", "evil", + "evoke", "evolve", "exact", "example", "excess", "exchange", "excite", "exclude", + "excuse", "execute", "exercise", "exhaust", "exhibit", "exile", "exist", "exit", + "exotic", "expand", "expect", "expire", "explain", "expose", "express", "extend", + "extra", "eye", "eyebrow", "fabric", "face", "faculty", "fade", "faint", + "faith", "fall", "false", "fame", "family", "famous", "fan", "fancy", + "fantasy", "farm", "fashion", "fat", "fatal", "father", "fatigue", "fault", + "favorite", "feature", "february", "federal", "fee", "feed", "feel", "female", + "fence", "festival", "fetch", "fever", "few", "fiber", "fiction", "field", + "figure", "file", "film", "filter", "final", "find", "fine", "finger", + "finish", "fire", "firm", "first", "fiscal", "fish", "fit", "fitness", + "fix", "flag", "flame", "flash", "flat", "flavor", "flee", "flight", + "flip", "float", "flock", "floor", "flower", "fluid", "flush", "fly", + "foam", "focus", "fog", "foil", "fold", "follow", "food", "foot", + "force", "forest", "forget", "fork", "fortune", "forum", "forward", "fossil", + "foster", "found", "fox", "fragile", "frame", "frequent", "fresh", "friend", + "fringe", "frog", "front", "frost", "frown", "frozen", "fruit", "fuel", + "fun", "funny", "furnace", "fury", "future", "gadget", "gain", "galaxy", + "gallery", "game", "gap", "garage", "garbage", "garden", "garlic", "garment", + "gas", "gasp", "gate", "gather", "gauge", "gaze", "general", "genius", + "genre", "gentle", "genuine", "gesture", "ghost", "giant", "gift", "giggle", + "ginger", "giraffe", "girl", "give", "glad", "glance", "glare", "glass", + "glide", "glimpse", "globe", "gloom", "glory", "glove", "glow", "glue", + "goat", "goddess", "gold", "good", "goose", "gorilla", "gospel", "gossip", + "govern", "gown", "grab", "grace", "grain", "grant", "grape", "grass", + "gravity", "great", "green", "grid", "grief", "grit", "grocery", "group", + "grow", "grunt", "guard", "guess", "guide", "guilt", "guitar", "gun", + "gym", "habit", "hair", "half", "hammer", "hamster", "hand", "happy", + "harbor", "hard", "harsh", "harvest", "hat", "have", "hawk", "hazard", + "head", "health", "heart", "heavy", "hedgehog", "height", "hello", "helmet", + "help", "hen", "hero", "hidden", "high", "hill", "hint", "hip", + "hire", "history", "hobby", "hockey", "hold", "hole", "holiday", "hollow", + "home", "honey", "hood", "hope", "horn", "horror", "horse", "hospital", + "host", "hotel", "hour", "hover", "hub", "huge", "human", "humble", + "humor", "hundred", "hungry", "hunt", "hurdle", "hurry", "hurt", "husband", + "hybrid", "ice", "icon", "idea", "identify", "idle", "ignore", "ill", + "illegal", "illness", "image", "imitate", "immense", "immune", "impact", "impose", + "improve", "impulse", "inch", "include", "income", "increase", "index", "indicate", + "indoor", "industry", "infant", "inflict", "inform", "inhale", "inherit", "initial", + "inject", "injury", "inmate", "inner", "innocent", "input", "inquiry", "insane", + "insect", "inside", "inspire", "install", "intact", "interest", "into", "invest", + "invite", "involve", "iron", "island", "isolate", "issue", "item", "ivory", + "jacket", "jaguar", "jar", "jazz", "jealous", "jeans", "jelly", "jewel", + "job", "join", "joke", "journey", "joy", "judge", "juice", "jump", + "jungle", "junior", "junk", "just", "kangaroo", "keen", "keep", "ketchup", + "key", "kick", "kid", "kidney", "kind", "kingdom", "kiss", "kit", + "kitchen", "kite", "kitten", "kiwi", "knee", "knife", "knock", "know", + "lab", "label", "labor", "ladder", "lady", "lake", "lamp", "language", + "laptop", "large", "later", "latin", "laugh", "laundry", "lava", "law", + "lawn", "lawsuit", "layer", "lazy", "leader", "leaf", "learn", "leave", + "lecture", "left", "leg", "legal", "legend", "leisure", "lemon", "lend", + "length", "lens", "leopard", "lesson", "letter", "level", "liar", "liberty", + "library", "license", "life", "lift", "light", "like", "limb", "limit", + "link", "lion", "liquid", "list", "little", "live", "lizard", "load", + "loan", "lobster", "local", "lock", "logic", "lonely", "long", "loop", + "lottery", "loud", "lounge", "love", "loyal", "lucky", "luggage", "lumber", + "lunar", "lunch", "luxury", "lyrics", "machine", "mad", "magic", "magnet", + "maid", "mail", "main", "major", "make", "mammal", "man", "manage", + "mandate", "mango", "mansion", "manual", "maple", "marble", "march", "margin", + "marine", "market", "marriage", "mask", "mass", "master", "match", "material", + "math", "matrix", "matter", "maximum", "maze", "meadow", "mean", "measure", + "meat", "mechanic", "medal", "media", "melody", "melt", "member", "memory", + "mention", "menu", "mercy", "merge", "merit", "merry", "mesh", "message", + "metal", "method", "middle", "midnight", "milk", "million", "mimic", "mind", + "minimum", "minor", "minute", "miracle", "mirror", "misery", "miss", "mistake", + "mix", "mixed", "mixture", "mobile", "model", "modify", "mom", "moment", + "monitor", "monkey", "monster", "month", "moon", "moral", "more", "morning", + "mosquito", "mother", "motion", "motor", "mountain", "mouse", "move", "movie", + "much", "muffin", "mule", "multiply", "muscle", "museum", "mushroom", "music", + "must", "mutual", "myself", "mystery", "myth", "naive", "name", "napkin", + "narrow", "nasty", "nation", "nature", "near", "neck", "need", "negative", + "neglect", "neither", "nephew", "nerve", "nest", "net", "network", "neutral", + "never", "news", "next", "nice", "night", "noble", "noise", "nominee", + "noodle", "normal", "north", "nose", "notable", "note", "nothing", "notice", + "novel", "now", "nuclear", "number", "nurse", "nut", "oak", "obey", + "object", "oblige", "obscure", "observe", "obtain", "obvious", "occur", "ocean", + "october", "odor", "off", "offer", "office", "often", "oil", "okay", + "old", "olive", "olympic", "omit", "once", "one", "onion", "online", + "only", "open", "opera", "opinion", "oppose", "option", "orange", "orbit", + "orchard", "order", "ordinary", "organ", "orient", "original", "orphan", "ostrich", + "other", "outdoor", "outer", "output", "outside", "oval", "oven", "over", + "own", "owner", "oxygen", "oyster", "ozone", "pact", "paddle", "page", + "pair", "palace", "palm", "panda", "panel", "panic", "panther", "paper", + "parade", "parent", "park", "parrot", "party", "pass", "patch", "path", + "patient", "patrol", "pattern", "pause", "pave", "payment", "peace", "peanut", + "pear", "peasant", "pelican", "pen", "penalty", "pencil", "people", "pepper", + "perfect", "permit", "person", "pet", "phone", "photo", "phrase", "physical", + "piano", "picnic", "picture", "piece", "pig", "pigeon", "pill", "pilot", + "pink", "pioneer", "pipe", "pistol", "pitch", "pizza", "place", "planet", + "plastic", "plate", "play", "please", "pledge", "pluck", "plug", "plunge", + "poem", "poet", "point", "polar", "pole", "police", "pond", "pony", + "pool", "popular", "portion", "position", "possible", "post", "potato", "pottery", + "poverty", "powder", "power", "practice", "praise", "predict", "prefer", "prepare", + "present", "pretty", "prevent", "price", "pride", "primary", "print", "priority", + "prison", "private", "prize", "problem", "process", "produce", "profit", "program", + "project", "promote", "proof", "property", "prosper", "protect", "proud", "provide", + "public", "pudding", "pull", "pulp", "pulse", "pumpkin", "punch", "pupil", + "puppy", "purchase", "purity", "purpose", "purse", "push", "put", "puzzle", + "pyramid", "quality", "quantum", "quarter", "question", "quick", "quit", "quiz", + "quote", "rabbit", "raccoon", "race", "rack", "radar", "radio", "rail", + "rain", "raise", "rally", "ramp", "ranch", "random", "range", "rapid", + "rare", "rate", "rather", "raven", "raw", "razor", "ready", "real", + "reason", "rebel", "rebuild", "recall", "receive", "recipe", "record", "recycle", + "reduce", "reflect", "reform", "refuse", "region", "regret", "regular", "reject", + "relax", "release", "relief", "rely", "remain", "remember", "remind", "remove", + "render", "renew", "rent", "reopen", "repair", "repeat", "replace", "report", + "require", "rescue", "resemble", "resist", "resource", "response", "result", "retire", + "retreat", "return", "reunion", "reveal", "review", "reward", "rhythm", "rib", + "ribbon", "rice", "rich", "ride", "ridge", "rifle", "right", "rigid", + "ring", "riot", "ripple", "risk", "ritual", "rival", "river", "road", + "roast", "robot", "robust", "rocket", "romance", "roof", "rookie", "room", + "rose", "rotate", "rough", "round", "route", "royal", "rubber", "rude", + "rug", "rule", "run", "runway", "rural", "sad", "saddle", "sadness", + "safe", "sail", "salad", "salmon", "salon", "salt", "salute", "same", + "sample", "sand", "satisfy", "satoshi", "sauce", "sausage", "save", "say", + "scale", "scan", "scare", "scatter", "scene", "scheme", "school", "science", + "scissors", "scorpion", "scout", "scrap", "screen", "script", "scrub", "sea", + "search", "season", "seat", "second", "secret", "section", "security", "seed", + "seek", "segment", "select", "sell", "seminar", "senior", "sense", "sentence", + "series", "service", "session", "settle", "setup", "seven", "shadow", "shaft", + "shallow", "share", "shed", "shell", "sheriff", "shield", "shift", "shine", + "ship", "shiver", "shock", "shoe", "shoot", "shop", "short", "shoulder", + "shove", "shrimp", "shrug", "shuffle", "shy", "sibling", "sick", "side", + "siege", "sight", "sign", "silent", "silk", "silly", "silver", "similar", + "simple", "since", "sing", "siren", "sister", "situate", "six", "size", + "skate", "sketch", "ski", "skill", "skin", "skirt", "skull", "slab", + "slam", "sleep", "slender", "slice", "slide", "slight", "slim", "slogan", + "slot", "slow", "slush", "small", "smart", "smile", "smoke", "smooth", + "snack", "snake", "snap", "sniff", "snow", "soap", "soccer", "social", + "sock", "soda", "soft", "solar", "soldier", "solid", "solution", "solve", + "someone", "song", "soon", "sorry", "sort", "soul", "sound", "soup", + "source", "south", "space", "spare", "spatial", "spawn", "speak", "special", + "speed", "spell", "spend", "sphere", "spice", "spider", "spike", "spin", + "spirit", "split", "spoil", "sponsor", "spoon", "sport", "spot", "spray", + "spread", "spring", "spy", "square", "squeeze", "squirrel", "stable", "stadium", + "staff", "stage", "stairs", "stamp", "stand", "start", "state", "stay", + "steak", "steel", "stem", "step", "stereo", "stick", "still", "sting", + "stock", "stomach", "stone", "stool", "story", "stove", "strategy", "street", + "strike", "strong", "struggle", "student", "stuff", "stumble", "style", "subject", + "submit", "subway", "success", "such", "sudden", "suffer", "sugar", "suggest", + "suit", "summer", "sun", "sunny", "sunset", "super", "supply", "supreme", + "sure", "surface", "surge", "surprise", "surround", "survey", "suspect", "sustain", + "swallow", "swamp", "swap", "swarm", "swear", "sweet", "swift", "swim", + "swing", "switch", "sword", "symbol", "symptom", "syrup", "system", "table", + "tackle", "tag", "tail", "talent", "talk", "tank", "tape", "target", + "task", "taste", "tattoo", "taxi", "teach", "team", "tell", "ten", + "tenant", "tennis", "tent", "term", "test", "text", "thank", "that", + "theme", "then", "theory", "there", "they", "thing", "this", "thought", + "three", "thrive", "throw", "thumb", "thunder", "ticket", "tide", "tiger", + "tilt", "timber", "time", "tiny", "tip", "tired", "tissue", "title", + "toast", "tobacco", "today", "toddler", "toe", "together", "toilet", "token", + "tomato", "tomorrow", "tone", "tongue", "tonight", "tool", "tooth", "top", + "topic", "topple", "torch", "tornado", "tortoise", "toss", "total", "tourist", + "toward", "tower", "town", "toy", "track", "trade", "traffic", "tragic", + "train", "transfer", "trap", "trash", "travel", "tray", "treat", "tree", + "trend", "trial", "tribe", "trick", "trigger", "trim", "trip", "trophy", + "trouble", "truck", "true", "truly", "trumpet", "trust", "truth", "try", + "tube", "tuition", "tumble", "tuna", "tunnel", "turkey", "turn", "turtle", + "twelve", "twenty", "twice", "twin", "twist", "two", "type", "typical", + "ugly", "umbrella", "unable", "unaware", "uncle", "uncover", "under", "undo", + "unfair", "unfold", "unhappy", "uniform", "unique", "unit", "universe", "unknown", + "unlock", "until", "unusual", "unveil", "update", "upgrade", "uphold", "upon", + "upper", "upset", "urban", "urge", "usage", "use", "used", "useful", + "useless", "usual", "utility", "vacant", "vacuum", "vague", "valid", "valley", + "valve", "van", "vanish", "vapor", "various", "vast", "vault", "vehicle", + "velvet", "vendor", "venture", "venue", "verb", "verify", "version", "very", + "vessel", "veteran", "viable", "vibrant", "vicious", "victory", "video", "view", + "village", "vintage", "violin", "virtual", "virus", "visa", "visit", "visual", + "vital", "vivid", "vocal", "voice", "void", "volcano", "volume", "vote", + "voyage", "wage", "wagon", "wait", "walk", "wall", "walnut", "want", + "warfare", "warm", "warrior", "wash", "wasp", "waste", "water", "wave", + "way", "wealth", "weapon", "wear", "weasel", "weather", "web", "wedding", + "weekend", "weird", "welcome", "west", "wet", "whale", "what", "wheat", + "wheel", "when", "where", "whip", "whisper", "wide", "width", "wife", + "wild", "will", "win", "window", "wine", "wing", "wink", "winner", + "winter", "wire", "wisdom", "wise", "wish", "witness", "wolf", "woman", + "wonder", "wood", "wool", "word", "work", "world", "worry", "worth", + "wrap", "wreck", "wrestle", "wrist", "write", "wrong", "yard", "year", + "yellow", "you", "young", "youth", "zebra", "zero", "zone", "zoo", +}; diff --git a/applications/external/flipbip/lib/crypto/blake256.c b/applications/external/flipbip/lib/crypto/blake256.c new file mode 100644 index 0000000000..dfe6213b63 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/blake256.c @@ -0,0 +1,222 @@ +/* + BLAKE reference C implementation + + Copyright (c) 2012 Jean-Philippe Aumasson + + To the extent possible under law, the author(s) have dedicated all copyright + and related and neighboring rights to this software to the public domain + worldwide. This software is distributed without any warranty. + + You should have received a copy of the CC0 Public Domain Dedication along with + this software. If not, see . + */ +#include "blake256.h" + +#include + +#define U8TO32_BIG(p) \ + (((uint32_t)((p)[0]) << 24) | ((uint32_t)((p)[1]) << 16) | ((uint32_t)((p)[2]) << 8) | \ + ((uint32_t)((p)[3]))) + +#define U32TO8_BIG(p, v) \ + (p)[0] = (uint8_t)((v) >> 24); \ + (p)[1] = (uint8_t)((v) >> 16); \ + (p)[2] = (uint8_t)((v) >> 8); \ + (p)[3] = (uint8_t)((v)); + +static const uint8_t sigma[][16] = { + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + {14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3}, + {11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4}, + {7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8}, + {9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13}, + {2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9}, + {12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11}, + {13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10}, + {6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5}, + {10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + {14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3}, + {11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4}, + {7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8}, + {9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13}, + {2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9}}; + +static const uint32_t u256[16] = { + 0x243f6a88, + 0x85a308d3, + 0x13198a2e, + 0x03707344, + 0xa4093822, + 0x299f31d0, + 0x082efa98, + 0xec4e6c89, + 0x452821e6, + 0x38d01377, + 0xbe5466cf, + 0x34e90c6c, + 0xc0ac29b7, + 0xc97c50dd, + 0x3f84d5b5, + 0xb5470917}; + +static const uint8_t padding[129] = {0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +static void blake256_compress(BLAKE256_CTX* S, const uint8_t* block) { + uint32_t v[16] = {0}, m[16] = {0}, i = 0; +#define ROT(x, n) (((x) << (32 - n)) | ((x) >> (n))) +#define G(a, b, c, d, e) \ + v[a] += (m[sigma[i][e]] ^ u256[sigma[i][e + 1]]) + v[b]; \ + v[d] = ROT(v[d] ^ v[a], 16); \ + v[c] += v[d]; \ + v[b] = ROT(v[b] ^ v[c], 12); \ + v[a] += (m[sigma[i][e + 1]] ^ u256[sigma[i][e]]) + v[b]; \ + v[d] = ROT(v[d] ^ v[a], 8); \ + v[c] += v[d]; \ + v[b] = ROT(v[b] ^ v[c], 7); + + for(i = 0; i < 16; ++i) m[i] = U8TO32_BIG(block + i * 4); + + for(i = 0; i < 8; ++i) v[i] = S->h[i]; + + v[8] = S->s[0] ^ u256[0]; + v[9] = S->s[1] ^ u256[1]; + v[10] = S->s[2] ^ u256[2]; + v[11] = S->s[3] ^ u256[3]; + v[12] = u256[4]; + v[13] = u256[5]; + v[14] = u256[6]; + v[15] = u256[7]; + + /* don't xor t when the block is only padding */ + if(!S->nullt) { + v[12] ^= S->t[0]; + v[13] ^= S->t[0]; + v[14] ^= S->t[1]; + v[15] ^= S->t[1]; + } + + for(i = 0; i < 14; ++i) { + /* column step */ + G(0, 4, 8, 12, 0); + G(1, 5, 9, 13, 2); + G(2, 6, 10, 14, 4); + G(3, 7, 11, 15, 6); + /* diagonal step */ + G(0, 5, 10, 15, 8); + G(1, 6, 11, 12, 10); + G(2, 7, 8, 13, 12); + G(3, 4, 9, 14, 14); + } + + for(i = 0; i < 16; ++i) S->h[i % 8] ^= v[i]; + + for(i = 0; i < 8; ++i) S->h[i] ^= S->s[i % 4]; +} + +void blake256_Init(BLAKE256_CTX* S) { + S->h[0] = 0x6a09e667; + S->h[1] = 0xbb67ae85; + S->h[2] = 0x3c6ef372; + S->h[3] = 0xa54ff53a; + S->h[4] = 0x510e527f; + S->h[5] = 0x9b05688c; + S->h[6] = 0x1f83d9ab; + S->h[7] = 0x5be0cd19; + S->t[0] = S->t[1] = S->buflen = S->nullt = 0; + S->s[0] = S->s[1] = S->s[2] = S->s[3] = 0; +} + +void blake256_Update(BLAKE256_CTX* S, const uint8_t* in, size_t inlen) { + size_t left = S->buflen; + size_t fill = 64 - left; + + /* data left and data received fill a block */ + if(left && (inlen >= fill)) { + memcpy((void*)(S->buf + left), (void*)in, fill); + S->t[0] += 512; + + if(S->t[0] == 0) S->t[1]++; + + blake256_compress(S, S->buf); + in += fill; + inlen -= fill; + left = 0; + } + + /* compress blocks of data received */ + while(inlen >= 64) { + S->t[0] += 512; + + if(S->t[0] == 0) S->t[1]++; + + blake256_compress(S, in); + in += 64; + inlen -= 64; + } + + /* store any data left */ + if(inlen > 0) { + memcpy((void*)(S->buf + left), (void*)in, (size_t)inlen); + } + S->buflen = left + inlen; +} + +void blake256_Final(BLAKE256_CTX* S, uint8_t* out) { + uint8_t msglen[8] = {0}, zo = 0x01, oo = 0x81; + uint32_t lo = S->t[0] + (S->buflen << 3), hi = S->t[1]; + + /* support for hashing more than 2^32 bits */ + if(lo < (S->buflen << 3)) hi++; + + U32TO8_BIG(msglen + 0, hi); + U32TO8_BIG(msglen + 4, lo); + + if(S->buflen == 55) /* one padding byte */ + { + S->t[0] -= 8; + blake256_Update(S, &oo, 1); + } else { + if(S->buflen < 55) /* enough space to fill the block */ + { + if(!S->buflen) S->nullt = 1; + + S->t[0] -= 440 - (S->buflen << 3); + blake256_Update(S, padding, 55 - S->buflen); + } else /* need 2 compressions */ + { + S->t[0] -= 512 - (S->buflen << 3); + blake256_Update(S, padding, 64 - S->buflen); + S->t[0] -= 440; + blake256_Update(S, padding + 1, 55); + S->nullt = 1; + } + + blake256_Update(S, &zo, 1); + S->t[0] -= 8; + } + + S->t[0] -= 64; + blake256_Update(S, msglen, 8); + U32TO8_BIG(out + 0, S->h[0]); + U32TO8_BIG(out + 4, S->h[1]); + U32TO8_BIG(out + 8, S->h[2]); + U32TO8_BIG(out + 12, S->h[3]); + U32TO8_BIG(out + 16, S->h[4]); + U32TO8_BIG(out + 20, S->h[5]); + U32TO8_BIG(out + 24, S->h[6]); + U32TO8_BIG(out + 28, S->h[7]); +} + +void blake256(const uint8_t* in, size_t inlen, uint8_t* out) { + BLAKE256_CTX S = {0}; + blake256_Init(&S); + blake256_Update(&S, in, inlen); + blake256_Final(&S, out); +} diff --git a/applications/external/flipbip/lib/crypto/blake256.h b/applications/external/flipbip/lib/crypto/blake256.h new file mode 100644 index 0000000000..c02f7c0045 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/blake256.h @@ -0,0 +1,53 @@ +// Copyright (c) 2014-2017, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers + +#ifndef __BLAKE256_H__ +#define __BLAKE256_H__ + +#include +#include + +#define BLAKE256_DIGEST_LENGTH 32 +#define BLAKE256_BLOCK_LENGTH 64 + +typedef struct { + uint32_t h[8], s[4], t[2]; + size_t buflen; + uint8_t nullt; + uint8_t buf[64]; +} BLAKE256_CTX; + +void blake256_Init(BLAKE256_CTX*); +void blake256_Update(BLAKE256_CTX*, const uint8_t*, size_t); +void blake256_Final(BLAKE256_CTX*, uint8_t*); + +void blake256(const uint8_t*, size_t, uint8_t*); + +#endif /* __BLAKE256_H__ */ diff --git a/applications/external/flipbip/lib/crypto/blake2_common.h b/applications/external/flipbip/lib/crypto/blake2_common.h new file mode 100644 index 0000000000..369c7cc0f8 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/blake2_common.h @@ -0,0 +1,46 @@ + +#include "byte_order.h" + +static inline uint32_t load32(const void* src) { + uint32_t w; + memcpy(&w, src, sizeof w); +#if BYTE_ORDER == BIG_ENDIAN + REVERSE32(w, w); +#endif + return w; +} + +static inline uint64_t load64(const void* src) { + uint64_t w; + memcpy(&w, src, sizeof w); +#if BYTE_ORDER == BIG_ENDIAN + REVERSE64(w, w); +#endif + return w; +} + +static inline void store16(void* dst, uint16_t w) { + memcpy(dst, &w, sizeof w); +} + +static inline void store32(void* dst, uint32_t w) { +#if BYTE_ORDER == BIG_ENDIAN + REVERSE32(w, w); +#endif + memcpy(dst, &w, sizeof w); +} + +static inline void store64(void* dst, uint64_t w) { +#if BYTE_ORDER == BIG_ENDIAN + REVERSE64(w, w); +#endif + memcpy(dst, &w, sizeof w); +} + +static inline uint32_t rotr32(const uint32_t w, const unsigned c) { + return (w >> c) | (w << (32 - c)); +} + +static inline uint64_t rotr64(const uint64_t w, const unsigned c) { + return (w >> c) | (w << (64 - c)); +} diff --git a/applications/external/flipbip/lib/crypto/blake2b.c b/applications/external/flipbip/lib/crypto/blake2b.c new file mode 100644 index 0000000000..1f92b66f29 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/blake2b.c @@ -0,0 +1,313 @@ +/* + BLAKE2 reference source code package - reference C implementations + + Copyright 2012, Samuel Neves . You may use this under the + terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at + your option. The terms of these licenses can be found at: + + - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 + - OpenSSL license : https://www.openssl.org/source/license.html + - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 + + More information about the BLAKE2 hash function can be found at + https://blake2.net. +*/ + +#include + +#include "blake2b.h" +#include "blake2_common.h" +#include "memzero.h" + +typedef struct blake2b_param__ { + uint8_t digest_length; /* 1 */ + uint8_t key_length; /* 2 */ + uint8_t fanout; /* 3 */ + uint8_t depth; /* 4 */ + uint32_t leaf_length; /* 8 */ + uint32_t node_offset; /* 12 */ + uint32_t xof_length; /* 16 */ + uint8_t node_depth; /* 17 */ + uint8_t inner_length; /* 18 */ + uint8_t reserved[14]; /* 32 */ + uint8_t salt[BLAKE2B_SALTBYTES]; /* 48 */ + uint8_t personal[BLAKE2B_PERSONALBYTES]; /* 64 */ +} __attribute__((packed)) blake2b_param; + +static const uint64_t blake2b_IV[8] = { + 0x6a09e667f3bcc908ULL, + 0xbb67ae8584caa73bULL, + 0x3c6ef372fe94f82bULL, + 0xa54ff53a5f1d36f1ULL, + 0x510e527fade682d1ULL, + 0x9b05688c2b3e6c1fULL, + 0x1f83d9abfb41bd6bULL, + 0x5be0cd19137e2179ULL}; + +static const uint8_t blake2b_sigma[12][16] = { + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + {14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3}, + {11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4}, + {7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8}, + {9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13}, + {2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9}, + {12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11}, + {13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10}, + {6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5}, + {10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + {14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3}}; + +static void blake2b_set_lastnode(blake2b_state* S) { + S->f[1] = (uint64_t)-1; +} + +/* Some helper functions, not necessarily useful */ +static int blake2b_is_lastblock(const blake2b_state* S) { + return S->f[0] != 0; +} + +static void blake2b_set_lastblock(blake2b_state* S) { + if(S->last_node) blake2b_set_lastnode(S); + + S->f[0] = (uint64_t)-1; +} + +static void blake2b_increment_counter(blake2b_state* S, const uint64_t inc) { + S->t[0] += inc; + S->t[1] += (S->t[0] < inc); +} + +static void blake2b_init0(blake2b_state* S) { + size_t i = 0; + memzero(S, sizeof(blake2b_state)); + + for(i = 0; i < 8; ++i) S->h[i] = blake2b_IV[i]; +} + +/* init xors IV with input parameter block */ +int blake2b_init_param(blake2b_state* S, const blake2b_param* P) { + const uint8_t* p = (const uint8_t*)(P); + size_t i = 0; + + blake2b_init0(S); + + /* IV XOR ParamBlock */ + for(i = 0; i < 8; ++i) S->h[i] ^= load64(p + sizeof(S->h[i]) * i); + + S->outlen = P->digest_length; + return 0; +} + +/* Sequential blake2b initialization */ +int blake2b_Init(blake2b_state* S, size_t outlen) { + blake2b_param P[1] = {0}; + + if((!outlen) || (outlen > BLAKE2B_OUTBYTES)) return -1; + + P->digest_length = (uint8_t)outlen; + P->key_length = 0; + P->fanout = 1; + P->depth = 1; + store32(&P->leaf_length, 0); + store32(&P->node_offset, 0); + store32(&P->xof_length, 0); + P->node_depth = 0; + P->inner_length = 0; + memzero(P->reserved, sizeof(P->reserved)); + memzero(P->salt, sizeof(P->salt)); + memzero(P->personal, sizeof(P->personal)); + return blake2b_init_param(S, P); +} + +int blake2b_InitPersonal( + blake2b_state* S, + size_t outlen, + const void* personal, + size_t personal_len) { + blake2b_param P[1] = {0}; + + if((!outlen) || (outlen > BLAKE2B_OUTBYTES)) return -1; + if((!personal) || (personal_len != BLAKE2B_PERSONALBYTES)) return -1; + + P->digest_length = (uint8_t)outlen; + P->key_length = 0; + P->fanout = 1; + P->depth = 1; + store32(&P->leaf_length, 0); + store32(&P->node_offset, 0); + store32(&P->xof_length, 0); + P->node_depth = 0; + P->inner_length = 0; + memzero(P->reserved, sizeof(P->reserved)); + memzero(P->salt, sizeof(P->salt)); + memcpy(P->personal, personal, BLAKE2B_PERSONALBYTES); + return blake2b_init_param(S, P); +} + +int blake2b_InitKey(blake2b_state* S, size_t outlen, const void* key, size_t keylen) { + blake2b_param P[1] = {0}; + + if((!outlen) || (outlen > BLAKE2B_OUTBYTES)) return -1; + + if(!key || !keylen || keylen > BLAKE2B_KEYBYTES) return -1; + + P->digest_length = (uint8_t)outlen; + P->key_length = (uint8_t)keylen; + P->fanout = 1; + P->depth = 1; + store32(&P->leaf_length, 0); + store32(&P->node_offset, 0); + store32(&P->xof_length, 0); + P->node_depth = 0; + P->inner_length = 0; + memzero(P->reserved, sizeof(P->reserved)); + memzero(P->salt, sizeof(P->salt)); + memzero(P->personal, sizeof(P->personal)); + + if(blake2b_init_param(S, P) < 0) return -1; + + { + uint8_t block[BLAKE2B_BLOCKBYTES] = {0}; + memzero(block, BLAKE2B_BLOCKBYTES); + memcpy(block, key, keylen); + blake2b_Update(S, block, BLAKE2B_BLOCKBYTES); + memzero(block, BLAKE2B_BLOCKBYTES); /* Burn the key from stack */ + } + return 0; +} + +#define G(r, i, a, b, c, d) \ + do { \ + a = a + b + m[blake2b_sigma[r][2 * i + 0]]; \ + d = rotr64(d ^ a, 32); \ + c = c + d; \ + b = rotr64(b ^ c, 24); \ + a = a + b + m[blake2b_sigma[r][2 * i + 1]]; \ + d = rotr64(d ^ a, 16); \ + c = c + d; \ + b = rotr64(b ^ c, 63); \ + } while(0) + +#define ROUND(r) \ + do { \ + G(r, 0, v[0], v[4], v[8], v[12]); \ + G(r, 1, v[1], v[5], v[9], v[13]); \ + G(r, 2, v[2], v[6], v[10], v[14]); \ + G(r, 3, v[3], v[7], v[11], v[15]); \ + G(r, 4, v[0], v[5], v[10], v[15]); \ + G(r, 5, v[1], v[6], v[11], v[12]); \ + G(r, 6, v[2], v[7], v[8], v[13]); \ + G(r, 7, v[3], v[4], v[9], v[14]); \ + } while(0) + +static void blake2b_compress(blake2b_state* S, const uint8_t block[BLAKE2B_BLOCKBYTES]) { + uint64_t m[16] = {0}; + uint64_t v[16] = {0}; + size_t i = 0; + + for(i = 0; i < 16; ++i) { + m[i] = load64(block + i * sizeof(m[i])); + } + + for(i = 0; i < 8; ++i) { + v[i] = S->h[i]; + } + + v[8] = blake2b_IV[0]; + v[9] = blake2b_IV[1]; + v[10] = blake2b_IV[2]; + v[11] = blake2b_IV[3]; + v[12] = blake2b_IV[4] ^ S->t[0]; + v[13] = blake2b_IV[5] ^ S->t[1]; + v[14] = blake2b_IV[6] ^ S->f[0]; + v[15] = blake2b_IV[7] ^ S->f[1]; + + ROUND(0); + ROUND(1); + ROUND(2); + ROUND(3); + ROUND(4); + ROUND(5); + ROUND(6); + ROUND(7); + ROUND(8); + ROUND(9); + ROUND(10); + ROUND(11); + + for(i = 0; i < 8; ++i) { + S->h[i] = S->h[i] ^ v[i] ^ v[i + 8]; + } +} + +#undef G +#undef ROUND + +int blake2b_Update(blake2b_state* S, const void* pin, size_t inlen) { + const unsigned char* in = (const unsigned char*)pin; + if(inlen > 0) { + size_t left = S->buflen; + size_t fill = BLAKE2B_BLOCKBYTES - left; + if(inlen > fill) { + S->buflen = 0; + memcpy(S->buf + left, in, fill); /* Fill buffer */ + blake2b_increment_counter(S, BLAKE2B_BLOCKBYTES); + blake2b_compress(S, S->buf); /* Compress */ + in += fill; + inlen -= fill; + while(inlen > BLAKE2B_BLOCKBYTES) { + blake2b_increment_counter(S, BLAKE2B_BLOCKBYTES); + blake2b_compress(S, in); + in += BLAKE2B_BLOCKBYTES; + inlen -= BLAKE2B_BLOCKBYTES; + } + } + memcpy(S->buf + S->buflen, in, inlen); + S->buflen += inlen; + } + return 0; +} + +int blake2b_Final(blake2b_state* S, void* out, size_t outlen) { + uint8_t buffer[BLAKE2B_OUTBYTES] = {0}; + size_t i = 0; + + if(out == NULL || outlen < S->outlen) return -1; + + if(blake2b_is_lastblock(S)) return -1; + + blake2b_increment_counter(S, S->buflen); + blake2b_set_lastblock(S); + memzero(S->buf + S->buflen, BLAKE2B_BLOCKBYTES - S->buflen); /* Padding */ + blake2b_compress(S, S->buf); + + for(i = 0; i < 8; ++i) /* Output full hash to temp buffer */ + store64(buffer + sizeof(S->h[i]) * i, S->h[i]); + + memcpy(out, buffer, S->outlen); + memzero(buffer, sizeof(buffer)); + return 0; +} + +int blake2b(const uint8_t* msg, uint32_t msg_len, void* out, size_t outlen) { + BLAKE2B_CTX ctx; + if(0 != blake2b_Init(&ctx, outlen)) return -1; + if(0 != blake2b_Update(&ctx, msg, msg_len)) return -1; + if(0 != blake2b_Final(&ctx, out, outlen)) return -1; + return 0; +} + +int blake2b_Key( + const uint8_t* msg, + uint32_t msg_len, + const void* key, + size_t keylen, + void* out, + size_t outlen) { + BLAKE2B_CTX ctx; + if(0 != blake2b_InitKey(&ctx, outlen, key, keylen)) return -1; + if(0 != blake2b_Update(&ctx, msg, msg_len)) return -1; + if(0 != blake2b_Final(&ctx, out, outlen)) return -1; + return 0; +} diff --git a/applications/external/flipbip/lib/crypto/blake2b.h b/applications/external/flipbip/lib/crypto/blake2b.h new file mode 100644 index 0000000000..5a47a912c8 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/blake2b.h @@ -0,0 +1,49 @@ +#ifndef __BLAKE2B_H__ +#define __BLAKE2B_H__ + +#include +#include + +enum blake2b_constant { + BLAKE2B_BLOCKBYTES = 128, + BLAKE2B_OUTBYTES = 64, + BLAKE2B_KEYBYTES = 64, + BLAKE2B_SALTBYTES = 16, + BLAKE2B_PERSONALBYTES = 16 +}; + +typedef struct __blake2b_state { + uint64_t h[8]; + uint64_t t[2]; + uint64_t f[2]; + uint8_t buf[BLAKE2B_BLOCKBYTES]; + size_t buflen; + size_t outlen; + uint8_t last_node; +} blake2b_state; + +#define BLAKE2B_CTX blake2b_state +#define BLAKE2B_BLOCK_LENGTH BLAKE2B_BLOCKBYTES +#define BLAKE2B_DIGEST_LENGTH BLAKE2B_OUTBYTES +#define BLAKE2B_KEY_LENGTH BLAKE2B_KEYBYTES + +int blake2b_Init(blake2b_state* S, size_t outlen); +int blake2b_InitKey(blake2b_state* S, size_t outlen, const void* key, size_t keylen); +int blake2b_InitPersonal( + blake2b_state* S, + size_t outlen, + const void* personal, + size_t personal_len); +int blake2b_Update(blake2b_state* S, const void* pin, size_t inlen); +int blake2b_Final(blake2b_state* S, void* out, size_t outlen); + +int blake2b(const uint8_t* msg, uint32_t msg_len, void* out, size_t outlen); +int blake2b_Key( + const uint8_t* msg, + uint32_t msg_len, + const void* key, + size_t keylen, + void* out, + size_t outlen); + +#endif diff --git a/applications/external/flipbip/lib/crypto/blake2s.c b/applications/external/flipbip/lib/crypto/blake2s.c new file mode 100644 index 0000000000..0e75ef7c6c --- /dev/null +++ b/applications/external/flipbip/lib/crypto/blake2s.c @@ -0,0 +1,310 @@ +/* + BLAKE2 reference source code package - reference C implementations + + Copyright 2012, Samuel Neves . You may use this under the + terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at + your option. The terms of these licenses can be found at: + + - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 + - OpenSSL license : https://www.openssl.org/source/license.html + - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 + + More information about the BLAKE2 hash function can be found at + https://blake2.net. +*/ + +#include + +#include "blake2s.h" +#include "blake2_common.h" +#include "memzero.h" + +typedef struct blake2s_param__ { + uint8_t digest_length; /* 1 */ + uint8_t key_length; /* 2 */ + uint8_t fanout; /* 3 */ + uint8_t depth; /* 4 */ + uint32_t leaf_length; /* 8 */ + uint32_t node_offset; /* 12 */ + uint16_t xof_length; /* 14 */ + uint8_t node_depth; /* 15 */ + uint8_t inner_length; /* 16 */ + /* uint8_t reserved[0]; */ + uint8_t salt[BLAKE2S_SALTBYTES]; /* 24 */ + uint8_t personal[BLAKE2S_PERSONALBYTES]; /* 32 */ +} __attribute__((packed)) blake2s_param; + +static const uint32_t blake2s_IV[8] = { + 0x6A09E667UL, + 0xBB67AE85UL, + 0x3C6EF372UL, + 0xA54FF53AUL, + 0x510E527FUL, + 0x9B05688CUL, + 0x1F83D9ABUL, + 0x5BE0CD19UL}; + +static const uint8_t blake2s_sigma[10][16] = { + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + {14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3}, + {11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4}, + {7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8}, + {9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13}, + {2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9}, + {12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11}, + {13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10}, + {6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5}, + {10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0}, +}; + +static void blake2s_set_lastnode(blake2s_state* S) { + S->f[1] = (uint32_t)-1; +} + +/* Some helper functions, not necessarily useful */ +static int blake2s_is_lastblock(const blake2s_state* S) { + return S->f[0] != 0; +} + +static void blake2s_set_lastblock(blake2s_state* S) { + if(S->last_node) blake2s_set_lastnode(S); + + S->f[0] = (uint32_t)-1; +} + +static void blake2s_increment_counter(blake2s_state* S, const uint32_t inc) { + S->t[0] += inc; + S->t[1] += (S->t[0] < inc); +} + +static void blake2s_init0(blake2s_state* S) { + size_t i = 0; + memzero(S, sizeof(blake2s_state)); + + for(i = 0; i < 8; ++i) S->h[i] = blake2s_IV[i]; +} + +/* init2 xors IV with input parameter block */ +int blake2s_init_param(blake2s_state* S, const blake2s_param* P) { + const unsigned char* p = (const unsigned char*)(P); + size_t i = 0; + + blake2s_init0(S); + + /* IV XOR ParamBlock */ + for(i = 0; i < 8; ++i) S->h[i] ^= load32(&p[i * 4]); + + S->outlen = P->digest_length; + return 0; +} + +/* Sequential blake2s initialization */ +int blake2s_Init(blake2s_state* S, size_t outlen) { + blake2s_param P[1] = {0}; + + if((!outlen) || (outlen > BLAKE2S_OUTBYTES)) return -1; + + P->digest_length = (uint8_t)outlen; + P->key_length = 0; + P->fanout = 1; + P->depth = 1; + store32(&P->leaf_length, 0); + store32(&P->node_offset, 0); + store16(&P->xof_length, 0); + P->node_depth = 0; + P->inner_length = 0; + /* memzero(P->reserved, sizeof(P->reserved) ); */ + memzero(P->salt, sizeof(P->salt)); + memzero(P->personal, sizeof(P->personal)); + return blake2s_init_param(S, P); +} + +int blake2s_InitPersonal( + blake2s_state* S, + size_t outlen, + const void* personal, + size_t personal_len) { + blake2s_param P[1] = {0}; + + if((!outlen) || (outlen > BLAKE2S_OUTBYTES)) return -1; + if((!personal) || (personal_len != BLAKE2S_PERSONALBYTES)) return -1; + + P->digest_length = (uint8_t)outlen; + P->key_length = 0; + P->fanout = 1; + P->depth = 1; + store32(&P->leaf_length, 0); + store32(&P->node_offset, 0); + store16(&P->xof_length, 0); + P->node_depth = 0; + P->inner_length = 0; + /* memzero(P->reserved, sizeof(P->reserved) ); */ + memzero(P->salt, sizeof(P->salt)); + memcpy(P->personal, personal, BLAKE2S_PERSONALBYTES); + return blake2s_init_param(S, P); +} + +int blake2s_InitKey(blake2s_state* S, size_t outlen, const void* key, size_t keylen) { + blake2s_param P[1] = {0}; + + if((!outlen) || (outlen > BLAKE2S_OUTBYTES)) return -1; + + if(!key || !keylen || keylen > BLAKE2S_KEYBYTES) return -1; + + P->digest_length = (uint8_t)outlen; + P->key_length = (uint8_t)keylen; + P->fanout = 1; + P->depth = 1; + store32(&P->leaf_length, 0); + store32(&P->node_offset, 0); + store16(&P->xof_length, 0); + P->node_depth = 0; + P->inner_length = 0; + /* memzero(P->reserved, sizeof(P->reserved) ); */ + memzero(P->salt, sizeof(P->salt)); + memzero(P->personal, sizeof(P->personal)); + + if(blake2s_init_param(S, P) < 0) return -1; + + { + uint8_t block[BLAKE2S_BLOCKBYTES] = {0}; + memzero(block, BLAKE2S_BLOCKBYTES); + memcpy(block, key, keylen); + blake2s_Update(S, block, BLAKE2S_BLOCKBYTES); + memzero(block, BLAKE2S_BLOCKBYTES); /* Burn the key from stack */ + } + return 0; +} + +#define G(r, i, a, b, c, d) \ + do { \ + a = a + b + m[blake2s_sigma[r][2 * i + 0]]; \ + d = rotr32(d ^ a, 16); \ + c = c + d; \ + b = rotr32(b ^ c, 12); \ + a = a + b + m[blake2s_sigma[r][2 * i + 1]]; \ + d = rotr32(d ^ a, 8); \ + c = c + d; \ + b = rotr32(b ^ c, 7); \ + } while(0) + +#define ROUND(r) \ + do { \ + G(r, 0, v[0], v[4], v[8], v[12]); \ + G(r, 1, v[1], v[5], v[9], v[13]); \ + G(r, 2, v[2], v[6], v[10], v[14]); \ + G(r, 3, v[3], v[7], v[11], v[15]); \ + G(r, 4, v[0], v[5], v[10], v[15]); \ + G(r, 5, v[1], v[6], v[11], v[12]); \ + G(r, 6, v[2], v[7], v[8], v[13]); \ + G(r, 7, v[3], v[4], v[9], v[14]); \ + } while(0) + +static void blake2s_compress(blake2s_state* S, const uint8_t in[BLAKE2S_BLOCKBYTES]) { + uint32_t m[16] = {0}; + uint32_t v[16] = {0}; + size_t i = 0; + + for(i = 0; i < 16; ++i) { + m[i] = load32(in + i * sizeof(m[i])); + } + + for(i = 0; i < 8; ++i) { + v[i] = S->h[i]; + } + + v[8] = blake2s_IV[0]; + v[9] = blake2s_IV[1]; + v[10] = blake2s_IV[2]; + v[11] = blake2s_IV[3]; + v[12] = S->t[0] ^ blake2s_IV[4]; + v[13] = S->t[1] ^ blake2s_IV[5]; + v[14] = S->f[0] ^ blake2s_IV[6]; + v[15] = S->f[1] ^ blake2s_IV[7]; + + ROUND(0); + ROUND(1); + ROUND(2); + ROUND(3); + ROUND(4); + ROUND(5); + ROUND(6); + ROUND(7); + ROUND(8); + ROUND(9); + + for(i = 0; i < 8; ++i) { + S->h[i] = S->h[i] ^ v[i] ^ v[i + 8]; + } +} + +#undef G +#undef ROUND + +int blake2s_Update(blake2s_state* S, const void* pin, size_t inlen) { + const unsigned char* in = (const unsigned char*)pin; + if(inlen > 0) { + size_t left = S->buflen; + size_t fill = BLAKE2S_BLOCKBYTES - left; + if(inlen > fill) { + S->buflen = 0; + memcpy(S->buf + left, in, fill); /* Fill buffer */ + blake2s_increment_counter(S, BLAKE2S_BLOCKBYTES); + blake2s_compress(S, S->buf); /* Compress */ + in += fill; + inlen -= fill; + while(inlen > BLAKE2S_BLOCKBYTES) { + blake2s_increment_counter(S, BLAKE2S_BLOCKBYTES); + blake2s_compress(S, in); + in += BLAKE2S_BLOCKBYTES; + inlen -= BLAKE2S_BLOCKBYTES; + } + } + memcpy(S->buf + S->buflen, in, inlen); + S->buflen += inlen; + } + return 0; +} + +int blake2s_Final(blake2s_state* S, void* out, size_t outlen) { + uint8_t buffer[BLAKE2S_OUTBYTES] = {0}; + size_t i = 0; + + if(out == NULL || outlen < S->outlen) return -1; + + if(blake2s_is_lastblock(S)) return -1; + + blake2s_increment_counter(S, (uint32_t)S->buflen); + blake2s_set_lastblock(S); + memzero(S->buf + S->buflen, BLAKE2S_BLOCKBYTES - S->buflen); /* Padding */ + blake2s_compress(S, S->buf); + + for(i = 0; i < 8; ++i) /* Output full hash to temp buffer */ + store32(buffer + sizeof(S->h[i]) * i, S->h[i]); + + memcpy(out, buffer, outlen); + memzero(buffer, sizeof(buffer)); + return 0; +} + +int blake2s(const uint8_t* msg, uint32_t msg_len, void* out, size_t outlen) { + BLAKE2S_CTX ctx; + if(0 != blake2s_Init(&ctx, outlen)) return -1; + if(0 != blake2s_Update(&ctx, msg, msg_len)) return -1; + if(0 != blake2s_Final(&ctx, out, outlen)) return -1; + return 0; +} + +int blake2s_Key( + const uint8_t* msg, + uint32_t msg_len, + const void* key, + size_t keylen, + void* out, + size_t outlen) { + BLAKE2S_CTX ctx; + if(0 != blake2s_InitKey(&ctx, outlen, key, keylen)) return -1; + if(0 != blake2s_Update(&ctx, msg, msg_len)) return -1; + if(0 != blake2s_Final(&ctx, out, outlen)) return -1; + return 0; +} diff --git a/applications/external/flipbip/lib/crypto/blake2s.h b/applications/external/flipbip/lib/crypto/blake2s.h new file mode 100644 index 0000000000..452090693d --- /dev/null +++ b/applications/external/flipbip/lib/crypto/blake2s.h @@ -0,0 +1,49 @@ +#ifndef __BLAKE2S_H__ +#define __BLAKE2S_H__ + +#include +#include + +enum blake2s_constant { + BLAKE2S_BLOCKBYTES = 64, + BLAKE2S_OUTBYTES = 32, + BLAKE2S_KEYBYTES = 32, + BLAKE2S_SALTBYTES = 8, + BLAKE2S_PERSONALBYTES = 8 +}; + +typedef struct __blake2s_state { + uint32_t h[8]; + uint32_t t[2]; + uint32_t f[2]; + uint8_t buf[BLAKE2S_BLOCKBYTES]; + uint32_t buflen; + uint8_t outlen; + uint8_t last_node; +} blake2s_state; + +#define BLAKE2S_CTX blake2s_state +#define BLAKE2S_BLOCK_LENGTH BLAKE2S_BLOCKBYTES +#define BLAKE2S_DIGEST_LENGTH BLAKE2S_OUTBYTES +#define BLAKE2S_KEY_LENGTH BLAKE2S_KEYBYTES + +int blake2s_Init(blake2s_state* S, size_t outlen); +int blake2s_InitKey(blake2s_state* S, size_t outlen, const void* key, size_t keylen); +int blake2s_InitPersonal( + blake2s_state* S, + size_t outlen, + const void* personal, + size_t personal_len); +int blake2s_Update(blake2s_state* S, const void* pin, size_t inlen); +int blake2s_Final(blake2s_state* S, void* out, size_t outlen); + +int blake2s(const uint8_t* msg, uint32_t msg_len, void* out, size_t outlen); +int blake2s_Key( + const uint8_t* msg, + uint32_t msg_len, + const void* key, + size_t keylen, + void* out, + size_t outlen); + +#endif diff --git a/applications/external/flipbip/lib/crypto/byte_order.h b/applications/external/flipbip/lib/crypto/byte_order.h new file mode 100644 index 0000000000..8484f43496 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/byte_order.h @@ -0,0 +1,57 @@ +#ifndef __BYTE_ORDER_H__ +#define __BYTE_ORDER_H__ + +// FROM sha2.h: +/* + * BYTE_ORDER NOTE: + * + * Please make sure that your system defines BYTE_ORDER. If your + * architecture is little-endian, make sure it also defines + * LITTLE_ENDIAN and that the two (BYTE_ORDER and LITTLE_ENDIAN) are + * equivalent. + * + * If your system does not define the above, then you can do so by + * hand like this: + * + * #define LITTLE_ENDIAN 1234 + * #define BIG_ENDIAN 4321 + * + * And for little-endian machines, add: + * + * #define BYTE_ORDER LITTLE_ENDIAN + * + * Or for big-endian machines: + * + * #define BYTE_ORDER BIG_ENDIAN + * + * The FreeBSD machine this was written on defines BYTE_ORDER + * appropriately by including (which in turn includes + * where the appropriate definitions are actually + * made). + */ + +#ifndef LITTLE_ENDIAN +#define LITTLE_ENDIAN 1234 +#define BIG_ENDIAN 4321 +#endif + +#ifndef BYTE_ORDER +#define BYTE_ORDER LITTLE_ENDIAN +#endif + +#define REVERSE32(w, x) \ + { \ + uint32_t tmp = (w); \ + tmp = (tmp >> 16) | (tmp << 16); \ + (x) = ((tmp & 0xff00ff00UL) >> 8) | ((tmp & 0x00ff00ffUL) << 8); \ + } + +#define REVERSE64(w, x) \ + { \ + uint64_t tmp = (w); \ + tmp = (tmp >> 32) | (tmp << 32); \ + tmp = ((tmp & 0xff00ff00ff00ff00ULL) >> 8) | ((tmp & 0x00ff00ff00ff00ffULL) << 8); \ + (x) = ((tmp & 0xffff0000ffff0000ULL) >> 16) | ((tmp & 0x0000ffff0000ffffULL) << 16); \ + } + +#endif diff --git a/applications/external/flipbip/lib/crypto/cardano.c b/applications/external/flipbip/lib/crypto/cardano.c new file mode 100644 index 0000000000..8542799a78 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/cardano.c @@ -0,0 +1,307 @@ +/** + * Copyright (c) 2013-2021 SatoshiLabs + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include + +#include "bignum.h" +#include "bip32.h" +#include "cardano.h" +#include "curves.h" +#include "hasher.h" +#include "hmac.h" +#include "memzero.h" +#include "options.h" +#include "pbkdf2.h" +#include "sha2.h" + +#if USE_CARDANO + +#define CARDANO_MAX_NODE_DEPTH 1048576 + +const curve_info ed25519_cardano_info = { + .bip32_name = ED25519_CARDANO_NAME, + .params = NULL, + .hasher_base58 = HASHER_SHA2D, + .hasher_sign = HASHER_SHA2D, + .hasher_pubkey = HASHER_SHA2_RIPEMD, + .hasher_script = HASHER_SHA2, +}; + +static void scalar_multiply8(const uint8_t* src, int bytes, uint8_t* dst) { + uint8_t prev_acc = 0; + for(int i = 0; i < bytes; i++) { + dst[i] = (src[i] << 3) + (prev_acc & 0x7); + prev_acc = src[i] >> 5; + } + dst[bytes] = src[bytes - 1] >> 5; +} + +static void scalar_add_256bits(const uint8_t* src1, const uint8_t* src2, uint8_t* dst) { + uint16_t r = 0; + for(int i = 0; i < 32; i++) { + r = r + (uint16_t)src1[i] + (uint16_t)src2[i]; + dst[i] = r & 0xff; + r >>= 8; + } +} + +static void cardano_ed25519_tweak_bits(uint8_t private_key[32]) { + private_key[0] &= 0xf8; + private_key[31] &= 0x1f; + private_key[31] |= 0x40; +} + +int hdnode_private_ckd_cardano(HDNode* inout, uint32_t index) { + if(inout->curve != &ed25519_cardano_info) { + return 0; + } + + if(inout->depth >= CARDANO_MAX_NODE_DEPTH) { + return 0; + } + + // checks for hardened/non-hardened derivation, keysize 32 means we are + // dealing with public key and thus non-h, keysize 64 is for private key + int keysize = 32; + if(index & 0x80000000) { + keysize = 64; + } + + static CONFIDENTIAL uint8_t data[1 + 64 + 4]; + static CONFIDENTIAL uint8_t z[32 + 32]; + static CONFIDENTIAL uint8_t priv_key[64]; + static CONFIDENTIAL uint8_t res_key[64]; + + write_le(data + keysize + 1, index); + + memcpy(priv_key, inout->private_key, 32); + memcpy(priv_key + 32, inout->private_key_extension, 32); + + if(keysize == 64) { // private derivation + data[0] = 0; + memcpy(data + 1, inout->private_key, 32); + memcpy(data + 1 + 32, inout->private_key_extension, 32); + } else { // public derivation + if(hdnode_fill_public_key(inout) != 0) { + return 0; + } + data[0] = 2; + memcpy(data + 1, inout->public_key + 1, 32); + } + + static CONFIDENTIAL HMAC_SHA512_CTX ctx; + hmac_sha512_Init(&ctx, inout->chain_code, 32); + hmac_sha512_Update(&ctx, data, 1 + keysize + 4); + hmac_sha512_Final(&ctx, z); + + static CONFIDENTIAL uint8_t zl8[32]; + memzero(zl8, 32); + + /* get 8 * Zl */ + scalar_multiply8(z, 28, zl8); + /* Kl = 8*Zl + parent(K)l */ + scalar_add_256bits(zl8, priv_key, res_key); + + /* Kr = Zr + parent(K)r */ + scalar_add_256bits(z + 32, priv_key + 32, res_key + 32); + + memcpy(inout->private_key, res_key, 32); + memcpy(inout->private_key_extension, res_key + 32, 32); + + if(keysize == 64) { + data[0] = 1; + } else { + data[0] = 3; + } + hmac_sha512_Init(&ctx, inout->chain_code, 32); + hmac_sha512_Update(&ctx, data, 1 + keysize + 4); + hmac_sha512_Final(&ctx, z); + + memcpy(inout->chain_code, z + 32, 32); + inout->depth++; + inout->child_num = index; + memzero(inout->public_key, sizeof(inout->public_key)); + + // making sure to wipe our memory + memzero(z, sizeof(z)); + memzero(data, sizeof(data)); + memzero(priv_key, sizeof(priv_key)); + memzero(res_key, sizeof(res_key)); + return 1; +} + +int hdnode_from_secret_cardano(const uint8_t secret[CARDANO_SECRET_LENGTH], HDNode* out) { + memzero(out, sizeof(HDNode)); + out->depth = 0; + out->child_num = 0; + out->curve = &ed25519_cardano_info; + memcpy(out->private_key, secret, 32); + memcpy(out->private_key_extension, secret + 32, 32); + memcpy(out->chain_code, secret + 64, 32); + + cardano_ed25519_tweak_bits(out->private_key); + + out->public_key[0] = 0; + if(hdnode_fill_public_key(out) != 0) { + return 0; + } + + return 1; +} + +// Derives the root Cardano secret from a master secret, aka seed, as defined in +// SLIP-0023. +int secret_from_seed_cardano_slip23( + const uint8_t* seed, + int seed_len, + uint8_t secret_out[CARDANO_SECRET_LENGTH]) { + static CONFIDENTIAL uint8_t I[SHA512_DIGEST_LENGTH]; + static CONFIDENTIAL HMAC_SHA512_CTX ctx; + + hmac_sha512_Init(&ctx, (const uint8_t*)ED25519_CARDANO_NAME, strlen(ED25519_CARDANO_NAME)); + hmac_sha512_Update(&ctx, seed, seed_len); + hmac_sha512_Final(&ctx, I); + + sha512_Raw(I, 32, secret_out); + + memcpy(secret_out + SHA512_DIGEST_LENGTH, I + 32, 32); + cardano_ed25519_tweak_bits(secret_out); + + memzero(I, sizeof(I)); + memzero(&ctx, sizeof(ctx)); + return 1; +} + +// Derives the root Cardano secret from a BIP-32 master secret via the Ledger +// derivation: +// https://github.com/cardano-foundation/CIPs/blob/09d7d8ee1bd64f7e6b20b5a6cae088039dce00cb/CIP-0003/Ledger.md +int secret_from_seed_cardano_ledger( + const uint8_t* seed, + int seed_len, + uint8_t secret_out[CARDANO_SECRET_LENGTH]) { + static CONFIDENTIAL uint8_t chain_code[SHA256_DIGEST_LENGTH]; + static CONFIDENTIAL uint8_t root_key[SHA512_DIGEST_LENGTH]; + static CONFIDENTIAL HMAC_SHA256_CTX ctx; + static CONFIDENTIAL HMAC_SHA512_CTX sctx; + + const uint8_t* intermediate_result = seed; + int intermediate_result_len = seed_len; + do { + // STEP 1: derive a master secret like in BIP-32/SLIP-10 + hmac_sha512_Init(&sctx, (const uint8_t*)ED25519_SEED_NAME, strlen(ED25519_SEED_NAME)); + hmac_sha512_Update(&sctx, intermediate_result, intermediate_result_len); + hmac_sha512_Final(&sctx, root_key); + + // STEP 2: check that the resulting key does not have a particular bit set, + // otherwise iterate like in SLIP-10 + intermediate_result = root_key; + intermediate_result_len = sizeof(root_key); + } while(root_key[31] & 0x20); + + // STEP 3: calculate the chain code as a HMAC-SHA256 of "\x01" + seed, + // key is "ed25519 seed" + hmac_sha256_Init(&ctx, (const unsigned char*)ED25519_SEED_NAME, strlen(ED25519_SEED_NAME)); + hmac_sha256_Update(&ctx, (const unsigned char*)"\x01", 1); + hmac_sha256_Update(&ctx, seed, seed_len); + hmac_sha256_Final(&ctx, chain_code); + + // STEP 4: extract information into output + _Static_assert( + SHA512_DIGEST_LENGTH + SHA256_DIGEST_LENGTH == CARDANO_SECRET_LENGTH, + "Invalid configuration of Cardano secret size"); + memcpy(secret_out, root_key, SHA512_DIGEST_LENGTH); + memcpy(secret_out + SHA512_DIGEST_LENGTH, chain_code, SHA256_DIGEST_LENGTH); + + // STEP 5: tweak bits of the private key + cardano_ed25519_tweak_bits(secret_out); + + memzero(&ctx, sizeof(ctx)); + memzero(&sctx, sizeof(sctx)); + memzero(root_key, sizeof(root_key)); + memzero(chain_code, sizeof(chain_code)); + return 1; +} + +#define CARDANO_ICARUS_STEPS 32 +_Static_assert( + CARDANO_ICARUS_PBKDF2_ROUNDS % CARDANO_ICARUS_STEPS == 0, + "CARDANO_ICARUS_STEPS does not divide CARDANO_ICARUS_PBKDF2_ROUNDS"); +#define CARDANO_ICARUS_ROUNDS_PER_STEP (CARDANO_ICARUS_PBKDF2_ROUNDS / CARDANO_ICARUS_STEPS) + +// Derives the root Cardano HDNode from a passphrase and the entropy encoded in +// a BIP-0039 mnemonic using the Icarus derivation scheme, aka V2 derivation +// scheme: +// https://github.com/cardano-foundation/CIPs/blob/09d7d8ee1bd64f7e6b20b5a6cae088039dce00cb/CIP-0003/Icarus.md +int secret_from_entropy_cardano_icarus( + const uint8_t* pass, + int pass_len, + const uint8_t* entropy, + int entropy_len, + uint8_t secret_out[CARDANO_SECRET_LENGTH], + void (*progress_callback)(uint32_t, uint32_t)) { + static CONFIDENTIAL PBKDF2_HMAC_SHA512_CTX pctx; + static CONFIDENTIAL uint8_t digest[SHA512_DIGEST_LENGTH]; + uint32_t progress = 0; + + // PASS 1: first 64 bytes + pbkdf2_hmac_sha512_Init(&pctx, pass, pass_len, entropy, entropy_len, 1); + if(progress_callback) { + progress_callback(progress, CARDANO_ICARUS_PBKDF2_ROUNDS * 2); + } + for(int i = 0; i < CARDANO_ICARUS_STEPS; i++) { + pbkdf2_hmac_sha512_Update(&pctx, CARDANO_ICARUS_ROUNDS_PER_STEP); + if(progress_callback) { + progress += CARDANO_ICARUS_ROUNDS_PER_STEP; + progress_callback(progress, CARDANO_ICARUS_PBKDF2_ROUNDS * 2); + } + } + pbkdf2_hmac_sha512_Final(&pctx, digest); + + memcpy(secret_out, digest, SHA512_DIGEST_LENGTH); + + // PASS 2: remaining 32 bytes + pbkdf2_hmac_sha512_Init(&pctx, pass, pass_len, entropy, entropy_len, 2); + if(progress_callback) { + progress_callback(progress, CARDANO_ICARUS_PBKDF2_ROUNDS * 2); + } + for(int i = 0; i < CARDANO_ICARUS_STEPS; i++) { + pbkdf2_hmac_sha512_Update(&pctx, CARDANO_ICARUS_ROUNDS_PER_STEP); + if(progress_callback) { + progress += CARDANO_ICARUS_ROUNDS_PER_STEP; + progress_callback(progress, CARDANO_ICARUS_PBKDF2_ROUNDS * 2); + } + } + pbkdf2_hmac_sha512_Final(&pctx, digest); + + memcpy( + secret_out + SHA512_DIGEST_LENGTH, digest, CARDANO_SECRET_LENGTH - SHA512_DIGEST_LENGTH); + + cardano_ed25519_tweak_bits(secret_out); + + memzero(&pctx, sizeof(pctx)); + memzero(digest, sizeof(digest)); + return 1; +} + +#endif // USE_CARDANO diff --git a/applications/external/flipbip/lib/crypto/cardano.h b/applications/external/flipbip/lib/crypto/cardano.h new file mode 100644 index 0000000000..761ce978cc --- /dev/null +++ b/applications/external/flipbip/lib/crypto/cardano.h @@ -0,0 +1,60 @@ +/** + * Copyright (c) 2013-2021 SatoshiLabs + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __CARDANO_H__ +#define __CARDANO_H__ + +#include +#include +#include "bip32.h" +#include "options.h" + +#if USE_CARDANO + +#define CARDANO_SECRET_LENGTH 96 +#define CARDANO_ICARUS_PBKDF2_ROUNDS 4096 + +extern const curve_info ed25519_cardano_info; + +int hdnode_private_ckd_cardano(HDNode* inout, uint32_t i); + +int secret_from_entropy_cardano_icarus( + const uint8_t* pass, + int pass_len, + const uint8_t* entropy, + int entropy_len, + uint8_t secret_out[CARDANO_SECRET_LENGTH], + void (*progress_callback)(uint32_t current, uint32_t total)); +int secret_from_seed_cardano_ledger( + const uint8_t* seed, + int seed_len, + uint8_t secret_out[CARDANO_SECRET_LENGTH]); +int secret_from_seed_cardano_slip23( + const uint8_t* seed, + int seed_len, + uint8_t secret_out[CARDANO_SECRET_LENGTH]); + +int hdnode_from_secret_cardano(const uint8_t secret[CARDANO_SECRET_LENGTH], HDNode* out); + +#endif // USE_CARDANO + +#endif // __CARDANO_H__ diff --git a/applications/external/flipbip/lib/crypto/cash_addr.c b/applications/external/flipbip/lib/crypto/cash_addr.c new file mode 100644 index 0000000000..31656c8887 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/cash_addr.c @@ -0,0 +1,188 @@ +/* Copyright (c) 2017 Jochen Hoenicke + * based on code Copyright (c) 2017 Peter Wuille + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include +#include +#include + +#include "cash_addr.h" + +#define MAX_CASHADDR_SIZE 129 +#define MAX_BASE32_SIZE 104 +#define MAX_DATA_SIZE 65 +#define MAX_HRP_SIZE 20 +#define CHECKSUM_SIZE 8 + +uint64_t cashaddr_polymod_step(uint64_t pre) { + uint8_t b = pre >> 35; + return ((pre & 0x7FFFFFFFFULL) << 5) ^ (-((b >> 0) & 1) & 0x98f2bc8e61ULL) ^ + (-((b >> 1) & 1) & 0x79b76d99e2ULL) ^ (-((b >> 2) & 1) & 0xf33e5fb3c4ULL) ^ + (-((b >> 3) & 1) & 0xae2eabe2a8ULL) ^ (-((b >> 4) & 1) & 0x1e4f43e470ULL); +} + +static const char* charset = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"; + +static const int8_t charset_rev[128] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 15, -1, 10, 17, 21, 20, 26, 30, 7, 5, -1, -1, -1, -1, -1, -1, -1, 29, + -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1, 1, 0, 3, 16, 11, 28, 12, 14, + 6, 4, 2, -1, -1, -1, -1, -1, -1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, + 19, -1, 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1}; + +int cash_encode(char* output, const char* hrp, const uint8_t* data, size_t data_len) { + uint64_t chk = 1; + size_t i = 0; + while(hrp[i] != 0) { + int ch = hrp[i]; + if(ch < 33 || ch > 126) { + return 0; + } + *(output++) = ch; + chk = cashaddr_polymod_step(chk) ^ (ch & 0x1f); + ++i; + } + if(i + 1 + data_len + CHECKSUM_SIZE > MAX_CASHADDR_SIZE) { + return 0; + } + chk = cashaddr_polymod_step(chk); + *(output++) = ':'; + for(i = 0; i < data_len; ++i) { + if(*data >> 5) return 0; + chk = cashaddr_polymod_step(chk) ^ (*data); + *(output++) = charset[*(data++)]; + } + for(i = 0; i < CHECKSUM_SIZE; ++i) { + chk = cashaddr_polymod_step(chk); + } + chk ^= 1; + for(i = 0; i < CHECKSUM_SIZE; ++i) { + *(output++) = charset[(chk >> ((CHECKSUM_SIZE - 1 - i) * 5)) & 0x1f]; + } + *output = 0; + return 1; +} + +int cash_decode(char* hrp, uint8_t* data, size_t* data_len, const char* input) { + uint64_t chk = 1; + size_t i = 0; + size_t input_len = strlen(input); + size_t hrp_len = 0; + int have_lower = 0, have_upper = 0; + if(input_len < CHECKSUM_SIZE || input_len > MAX_CASHADDR_SIZE) { + return 0; + } + *data_len = 0; + while(*data_len < input_len && input[(input_len - 1) - *data_len] != ':') { + ++(*data_len); + } + hrp_len = input_len - (1 + *data_len); + if(1 + *data_len >= input_len || hrp_len > MAX_HRP_SIZE || *data_len < CHECKSUM_SIZE || + *data_len > CHECKSUM_SIZE + MAX_BASE32_SIZE) { + return 0; + } + // subtract checksum + *(data_len) -= CHECKSUM_SIZE; + for(i = 0; i < hrp_len; ++i) { + int ch = input[i]; + if(ch < 33 || ch > 126) { + return 0; + } + if(ch >= 'a' && ch <= 'z') { + have_lower = 1; + } else if(ch >= 'A' && ch <= 'Z') { + have_upper = 1; + ch = (ch - 'A') + 'a'; + } + hrp[i] = ch; + chk = cashaddr_polymod_step(chk) ^ (ch & 0x1f); + } + hrp[i] = 0; + chk = cashaddr_polymod_step(chk); + ++i; + while(i < input_len) { + int v = (input[i] & 0x80) ? -1 : charset_rev[(int)input[i]]; + if(input[i] >= 'a' && input[i] <= 'z') have_lower = 1; + if(input[i] >= 'A' && input[i] <= 'Z') have_upper = 1; + if(v == -1) { + return 0; + } + chk = cashaddr_polymod_step(chk) ^ v; + if(i + CHECKSUM_SIZE < input_len) { + data[i - (1 + hrp_len)] = v; + } + ++i; + } + if(have_lower && have_upper) { + return 0; + } + return chk == 1; +} + +static int convert_bits( + uint8_t* out, + size_t* outlen, + int outbits, + const uint8_t* in, + size_t inlen, + int inbits, + int pad) { + uint32_t val = 0; + int bits = 0; + uint32_t maxv = (((uint32_t)1) << outbits) - 1; + while(inlen--) { + val = (val << inbits) | *(in++); + bits += inbits; + while(bits >= outbits) { + bits -= outbits; + out[(*outlen)++] = (val >> bits) & maxv; + } + } + if(pad) { + if(bits) { + out[(*outlen)++] = (val << (outbits - bits)) & maxv; + } + } else if(((val << (outbits - bits)) & maxv) || bits >= inbits) { + return 0; + } + return 1; +} + +int cash_addr_encode(char* output, const char* hrp, const uint8_t* data, size_t data_len) { + uint8_t base32[MAX_BASE32_SIZE] = {0}; + size_t base32len = 0; + if(data_len < 2 || data_len > MAX_DATA_SIZE) return 0; + convert_bits(base32, &base32len, 5, data, data_len, 8, 1); + return cash_encode(output, hrp, base32, base32len); +} + +int cash_addr_decode(uint8_t* witdata, size_t* witdata_len, const char* hrp, const char* addr) { + uint8_t data[MAX_BASE32_SIZE] = {0}; + char hrp_actual[MAX_HRP_SIZE + 1] = {0}; + size_t data_len = 0; + if(!cash_decode(hrp_actual, data, &data_len, addr)) return 0; + if(data_len == 0 || data_len > MAX_BASE32_SIZE) return 0; + if(strncmp(hrp, hrp_actual, MAX_HRP_SIZE + 1) != 0) return 0; + *witdata_len = 0; + if(!convert_bits(witdata, witdata_len, 8, data, data_len, 5, 0)) return 0; + if(*witdata_len < 2 || *witdata_len > MAX_DATA_SIZE) return 0; + return 1; +} diff --git a/applications/external/flipbip/lib/crypto/cash_addr.h b/applications/external/flipbip/lib/crypto/cash_addr.h new file mode 100644 index 0000000000..5590b5b874 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/cash_addr.h @@ -0,0 +1,74 @@ +/* Copyright (c) 2017 Jochen Hoenicke, Pieter Wuille + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef _CASH_ADDR_H_ +#define _CASH_ADDR_H_ 1 + +#include + +/** Encode a Cashaddr address + * + * Out: output: Pointer to a buffer of size 105 + strlen(hrp) that will be + * updated to contain the null-terminated address. + * In: hrp: Pointer to the null-terminated human readable part to use + * (chain/network specific). + * prog: Data bytes for the hash (between 21 and 65 bytes). + * prog_len: Number of data bytes in prog. + * Returns 1 if successful. + */ +int cash_addr_encode(char* output, const char* hrp, const uint8_t* prog, size_t prog_len); + +/** Decode a CashAddr address + * + * Out: prog: Pointer to a buffer of size 65 that will be updated to + * contain the witness program bytes. + * prog_len: Pointer to a size_t that will be updated to contain the + * length of bytes in prog. hrp: Pointer to the null-terminated human + * readable part that is expected (chain/network specific). addr: Pointer to + * the null-terminated address. Returns 1 if successful. + */ +int cash_addr_decode(uint8_t* prog, size_t* prog_len, const char* hrp, const char* addr); + +/** Encode a Cash string + * + * Out: output: Pointer to a buffer of size strlen(hrp) + data_len + 8 that + * will be updated to contain the null-terminated Cash string. + * In: hrp : Pointer to the null-terminated human readable part. + * data : Pointer to an array of 5-bit values. + * data_len: Length of the data array. + * Returns 1 if successful. + */ +int cash_encode(char* output, const char* hrp, const uint8_t* data, size_t data_len); + +/** Decode a Cash string + * + * Out: hrp: Pointer to a buffer of size strlen(input) - 6. Will be + * updated to contain the null-terminated human readable part. + * data: Pointer to a buffer of size strlen(input) - 8 that will + * hold the encoded 5-bit data values. + * data_len: Pointer to a size_t that will be updated to be the number + * of entries in data. + * In: input: Pointer to a null-terminated Cash string. + * Returns 1 if succesful. + */ +int cash_decode(char* hrp, uint8_t* data, size_t* data_len, const char* input); + +#endif diff --git a/applications/external/flipbip/lib/crypto/chacha20poly1305/LICENSE b/applications/external/flipbip/lib/crypto/chacha20poly1305/LICENSE new file mode 100644 index 0000000000..95404966f0 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/chacha20poly1305/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (C) 2016 Will Glozer + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/applications/external/flipbip/lib/crypto/chacha20poly1305/chacha20poly1305.c b/applications/external/flipbip/lib/crypto/chacha20poly1305/chacha20poly1305.c new file mode 100644 index 0000000000..281dd465a5 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/chacha20poly1305/chacha20poly1305.c @@ -0,0 +1,63 @@ +// Implementations of the XChaCha20 + Poly1305 and ChaCha20 + Poly1305 +// AEAD constructions with a goal of simplicity and correctness rather +// than performance. + +#include "chacha20poly1305.h" +#include "ecrypt_portable.h" + +void hchacha20(ECRYPT_ctx* x, u8* c); + +// Initialize the XChaCha20 + Poly1305 context for encryption or decryption +// using a 32 byte key and 24 byte nonce. The key and the first 16 bytes of +// the nonce are used as input to HChaCha20 to derive the Chacha20 key. +void xchacha20poly1305_init( + chacha20poly1305_ctx* ctx, + const uint8_t key[32], + const uint8_t nonce[24]) { + unsigned char subkey[32] = {0}; + unsigned char block0[64] = {0}; + ECRYPT_ctx tmp = {0}; + + // Generate the Chacha20 key by applying HChaCha20 to the + // original key and the first 16 bytes of the nonce. + ECRYPT_keysetup(&tmp, key, 256, 16); + tmp.input[12] = U8TO32_LITTLE(nonce + 0); + tmp.input[13] = U8TO32_LITTLE(nonce + 4); + tmp.input[14] = U8TO32_LITTLE(nonce + 8); + tmp.input[15] = U8TO32_LITTLE(nonce + 12); + hchacha20(&tmp, subkey); + + // Initialize Chacha20 with the newly generated key and + // the last 8 bytes of the nonce. + ECRYPT_keysetup(&ctx->chacha20, subkey, 256, 16); + ECRYPT_ivsetup(&ctx->chacha20, nonce + 16); + + // Encrypt 64 bytes of zeros and use the first 32 bytes + // as the Poly1305 key. + ECRYPT_encrypt_bytes(&ctx->chacha20, block0, block0, 64); + poly1305_init(&ctx->poly1305, block0); +} + +// Encrypt n bytes of plaintext where n must be evenly divisible by the +// Chacha20 blocksize of 64, except for the final n bytes of plaintext. +void chacha20poly1305_encrypt(chacha20poly1305_ctx* ctx, const uint8_t* in, uint8_t* out, size_t n) { + ECRYPT_encrypt_bytes(&ctx->chacha20, in, out, n); + poly1305_update(&ctx->poly1305, out, n); +} + +// Decrypt n bytes of ciphertext where n must be evenly divisible by the +// Chacha20 blocksize of 64, except for the final n bytes of ciphertext. +void chacha20poly1305_decrypt(chacha20poly1305_ctx* ctx, const uint8_t* in, uint8_t* out, size_t n) { + poly1305_update(&ctx->poly1305, in, n); + ECRYPT_encrypt_bytes(&ctx->chacha20, in, out, n); +} + +// Include authenticated data in the Poly1305 MAC. +void chacha20poly1305_auth(chacha20poly1305_ctx* ctx, const uint8_t* in, size_t n) { + poly1305_update(&ctx->poly1305, in, n); +} + +// Compute NaCl secretbox-style Poly1305 MAC. +void chacha20poly1305_finish(chacha20poly1305_ctx* ctx, uint8_t mac[16]) { + poly1305_finish(&ctx->poly1305, mac); +} diff --git a/applications/external/flipbip/lib/crypto/chacha20poly1305/chacha20poly1305.h b/applications/external/flipbip/lib/crypto/chacha20poly1305/chacha20poly1305.h new file mode 100644 index 0000000000..803772b7da --- /dev/null +++ b/applications/external/flipbip/lib/crypto/chacha20poly1305/chacha20poly1305.h @@ -0,0 +1,22 @@ +#ifndef CHACHA20POLY1305_H +#define CHACHA20POLY1305_H + +#include +#include "ecrypt_sync.h" +#include "poly1305_donna.h" + +typedef struct { + ECRYPT_ctx chacha20; + poly1305_context poly1305; +} chacha20poly1305_ctx; + +void xchacha20poly1305_init( + chacha20poly1305_ctx* ctx, + const uint8_t key[32], + const uint8_t nonce[24]); +void chacha20poly1305_encrypt(chacha20poly1305_ctx* ctx, const uint8_t* in, uint8_t* out, size_t n); +void chacha20poly1305_decrypt(chacha20poly1305_ctx* ctx, const uint8_t* in, uint8_t* out, size_t n); +void chacha20poly1305_auth(chacha20poly1305_ctx* ctx, const uint8_t* in, size_t n); +void chacha20poly1305_finish(chacha20poly1305_ctx* ctx, uint8_t mac[16]); + +#endif // CHACHA20POLY1305_H diff --git a/applications/external/flipbip/lib/crypto/chacha20poly1305/chacha_merged.c b/applications/external/flipbip/lib/crypto/chacha20poly1305/chacha_merged.c new file mode 100644 index 0000000000..ee228b471a --- /dev/null +++ b/applications/external/flipbip/lib/crypto/chacha20poly1305/chacha_merged.c @@ -0,0 +1,251 @@ +/* +chacha-merged.c version 20080118 +D. J. Bernstein +Public domain. +*/ + +#include "ecrypt_sync.h" +#include "ecrypt_portable.h" + +#define ROTATE(v, c) (ROTL32(v, c)) +#define XOR(v, w) ((v) ^ (w)) +#define PLUS(v, w) (U32V((v) + (w))) +#define PLUSONE(v) (PLUS((v), 1)) + +#define QUARTERROUND(a, b, c, d) \ + a = PLUS(a, b); \ + d = ROTATE(XOR(d, a), 16); \ + c = PLUS(c, d); \ + b = ROTATE(XOR(b, c), 12); \ + a = PLUS(a, b); \ + d = ROTATE(XOR(d, a), 8); \ + c = PLUS(c, d); \ + b = ROTATE(XOR(b, c), 7); + +void ECRYPT_init(void) { + return; +} + +static const char sigma[16] = "expand 32-byte k"; +static const char tau[16] = "expand 16-byte k"; + +void ECRYPT_keysetup(ECRYPT_ctx* x, const u8* k, u32 kbits, u32 ivbits) { + (void)ivbits; + const char* constants = (const char*)0; + + x->input[4] = U8TO32_LITTLE(k + 0); + x->input[5] = U8TO32_LITTLE(k + 4); + x->input[6] = U8TO32_LITTLE(k + 8); + x->input[7] = U8TO32_LITTLE(k + 12); + if(kbits == 256) { /* recommended */ + k += 16; + constants = sigma; + } else { /* kbits == 128 */ + constants = tau; + } + x->input[8] = U8TO32_LITTLE(k + 0); + x->input[9] = U8TO32_LITTLE(k + 4); + x->input[10] = U8TO32_LITTLE(k + 8); + x->input[11] = U8TO32_LITTLE(k + 12); + x->input[0] = U8TO32_LITTLE(constants + 0); + x->input[1] = U8TO32_LITTLE(constants + 4); + x->input[2] = U8TO32_LITTLE(constants + 8); + x->input[3] = U8TO32_LITTLE(constants + 12); +} + +void ECRYPT_ivsetup(ECRYPT_ctx* x, const u8* iv) { + x->input[12] = 0; + x->input[13] = 0; + x->input[14] = U8TO32_LITTLE(iv + 0); + x->input[15] = U8TO32_LITTLE(iv + 4); +} + +void ECRYPT_ctrsetup(ECRYPT_ctx* x, const u8* ctr) { + x->input[12] = U8TO32_LITTLE(ctr + 0); + x->input[13] = U8TO32_LITTLE(ctr + 4); +} + +void ECRYPT_encrypt_bytes(ECRYPT_ctx* x, const u8* m, u8* c, u32 bytes) { + u32 x0 = 0, x1 = 0, x2 = 0, x3 = 0, x4 = 0, x5 = 0, x6 = 0, x7 = 0, x8 = 0, x9 = 0, x10 = 0, + x11 = 0, x12 = 0, x13 = 0, x14 = 0, x15 = 0; + u32 j0 = 0, j1 = 0, j2 = 0, j3 = 0, j4 = 0, j5 = 0, j6 = 0, j7 = 0, j8 = 0, j9 = 0, j10 = 0, + j11 = 0, j12 = 0, j13 = 0, j14 = 0, j15 = 0; + u8* ctarget = 0; + u8 tmp[64] = {0}; + int i = 0; + + if(!bytes) return; + + j0 = x->input[0]; + j1 = x->input[1]; + j2 = x->input[2]; + j3 = x->input[3]; + j4 = x->input[4]; + j5 = x->input[5]; + j6 = x->input[6]; + j7 = x->input[7]; + j8 = x->input[8]; + j9 = x->input[9]; + j10 = x->input[10]; + j11 = x->input[11]; + j12 = x->input[12]; + j13 = x->input[13]; + j14 = x->input[14]; + j15 = x->input[15]; + + for(;;) { + if(bytes < 64) { + for(i = 0; i < (int)bytes; ++i) tmp[i] = m[i]; + m = tmp; + ctarget = c; + c = tmp; + } + x0 = j0; + x1 = j1; + x2 = j2; + x3 = j3; + x4 = j4; + x5 = j5; + x6 = j6; + x7 = j7; + x8 = j8; + x9 = j9; + x10 = j10; + x11 = j11; + x12 = j12; + x13 = j13; + x14 = j14; + x15 = j15; + for(i = 20; i > 0; i -= 2) { + QUARTERROUND(x0, x4, x8, x12) + QUARTERROUND(x1, x5, x9, x13) + QUARTERROUND(x2, x6, x10, x14) + QUARTERROUND(x3, x7, x11, x15) + QUARTERROUND(x0, x5, x10, x15) + QUARTERROUND(x1, x6, x11, x12) + QUARTERROUND(x2, x7, x8, x13) + QUARTERROUND(x3, x4, x9, x14) + } + x0 = PLUS(x0, j0); + x1 = PLUS(x1, j1); + x2 = PLUS(x2, j2); + x3 = PLUS(x3, j3); + x4 = PLUS(x4, j4); + x5 = PLUS(x5, j5); + x6 = PLUS(x6, j6); + x7 = PLUS(x7, j7); + x8 = PLUS(x8, j8); + x9 = PLUS(x9, j9); + x10 = PLUS(x10, j10); + x11 = PLUS(x11, j11); + x12 = PLUS(x12, j12); + x13 = PLUS(x13, j13); + x14 = PLUS(x14, j14); + x15 = PLUS(x15, j15); + + x0 = XOR(x0, U8TO32_LITTLE(m + 0)); + x1 = XOR(x1, U8TO32_LITTLE(m + 4)); + x2 = XOR(x2, U8TO32_LITTLE(m + 8)); + x3 = XOR(x3, U8TO32_LITTLE(m + 12)); + x4 = XOR(x4, U8TO32_LITTLE(m + 16)); + x5 = XOR(x5, U8TO32_LITTLE(m + 20)); + x6 = XOR(x6, U8TO32_LITTLE(m + 24)); + x7 = XOR(x7, U8TO32_LITTLE(m + 28)); + x8 = XOR(x8, U8TO32_LITTLE(m + 32)); + x9 = XOR(x9, U8TO32_LITTLE(m + 36)); + x10 = XOR(x10, U8TO32_LITTLE(m + 40)); + x11 = XOR(x11, U8TO32_LITTLE(m + 44)); + x12 = XOR(x12, U8TO32_LITTLE(m + 48)); + x13 = XOR(x13, U8TO32_LITTLE(m + 52)); + x14 = XOR(x14, U8TO32_LITTLE(m + 56)); + x15 = XOR(x15, U8TO32_LITTLE(m + 60)); + + j12 = PLUSONE(j12); + if(!j12) { + j13 = PLUSONE(j13); + /* stopping at 2^70 bytes per nonce is user's responsibility */ + } + + U32TO8_LITTLE(c + 0, x0); + U32TO8_LITTLE(c + 4, x1); + U32TO8_LITTLE(c + 8, x2); + U32TO8_LITTLE(c + 12, x3); + U32TO8_LITTLE(c + 16, x4); + U32TO8_LITTLE(c + 20, x5); + U32TO8_LITTLE(c + 24, x6); + U32TO8_LITTLE(c + 28, x7); + U32TO8_LITTLE(c + 32, x8); + U32TO8_LITTLE(c + 36, x9); + U32TO8_LITTLE(c + 40, x10); + U32TO8_LITTLE(c + 44, x11); + U32TO8_LITTLE(c + 48, x12); + U32TO8_LITTLE(c + 52, x13); + U32TO8_LITTLE(c + 56, x14); + U32TO8_LITTLE(c + 60, x15); + + if(bytes <= 64) { + if(bytes < 64) { + for(i = 0; i < (int)bytes; ++i) ctarget[i] = c[i]; + } + x->input[12] = j12; + x->input[13] = j13; + return; + } + bytes -= 64; + c += 64; + m += 64; + } +} + +void ECRYPT_decrypt_bytes(ECRYPT_ctx* x, const u8* c, u8* m, u32 bytes) { + ECRYPT_encrypt_bytes(x, c, m, bytes); +} + +void ECRYPT_keystream_bytes(ECRYPT_ctx* x, u8* stream, u32 bytes) { + u32 i = 0; + for(i = 0; i < bytes; ++i) stream[i] = 0; + ECRYPT_encrypt_bytes(x, stream, stream, bytes); +} + +void hchacha20(ECRYPT_ctx* x, u8* c) { + u32 x0 = 0, x1 = 0, x2 = 0, x3 = 0, x4 = 0, x5 = 0, x6 = 0, x7 = 0, x8 = 0, x9 = 0, x10 = 0, + x11 = 0, x12 = 0, x13 = 0, x14 = 0, x15 = 0; + int i = 0; + + x0 = x->input[0]; + x1 = x->input[1]; + x2 = x->input[2]; + x3 = x->input[3]; + x4 = x->input[4]; + x5 = x->input[5]; + x6 = x->input[6]; + x7 = x->input[7]; + x8 = x->input[8]; + x9 = x->input[9]; + x10 = x->input[10]; + x11 = x->input[11]; + x12 = x->input[12]; + x13 = x->input[13]; + x14 = x->input[14]; + x15 = x->input[15]; + + for(i = 20; i > 0; i -= 2) { + QUARTERROUND(x0, x4, x8, x12) + QUARTERROUND(x1, x5, x9, x13) + QUARTERROUND(x2, x6, x10, x14) + QUARTERROUND(x3, x7, x11, x15) + QUARTERROUND(x0, x5, x10, x15) + QUARTERROUND(x1, x6, x11, x12) + QUARTERROUND(x2, x7, x8, x13) + QUARTERROUND(x3, x4, x9, x14) + } + + U32TO8_LITTLE(c + 0, x0); + U32TO8_LITTLE(c + 4, x1); + U32TO8_LITTLE(c + 8, x2); + U32TO8_LITTLE(c + 12, x3); + U32TO8_LITTLE(c + 16, x12); + U32TO8_LITTLE(c + 20, x13); + U32TO8_LITTLE(c + 24, x14); + U32TO8_LITTLE(c + 28, x15); +} diff --git a/applications/external/flipbip/lib/crypto/chacha20poly1305/ecrypt_config.h b/applications/external/flipbip/lib/crypto/chacha20poly1305/ecrypt_config.h new file mode 100644 index 0000000000..40a3a484a5 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/chacha20poly1305/ecrypt_config.h @@ -0,0 +1,316 @@ +/* ecrypt_config.h */ + +/* *** Normally, it should not be necessary to edit this file. *** */ + +#ifndef ECRYPT_CONFIG +#define ECRYPT_CONFIG + +/* ------------------------------------------------------------------------- */ + +/* Guess the endianness of the target architecture. */ + +/* + * The LITTLE endian machines: + */ +#if defined(__ultrix) /* Older MIPS */ +#define ECRYPT_LITTLE_ENDIAN +#elif defined(__alpha) /* Alpha */ +#define ECRYPT_LITTLE_ENDIAN +#elif defined(i386) /* x86 (gcc) */ +#define ECRYPT_LITTLE_ENDIAN +#elif defined(__i386) /* x86 (gcc) */ +#define ECRYPT_LITTLE_ENDIAN +#elif defined(__x86_64) /* x86_64 (gcc) */ +#define ECRYPT_LITTLE_ENDIAN +#elif defined(_M_IX86) /* x86 (MSC, Borland) */ +#define ECRYPT_LITTLE_ENDIAN +#elif defined(_MSC_VER) /* x86 (surely MSC) */ +#define ECRYPT_LITTLE_ENDIAN +#elif defined(__INTEL_COMPILER) /* x86 (surely Intel compiler icl.exe) */ +#define ECRYPT_LITTLE_ENDIAN + +/* + * The BIG endian machines: + */ +#elif defined(__sparc) /* Newer Sparc's */ +#define ECRYPT_BIG_ENDIAN +#elif defined(__powerpc__) /* PowerPC */ +#define ECRYPT_BIG_ENDIAN +#elif defined(__ppc__) /* PowerPC */ +#define ECRYPT_BIG_ENDIAN +#elif defined(__hppa) /* HP-PA */ +#define ECRYPT_BIG_ENDIAN + +/* + * Finally machines with UNKNOWN endianness: + */ +#elif defined(_AIX) /* RS6000 */ +#define ECRYPT_UNKNOWN +#elif defined(__aux) /* 68K */ +#define ECRYPT_UNKNOWN +#elif defined(__dgux) /* 88K (but P6 in latest boxes) */ +#define ECRYPT_UNKNOWN +#elif defined(__sgi) /* Newer MIPS */ +#define ECRYPT_UNKNOWN +#else /* Any other processor */ +#define ECRYPT_UNKNOWN +#endif + +/* ------------------------------------------------------------------------- */ + +/* + * Find minimal-width types to store 8-bit, 16-bit, 32-bit, and 64-bit + * integers. + * + * Note: to enable 64-bit types on 32-bit compilers, it might be + * necessary to switch from ISO C90 mode to ISO C99 mode (e.g., gcc + * -std=c99), or to allow compiler-specific extensions. + */ + +#include + +/* --- check char --- */ + +#if(UCHAR_MAX / 0xFU > 0xFU) +#ifndef I8T +#define I8T char +#define U8C(v) (v##U) + +#if(UCHAR_MAX == 0xFFU) +#define ECRYPT_I8T_IS_BYTE +#endif + +#endif + +#if(UCHAR_MAX / 0xFFU > 0xFFU) +#ifndef I16T +#define I16T char +#define U16C(v) (v##U) +#endif + +#if(UCHAR_MAX / 0xFFFFU > 0xFFFFU) +#ifndef I32T +#define I32T char +#define U32C(v) (v##U) +#endif + +#if(UCHAR_MAX / 0xFFFFFFFFU > 0xFFFFFFFFU) +#ifndef I64T +#define I64T char +#define U64C(v) (v##U) +#define ECRYPT_NATIVE64 +#endif + +#endif +#endif +#endif +#endif + +/* --- check short --- */ + +#if(USHRT_MAX / 0xFU > 0xFU) +#ifndef I8T +#define I8T short +#define U8C(v) (v##U) + +#if(USHRT_MAX == 0xFFU) +#define ECRYPT_I8T_IS_BYTE +#endif + +#endif + +#if(USHRT_MAX / 0xFFU > 0xFFU) +#ifndef I16T +#define I16T short +#define U16C(v) (v##U) +#endif + +#if(USHRT_MAX / 0xFFFFU > 0xFFFFU) +#ifndef I32T +#define I32T short +#define U32C(v) (v##U) +#endif + +#if(USHRT_MAX / 0xFFFFFFFFU > 0xFFFFFFFFU) +#ifndef I64T +#define I64T short +#define U64C(v) (v##U) +#define ECRYPT_NATIVE64 +#endif + +#endif +#endif +#endif +#endif + +/* --- check int --- */ + +#if(UINT_MAX / 0xFU > 0xFU) +#ifndef I8T +#define I8T int +#define U8C(v) (v##U) + +#if(ULONG_MAX == 0xFFU) +#define ECRYPT_I8T_IS_BYTE +#endif + +#endif + +#if(UINT_MAX / 0xFFU > 0xFFU) +#ifndef I16T +#define I16T int +#define U16C(v) (v##U) +#endif + +#if(UINT_MAX / 0xFFFFU > 0xFFFFU) +#ifndef I32T +#define I32T int +#define U32C(v) (v##U) +#endif + +#if(UINT_MAX / 0xFFFFFFFFU > 0xFFFFFFFFU) +#ifndef I64T +#define I64T int +#define U64C(v) (v##U) +#define ECRYPT_NATIVE64 +#endif + +#endif +#endif +#endif +#endif + +/* --- check long --- */ + +#if(ULONG_MAX / 0xFUL > 0xFUL) +#ifndef I8T +#define I8T long +#define U8C(v) (v##UL) + +#if(ULONG_MAX == 0xFFUL) +#define ECRYPT_I8T_IS_BYTE +#endif + +#endif + +#if(ULONG_MAX / 0xFFUL > 0xFFUL) +#ifndef I16T +#define I16T long +#define U16C(v) (v##UL) +#endif + +#if(ULONG_MAX / 0xFFFFUL > 0xFFFFUL) +#ifndef I32T +#define I32T long +#define U32C(v) (v##UL) +#endif + +#if(ULONG_MAX / 0xFFFFFFFFUL > 0xFFFFFFFFUL) +#ifndef I64T +#define I64T long +#define U64C(v) (v##UL) +#define ECRYPT_NATIVE64 +#endif + +#endif +#endif +#endif +#endif + +/* --- check long long --- */ + +#ifdef ULLONG_MAX + +#if(ULLONG_MAX / 0xFULL > 0xFULL) +#ifndef I8T +#define I8T long long +#define U8C(v) (v##ULL) + +#if(ULLONG_MAX == 0xFFULL) +#define ECRYPT_I8T_IS_BYTE +#endif + +#endif + +#if(ULLONG_MAX / 0xFFULL > 0xFFULL) +#ifndef I16T +#define I16T long long +#define U16C(v) (v##ULL) +#endif + +#if(ULLONG_MAX / 0xFFFFULL > 0xFFFFULL) +#ifndef I32T +#define I32T long long +#define U32C(v) (v##ULL) +#endif + +#if(ULLONG_MAX / 0xFFFFFFFFULL > 0xFFFFFFFFULL) +#ifndef I64T +#define I64T long long +#define U64C(v) (v##ULL) +#endif + +#endif +#endif +#endif +#endif + +#endif + +/* --- check __int64 --- */ + +#if !defined(__STDC__) && defined(_UI64_MAX) + +#ifndef I64T +#define I64T __int64 +#define U64C(v) (v##ui64) +#endif + +#endif + +/* --- if platform doesn't announce anything, use most common choices --- */ + +#ifndef I8T +#define I8T char +#define U8C(v) (v##U) +#endif +#ifndef I16T +#define I16T short +#define U16C(v) (v##U) +#endif +#ifndef I32T +#define I32T int +#define U32C(v) (v##U) +#endif +#ifndef I64T +#define I64T long long +#define U64C(v) (v##ULL) +#endif + +/* ------------------------------------------------------------------------- */ + +/* find the largest type on this platform (used for alignment) */ + +#if defined(__SSE__) || (defined(_MSC_VER) && (_MSC_VER >= 1300)) + +#include +#define MAXT __m128 + +#elif defined(__MMX__) + +#include +#define MAXT __m64 + +#elif defined(__ALTIVEC__) + +#define MAXT __vector int + +#else + +#define MAXT long + +#endif + +/* ------------------------------------------------------------------------- */ + +#endif diff --git a/applications/external/flipbip/lib/crypto/chacha20poly1305/ecrypt_machine.h b/applications/external/flipbip/lib/crypto/chacha20poly1305/ecrypt_machine.h new file mode 100644 index 0000000000..6773c25719 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/chacha20poly1305/ecrypt_machine.h @@ -0,0 +1,49 @@ +/* ecrypt_machine.h */ + +/* + * This file is included by 'ecrypt_portable.h'. It allows to override + * the default macros for specific platforms. Please carefully check + * the machine code generated by your compiler (with optimisations + * turned on) before deciding to edit this file. + */ + +/* ------------------------------------------------------------------------- */ + +#if(defined(ECRYPT_DEFAULT_ROT) && !defined(ECRYPT_MACHINE_ROT)) + +#define ECRYPT_MACHINE_ROT + +#if(defined(WIN32) && defined(_MSC_VER)) + +#undef ROTL32 +#undef ROTR32 +#undef ROTL64 +#undef ROTR64 + +#include + +#pragma intrinsic(_lrotl) /* compile rotations "inline" */ +#pragma intrinsic(_lrotr) + +#define ROTL32(v, n) _lrotl(v, n) +#define ROTR32(v, n) _lrotr(v, n) +#define ROTL64(v, n) _rotl64(v, n) +#define ROTR64(v, n) _rotr64(v, n) + +#endif + +#endif + +/* ------------------------------------------------------------------------- */ + +#if(defined(ECRYPT_DEFAULT_SWAP) && !defined(ECRYPT_MACHINE_SWAP)) + +#define ECRYPT_MACHINE_SWAP + +/* + * If you want to overwrite the default swap macros, put it here. And so on. + */ + +#endif + +/* ------------------------------------------------------------------------- */ diff --git a/applications/external/flipbip/lib/crypto/chacha20poly1305/ecrypt_portable.h b/applications/external/flipbip/lib/crypto/chacha20poly1305/ecrypt_portable.h new file mode 100644 index 0000000000..f5d5001140 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/chacha20poly1305/ecrypt_portable.h @@ -0,0 +1,246 @@ +/* ecrypt_portable.h */ + +/* + * WARNING: the conversions defined below are implemented as macros, + * and should be used carefully. They should NOT be used with + * parameters which perform some action. E.g., the following two lines + * are not equivalent: + * + * 1) ++x; y = ROTL32(x, n); + * 2) y = ROTL32(++x, n); + */ + +/* + * *** Please do not edit this file. *** + * + * The default macros can be overridden for specific architectures by + * editing 'ecrypt_machine.h'. + */ + +#ifndef ECRYPT_PORTABLE +#define ECRYPT_PORTABLE + +#include "ecrypt_config.h" +#include "ecrypt_types.h" + +/* ------------------------------------------------------------------------- */ + +/* + * The following macros are used to obtain exact-width results. + */ + +#define U8V(v) ((u8)(v)&U8C(0xFF)) +#define U16V(v) ((u16)(v)&U16C(0xFFFF)) +#define U32V(v) ((u32)(v)&U32C(0xFFFFFFFF)) +#define U64V(v) ((u64)(v)&U64C(0xFFFFFFFFFFFFFFFF)) + +/* ------------------------------------------------------------------------- */ + +/* + * The following macros return words with their bits rotated over n + * positions to the left/right. + */ + +#define ECRYPT_DEFAULT_ROT + +#define ROTL8(v, n) (U8V((v) << (n)) | ((v) >> (8 - (n)))) + +#define ROTL16(v, n) (U16V((v) << (n)) | ((v) >> (16 - (n)))) + +#define ROTL32(v, n) (U32V((v) << (n)) | ((v) >> (32 - (n)))) + +#define ROTL64(v, n) (U64V((v) << (n)) | ((v) >> (64 - (n)))) + +#define ROTR8(v, n) ROTL8(v, 8 - (n)) +#define ROTR16(v, n) ROTL16(v, 16 - (n)) +#define ROTR32(v, n) ROTL32(v, 32 - (n)) +#define ROTR64(v, n) ROTL64(v, 64 - (n)) + +#include "ecrypt_machine.h" + +/* ------------------------------------------------------------------------- */ + +/* + * The following macros return a word with bytes in reverse order. + */ + +#define ECRYPT_DEFAULT_SWAP + +#define SWAP16(v) ROTL16(v, 8) + +#define SWAP32(v) ((ROTL32(v, 8) & U32C(0x00FF00FF)) | (ROTL32(v, 24) & U32C(0xFF00FF00))) + +#ifdef ECRYPT_NATIVE64 +#define SWAP64(v) \ + ((ROTL64(v, 8) & U64C(0x000000FF000000FF)) | (ROTL64(v, 24) & U64C(0x0000FF000000FF00)) | \ + (ROTL64(v, 40) & U64C(0x00FF000000FF0000)) | (ROTL64(v, 56) & U64C(0xFF000000FF000000))) +#else +#define SWAP64(v) (((u64)SWAP32(U32V(v)) << 32) | (u64)SWAP32(U32V(v >> 32))) +#endif + +#include "ecrypt_machine.h" + +#define ECRYPT_DEFAULT_WTOW + +#ifdef ECRYPT_LITTLE_ENDIAN +#define U16TO16_LITTLE(v) (v) +#define U32TO32_LITTLE(v) (v) +#define U64TO64_LITTLE(v) (v) + +#define U16TO16_BIG(v) SWAP16(v) +#define U32TO32_BIG(v) SWAP32(v) +#define U64TO64_BIG(v) SWAP64(v) +#endif + +#ifdef ECRYPT_BIG_ENDIAN +#define U16TO16_LITTLE(v) SWAP16(v) +#define U32TO32_LITTLE(v) SWAP32(v) +#define U64TO64_LITTLE(v) SWAP64(v) + +#define U16TO16_BIG(v) (v) +#define U32TO32_BIG(v) (v) +#define U64TO64_BIG(v) (v) +#endif + +#include "ecrypt_machine.h" + +/* + * The following macros load words from an array of bytes with + * different types of endianness, and vice versa. + */ + +#define ECRYPT_DEFAULT_BTOW + +#if(!defined(ECRYPT_UNKNOWN) && defined(ECRYPT_I8T_IS_BYTE)) + +#define U8TO16_LITTLE(p) U16TO16_LITTLE(((u16*)(p))[0]) +#define U8TO32_LITTLE(p) U32TO32_LITTLE(((u32*)(p))[0]) +#define U8TO64_LITTLE(p) U64TO64_LITTLE(((u64*)(p))[0]) + +#define U8TO16_BIG(p) U16TO16_BIG(((u16*)(p))[0]) +#define U8TO32_BIG(p) U32TO32_BIG(((u32*)(p))[0]) +#define U8TO64_BIG(p) U64TO64_BIG(((u64*)(p))[0]) + +#define U16TO8_LITTLE(p, v) (((u16*)(p))[0] = U16TO16_LITTLE(v)) +#define U32TO8_LITTLE(p, v) (((u32*)(p))[0] = U32TO32_LITTLE(v)) +#define U64TO8_LITTLE(p, v) (((u64*)(p))[0] = U64TO64_LITTLE(v)) + +#define U16TO8_BIG(p, v) (((u16*)(p))[0] = U16TO16_BIG(v)) +#define U32TO8_BIG(p, v) (((u32*)(p))[0] = U32TO32_BIG(v)) +#define U64TO8_BIG(p, v) (((u64*)(p))[0] = U64TO64_BIG(v)) + +#else + +#define U8TO16_LITTLE(p) (((u16)((p)[0])) | ((u16)((p)[1]) << 8)) + +#define U8TO32_LITTLE(p) \ + (((u32)((p)[0])) | ((u32)((p)[1]) << 8) | ((u32)((p)[2]) << 16) | ((u32)((p)[3]) << 24)) + +#ifdef ECRYPT_NATIVE64 +#define U8TO64_LITTLE(p) \ + (((u64)((p)[0])) | ((u64)((p)[1]) << 8) | ((u64)((p)[2]) << 16) | ((u64)((p)[3]) << 24) | \ + ((u64)((p)[4]) << 32) | ((u64)((p)[5]) << 40) | ((u64)((p)[6]) << 48) | \ + ((u64)((p)[7]) << 56)) +#else +#define U8TO64_LITTLE(p) ((u64)U8TO32_LITTLE(p) | ((u64)U8TO32_LITTLE((p) + 4) << 32)) +#endif + +#define U8TO16_BIG(p) (((u16)((p)[0]) << 8) | ((u16)((p)[1]))) + +#define U8TO32_BIG(p) \ + (((u32)((p)[0]) << 24) | ((u32)((p)[1]) << 16) | ((u32)((p)[2]) << 8) | ((u32)((p)[3]))) + +#ifdef ECRYPT_NATIVE64 +#define U8TO64_BIG(p) \ + (((u64)((p)[0]) << 56) | ((u64)((p)[1]) << 48) | ((u64)((p)[2]) << 40) | \ + ((u64)((p)[3]) << 32) | ((u64)((p)[4]) << 24) | ((u64)((p)[5]) << 16) | \ + ((u64)((p)[6]) << 8) | ((u64)((p)[7]))) +#else +#define U8TO64_BIG(p) (((u64)U8TO32_BIG(p) << 32) | (u64)U8TO32_BIG((p) + 4)) +#endif + +#define U16TO8_LITTLE(p, v) \ + do { \ + (p)[0] = U8V((v)); \ + (p)[1] = U8V((v) >> 8); \ + } while(0) + +#define U32TO8_LITTLE(p, v) \ + do { \ + (p)[0] = U8V((v)); \ + (p)[1] = U8V((v) >> 8); \ + (p)[2] = U8V((v) >> 16); \ + (p)[3] = U8V((v) >> 24); \ + } while(0) + +#ifdef ECRYPT_NATIVE64 +#define U64TO8_LITTLE(p, v) \ + do { \ + (p)[0] = U8V((v)); \ + (p)[1] = U8V((v) >> 8); \ + (p)[2] = U8V((v) >> 16); \ + (p)[3] = U8V((v) >> 24); \ + (p)[4] = U8V((v) >> 32); \ + (p)[5] = U8V((v) >> 40); \ + (p)[6] = U8V((v) >> 48); \ + (p)[7] = U8V((v) >> 56); \ + } while(0) +#else +#define U64TO8_LITTLE(p, v) \ + do { \ + U32TO8_LITTLE((p), U32V((v))); \ + U32TO8_LITTLE((p) + 4, U32V((v) >> 32)); \ + } while(0) +#endif + +#define U16TO8_BIG(p, v) \ + do { \ + (p)[0] = U8V((v)); \ + (p)[1] = U8V((v) >> 8); \ + } while(0) + +#define U32TO8_BIG(p, v) \ + do { \ + (p)[0] = U8V((v) >> 24); \ + (p)[1] = U8V((v) >> 16); \ + (p)[2] = U8V((v) >> 8); \ + (p)[3] = U8V((v)); \ + } while(0) + +#ifdef ECRYPT_NATIVE64 +#define U64TO8_BIG(p, v) \ + do { \ + (p)[0] = U8V((v) >> 56); \ + (p)[1] = U8V((v) >> 48); \ + (p)[2] = U8V((v) >> 40); \ + (p)[3] = U8V((v) >> 32); \ + (p)[4] = U8V((v) >> 24); \ + (p)[5] = U8V((v) >> 16); \ + (p)[6] = U8V((v) >> 8); \ + (p)[7] = U8V((v)); \ + } while(0) +#else +#define U64TO8_BIG(p, v) \ + do { \ + U32TO8_BIG((p), U32V((v) >> 32)); \ + U32TO8_BIG((p) + 4, U32V((v))); \ + } while(0) +#endif + +#endif + +#include "ecrypt_machine.h" + +/* ------------------------------------------------------------------------- */ + +#define AT_LEAST_ONE(n) (((n) < 1) ? 1 : (n)) + +#define ALIGN(t, v, n) \ + union { \ + t b[n]; \ + MAXT l[AT_LEAST_ONE(n * sizeof(t) / sizeof(MAXT))]; \ + } v + +/* ------------------------------------------------------------------------- */ + +#endif diff --git a/applications/external/flipbip/lib/crypto/chacha20poly1305/ecrypt_sync.h b/applications/external/flipbip/lib/crypto/chacha20poly1305/ecrypt_sync.h new file mode 100644 index 0000000000..f51608f9af --- /dev/null +++ b/applications/external/flipbip/lib/crypto/chacha20poly1305/ecrypt_sync.h @@ -0,0 +1,282 @@ +#define ECRYPT_VARIANT 1 +#define ECRYPT_API +/* ecrypt_sync.h */ + +/* + * Header file for synchronous stream ciphers without authentication + * mechanism. + * + * *** Please only edit parts marked with "[edit]". *** + */ + +#ifndef ECRYPT_SYNC +#define ECRYPT_SYNC + +#include "ecrypt_types.h" + +/* ------------------------------------------------------------------------- */ + +/* Cipher parameters */ + +/* + * The name of your cipher. + */ +#define ECRYPT_NAME "ChaCha20" +#define ECRYPT_PROFILE "_____" + +/* + * Specify which key and IV sizes are supported by your cipher. A user + * should be able to enumerate the supported sizes by running the + * following code: + * + * for (i = 0; ECRYPT_KEYSIZE(i) <= ECRYPT_MAXKEYSIZE; ++i) + * { + * keysize = ECRYPT_KEYSIZE(i); + * + * ... + * } + * + * All sizes are in bits. + */ + +#define ECRYPT_MAXKEYSIZE 256 /* [edit] */ +#define ECRYPT_KEYSIZE(i) (128 + (i)*128) /* [edit] */ + +#define ECRYPT_MAXIVSIZE 64 /* [edit] */ +#define ECRYPT_IVSIZE(i) (64 + (i)*64) /* [edit] */ + +/* ------------------------------------------------------------------------- */ + +/* Data structures */ + +/* + * ECRYPT_ctx is the structure containing the representation of the + * internal state of your cipher. + */ + +typedef struct { + u32 input[16]; /* could be compressed */ + /* + * [edit] + * + * Put here all state variable needed during the encryption process. + */ +} ECRYPT_ctx; + +/* ------------------------------------------------------------------------- */ + +/* Mandatory functions */ + +/* + * Key and message independent initialization. This function will be + * called once when the program starts (e.g., to build expanded S-box + * tables). + */ +void ECRYPT_init(void); + +/* + * Key setup. It is the user's responsibility to select the values of + * keysize and ivsize from the set of supported values specified + * above. + */ +void ECRYPT_keysetup( + ECRYPT_ctx* ctx, + const u8* key, + u32 keysize, /* Key size in bits. */ + u32 ivsize); /* IV size in bits. */ + +/* + * IV setup. After having called ECRYPT_keysetup(), the user is + * allowed to call ECRYPT_ivsetup() different times in order to + * encrypt/decrypt different messages with the same key but different + * IV's. ECRYPT_ivsetup() also sets block counter to zero. + */ +void ECRYPT_ivsetup(ECRYPT_ctx* ctx, const u8* iv); + +/* + * Block counter setup. It is used only for special purposes, + * since block counter is usually initialized with ECRYPT_ivsetup. + * ECRYPT_ctrsetup has to be called after ECRYPT_ivsetup. + */ +void ECRYPT_ctrsetup(ECRYPT_ctx* ctx, const u8* ctr); + +/* + * Encryption/decryption of arbitrary length messages. + * + * For efficiency reasons, the API provides two types of + * encrypt/decrypt functions. The ECRYPT_encrypt_bytes() function + * (declared here) encrypts byte strings of arbitrary length, while + * the ECRYPT_encrypt_blocks() function (defined later) only accepts + * lengths which are multiples of ECRYPT_BLOCKLENGTH. + * + * The user is allowed to make multiple calls to + * ECRYPT_encrypt_blocks() to incrementally encrypt a long message, + * but he is NOT allowed to make additional encryption calls once he + * has called ECRYPT_encrypt_bytes() (unless he starts a new message + * of course). For example, this sequence of calls is acceptable: + * + * ECRYPT_keysetup(); + * + * ECRYPT_ivsetup(); + * ECRYPT_encrypt_blocks(); + * ECRYPT_encrypt_blocks(); + * ECRYPT_encrypt_bytes(); + * + * ECRYPT_ivsetup(); + * ECRYPT_encrypt_blocks(); + * ECRYPT_encrypt_blocks(); + * + * ECRYPT_ivsetup(); + * ECRYPT_encrypt_bytes(); + * + * The following sequence is not: + * + * ECRYPT_keysetup(); + * ECRYPT_ivsetup(); + * ECRYPT_encrypt_blocks(); + * ECRYPT_encrypt_bytes(); + * ECRYPT_encrypt_blocks(); + */ + +void ECRYPT_encrypt_bytes( + ECRYPT_ctx* ctx, + const u8* plaintext, + u8* ciphertext, + u32 msglen); /* Message length in bytes. */ + +void ECRYPT_decrypt_bytes( + ECRYPT_ctx* ctx, + const u8* ciphertext, + u8* plaintext, + u32 msglen); /* Message length in bytes. */ + +/* ------------------------------------------------------------------------- */ + +/* Optional features */ + +/* + * For testing purposes it can sometimes be useful to have a function + * which immediately generates keystream without having to provide it + * with a zero plaintext. If your cipher cannot provide this function + * (e.g., because it is not strictly a synchronous cipher), please + * reset the ECRYPT_GENERATES_KEYSTREAM flag. + */ + +#define ECRYPT_GENERATES_KEYSTREAM +#ifdef ECRYPT_GENERATES_KEYSTREAM + +void ECRYPT_keystream_bytes( + ECRYPT_ctx* ctx, + u8* keystream, + u32 length); /* Length of keystream in bytes. */ + +#endif + +/* ------------------------------------------------------------------------- */ + +/* Optional optimizations */ + +/* + * By default, the functions in this section are implemented using + * calls to functions declared above. However, you might want to + * implement them differently for performance reasons. + */ + +/* + * All-in-one encryption/decryption of (short) packets. + * + * The default definitions of these functions can be found in + * "ecrypt-sync.c". If you want to implement them differently, please + * undef the ECRYPT_USES_DEFAULT_ALL_IN_ONE flag. + */ +#define ECRYPT_USES_DEFAULT_ALL_IN_ONE /* [edit] */ + +void ECRYPT_encrypt_packet( + ECRYPT_ctx* ctx, + const u8* iv, + const u8* plaintext, + u8* ciphertext, + u32 msglen); + +void ECRYPT_decrypt_packet( + ECRYPT_ctx* ctx, + const u8* iv, + const u8* ciphertext, + u8* plaintext, + u32 msglen); + +/* + * Encryption/decryption of blocks. + * + * By default, these functions are defined as macros. If you want to + * provide a different implementation, please undef the + * ECRYPT_USES_DEFAULT_BLOCK_MACROS flag and implement the functions + * declared below. + */ + +#define ECRYPT_BLOCKLENGTH 64 /* [edit] */ + +#define ECRYPT_USES_DEFAULT_BLOCK_MACROS /* [edit] */ +#ifdef ECRYPT_USES_DEFAULT_BLOCK_MACROS + +#define ECRYPT_encrypt_blocks(ctx, plaintext, ciphertext, blocks) \ + ECRYPT_encrypt_bytes(ctx, plaintext, ciphertext, (blocks)*ECRYPT_BLOCKLENGTH) + +#define ECRYPT_decrypt_blocks(ctx, ciphertext, plaintext, blocks) \ + ECRYPT_decrypt_bytes(ctx, ciphertext, plaintext, (blocks)*ECRYPT_BLOCKLENGTH) + +#ifdef ECRYPT_GENERATES_KEYSTREAM + +#define ECRYPT_keystream_blocks(ctx, keystream, blocks) \ + ECRYPT_keystream_bytes(ctx, keystream, (blocks)*ECRYPT_BLOCKLENGTH) + +#endif + +#else + +void ECRYPT_encrypt_blocks( + ECRYPT_ctx* ctx, + const u8* plaintext, + u8* ciphertext, + u32 blocks); /* Message length in blocks. */ + +void ECRYPT_decrypt_blocks( + ECRYPT_ctx* ctx, + const u8* ciphertext, + u8* plaintext, + u32 blocks); /* Message length in blocks. */ + +#ifdef ECRYPT_GENERATES_KEYSTREAM + +void ECRYPT_keystream_blocks( + ECRYPT_ctx* ctx, + const u8* keystream, + u32 blocks); /* Keystream length in blocks. */ + +#endif + +#endif + +/* + * If your cipher can be implemented in different ways, you can use + * the ECRYPT_VARIANT parameter to allow the user to choose between + * them at compile time (e.g., gcc -DECRYPT_VARIANT=3 ...). Please + * only use this possibility if you really think it could make a + * significant difference and keep the number of variants + * (ECRYPT_MAXVARIANT) as small as possible (definitely not more than + * 10). Note also that all variants should have exactly the same + * external interface (i.e., the same ECRYPT_BLOCKLENGTH, etc.). + */ +#define ECRYPT_MAXVARIANT 1 /* [edit] */ + +#ifndef ECRYPT_VARIANT +#define ECRYPT_VARIANT 1 +#endif + +#if(ECRYPT_VARIANT > ECRYPT_MAXVARIANT) +#error this variant does not exist +#endif + +/* ------------------------------------------------------------------------- */ + +#endif diff --git a/applications/external/flipbip/lib/crypto/chacha20poly1305/ecrypt_types.h b/applications/external/flipbip/lib/crypto/chacha20poly1305/ecrypt_types.h new file mode 100644 index 0000000000..2d1a419759 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/chacha20poly1305/ecrypt_types.h @@ -0,0 +1,53 @@ +/* ecrypt_types.h */ + +/* + * *** Please do not edit this file. *** + * + * The default macros can be overridden for specific architectures by + * editing 'ecrypt_machine.h'. + */ + +#ifndef ECRYPT_TYPES +#define ECRYPT_TYPES + +#include "ecrypt_config.h" + +/* ------------------------------------------------------------------------- */ + +/* + * The following types are defined (if available): + * + * u8: unsigned integer type, at least 8 bits + * u16: unsigned integer type, at least 16 bits + * u32: unsigned integer type, at least 32 bits + * u64: unsigned integer type, at least 64 bits + * + * s8, s16, s32, s64 -> signed counterparts of u8, u16, u32, u64 + * + * The selection of minimum-width integer types is taken care of by + * 'ecrypt_config.h'. Note: to enable 64-bit types on 32-bit + * compilers, it might be necessary to switch from ISO C90 mode to ISO + * C99 mode (e.g., gcc -std=c99). + */ + +#ifdef I8T +typedef signed I8T s8; +typedef unsigned I8T u8; +#endif + +#ifdef I16T +typedef signed I16T s16; +typedef unsigned I16T u16; +#endif + +#ifdef I32T +typedef signed I32T s32; +typedef unsigned I32T u32; +#endif + +#ifdef I64T +typedef signed I64T s64; +typedef unsigned I64T u64; +#endif + +#endif diff --git a/applications/external/flipbip/lib/crypto/chacha20poly1305/poly1305_donna.c b/applications/external/flipbip/lib/crypto/chacha20poly1305/poly1305_donna.c new file mode 100644 index 0000000000..4ab3531276 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/chacha20poly1305/poly1305_donna.c @@ -0,0 +1,208 @@ +#include "poly1305_donna.h" +#include "poly1305_donna_32.h" + +void poly1305_update(poly1305_context* ctx, const unsigned char* m, size_t bytes) { + poly1305_state_internal_t* st = (poly1305_state_internal_t*)ctx; + size_t i = 0; + + /* handle leftover */ + if(st->leftover) { + size_t want = (poly1305_block_size - st->leftover); + if(want > bytes) want = bytes; + for(i = 0; i < want; i++) st->buffer[st->leftover + i] = m[i]; + bytes -= want; + m += want; + st->leftover += want; + if(st->leftover < poly1305_block_size) return; + poly1305_blocks(st, st->buffer, poly1305_block_size); + st->leftover = 0; + } + + /* process full blocks */ + if(bytes >= poly1305_block_size) { + size_t want = (bytes & ~(poly1305_block_size - 1)); + poly1305_blocks(st, m, want); + m += want; + bytes -= want; + } + + /* store leftover */ + if(bytes) { + for(i = 0; i < bytes; i++) st->buffer[st->leftover + i] = m[i]; + st->leftover += bytes; + } +} + +void poly1305_auth( + unsigned char mac[16], + const unsigned char* m, + size_t bytes, + const unsigned char key[32]) { + poly1305_context ctx = {0}; + poly1305_init(&ctx, key); + poly1305_update(&ctx, m, bytes); + poly1305_finish(&ctx, mac); +} + +int poly1305_verify(const unsigned char mac1[16], const unsigned char mac2[16]) { + size_t i = 0; + unsigned int dif = 0; + for(i = 0; i < 16; i++) dif |= (mac1[i] ^ mac2[i]); + dif = (dif - 1) >> ((sizeof(unsigned int) * 8) - 1); + return (dif & 1); +} + +/* test a few basic operations */ +int poly1305_power_on_self_test(void) { + /* example from nacl */ + static const unsigned char nacl_key[32] = { + 0xee, 0xa6, 0xa7, 0x25, 0x1c, 0x1e, 0x72, 0x91, 0x6d, 0x11, 0xc2, + 0xcb, 0x21, 0x4d, 0x3c, 0x25, 0x25, 0x39, 0x12, 0x1d, 0x8e, 0x23, + 0x4e, 0x65, 0x2d, 0x65, 0x1f, 0xa4, 0xc8, 0xcf, 0xf8, 0x80, + }; + + static const unsigned char nacl_msg[131] = { + 0x8e, 0x99, 0x3b, 0x9f, 0x48, 0x68, 0x12, 0x73, 0xc2, 0x96, 0x50, 0xba, 0x32, 0xfc, 0x76, + 0xce, 0x48, 0x33, 0x2e, 0xa7, 0x16, 0x4d, 0x96, 0xa4, 0x47, 0x6f, 0xb8, 0xc5, 0x31, 0xa1, + 0x18, 0x6a, 0xc0, 0xdf, 0xc1, 0x7c, 0x98, 0xdc, 0xe8, 0x7b, 0x4d, 0xa7, 0xf0, 0x11, 0xec, + 0x48, 0xc9, 0x72, 0x71, 0xd2, 0xc2, 0x0f, 0x9b, 0x92, 0x8f, 0xe2, 0x27, 0x0d, 0x6f, 0xb8, + 0x63, 0xd5, 0x17, 0x38, 0xb4, 0x8e, 0xee, 0xe3, 0x14, 0xa7, 0xcc, 0x8a, 0xb9, 0x32, 0x16, + 0x45, 0x48, 0xe5, 0x26, 0xae, 0x90, 0x22, 0x43, 0x68, 0x51, 0x7a, 0xcf, 0xea, 0xbd, 0x6b, + 0xb3, 0x73, 0x2b, 0xc0, 0xe9, 0xda, 0x99, 0x83, 0x2b, 0x61, 0xca, 0x01, 0xb6, 0xde, 0x56, + 0x24, 0x4a, 0x9e, 0x88, 0xd5, 0xf9, 0xb3, 0x79, 0x73, 0xf6, 0x22, 0xa4, 0x3d, 0x14, 0xa6, + 0x59, 0x9b, 0x1f, 0x65, 0x4c, 0xb4, 0x5a, 0x74, 0xe3, 0x55, 0xa5}; + + static const unsigned char nacl_mac[16] = { + 0xf3, + 0xff, + 0xc7, + 0x70, + 0x3f, + 0x94, + 0x00, + 0xe5, + 0x2a, + 0x7d, + 0xfb, + 0x4b, + 0x3d, + 0x33, + 0x05, + 0xd9}; + + /* generates a final value of (2^130 - 2) == 3 */ + static const unsigned char wrap_key[32] = { + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + + static const unsigned char wrap_msg[16] = { + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff}; + + static const unsigned char wrap_mac[16] = { + 0x03, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + }; + + /* + mac of the macs of messages of length 0 to 256, where the key and messages + have all their values set to the length + */ + static const unsigned char total_key[32] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + + static const unsigned char total_mac[16] = { + 0x64, + 0xaf, + 0xe2, + 0xe8, + 0xd6, + 0xad, + 0x7b, + 0xbd, + 0xd2, + 0x87, + 0xf9, + 0x7c, + 0x44, + 0x62, + 0x3d, + 0x39}; + + poly1305_context ctx = {0}; + poly1305_context total_ctx = {0}; + unsigned char all_key[32] = {0}; + unsigned char all_msg[256] = {0}; + unsigned char mac[16] = {0}; + size_t i = 0, j = 0; + int result = 1; + + for(i = 0; i < sizeof(mac); i++) mac[i] = 0; + poly1305_auth(mac, nacl_msg, sizeof(nacl_msg), nacl_key); + result &= poly1305_verify(nacl_mac, mac); + + for(i = 0; i < sizeof(mac); i++) mac[i] = 0; + poly1305_init(&ctx, nacl_key); + poly1305_update(&ctx, nacl_msg + 0, 32); + poly1305_update(&ctx, nacl_msg + 32, 64); + poly1305_update(&ctx, nacl_msg + 96, 16); + poly1305_update(&ctx, nacl_msg + 112, 8); + poly1305_update(&ctx, nacl_msg + 120, 4); + poly1305_update(&ctx, nacl_msg + 124, 2); + poly1305_update(&ctx, nacl_msg + 126, 1); + poly1305_update(&ctx, nacl_msg + 127, 1); + poly1305_update(&ctx, nacl_msg + 128, 1); + poly1305_update(&ctx, nacl_msg + 129, 1); + poly1305_update(&ctx, nacl_msg + 130, 1); + poly1305_finish(&ctx, mac); + result &= poly1305_verify(nacl_mac, mac); + + for(i = 0; i < sizeof(mac); i++) mac[i] = 0; + poly1305_auth(mac, wrap_msg, sizeof(wrap_msg), wrap_key); + result &= poly1305_verify(wrap_mac, mac); + + poly1305_init(&total_ctx, total_key); + for(i = 0; i < 256; i++) { + /* set key and message to 'i,i,i..' */ + for(j = 0; j < sizeof(all_key); j++) all_key[j] = i; + for(j = 0; j < i; j++) all_msg[j] = i; + poly1305_auth(mac, all_msg, i, all_key); + poly1305_update(&total_ctx, mac, 16); + } + poly1305_finish(&total_ctx, mac); + result &= poly1305_verify(total_mac, mac); + + return result; +} diff --git a/applications/external/flipbip/lib/crypto/chacha20poly1305/poly1305_donna.h b/applications/external/flipbip/lib/crypto/chacha20poly1305/poly1305_donna.h new file mode 100644 index 0000000000..108dee1f6d --- /dev/null +++ b/applications/external/flipbip/lib/crypto/chacha20poly1305/poly1305_donna.h @@ -0,0 +1,23 @@ +#ifndef POLY1305_DONNA_H +#define POLY1305_DONNA_H + +#include + +typedef struct poly1305_context { + size_t aligner; + unsigned char opaque[136]; +} poly1305_context; + +void poly1305_init(poly1305_context* ctx, const unsigned char key[32]); +void poly1305_update(poly1305_context* ctx, const unsigned char* m, size_t bytes); +void poly1305_finish(poly1305_context* ctx, unsigned char mac[16]); +void poly1305_auth( + unsigned char mac[16], + const unsigned char* m, + size_t bytes, + const unsigned char key[32]); + +int poly1305_verify(const unsigned char mac1[16], const unsigned char mac2[16]); +int poly1305_power_on_self_test(void); + +#endif /* POLY1305_DONNA_H */ diff --git a/applications/external/flipbip/lib/crypto/chacha20poly1305/poly1305_donna_32.h b/applications/external/flipbip/lib/crypto/chacha20poly1305/poly1305_donna_32.h new file mode 100644 index 0000000000..c2289bb988 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/chacha20poly1305/poly1305_donna_32.h @@ -0,0 +1,252 @@ +/* + poly1305 implementation using 32 bit * 32 bit = 64 bit multiplication and 64 bit addition +*/ + +#if defined(_MSC_VER) +#define POLY1305_NOINLINE __declspec(noinline) +#elif defined(__GNUC__) +#define POLY1305_NOINLINE __attribute__((noinline)) +#else +#define POLY1305_NOINLINE +#endif + +#define poly1305_block_size 16 + +/* 17 + sizeof(size_t) + 14*sizeof(unsigned long) */ +typedef struct poly1305_state_internal_t { + unsigned long r[5]; + unsigned long h[5]; + unsigned long pad[4]; + size_t leftover; + unsigned char buffer[poly1305_block_size]; + unsigned char final; +} poly1305_state_internal_t; + +/* interpret four 8 bit unsigned integers as a 32 bit unsigned integer in little endian */ +static unsigned long U8TO32(const unsigned char* p) { + return ( + ((unsigned long)(p[0] & 0xff)) | ((unsigned long)(p[1] & 0xff) << 8) | + ((unsigned long)(p[2] & 0xff) << 16) | ((unsigned long)(p[3] & 0xff) << 24)); +} + +/* store a 32 bit unsigned integer as four 8 bit unsigned integers in little endian */ +static void U32TO8(unsigned char* p, unsigned long v) { + p[0] = (v)&0xff; + p[1] = (v >> 8) & 0xff; + p[2] = (v >> 16) & 0xff; + p[3] = (v >> 24) & 0xff; +} + +void poly1305_init(poly1305_context* ctx, const unsigned char key[32]) { + poly1305_state_internal_t* st = (poly1305_state_internal_t*)ctx; + + /* r &= 0xffffffc0ffffffc0ffffffc0fffffff */ + st->r[0] = (U8TO32(&key[0])) & 0x3ffffff; + st->r[1] = (U8TO32(&key[3]) >> 2) & 0x3ffff03; + st->r[2] = (U8TO32(&key[6]) >> 4) & 0x3ffc0ff; + st->r[3] = (U8TO32(&key[9]) >> 6) & 0x3f03fff; + st->r[4] = (U8TO32(&key[12]) >> 8) & 0x00fffff; + + /* h = 0 */ + st->h[0] = 0; + st->h[1] = 0; + st->h[2] = 0; + st->h[3] = 0; + st->h[4] = 0; + + /* save pad for later */ + st->pad[0] = U8TO32(&key[16]); + st->pad[1] = U8TO32(&key[20]); + st->pad[2] = U8TO32(&key[24]); + st->pad[3] = U8TO32(&key[28]); + + st->leftover = 0; + st->final = 0; +} + +static void poly1305_blocks(poly1305_state_internal_t* st, const unsigned char* m, size_t bytes) { + const unsigned long hibit = (st->final) ? 0 : (1UL << 24); /* 1 << 128 */ + unsigned long r0, r1, r2, r3, r4; + unsigned long s1, s2, s3, s4; + unsigned long h0, h1, h2, h3, h4; + unsigned long long d0, d1, d2, d3, d4; + unsigned long c; + + r0 = st->r[0]; + r1 = st->r[1]; + r2 = st->r[2]; + r3 = st->r[3]; + r4 = st->r[4]; + + s1 = r1 * 5; + s2 = r2 * 5; + s3 = r3 * 5; + s4 = r4 * 5; + + h0 = st->h[0]; + h1 = st->h[1]; + h2 = st->h[2]; + h3 = st->h[3]; + h4 = st->h[4]; + + while(bytes >= poly1305_block_size) { + /* h += m[i] */ + h0 += (U8TO32(m + 0)) & 0x3ffffff; + h1 += (U8TO32(m + 3) >> 2) & 0x3ffffff; + h2 += (U8TO32(m + 6) >> 4) & 0x3ffffff; + h3 += (U8TO32(m + 9) >> 6) & 0x3ffffff; + h4 += (U8TO32(m + 12) >> 8) | hibit; + + /* h *= r */ + d0 = ((unsigned long long)h0 * r0) + ((unsigned long long)h1 * s4) + + ((unsigned long long)h2 * s3) + ((unsigned long long)h3 * s2) + + ((unsigned long long)h4 * s1); + d1 = ((unsigned long long)h0 * r1) + ((unsigned long long)h1 * r0) + + ((unsigned long long)h2 * s4) + ((unsigned long long)h3 * s3) + + ((unsigned long long)h4 * s2); + d2 = ((unsigned long long)h0 * r2) + ((unsigned long long)h1 * r1) + + ((unsigned long long)h2 * r0) + ((unsigned long long)h3 * s4) + + ((unsigned long long)h4 * s3); + d3 = ((unsigned long long)h0 * r3) + ((unsigned long long)h1 * r2) + + ((unsigned long long)h2 * r1) + ((unsigned long long)h3 * r0) + + ((unsigned long long)h4 * s4); + d4 = ((unsigned long long)h0 * r4) + ((unsigned long long)h1 * r3) + + ((unsigned long long)h2 * r2) + ((unsigned long long)h3 * r1) + + ((unsigned long long)h4 * r0); + + /* (partial) h %= p */ + c = (unsigned long)(d0 >> 26); + h0 = (unsigned long)d0 & 0x3ffffff; + d1 += c; + c = (unsigned long)(d1 >> 26); + h1 = (unsigned long)d1 & 0x3ffffff; + d2 += c; + c = (unsigned long)(d2 >> 26); + h2 = (unsigned long)d2 & 0x3ffffff; + d3 += c; + c = (unsigned long)(d3 >> 26); + h3 = (unsigned long)d3 & 0x3ffffff; + d4 += c; + c = (unsigned long)(d4 >> 26); + h4 = (unsigned long)d4 & 0x3ffffff; + h0 += c * 5; + c = (h0 >> 26); + h0 = h0 & 0x3ffffff; + h1 += c; + + m += poly1305_block_size; + bytes -= poly1305_block_size; + } + + st->h[0] = h0; + st->h[1] = h1; + st->h[2] = h2; + st->h[3] = h3; + st->h[4] = h4; +} + +POLY1305_NOINLINE void poly1305_finish(poly1305_context* ctx, unsigned char mac[16]) { + poly1305_state_internal_t* st = (poly1305_state_internal_t*)ctx; + unsigned long h0, h1, h2, h3, h4, c; + unsigned long g0, g1, g2, g3, g4; + unsigned long long f; + unsigned long mask; + + /* process the remaining block */ + if(st->leftover) { + size_t i = st->leftover; + st->buffer[i++] = 1; + for(; i < poly1305_block_size; i++) st->buffer[i] = 0; + st->final = 1; + poly1305_blocks(st, st->buffer, poly1305_block_size); + } + + /* fully carry h */ + h0 = st->h[0]; + h1 = st->h[1]; + h2 = st->h[2]; + h3 = st->h[3]; + h4 = st->h[4]; + + c = h1 >> 26; + h1 = h1 & 0x3ffffff; + h2 += c; + c = h2 >> 26; + h2 = h2 & 0x3ffffff; + h3 += c; + c = h3 >> 26; + h3 = h3 & 0x3ffffff; + h4 += c; + c = h4 >> 26; + h4 = h4 & 0x3ffffff; + h0 += c * 5; + c = h0 >> 26; + h0 = h0 & 0x3ffffff; + h1 += c; + + /* compute h + -p */ + g0 = h0 + 5; + c = g0 >> 26; + g0 &= 0x3ffffff; + g1 = h1 + c; + c = g1 >> 26; + g1 &= 0x3ffffff; + g2 = h2 + c; + c = g2 >> 26; + g2 &= 0x3ffffff; + g3 = h3 + c; + c = g3 >> 26; + g3 &= 0x3ffffff; + g4 = h4 + c - (1UL << 26); + + /* select h if h < p, or h + -p if h >= p */ + mask = (g4 >> ((sizeof(unsigned long) * 8) - 1)) - 1; + g0 &= mask; + g1 &= mask; + g2 &= mask; + g3 &= mask; + g4 &= mask; + mask = ~mask; + h0 = (h0 & mask) | g0; + h1 = (h1 & mask) | g1; + h2 = (h2 & mask) | g2; + h3 = (h3 & mask) | g3; + h4 = (h4 & mask) | g4; + + /* h = h % (2^128) */ + h0 = ((h0) | (h1 << 26)) & 0xffffffff; + h1 = ((h1 >> 6) | (h2 << 20)) & 0xffffffff; + h2 = ((h2 >> 12) | (h3 << 14)) & 0xffffffff; + h3 = ((h3 >> 18) | (h4 << 8)) & 0xffffffff; + + /* mac = (h + pad) % (2^128) */ + f = (unsigned long long)h0 + st->pad[0]; + h0 = (unsigned long)f; + f = (unsigned long long)h1 + st->pad[1] + (f >> 32); + h1 = (unsigned long)f; + f = (unsigned long long)h2 + st->pad[2] + (f >> 32); + h2 = (unsigned long)f; + f = (unsigned long long)h3 + st->pad[3] + (f >> 32); + h3 = (unsigned long)f; + + U32TO8(mac + 0, h0); + U32TO8(mac + 4, h1); + U32TO8(mac + 8, h2); + U32TO8(mac + 12, h3); + + /* zero out the state */ + st->h[0] = 0; + st->h[1] = 0; + st->h[2] = 0; + st->h[3] = 0; + st->h[4] = 0; + st->r[0] = 0; + st->r[1] = 0; + st->r[2] = 0; + st->r[3] = 0; + st->r[4] = 0; + st->pad[0] = 0; + st->pad[1] = 0; + st->pad[2] = 0; + st->pad[3] = 0; +} diff --git a/applications/external/flipbip/lib/crypto/chacha20poly1305/rfc7539.c b/applications/external/flipbip/lib/crypto/chacha20poly1305/rfc7539.c new file mode 100644 index 0000000000..f26026fbc5 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/chacha20poly1305/rfc7539.c @@ -0,0 +1,46 @@ +// Implementation of the ChaCha20 + Poly1305 AEAD construction +// as described in RFC 7539. + +#include +#include "rfc7539.h" +#include "ecrypt_portable.h" + +// Initialize the ChaCha20 + Poly1305 context for encryption or decryption +// using a 32 byte key and 12 byte nonce as in the RFC 7539 style. +void rfc7539_init(chacha20poly1305_ctx* ctx, const uint8_t key[32], const uint8_t nonce[12]) { + unsigned char block0[64] = {0}; + + ECRYPT_keysetup(&ctx->chacha20, key, 256, 16); + ctx->chacha20.input[12] = 0; + ctx->chacha20.input[13] = U8TO32_LITTLE(nonce + 0); + ctx->chacha20.input[14] = U8TO32_LITTLE(nonce + 4); + ctx->chacha20.input[15] = U8TO32_LITTLE(nonce + 8); + + // Encrypt 64 bytes of zeros and use the first 32 bytes + // as the Poly1305 key. + ECRYPT_encrypt_bytes(&ctx->chacha20, block0, block0, 64); + poly1305_init(&ctx->poly1305, block0); +} + +// Include authenticated data in the Poly1305 MAC using the RFC 7539 +// style with 16 byte padding. This must only be called once and prior +// to encryption or decryption. +void rfc7539_auth(chacha20poly1305_ctx* ctx, const uint8_t* in, size_t n) { + uint8_t padding[16] = {0}; + poly1305_update(&ctx->poly1305, in, n); + if(n % 16 != 0) poly1305_update(&ctx->poly1305, padding, 16 - n % 16); +} + +// Compute RFC 7539-style Poly1305 MAC. +void rfc7539_finish(chacha20poly1305_ctx* ctx, int64_t alen, int64_t plen, uint8_t mac[16]) { + uint8_t padding[16] = {0}; + uint8_t lengths[16] = {0}; + + memcpy(lengths, &alen, sizeof(int64_t)); + memcpy(lengths + 8, &plen, sizeof(int64_t)); + + if(plen % 16 != 0) poly1305_update(&ctx->poly1305, padding, 16 - plen % 16); + poly1305_update(&ctx->poly1305, lengths, 16); + + poly1305_finish(&ctx->poly1305, mac); +} diff --git a/applications/external/flipbip/lib/crypto/chacha20poly1305/rfc7539.h b/applications/external/flipbip/lib/crypto/chacha20poly1305/rfc7539.h new file mode 100644 index 0000000000..262dc433bb --- /dev/null +++ b/applications/external/flipbip/lib/crypto/chacha20poly1305/rfc7539.h @@ -0,0 +1,10 @@ +#ifndef RFC7539_H +#define RFC7539_H + +#include "chacha20poly1305.h" + +void rfc7539_init(chacha20poly1305_ctx* ctx, const uint8_t key[32], const uint8_t nonce[12]); +void rfc7539_auth(chacha20poly1305_ctx* ctx, const uint8_t* in, size_t n); +void rfc7539_finish(chacha20poly1305_ctx* ctx, int64_t alen, int64_t plen, uint8_t mac[16]); + +#endif // RFC7539_H diff --git a/applications/external/flipbip/lib/crypto/chacha_drbg.c b/applications/external/flipbip/lib/crypto/chacha_drbg.c new file mode 100644 index 0000000000..fe9b5fd405 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/chacha_drbg.c @@ -0,0 +1,131 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "chacha_drbg.h" + +#include +#include +#include + +#include "chacha20poly1305/ecrypt_portable.h" +#include "memzero.h" +#include "sha2.h" + +#define CHACHA_DRBG_KEY_LENGTH 32 +#define CHACHA_DRBG_COUNTER_LENGTH 8 +#define CHACHA_DRBG_IV_LENGTH 8 +#define CHACHA_DRBG_SEED_LENGTH \ + (CHACHA_DRBG_KEY_LENGTH + CHACHA_DRBG_COUNTER_LENGTH + CHACHA_DRBG_IV_LENGTH) + +#define MAX(a, b) (a) > (b) ? (a) : (b) + +static void derivation_function( + const uint8_t* input1, + size_t input1_length, + const uint8_t* input2, + size_t input2_length, + uint8_t* output, + size_t output_length) { + // Implementation of Hash_df from NIST SP 800-90A + uint32_t block_count = (output_length - 1) / SHA256_DIGEST_LENGTH + 1; + size_t partial_block_length = output_length % SHA256_DIGEST_LENGTH; + assert(block_count <= 255); + + uint32_t output_length_bits = output_length * 8; +#if BYTE_ORDER == LITTLE_ENDIAN + REVERSE32(output_length_bits, output_length_bits); +#endif + + SHA256_CTX ctx = {0}; + + for(uint8_t counter = 1; counter <= block_count; counter++) { + sha256_Init(&ctx); + sha256_Update(&ctx, &counter, sizeof(counter)); + sha256_Update(&ctx, (uint8_t*)&output_length_bits, sizeof(output_length_bits)); + sha256_Update(&ctx, input1, input1_length); + sha256_Update(&ctx, input2, input2_length); + + if(counter != block_count || partial_block_length == 0) { + sha256_Final(&ctx, output); + output += SHA256_DIGEST_LENGTH; + } else { // last block is partial + uint8_t digest[SHA256_DIGEST_LENGTH] = {0}; + sha256_Final(&ctx, digest); + memcpy(output, digest, partial_block_length); + memzero(digest, sizeof(digest)); + } + } + + memzero(&ctx, sizeof(ctx)); +} + +void chacha_drbg_init( + CHACHA_DRBG_CTX* ctx, + const uint8_t* entropy, + size_t entropy_length, + const uint8_t* nonce, + size_t nonce_length) { + uint8_t buffer[MAX(CHACHA_DRBG_KEY_LENGTH, CHACHA_DRBG_IV_LENGTH)] = {0}; + ECRYPT_keysetup( + &ctx->chacha_ctx, buffer, CHACHA_DRBG_KEY_LENGTH * 8, CHACHA_DRBG_IV_LENGTH * 8); + ECRYPT_ivsetup(&ctx->chacha_ctx, buffer); + + chacha_drbg_reseed(ctx, entropy, entropy_length, nonce, nonce_length); +} + +static void chacha_drbg_update(CHACHA_DRBG_CTX* ctx, const uint8_t data[CHACHA_DRBG_SEED_LENGTH]) { + uint8_t seed[CHACHA_DRBG_SEED_LENGTH] = {0}; + + if(data) + ECRYPT_encrypt_bytes(&ctx->chacha_ctx, data, seed, CHACHA_DRBG_SEED_LENGTH); + else + ECRYPT_keystream_bytes(&ctx->chacha_ctx, seed, CHACHA_DRBG_SEED_LENGTH); + + ECRYPT_keysetup(&ctx->chacha_ctx, seed, CHACHA_DRBG_KEY_LENGTH * 8, CHACHA_DRBG_IV_LENGTH * 8); + + ECRYPT_ivsetup(&ctx->chacha_ctx, seed + CHACHA_DRBG_KEY_LENGTH + CHACHA_DRBG_COUNTER_LENGTH); + + ECRYPT_ctrsetup(&ctx->chacha_ctx, seed + CHACHA_DRBG_KEY_LENGTH); + + memzero(seed, sizeof(seed)); +} + +void chacha_drbg_generate(CHACHA_DRBG_CTX* ctx, uint8_t* output, size_t output_length) { + assert(output_length < 65536); + assert(ctx->reseed_counter + 1 != 0); + + ECRYPT_keystream_bytes(&ctx->chacha_ctx, output, output_length); + chacha_drbg_update(ctx, NULL); + ctx->reseed_counter++; +} + +void chacha_drbg_reseed( + CHACHA_DRBG_CTX* ctx, + const uint8_t* entropy, + size_t entropy_length, + const uint8_t* additional_input, + size_t additional_input_length) { + uint8_t seed[CHACHA_DRBG_SEED_LENGTH] = {0}; + derivation_function( + entropy, entropy_length, additional_input, additional_input_length, seed, sizeof(seed)); + chacha_drbg_update(ctx, seed); + memzero(seed, sizeof(seed)); + + ctx->reseed_counter = 1; +} diff --git a/applications/external/flipbip/lib/crypto/chacha_drbg.h b/applications/external/flipbip/lib/crypto/chacha_drbg.h new file mode 100644 index 0000000000..740c11c6cc --- /dev/null +++ b/applications/external/flipbip/lib/crypto/chacha_drbg.h @@ -0,0 +1,59 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __CHACHA_DRBG__ +#define __CHACHA_DRBG__ + +#include "chacha20poly1305/chacha20poly1305.h" +#include "sha2.h" + +// A very fast deterministic random bit generator based on CTR_DRBG in NIST SP +// 800-90A. Chacha is used instead of a block cipher in the counter mode, SHA256 +// is used as a derivation function. The highest supported security strength is +// at least 256 bits. Reseeding is left up to caller. + +// Length of inputs of chacha_drbg_init (entropy and nonce) or +// chacha_drbg_reseed (entropy and additional_input) that fill exactly +// block_count blocks of hash function in derivation_function. There is no need +// the input to have this length, it's just an optimalization. +#define CHACHA_DRBG_OPTIMAL_RESEED_LENGTH(block_count) \ + ((block_count)*SHA256_BLOCK_LENGTH - 1 - 4 - 9) +// 1 = sizeof(counter), 4 = sizeof(output_length) in +// derivation_function, 9 is length of SHA256 padding of message +// aligned to bytes + +typedef struct _CHACHA_DRBG_CTX { + ECRYPT_ctx chacha_ctx; + uint32_t reseed_counter; +} CHACHA_DRBG_CTX; + +void chacha_drbg_init( + CHACHA_DRBG_CTX* ctx, + const uint8_t* entropy, + size_t entropy_length, + const uint8_t* nonce, + size_t nonce_length); +void chacha_drbg_generate(CHACHA_DRBG_CTX* ctx, uint8_t* output, size_t output_length); +void chacha_drbg_reseed( + CHACHA_DRBG_CTX* ctx, + const uint8_t* entropy, + size_t entropy_length, + const uint8_t* additional_input, + size_t additional_input_length); +#endif // __CHACHA_DRBG__ diff --git a/applications/external/flipbip/lib/crypto/check_mem.h b/applications/external/flipbip/lib/crypto/check_mem.h new file mode 100644 index 0000000000..ef8f8c79a5 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/check_mem.h @@ -0,0 +1,34 @@ +#ifndef CHECK_MEM_H +#define CHECK_MEM_H + +#if CHECK_MAJOR_VERSION == 0 && CHECK_MINOR_VERSION < 11 + +#define _ck_assert_mem(X, Y, L, OP) \ + do { \ + const char* _ck_x = (const char*)(void*)(X); \ + const char* _ck_y = (const char*)(void*)(Y); \ + size_t _ck_l = (L); \ + char _ck_x_str[129]; \ + char _ck_y_str[129]; \ + static char _ck_hexdigits[] = "0123456789abcdef"; \ + size_t _ck_i; \ + for(_ck_i = 0; _ck_i < ((_ck_l > 64) ? 64 : _ck_l); _ck_i++) { \ + _ck_x_str[_ck_i * 2] = _ck_hexdigits[(_ck_x[_ck_i] >> 4) & 0xF]; \ + _ck_y_str[_ck_i * 2] = _ck_hexdigits[(_ck_y[_ck_i] >> 4) & 0xF]; \ + _ck_x_str[_ck_i * 2 + 1] = _ck_hexdigits[_ck_x[_ck_i] & 0xF]; \ + _ck_y_str[_ck_i * 2 + 1] = _ck_hexdigits[_ck_y[_ck_i] & 0xF]; \ + } \ + _ck_x_str[_ck_i * 2] = 0; \ + _ck_y_str[_ck_i * 2] = 0; \ + ck_assert_msg( \ + 0 OP memcmp(_ck_y, _ck_x, _ck_l), \ + "Assertion '" #X #OP #Y "' failed: " #X "==\"%s\", " #Y "==\"%s\"", \ + _ck_x_str, \ + _ck_y_str); \ + } while(0) +#define ck_assert_mem_eq(X, Y, L) _ck_assert_mem(X, Y, L, ==) +#define ck_assert_mem_ne(X, Y, L) _ck_assert_mem(X, Y, L, !=) + +#endif + +#endif diff --git a/applications/external/flipbip/lib/crypto/curves.c b/applications/external/flipbip/lib/crypto/curves.c new file mode 100644 index 0000000000..dcdaf7ba45 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/curves.c @@ -0,0 +1,39 @@ +/** + * Copyright (c) 2016 Jochen Hoenicke + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "curves.h" + +const char SECP256K1_NAME[] = "secp256k1"; +const char SECP256K1_DECRED_NAME[] = "secp256k1-decred"; +const char SECP256K1_GROESTL_NAME[] = "secp256k1-groestl"; +const char SECP256K1_SMART_NAME[] = "secp256k1-smart"; +const char NIST256P1_NAME[] = "nist256p1"; +const char ED25519_NAME[] = "ed25519"; +const char ED25519_SEED_NAME[] = "ed25519 seed"; +#if USE_CARDANO +const char ED25519_CARDANO_NAME[] = "ed25519 cardano seed"; +#endif +const char ED25519_SHA3_NAME[] = "ed25519-sha3"; +#if USE_KECCAK +const char ED25519_KECCAK_NAME[] = "ed25519-keccak"; +#endif +const char CURVE25519_NAME[] = "curve25519"; diff --git a/applications/external/flipbip/lib/crypto/curves.h b/applications/external/flipbip/lib/crypto/curves.h new file mode 100644 index 0000000000..aabf5b3693 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/curves.h @@ -0,0 +1,42 @@ +/** + * Copyright (c) 2016 Jochen Hoenicke + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __CURVES_H__ +#define __CURVES_H__ + +#include "options.h" + +extern const char SECP256K1_NAME[]; +extern const char SECP256K1_DECRED_NAME[]; +extern const char SECP256K1_GROESTL_NAME[]; +extern const char SECP256K1_SMART_NAME[]; +extern const char NIST256P1_NAME[]; +extern const char ED25519_NAME[]; +extern const char ED25519_SEED_NAME[]; +extern const char ED25519_CARDANO_NAME[]; +extern const char ED25519_SHA3_NAME[]; +#if USE_KECCAK +extern const char ED25519_KECCAK_NAME[]; +#endif +extern const char CURVE25519_NAME[]; + +#endif diff --git a/applications/external/flipbip/lib/crypto/ecdsa.c b/applications/external/flipbip/lib/crypto/ecdsa.c new file mode 100644 index 0000000000..80ab76b493 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/ecdsa.c @@ -0,0 +1,1284 @@ +/** + * Copyright (c) 2013-2014 Tomas Dzetkulic + * Copyright (c) 2013-2014 Pavol Rusnak + * Copyright (c) 2015 Jochen Hoenicke + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include + +#include "address.h" +#include "base58.h" +#include "bignum.h" +#include "ecdsa.h" +#include "hmac.h" +#include "memzero.h" +#include "rand.h" +#include "rfc6979.h" +#include "secp256k1.h" + +// Set cp2 = cp1 +void point_copy(const curve_point* cp1, curve_point* cp2) { + *cp2 = *cp1; +} + +// cp2 = cp1 + cp2 +void point_add(const ecdsa_curve* curve, const curve_point* cp1, curve_point* cp2) { + bignum256 lambda = {0}, inv = {0}, xr = {0}, yr = {0}; + + if(point_is_infinity(cp1)) { + return; + } + if(point_is_infinity(cp2)) { + point_copy(cp1, cp2); + return; + } + if(point_is_equal(cp1, cp2)) { + point_double(curve, cp2); + return; + } + if(point_is_negative_of(cp1, cp2)) { + point_set_infinity(cp2); + return; + } + + // lambda = (y2 - y1) / (x2 - x1) + bn_subtractmod(&(cp2->x), &(cp1->x), &inv, &curve->prime); + bn_inverse(&inv, &curve->prime); + bn_subtractmod(&(cp2->y), &(cp1->y), &lambda, &curve->prime); + bn_multiply(&inv, &lambda, &curve->prime); + + // xr = lambda^2 - x1 - x2 + xr = lambda; + bn_multiply(&xr, &xr, &curve->prime); + yr = cp1->x; + bn_addmod(&yr, &(cp2->x), &curve->prime); + bn_subtractmod(&xr, &yr, &xr, &curve->prime); + bn_fast_mod(&xr, &curve->prime); + bn_mod(&xr, &curve->prime); + + // yr = lambda (x1 - xr) - y1 + bn_subtractmod(&(cp1->x), &xr, &yr, &curve->prime); + bn_multiply(&lambda, &yr, &curve->prime); + bn_subtractmod(&yr, &(cp1->y), &yr, &curve->prime); + bn_fast_mod(&yr, &curve->prime); + bn_mod(&yr, &curve->prime); + + cp2->x = xr; + cp2->y = yr; +} + +// cp = cp + cp +void point_double(const ecdsa_curve* curve, curve_point* cp) { + bignum256 lambda = {0}, xr = {0}, yr = {0}; + + if(point_is_infinity(cp)) { + return; + } + if(bn_is_zero(&(cp->y))) { + point_set_infinity(cp); + return; + } + + // lambda = (3 x^2 + a) / (2 y) + lambda = cp->y; + bn_mult_k(&lambda, 2, &curve->prime); + bn_fast_mod(&lambda, &curve->prime); + bn_mod(&lambda, &curve->prime); + bn_inverse(&lambda, &curve->prime); + + xr = cp->x; + bn_multiply(&xr, &xr, &curve->prime); + bn_mult_k(&xr, 3, &curve->prime); + bn_subi(&xr, -curve->a, &curve->prime); + bn_multiply(&xr, &lambda, &curve->prime); + + // xr = lambda^2 - 2*x + xr = lambda; + bn_multiply(&xr, &xr, &curve->prime); + yr = cp->x; + bn_lshift(&yr); + bn_subtractmod(&xr, &yr, &xr, &curve->prime); + bn_fast_mod(&xr, &curve->prime); + bn_mod(&xr, &curve->prime); + + // yr = lambda (x - xr) - y + bn_subtractmod(&(cp->x), &xr, &yr, &curve->prime); + bn_multiply(&lambda, &yr, &curve->prime); + bn_subtractmod(&yr, &(cp->y), &yr, &curve->prime); + bn_fast_mod(&yr, &curve->prime); + bn_mod(&yr, &curve->prime); + + cp->x = xr; + cp->y = yr; +} + +// set point to internal representation of point at infinity +void point_set_infinity(curve_point* p) { + bn_zero(&(p->x)); + bn_zero(&(p->y)); +} + +// return true iff p represent point at infinity +// both coords are zero in internal representation +int point_is_infinity(const curve_point* p) { + return bn_is_zero(&(p->x)) && bn_is_zero(&(p->y)); +} + +// return true iff both points are equal +int point_is_equal(const curve_point* p, const curve_point* q) { + return bn_is_equal(&(p->x), &(q->x)) && bn_is_equal(&(p->y), &(q->y)); +} + +// returns true iff p == -q +// expects p and q be valid points on curve other than point at infinity +int point_is_negative_of(const curve_point* p, const curve_point* q) { + // if P == (x, y), then -P would be (x, -y) on this curve + if(!bn_is_equal(&(p->x), &(q->x))) { + return 0; + } + + // we shouldn't hit this for a valid point + if(bn_is_zero(&(p->y))) { + return 0; + } + + return !bn_is_equal(&(p->y), &(q->y)); +} + +typedef struct jacobian_curve_point { + bignum256 x, y, z; +} jacobian_curve_point; + +// generate random K for signing/side-channel noise +static void generate_k_random(bignum256* k, const bignum256* prime) { + do { + int i = 0; + for(i = 0; i < 8; i++) { + k->val[i] = random32() & ((1u << BN_BITS_PER_LIMB) - 1); + } + k->val[8] = random32() & ((1u << BN_BITS_LAST_LIMB) - 1); + // check that k is in range and not zero. + } while(bn_is_zero(k) || !bn_is_less(k, prime)); +} + +void curve_to_jacobian(const curve_point* p, jacobian_curve_point* jp, const bignum256* prime) { + // randomize z coordinate + generate_k_random(&jp->z, prime); + + jp->x = jp->z; + bn_multiply(&jp->z, &jp->x, prime); + // x = z^2 + jp->y = jp->x; + bn_multiply(&jp->z, &jp->y, prime); + // y = z^3 + + bn_multiply(&p->x, &jp->x, prime); + bn_multiply(&p->y, &jp->y, prime); +} + +void jacobian_to_curve(const jacobian_curve_point* jp, curve_point* p, const bignum256* prime) { + p->y = jp->z; + bn_inverse(&p->y, prime); + // p->y = z^-1 + p->x = p->y; + bn_multiply(&p->x, &p->x, prime); + // p->x = z^-2 + bn_multiply(&p->x, &p->y, prime); + // p->y = z^-3 + bn_multiply(&jp->x, &p->x, prime); + // p->x = jp->x * z^-2 + bn_multiply(&jp->y, &p->y, prime); + // p->y = jp->y * z^-3 + bn_mod(&p->x, prime); + bn_mod(&p->y, prime); +} + +void point_jacobian_add(const curve_point* p1, jacobian_curve_point* p2, const ecdsa_curve* curve) { + bignum256 r = {0}, h = {0}, r2 = {0}; + bignum256 hcby = {0}, hsqx = {0}; + bignum256 xz = {0}, yz = {0}, az = {0}; + int is_doubling = 0; + const bignum256* prime = &curve->prime; + int a = curve->a; + + assert(-3 <= a && a <= 0); + + /* First we bring p1 to the same denominator: + * x1' := x1 * z2^2 + * y1' := y1 * z2^3 + */ + /* + * lambda = ((y1' - y2)/z2^3) / ((x1' - x2)/z2^2) + * = (y1' - y2) / (x1' - x2) z2 + * x3/z3^2 = lambda^2 - (x1' + x2)/z2^2 + * y3/z3^3 = 1/2 lambda * (2x3/z3^2 - (x1' + x2)/z2^2) + (y1'+y2)/z2^3 + * + * For the special case x1=x2, y1=y2 (doubling) we have + * lambda = 3/2 ((x2/z2^2)^2 + a) / (y2/z2^3) + * = 3/2 (x2^2 + a*z2^4) / y2*z2) + * + * to get rid of fraction we write lambda as + * lambda = r / (h*z2) + * with r = is_doubling ? 3/2 x2^2 + az2^4 : (y1 - y2) + * h = is_doubling ? y1+y2 : (x1 - x2) + * + * With z3 = h*z2 (the denominator of lambda) + * we get x3 = lambda^2*z3^2 - (x1' + x2)/z2^2*z3^2 + * = r^2 - h^2 * (x1' + x2) + * and y3 = 1/2 r * (2x3 - h^2*(x1' + x2)) + h^3*(y1' + y2) + */ + + /* h = x1 - x2 + * r = y1 - y2 + * x3 = r^2 - h^3 - 2*h^2*x2 + * y3 = r*(h^2*x2 - x3) - h^3*y2 + * z3 = h*z2 + */ + + xz = p2->z; + bn_multiply(&xz, &xz, prime); // xz = z2^2 + yz = p2->z; + bn_multiply(&xz, &yz, prime); // yz = z2^3 + + if(a != 0) { + az = xz; + bn_multiply(&az, &az, prime); // az = z2^4 + bn_mult_k(&az, -a, prime); // az = -az2^4 + } + + bn_multiply(&p1->x, &xz, prime); // xz = x1' = x1*z2^2; + h = xz; + bn_subtractmod(&h, &p2->x, &h, prime); + bn_fast_mod(&h, prime); + // h = x1' - x2; + + bn_add(&xz, &p2->x); + // xz = x1' + x2 + + // check for h == 0 % prime. Note that h never normalizes to + // zero, since h = x1' + 2*prime - x2 > 0 and a positive + // multiple of prime is always normalized to prime by + // bn_fast_mod. + is_doubling = bn_is_equal(&h, prime); + + bn_multiply(&p1->y, &yz, prime); // yz = y1' = y1*z2^3; + bn_subtractmod(&yz, &p2->y, &r, prime); + // r = y1' - y2; + + bn_add(&yz, &p2->y); + // yz = y1' + y2 + + r2 = p2->x; + bn_multiply(&r2, &r2, prime); + bn_mult_k(&r2, 3, prime); + + if(a != 0) { + // subtract -a z2^4, i.e, add a z2^4 + bn_subtractmod(&r2, &az, &r2, prime); + } + bn_cmov(&r, is_doubling, &r2, &r); + bn_cmov(&h, is_doubling, &yz, &h); + + // hsqx = h^2 + hsqx = h; + bn_multiply(&hsqx, &hsqx, prime); + + // hcby = h^3 + hcby = h; + bn_multiply(&hsqx, &hcby, prime); + + // hsqx = h^2 * (x1 + x2) + bn_multiply(&xz, &hsqx, prime); + + // hcby = h^3 * (y1 + y2) + bn_multiply(&yz, &hcby, prime); + + // z3 = h*z2 + bn_multiply(&h, &p2->z, prime); + + // x3 = r^2 - h^2 (x1 + x2) + p2->x = r; + bn_multiply(&p2->x, &p2->x, prime); + bn_subtractmod(&p2->x, &hsqx, &p2->x, prime); + bn_fast_mod(&p2->x, prime); + + // y3 = 1/2 (r*(h^2 (x1 + x2) - 2x3) - h^3 (y1 + y2)) + bn_subtractmod(&hsqx, &p2->x, &p2->y, prime); + bn_subtractmod(&p2->y, &p2->x, &p2->y, prime); + bn_multiply(&r, &p2->y, prime); + bn_subtractmod(&p2->y, &hcby, &p2->y, prime); + bn_mult_half(&p2->y, prime); + bn_fast_mod(&p2->y, prime); +} + +void point_jacobian_double(jacobian_curve_point* p, const ecdsa_curve* curve) { + bignum256 az4 = {0}, m = {0}, msq = {0}, ysq = {0}, xysq = {0}; + const bignum256* prime = &curve->prime; + + assert(-3 <= curve->a && curve->a <= 0); + /* usual algorithm: + * + * lambda = (3((x/z^2)^2 + a) / 2y/z^3) = (3x^2 + az^4)/2yz + * x3/z3^2 = lambda^2 - 2x/z^2 + * y3/z3^3 = lambda * (x/z^2 - x3/z3^2) - y/z^3 + * + * to get rid of fraction we set + * m = (3 x^2 + az^4) / 2 + * Hence, + * lambda = m / yz = m / z3 + * + * With z3 = yz (the denominator of lambda) + * we get x3 = lambda^2*z3^2 - 2*x/z^2*z3^2 + * = m^2 - 2*xy^2 + * and y3 = (lambda * (x/z^2 - x3/z3^2) - y/z^3) * z3^3 + * = m * (xy^2 - x3) - y^4 + */ + + /* m = (3*x^2 + a z^4) / 2 + * x3 = m^2 - 2*xy^2 + * y3 = m*(xy^2 - x3) - 8y^4 + * z3 = y*z + */ + + m = p->x; + bn_multiply(&m, &m, prime); + bn_mult_k(&m, 3, prime); + + az4 = p->z; + bn_multiply(&az4, &az4, prime); + bn_multiply(&az4, &az4, prime); + bn_mult_k(&az4, -curve->a, prime); + bn_subtractmod(&m, &az4, &m, prime); + bn_mult_half(&m, prime); + + // msq = m^2 + msq = m; + bn_multiply(&msq, &msq, prime); + // ysq = y^2 + ysq = p->y; + bn_multiply(&ysq, &ysq, prime); + // xysq = xy^2 + xysq = p->x; + bn_multiply(&ysq, &xysq, prime); + + // z3 = yz + bn_multiply(&p->y, &p->z, prime); + + // x3 = m^2 - 2*xy^2 + p->x = xysq; + bn_lshift(&p->x); + bn_fast_mod(&p->x, prime); + bn_subtractmod(&msq, &p->x, &p->x, prime); + bn_fast_mod(&p->x, prime); + + // y3 = m*(xy^2 - x3) - y^4 + bn_subtractmod(&xysq, &p->x, &p->y, prime); + bn_multiply(&m, &p->y, prime); + bn_multiply(&ysq, &ysq, prime); + bn_subtractmod(&p->y, &ysq, &p->y, prime); + bn_fast_mod(&p->y, prime); +} + +// res = k * p +// returns 0 on success +int point_multiply( + const ecdsa_curve* curve, + const bignum256* k, + const curve_point* p, + curve_point* res) { + // this algorithm is loosely based on + // Katsuyuki Okeya and Tsuyoshi Takagi, The Width-w NAF Method Provides + // Small Memory and Fast Elliptic Scalar Multiplications Secure against + // Side Channel Attacks. + if(!bn_is_less(k, &curve->order)) { + return 1; + } + + int i = 0, j = 0; + static CONFIDENTIAL bignum256 a; + uint32_t* aptr = NULL; + uint32_t abits = 0; + int ashift = 0; + uint32_t is_even = (k->val[0] & 1) - 1; + uint32_t bits = {0}, sign = {0}, nsign = {0}; + static CONFIDENTIAL jacobian_curve_point jres; + curve_point pmult[8] = {0}; + const bignum256* prime = &curve->prime; + + // is_even = 0xffffffff if k is even, 0 otherwise. + + // add 2^256. + // make number odd: subtract curve->order if even + uint32_t tmp = 1; + uint32_t is_non_zero = 0; + for(j = 0; j < 8; j++) { + is_non_zero |= k->val[j]; + tmp += (BN_BASE - 1) + k->val[j] - (curve->order.val[j] & is_even); + a.val[j] = tmp & (BN_BASE - 1); + tmp >>= BN_BITS_PER_LIMB; + } + is_non_zero |= k->val[j]; + a.val[j] = tmp + 0xffffff + k->val[j] - (curve->order.val[j] & is_even); + assert((a.val[0] & 1) != 0); + + // special case 0*p: just return zero. We don't care about constant time. + if(!is_non_zero) { + point_set_infinity(res); + return 1; + } + + // Now a = k + 2^256 (mod curve->order) and a is odd. + // + // The idea is to bring the new a into the form. + // sum_{i=0..64} a[i] 16^i, where |a[i]| < 16 and a[i] is odd. + // a[0] is odd, since a is odd. If a[i] would be even, we can + // add 1 to it and subtract 16 from a[i-1]. Afterwards, + // a[64] = 1, which is the 2^256 that we added before. + // + // Since k = a - 2^256 (mod curve->order), we can compute + // k*p = sum_{i=0..63} a[i] 16^i * p + // + // We compute |a[i]| * p in advance for all possible + // values of |a[i]| * p. pmult[i] = (2*i+1) * p + // We compute p, 3*p, ..., 15*p and store it in the table pmult. + // store p^2 temporarily in pmult[7] + pmult[7] = *p; + point_double(curve, &pmult[7]); + // compute 3*p, etc by repeatedly adding p^2. + pmult[0] = *p; + for(i = 1; i < 8; i++) { + pmult[i] = pmult[7]; + point_add(curve, &pmult[i - 1], &pmult[i]); + } + + // now compute res = sum_{i=0..63} a[i] * 16^i * p step by step, + // starting with i = 63. + // initialize jres = |a[63]| * p. + // Note that a[i] = a>>(4*i) & 0xf if (a&0x10) != 0 + // and - (16 - (a>>(4*i) & 0xf)) otherwise. We can compute this as + // ((a ^ (((a >> 4) & 1) - 1)) & 0xf) >> 1 + // since a is odd. + aptr = &a.val[8]; + abits = *aptr; + ashift = 256 - (BN_BITS_PER_LIMB * 8) - 4; + bits = abits >> ashift; + sign = (bits >> 4) - 1; + bits ^= sign; + bits &= 15; + curve_to_jacobian(&pmult[bits >> 1], &jres, prime); + for(i = 62; i >= 0; i--) { + // sign = sign(a[i+1]) (0xffffffff for negative, 0 for positive) + // invariant jres = (-1)^sign sum_{j=i+1..63} (a[j] * 16^{j-i-1} * p) + // abits >> (ashift - 4) = lowbits(a >> (i*4)) + + point_jacobian_double(&jres, curve); + point_jacobian_double(&jres, curve); + point_jacobian_double(&jres, curve); + point_jacobian_double(&jres, curve); + + // get lowest 5 bits of a >> (i*4). + ashift -= 4; + if(ashift < 0) { + // the condition only depends on the iteration number and + // leaks no private information to a side-channel. + bits = abits << (-ashift); + abits = *(--aptr); + ashift += BN_BITS_PER_LIMB; + bits |= abits >> ashift; + } else { + bits = abits >> ashift; + } + bits &= 31; + nsign = (bits >> 4) - 1; + bits ^= nsign; + bits &= 15; + + // negate last result to make signs of this round and the + // last round equal. + bn_cnegate((sign ^ nsign) & 1, &jres.z, prime); + + // add odd factor + point_jacobian_add(&pmult[bits >> 1], &jres, curve); + sign = nsign; + } + bn_cnegate(sign & 1, &jres.z, prime); + jacobian_to_curve(&jres, res, prime); + memzero(&a, sizeof(a)); + memzero(&jres, sizeof(jres)); + + return 0; +} + +#if USE_PRECOMPUTED_CP + +// res = k * G +// k must be a normalized number with 0 <= k < curve->order +// returns 0 on success +int scalar_multiply(const ecdsa_curve* curve, const bignum256* k, curve_point* res) { + if(!bn_is_less(k, &curve->order)) { + return 1; + } + + int i = {0}, j = {0}; + static CONFIDENTIAL bignum256 a; + uint32_t is_even = (k->val[0] & 1) - 1; + uint32_t lowbits = 0; + static CONFIDENTIAL jacobian_curve_point jres; + const bignum256* prime = &curve->prime; + + // is_even = 0xffffffff if k is even, 0 otherwise. + + // add 2^256. + // make number odd: subtract curve->order if even + uint32_t tmp = 1; + uint32_t is_non_zero = 0; + for(j = 0; j < 8; j++) { + is_non_zero |= k->val[j]; + tmp += (BN_BASE - 1) + k->val[j] - (curve->order.val[j] & is_even); + a.val[j] = tmp & (BN_BASE - 1); + tmp >>= BN_BITS_PER_LIMB; + } + is_non_zero |= k->val[j]; + a.val[j] = tmp + 0xffffff + k->val[j] - (curve->order.val[j] & is_even); + assert((a.val[0] & 1) != 0); + + // special case 0*G: just return zero. We don't care about constant time. + if(!is_non_zero) { + point_set_infinity(res); + return 0; + } + + // Now a = k + 2^256 (mod curve->order) and a is odd. + // + // The idea is to bring the new a into the form. + // sum_{i=0..64} a[i] 16^i, where |a[i]| < 16 and a[i] is odd. + // a[0] is odd, since a is odd. If a[i] would be even, we can + // add 1 to it and subtract 16 from a[i-1]. Afterwards, + // a[64] = 1, which is the 2^256 that we added before. + // + // Since k = a - 2^256 (mod curve->order), we can compute + // k*G = sum_{i=0..63} a[i] 16^i * G + // + // We have a big table curve->cp that stores all possible + // values of |a[i]| 16^i * G. + // curve->cp[i][j] = (2*j+1) * 16^i * G + + // now compute res = sum_{i=0..63} a[i] * 16^i * G step by step. + // initial res = |a[0]| * G. Note that a[0] = a & 0xf if (a&0x10) != 0 + // and - (16 - (a & 0xf)) otherwise. We can compute this as + // ((a ^ (((a >> 4) & 1) - 1)) & 0xf) >> 1 + // since a is odd. + lowbits = a.val[0] & ((1 << 5) - 1); + lowbits ^= (lowbits >> 4) - 1; + lowbits &= 15; + curve_to_jacobian(&curve->cp[0][lowbits >> 1], &jres, prime); + for(i = 1; i < 64; i++) { + // invariant res = sign(a[i-1]) sum_{j=0..i-1} (a[j] * 16^j * G) + + // shift a by 4 places. + for(j = 0; j < 8; j++) { + a.val[j] = (a.val[j] >> 4) | ((a.val[j + 1] & 0xf) << (BN_BITS_PER_LIMB - 4)); + } + a.val[j] >>= 4; + // a = old(a)>>(4*i) + // a is even iff sign(a[i-1]) = -1 + + lowbits = a.val[0] & ((1 << 5) - 1); + lowbits ^= (lowbits >> 4) - 1; + lowbits &= 15; + // negate last result to make signs of this round and the + // last round equal. + bn_cnegate(~lowbits & 1, &jres.y, prime); + + // add odd factor + point_jacobian_add(&curve->cp[i][lowbits >> 1], &jres, curve); + } + bn_cnegate(~(a.val[0] >> 4) & 1, &jres.y, prime); + jacobian_to_curve(&jres, res, prime); + memzero(&a, sizeof(a)); + memzero(&jres, sizeof(jres)); + + return 0; +} + +#else + +int scalar_multiply(const ecdsa_curve* curve, const bignum256* k, curve_point* res) { + return point_multiply(curve, k, &curve->G, res); +} + +#endif + +int ecdh_multiply( + const ecdsa_curve* curve, + const uint8_t* priv_key, + const uint8_t* pub_key, + uint8_t* session_key) { + curve_point point = {0}; + if(!ecdsa_read_pubkey(curve, pub_key, &point)) { + return 1; + } + + bignum256 k = {0}; + bn_read_be(priv_key, &k); + if(bn_is_zero(&k) || !bn_is_less(&k, &curve->order)) { + // Invalid private key. + return 2; + } + + point_multiply(curve, &k, &point, &point); + memzero(&k, sizeof(k)); + + session_key[0] = 0x04; + bn_write_be(&point.x, session_key + 1); + bn_write_be(&point.y, session_key + 33); + memzero(&point, sizeof(point)); + + return 0; +} + +// msg is a data to be signed +// msg_len is the message length +int ecdsa_sign( + const ecdsa_curve* curve, + HasherType hasher_sign, + const uint8_t* priv_key, + const uint8_t* msg, + uint32_t msg_len, + uint8_t* sig, + uint8_t* pby, + int (*is_canonical)(uint8_t by, uint8_t sig[64])) { + uint8_t hash[32] = {0}; + hasher_Raw(hasher_sign, msg, msg_len, hash); + int res = ecdsa_sign_digest(curve, priv_key, hash, sig, pby, is_canonical); + memzero(hash, sizeof(hash)); + return res; +} + +// uses secp256k1 curve +// priv_key is a 32 byte big endian stored number +// sig is 64 bytes long array for the signature +// digest is 32 bytes of digest +// is_canonical is an optional function that checks if the signature +// conforms to additional coin-specific rules. +int ecdsa_sign_digest( + const ecdsa_curve* curve, + const uint8_t* priv_key, + const uint8_t* digest, + uint8_t* sig, + uint8_t* pby, + int (*is_canonical)(uint8_t by, uint8_t sig[64])) { + int i = 0; + curve_point R = {0}; + bignum256 k = {0}, z = {0}, randk = {0}; + bignum256* s = &R.y; + uint8_t by; // signature recovery byte + +#if USE_RFC6979 + rfc6979_state rng = {0}; + init_rfc6979(priv_key, digest, curve, &rng); +#endif + + bn_read_be(digest, &z); + if(bn_is_zero(&z)) { + // The probability of the digest being all-zero by chance is infinitesimal, + // so this is most likely an indication of a bug. Furthermore, the signature + // has no value, because in this case it can be easily forged for any public + // key, see ecdsa_verify_digest(). + return 1; + } + + for(i = 0; i < 10000; i++) { +#if USE_RFC6979 + // generate K deterministically + generate_k_rfc6979(&k, &rng); + // if k is too big or too small, we don't like it + if(bn_is_zero(&k) || !bn_is_less(&k, &curve->order)) { + continue; + } +#else + // generate random number k + generate_k_random(&k, &curve->order); +#endif + + // compute k*G + scalar_multiply(curve, &k, &R); + by = R.y.val[0] & 1; + // r = (rx mod n) + if(!bn_is_less(&R.x, &curve->order)) { + bn_subtract(&R.x, &curve->order, &R.x); + by |= 2; + } + // if r is zero, we retry + if(bn_is_zero(&R.x)) { + continue; + } + + bn_read_be(priv_key, s); + if(bn_is_zero(s) || !bn_is_less(s, &curve->order)) { + // Invalid private key. + return 2; + } + + // randomize operations to counter side-channel attacks + generate_k_random(&randk, &curve->order); + bn_multiply(&randk, &k, &curve->order); // k*rand + bn_inverse(&k, &curve->order); // (k*rand)^-1 + bn_multiply(&R.x, s, &curve->order); // R.x*priv + bn_add(s, &z); // R.x*priv + z + bn_multiply(&k, s, &curve->order); // (k*rand)^-1 (R.x*priv + z) + bn_multiply(&randk, s, &curve->order); // k^-1 (R.x*priv + z) + bn_mod(s, &curve->order); + // if s is zero, we retry + if(bn_is_zero(s)) { + continue; + } + + // if S > order/2 => S = -S + if(bn_is_less(&curve->order_half, s)) { + bn_subtract(&curve->order, s, s); + by ^= 1; + } + // we are done, R.x and s is the result signature + bn_write_be(&R.x, sig); + bn_write_be(s, sig + 32); + + // check if the signature is acceptable or retry + if(is_canonical && !is_canonical(by, sig)) { + continue; + } + + if(pby) { + *pby = by; + } + + memzero(&k, sizeof(k)); + memzero(&randk, sizeof(randk)); +#if USE_RFC6979 + memzero(&rng, sizeof(rng)); +#endif + return 0; + } + + // Too many retries without a valid signature + // -> fail with an error + memzero(&k, sizeof(k)); + memzero(&randk, sizeof(randk)); +#if USE_RFC6979 + memzero(&rng, sizeof(rng)); +#endif + return -1; +} + +// returns 0 on success +int ecdsa_get_public_key33(const ecdsa_curve* curve, const uint8_t* priv_key, uint8_t* pub_key) { + curve_point R = {0}; + bignum256 k = {0}; + + bn_read_be(priv_key, &k); + if(bn_is_zero(&k) || !bn_is_less(&k, &curve->order)) { + // Invalid private key. + memzero(pub_key, 33); + return -1; + } + + // compute k*G + if(scalar_multiply(curve, &k, &R) != 0) { + memzero(&k, sizeof(k)); + return 1; + } + pub_key[0] = 0x02 | (R.y.val[0] & 0x01); + bn_write_be(&R.x, pub_key + 1); + memzero(&R, sizeof(R)); + memzero(&k, sizeof(k)); + return 0; +} + +// returns 0 on success +int ecdsa_get_public_key65(const ecdsa_curve* curve, const uint8_t* priv_key, uint8_t* pub_key) { + curve_point R = {0}; + bignum256 k = {0}; + + bn_read_be(priv_key, &k); + if(bn_is_zero(&k) || !bn_is_less(&k, &curve->order)) { + // Invalid private key. + memzero(pub_key, 65); + return -1; + } + + // compute k*G + if(scalar_multiply(curve, &k, &R) != 0) { + memzero(&k, sizeof(k)); + return 1; + } + pub_key[0] = 0x04; + bn_write_be(&R.x, pub_key + 1); + bn_write_be(&R.y, pub_key + 33); + memzero(&R, sizeof(R)); + memzero(&k, sizeof(k)); + return 0; +} + +int ecdsa_uncompress_pubkey( + const ecdsa_curve* curve, + const uint8_t* pub_key, + uint8_t* uncompressed) { + curve_point pub = {0}; + + if(!ecdsa_read_pubkey(curve, pub_key, &pub)) { + return 0; + } + + uncompressed[0] = 4; + bn_write_be(&pub.x, uncompressed + 1); + bn_write_be(&pub.y, uncompressed + 33); + + return 1; +} + +void ecdsa_get_pubkeyhash(const uint8_t* pub_key, HasherType hasher_pubkey, uint8_t* pubkeyhash) { + uint8_t h[HASHER_DIGEST_LENGTH] = {0}; + if(pub_key[0] == 0x04) { // uncompressed format + hasher_Raw(hasher_pubkey, pub_key, 65, h); + } else if(pub_key[0] == 0x00) { // point at infinity + hasher_Raw(hasher_pubkey, pub_key, 1, h); + } else { // expecting compressed format + hasher_Raw(hasher_pubkey, pub_key, 33, h); + } + memcpy(pubkeyhash, h, 20); + memzero(h, sizeof(h)); +} + +void ecdsa_get_address_raw( + const uint8_t* pub_key, + uint32_t version, + HasherType hasher_pubkey, + uint8_t* addr_raw) { + size_t prefix_len = address_prefix_bytes_len(version); + address_write_prefix_bytes(version, addr_raw); + ecdsa_get_pubkeyhash(pub_key, hasher_pubkey, addr_raw + prefix_len); +} + +void ecdsa_get_address( + const uint8_t* pub_key, + uint32_t version, + HasherType hasher_pubkey, + HasherType hasher_base58, + char* addr, + int addrsize) { + uint8_t raw[MAX_ADDR_RAW_SIZE] = {0}; + size_t prefix_len = address_prefix_bytes_len(version); + ecdsa_get_address_raw(pub_key, version, hasher_pubkey, raw); + base58_encode_check(raw, 20 + prefix_len, hasher_base58, addr, addrsize); + // not as important to clear this one, but we might as well + memzero(raw, sizeof(raw)); +} + +void ecdsa_get_address_segwit_p2sh_raw( + const uint8_t* pub_key, + uint32_t version, + HasherType hasher_pubkey, + uint8_t* addr_raw) { + uint8_t buf[32 + 2] = {0}; + buf[0] = 0; // version byte + buf[1] = 20; // push 20 bytes + ecdsa_get_pubkeyhash(pub_key, hasher_pubkey, buf + 2); + size_t prefix_len = address_prefix_bytes_len(version); + address_write_prefix_bytes(version, addr_raw); + hasher_Raw(hasher_pubkey, buf, 22, addr_raw + prefix_len); +} + +void ecdsa_get_address_segwit_p2sh( + const uint8_t* pub_key, + uint32_t version, + HasherType hasher_pubkey, + HasherType hasher_base58, + char* addr, + int addrsize) { + uint8_t raw[MAX_ADDR_RAW_SIZE] = {0}; + size_t prefix_len = address_prefix_bytes_len(version); + ecdsa_get_address_segwit_p2sh_raw(pub_key, version, hasher_pubkey, raw); + base58_encode_check(raw, prefix_len + 20, hasher_base58, addr, addrsize); + memzero(raw, sizeof(raw)); +} + +void ecdsa_get_wif( + const uint8_t* priv_key, + uint32_t version, + HasherType hasher_base58, + char* wif, + int wifsize) { + uint8_t wif_raw[MAX_WIF_RAW_SIZE] = {0}; + size_t prefix_len = address_prefix_bytes_len(version); + address_write_prefix_bytes(version, wif_raw); + memcpy(wif_raw + prefix_len, priv_key, 32); + wif_raw[prefix_len + 32] = 0x01; + base58_encode_check(wif_raw, prefix_len + 32 + 1, hasher_base58, wif, wifsize); + // private keys running around our stack can cause trouble + memzero(wif_raw, sizeof(wif_raw)); +} + +int ecdsa_address_decode( + const char* addr, + uint32_t version, + HasherType hasher_base58, + uint8_t* out) { + if(!addr) return 0; + int prefix_len = address_prefix_bytes_len(version); + return base58_decode_check(addr, hasher_base58, out, 20 + prefix_len) == 20 + prefix_len && + address_check_prefix(out, version); +} + +void compress_coords(const curve_point* cp, uint8_t* compressed) { + compressed[0] = bn_is_odd(&cp->y) ? 0x03 : 0x02; + bn_write_be(&cp->x, compressed + 1); +} + +void uncompress_coords(const ecdsa_curve* curve, uint8_t odd, const bignum256* x, bignum256* y) { + // y^2 = x^3 + a*x + b + memcpy(y, x, sizeof(bignum256)); // y is x + bn_multiply(x, y, &curve->prime); // y is x^2 + bn_subi(y, -curve->a, &curve->prime); // y is x^2 + a + bn_multiply(x, y, &curve->prime); // y is x^3 + ax + bn_add(y, &curve->b); // y is x^3 + ax + b + bn_sqrt(y, &curve->prime); // y = sqrt(y) + if((odd & 0x01) != (y->val[0] & 1)) { + bn_subtract(&curve->prime, y, y); // y = -y + } +} + +int ecdsa_read_pubkey(const ecdsa_curve* curve, const uint8_t* pub_key, curve_point* pub) { + if(!curve) { + curve = &secp256k1; + } + if(pub_key[0] == 0x04) { + bn_read_be(pub_key + 1, &(pub->x)); + bn_read_be(pub_key + 33, &(pub->y)); + return ecdsa_validate_pubkey(curve, pub); + } + if(pub_key[0] == 0x02 || pub_key[0] == 0x03) { // compute missing y coords + bn_read_be(pub_key + 1, &(pub->x)); + uncompress_coords(curve, pub_key[0], &(pub->x), &(pub->y)); + return ecdsa_validate_pubkey(curve, pub); + } + // error + return 0; +} + +// Verifies that: +// - pub is not the point at infinity. +// - pub->x and pub->y are in range [0,p-1]. +// - pub is on the curve. +// We assume that all curves using this code have cofactor 1, so there is no +// need to verify that pub is a scalar multiple of G. +int ecdsa_validate_pubkey(const ecdsa_curve* curve, const curve_point* pub) { + bignum256 y_2 = {0}, x3_ax_b = {0}; + + if(point_is_infinity(pub)) { + return 0; + } + + if(!bn_is_less(&(pub->x), &curve->prime) || !bn_is_less(&(pub->y), &curve->prime)) { + return 0; + } + + memcpy(&y_2, &(pub->y), sizeof(bignum256)); + memcpy(&x3_ax_b, &(pub->x), sizeof(bignum256)); + + // y^2 + bn_multiply(&(pub->y), &y_2, &curve->prime); + bn_mod(&y_2, &curve->prime); + + // x^3 + ax + b + bn_multiply(&(pub->x), &x3_ax_b, &curve->prime); // x^2 + bn_subi(&x3_ax_b, -curve->a, &curve->prime); // x^2 + a + bn_multiply(&(pub->x), &x3_ax_b, &curve->prime); // x^3 + ax + bn_addmod(&x3_ax_b, &curve->b, &curve->prime); // x^3 + ax + b + bn_mod(&x3_ax_b, &curve->prime); + + if(!bn_is_equal(&x3_ax_b, &y_2)) { + return 0; + } + + return 1; +} + +// uses secp256k1 curve +// pub_key - 65 bytes uncompressed key +// signature - 64 bytes signature +// msg is a data that was signed +// msg_len is the message length + +int ecdsa_verify( + const ecdsa_curve* curve, + HasherType hasher_sign, + const uint8_t* pub_key, + const uint8_t* sig, + const uint8_t* msg, + uint32_t msg_len) { + uint8_t hash[32] = {0}; + hasher_Raw(hasher_sign, msg, msg_len, hash); + int res = ecdsa_verify_digest(curve, pub_key, sig, hash); + memzero(hash, sizeof(hash)); + return res; +} + +// Compute public key from signature and recovery id. +// returns 0 if the key is successfully recovered +int ecdsa_recover_pub_from_sig( + const ecdsa_curve* curve, + uint8_t* pub_key, + const uint8_t* sig, + const uint8_t* digest, + int recid) { + bignum256 r = {0}, s = {0}, e = {0}; + curve_point cp = {0}, cp2 = {0}; + + // read r and s + bn_read_be(sig, &r); + bn_read_be(sig + 32, &s); + if(!bn_is_less(&r, &curve->order) || bn_is_zero(&r)) { + return 1; + } + if(!bn_is_less(&s, &curve->order) || bn_is_zero(&s)) { + return 1; + } + // cp = R = k * G (k is secret nonce when signing) + memcpy(&cp.x, &r, sizeof(bignum256)); + if(recid & 2) { + bn_add(&cp.x, &curve->order); + if(!bn_is_less(&cp.x, &curve->prime)) { + return 1; + } + } + // compute y from x + uncompress_coords(curve, recid & 1, &cp.x, &cp.y); + if(!ecdsa_validate_pubkey(curve, &cp)) { + return 1; + } + // e = -digest + bn_read_be(digest, &e); + bn_mod(&e, &curve->order); + bn_subtract(&curve->order, &e, &e); + // r = r^-1 + bn_inverse(&r, &curve->order); + // e = -digest * r^-1 + bn_multiply(&r, &e, &curve->order); + bn_mod(&e, &curve->order); + // s = s * r^-1 + bn_multiply(&r, &s, &curve->order); + bn_mod(&s, &curve->order); + // cp = s * r^-1 * k * G + point_multiply(curve, &s, &cp, &cp); + // cp2 = -digest * r^-1 * G + scalar_multiply(curve, &e, &cp2); + // cp = (s * r^-1 * k - digest * r^-1) * G = Pub + point_add(curve, &cp2, &cp); + // The point at infinity is not considered to be a valid public key. + if(point_is_infinity(&cp)) { + return 1; + } + pub_key[0] = 0x04; + bn_write_be(&cp.x, pub_key + 1); + bn_write_be(&cp.y, pub_key + 33); + return 0; +} + +// returns 0 if verification succeeded +int ecdsa_verify_digest( + const ecdsa_curve* curve, + const uint8_t* pub_key, + const uint8_t* sig, + const uint8_t* digest) { + curve_point pub = {0}, res = {0}; + bignum256 r = {0}, s = {0}, z = {0}; + int result = 0; + + if(!ecdsa_read_pubkey(curve, pub_key, &pub)) { + result = 1; + } + + if(result == 0) { + bn_read_be(sig, &r); + bn_read_be(sig + 32, &s); + bn_read_be(digest, &z); + if(bn_is_zero(&r) || bn_is_zero(&s) || (!bn_is_less(&r, &curve->order)) || + (!bn_is_less(&s, &curve->order))) { + result = 2; + } + if(bn_is_zero(&z)) { + // The digest was all-zero. The probability of this happening by chance is + // infinitesimal, but it could be induced by a fault injection. In this + // case the signature (r,s) can be forged by taking r := (t * Q).x mod n + // and s := r * t^-1 mod n for any t in [1, n-1]. We fail verification, + // because there is no guarantee that the signature was created by the + // owner of the private key. + result = 3; + } + } + + if(result == 0) { + bn_inverse(&s, &curve->order); // s = s^-1 + bn_multiply(&s, &z, &curve->order); // z = z * s [u1 = z * s^-1 mod n] + bn_mod(&z, &curve->order); + } + + if(result == 0) { + bn_multiply(&r, &s, &curve->order); // s = r * s [u2 = r * s^-1 mod n] + bn_mod(&s, &curve->order); + scalar_multiply(curve, &z, &res); // res = z * G [= u1 * G] + point_multiply(curve, &s, &pub, &pub); // pub = s * pub [= u2 * Q] + point_add(curve, &pub, &res); // res = pub + res [R = u1 * G + u2 * Q] + if(point_is_infinity(&res)) { + // R == Infinity + result = 4; + } + } + + if(result == 0) { + bn_mod(&(res.x), &curve->order); + if(!bn_is_equal(&res.x, &r)) { + // R.x != r + // signature does not match + result = 5; + } + } + + memzero(&pub, sizeof(pub)); + memzero(&res, sizeof(res)); + memzero(&r, sizeof(r)); + memzero(&s, sizeof(s)); + memzero(&z, sizeof(z)); + + // all OK + return result; +} + +int ecdsa_sig_to_der(const uint8_t* sig, uint8_t* der) { + int i = 0; + uint8_t *p = der, *len = NULL, *len1 = NULL, *len2 = NULL; + *p = 0x30; + p++; // sequence + *p = 0x00; + len = p; + p++; // len(sequence) + + *p = 0x02; + p++; // integer + *p = 0x00; + len1 = p; + p++; // len(integer) + + // process R + i = 0; + while(i < 31 && sig[i] == 0) { + i++; + } // skip leading zeroes + if(sig[i] >= 0x80) { // put zero in output if MSB set + *p = 0x00; + p++; + *len1 = *len1 + 1; + } + while(i < 32) { // copy bytes to output + *p = sig[i]; + p++; + *len1 = *len1 + 1; + i++; + } + + *p = 0x02; + p++; // integer + *p = 0x00; + len2 = p; + p++; // len(integer) + + // process S + i = 32; + while(i < 63 && sig[i] == 0) { + i++; + } // skip leading zeroes + if(sig[i] >= 0x80) { // put zero in output if MSB set + *p = 0x00; + p++; + *len2 = *len2 + 1; + } + while(i < 64) { // copy bytes to output + *p = sig[i]; + p++; + *len2 = *len2 + 1; + i++; + } + + *len = *len1 + *len2 + 4; + return *len + 2; +} + +// Parse a DER-encoded signature. We don't check whether the encoded integers +// satisfy DER requirements regarding leading zeros. +int ecdsa_sig_from_der(const uint8_t* der, size_t der_len, uint8_t sig[64]) { + memzero(sig, 64); + + // Check sequence header. + if(der_len < 2 || der_len > 72 || der[0] != 0x30 || der[1] != der_len - 2) { + return 1; + } + + // Read two DER-encoded integers. + size_t pos = 2; + for(int i = 0; i < 2; ++i) { + // Check integer header. + if(der_len < pos + 2 || der[pos] != 0x02) { + return 1; + } + + // Locate the integer. + size_t int_len = der[pos + 1]; + pos += 2; + if(pos + int_len > der_len) { + return 1; + } + + // Skip a possible leading zero. + if(int_len != 0 && der[pos] == 0) { + int_len--; + pos++; + } + + // Copy the integer to the output, making sure it fits. + if(int_len > 32) { + return 1; + } + memcpy(sig + 32 * (i + 1) - int_len, der + pos, int_len); + + // Move on to the next one. + pos += int_len; + } + + // Check that there are no trailing elements in the sequence. + if(pos != der_len) { + return 1; + } + + return 0; +} diff --git a/applications/external/flipbip/lib/crypto/ecdsa.h b/applications/external/flipbip/lib/crypto/ecdsa.h new file mode 100644 index 0000000000..26192aa084 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/ecdsa.h @@ -0,0 +1,167 @@ +/** + * Copyright (c) 2013-2014 Tomas Dzetkulic + * Copyright (c) 2013-2014 Pavol Rusnak + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __ECDSA_H__ +#define __ECDSA_H__ + +#include +#include "bignum.h" +#include "hasher.h" +#include "options.h" + +// curve point x and y +typedef struct { + bignum256 x, y; +} curve_point; + +typedef struct { + bignum256 prime; // prime order of the finite field + curve_point G; // initial curve point + bignum256 order; // order of G + bignum256 order_half; // order of G divided by 2 + int a; // coefficient 'a' of the elliptic curve + bignum256 b; // coefficient 'b' of the elliptic curve + +#if USE_PRECOMPUTED_CP + const curve_point cp[64][8]; +#endif + +} ecdsa_curve; + +// 4 byte prefix + 40 byte data (segwit) +// 1 byte prefix + 64 byte data (cashaddr) +#define MAX_ADDR_RAW_SIZE 65 +// bottle neck is cashaddr +// segwit is at most 90 characters plus NUL separator +// cashaddr: human readable prefix + 1 separator + 104 data + 8 checksum + 1 NUL +// we choose 130 as maximum (including NUL character) +#define MAX_ADDR_SIZE 130 +// 4 byte prefix + 32 byte privkey + 1 byte compressed marker +#define MAX_WIF_RAW_SIZE (4 + 32 + 1) +// (4 + 32 + 1 + 4 [checksum]) * 8 / log2(58) plus NUL. +#define MAX_WIF_SIZE (57) + +void point_copy(const curve_point* cp1, curve_point* cp2); +void point_add(const ecdsa_curve* curve, const curve_point* cp1, curve_point* cp2); +void point_double(const ecdsa_curve* curve, curve_point* cp); +int point_multiply( + const ecdsa_curve* curve, + const bignum256* k, + const curve_point* p, + curve_point* res); +void point_set_infinity(curve_point* p); +int point_is_infinity(const curve_point* p); +int point_is_equal(const curve_point* p, const curve_point* q); +int point_is_negative_of(const curve_point* p, const curve_point* q); +int scalar_multiply(const ecdsa_curve* curve, const bignum256* k, curve_point* res); +int ecdh_multiply( + const ecdsa_curve* curve, + const uint8_t* priv_key, + const uint8_t* pub_key, + uint8_t* session_key); +void compress_coords(const curve_point* cp, uint8_t* compressed); +void uncompress_coords(const ecdsa_curve* curve, uint8_t odd, const bignum256* x, bignum256* y); +int ecdsa_uncompress_pubkey( + const ecdsa_curve* curve, + const uint8_t* pub_key, + uint8_t* uncompressed); + +int ecdsa_sign( + const ecdsa_curve* curve, + HasherType hasher_sign, + const uint8_t* priv_key, + const uint8_t* msg, + uint32_t msg_len, + uint8_t* sig, + uint8_t* pby, + int (*is_canonical)(uint8_t by, uint8_t sig[64])); +int ecdsa_sign_digest( + const ecdsa_curve* curve, + const uint8_t* priv_key, + const uint8_t* digest, + uint8_t* sig, + uint8_t* pby, + int (*is_canonical)(uint8_t by, uint8_t sig[64])); +int ecdsa_get_public_key33(const ecdsa_curve* curve, const uint8_t* priv_key, uint8_t* pub_key); +int ecdsa_get_public_key65(const ecdsa_curve* curve, const uint8_t* priv_key, uint8_t* pub_key); +void ecdsa_get_pubkeyhash(const uint8_t* pub_key, HasherType hasher_pubkey, uint8_t* pubkeyhash); +void ecdsa_get_address_raw( + const uint8_t* pub_key, + uint32_t version, + HasherType hasher_pubkey, + uint8_t* addr_raw); +void ecdsa_get_address( + const uint8_t* pub_key, + uint32_t version, + HasherType hasher_pubkey, + HasherType hasher_base58, + char* addr, + int addrsize); +void ecdsa_get_address_segwit_p2sh_raw( + const uint8_t* pub_key, + uint32_t version, + HasherType hasher_pubkey, + uint8_t* addr_raw); +void ecdsa_get_address_segwit_p2sh( + const uint8_t* pub_key, + uint32_t version, + HasherType hasher_pubkey, + HasherType hasher_base58, + char* addr, + int addrsize); +void ecdsa_get_wif( + const uint8_t* priv_key, + uint32_t version, + HasherType hasher_base58, + char* wif, + int wifsize); + +int ecdsa_address_decode( + const char* addr, + uint32_t version, + HasherType hasher_base58, + uint8_t* out); +int ecdsa_read_pubkey(const ecdsa_curve* curve, const uint8_t* pub_key, curve_point* pub); +int ecdsa_validate_pubkey(const ecdsa_curve* curve, const curve_point* pub); +int ecdsa_verify( + const ecdsa_curve* curve, + HasherType hasher_sign, + const uint8_t* pub_key, + const uint8_t* sig, + const uint8_t* msg, + uint32_t msg_len); +int ecdsa_verify_digest( + const ecdsa_curve* curve, + const uint8_t* pub_key, + const uint8_t* sig, + const uint8_t* digest); +int ecdsa_recover_pub_from_sig( + const ecdsa_curve* curve, + uint8_t* pub_key, + const uint8_t* sig, + const uint8_t* digest, + int recid); +int ecdsa_sig_to_der(const uint8_t* sig, uint8_t* der); +int ecdsa_sig_from_der(const uint8_t* der, size_t der_len, uint8_t sig[64]); + +#endif diff --git a/applications/external/flipbip/lib/crypto/ed25519_donna/curve25519_donna_32bit.c b/applications/external/flipbip/lib/crypto/ed25519_donna/curve25519_donna_32bit.c new file mode 100644 index 0000000000..603abeb240 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/ed25519_donna/curve25519_donna_32bit.c @@ -0,0 +1,953 @@ +/* + Public domain by Andrew M. + See: https://github.com/floodyberry/curve25519-donna + + 32 bit integer curve25519 implementation +*/ + +#include "ed25519_donna.h" + +static const uint32_t reduce_mask_25 = (1 << 25) - 1; +static const uint32_t reduce_mask_26 = (1 << 26) - 1; + +/* out = in */ +void curve25519_copy(bignum25519 out, const bignum25519 in) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; + out[4] = in[4]; + out[5] = in[5]; + out[6] = in[6]; + out[7] = in[7]; + out[8] = in[8]; + out[9] = in[9]; +} + +/* out = a + b */ +void curve25519_add(bignum25519 out, const bignum25519 a, const bignum25519 b) { + out[0] = a[0] + b[0]; + out[1] = a[1] + b[1]; + out[2] = a[2] + b[2]; + out[3] = a[3] + b[3]; + out[4] = a[4] + b[4]; + out[5] = a[5] + b[5]; + out[6] = a[6] + b[6]; + out[7] = a[7] + b[7]; + out[8] = a[8] + b[8]; + out[9] = a[9] + b[9]; +} + +void curve25519_add_after_basic(bignum25519 out, const bignum25519 a, const bignum25519 b) { + uint32_t c = 0; + out[0] = a[0] + b[0]; + c = (out[0] >> 26); + out[0] &= reduce_mask_26; + out[1] = a[1] + b[1] + c; + c = (out[1] >> 25); + out[1] &= reduce_mask_25; + out[2] = a[2] + b[2] + c; + c = (out[2] >> 26); + out[2] &= reduce_mask_26; + out[3] = a[3] + b[3] + c; + c = (out[3] >> 25); + out[3] &= reduce_mask_25; + out[4] = a[4] + b[4] + c; + c = (out[4] >> 26); + out[4] &= reduce_mask_26; + out[5] = a[5] + b[5] + c; + c = (out[5] >> 25); + out[5] &= reduce_mask_25; + out[6] = a[6] + b[6] + c; + c = (out[6] >> 26); + out[6] &= reduce_mask_26; + out[7] = a[7] + b[7] + c; + c = (out[7] >> 25); + out[7] &= reduce_mask_25; + out[8] = a[8] + b[8] + c; + c = (out[8] >> 26); + out[8] &= reduce_mask_26; + out[9] = a[9] + b[9] + c; + c = (out[9] >> 25); + out[9] &= reduce_mask_25; + out[0] += 19 * c; +} + +void curve25519_add_reduce(bignum25519 out, const bignum25519 a, const bignum25519 b) { + uint32_t c = 0; + out[0] = a[0] + b[0]; + c = (out[0] >> 26); + out[0] &= reduce_mask_26; + out[1] = a[1] + b[1] + c; + c = (out[1] >> 25); + out[1] &= reduce_mask_25; + out[2] = a[2] + b[2] + c; + c = (out[2] >> 26); + out[2] &= reduce_mask_26; + out[3] = a[3] + b[3] + c; + c = (out[3] >> 25); + out[3] &= reduce_mask_25; + out[4] = a[4] + b[4] + c; + c = (out[4] >> 26); + out[4] &= reduce_mask_26; + out[5] = a[5] + b[5] + c; + c = (out[5] >> 25); + out[5] &= reduce_mask_25; + out[6] = a[6] + b[6] + c; + c = (out[6] >> 26); + out[6] &= reduce_mask_26; + out[7] = a[7] + b[7] + c; + c = (out[7] >> 25); + out[7] &= reduce_mask_25; + out[8] = a[8] + b[8] + c; + c = (out[8] >> 26); + out[8] &= reduce_mask_26; + out[9] = a[9] + b[9] + c; + c = (out[9] >> 25); + out[9] &= reduce_mask_25; + out[0] += 19 * c; +} + +/* multiples of p */ +static const uint32_t twoP0 = 0x07ffffda; +static const uint32_t twoP13579 = 0x03fffffe; +static const uint32_t twoP2468 = 0x07fffffe; +static const uint32_t fourP0 = 0x0fffffb4; +static const uint32_t fourP13579 = 0x07fffffc; +static const uint32_t fourP2468 = 0x0ffffffc; + +/* out = a - b */ +void curve25519_sub(bignum25519 out, const bignum25519 a, const bignum25519 b) { + uint32_t c = 0; + out[0] = twoP0 + a[0] - b[0]; + c = (out[0] >> 26); + out[0] &= reduce_mask_26; + out[1] = twoP13579 + a[1] - b[1] + c; + c = (out[1] >> 25); + out[1] &= reduce_mask_25; + out[2] = twoP2468 + a[2] - b[2] + c; + c = (out[2] >> 26); + out[2] &= reduce_mask_26; + out[3] = twoP13579 + a[3] - b[3] + c; + c = (out[3] >> 25); + out[3] &= reduce_mask_25; + out[4] = twoP2468 + a[4] - b[4] + c; + out[5] = twoP13579 + a[5] - b[5]; + out[6] = twoP2468 + a[6] - b[6]; + out[7] = twoP13579 + a[7] - b[7]; + out[8] = twoP2468 + a[8] - b[8]; + out[9] = twoP13579 + a[9] - b[9]; +} + +/* out = in * scalar */ +void curve25519_scalar_product(bignum25519 out, const bignum25519 in, const uint32_t scalar) { + uint64_t a = 0; + uint32_t c = 0; + a = mul32x32_64(in[0], scalar); + out[0] = (uint32_t)a & reduce_mask_26; + c = (uint32_t)(a >> 26); + a = mul32x32_64(in[1], scalar) + c; + out[1] = (uint32_t)a & reduce_mask_25; + c = (uint32_t)(a >> 25); + a = mul32x32_64(in[2], scalar) + c; + out[2] = (uint32_t)a & reduce_mask_26; + c = (uint32_t)(a >> 26); + a = mul32x32_64(in[3], scalar) + c; + out[3] = (uint32_t)a & reduce_mask_25; + c = (uint32_t)(a >> 25); + a = mul32x32_64(in[4], scalar) + c; + out[4] = (uint32_t)a & reduce_mask_26; + c = (uint32_t)(a >> 26); + a = mul32x32_64(in[5], scalar) + c; + out[5] = (uint32_t)a & reduce_mask_25; + c = (uint32_t)(a >> 25); + a = mul32x32_64(in[6], scalar) + c; + out[6] = (uint32_t)a & reduce_mask_26; + c = (uint32_t)(a >> 26); + a = mul32x32_64(in[7], scalar) + c; + out[7] = (uint32_t)a & reduce_mask_25; + c = (uint32_t)(a >> 25); + a = mul32x32_64(in[8], scalar) + c; + out[8] = (uint32_t)a & reduce_mask_26; + c = (uint32_t)(a >> 26); + a = mul32x32_64(in[9], scalar) + c; + out[9] = (uint32_t)a & reduce_mask_25; + c = (uint32_t)(a >> 25); + out[0] += c * 19; +} + +/* out = a - b, where a is the result of a basic op (add,sub) */ +void curve25519_sub_after_basic(bignum25519 out, const bignum25519 a, const bignum25519 b) { + uint32_t c = 0; + out[0] = fourP0 + a[0] - b[0]; + c = (out[0] >> 26); + out[0] &= reduce_mask_26; + out[1] = fourP13579 + a[1] - b[1] + c; + c = (out[1] >> 25); + out[1] &= reduce_mask_25; + out[2] = fourP2468 + a[2] - b[2] + c; + c = (out[2] >> 26); + out[2] &= reduce_mask_26; + out[3] = fourP13579 + a[3] - b[3] + c; + c = (out[3] >> 25); + out[3] &= reduce_mask_25; + out[4] = fourP2468 + a[4] - b[4] + c; + c = (out[4] >> 26); + out[4] &= reduce_mask_26; + out[5] = fourP13579 + a[5] - b[5] + c; + c = (out[5] >> 25); + out[5] &= reduce_mask_25; + out[6] = fourP2468 + a[6] - b[6] + c; + c = (out[6] >> 26); + out[6] &= reduce_mask_26; + out[7] = fourP13579 + a[7] - b[7] + c; + c = (out[7] >> 25); + out[7] &= reduce_mask_25; + out[8] = fourP2468 + a[8] - b[8] + c; + c = (out[8] >> 26); + out[8] &= reduce_mask_26; + out[9] = fourP13579 + a[9] - b[9] + c; + c = (out[9] >> 25); + out[9] &= reduce_mask_25; + out[0] += 19 * c; +} + +void curve25519_sub_reduce(bignum25519 out, const bignum25519 a, const bignum25519 b) { + uint32_t c = 0; + out[0] = fourP0 + a[0] - b[0]; + c = (out[0] >> 26); + out[0] &= reduce_mask_26; + out[1] = fourP13579 + a[1] - b[1] + c; + c = (out[1] >> 25); + out[1] &= reduce_mask_25; + out[2] = fourP2468 + a[2] - b[2] + c; + c = (out[2] >> 26); + out[2] &= reduce_mask_26; + out[3] = fourP13579 + a[3] - b[3] + c; + c = (out[3] >> 25); + out[3] &= reduce_mask_25; + out[4] = fourP2468 + a[4] - b[4] + c; + c = (out[4] >> 26); + out[4] &= reduce_mask_26; + out[5] = fourP13579 + a[5] - b[5] + c; + c = (out[5] >> 25); + out[5] &= reduce_mask_25; + out[6] = fourP2468 + a[6] - b[6] + c; + c = (out[6] >> 26); + out[6] &= reduce_mask_26; + out[7] = fourP13579 + a[7] - b[7] + c; + c = (out[7] >> 25); + out[7] &= reduce_mask_25; + out[8] = fourP2468 + a[8] - b[8] + c; + c = (out[8] >> 26); + out[8] &= reduce_mask_26; + out[9] = fourP13579 + a[9] - b[9] + c; + c = (out[9] >> 25); + out[9] &= reduce_mask_25; + out[0] += 19 * c; +} + +/* out = -a */ +void curve25519_neg(bignum25519 out, const bignum25519 a) { + uint32_t c = 0; + out[0] = twoP0 - a[0]; + c = (out[0] >> 26); + out[0] &= reduce_mask_26; + out[1] = twoP13579 - a[1] + c; + c = (out[1] >> 25); + out[1] &= reduce_mask_25; + out[2] = twoP2468 - a[2] + c; + c = (out[2] >> 26); + out[2] &= reduce_mask_26; + out[3] = twoP13579 - a[3] + c; + c = (out[3] >> 25); + out[3] &= reduce_mask_25; + out[4] = twoP2468 - a[4] + c; + c = (out[4] >> 26); + out[4] &= reduce_mask_26; + out[5] = twoP13579 - a[5] + c; + c = (out[5] >> 25); + out[5] &= reduce_mask_25; + out[6] = twoP2468 - a[6] + c; + c = (out[6] >> 26); + out[6] &= reduce_mask_26; + out[7] = twoP13579 - a[7] + c; + c = (out[7] >> 25); + out[7] &= reduce_mask_25; + out[8] = twoP2468 - a[8] + c; + c = (out[8] >> 26); + out[8] &= reduce_mask_26; + out[9] = twoP13579 - a[9] + c; + c = (out[9] >> 25); + out[9] &= reduce_mask_25; + out[0] += 19 * c; +} + +/* out = a * b */ +#define curve25519_mul_noinline curve25519_mul +void curve25519_mul(bignum25519 out, const bignum25519 a, const bignum25519 b) { + uint32_t r0 = 0, r1 = 0, r2 = 0, r3 = 0, r4 = 0, r5 = 0, r6 = 0, r7 = 0, r8 = 0, r9 = 0; + uint32_t s0 = 0, s1 = 0, s2 = 0, s3 = 0, s4 = 0, s5 = 0, s6 = 0, s7 = 0, s8 = 0, s9 = 0; + uint64_t m0 = 0, m1 = 0, m2 = 0, m3 = 0, m4 = 0, m5 = 0, m6 = 0, m7 = 0, m8 = 0, m9 = 0, c = 0; + uint32_t p = 0; + + r0 = b[0]; + r1 = b[1]; + r2 = b[2]; + r3 = b[3]; + r4 = b[4]; + r5 = b[5]; + r6 = b[6]; + r7 = b[7]; + r8 = b[8]; + r9 = b[9]; + + s0 = a[0]; + s1 = a[1]; + s2 = a[2]; + s3 = a[3]; + s4 = a[4]; + s5 = a[5]; + s6 = a[6]; + s7 = a[7]; + s8 = a[8]; + s9 = a[9]; + + m1 = mul32x32_64(r0, s1) + mul32x32_64(r1, s0); + m3 = mul32x32_64(r0, s3) + mul32x32_64(r1, s2) + mul32x32_64(r2, s1) + mul32x32_64(r3, s0); + m5 = mul32x32_64(r0, s5) + mul32x32_64(r1, s4) + mul32x32_64(r2, s3) + mul32x32_64(r3, s2) + + mul32x32_64(r4, s1) + mul32x32_64(r5, s0); + m7 = mul32x32_64(r0, s7) + mul32x32_64(r1, s6) + mul32x32_64(r2, s5) + mul32x32_64(r3, s4) + + mul32x32_64(r4, s3) + mul32x32_64(r5, s2) + mul32x32_64(r6, s1) + mul32x32_64(r7, s0); + m9 = mul32x32_64(r0, s9) + mul32x32_64(r1, s8) + mul32x32_64(r2, s7) + mul32x32_64(r3, s6) + + mul32x32_64(r4, s5) + mul32x32_64(r5, s4) + mul32x32_64(r6, s3) + mul32x32_64(r7, s2) + + mul32x32_64(r8, s1) + mul32x32_64(r9, s0); + + r1 *= 2; + r3 *= 2; + r5 *= 2; + r7 *= 2; + + m0 = mul32x32_64(r0, s0); + m2 = mul32x32_64(r0, s2) + mul32x32_64(r1, s1) + mul32x32_64(r2, s0); + m4 = mul32x32_64(r0, s4) + mul32x32_64(r1, s3) + mul32x32_64(r2, s2) + mul32x32_64(r3, s1) + + mul32x32_64(r4, s0); + m6 = mul32x32_64(r0, s6) + mul32x32_64(r1, s5) + mul32x32_64(r2, s4) + mul32x32_64(r3, s3) + + mul32x32_64(r4, s2) + mul32x32_64(r5, s1) + mul32x32_64(r6, s0); + m8 = mul32x32_64(r0, s8) + mul32x32_64(r1, s7) + mul32x32_64(r2, s6) + mul32x32_64(r3, s5) + + mul32x32_64(r4, s4) + mul32x32_64(r5, s3) + mul32x32_64(r6, s2) + mul32x32_64(r7, s1) + + mul32x32_64(r8, s0); + + r1 *= 19; + r2 *= 19; + r3 = (r3 / 2) * 19; + r4 *= 19; + r5 = (r5 / 2) * 19; + r6 *= 19; + r7 = (r7 / 2) * 19; + r8 *= 19; + r9 *= 19; + + m1 += + (mul32x32_64(r9, s2) + mul32x32_64(r8, s3) + mul32x32_64(r7, s4) + mul32x32_64(r6, s5) + + mul32x32_64(r5, s6) + mul32x32_64(r4, s7) + mul32x32_64(r3, s8) + mul32x32_64(r2, s9)); + m3 += + (mul32x32_64(r9, s4) + mul32x32_64(r8, s5) + mul32x32_64(r7, s6) + mul32x32_64(r6, s7) + + mul32x32_64(r5, s8) + mul32x32_64(r4, s9)); + m5 += (mul32x32_64(r9, s6) + mul32x32_64(r8, s7) + mul32x32_64(r7, s8) + mul32x32_64(r6, s9)); + m7 += (mul32x32_64(r9, s8) + mul32x32_64(r8, s9)); + + r3 *= 2; + r5 *= 2; + r7 *= 2; + r9 *= 2; + + m0 += + (mul32x32_64(r9, s1) + mul32x32_64(r8, s2) + mul32x32_64(r7, s3) + mul32x32_64(r6, s4) + + mul32x32_64(r5, s5) + mul32x32_64(r4, s6) + mul32x32_64(r3, s7) + mul32x32_64(r2, s8) + + mul32x32_64(r1, s9)); + m2 += + (mul32x32_64(r9, s3) + mul32x32_64(r8, s4) + mul32x32_64(r7, s5) + mul32x32_64(r6, s6) + + mul32x32_64(r5, s7) + mul32x32_64(r4, s8) + mul32x32_64(r3, s9)); + m4 += + (mul32x32_64(r9, s5) + mul32x32_64(r8, s6) + mul32x32_64(r7, s7) + mul32x32_64(r6, s8) + + mul32x32_64(r5, s9)); + m6 += (mul32x32_64(r9, s7) + mul32x32_64(r8, s8) + mul32x32_64(r7, s9)); + m8 += (mul32x32_64(r9, s9)); + + r0 = (uint32_t)m0 & reduce_mask_26; + c = (m0 >> 26); + m1 += c; + r1 = (uint32_t)m1 & reduce_mask_25; + c = (m1 >> 25); + m2 += c; + r2 = (uint32_t)m2 & reduce_mask_26; + c = (m2 >> 26); + m3 += c; + r3 = (uint32_t)m3 & reduce_mask_25; + c = (m3 >> 25); + m4 += c; + r4 = (uint32_t)m4 & reduce_mask_26; + c = (m4 >> 26); + m5 += c; + r5 = (uint32_t)m5 & reduce_mask_25; + c = (m5 >> 25); + m6 += c; + r6 = (uint32_t)m6 & reduce_mask_26; + c = (m6 >> 26); + m7 += c; + r7 = (uint32_t)m7 & reduce_mask_25; + c = (m7 >> 25); + m8 += c; + r8 = (uint32_t)m8 & reduce_mask_26; + c = (m8 >> 26); + m9 += c; + r9 = (uint32_t)m9 & reduce_mask_25; + p = (uint32_t)(m9 >> 25); + m0 = r0 + mul32x32_64(p, 19); + r0 = (uint32_t)m0 & reduce_mask_26; + p = (uint32_t)(m0 >> 26); + r1 += p; + + out[0] = r0; + out[1] = r1; + out[2] = r2; + out[3] = r3; + out[4] = r4; + out[5] = r5; + out[6] = r6; + out[7] = r7; + out[8] = r8; + out[9] = r9; +} + +/* out = in * in */ +void curve25519_square(bignum25519 out, const bignum25519 in) { + uint32_t r0 = 0, r1 = 0, r2 = 0, r3 = 0, r4 = 0, r5 = 0, r6 = 0, r7 = 0, r8 = 0, r9 = 0; + uint32_t d6 = 0, d7 = 0, d8 = 0, d9 = 0; + uint64_t m0 = 0, m1 = 0, m2 = 0, m3 = 0, m4 = 0, m5 = 0, m6 = 0, m7 = 0, m8 = 0, m9 = 0, c = 0; + uint32_t p = 0; + + r0 = in[0]; + r1 = in[1]; + r2 = in[2]; + r3 = in[3]; + r4 = in[4]; + r5 = in[5]; + r6 = in[6]; + r7 = in[7]; + r8 = in[8]; + r9 = in[9]; + + m0 = mul32x32_64(r0, r0); + r0 *= 2; + m1 = mul32x32_64(r0, r1); + m2 = mul32x32_64(r0, r2) + mul32x32_64(r1, r1 * 2); + r1 *= 2; + m3 = mul32x32_64(r0, r3) + mul32x32_64(r1, r2); + m4 = mul32x32_64(r0, r4) + mul32x32_64(r1, r3 * 2) + mul32x32_64(r2, r2); + r2 *= 2; + m5 = mul32x32_64(r0, r5) + mul32x32_64(r1, r4) + mul32x32_64(r2, r3); + m6 = mul32x32_64(r0, r6) + mul32x32_64(r1, r5 * 2) + mul32x32_64(r2, r4) + + mul32x32_64(r3, r3 * 2); + r3 *= 2; + m7 = mul32x32_64(r0, r7) + mul32x32_64(r1, r6) + mul32x32_64(r2, r5) + mul32x32_64(r3, r4); + m8 = mul32x32_64(r0, r8) + mul32x32_64(r1, r7 * 2) + mul32x32_64(r2, r6) + + mul32x32_64(r3, r5 * 2) + mul32x32_64(r4, r4); + m9 = mul32x32_64(r0, r9) + mul32x32_64(r1, r8) + mul32x32_64(r2, r7) + mul32x32_64(r3, r6) + + mul32x32_64(r4, r5 * 2); + + d6 = r6 * 19; + d7 = r7 * 2 * 19; + d8 = r8 * 19; + d9 = r9 * 2 * 19; + + m0 += + (mul32x32_64(d9, r1) + mul32x32_64(d8, r2) + mul32x32_64(d7, r3) + + mul32x32_64(d6, r4 * 2) + mul32x32_64(r5, r5 * 2 * 19)); + m1 += + (mul32x32_64(d9, r2 / 2) + mul32x32_64(d8, r3) + mul32x32_64(d7, r4) + + mul32x32_64(d6, r5 * 2)); + m2 += + (mul32x32_64(d9, r3) + mul32x32_64(d8, r4 * 2) + mul32x32_64(d7, r5 * 2) + + mul32x32_64(d6, r6)); + m3 += (mul32x32_64(d9, r4) + mul32x32_64(d8, r5 * 2) + mul32x32_64(d7, r6)); + m4 += (mul32x32_64(d9, r5 * 2) + mul32x32_64(d8, r6 * 2) + mul32x32_64(d7, r7)); + m5 += (mul32x32_64(d9, r6) + mul32x32_64(d8, r7 * 2)); + m6 += (mul32x32_64(d9, r7 * 2) + mul32x32_64(d8, r8)); + m7 += (mul32x32_64(d9, r8)); + m8 += (mul32x32_64(d9, r9)); + + r0 = (uint32_t)m0 & reduce_mask_26; + c = (m0 >> 26); + m1 += c; + r1 = (uint32_t)m1 & reduce_mask_25; + c = (m1 >> 25); + m2 += c; + r2 = (uint32_t)m2 & reduce_mask_26; + c = (m2 >> 26); + m3 += c; + r3 = (uint32_t)m3 & reduce_mask_25; + c = (m3 >> 25); + m4 += c; + r4 = (uint32_t)m4 & reduce_mask_26; + c = (m4 >> 26); + m5 += c; + r5 = (uint32_t)m5 & reduce_mask_25; + c = (m5 >> 25); + m6 += c; + r6 = (uint32_t)m6 & reduce_mask_26; + c = (m6 >> 26); + m7 += c; + r7 = (uint32_t)m7 & reduce_mask_25; + c = (m7 >> 25); + m8 += c; + r8 = (uint32_t)m8 & reduce_mask_26; + c = (m8 >> 26); + m9 += c; + r9 = (uint32_t)m9 & reduce_mask_25; + p = (uint32_t)(m9 >> 25); + m0 = r0 + mul32x32_64(p, 19); + r0 = (uint32_t)m0 & reduce_mask_26; + p = (uint32_t)(m0 >> 26); + r1 += p; + + out[0] = r0; + out[1] = r1; + out[2] = r2; + out[3] = r3; + out[4] = r4; + out[5] = r5; + out[6] = r6; + out[7] = r7; + out[8] = r8; + out[9] = r9; +} + +/* out = in ^ (2 * count) */ +void curve25519_square_times(bignum25519 out, const bignum25519 in, int count) { + uint32_t r0 = 0, r1 = 0, r2 = 0, r3 = 0, r4 = 0, r5 = 0, r6 = 0, r7 = 0, r8 = 0, r9 = 0; + uint32_t d6 = 0, d7 = 0, d8 = 0, d9 = 0; + uint64_t m0 = 0, m1 = 0, m2 = 0, m3 = 0, m4 = 0, m5 = 0, m6 = 0, m7 = 0, m8 = 0, m9 = 0, c = 0; + uint32_t p = 0; + + r0 = in[0]; + r1 = in[1]; + r2 = in[2]; + r3 = in[3]; + r4 = in[4]; + r5 = in[5]; + r6 = in[6]; + r7 = in[7]; + r8 = in[8]; + r9 = in[9]; + + do { + m0 = mul32x32_64(r0, r0); + r0 *= 2; + m1 = mul32x32_64(r0, r1); + m2 = mul32x32_64(r0, r2) + mul32x32_64(r1, r1 * 2); + r1 *= 2; + m3 = mul32x32_64(r0, r3) + mul32x32_64(r1, r2); + m4 = mul32x32_64(r0, r4) + mul32x32_64(r1, r3 * 2) + mul32x32_64(r2, r2); + r2 *= 2; + m5 = mul32x32_64(r0, r5) + mul32x32_64(r1, r4) + mul32x32_64(r2, r3); + m6 = mul32x32_64(r0, r6) + mul32x32_64(r1, r5 * 2) + mul32x32_64(r2, r4) + + mul32x32_64(r3, r3 * 2); + r3 *= 2; + m7 = mul32x32_64(r0, r7) + mul32x32_64(r1, r6) + mul32x32_64(r2, r5) + mul32x32_64(r3, r4); + m8 = mul32x32_64(r0, r8) + mul32x32_64(r1, r7 * 2) + mul32x32_64(r2, r6) + + mul32x32_64(r3, r5 * 2) + mul32x32_64(r4, r4); + m9 = mul32x32_64(r0, r9) + mul32x32_64(r1, r8) + mul32x32_64(r2, r7) + + mul32x32_64(r3, r6) + mul32x32_64(r4, r5 * 2); + + d6 = r6 * 19; + d7 = r7 * 2 * 19; + d8 = r8 * 19; + d9 = r9 * 2 * 19; + + m0 += + (mul32x32_64(d9, r1) + mul32x32_64(d8, r2) + mul32x32_64(d7, r3) + + mul32x32_64(d6, r4 * 2) + mul32x32_64(r5, r5 * 2 * 19)); + m1 += + (mul32x32_64(d9, r2 / 2) + mul32x32_64(d8, r3) + mul32x32_64(d7, r4) + + mul32x32_64(d6, r5 * 2)); + m2 += + (mul32x32_64(d9, r3) + mul32x32_64(d8, r4 * 2) + mul32x32_64(d7, r5 * 2) + + mul32x32_64(d6, r6)); + m3 += (mul32x32_64(d9, r4) + mul32x32_64(d8, r5 * 2) + mul32x32_64(d7, r6)); + m4 += (mul32x32_64(d9, r5 * 2) + mul32x32_64(d8, r6 * 2) + mul32x32_64(d7, r7)); + m5 += (mul32x32_64(d9, r6) + mul32x32_64(d8, r7 * 2)); + m6 += (mul32x32_64(d9, r7 * 2) + mul32x32_64(d8, r8)); + m7 += (mul32x32_64(d9, r8)); + m8 += (mul32x32_64(d9, r9)); + + r0 = (uint32_t)m0 & reduce_mask_26; + c = (m0 >> 26); + m1 += c; + r1 = (uint32_t)m1 & reduce_mask_25; + c = (m1 >> 25); + m2 += c; + r2 = (uint32_t)m2 & reduce_mask_26; + c = (m2 >> 26); + m3 += c; + r3 = (uint32_t)m3 & reduce_mask_25; + c = (m3 >> 25); + m4 += c; + r4 = (uint32_t)m4 & reduce_mask_26; + c = (m4 >> 26); + m5 += c; + r5 = (uint32_t)m5 & reduce_mask_25; + c = (m5 >> 25); + m6 += c; + r6 = (uint32_t)m6 & reduce_mask_26; + c = (m6 >> 26); + m7 += c; + r7 = (uint32_t)m7 & reduce_mask_25; + c = (m7 >> 25); + m8 += c; + r8 = (uint32_t)m8 & reduce_mask_26; + c = (m8 >> 26); + m9 += c; + r9 = (uint32_t)m9 & reduce_mask_25; + p = (uint32_t)(m9 >> 25); + m0 = r0 + mul32x32_64(p, 19); + r0 = (uint32_t)m0 & reduce_mask_26; + p = (uint32_t)(m0 >> 26); + r1 += p; + } while(--count); + + out[0] = r0; + out[1] = r1; + out[2] = r2; + out[3] = r3; + out[4] = r4; + out[5] = r5; + out[6] = r6; + out[7] = r7; + out[8] = r8; + out[9] = r9; +} + +/* Take a little-endian, 32-byte number and expand it into polynomial form */ +void curve25519_expand(bignum25519 out, const unsigned char in[32]) { + uint32_t x0 = 0, x1 = 0, x2 = 0, x3 = 0, x4 = 0, x5 = 0, x6 = 0, x7 = 0; +#define F(s) \ + ((((uint32_t)in[s + 0])) | (((uint32_t)in[s + 1]) << 8) | (((uint32_t)in[s + 2]) << 16) | \ + (((uint32_t)in[s + 3]) << 24)) + x0 = F(0); + x1 = F(4); + x2 = F(8); + x3 = F(12); + x4 = F(16); + x5 = F(20); + x6 = F(24); + x7 = F(28); +#undef F + + out[0] = (x0)&reduce_mask_26; + out[1] = ((((uint64_t)x1 << 32) | x0) >> 26) & reduce_mask_25; + out[2] = ((((uint64_t)x2 << 32) | x1) >> 19) & reduce_mask_26; + out[3] = ((((uint64_t)x3 << 32) | x2) >> 13) & reduce_mask_25; + out[4] = ((x3) >> 6) & reduce_mask_26; + out[5] = (x4)&reduce_mask_25; + out[6] = ((((uint64_t)x5 << 32) | x4) >> 25) & reduce_mask_26; + out[7] = ((((uint64_t)x6 << 32) | x5) >> 19) & reduce_mask_25; + out[8] = ((((uint64_t)x7 << 32) | x6) >> 12) & reduce_mask_26; + out[9] = ((x7) >> 6) & reduce_mask_25; /* ignore the top bit */ +} + +/* Take a fully reduced polynomial form number and contract it into a + * little-endian, 32-byte array + */ +void curve25519_contract(unsigned char out[32], const bignum25519 in) { + bignum25519 f = {0}; + curve25519_copy(f, in); + +#define carry_pass() \ + f[1] += f[0] >> 26; \ + f[0] &= reduce_mask_26; \ + f[2] += f[1] >> 25; \ + f[1] &= reduce_mask_25; \ + f[3] += f[2] >> 26; \ + f[2] &= reduce_mask_26; \ + f[4] += f[3] >> 25; \ + f[3] &= reduce_mask_25; \ + f[5] += f[4] >> 26; \ + f[4] &= reduce_mask_26; \ + f[6] += f[5] >> 25; \ + f[5] &= reduce_mask_25; \ + f[7] += f[6] >> 26; \ + f[6] &= reduce_mask_26; \ + f[8] += f[7] >> 25; \ + f[7] &= reduce_mask_25; \ + f[9] += f[8] >> 26; \ + f[8] &= reduce_mask_26; + +#define carry_pass_full() \ + carry_pass() f[0] += 19 * (f[9] >> 25); \ + f[9] &= reduce_mask_25; + +#define carry_pass_final() carry_pass() f[9] &= reduce_mask_25; + + carry_pass_full() carry_pass_full() + + /* now t is between 0 and 2^255-1, properly carried. */ + /* case 1: between 0 and 2^255-20. case 2: between 2^255-19 and 2^255-1. */ + f[0] += 19; + carry_pass_full() + + /* now between 19 and 2^255-1 in both cases, and offset by 19. */ + f[0] += (reduce_mask_26 + 1) - 19; + f[1] += (reduce_mask_25 + 1) - 1; + f[2] += (reduce_mask_26 + 1) - 1; + f[3] += (reduce_mask_25 + 1) - 1; + f[4] += (reduce_mask_26 + 1) - 1; + f[5] += (reduce_mask_25 + 1) - 1; + f[6] += (reduce_mask_26 + 1) - 1; + f[7] += (reduce_mask_25 + 1) - 1; + f[8] += (reduce_mask_26 + 1) - 1; + f[9] += (reduce_mask_25 + 1) - 1; + + /* now between 2^255 and 2^256-20, and offset by 2^255. */ + carry_pass_final() + +#undef carry_pass +#undef carry_full +#undef carry_final + + f[1] <<= 2; + f[2] <<= 3; + f[3] <<= 5; + f[4] <<= 6; + f[6] <<= 1; + f[7] <<= 3; + f[8] <<= 4; + f[9] <<= 6; + +#define F(i, s) \ + out[s + 0] |= (unsigned char)(f[i] & 0xff); \ + out[s + 1] = (unsigned char)((f[i] >> 8) & 0xff); \ + out[s + 2] = (unsigned char)((f[i] >> 16) & 0xff); \ + out[s + 3] = (unsigned char)((f[i] >> 24) & 0xff); + + out[0] = 0; + out[16] = 0; + F(0, 0); + F(1, 3); + F(2, 6); + F(3, 9); + F(4, 12); + F(5, 16); + F(6, 19); + F(7, 22); + F(8, 25); + F(9, 28); +#undef F +} + +/* if (iswap) swap(a, b) */ +void curve25519_swap_conditional(bignum25519 a, bignum25519 b, uint32_t iswap) { + const uint32_t swap = (uint32_t)(-(int32_t)iswap); + uint32_t x0 = 0, x1 = 0, x2 = 0, x3 = 0, x4 = 0, x5 = 0, x6 = 0, x7 = 0, x8 = 0, x9 = 0; + + x0 = swap & (a[0] ^ b[0]); + a[0] ^= x0; + b[0] ^= x0; + x1 = swap & (a[1] ^ b[1]); + a[1] ^= x1; + b[1] ^= x1; + x2 = swap & (a[2] ^ b[2]); + a[2] ^= x2; + b[2] ^= x2; + x3 = swap & (a[3] ^ b[3]); + a[3] ^= x3; + b[3] ^= x3; + x4 = swap & (a[4] ^ b[4]); + a[4] ^= x4; + b[4] ^= x4; + x5 = swap & (a[5] ^ b[5]); + a[5] ^= x5; + b[5] ^= x5; + x6 = swap & (a[6] ^ b[6]); + a[6] ^= x6; + b[6] ^= x6; + x7 = swap & (a[7] ^ b[7]); + a[7] ^= x7; + b[7] ^= x7; + x8 = swap & (a[8] ^ b[8]); + a[8] ^= x8; + b[8] ^= x8; + x9 = swap & (a[9] ^ b[9]); + a[9] ^= x9; + b[9] ^= x9; +} + +void curve25519_set(bignum25519 r, uint32_t x) { + r[0] = x & reduce_mask_26; + x >>= 26; + r[1] = x & reduce_mask_25; + r[2] = 0; + r[3] = 0; + r[4] = 0; + r[5] = 0; + r[6] = 0; + r[7] = 0; + r[8] = 0; + r[9] = 0; +} + +void curve25519_set_d(bignum25519 r) { + curve25519_copy(r, ge25519_ecd); +} + +void curve25519_set_2d(bignum25519 r) { + curve25519_copy(r, ge25519_ec2d); +} + +void curve25519_set_sqrtneg1(bignum25519 r) { + curve25519_copy(r, ge25519_sqrtneg1); +} + +int curve25519_isnegative(const bignum25519 f) { + unsigned char s[32] = {0}; + curve25519_contract(s, f); + return s[0] & 1; +} + +int curve25519_isnonzero(const bignum25519 f) { + unsigned char s[32] = {0}; + curve25519_contract(s, f); + return ((((int)(s[0] | s[1] | s[2] | s[3] | s[4] | s[5] | s[6] | s[7] | s[8] | s[9] | s[10] | s[11] | s[12] | s[13] | s[14] | s[15] | s[16] | s[17] | s[18] | s[19] | s[20] | s[21] | s[22] | s[23] | s[24] | s[25] | s[26] | s[27] | s[28] | s[29] | s[30] | s[31]) - + 1) >> + 8) + + 1) & + 0x1; +} + +void curve25519_reduce(bignum25519 out, const bignum25519 in) { + uint32_t c = 0; + out[0] = in[0]; + c = (out[0] >> 26); + out[0] &= reduce_mask_26; + out[1] = in[1] + c; + c = (out[1] >> 25); + out[1] &= reduce_mask_25; + out[2] = in[2] + c; + c = (out[2] >> 26); + out[2] &= reduce_mask_26; + out[3] = in[3] + c; + c = (out[3] >> 25); + out[3] &= reduce_mask_25; + out[4] = in[4] + c; + c = (out[4] >> 26); + out[4] &= reduce_mask_26; + out[5] = in[5] + c; + c = (out[5] >> 25); + out[5] &= reduce_mask_25; + out[6] = in[6] + c; + c = (out[6] >> 26); + out[6] &= reduce_mask_26; + out[7] = in[7] + c; + c = (out[7] >> 25); + out[7] &= reduce_mask_25; + out[8] = in[8] + c; + c = (out[8] >> 26); + out[8] &= reduce_mask_26; + out[9] = in[9] + c; + c = (out[9] >> 25); + out[9] &= reduce_mask_25; + out[0] += 19 * c; +} + +void curve25519_divpowm1(bignum25519 r, const bignum25519 u, const bignum25519 v) { + bignum25519 v3 = {0}, uv7 = {0}, t0 = {0}, t1 = {0}, t2 = {0}; + int i = 0; + + curve25519_square(v3, v); + curve25519_mul(v3, v3, v); /* v3 = v^3 */ + curve25519_square(uv7, v3); + curve25519_mul(uv7, uv7, v); + curve25519_mul(uv7, uv7, u); /* uv7 = uv^7 */ + + /*fe_pow22523(uv7, uv7);*/ + /* From fe_pow22523.c */ + + curve25519_square(t0, uv7); + curve25519_square(t1, t0); + curve25519_square(t1, t1); + curve25519_mul(t1, uv7, t1); + curve25519_mul(t0, t0, t1); + curve25519_square(t0, t0); + curve25519_mul(t0, t1, t0); + curve25519_square(t1, t0); + for(i = 0; i < 4; ++i) { + curve25519_square(t1, t1); + } + curve25519_mul(t0, t1, t0); + curve25519_square(t1, t0); + for(i = 0; i < 9; ++i) { + curve25519_square(t1, t1); + } + curve25519_mul(t1, t1, t0); + curve25519_square(t2, t1); + for(i = 0; i < 19; ++i) { + curve25519_square(t2, t2); + } + curve25519_mul(t1, t2, t1); + for(i = 0; i < 10; ++i) { + curve25519_square(t1, t1); + } + curve25519_mul(t0, t1, t0); + curve25519_square(t1, t0); + for(i = 0; i < 49; ++i) { + curve25519_square(t1, t1); + } + curve25519_mul(t1, t1, t0); + curve25519_square(t2, t1); + for(i = 0; i < 99; ++i) { + curve25519_square(t2, t2); + } + curve25519_mul(t1, t2, t1); + for(i = 0; i < 50; ++i) { + curve25519_square(t1, t1); + } + curve25519_mul(t0, t1, t0); + curve25519_square(t0, t0); + curve25519_square(t0, t0); + curve25519_mul(t0, t0, uv7); + + /* End fe_pow22523.c */ + /* t0 = (uv^7)^((q-5)/8) */ + curve25519_mul(t0, t0, v3); + curve25519_mul(r, t0, u); /* u^(m+1)v^(-(m+1)) */ +} + +void curve25519_expand_reduce(bignum25519 out, const unsigned char in[32]) { + uint32_t x0 = 0, x1 = 0, x2 = 0, x3 = 0, x4 = 0, x5 = 0, x6 = 0, x7 = 0; +#define F(s) \ + ((((uint32_t)in[s + 0])) | (((uint32_t)in[s + 1]) << 8) | (((uint32_t)in[s + 2]) << 16) | \ + (((uint32_t)in[s + 3]) << 24)) + x0 = F(0); + x1 = F(4); + x2 = F(8); + x3 = F(12); + x4 = F(16); + x5 = F(20); + x6 = F(24); + x7 = F(28); +#undef F + + out[0] = (x0)&reduce_mask_26; + out[1] = ((((uint64_t)x1 << 32) | x0) >> 26) & reduce_mask_25; + out[2] = ((((uint64_t)x2 << 32) | x1) >> 19) & reduce_mask_26; + out[3] = ((((uint64_t)x3 << 32) | x2) >> 13) & reduce_mask_25; + out[4] = ((x3) >> 6) & reduce_mask_26; + out[5] = (x4)&reduce_mask_25; + out[6] = ((((uint64_t)x5 << 32) | x4) >> 25) & reduce_mask_26; + out[7] = ((((uint64_t)x6 << 32) | x5) >> 19) & reduce_mask_25; + out[8] = ((((uint64_t)x7 << 32) | x6) >> 12) & reduce_mask_26; + out[9] = ((x7) >> 6); // & reduce_mask_25; /* ignore the top bit */ + out[0] += 19 * (out[9] >> 25); + out[9] &= reduce_mask_25; +} diff --git a/applications/external/flipbip/lib/crypto/ed25519_donna/curve25519_donna_32bit.h b/applications/external/flipbip/lib/crypto/ed25519_donna/curve25519_donna_32bit.h new file mode 100644 index 0000000000..87bea94f98 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/ed25519_donna/curve25519_donna_32bit.h @@ -0,0 +1,79 @@ +/* + Public domain by Andrew M. + See: https://github.com/floodyberry/curve25519-donna + + 32 bit integer curve25519 implementation +*/ + +typedef uint32_t bignum25519[10]; + +/* out = in */ +void curve25519_copy(bignum25519 out, const bignum25519 in); + +/* out = a + b */ +void curve25519_add(bignum25519 out, const bignum25519 a, const bignum25519 b); + +void curve25519_add_after_basic(bignum25519 out, const bignum25519 a, const bignum25519 b); + +void curve25519_add_reduce(bignum25519 out, const bignum25519 a, const bignum25519 b); + +/* out = a - b */ +void curve25519_sub(bignum25519 out, const bignum25519 a, const bignum25519 b); + +/* out = in * scalar */ +void curve25519_scalar_product(bignum25519 out, const bignum25519 in, const uint32_t scalar); + +/* out = a - b, where a is the result of a basic op (add,sub) */ +void curve25519_sub_after_basic(bignum25519 out, const bignum25519 a, const bignum25519 b); + +void curve25519_sub_reduce(bignum25519 out, const bignum25519 a, const bignum25519 b); + +/* out = -a */ +void curve25519_neg(bignum25519 out, const bignum25519 a); + +/* out = a * b */ +#define curve25519_mul_noinline curve25519_mul +void curve25519_mul(bignum25519 out, const bignum25519 a, const bignum25519 b); + +/* out = in * in */ +void curve25519_square(bignum25519 out, const bignum25519 in); + +/* out = in ^ (2 * count) */ +void curve25519_square_times(bignum25519 out, const bignum25519 in, int count); + +/* Take a little-endian, 32-byte number and expand it into polynomial form */ +void curve25519_expand(bignum25519 out, const unsigned char in[32]); + +/* Take a fully reduced polynomial form number and contract it into a + * little-endian, 32-byte array + */ +void curve25519_contract(unsigned char out[32], const bignum25519 in); + +/* if (iswap) swap(a, b) */ +void curve25519_swap_conditional(bignum25519 a, bignum25519 b, uint32_t iswap); + +/* uint32_t to Zmod(2^255-19) */ +void curve25519_set(bignum25519 r, uint32_t x); + +/* set d */ +void curve25519_set_d(bignum25519 r); + +/* set 2d */ +void curve25519_set_2d(bignum25519 r); + +/* set sqrt(-1) */ +void curve25519_set_sqrtneg1(bignum25519 r); + +/* constant time Zmod(2^255-19) negative test */ +int curve25519_isnegative(const bignum25519 f); + +/* constant time Zmod(2^255-19) non-zero test */ +int curve25519_isnonzero(const bignum25519 f); + +/* reduce Zmod(2^255-19) */ +void curve25519_reduce(bignum25519 r, const bignum25519 in); + +void curve25519_divpowm1(bignum25519 r, const bignum25519 u, const bignum25519 v); + +/* Zmod(2^255-19) from byte array to bignum25519 expansion with modular reduction */ +void curve25519_expand_reduce(bignum25519 out, const unsigned char in[32]); diff --git a/applications/external/flipbip/lib/crypto/ed25519_donna/curve25519_donna_helpers.c b/applications/external/flipbip/lib/crypto/ed25519_donna/curve25519_donna_helpers.c new file mode 100644 index 0000000000..22a7b02548 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/ed25519_donna/curve25519_donna_helpers.c @@ -0,0 +1,66 @@ +/* + Public domain by Andrew M. + See: https://github.com/floodyberry/curve25519-donna + + Curve25519 implementation agnostic helpers +*/ + +#include "ed25519_donna.h" + +/* + * In: b = 2^5 - 2^0 + * Out: b = 2^250 - 2^0 + */ +void curve25519_pow_two5mtwo0_two250mtwo0(bignum25519 b) { + bignum25519 ALIGN(16) t0 = {0}, c = {0}; + + /* 2^5 - 2^0 */ /* b */ + /* 2^10 - 2^5 */ curve25519_square_times(t0, b, 5); + /* 2^10 - 2^0 */ curve25519_mul_noinline(b, t0, b); + /* 2^20 - 2^10 */ curve25519_square_times(t0, b, 10); + /* 2^20 - 2^0 */ curve25519_mul_noinline(c, t0, b); + /* 2^40 - 2^20 */ curve25519_square_times(t0, c, 20); + /* 2^40 - 2^0 */ curve25519_mul_noinline(t0, t0, c); + /* 2^50 - 2^10 */ curve25519_square_times(t0, t0, 10); + /* 2^50 - 2^0 */ curve25519_mul_noinline(b, t0, b); + /* 2^100 - 2^50 */ curve25519_square_times(t0, b, 50); + /* 2^100 - 2^0 */ curve25519_mul_noinline(c, t0, b); + /* 2^200 - 2^100 */ curve25519_square_times(t0, c, 100); + /* 2^200 - 2^0 */ curve25519_mul_noinline(t0, t0, c); + /* 2^250 - 2^50 */ curve25519_square_times(t0, t0, 50); + /* 2^250 - 2^0 */ curve25519_mul_noinline(b, t0, b); +} + +/* + * z^(p - 2) = z(2^255 - 21) + */ +void curve25519_recip(bignum25519 out, const bignum25519 z) { + bignum25519 ALIGN(16) a = {0}, t0 = {0}, b = {0}; + + /* 2 */ curve25519_square_times(a, z, 1); /* a = 2 */ + /* 8 */ curve25519_square_times(t0, a, 2); + /* 9 */ curve25519_mul_noinline(b, t0, z); /* b = 9 */ + /* 11 */ curve25519_mul_noinline(a, b, a); /* a = 11 */ + /* 22 */ curve25519_square_times(t0, a, 1); + /* 2^5 - 2^0 = 31 */ curve25519_mul_noinline(b, t0, b); + /* 2^250 - 2^0 */ curve25519_pow_two5mtwo0_two250mtwo0(b); + /* 2^255 - 2^5 */ curve25519_square_times(b, b, 5); + /* 2^255 - 21 */ curve25519_mul_noinline(out, b, a); +} + +/* + * z^((p-5)/8) = z^(2^252 - 3) + */ +void curve25519_pow_two252m3(bignum25519 two252m3, const bignum25519 z) { + bignum25519 ALIGN(16) b, c, t0; + + /* 2 */ curve25519_square_times(c, z, 1); /* c = 2 */ + /* 8 */ curve25519_square_times(t0, c, 2); /* t0 = 8 */ + /* 9 */ curve25519_mul_noinline(b, t0, z); /* b = 9 */ + /* 11 */ curve25519_mul_noinline(c, b, c); /* c = 11 */ + /* 22 */ curve25519_square_times(t0, c, 1); + /* 2^5 - 2^0 = 31 */ curve25519_mul_noinline(b, t0, b); + /* 2^250 - 2^0 */ curve25519_pow_two5mtwo0_two250mtwo0(b); + /* 2^252 - 2^2 */ curve25519_square_times(b, b, 2); + /* 2^252 - 3 */ curve25519_mul_noinline(two252m3, b, z); +} diff --git a/applications/external/flipbip/lib/crypto/ed25519_donna/curve25519_donna_helpers.h b/applications/external/flipbip/lib/crypto/ed25519_donna/curve25519_donna_helpers.h new file mode 100644 index 0000000000..62fde9099c --- /dev/null +++ b/applications/external/flipbip/lib/crypto/ed25519_donna/curve25519_donna_helpers.h @@ -0,0 +1,22 @@ +/* + Public domain by Andrew M. + See: https://github.com/floodyberry/curve25519-donna + + Curve25519 implementation agnostic helpers +*/ + +/* + * In: b = 2^5 - 2^0 + * Out: b = 2^250 - 2^0 + */ +void curve25519_pow_two5mtwo0_two250mtwo0(bignum25519 b); + +/* + * z^(p - 2) = z(2^255 - 21) + */ +void curve25519_recip(bignum25519 out, const bignum25519 z); + +/* + * z^((p-5)/8) = z^(2^252 - 3) + */ +void curve25519_pow_two252m3(bignum25519 two252m3, const bignum25519 z); diff --git a/applications/external/flipbip/lib/crypto/ed25519_donna/curve25519_donna_scalarmult_base.c b/applications/external/flipbip/lib/crypto/ed25519_donna/curve25519_donna_scalarmult_base.c new file mode 100644 index 0000000000..ff162869aa --- /dev/null +++ b/applications/external/flipbip/lib/crypto/ed25519_donna/curve25519_donna_scalarmult_base.c @@ -0,0 +1,70 @@ +#include "ed25519_donna.h" +#include "ed25519.h" + +/* Calculates nQ where Q is the x-coordinate of a point on the curve + * + * mypublic: the packed little endian x coordinate of the resulting curve point + * n: a little endian, 32-byte number + * basepoint: a packed little endian point of the curve + */ + +void curve25519_scalarmult_donna( + curve25519_key mypublic, + const curve25519_key n, + const curve25519_key basepoint) { + bignum25519 nqpqx = {1}, nqpqz = {0}, nqz = {1}, nqx = {0}; + bignum25519 q = {0}, qx = {0}, qpqx = {0}, qqx = {0}, zzz = {0}, zmone = {0}; + size_t bit = 0, lastbit = 0; + int32_t i = 0; + + curve25519_expand(q, basepoint); + curve25519_copy(nqx, q); + + /* bit 255 is always 0, and bit 254 is always 1, so skip bit 255 and + start pre-swapped on bit 254 */ + lastbit = 1; + + /* we are doing bits 254..3 in the loop, but are swapping in bits 253..2 */ + for(i = 253; i >= 2; i--) { + curve25519_add(qx, nqx, nqz); + curve25519_sub(nqz, nqx, nqz); + curve25519_add(qpqx, nqpqx, nqpqz); + curve25519_sub(nqpqz, nqpqx, nqpqz); + curve25519_mul(nqpqx, qpqx, nqz); + curve25519_mul(nqpqz, qx, nqpqz); + curve25519_add(qqx, nqpqx, nqpqz); + curve25519_sub(nqpqz, nqpqx, nqpqz); + curve25519_square(nqpqz, nqpqz); + curve25519_square(nqpqx, qqx); + curve25519_mul(nqpqz, nqpqz, q); + curve25519_square(qx, qx); + curve25519_square(nqz, nqz); + curve25519_mul(nqx, qx, nqz); + curve25519_sub(nqz, qx, nqz); + curve25519_scalar_product(zzz, nqz, 121665); + curve25519_add(zzz, zzz, qx); + curve25519_mul(nqz, nqz, zzz); + + bit = (n[i / 8] >> (i & 7)) & 1; + curve25519_swap_conditional(nqx, nqpqx, bit ^ lastbit); + curve25519_swap_conditional(nqz, nqpqz, bit ^ lastbit); + lastbit = bit; + } + + /* the final 3 bits are always zero, so we only need to double */ + for(i = 0; i < 3; i++) { + curve25519_add(qx, nqx, nqz); + curve25519_sub(nqz, nqx, nqz); + curve25519_square(qx, qx); + curve25519_square(nqz, nqz); + curve25519_mul(nqx, qx, nqz); + curve25519_sub(nqz, qx, nqz); + curve25519_scalar_product(zzz, nqz, 121665); + curve25519_add(zzz, zzz, qx); + curve25519_mul(nqz, nqz, zzz); + } + + curve25519_recip(zmone, nqz); + curve25519_mul(nqz, nqx, zmone); + curve25519_contract(mypublic, nqz); +} diff --git a/applications/external/flipbip/lib/crypto/ed25519_donna/curve25519_donna_scalarmult_base.h b/applications/external/flipbip/lib/crypto/ed25519_donna/curve25519_donna_scalarmult_base.h new file mode 100644 index 0000000000..c6f18ed60f --- /dev/null +++ b/applications/external/flipbip/lib/crypto/ed25519_donna/curve25519_donna_scalarmult_base.h @@ -0,0 +1,11 @@ +/* Calculates nQ where Q is the x-coordinate of a point on the curve + * + * mypublic: the packed little endian x coordinate of the resulting curve point + * n: a little endian, 32-byte number + * basepoint: a packed little endian point of the curve + */ + +void curve25519_scalarmult_donna( + curve25519_key mypublic, + const curve25519_key n, + const curve25519_key basepoint); diff --git a/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519.c b/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519.c new file mode 100644 index 0000000000..2ccc2021dc --- /dev/null +++ b/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519.c @@ -0,0 +1,336 @@ +/* + Public domain by Andrew M. + + Ed25519 reference implementation using Ed25519-donna +*/ + +/* define ED25519_SUFFIX to have it appended to the end of each public function */ +#ifdef ED25519_SUFFIX +#define ED25519_FN3(fn, suffix) fn##suffix +#define ED25519_FN2(fn, suffix) ED25519_FN3(fn, suffix) +#define ED25519_FN(fn) ED25519_FN2(fn, ED25519_SUFFIX) +#else +#define ED25519_FN(fn) fn +#endif + +#include "ed25519_donna.h" +#include "ed25519.h" + +#include "ed25519_hash_custom.h" +#include "../rand.h" +#include "../memzero.h" + +/* + Generates a (extsk[0..31]) and aExt (extsk[32..63]) +*/ +DONNA_INLINE static void ed25519_extsk(hash_512bits extsk, const ed25519_secret_key sk) { + ed25519_hash(extsk, sk, 32); + extsk[0] &= 248; + extsk[31] &= 127; + extsk[31] |= 64; +} + +static void ed25519_hram( + hash_512bits hram, + const ed25519_public_key R, + const ed25519_public_key pk, + const unsigned char* m, + size_t mlen) { + ed25519_hash_context ctx; + ed25519_hash_init(&ctx); + ed25519_hash_update(&ctx, R, 32); + ed25519_hash_update(&ctx, pk, 32); + ed25519_hash_update(&ctx, m, mlen); + ed25519_hash_final(&ctx, hram); +} + +void ED25519_FN(ed25519_publickey)(const ed25519_secret_key sk, ed25519_public_key pk) { + hash_512bits extsk = {0}; + ed25519_extsk(extsk, sk); + ed25519_publickey_ext(extsk, pk); + memzero(&extsk, sizeof(extsk)); +} + +void ED25519_FN(ed25519_cosi_commit)(ed25519_secret_key nonce, ed25519_public_key commitment) { + bignum256modm r = {0}; + ge25519 ALIGN(16) R; + unsigned char extnonce[64] = {0}; + + /* r = random512 mod L */ + random_buffer(extnonce, sizeof(extnonce)); + expand256_modm(r, extnonce, sizeof(extnonce)); + memzero(&extnonce, sizeof(extnonce)); + contract256_modm(nonce, r); + + /* R = rB */ + ge25519_scalarmult_base_niels(&R, ge25519_niels_base_multiples, r); + memzero(&r, sizeof(r)); + ge25519_pack(commitment, &R); +} + +int ED25519_FN(ed25519_cosi_sign)( + const unsigned char* m, + size_t mlen, + const ed25519_secret_key sk, + const ed25519_secret_key nonce, + const ed25519_public_key R, + const ed25519_public_key pk, + ed25519_cosi_signature sig) { + bignum256modm r = {0}, S = {0}, a = {0}; + hash_512bits extsk = {0}, hram = {0}; + + ed25519_extsk(extsk, sk); + + /* r */ + expand_raw256_modm(r, nonce); + if(!is_reduced256_modm(r)) return -1; + + /* S = H(R,A,m).. */ + ed25519_hram(hram, R, pk, m, mlen); + expand256_modm(S, hram, 64); + + /* S = H(R,A,m)a */ + expand256_modm(a, extsk, 32); + memzero(&extsk, sizeof(extsk)); + mul256_modm(S, S, a); + memzero(&a, sizeof(a)); + + /* S = (r + H(R,A,m)a) */ + add256_modm(S, S, r); + memzero(&r, sizeof(r)); + + /* S = (r + H(R,A,m)a) mod L */ + contract256_modm(sig, S); + + return 0; +} + +void ED25519_FN(ed25519_sign_ext)( + const unsigned char* m, + size_t mlen, + const ed25519_secret_key sk, + const ed25519_secret_key skext, + ed25519_signature RS) { + ed25519_hash_context ctx; + bignum256modm r = {0}, S = {0}, a = {0}; + ge25519 ALIGN(16) R = {0}; + ge25519 ALIGN(16) A = {0}; + ed25519_public_key pk = {0}; + hash_512bits extsk = {0}, hashr = {0}, hram = {0}; + + /* we don't stretch the key through hashing first since its already 64 bytes */ + + memcpy(extsk, sk, 32); + memcpy(extsk + 32, skext, 32); + + /* r = H(aExt[32..64], m) */ + ed25519_hash_init(&ctx); + ed25519_hash_update(&ctx, extsk + 32, 32); + ed25519_hash_update(&ctx, m, mlen); + ed25519_hash_final(&ctx, hashr); + expand256_modm(r, hashr, 64); + memzero(&hashr, sizeof(hashr)); + + /* R = rB */ + ge25519_scalarmult_base_niels(&R, ge25519_niels_base_multiples, r); + ge25519_pack(RS, &R); + + /* a = aExt[0..31] */ + expand256_modm(a, extsk, 32); + memzero(&extsk, sizeof(extsk)); + + /* A = aB */ + ge25519_scalarmult_base_niels(&A, ge25519_niels_base_multiples, a); + ge25519_pack(pk, &A); + + /* S = H(R,A,m).. */ + ed25519_hram(hram, RS, pk, m, mlen); + expand256_modm(S, hram, 64); + + /* S = H(R,A,m)a */ + mul256_modm(S, S, a); + memzero(&a, sizeof(a)); + + /* S = (r + H(R,A,m)a) */ + add256_modm(S, S, r); + memzero(&r, sizeof(r)); + + /* S = (r + H(R,A,m)a) mod L */ + contract256_modm(RS + 32, S); +} + +void ED25519_FN(ed25519_sign)( + const unsigned char* m, + size_t mlen, + const ed25519_secret_key sk, + ed25519_signature RS) { + hash_512bits extsk = {0}; + ed25519_extsk(extsk, sk); + ED25519_FN(ed25519_sign_ext)(m, mlen, extsk, extsk + 32, RS); + memzero(&extsk, sizeof(extsk)); +} + +int ED25519_FN(ed25519_sign_open)( + const unsigned char* m, + size_t mlen, + const ed25519_public_key pk, + const ed25519_signature RS) { + ge25519 ALIGN(16) R = {0}, A = {0}; + hash_512bits hash = {0}; + bignum256modm hram = {0}, S = {0}; + unsigned char checkR[32] = {0}; + + if((RS[63] & 224) || !ge25519_unpack_negative_vartime(&A, pk)) return -1; + + /* hram = H(R,A,m) */ + ed25519_hram(hash, RS, pk, m, mlen); + expand256_modm(hram, hash, 64); + + /* S */ + expand_raw256_modm(S, RS + 32); + if(!is_reduced256_modm(S)) return -1; + + /* SB - H(R,A,m)A */ + ge25519_double_scalarmult_vartime(&R, &A, hram, S); + ge25519_pack(checkR, &R); + + /* check that R = SB - H(R,A,m)A */ + return ed25519_verify(RS, checkR, 32) ? 0 : -1; +} + +int ED25519_FN(ed25519_scalarmult)( + ed25519_public_key res, + const ed25519_secret_key sk, + const ed25519_public_key pk) { + bignum256modm a = {0}; + ge25519 ALIGN(16) A = {0}, P = {0}; + hash_512bits extsk = {0}; + + ed25519_extsk(extsk, sk); + expand256_modm(a, extsk, 32); + memzero(&extsk, sizeof(extsk)); + + if(!ge25519_unpack_negative_vartime(&P, pk)) { + return -1; + } + + ge25519_scalarmult(&A, &P, a); + memzero(&a, sizeof(a)); + curve25519_neg(A.x, A.x); + ge25519_pack(res, &A); + return 0; +} + +#ifndef ED25519_SUFFIX + +#include "curve25519_donna_scalarmult_base.h" + +void ed25519_publickey_ext(const ed25519_secret_key extsk, ed25519_public_key pk) { + bignum256modm a = {0}; + ge25519 ALIGN(16) A = {0}; + + expand256_modm(a, extsk, 32); + + /* A = aB */ + ge25519_scalarmult_base_niels(&A, ge25519_niels_base_multiples, a); + memzero(&a, sizeof(a)); + ge25519_pack(pk, &A); +} + +int ed25519_cosi_combine_publickeys( + ed25519_public_key res, + CONST ed25519_public_key* pks, + size_t n) { + size_t i = 0; + ge25519 P = {0}; + ge25519_pniels sump = {0}; + ge25519_p1p1 sump1 = {0}; + + if(n == 1) { + memcpy(res, pks, sizeof(ed25519_public_key)); + return 0; + } + if(!ge25519_unpack_negative_vartime(&P, pks[i++])) { + return -1; + } + ge25519_full_to_pniels(&sump, &P); + while(i < n - 1) { + if(!ge25519_unpack_negative_vartime(&P, pks[i++])) { + return -1; + } + ge25519_pnielsadd(&sump, &P, &sump); + } + if(!ge25519_unpack_negative_vartime(&P, pks[i++])) { + return -1; + } + ge25519_pnielsadd_p1p1(&sump1, &P, &sump, 0); + ge25519_p1p1_to_partial(&P, &sump1); + curve25519_neg(P.x, P.x); + ge25519_pack(res, &P); + return 0; +} + +void ed25519_cosi_combine_signatures( + ed25519_signature res, + const ed25519_public_key R, + CONST ed25519_cosi_signature* sigs, + size_t n) { + bignum256modm s = {0}, t = {0}; + size_t i = 0; + + expand256_modm(s, sigs[i++], 32); + while(i < n) { + expand256_modm(t, sigs[i++], 32); + add256_modm(s, s, t); + } + memcpy(res, R, 32); + contract256_modm(res + 32, s); +} + +/* + Fast Curve25519 basepoint scalar multiplication +*/ +void curve25519_scalarmult_basepoint(curve25519_key pk, const curve25519_key e) { + curve25519_key ec = {0}; + bignum256modm s = {0}; + bignum25519 ALIGN(16) yplusz = {0}, zminusy = {0}; + ge25519 ALIGN(16) p = {0}; + size_t i = 0; + + /* clamp */ + for(i = 0; i < 32; i++) ec[i] = e[i]; + ec[0] &= 248; + ec[31] &= 127; + ec[31] |= 64; + + expand_raw256_modm(s, ec); + memzero(&ec, sizeof(ec)); + + /* scalar * basepoint */ + ge25519_scalarmult_base_niels(&p, ge25519_niels_base_multiples, s); + memzero(&s, sizeof(s)); + + /* u = (y + z) / (z - y) */ + curve25519_add(yplusz, p.y, p.z); + curve25519_sub(zminusy, p.z, p.y); + curve25519_recip(zminusy, zminusy); + curve25519_mul(yplusz, yplusz, zminusy); + curve25519_contract(pk, yplusz); +} + +void curve25519_scalarmult( + curve25519_key mypublic, + const curve25519_key secret, + const curve25519_key basepoint) { + curve25519_key e = {0}; + size_t i = 0; + + for(i = 0; i < 32; ++i) e[i] = secret[i]; + e[0] &= 0xf8; + e[31] &= 0x7f; + e[31] |= 0x40; + curve25519_scalarmult_donna(mypublic, e, basepoint); + memzero(&e, sizeof(e)); +} + +#endif // ED25519_SUFFIX diff --git a/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519.h b/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519.h new file mode 100644 index 0000000000..f2f0017b63 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519.h @@ -0,0 +1,78 @@ +#ifndef ED25519_H +#define ED25519_H + +#include "../options.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +typedef unsigned char ed25519_signature[64]; +typedef unsigned char ed25519_public_key[32]; +typedef unsigned char ed25519_secret_key[32]; + +typedef unsigned char curve25519_key[32]; + +typedef unsigned char ed25519_cosi_signature[32]; + +void ed25519_publickey(const ed25519_secret_key sk, ed25519_public_key pk); +void ed25519_publickey_ext(const ed25519_secret_key extsk, ed25519_public_key pk); + +int ed25519_sign_open( + const unsigned char* m, + size_t mlen, + const ed25519_public_key pk, + const ed25519_signature RS); +void ed25519_sign( + const unsigned char* m, + size_t mlen, + const ed25519_secret_key sk, + ed25519_signature RS); +void ed25519_sign_ext( + const unsigned char* m, + size_t mlen, + const ed25519_secret_key sk, + const ed25519_secret_key skext, + ed25519_signature RS); + +int ed25519_scalarmult( + ed25519_public_key res, + const ed25519_secret_key sk, + const ed25519_public_key pk); + +void curve25519_scalarmult( + curve25519_key mypublic, + const curve25519_key secret, + const curve25519_key basepoint); +void curve25519_scalarmult_basepoint(curve25519_key mypublic, const curve25519_key secret); + +#if !defined(__GNUC__) || __GNUC__ > 4 +#define CONST const +#else +#define CONST +#endif + +int ed25519_cosi_combine_publickeys( + ed25519_public_key res, + CONST ed25519_public_key* pks, + size_t n); +void ed25519_cosi_combine_signatures( + ed25519_signature res, + const ed25519_public_key R, + CONST ed25519_cosi_signature* sigs, + size_t n); +void ed25519_cosi_commit(ed25519_secret_key nonce, ed25519_public_key commitment); +int ed25519_cosi_sign( + const unsigned char* m, + size_t mlen, + const ed25519_secret_key key, + const ed25519_secret_key nonce, + const ed25519_public_key R, + const ed25519_public_key pk, + ed25519_cosi_signature sig); + +#if defined(__cplusplus) +} +#endif + +#endif // ED25519_H diff --git a/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_donna.h b/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_donna.h new file mode 100644 index 0000000000..00746ab8d9 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_donna.h @@ -0,0 +1,52 @@ +/* + Public domain by Andrew M. + Modified from the amd64-51-30k implementation by + Daniel J. Bernstein + Niels Duif + Tanja Lange + Peter Schwabe + Bo-Yin Yang +*/ + +#ifndef ED25519_DONNA_H +#define ED25519_DONNA_H + +#include "ed25519_donna_portable.h" + +#include "curve25519_donna_32bit.h" + +#include "curve25519_donna_helpers.h" + +#include "modm_donna_32bit.h" + +typedef unsigned char hash_512bits[64]; + +/* + * Arithmetic on the twisted Edwards curve -x^2 + y^2 = 1 + dx^2y^2 + * with d = -(121665/121666) = 37095705934669439343138083508754565189542113879843219016388785533085940283555 + * Base point: (15112221349535400772501151409588531511454012693041857206046113283949847762202,46316835694926478169428394003475163141307993866256225615783033603165251855960); + */ + +typedef struct ge25519_t { + bignum25519 x, y, z, t; +} ge25519; + +typedef struct ge25519_p1p1_t { + bignum25519 x, y, z, t; +} ge25519_p1p1; + +typedef struct ge25519_niels_t { + bignum25519 ysubx, xaddy, t2d; +} ge25519_niels; + +typedef struct ge25519_pniels_t { + bignum25519 ysubx, xaddy, z, t2d; +} ge25519_pniels; + +#include "ed25519_donna_basepoint_table.h" + +#include "ed25519_donna_32bit_tables.h" + +#include "ed25519_donna_impl_base.h" + +#endif diff --git a/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_donna_32bit_tables.c b/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_donna_32bit_tables.c new file mode 100644 index 0000000000..4b932dd0fe --- /dev/null +++ b/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_donna_32bit_tables.c @@ -0,0 +1,1049 @@ +#include "ed25519_donna.h" + +const ge25519 ALIGN(16) ge25519_basepoint = { + {0x0325d51a, + 0x018b5823, + 0x00f6592a, + 0x0104a92d, + 0x01a4b31d, + 0x01d6dc5c, + 0x027118fe, + 0x007fd814, + 0x013cd6e5, + 0x0085a4db}, + {0x02666658, + 0x01999999, + 0x00cccccc, + 0x01333333, + 0x01999999, + 0x00666666, + 0x03333333, + 0x00cccccc, + 0x02666666, + 0x01999999}, + {0x00000001, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000}, + {0x01b7dda3, + 0x01a2ace9, + 0x025eadbb, + 0x0003ba8a, + 0x0083c27e, + 0x00abe37d, + 0x01274732, + 0x00ccacdd, + 0x00fd78b7, + 0x019e1d7c}}; + +/* + d +*/ + +const bignum25519 ALIGN(16) ge25519_ecd = { + 0x035978a3, + 0x00d37284, + 0x03156ebd, + 0x006a0a0e, + 0x0001c029, + 0x0179e898, + 0x03a03cbb, + 0x01ce7198, + 0x02e2b6ff, + 0x01480db3}; + +const bignum25519 ALIGN(16) ge25519_ec2d = { + 0x02b2f159, + 0x01a6e509, + 0x022add7a, + 0x00d4141d, + 0x00038052, + 0x00f3d130, + 0x03407977, + 0x019ce331, + 0x01c56dff, + 0x00901b67}; + +/* + sqrt(-1) +*/ + +const bignum25519 ALIGN(16) ge25519_sqrtneg1 = { + 0x020ea0b0, + 0x0186c9d2, + 0x008f189d, + 0x0035697f, + 0x00bd0c60, + 0x01fbd7a7, + 0x02804c9e, + 0x01e16569, + 0x0004fc1d, + 0x00ae0c92}; + +const ge25519_niels ALIGN(16) ge25519_niels_sliding_multiples[32] = { + {{0x0340913e, + 0x000e4175, + 0x03d673a2, + 0x002e8a05, + 0x03f4e67c, + 0x008f8a09, + 0x00c21a34, + 0x004cf4b8, + 0x01298f81, + 0x0113f4be}, + {0x018c3b85, + 0x0124f1bd, + 0x01c325f7, + 0x0037dc60, + 0x033e4cb7, + 0x003d42c2, + 0x01a44c32, + 0x014ca4e1, + 0x03a33d4b, + 0x001f3e74}, + {0x037aaa68, + 0x00448161, + 0x0093d579, + 0x011e6556, + 0x009b67a0, + 0x0143598c, + 0x01bee5ee, + 0x00b50b43, + 0x0289f0c6, + 0x01bc45ed}}, + {{0x00fcd265, + 0x0047fa29, + 0x034faacc, + 0x01ef2e0d, + 0x00ef4d4f, + 0x014bd6bd, + 0x00f98d10, + 0x014c5026, + 0x007555bd, + 0x00aae456}, + {0x00ee9730, + 0x016c2a13, + 0x017155e4, + 0x01874432, + 0x00096a10, + 0x01016732, + 0x01a8014f, + 0x011e9823, + 0x01b9a80f, + 0x01e85938}, + {0x01d0d889, + 0x01a4cfc3, + 0x034c4295, + 0x0110e1ae, + 0x0162508c, + 0x00f2db4c, + 0x0072a2c6, + 0x0098da2e, + 0x02f12b9b, + 0x0168a09a}}, + {{0x0047d6ba, + 0x0060b0e9, + 0x0136eff2, + 0x008a5939, + 0x03540053, + 0x0064a087, + 0x02788e5c, + 0x00be7c67, + 0x033eb1b5, + 0x005529f9}, + {0x00a5bb33, + 0x00af1102, + 0x01a05442, + 0x001e3af7, + 0x02354123, + 0x00bfec44, + 0x01f5862d, + 0x00dd7ba3, + 0x03146e20, + 0x00a51733}, + {0x012a8285, + 0x00f6fc60, + 0x023f9797, + 0x003e85ee, + 0x009c3820, + 0x01bda72d, + 0x01b3858d, + 0x00d35683, + 0x0296b3bb, + 0x010eaaf9}}, + {{0x023221b1, + 0x01cb26aa, + 0x0074f74d, + 0x0099ddd1, + 0x01b28085, + 0x00192c3a, + 0x013b27c9, + 0x00fc13bd, + 0x01d2e531, + 0x0075bb75}, + {0x004ea3bf, + 0x00973425, + 0x001a4d63, + 0x01d59cee, + 0x01d1c0d4, + 0x00542e49, + 0x01294114, + 0x004fce36, + 0x029283c9, + 0x01186fa9}, + {0x01b8b3a2, + 0x00db7200, + 0x00935e30, + 0x003829f5, + 0x02cc0d7d, + 0x0077adf3, + 0x0220dd2c, + 0x0014ea53, + 0x01c6a0f9, + 0x01ea7eec}}, + {{0x039d8064, + 0x01885f80, + 0x00337e6d, + 0x01b7a902, + 0x02628206, + 0x015eb044, + 0x01e30473, + 0x0191f2d9, + 0x011fadc9, + 0x01270169}, + {0x02a8632f, + 0x0199e2a9, + 0x00d8b365, + 0x017a8de2, + 0x02994279, + 0x0086f5b5, + 0x0119e4e3, + 0x01eb39d6, + 0x0338add7, + 0x00d2e7b4}, + {0x0045af1b, + 0x013a2fe4, + 0x0245e0d6, + 0x014538ce, + 0x038bfe0f, + 0x01d4cf16, + 0x037e14c9, + 0x0160d55e, + 0x0021b008, + 0x01cf05c8}}, + {{0x01864348, + 0x01d6c092, + 0x0070262b, + 0x014bb844, + 0x00fb5acd, + 0x008deb95, + 0x003aaab5, + 0x00eff474, + 0x00029d5c, + 0x0062ad66}, + {0x02802ade, + 0x01c02122, + 0x01c4e5f7, + 0x00781181, + 0x039767fb, + 0x01703406, + 0x0342388b, + 0x01f5e227, + 0x022546d8, + 0x0109d6ab}, + {0x016089e9, + 0x00cb317f, + 0x00949b05, + 0x01099417, + 0x000c7ad2, + 0x011a8622, + 0x0088ccda, + 0x01290886, + 0x022b53df, + 0x00f71954}}, + {{0x027fbf93, + 0x01c04ecc, + 0x01ed6a0d, + 0x004cdbbb, + 0x02bbf3af, + 0x00ad5968, + 0x01591955, + 0x0094f3a2, + 0x02d17602, + 0x00099e20}, + {0x02007f6d, + 0x003088a8, + 0x03db77ee, + 0x00d5ade6, + 0x02fe12ce, + 0x0107ba07, + 0x0107097d, + 0x00482a6f, + 0x02ec346f, + 0x008d3f5f}, + {0x032ea378, + 0x0028465c, + 0x028e2a6c, + 0x018efc6e, + 0x0090df9a, + 0x01a7e533, + 0x039bfc48, + 0x010c745d, + 0x03daa097, + 0x0125ee9b}}, + {{0x028ccf0b, + 0x00f36191, + 0x021ac081, + 0x012154c8, + 0x034e0a6e, + 0x01b25192, + 0x00180403, + 0x01d7eea1, + 0x00218d05, + 0x010ed735}, + {0x03cfeaa0, + 0x01b300c4, + 0x008da499, + 0x0068c4e1, + 0x0219230a, + 0x01f2d4d0, + 0x02defd60, + 0x00e565b7, + 0x017f12de, + 0x018788a4}, + {0x03d0b516, + 0x009d8be6, + 0x03ddcbb3, + 0x0071b9fe, + 0x03ace2bd, + 0x01d64270, + 0x032d3ec9, + 0x01084065, + 0x0210ae4d, + 0x01447584}}, + {{0x0020de87, + 0x00e19211, + 0x01b68102, + 0x00b5ac97, + 0x022873c0, + 0x01942d25, + 0x01271394, + 0x0102073f, + 0x02fe2482, + 0x01c69ff9}, + {0x010e9d81, + 0x019dbbe5, + 0x0089f258, + 0x006e06b8, + 0x02951883, + 0x018f1248, + 0x019b3237, + 0x00bc7553, + 0x024ddb85, + 0x01b4c964}, + {0x01c8c854, + 0x0060ae29, + 0x01406d8e, + 0x01cff2f9, + 0x00cff451, + 0x01778d0c, + 0x03ac8c41, + 0x01552e59, + 0x036559ee, + 0x011d1b12}}, + {{0x00741147, + 0x0151b219, + 0x01092690, + 0x00e877e6, + 0x01f4d6bb, + 0x0072a332, + 0x01cd3b03, + 0x00dadff2, + 0x0097db5e, + 0x0086598d}, + {0x01c69a2b, + 0x01decf1b, + 0x02c2fa6e, + 0x013b7c4f, + 0x037beac8, + 0x013a16b5, + 0x028e7bda, + 0x01f6e8ac, + 0x01e34fe9, + 0x01726947}, + {0x01f10e67, + 0x003c73de, + 0x022b7ea2, + 0x010f32c2, + 0x03ff776a, + 0x00142277, + 0x01d38b88, + 0x00776138, + 0x03c60822, + 0x01201140}}, + {{0x0236d175, + 0x0008748e, + 0x03c6476d, + 0x013f4cdc, + 0x02eed02a, + 0x00838a47, + 0x032e7210, + 0x018bcbb3, + 0x00858de4, + 0x01dc7826}, + {0x00a37fc7, + 0x0127b40b, + 0x01957884, + 0x011d30ad, + 0x02816683, + 0x016e0e23, + 0x00b76be4, + 0x012db115, + 0x02516506, + 0x0154ce62}, + {0x00451edf, + 0x00bd749e, + 0x03997342, + 0x01cc2c4c, + 0x00eb6975, + 0x01a59508, + 0x03a516cf, + 0x00c228ef, + 0x0168ff5a, + 0x01697b47}}, + {{0x00527359, + 0x01783156, + 0x03afd75c, + 0x00ce56dc, + 0x00e4b970, + 0x001cabe9, + 0x029e0f6d, + 0x0188850c, + 0x0135fefd, + 0x00066d80}, + {0x02150e83, + 0x01448abf, + 0x02bb0232, + 0x012bf259, + 0x033c8268, + 0x00711e20, + 0x03fc148f, + 0x005e0e70, + 0x017d8bf9, + 0x0112b2e2}, + {0x02134b83, + 0x001a0517, + 0x0182c3cc, + 0x00792182, + 0x0313d799, + 0x001a3ed7, + 0x0344547e, + 0x01f24a0d, + 0x03de6ad2, + 0x00543127}}, + {{0x00dca868, + 0x00618f27, + 0x015a1709, + 0x00ddc38a, + 0x0320fd13, + 0x0036168d, + 0x0371ab06, + 0x01783fc7, + 0x0391e05f, + 0x01e29b5d}, + {0x01471138, + 0x00fca542, + 0x00ca31cf, + 0x01ca7bad, + 0x0175bfbc, + 0x01a708ad, + 0x03bce212, + 0x01244215, + 0x0075bb99, + 0x01acad68}, + {0x03a0b976, + 0x01dc12d1, + 0x011aab17, + 0x00aba0ba, + 0x029806cd, + 0x0142f590, + 0x018fd8ea, + 0x01a01545, + 0x03c4ad55, + 0x01c971ff}}, + {{0x00d098c0, + 0x000afdc7, + 0x006cd230, + 0x01276af3, + 0x03f905b2, + 0x0102994c, + 0x002eb8a4, + 0x015cfbeb, + 0x025f855f, + 0x01335518}, + {0x01cf99b2, + 0x0099c574, + 0x01a69c88, + 0x00881510, + 0x01cd4b54, + 0x0112109f, + 0x008abdc5, + 0x0074647a, + 0x0277cb1f, + 0x01e53324}, + {0x02ac5053, + 0x01b109b0, + 0x024b095e, + 0x016997b3, + 0x02f26bb6, + 0x00311021, + 0x00197885, + 0x01d0a55a, + 0x03b6fcc8, + 0x01c020d5}}, + {{0x02584a34, + 0x00e7eee0, + 0x03257a03, + 0x011e95a3, + 0x011ead91, + 0x00536202, + 0x00b1ce24, + 0x008516c6, + 0x03669d6d, + 0x004ea4a8}, + {0x00773f01, + 0x0019c9ce, + 0x019f6171, + 0x01d4afde, + 0x02e33323, + 0x01ad29b6, + 0x02ead1dc, + 0x01ed51a5, + 0x01851ad0, + 0x001bbdfa}, + {0x00577de5, + 0x00ddc730, + 0x038b9952, + 0x00f281ae, + 0x01d50390, + 0x0002e071, + 0x000780ec, + 0x010d448d, + 0x01f8a2af, + 0x00f0a5b7}}, + {{0x031f2541, + 0x00d34bae, + 0x0323ff9d, + 0x003a056d, + 0x02e25443, + 0x00a1ad05, + 0x00d1bee8, + 0x002f7f8e, + 0x03007477, + 0x002a24b1}, + {0x0114a713, + 0x01457e76, + 0x032255d5, + 0x01cc647f, + 0x02a4bdef, + 0x0153d730, + 0x00118bcf, + 0x00f755ff, + 0x013490c7, + 0x01ea674e}, + {0x02bda3e8, + 0x00bb490d, + 0x00f291ea, + 0x000abf40, + 0x01dea321, + 0x002f9ce0, + 0x00b2b193, + 0x00fa54b5, + 0x0128302f, + 0x00a19d8b}}, + {{0x022ef5bd, + 0x01638af3, + 0x038c6f8a, + 0x01a33a3d, + 0x039261b2, + 0x01bb89b8, + 0x010bcf9d, + 0x00cf42a9, + 0x023d6f17, + 0x01da1bca}, + {0x00e35b25, + 0x000d824f, + 0x0152e9cf, + 0x00ed935d, + 0x020b8460, + 0x01c7b83f, + 0x00c969e5, + 0x01a74198, + 0x0046a9d9, + 0x00cbc768}, + {0x01597c6a, + 0x0144a99b, + 0x00a57551, + 0x0018269c, + 0x023c464c, + 0x0009b022, + 0x00ee39e1, + 0x0114c7f2, + 0x038a9ad2, + 0x01584c17}}, + {{0x03b0c0d5, + 0x00b30a39, + 0x038a6ce4, + 0x01ded83a, + 0x01c277a6, + 0x01010a61, + 0x0346d3eb, + 0x018d995e, + 0x02f2c57c, + 0x000c286b}, + {0x0092aed1, + 0x0125e37b, + 0x027ca201, + 0x001a6b6b, + 0x03290f55, + 0x0047ba48, + 0x018d916c, + 0x01a59062, + 0x013e35d4, + 0x0002abb1}, + {0x003ad2aa, + 0x007ddcc0, + 0x00c10f76, + 0x0001590b, + 0x002cfca6, + 0x000ed23e, + 0x00ee4329, + 0x00900f04, + 0x01c24065, + 0x0082fa70}}, + {{0x02025e60, + 0x003912b8, + 0x0327041c, + 0x017e5ee5, + 0x02c0ecec, + 0x015a0d1c, + 0x02b1ce7c, + 0x0062220b, + 0x0145067e, + 0x01a5d931}, + {0x009673a6, + 0x00e1f609, + 0x00927c2a, + 0x016faa37, + 0x01650ef0, + 0x016f63b5, + 0x03cd40e1, + 0x003bc38f, + 0x0361f0ac, + 0x01d42acc}, + {0x02f81037, + 0x008ca0e8, + 0x017e23d1, + 0x011debfe, + 0x01bcbb68, + 0x002e2563, + 0x03e8add6, + 0x000816e5, + 0x03fb7075, + 0x0153e5ac}}, + {{0x02b11ecd, + 0x016bf185, + 0x008f22ef, + 0x00e7d2bb, + 0x0225d92e, + 0x00ece785, + 0x00508873, + 0x017e16f5, + 0x01fbe85d, + 0x01e39a0e}, + {0x01669279, + 0x017c810a, + 0x024941f5, + 0x0023ebeb, + 0x00eb7688, + 0x005760f1, + 0x02ca4146, + 0x0073cde7, + 0x0052bb75, + 0x00f5ffa7}, + {0x03b8856b, + 0x00cb7dcd, + 0x02f14e06, + 0x001820d0, + 0x01d74175, + 0x00e59e22, + 0x03fba550, + 0x00484641, + 0x03350088, + 0x01c3c9a3}}, + {{0x00dcf355, + 0x0104481c, + 0x0022e464, + 0x01f73fe7, + 0x00e03325, + 0x0152b698, + 0x02ef769a, + 0x00973663, + 0x00039b8c, + 0x0101395b}, + {0x01805f47, + 0x019160ec, + 0x03832cd0, + 0x008b06eb, + 0x03d4d717, + 0x004cb006, + 0x03a75b8f, + 0x013b3d30, + 0x01cfad88, + 0x01f034d1}, + {0x0078338a, + 0x01c7d2e3, + 0x02bc2b23, + 0x018b3f05, + 0x0280d9aa, + 0x005f3d44, + 0x0220a95a, + 0x00eeeb97, + 0x0362aaec, + 0x00835d51}}, + {{0x01b9f543, + 0x013fac4d, + 0x02ad93ae, + 0x018ef464, + 0x0212cdf7, + 0x01138ba9, + 0x011583ab, + 0x019c3d26, + 0x028790b4, + 0x00e2e2b6}, + {0x033bb758, + 0x01f0dbf1, + 0x03734bd1, + 0x0129b1e5, + 0x02b3950e, + 0x003bc922, + 0x01a53ec8, + 0x018c5532, + 0x006f3cee, + 0x00ae3c79}, + {0x0351f95d, + 0x0012a737, + 0x03d596b8, + 0x017658fe, + 0x00ace54a, + 0x008b66da, + 0x0036c599, + 0x012a63a2, + 0x032ceba1, + 0x00126bac}}, + {{0x03dcfe7e, + 0x019f4f18, + 0x01c81aee, + 0x0044bc2b, + 0x00827165, + 0x014f7c13, + 0x03b430f0, + 0x00bf96cc, + 0x020c8d62, + 0x01471997}, + {0x01fc7931, + 0x001f42dd, + 0x00ba754a, + 0x005bd339, + 0x003fbe49, + 0x016b3930, + 0x012a159c, + 0x009f83b0, + 0x03530f67, + 0x01e57b85}, + {0x02ecbd81, + 0x0096c294, + 0x01fce4a9, + 0x017701a5, + 0x0175047d, + 0x00ee4a31, + 0x012686e5, + 0x008efcd4, + 0x0349dc54, + 0x01b3466f}}, + {{0x02179ca3, + 0x01d86414, + 0x03f0afd0, + 0x00305964, + 0x015c7428, + 0x0099711e, + 0x015d5442, + 0x00c71014, + 0x01b40b2e, + 0x01d483cf}, + {0x01afc386, + 0x01984859, + 0x036203ff, + 0x0045c6a8, + 0x0020a8aa, + 0x00990baa, + 0x03313f10, + 0x007ceede, + 0x027429e4, + 0x017806ce}, + {0x039357a1, + 0x0142f8f4, + 0x0294a7b6, + 0x00eaccf4, + 0x0259edb3, + 0x01311e6e, + 0x004d326f, + 0x0130c346, + 0x01ccef3c, + 0x01c424b2}}, + {{0x0364918c, + 0x00148fc0, + 0x01638a7b, + 0x01a1fd5b, + 0x028ad013, + 0x0081e5a4, + 0x01a54f33, + 0x0174e101, + 0x003d0257, + 0x003a856c}, + {0x00051dcf, + 0x00f62b1d, + 0x0143d0ad, + 0x0042adbd, + 0x000fda90, + 0x01743ceb, + 0x0173e5e4, + 0x017bc749, + 0x03b7137a, + 0x0105ce96}, + {0x00f9218a, + 0x015b8c7c, + 0x00e102f8, + 0x0158d7e2, + 0x0169a5b8, + 0x00b2f176, + 0x018b347a, + 0x014cfef2, + 0x0214a4e3, + 0x017f1595}}, + {{0x006d7ae5, + 0x0195c371, + 0x0391e26d, + 0x0062a7c6, + 0x003f42ab, + 0x010dad86, + 0x024f8198, + 0x01542b2a, + 0x0014c454, + 0x0189c471}, + {0x0390988e, + 0x00b8799d, + 0x02e44912, + 0x0078e2e6, + 0x00075654, + 0x01923eed, + 0x0040cd72, + 0x00a37c76, + 0x0009d466, + 0x00c8531d}, + {0x02651770, + 0x00609d01, + 0x0286c265, + 0x0134513c, + 0x00ee9281, + 0x005d223c, + 0x035c760c, + 0x00679b36, + 0x0073ecb8, + 0x016faa50}}, + {{0x02c89be4, + 0x016fc244, + 0x02f38c83, + 0x018beb72, + 0x02b3ce2c, + 0x0097b065, + 0x034f017b, + 0x01dd957f, + 0x00148f61, + 0x00eab357}, + {0x0343d2f8, + 0x003398fc, + 0x011e368e, + 0x00782a1f, + 0x00019eea, + 0x00117b6f, + 0x0128d0d1, + 0x01a5e6bb, + 0x01944f1b, + 0x012b41e1}, + {0x03318301, + 0x018ecd30, + 0x0104d0b1, + 0x0038398b, + 0x03726701, + 0x019da88c, + 0x002d9769, + 0x00a7a681, + 0x031d9028, + 0x00ebfc32}}, + {{0x0220405e, + 0x0171face, + 0x02d930f8, + 0x017f6d6a, + 0x023b8c47, + 0x0129d5f9, + 0x02972456, + 0x00a3a524, + 0x006f4cd2, + 0x004439fa}, + {0x00c53505, + 0x0190c2fd, + 0x00507244, + 0x009930f9, + 0x01a39270, + 0x01d327c6, + 0x0399bc47, + 0x01cfe13d, + 0x0332bd99, + 0x00b33e7d}, + {0x0203f5e4, + 0x003627b5, + 0x00018af8, + 0x01478581, + 0x004a2218, + 0x002e3bb7, + 0x039384d0, + 0x0146ea62, + 0x020b9693, + 0x0017155f}}, + {{0x03c97e6f, + 0x00738c47, + 0x03b5db1f, + 0x01808fcf, + 0x01e8fc98, + 0x01ed25dd, + 0x01bf5045, + 0x00eb5c2b, + 0x0178fe98, + 0x01b85530}, + {0x01c20eb0, + 0x01aeec22, + 0x030b9eee, + 0x01b7d07e, + 0x0187e16f, + 0x014421fb, + 0x009fa731, + 0x0040b6d7, + 0x00841861, + 0x00a27fbc}, + {0x02d69abf, + 0x0058cdbf, + 0x0129f9ec, + 0x013c19ae, + 0x026c5b93, + 0x013a7fe7, + 0x004bb2ba, + 0x0063226f, + 0x002a95ca, + 0x01abefd9}}, + {{0x02f5d2c1, + 0x00378318, + 0x03734fb5, + 0x01258073, + 0x0263f0f6, + 0x01ad70e0, + 0x01b56d06, + 0x01188fbd, + 0x011b9503, + 0x0036d2e1}, + {0x0113a8cc, + 0x01541c3e, + 0x02ac2bbc, + 0x01d95867, + 0x01f47459, + 0x00ead489, + 0x00ab5b48, + 0x01db3b45, + 0x00edb801, + 0x004b024f}, + {0x00b8190f, + 0x011fe4c2, + 0x00621f82, + 0x010508d7, + 0x001a5a76, + 0x00c7d7fd, + 0x03aab96d, + 0x019cd9dc, + 0x019c6635, + 0x00ceaa1e}}, + {{0x01085cf2, + 0x01fd47af, + 0x03e3f5e1, + 0x004b3e99, + 0x01e3d46a, + 0x0060033c, + 0x015ff0a8, + 0x0150cdd8, + 0x029e8e21, + 0x008cf1bc}, + {0x00156cb1, + 0x003d623f, + 0x01a4f069, + 0x00d8d053, + 0x01b68aea, + 0x01ca5ab6, + 0x0316ae43, + 0x0134dc44, + 0x001c8d58, + 0x0084b343}, + {0x0318c781, + 0x0135441f, + 0x03a51a5e, + 0x019293f4, + 0x0048bb37, + 0x013d3341, + 0x0143151e, + 0x019c74e1, + 0x00911914, + 0x0076ddde}}, + {{0x006bc26f, + 0x00d48e5f, + 0x00227bbe, + 0x00629ea8, + 0x01ea5f8b, + 0x0179a330, + 0x027a1d5f, + 0x01bf8f8e, + 0x02d26e2a, + 0x00c6b65e}, + {0x01701ab6, + 0x0051da77, + 0x01b4b667, + 0x00a0ce7c, + 0x038ae37b, + 0x012ac852, + 0x03a0b0fe, + 0x0097c2bb, + 0x00a017d2, + 0x01eb8b2a}, + {0x0120b962, + 0x0005fb42, + 0x0353b6fd, + 0x0061f8ce, + 0x007a1463, + 0x01560a64, + 0x00e0a792, + 0x01907c92, + 0x013a6622, + 0x007b47f1}}}; diff --git a/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_donna_32bit_tables.h b/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_donna_32bit_tables.h new file mode 100644 index 0000000000..f76a832cfc --- /dev/null +++ b/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_donna_32bit_tables.h @@ -0,0 +1,17 @@ +extern const ge25519 ALIGN(16) ge25519_basepoint; + +/* + d +*/ + +extern const bignum25519 ALIGN(16) ge25519_ecd; + +extern const bignum25519 ALIGN(16) ge25519_ec2d; + +/* + sqrt(-1) +*/ + +extern const bignum25519 ALIGN(16) ge25519_sqrtneg1; + +extern const ge25519_niels ALIGN(16) ge25519_niels_sliding_multiples[32]; diff --git a/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_donna_basepoint_table.c b/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_donna_basepoint_table.c new file mode 100644 index 0000000000..29d8146758 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_donna_basepoint_table.c @@ -0,0 +1,1796 @@ +#include "ed25519_donna.h" + +/* multiples of the base point in packed {ysubx, xaddy, t2d} form */ +const uint8_t ALIGN(16) ge25519_niels_base_multiples[256][96] = { + {0x3e, 0x91, 0x40, 0xd7, 0x05, 0x39, 0x10, 0x9d, 0xb3, 0xbe, 0x40, 0xd1, 0x05, 0x9f, + 0x39, 0xfd, 0x09, 0x8a, 0x8f, 0x68, 0x34, 0x84, 0xc1, 0xa5, 0x67, 0x12, 0xf8, 0x98, + 0x92, 0x2f, 0xfd, 0x44, 0x85, 0x3b, 0x8c, 0xf5, 0xc6, 0x93, 0xbc, 0x2f, 0x19, 0x0e, + 0x8c, 0xfb, 0xc6, 0x2d, 0x93, 0xcf, 0xc2, 0x42, 0x3d, 0x64, 0x98, 0x48, 0x0b, 0x27, + 0x65, 0xba, 0xd4, 0x33, 0x3a, 0x9d, 0xcf, 0x07, 0x59, 0xbb, 0x6f, 0x4b, 0x67, 0x15, + 0xbd, 0xdb, 0xea, 0xa5, 0xa2, 0xee, 0x00, 0x3f, 0xe1, 0x41, 0xfa, 0xc6, 0x57, 0xc9, + 0x1c, 0x9d, 0xd4, 0xcd, 0xca, 0xec, 0x16, 0xaf, 0x1f, 0xbe, 0x0e, 0x4f}, + {0xa8, 0xd5, 0xb4, 0x42, 0x60, 0xa5, 0x99, 0x8a, 0xf6, 0xac, 0x60, 0x4e, 0x0c, 0x81, + 0x2b, 0x8f, 0xaa, 0x37, 0x6e, 0xb1, 0x6b, 0x23, 0x9e, 0xe0, 0x55, 0x25, 0xc9, 0x69, + 0xa6, 0x95, 0xb5, 0x6b, 0xd7, 0x71, 0x3c, 0x93, 0xfc, 0xe7, 0x24, 0x92, 0xb5, 0xf5, + 0x0f, 0x7a, 0x96, 0x9d, 0x46, 0x9f, 0x02, 0x07, 0xd6, 0xe1, 0x65, 0x9a, 0xa6, 0x5a, + 0x2e, 0x2e, 0x7d, 0xa8, 0x3f, 0x06, 0x0c, 0x59, 0x02, 0x68, 0xd3, 0xda, 0xaa, 0x7e, + 0x34, 0x6e, 0x05, 0x48, 0xee, 0x83, 0x93, 0x59, 0xf3, 0xba, 0x26, 0x68, 0x07, 0xe6, + 0x10, 0xbe, 0xca, 0x3b, 0xb8, 0xd1, 0x5e, 0x16, 0x0a, 0x4f, 0x31, 0x49}, + {0x65, 0xd2, 0xfc, 0xa4, 0xe8, 0x1f, 0x61, 0x56, 0x7d, 0xba, 0xc1, 0xe5, 0xfd, 0x53, + 0xd3, 0x3b, 0xbd, 0xd6, 0x4b, 0x21, 0x1a, 0xf3, 0x31, 0x81, 0x62, 0xda, 0x5b, 0x55, + 0x87, 0x15, 0xb9, 0x2a, 0x30, 0x97, 0xee, 0x4c, 0xa8, 0xb0, 0x25, 0xaf, 0x8a, 0x4b, + 0x86, 0xe8, 0x30, 0x84, 0x5a, 0x02, 0x32, 0x67, 0x01, 0x9f, 0x02, 0x50, 0x1b, 0xc1, + 0xf4, 0xf8, 0x80, 0x9a, 0x1b, 0x4e, 0x16, 0x7a, 0x34, 0x48, 0x67, 0xf1, 0xf4, 0x11, + 0xf2, 0x9b, 0x95, 0xf8, 0x2d, 0xf6, 0x17, 0x6b, 0x4e, 0xb8, 0x4e, 0x2a, 0x72, 0x5b, + 0x07, 0x6f, 0xde, 0xd7, 0x21, 0x2a, 0xbb, 0x63, 0xb9, 0x04, 0x9a, 0x54}, + {0xbf, 0x18, 0x68, 0x05, 0x0a, 0x05, 0xfe, 0x95, 0xa9, 0xfa, 0x60, 0x56, 0x71, 0x89, + 0x7e, 0x32, 0x73, 0x50, 0xa0, 0x06, 0xcd, 0xe3, 0xe8, 0xc3, 0x9a, 0xa4, 0x45, 0x74, + 0x4c, 0x3f, 0x93, 0x27, 0x9f, 0x09, 0xfc, 0x8e, 0xb9, 0x51, 0x73, 0x28, 0x38, 0x25, + 0xfd, 0x7d, 0xf4, 0xc6, 0x65, 0x67, 0x65, 0x92, 0x0a, 0xfb, 0x3d, 0x8d, 0x34, 0xca, + 0x27, 0x87, 0xe5, 0x21, 0x03, 0x91, 0x0e, 0x68, 0xb0, 0x26, 0x14, 0xe5, 0xec, 0x45, + 0x1e, 0xbf, 0x94, 0x0f, 0xba, 0x6d, 0x3d, 0xc6, 0x2b, 0xe3, 0xc0, 0x52, 0xf8, 0x8c, + 0xd5, 0x74, 0x29, 0xe4, 0x18, 0x4c, 0xe6, 0xb0, 0xb1, 0x79, 0xf0, 0x44}, + {0xba, 0xd6, 0x47, 0xa4, 0xc3, 0x82, 0x91, 0x7f, 0xb7, 0x29, 0x27, 0x4b, 0xd1, 0x14, + 0x00, 0xd5, 0x87, 0xa0, 0x64, 0xb8, 0x1c, 0xf1, 0x3c, 0xe3, 0xf3, 0x55, 0x1b, 0xeb, + 0x73, 0x7e, 0x4a, 0x15, 0x33, 0xbb, 0xa5, 0x08, 0x44, 0xbc, 0x12, 0xa2, 0x02, 0xed, + 0x5e, 0xc7, 0xc3, 0x48, 0x50, 0x8d, 0x44, 0xec, 0xbf, 0x5a, 0x0c, 0xeb, 0x1b, 0xdd, + 0xeb, 0x06, 0xe2, 0x46, 0xf1, 0xcc, 0x45, 0x29, 0xb3, 0x03, 0xd0, 0xe7, 0x79, 0xa1, + 0x32, 0xc8, 0x7e, 0x4d, 0x12, 0x00, 0x0a, 0x9d, 0x72, 0x5f, 0xf3, 0x8f, 0x6d, 0x0e, + 0xa1, 0xd4, 0xc1, 0x62, 0x98, 0x7a, 0xb2, 0x38, 0x59, 0xac, 0xb8, 0x68}, + {0xa4, 0x8c, 0x7d, 0x7b, 0xb6, 0x06, 0x98, 0x49, 0x39, 0x27, 0xd2, 0x27, 0x84, 0xe2, + 0x5b, 0x57, 0xb9, 0x53, 0x45, 0x20, 0xe7, 0x5c, 0x08, 0xbb, 0x84, 0x78, 0x41, 0xae, + 0x41, 0x4c, 0xb6, 0x38, 0x31, 0x71, 0x15, 0x77, 0xeb, 0xee, 0x0c, 0x3a, 0x88, 0xaf, + 0xc8, 0x00, 0x89, 0x15, 0x27, 0x9b, 0x36, 0xa7, 0x59, 0xda, 0x68, 0xb6, 0x65, 0x80, + 0xbd, 0x38, 0xcc, 0xa2, 0xb6, 0x7b, 0xe5, 0x51, 0xa4, 0xe3, 0x9d, 0x68, 0x91, 0xad, + 0x9d, 0x8f, 0x37, 0x91, 0xfb, 0xf8, 0x28, 0x24, 0x5f, 0x17, 0x88, 0xb9, 0xcf, 0x9f, + 0x32, 0xb5, 0x0a, 0x05, 0x9f, 0xc0, 0x54, 0x13, 0xa2, 0xdf, 0x65, 0x78}, + {0xb1, 0x21, 0x32, 0xaa, 0x9a, 0x2c, 0x6f, 0xba, 0xa7, 0x23, 0xba, 0x3b, 0x53, 0x21, + 0xa0, 0x6c, 0x3a, 0x2c, 0x19, 0x92, 0x4f, 0x76, 0xea, 0x9d, 0xe0, 0x17, 0x53, 0x2e, + 0x5d, 0xdd, 0x6e, 0x1d, 0xbf, 0xa3, 0x4e, 0x94, 0xd0, 0x5c, 0x1a, 0x6b, 0xd2, 0xc0, + 0x9d, 0xb3, 0x3a, 0x35, 0x70, 0x74, 0x49, 0x2e, 0x54, 0x28, 0x82, 0x52, 0xb2, 0x71, + 0x7e, 0x92, 0x3c, 0x28, 0x69, 0xea, 0x1b, 0x46, 0x36, 0xda, 0x0f, 0xab, 0xac, 0x8a, + 0x7a, 0x21, 0xc8, 0x49, 0x35, 0x3d, 0x54, 0xc6, 0x28, 0xa5, 0x68, 0x75, 0xab, 0x13, + 0x8b, 0x5b, 0xd0, 0x37, 0x37, 0xbc, 0x2c, 0x3a, 0x62, 0xef, 0x3c, 0x23}, + {0xd9, 0x34, 0x92, 0xf3, 0xed, 0x5d, 0xa7, 0xe2, 0xf9, 0x58, 0xb5, 0xe1, 0x80, 0x76, + 0x3d, 0x96, 0xfb, 0x23, 0x3c, 0x6e, 0xac, 0x41, 0x27, 0x2c, 0xc3, 0x01, 0x0e, 0x32, + 0xa1, 0x24, 0x90, 0x3a, 0x8f, 0x3e, 0xdd, 0x04, 0x66, 0x59, 0xb7, 0x59, 0x2c, 0x70, + 0x88, 0xe2, 0x77, 0x03, 0xb3, 0x6c, 0x23, 0xc3, 0xd9, 0x5e, 0x66, 0x9c, 0x33, 0xb1, + 0x2f, 0xe5, 0xbc, 0x61, 0x60, 0xe7, 0x15, 0x09, 0x7e, 0xa3, 0x34, 0xa8, 0x35, 0xe8, + 0x7d, 0xdf, 0xea, 0x57, 0x98, 0x68, 0xda, 0x9c, 0xe1, 0x8b, 0x26, 0xb3, 0x67, 0x71, + 0x36, 0x85, 0x11, 0x2c, 0xc2, 0xd5, 0xef, 0xdb, 0xd9, 0xb3, 0x9e, 0x58}, + {0x5e, 0x51, 0xaa, 0x49, 0x54, 0x63, 0x5b, 0xed, 0x3a, 0x82, 0xc6, 0x0b, 0x9f, 0xc4, + 0x65, 0xa8, 0xc4, 0xd1, 0x42, 0x5b, 0xe9, 0x1f, 0x0c, 0x85, 0xb9, 0x15, 0xd3, 0x03, + 0x6f, 0x6d, 0xd7, 0x30, 0x1d, 0x9c, 0x2f, 0x63, 0x0e, 0xdd, 0xcc, 0x2e, 0x15, 0x31, + 0x89, 0x76, 0x96, 0xb6, 0xd0, 0x51, 0x58, 0x7a, 0x63, 0xa8, 0x6b, 0xb7, 0xdf, 0x52, + 0x39, 0xef, 0x0e, 0xa0, 0x49, 0x7d, 0xd3, 0x6d, 0xc7, 0xe4, 0x06, 0x21, 0x17, 0x44, + 0x44, 0x6c, 0x69, 0x7f, 0x8d, 0x92, 0x80, 0xd6, 0x53, 0xfb, 0x26, 0x3f, 0x4d, 0x69, + 0xa4, 0x9e, 0x73, 0xb4, 0xb0, 0x4b, 0x86, 0x2e, 0x11, 0x97, 0xc6, 0x10}, + {0xde, 0x5f, 0xbe, 0x7d, 0x27, 0xc4, 0x93, 0x64, 0xa2, 0x7e, 0xad, 0x19, 0xad, 0x4f, + 0x5d, 0x26, 0x90, 0x45, 0x30, 0x46, 0xc8, 0xdf, 0x00, 0x0e, 0x09, 0xfe, 0x66, 0xed, + 0xab, 0x1c, 0xe6, 0x25, 0x05, 0xc8, 0x58, 0x83, 0xa0, 0x2a, 0xa6, 0x0c, 0x47, 0x42, + 0x20, 0x7a, 0xe3, 0x4a, 0x3d, 0x6a, 0xdc, 0xed, 0x11, 0x3b, 0xa6, 0xd3, 0x64, 0x74, + 0xef, 0x06, 0x08, 0x55, 0xaf, 0x9b, 0xbf, 0x03, 0x04, 0x66, 0x58, 0xcc, 0x28, 0xe1, + 0x13, 0x3f, 0x7e, 0x74, 0x59, 0xb4, 0xec, 0x73, 0x58, 0x6f, 0xf5, 0x68, 0x12, 0xcc, + 0xed, 0x3d, 0xb6, 0xa0, 0x2c, 0xe2, 0x86, 0x45, 0x63, 0x78, 0x6d, 0x56}, + {0x34, 0x08, 0xc1, 0x9c, 0x9f, 0xa4, 0x37, 0x16, 0x51, 0xc4, 0x9b, 0xa8, 0xd5, 0x56, + 0x8e, 0xbc, 0xdb, 0xd2, 0x7f, 0x7f, 0x0f, 0xec, 0xb5, 0x1c, 0xd9, 0x35, 0xcc, 0x5e, + 0xca, 0x5b, 0x97, 0x33, 0xd0, 0x2f, 0x5a, 0xc6, 0x85, 0x42, 0x05, 0xa1, 0xc3, 0x67, + 0x16, 0xf3, 0x2a, 0x11, 0x64, 0x6c, 0x58, 0xee, 0x1a, 0x73, 0x40, 0xe2, 0x0a, 0x68, + 0x2a, 0xb2, 0x93, 0x47, 0xf3, 0xa5, 0xfb, 0x14, 0xd4, 0xf7, 0x85, 0x69, 0x16, 0x46, + 0xd7, 0x3c, 0x57, 0x00, 0xc8, 0xc9, 0x84, 0x5e, 0x3e, 0x59, 0x1e, 0x13, 0x61, 0x7b, + 0xb6, 0xf2, 0xc3, 0x2f, 0x6c, 0x52, 0xfc, 0x83, 0xea, 0x9c, 0x82, 0x14}, + {0xc2, 0x95, 0xdd, 0x97, 0x84, 0x7b, 0x43, 0xff, 0xa7, 0xb5, 0x4e, 0xaa, 0x30, 0x4e, + 0x74, 0x6c, 0x8b, 0xe8, 0x85, 0x3c, 0x61, 0x5d, 0x0c, 0x9e, 0x73, 0x81, 0x75, 0x5f, + 0x1e, 0xc7, 0xd9, 0x2f, 0xb8, 0xec, 0x71, 0x4e, 0x2f, 0x0b, 0xe7, 0x21, 0xe3, 0x77, + 0xa4, 0x40, 0xb9, 0xdd, 0x56, 0xe6, 0x80, 0x4f, 0x1d, 0xce, 0xce, 0x56, 0x65, 0xbf, + 0x7e, 0x7b, 0x5d, 0x53, 0xc4, 0x3b, 0xfc, 0x05, 0xdd, 0xde, 0xaf, 0x52, 0xae, 0xb3, + 0xb8, 0x24, 0xcf, 0x30, 0x3b, 0xed, 0x8c, 0x63, 0x95, 0x34, 0x95, 0x81, 0xbe, 0xa9, + 0x83, 0xbc, 0xa4, 0x33, 0x04, 0x1f, 0x65, 0x5c, 0x47, 0x67, 0x37, 0x37}, + {0xd9, 0xad, 0xd1, 0x40, 0xfd, 0x99, 0xba, 0x2f, 0x27, 0xd0, 0xf4, 0x96, 0x6f, 0x16, + 0x07, 0xb3, 0xae, 0x3b, 0xf0, 0x15, 0x52, 0xf0, 0x63, 0x43, 0x99, 0xf9, 0x18, 0x3b, + 0x6c, 0xa5, 0xbe, 0x1f, 0x90, 0x65, 0x24, 0x14, 0xcb, 0x95, 0x40, 0x63, 0x35, 0x55, + 0xc1, 0x16, 0x40, 0x14, 0x12, 0xef, 0x60, 0xbc, 0x10, 0x89, 0x0c, 0x14, 0x38, 0x9e, + 0x8c, 0x7c, 0x90, 0x30, 0x57, 0x90, 0xf5, 0x6b, 0x8a, 0x5b, 0x41, 0xe1, 0xf1, 0x78, + 0xa7, 0x0f, 0x7e, 0xa7, 0xc3, 0xba, 0xf7, 0x9f, 0x40, 0x06, 0x50, 0x9a, 0xa2, 0x9a, + 0xb8, 0xd7, 0x52, 0x6f, 0x56, 0x5a, 0x63, 0x7a, 0xf6, 0x1c, 0x52, 0x02}, + {0x94, 0x52, 0x9d, 0x0a, 0x0b, 0xee, 0x3f, 0x51, 0x66, 0x5a, 0xdf, 0x0f, 0x5c, 0xe7, + 0x98, 0x8f, 0xce, 0x07, 0xe1, 0xbf, 0x88, 0x86, 0x61, 0xd4, 0xed, 0x2c, 0x38, 0x71, + 0x7e, 0x0a, 0xa0, 0x3f, 0xe4, 0x5e, 0x2f, 0x77, 0x20, 0x67, 0x14, 0xb1, 0xce, 0x9a, + 0x07, 0x96, 0xb1, 0x94, 0xf8, 0xe8, 0x4a, 0x82, 0xac, 0x00, 0x4d, 0x22, 0xf8, 0x4a, + 0xc4, 0x6c, 0xcd, 0xf7, 0xd9, 0x53, 0x17, 0x00, 0x34, 0xdb, 0x3d, 0x96, 0x2d, 0x23, + 0x69, 0x3c, 0x58, 0x38, 0x97, 0xb4, 0xda, 0x87, 0xde, 0x1d, 0x85, 0xf2, 0x91, 0xa0, + 0xf9, 0xd1, 0xd7, 0xaa, 0xb6, 0xed, 0x48, 0xa0, 0x2f, 0xfe, 0xb5, 0x12}, + {0x4d, 0xe3, 0xfc, 0x96, 0xc4, 0xfb, 0xf0, 0x71, 0xed, 0x5b, 0xf3, 0xad, 0x6b, 0x82, + 0xb9, 0x73, 0x61, 0xc5, 0x28, 0xff, 0x61, 0x72, 0x04, 0xd2, 0x6f, 0x20, 0xb1, 0x6f, + 0xf9, 0x76, 0x9b, 0x74, 0x92, 0x1e, 0x6f, 0xad, 0x26, 0x7c, 0x2b, 0xdf, 0x13, 0x89, + 0x4b, 0x50, 0x23, 0xd3, 0x66, 0x4b, 0xc3, 0x8b, 0x1c, 0x75, 0xc0, 0x9d, 0x40, 0x8c, + 0xb8, 0xc7, 0x96, 0x07, 0xc2, 0x93, 0x7e, 0x6f, 0x05, 0xae, 0xa6, 0xae, 0x04, 0xf6, + 0x5a, 0x1f, 0x99, 0x9c, 0xe4, 0xbe, 0xf1, 0x51, 0x23, 0xc1, 0x66, 0x6b, 0xff, 0xee, + 0xb5, 0x08, 0xa8, 0x61, 0x51, 0x21, 0xe0, 0x01, 0x0f, 0xc1, 0xce, 0x0f}, + {0x44, 0x1e, 0xfe, 0x49, 0xa6, 0x58, 0x4d, 0x64, 0x7e, 0x77, 0xad, 0x31, 0xa2, 0xae, + 0xfc, 0x21, 0xd2, 0xd0, 0x7f, 0x88, 0x5a, 0x1c, 0x44, 0x02, 0xf3, 0x11, 0xc5, 0x83, + 0x71, 0xaa, 0x01, 0x49, 0x45, 0x4e, 0x24, 0xc4, 0x9d, 0xd2, 0xf2, 0x3d, 0x0a, 0xde, + 0xd8, 0x93, 0x74, 0x0e, 0x02, 0x2b, 0x4d, 0x21, 0x0c, 0x82, 0x7e, 0x06, 0xc8, 0x6c, + 0x0a, 0xb9, 0xea, 0x6f, 0x16, 0x79, 0x37, 0x41, 0xf0, 0xf8, 0x1a, 0x8c, 0x54, 0xb7, + 0xb1, 0x08, 0xb4, 0x99, 0x62, 0x24, 0x7c, 0x7a, 0x0f, 0xce, 0x39, 0xd9, 0x06, 0x1e, + 0xf9, 0xb0, 0x60, 0xf7, 0x13, 0x12, 0x6d, 0x72, 0x7b, 0x88, 0xbb, 0x41}, + {0xbe, 0x46, 0x43, 0x74, 0x44, 0x7d, 0xe8, 0x40, 0x25, 0x2b, 0xb5, 0x15, 0xd4, 0xda, + 0x48, 0x1d, 0x3e, 0x60, 0x3b, 0xa1, 0x18, 0x8a, 0x3a, 0x7c, 0xf7, 0xbd, 0xcd, 0x2f, + 0xc1, 0x28, 0xb7, 0x4e, 0xae, 0x91, 0x66, 0x7c, 0x59, 0x4c, 0x23, 0x7e, 0xc8, 0xb4, + 0x85, 0x0a, 0x3d, 0x9d, 0x88, 0x64, 0xe7, 0xfa, 0x4a, 0x35, 0x0c, 0xc9, 0xe2, 0xda, + 0x1d, 0x9e, 0x6a, 0x0c, 0x07, 0x1e, 0x87, 0x0a, 0x89, 0x89, 0xbc, 0x4b, 0x99, 0xb5, + 0x01, 0x33, 0x60, 0x42, 0xdd, 0x5b, 0x3a, 0xae, 0x6b, 0x73, 0x3c, 0x9e, 0xd5, 0x19, + 0xe2, 0xad, 0x61, 0x0d, 0x64, 0xd4, 0x85, 0x26, 0x0f, 0x30, 0xe7, 0x3e}, + {0xb7, 0xd6, 0x7d, 0x9e, 0xe4, 0x55, 0xd2, 0xf5, 0xac, 0x1e, 0x0b, 0x61, 0x5c, 0x11, + 0x16, 0x80, 0xca, 0x87, 0xe1, 0x92, 0x5d, 0x97, 0x99, 0x3c, 0xc2, 0x25, 0x91, 0x97, + 0x62, 0x57, 0x81, 0x13, 0x18, 0x75, 0x1e, 0x84, 0x47, 0x79, 0xfa, 0x43, 0xd7, 0x46, + 0x9c, 0x63, 0x59, 0xfa, 0xc6, 0xe5, 0x74, 0x2b, 0x05, 0xe3, 0x1d, 0x5e, 0x06, 0xa1, + 0x30, 0x90, 0xb8, 0xcf, 0xa2, 0xc6, 0x47, 0x7d, 0xe0, 0xd6, 0xf0, 0x8e, 0x14, 0xd0, + 0xda, 0x3f, 0x3c, 0x6f, 0x54, 0x91, 0x9a, 0x74, 0x3e, 0x9d, 0x57, 0x81, 0xbb, 0x26, + 0x10, 0x62, 0xec, 0x71, 0x80, 0xec, 0xc9, 0x34, 0x8d, 0xf5, 0x8c, 0x14}, + {0x27, 0xf0, 0x34, 0x79, 0xf6, 0x92, 0xa4, 0x46, 0xa9, 0x0a, 0x84, 0xf6, 0xbe, 0x84, + 0x99, 0x46, 0x54, 0x18, 0x61, 0x89, 0x2a, 0xbc, 0xa1, 0x5c, 0xd4, 0xbb, 0x5d, 0xbd, + 0x1e, 0xfa, 0xf2, 0x3f, 0x6d, 0x75, 0xe4, 0x9a, 0x7d, 0x2f, 0x57, 0xe2, 0x7f, 0x48, + 0xf3, 0x88, 0xbb, 0x45, 0xc3, 0x56, 0x8d, 0xa8, 0x60, 0x69, 0x6d, 0x0b, 0xd1, 0x9f, + 0xb9, 0xa1, 0xae, 0x4e, 0xad, 0xeb, 0x8f, 0x27, 0x66, 0x39, 0x93, 0x8c, 0x1f, 0x68, + 0xaa, 0xb1, 0x98, 0x0c, 0x29, 0x20, 0x9c, 0x94, 0x21, 0x8c, 0x52, 0x3c, 0x9d, 0x21, + 0x91, 0x52, 0x11, 0x39, 0x7b, 0x67, 0x9c, 0xfe, 0x02, 0xdd, 0x04, 0x41}, + {0x2a, 0x42, 0x24, 0x11, 0x5e, 0xbf, 0xb2, 0x72, 0xb5, 0x3a, 0xa3, 0x98, 0x33, 0x0c, + 0xfa, 0xa1, 0x66, 0xb6, 0x52, 0xfa, 0x01, 0x61, 0xcb, 0x94, 0xd5, 0x53, 0xaf, 0xaf, + 0x00, 0x3b, 0x86, 0x2c, 0xb8, 0x6a, 0x09, 0xdb, 0x06, 0x4e, 0x21, 0x81, 0x35, 0x4f, + 0xe4, 0x0c, 0xc9, 0xb6, 0xa8, 0x21, 0xf5, 0x2a, 0x9e, 0x40, 0x2a, 0xc1, 0x24, 0x65, + 0x81, 0xa4, 0xfc, 0x8e, 0xa4, 0xb5, 0x65, 0x01, 0x76, 0x6a, 0x84, 0xa0, 0x74, 0xa4, + 0x90, 0xf1, 0xc0, 0x7c, 0x2f, 0xcd, 0x84, 0xf9, 0xef, 0x12, 0x8f, 0x2b, 0xaa, 0x58, + 0x06, 0x29, 0x5e, 0x69, 0xb8, 0xc8, 0xfe, 0xbf, 0xd9, 0x67, 0x1b, 0x59}, + {0xfa, 0x9b, 0xb4, 0x80, 0x1c, 0x0d, 0x2f, 0x31, 0x8a, 0xec, 0xf3, 0xab, 0x5e, 0x51, + 0x79, 0x59, 0x88, 0x1c, 0xf0, 0x9e, 0xc0, 0x33, 0x70, 0x72, 0xcb, 0x7b, 0x8f, 0xca, + 0xc7, 0x2e, 0xe0, 0x3d, 0x5d, 0xb5, 0x18, 0x9f, 0x71, 0xb3, 0xb9, 0x99, 0x1e, 0x64, + 0x8c, 0xa1, 0xfa, 0xe5, 0x65, 0xe4, 0xed, 0x05, 0x9f, 0xc2, 0x36, 0x11, 0x08, 0x61, + 0x8b, 0x12, 0x30, 0x70, 0x86, 0x4f, 0x9b, 0x48, 0xef, 0x92, 0xeb, 0x3a, 0x2d, 0x10, + 0x32, 0xd2, 0x61, 0xa8, 0x16, 0x61, 0xb4, 0x53, 0x62, 0xe1, 0x24, 0xaa, 0x0b, 0x19, + 0xe7, 0xab, 0x7e, 0x3d, 0xbf, 0xbe, 0x6c, 0x49, 0xba, 0xfb, 0xf5, 0x49}, + {0xd4, 0xcf, 0x5b, 0x8a, 0x10, 0x9a, 0x94, 0x30, 0xeb, 0x73, 0x64, 0xbc, 0x70, 0xdd, + 0x40, 0xdc, 0x1c, 0x0d, 0x7c, 0x30, 0xc1, 0x94, 0xc2, 0x92, 0x74, 0x6e, 0xfa, 0xcb, + 0x6d, 0xa8, 0x04, 0x56, 0x2e, 0x57, 0x9c, 0x1e, 0x8c, 0x62, 0x5d, 0x15, 0x41, 0x47, + 0x88, 0xc5, 0xac, 0x86, 0x4d, 0x8a, 0xeb, 0x63, 0x57, 0x51, 0xf6, 0x52, 0xa3, 0x91, + 0x5b, 0x51, 0x67, 0x88, 0xc2, 0xa6, 0xa1, 0x06, 0xb6, 0x64, 0x17, 0x7c, 0xd4, 0xd1, + 0x88, 0x72, 0x51, 0x8b, 0x41, 0xe0, 0x40, 0x11, 0x54, 0x72, 0xd1, 0xf6, 0xac, 0x18, + 0x60, 0x1a, 0x03, 0x9f, 0xc6, 0x42, 0x27, 0xfe, 0x89, 0x9e, 0x98, 0x20}, + {0x7f, 0xcc, 0x2d, 0x3a, 0xfd, 0x77, 0x97, 0x49, 0x92, 0xd8, 0x4f, 0xa5, 0x2c, 0x7c, + 0x85, 0x32, 0xa0, 0xe3, 0x07, 0xd2, 0x64, 0xd8, 0x79, 0xa2, 0x29, 0x7e, 0xa6, 0x0c, + 0x1d, 0xed, 0x03, 0x04, 0x2e, 0xec, 0xea, 0x85, 0x8b, 0x27, 0x74, 0x16, 0xdf, 0x2b, + 0xcb, 0x7a, 0x07, 0xdc, 0x21, 0x56, 0x5a, 0xf4, 0xcb, 0x61, 0x16, 0x4c, 0x0a, 0x64, + 0xd3, 0x95, 0x05, 0xf7, 0x50, 0x99, 0x0b, 0x73, 0x52, 0xc5, 0x4e, 0x87, 0x35, 0x2d, + 0x4b, 0xc9, 0x8d, 0x6f, 0x24, 0x98, 0xcf, 0xc8, 0xe6, 0xc5, 0xce, 0x35, 0xc0, 0x16, + 0xfa, 0x46, 0xcb, 0xf7, 0xcc, 0x3d, 0x30, 0x08, 0x43, 0x45, 0xd7, 0x5b}, + {0xc2, 0x4c, 0xb2, 0x28, 0x95, 0xd1, 0x9a, 0x7f, 0x81, 0xc1, 0x35, 0x63, 0x65, 0x54, + 0x6b, 0x7f, 0x36, 0x72, 0xc0, 0x4f, 0x6e, 0xb6, 0xb8, 0x66, 0x83, 0xad, 0x80, 0x73, + 0x00, 0x78, 0x3a, 0x13, 0x2a, 0x79, 0xe7, 0x15, 0x21, 0x93, 0xc4, 0x85, 0xc9, 0xdd, + 0xcd, 0xbd, 0xa2, 0x89, 0x4c, 0xc6, 0x62, 0xd7, 0xa3, 0xad, 0xa8, 0x3d, 0x1e, 0x9d, + 0x2c, 0xf8, 0x67, 0x30, 0x12, 0xdb, 0xb7, 0x5b, 0xbe, 0x62, 0xca, 0xc6, 0x67, 0xf4, + 0x61, 0x09, 0xee, 0x52, 0x19, 0x21, 0xd6, 0x21, 0xec, 0x04, 0x70, 0x47, 0xd5, 0x9b, + 0x77, 0x60, 0x23, 0x18, 0xd2, 0xe0, 0xf0, 0x58, 0x6d, 0xca, 0x0d, 0x74}, + {0x4e, 0xce, 0xcf, 0x52, 0x07, 0xee, 0x48, 0xdf, 0xb7, 0x08, 0xec, 0x06, 0xf3, 0xfa, + 0xff, 0xc3, 0xc4, 0x59, 0x54, 0xb9, 0x2a, 0x0b, 0x71, 0x05, 0x8d, 0xa3, 0x3e, 0x96, + 0xfa, 0x25, 0x1d, 0x16, 0x3c, 0x43, 0x78, 0x04, 0x57, 0x8c, 0x1a, 0x23, 0x9d, 0x43, + 0x81, 0xc2, 0x0e, 0x27, 0xb5, 0xb7, 0x9f, 0x07, 0xd9, 0xe3, 0xea, 0x99, 0xaa, 0xdb, + 0xd9, 0x03, 0x2b, 0x6c, 0x25, 0xf5, 0x03, 0x2c, 0x7d, 0xa4, 0x53, 0x7b, 0x75, 0x18, + 0x0f, 0x79, 0x79, 0x58, 0x0c, 0xcf, 0x30, 0x01, 0x7b, 0x30, 0xf9, 0xf7, 0x7e, 0x25, + 0x77, 0x3d, 0x90, 0x31, 0xaf, 0xbb, 0x96, 0xbd, 0xbd, 0x68, 0x94, 0x69}, + {0xcf, 0xfe, 0xda, 0xf4, 0x46, 0x2f, 0x1f, 0xbd, 0xf7, 0xd6, 0x7f, 0xa4, 0x14, 0x01, + 0xef, 0x7c, 0x7f, 0xb3, 0x47, 0x4a, 0xda, 0xfd, 0x1f, 0xd3, 0x85, 0x57, 0x90, 0x73, + 0xa4, 0x19, 0x52, 0x52, 0x48, 0x19, 0xa9, 0x6a, 0xe6, 0x3d, 0xdd, 0xd8, 0xcc, 0xd2, + 0xc0, 0x2f, 0xc2, 0x64, 0x50, 0x48, 0x2f, 0xea, 0xfd, 0x34, 0x66, 0x24, 0x48, 0x9b, + 0x3a, 0x2e, 0x4a, 0x6c, 0x4e, 0x1c, 0x3e, 0x29, 0xe1, 0x12, 0x51, 0x92, 0x4b, 0x13, + 0x6e, 0x37, 0xa0, 0x5d, 0xa1, 0xdc, 0xb5, 0x78, 0x37, 0x70, 0x11, 0x31, 0x1c, 0x46, + 0xaf, 0x89, 0x45, 0xb0, 0x23, 0x28, 0x03, 0x7f, 0x44, 0x5c, 0x60, 0x5b}, + {0x89, 0x7c, 0xc4, 0x20, 0x59, 0x80, 0x65, 0xb9, 0xcc, 0x8f, 0x3b, 0x92, 0x0c, 0x10, + 0xf0, 0xe7, 0x77, 0xef, 0xe2, 0x02, 0x65, 0x25, 0x01, 0x00, 0xee, 0xb3, 0xae, 0xa8, + 0xce, 0x6d, 0xa7, 0x24, 0x4c, 0xf0, 0xe7, 0xf0, 0xc6, 0xfe, 0xe9, 0x3b, 0x62, 0x49, + 0xe3, 0x75, 0x9e, 0x57, 0x6a, 0x86, 0x1a, 0xe6, 0x1d, 0x1e, 0x16, 0xef, 0x42, 0x55, + 0xd5, 0xbd, 0x5a, 0xcc, 0xf4, 0xfe, 0x12, 0x2f, 0x40, 0xc7, 0xc0, 0xdf, 0xb2, 0x22, + 0x45, 0x0a, 0x07, 0xa4, 0xc9, 0x40, 0x7f, 0x6e, 0xd0, 0x10, 0x68, 0xf6, 0xcf, 0x78, + 0x41, 0x14, 0xcf, 0xc6, 0x90, 0x37, 0xa4, 0x18, 0x25, 0x7b, 0x60, 0x5e}, + {0x18, 0x18, 0xdf, 0x6c, 0x8f, 0x1d, 0xb3, 0x58, 0xa2, 0x58, 0x62, 0xc3, 0x4f, 0xa7, + 0xcf, 0x35, 0x6e, 0x1d, 0xe6, 0x66, 0x4f, 0xff, 0xb3, 0xe1, 0xf7, 0xd5, 0xcd, 0x6c, + 0xab, 0xac, 0x67, 0x50, 0x14, 0xcf, 0x96, 0xa5, 0x1c, 0x43, 0x2c, 0xa0, 0x00, 0xe4, + 0xd3, 0xae, 0x40, 0x2d, 0xc4, 0xe3, 0xdb, 0x26, 0x0f, 0x2e, 0x80, 0x26, 0x45, 0xd2, + 0x68, 0x70, 0x45, 0x9e, 0x13, 0x33, 0x1f, 0x20, 0x51, 0x9d, 0x03, 0x08, 0x6b, 0x7f, + 0x52, 0xfd, 0x06, 0x00, 0x7c, 0x01, 0x64, 0x49, 0xb1, 0x18, 0xa8, 0xa4, 0x25, 0x2e, + 0xb0, 0x0e, 0x22, 0xd5, 0x75, 0x03, 0x46, 0x62, 0x88, 0xba, 0x7c, 0x39}, + {0xb2, 0x59, 0x59, 0xf0, 0x93, 0x30, 0xc1, 0x30, 0x76, 0x79, 0xa9, 0xe9, 0x8d, 0xa1, + 0x3a, 0xe2, 0x26, 0x5e, 0x1d, 0x72, 0x91, 0xd4, 0x2f, 0x22, 0x3a, 0x6c, 0x6e, 0x76, + 0x20, 0xd3, 0x39, 0x23, 0xe7, 0x79, 0x13, 0xc8, 0xfb, 0xc3, 0x15, 0x78, 0xf1, 0x2a, + 0xe1, 0xdd, 0x20, 0x94, 0x61, 0xa6, 0xd5, 0xfd, 0xa8, 0x85, 0xf8, 0xc0, 0xa9, 0xff, + 0x52, 0xc2, 0xe1, 0xc1, 0x22, 0x40, 0x1b, 0x77, 0xa7, 0x2f, 0x3a, 0x51, 0x86, 0xd9, + 0x7d, 0xd8, 0x08, 0xcf, 0xd4, 0xf9, 0x71, 0x9b, 0xac, 0xf5, 0xb3, 0x83, 0xa2, 0x1e, + 0x1b, 0xc3, 0x6b, 0xd0, 0x76, 0x1a, 0x97, 0x19, 0x92, 0x18, 0x1a, 0x33}, + {0xc6, 0x80, 0x4f, 0xfb, 0x45, 0x6f, 0x16, 0xf5, 0xcf, 0x75, 0xc7, 0x61, 0xde, 0xc7, + 0x36, 0x9c, 0x1c, 0xd9, 0x41, 0x90, 0x1b, 0xe8, 0xd4, 0xe3, 0x21, 0xfe, 0xbd, 0x83, + 0x6b, 0x7c, 0x16, 0x31, 0xaf, 0x72, 0x75, 0x9d, 0x3a, 0x2f, 0x51, 0x26, 0x9e, 0x4a, + 0x07, 0x68, 0x88, 0xe2, 0xcb, 0x5b, 0xc4, 0xf7, 0x80, 0x11, 0xc1, 0xc1, 0xed, 0x84, + 0x7b, 0xa6, 0x49, 0xf6, 0x9f, 0x61, 0xc9, 0x1a, 0x68, 0x10, 0x4b, 0x52, 0x42, 0x38, + 0x2b, 0xf2, 0x87, 0xe9, 0x9c, 0xee, 0x3b, 0x34, 0x68, 0x50, 0xc8, 0x50, 0x62, 0x4a, + 0x84, 0x71, 0x9d, 0xfc, 0x11, 0xb1, 0x08, 0x1f, 0x34, 0x36, 0x24, 0x61}, + {0x8d, 0x89, 0x4e, 0x87, 0xdb, 0x41, 0x9d, 0xd9, 0x20, 0xdc, 0x07, 0x6c, 0xf1, 0xa5, + 0xfe, 0x09, 0xbc, 0x9b, 0x0f, 0xd0, 0x67, 0x2c, 0x3d, 0x79, 0x40, 0xff, 0x5e, 0x9e, + 0x30, 0xe2, 0xeb, 0x46, 0x38, 0x26, 0x2d, 0x1a, 0xe3, 0x49, 0x63, 0x8b, 0x35, 0xfd, + 0xd3, 0x9b, 0x00, 0xb7, 0xdf, 0x9d, 0xa4, 0x6b, 0xa0, 0xa3, 0xb8, 0xf1, 0x8b, 0x7f, + 0x45, 0x04, 0xd9, 0x78, 0x31, 0xaa, 0x22, 0x15, 0x38, 0x49, 0x61, 0x69, 0x53, 0x2f, + 0x38, 0x2c, 0x10, 0x6d, 0x2d, 0xb7, 0x9a, 0x40, 0xfe, 0xda, 0x27, 0xf2, 0x46, 0xb6, + 0x91, 0x33, 0xc8, 0xe8, 0x6c, 0x30, 0x24, 0x05, 0xf5, 0x70, 0xfe, 0x45}, + {0x8c, 0x0b, 0x0c, 0x96, 0xa6, 0x75, 0x48, 0xda, 0x20, 0x2f, 0x0e, 0xef, 0x76, 0xd0, + 0x68, 0x5b, 0xd4, 0x8f, 0x0b, 0x3d, 0xcf, 0x51, 0xfb, 0x07, 0xd4, 0x92, 0xe3, 0xa0, + 0x23, 0x16, 0x8d, 0x42, 0x91, 0x14, 0x95, 0xc8, 0x20, 0x49, 0xf2, 0x62, 0xa2, 0x0c, + 0x63, 0x3f, 0xc8, 0x07, 0xf0, 0x05, 0xb8, 0xd4, 0xc9, 0xf5, 0xd2, 0x45, 0xbb, 0x6f, + 0x45, 0x22, 0x7a, 0xb5, 0x6d, 0x9f, 0x61, 0x16, 0xfd, 0x08, 0xa3, 0x01, 0x44, 0x4a, + 0x4f, 0x08, 0xac, 0xca, 0xa5, 0x76, 0xc3, 0x19, 0x22, 0xa8, 0x7d, 0xbc, 0xd1, 0x43, + 0x46, 0xde, 0xb8, 0xde, 0xc6, 0x38, 0xbd, 0x60, 0x2d, 0x59, 0x81, 0x1d}, + {0x5f, 0xac, 0x0d, 0xa6, 0x56, 0x87, 0x36, 0x61, 0x57, 0xdc, 0xab, 0xeb, 0x6a, 0x2f, + 0xe0, 0x17, 0x7d, 0x0f, 0xce, 0x4c, 0x2d, 0x3f, 0x19, 0x7f, 0xf0, 0xdc, 0xec, 0x89, + 0x77, 0x4a, 0x23, 0x20, 0xe8, 0xc5, 0x85, 0x7b, 0x9f, 0xb6, 0x65, 0x87, 0xb2, 0xba, + 0x68, 0xd1, 0x8b, 0x67, 0xf0, 0x6f, 0x9b, 0x0f, 0x33, 0x1d, 0x7c, 0xe7, 0x70, 0x3a, + 0x7c, 0x8e, 0xaf, 0xb0, 0x51, 0x6d, 0x5f, 0x3a, 0x52, 0xb2, 0x78, 0x71, 0xb6, 0x0d, + 0xd2, 0x76, 0x60, 0xd1, 0x1e, 0xd5, 0xf9, 0x34, 0x1c, 0x07, 0x70, 0x11, 0xe4, 0xb3, + 0x20, 0x4a, 0x2a, 0xf6, 0x66, 0xe3, 0xff, 0x3c, 0x35, 0x82, 0xd6, 0x7c}, + {0xb6, 0xfa, 0x87, 0xd8, 0x5b, 0xa4, 0xe1, 0x0b, 0x6e, 0x3b, 0x40, 0xba, 0x32, 0x6a, + 0x84, 0x2a, 0x00, 0x60, 0x6e, 0xe9, 0x12, 0x10, 0x92, 0xd9, 0x43, 0x09, 0xdc, 0x3b, + 0x86, 0xc8, 0x38, 0x28, 0xf3, 0xf4, 0xac, 0x68, 0x60, 0xcd, 0x65, 0xa6, 0xd3, 0xe3, + 0xd7, 0x3c, 0x18, 0x2d, 0xd9, 0x42, 0xd9, 0x25, 0x60, 0x33, 0x9d, 0x38, 0x59, 0x57, + 0xff, 0xd8, 0x2c, 0x2b, 0x3b, 0x25, 0xf0, 0x3e, 0x30, 0x50, 0x46, 0x4a, 0xcf, 0xb0, + 0x6b, 0xd1, 0xab, 0x77, 0xc5, 0x15, 0x41, 0x6b, 0x49, 0xfa, 0x9d, 0x41, 0xab, 0xf4, + 0x8a, 0xae, 0xcf, 0x82, 0x12, 0x28, 0xa8, 0x06, 0xa6, 0xb8, 0xdc, 0x21}, + {0xc8, 0x9f, 0x9d, 0x8c, 0x46, 0x04, 0x60, 0x5c, 0xcb, 0xa3, 0x2a, 0xd4, 0x6e, 0x09, + 0x40, 0x25, 0x9c, 0x2f, 0xee, 0x12, 0x4c, 0x4d, 0x5b, 0x12, 0xab, 0x1d, 0xa3, 0x94, + 0x81, 0xd0, 0xc3, 0x0b, 0xba, 0x31, 0x77, 0xbe, 0xfa, 0x00, 0x8d, 0x9a, 0x89, 0x18, + 0x9e, 0x62, 0x7e, 0x60, 0x03, 0x82, 0x7f, 0xd9, 0xf3, 0x43, 0x37, 0x02, 0xcc, 0xb2, + 0x8b, 0x67, 0x6f, 0x6c, 0xbf, 0x0d, 0x84, 0x5d, 0x8b, 0xe1, 0x9f, 0x30, 0x0d, 0x38, + 0x6e, 0x70, 0xc7, 0x65, 0xe1, 0xb9, 0xa6, 0x2d, 0xb0, 0x6e, 0xab, 0x20, 0xae, 0x7d, + 0x99, 0xba, 0xbb, 0x57, 0xdd, 0x96, 0xc1, 0x2a, 0x23, 0x76, 0x42, 0x3a}, + {0xfa, 0x84, 0x70, 0x8a, 0x2c, 0x43, 0x42, 0x4b, 0x45, 0xe5, 0xb9, 0xdf, 0xe3, 0x19, + 0x8a, 0x89, 0x5d, 0xe4, 0x58, 0x9c, 0x21, 0x00, 0x9f, 0xbe, 0xd1, 0xeb, 0x6d, 0xa1, + 0xce, 0x77, 0xf1, 0x1f, 0xcb, 0x7e, 0x44, 0xdb, 0x72, 0xc1, 0xf8, 0x3b, 0xbd, 0x2d, + 0x28, 0xc6, 0x1f, 0xc4, 0xcf, 0x5f, 0xfe, 0x15, 0xaa, 0x75, 0xc0, 0xff, 0xac, 0x80, + 0xf9, 0xa9, 0xe1, 0x24, 0xe8, 0xc9, 0x70, 0x07, 0xfd, 0xb5, 0xb5, 0x45, 0x9a, 0xd9, + 0x61, 0xcf, 0x24, 0x79, 0x3a, 0x1b, 0xe9, 0x84, 0x09, 0x86, 0x89, 0x3e, 0x3e, 0x30, + 0x19, 0x09, 0x30, 0xe7, 0x1e, 0x0b, 0x50, 0x41, 0xfd, 0x64, 0xf2, 0x39}, + {0x9c, 0xe2, 0xe7, 0xdb, 0x17, 0x34, 0xad, 0xa7, 0x9c, 0x13, 0x9c, 0x2b, 0x6a, 0x37, + 0x94, 0xbd, 0xa9, 0x7b, 0x59, 0x93, 0x8e, 0x1b, 0xe9, 0xa0, 0x40, 0x98, 0x88, 0x68, + 0x34, 0xd7, 0x12, 0x17, 0xe1, 0x7b, 0x09, 0xfe, 0xab, 0x4a, 0x9b, 0xd1, 0x29, 0x19, + 0xe0, 0xdf, 0xe1, 0xfc, 0x6d, 0xa4, 0xff, 0xf1, 0xa6, 0x2c, 0x94, 0x08, 0xc9, 0xc3, + 0x4e, 0xf1, 0x35, 0x2c, 0x27, 0x21, 0xc6, 0x65, 0xdd, 0x93, 0x31, 0xce, 0xf8, 0x89, + 0x2b, 0xe7, 0xbb, 0xc0, 0x25, 0xa1, 0x56, 0x33, 0x10, 0x4d, 0x83, 0xfe, 0x1c, 0x2e, + 0x3d, 0xa9, 0x19, 0x04, 0x72, 0xe2, 0x9c, 0xb1, 0x0a, 0x80, 0xf9, 0x22}, + {0xcb, 0xf8, 0x9e, 0x3e, 0x8a, 0x36, 0x5a, 0x60, 0x15, 0x47, 0x50, 0xa5, 0x22, 0xc0, + 0xe9, 0xe3, 0x8f, 0x24, 0x24, 0x5f, 0xb0, 0x48, 0x3d, 0x55, 0xe5, 0x26, 0x76, 0x64, + 0xcd, 0x16, 0xf4, 0x13, 0xac, 0xfd, 0x6e, 0x9a, 0xdd, 0x9f, 0x02, 0x42, 0x41, 0x49, + 0xa5, 0x34, 0xbe, 0xce, 0x12, 0xb9, 0x7b, 0xf3, 0xbd, 0x87, 0xb9, 0x64, 0x0f, 0x64, + 0xb4, 0xca, 0x98, 0x85, 0xd3, 0xa4, 0x71, 0x41, 0x8c, 0x4c, 0xc9, 0x99, 0xaa, 0x58, + 0x27, 0xfa, 0x07, 0xb8, 0x00, 0xb0, 0x6f, 0x6f, 0x00, 0x23, 0x92, 0x53, 0xda, 0xad, + 0xdd, 0x91, 0xd2, 0xfb, 0xab, 0xd1, 0x4b, 0x57, 0xfa, 0x14, 0x82, 0x50}, + {0x4b, 0xfe, 0xd6, 0x3e, 0x15, 0x69, 0x02, 0xc2, 0xc4, 0x77, 0x1d, 0x51, 0x39, 0x67, + 0x5a, 0xa6, 0x94, 0xaf, 0x14, 0x2c, 0x46, 0x26, 0xde, 0xcb, 0x4b, 0xa7, 0xab, 0x6f, + 0xec, 0x60, 0xf9, 0x22, 0xd6, 0x03, 0xd0, 0x53, 0xbb, 0x15, 0x1a, 0x46, 0x65, 0xc9, + 0xf3, 0xbc, 0x88, 0x28, 0x10, 0xb2, 0x5a, 0x3a, 0x68, 0x6c, 0x75, 0x76, 0xc5, 0x27, + 0x47, 0xb4, 0x6c, 0xc8, 0xa4, 0x58, 0x77, 0x3a, 0x76, 0x50, 0xae, 0x93, 0xf6, 0x11, + 0x81, 0x54, 0xa6, 0x54, 0xfd, 0x1d, 0xdf, 0x21, 0xae, 0x1d, 0x65, 0x5e, 0x11, 0xf3, + 0x90, 0x8c, 0x24, 0x12, 0x94, 0xf4, 0xe7, 0x8d, 0x5f, 0xd1, 0x9f, 0x5d}, + {0x7f, 0x72, 0x63, 0x6d, 0xd3, 0x08, 0x14, 0x03, 0x33, 0xb5, 0xc7, 0xd7, 0xef, 0x9a, + 0x37, 0x6a, 0x4b, 0xe2, 0xae, 0xcc, 0xc5, 0x8f, 0xe1, 0xa9, 0xd3, 0xbe, 0x8f, 0x4f, + 0x91, 0x35, 0x2f, 0x33, 0x1e, 0x52, 0xd7, 0xee, 0x2a, 0x4d, 0x24, 0x3f, 0x15, 0x96, + 0x2e, 0x43, 0x28, 0x90, 0x3a, 0x8e, 0xd4, 0x16, 0x9c, 0x2e, 0x77, 0xba, 0x64, 0xe1, + 0xd8, 0x98, 0xeb, 0x47, 0xfa, 0x87, 0xc1, 0x3b, 0x0c, 0xc2, 0x86, 0xea, 0x15, 0x01, + 0x47, 0x6d, 0x25, 0xd1, 0x46, 0x6c, 0xcb, 0xb7, 0x8a, 0x99, 0x88, 0x01, 0x66, 0x3a, + 0xb5, 0x32, 0x78, 0xd7, 0x03, 0xba, 0x6f, 0x90, 0xce, 0x81, 0x0d, 0x45}, + {0x75, 0x52, 0x20, 0xa6, 0xa1, 0xb6, 0x7b, 0x6e, 0x83, 0x8e, 0x3c, 0x41, 0xd7, 0x21, + 0x4f, 0xaa, 0xb2, 0x5c, 0x8f, 0xe8, 0x55, 0xd1, 0x56, 0x6f, 0xe1, 0x5b, 0x34, 0xa6, + 0x4b, 0x5d, 0xe2, 0x2d, 0x3f, 0x74, 0xae, 0x1c, 0x96, 0xd8, 0x74, 0xd0, 0xed, 0x63, + 0x1c, 0xee, 0xf5, 0x18, 0x6d, 0xf8, 0x29, 0xed, 0xf4, 0xe7, 0x5b, 0xc5, 0xbd, 0x97, + 0x08, 0xb1, 0x3a, 0x66, 0x79, 0xd2, 0xba, 0x4c, 0xcd, 0x1f, 0xd7, 0xa0, 0x24, 0x90, + 0xd1, 0x80, 0xf8, 0x8a, 0x28, 0xfb, 0x0a, 0xc2, 0x25, 0xc5, 0x19, 0x64, 0x3a, 0x5f, + 0x4b, 0x97, 0xa3, 0xb1, 0x33, 0x72, 0x00, 0xe2, 0xef, 0xbc, 0x7f, 0x7d}, + {0x01, 0x28, 0x6b, 0x26, 0x6a, 0x1e, 0xef, 0xfa, 0x16, 0x9f, 0x73, 0xd5, 0xc4, 0x68, + 0x6c, 0x86, 0x2c, 0x76, 0x03, 0x1b, 0xbc, 0x2f, 0x8a, 0xf6, 0x8d, 0x5a, 0xb7, 0x87, + 0x5e, 0x43, 0x75, 0x59, 0x94, 0x90, 0xc2, 0xf3, 0xc5, 0x5d, 0x7c, 0xcd, 0xab, 0x05, + 0x91, 0x2a, 0x9a, 0xa2, 0x81, 0xc7, 0x58, 0x30, 0x1c, 0x42, 0x36, 0x1d, 0xc6, 0x80, + 0xd7, 0xd4, 0xd8, 0xdc, 0x96, 0xd1, 0x9c, 0x4f, 0x68, 0x37, 0x7b, 0x6a, 0xd8, 0x97, + 0x92, 0x19, 0x63, 0x7a, 0xd1, 0x1a, 0x24, 0x58, 0xd0, 0xd0, 0x17, 0x0c, 0x1c, 0x5c, + 0xad, 0x9c, 0x02, 0xba, 0x07, 0x03, 0x7a, 0x38, 0x84, 0xd0, 0xcd, 0x7c}, + {0x17, 0x04, 0x26, 0x6d, 0x2c, 0x42, 0xa6, 0xdc, 0xbd, 0x40, 0x82, 0x94, 0x50, 0x3d, + 0x15, 0xae, 0x77, 0xc6, 0x68, 0xfb, 0xb4, 0xc1, 0xc0, 0xa9, 0x53, 0xcf, 0xd0, 0x61, + 0xed, 0xd0, 0x8b, 0x42, 0x93, 0xcc, 0x60, 0x67, 0x18, 0x84, 0x0c, 0x9b, 0x99, 0x2a, + 0xb3, 0x1a, 0x7a, 0x00, 0xae, 0xcd, 0x18, 0xda, 0x0b, 0x62, 0x86, 0xec, 0x8d, 0xa8, + 0x44, 0xca, 0x90, 0x81, 0x84, 0xca, 0x93, 0x35, 0xa7, 0x9a, 0x84, 0x5e, 0x9a, 0x18, + 0x13, 0x92, 0xcd, 0xfa, 0xd8, 0x65, 0x35, 0xc3, 0xd8, 0xd4, 0xd1, 0xbb, 0xfd, 0x53, + 0x5b, 0x54, 0x52, 0x8c, 0xe6, 0x63, 0x2d, 0xda, 0x08, 0x83, 0x39, 0x27}, + {0x13, 0xd4, 0x5e, 0x43, 0x28, 0x8d, 0xc3, 0x42, 0xc9, 0xcc, 0x78, 0x32, 0x60, 0xf3, + 0x50, 0xbd, 0xef, 0x03, 0xda, 0x79, 0x1a, 0xab, 0x07, 0xbb, 0x55, 0x33, 0x8c, 0xbe, + 0xae, 0x97, 0x95, 0x26, 0x53, 0x24, 0x70, 0x0a, 0x4c, 0x0e, 0xa1, 0xb9, 0xde, 0x1b, + 0x7d, 0xd5, 0x66, 0x58, 0xa2, 0x0f, 0xf7, 0xda, 0x27, 0xcd, 0xb5, 0xd9, 0xb9, 0xff, + 0xfd, 0x33, 0x2c, 0x49, 0x45, 0x29, 0x2c, 0x57, 0xbe, 0x30, 0xcd, 0xd6, 0x45, 0xc7, + 0x7f, 0xc7, 0xfb, 0xae, 0xba, 0xe3, 0xd3, 0xe8, 0xdf, 0xe4, 0x0c, 0xda, 0x5d, 0xaa, + 0x30, 0x88, 0x2c, 0xa2, 0x80, 0xca, 0x5b, 0xc0, 0x98, 0x54, 0x98, 0x7f}, + {0x17, 0xe1, 0x0b, 0x9f, 0x88, 0xce, 0x49, 0x38, 0x88, 0xa2, 0x54, 0x7b, 0x1b, 0xad, + 0x05, 0x80, 0x1c, 0x92, 0xfc, 0x23, 0x9f, 0xc3, 0xa3, 0x3d, 0x04, 0xf3, 0x31, 0x0a, + 0x47, 0xec, 0xc2, 0x76, 0x63, 0x63, 0xbf, 0x0f, 0x52, 0x15, 0x56, 0xd3, 0xa6, 0xfb, + 0x4d, 0xcf, 0x45, 0x5a, 0x04, 0x08, 0xc2, 0xa0, 0x3f, 0x87, 0xbc, 0x4f, 0xc2, 0xee, + 0xe7, 0x12, 0x9b, 0xd6, 0x3c, 0x65, 0xf2, 0x30, 0x85, 0x0c, 0xc1, 0xaa, 0x38, 0xc9, + 0x08, 0x8a, 0xcb, 0x6b, 0x27, 0xdb, 0x60, 0x9b, 0x17, 0x46, 0x70, 0xac, 0x6f, 0x0e, + 0x1e, 0xc0, 0x20, 0xa9, 0xda, 0x73, 0x64, 0x59, 0xf1, 0x73, 0x12, 0x2f}, + {0x11, 0x1e, 0xe0, 0x8a, 0x7c, 0xfc, 0x39, 0x47, 0x9f, 0xab, 0x6a, 0x4a, 0x90, 0x74, + 0x52, 0xfd, 0x2e, 0x8f, 0x72, 0x87, 0x82, 0x8a, 0xd9, 0x41, 0xf2, 0x69, 0x5b, 0xd8, + 0x2a, 0x57, 0x9e, 0x5d, 0xc0, 0x0b, 0xa7, 0x55, 0xd7, 0x8b, 0x48, 0x30, 0xe7, 0x42, + 0xd4, 0xf1, 0xa4, 0xb5, 0xd6, 0x06, 0x62, 0x61, 0x59, 0xbc, 0x9e, 0xa6, 0xd1, 0xea, + 0x84, 0xf7, 0xc5, 0xed, 0x97, 0x19, 0xac, 0x38, 0x3b, 0xb1, 0x51, 0xa7, 0x17, 0xb5, + 0x66, 0x06, 0x8c, 0x85, 0x9b, 0x7e, 0x86, 0x06, 0x7d, 0x74, 0x49, 0xde, 0x4d, 0x45, + 0x11, 0xc0, 0xac, 0xac, 0x9c, 0xe6, 0xe9, 0xbf, 0x9c, 0xcd, 0xdf, 0x22}, + {0xd9, 0x0c, 0x0d, 0xc3, 0xe0, 0xd2, 0xdb, 0x8d, 0x33, 0x43, 0xbb, 0xac, 0x5f, 0x66, + 0x8e, 0xad, 0x1f, 0x96, 0x2a, 0x32, 0x8c, 0x25, 0x6b, 0x8f, 0xc7, 0xc1, 0x48, 0x54, + 0xc0, 0x16, 0x29, 0x6b, 0xa1, 0xe0, 0x3b, 0x10, 0xb4, 0x59, 0xec, 0x56, 0x69, 0xf9, + 0x59, 0xd2, 0xec, 0xba, 0xe3, 0x2e, 0x32, 0xcd, 0xf5, 0x13, 0x94, 0xb2, 0x7c, 0x79, + 0x72, 0xe4, 0xcd, 0x24, 0x78, 0x87, 0xe9, 0x0f, 0x3b, 0x91, 0xba, 0x0a, 0xd1, 0x34, + 0xdb, 0x7e, 0x0e, 0xac, 0x6d, 0x2e, 0x82, 0xcd, 0xa3, 0x4e, 0x15, 0xf8, 0x78, 0x65, + 0xff, 0x3d, 0x08, 0x66, 0x17, 0x0a, 0xf0, 0x7f, 0x30, 0x3f, 0x30, 0x4c}, + {0x85, 0x8c, 0xb2, 0x17, 0xd6, 0x3b, 0x0a, 0xd3, 0xea, 0x3b, 0x77, 0x39, 0xb7, 0x77, + 0xd3, 0xc5, 0xbf, 0x5c, 0x6a, 0x1e, 0x8c, 0xe7, 0xc6, 0xc6, 0xc4, 0xb7, 0x2a, 0x8b, + 0xf7, 0xb8, 0x61, 0x0d, 0x00, 0x45, 0xd9, 0x0d, 0x58, 0x03, 0xfc, 0x29, 0x93, 0xec, + 0xbb, 0x6f, 0xa4, 0x7a, 0xd2, 0xec, 0xf8, 0xa7, 0xe2, 0xc2, 0x5f, 0x15, 0x0a, 0x13, + 0xd5, 0xa1, 0x06, 0xb7, 0x1a, 0x15, 0x6b, 0x41, 0xb0, 0x36, 0xc1, 0xe9, 0xef, 0xd7, + 0xa8, 0x56, 0x20, 0x4b, 0xe4, 0x58, 0xcd, 0xe5, 0x07, 0xbd, 0xab, 0xe0, 0x57, 0x1b, + 0xda, 0x2f, 0xe6, 0xaf, 0xd2, 0xe8, 0x77, 0x42, 0xf7, 0x2a, 0x1a, 0x19}, + {0x31, 0x14, 0x3c, 0xc5, 0x4b, 0xf7, 0x16, 0xce, 0xde, 0xed, 0x72, 0x20, 0xce, 0x25, + 0x97, 0x2b, 0xe7, 0x3e, 0xb2, 0xb5, 0x6f, 0xc3, 0xb9, 0xb8, 0x08, 0xc9, 0x5c, 0x0b, + 0x45, 0x0e, 0x2e, 0x7e, 0xfb, 0x0e, 0x46, 0x4f, 0x43, 0x2b, 0xe6, 0x9f, 0xd6, 0x07, + 0x36, 0xa6, 0xd4, 0x03, 0xd3, 0xde, 0x24, 0xda, 0xa0, 0xb7, 0x0e, 0x21, 0x52, 0xf0, + 0x93, 0x5b, 0x54, 0x00, 0xbe, 0x7d, 0x7e, 0x23, 0x30, 0xb4, 0x01, 0x67, 0xed, 0x75, + 0x35, 0x01, 0x10, 0xfd, 0x0b, 0x9f, 0xe6, 0x94, 0x10, 0x23, 0x22, 0x7f, 0xe4, 0x83, + 0x15, 0x0f, 0x32, 0x75, 0xe3, 0x55, 0x11, 0xb1, 0x99, 0xa6, 0xaf, 0x71}, + {0x1d, 0xb6, 0x53, 0x39, 0x9b, 0x6f, 0xce, 0x65, 0xe6, 0x41, 0xa1, 0xaf, 0xea, 0x39, + 0x58, 0xc6, 0xfe, 0x59, 0xf7, 0xa9, 0xfd, 0x5f, 0x43, 0x0f, 0x8e, 0xc2, 0xb1, 0xc2, + 0xe9, 0x42, 0x11, 0x02, 0xd6, 0x50, 0x3b, 0x47, 0x1c, 0x3c, 0x42, 0xea, 0x10, 0xef, + 0x38, 0x3b, 0x1f, 0x7a, 0xe8, 0x51, 0x95, 0xbe, 0xc9, 0xb2, 0x5f, 0xbf, 0x84, 0x9b, + 0x1c, 0x9a, 0xf8, 0x78, 0xbc, 0x1f, 0x73, 0x00, 0x80, 0x18, 0xf8, 0x48, 0x18, 0xc7, + 0x30, 0xe4, 0x19, 0xc1, 0xce, 0x5e, 0x22, 0x0c, 0x96, 0xbf, 0xe3, 0x15, 0xba, 0x6b, + 0x83, 0xe0, 0xda, 0xb6, 0x08, 0x58, 0xe1, 0x47, 0x33, 0x6f, 0x4d, 0x4c}, + {0xc9, 0x1f, 0x7d, 0xc1, 0xcf, 0xec, 0xf7, 0x18, 0x14, 0x3c, 0x40, 0x51, 0xa6, 0xf5, + 0x75, 0x6c, 0xdf, 0x0c, 0xee, 0xf7, 0x2b, 0x71, 0xde, 0xdb, 0x22, 0x7a, 0xe4, 0xa7, + 0xaa, 0xdd, 0x3f, 0x19, 0x70, 0x19, 0x8f, 0x98, 0xfc, 0xdd, 0x0c, 0x2f, 0x1b, 0xf5, + 0xb9, 0xb0, 0x27, 0x62, 0x91, 0x6b, 0xbe, 0x76, 0x91, 0x77, 0xc4, 0xb6, 0xc7, 0x6e, + 0xa8, 0x9f, 0x8f, 0xa8, 0x00, 0x95, 0xbf, 0x38, 0x6f, 0x87, 0xe8, 0x37, 0x3c, 0xc9, + 0xd2, 0x1f, 0x2c, 0x46, 0xd1, 0x18, 0x5a, 0x1e, 0xf6, 0xa2, 0x76, 0x12, 0x24, 0x39, + 0x82, 0xf5, 0x80, 0x50, 0x69, 0x49, 0x0d, 0xbf, 0x9e, 0xb9, 0x6f, 0x6a}, + {0xeb, 0x55, 0x08, 0x56, 0xbb, 0xc1, 0x46, 0x6a, 0x9d, 0xf0, 0x93, 0xf8, 0x38, 0xbb, + 0x16, 0x24, 0xc1, 0xac, 0x71, 0x8f, 0x37, 0x11, 0x1d, 0xd7, 0xea, 0x96, 0x18, 0xa3, + 0x14, 0x69, 0xf7, 0x75, 0xc6, 0x23, 0xe4, 0xb6, 0xb5, 0x22, 0xb1, 0xee, 0x8e, 0xff, + 0x86, 0xf2, 0x10, 0x70, 0x9d, 0x93, 0x8c, 0x5d, 0xcf, 0x1d, 0x83, 0x2a, 0xa9, 0x90, + 0x10, 0xeb, 0xc5, 0x42, 0x9f, 0xda, 0x6f, 0x13, 0xd1, 0xbd, 0x05, 0xa3, 0xb1, 0xdf, + 0x4c, 0xf9, 0x08, 0x2c, 0xf8, 0x9f, 0x9d, 0x4b, 0x36, 0x0f, 0x8a, 0x58, 0xbb, 0xc3, + 0xa5, 0xd8, 0x87, 0x2a, 0xba, 0xdc, 0xe8, 0x0b, 0x51, 0x83, 0x21, 0x02}, + {0x14, 0x2d, 0xad, 0x5e, 0x38, 0x66, 0xf7, 0x4a, 0x30, 0x58, 0x7c, 0xca, 0x80, 0xd8, + 0x8e, 0xa0, 0x3d, 0x1e, 0x21, 0x10, 0xe6, 0xa6, 0x13, 0x0d, 0x03, 0x6c, 0x80, 0x7b, + 0xe1, 0x1c, 0x07, 0x6a, 0x7f, 0x7a, 0x30, 0x43, 0x01, 0x71, 0x5a, 0x9d, 0x5f, 0xa4, + 0x7d, 0xc4, 0x9e, 0xde, 0x63, 0xb0, 0xd3, 0x7a, 0x92, 0xbe, 0x52, 0xfe, 0xbb, 0x22, + 0x6c, 0x42, 0x40, 0xfd, 0x41, 0xc4, 0x87, 0x13, 0xf8, 0x8a, 0x97, 0x87, 0xd1, 0xc3, + 0xd3, 0xb5, 0x13, 0x44, 0x0e, 0x7f, 0x3d, 0x5a, 0x2b, 0x72, 0xa0, 0x7c, 0x47, 0xbb, + 0x48, 0x48, 0x7b, 0x0d, 0x92, 0xdc, 0x1e, 0xaf, 0x6a, 0xb2, 0x71, 0x31}, + {0xa8, 0x4c, 0x56, 0x97, 0x90, 0x31, 0x2f, 0xa9, 0x19, 0xe1, 0x75, 0x22, 0x4c, 0xb8, + 0x7b, 0xff, 0x50, 0x51, 0x87, 0xa4, 0x37, 0xfe, 0x55, 0x4f, 0x5a, 0x83, 0xf0, 0x3c, + 0x87, 0xd4, 0x1f, 0x22, 0xd1, 0x47, 0x8a, 0xb2, 0xd8, 0xb7, 0x0d, 0xa6, 0xf1, 0xa4, + 0x70, 0x17, 0xd6, 0x14, 0xbf, 0xa6, 0x58, 0xbd, 0xdd, 0x53, 0x93, 0xf8, 0xa1, 0xd4, + 0xe9, 0x43, 0x42, 0x34, 0x63, 0x4a, 0x51, 0x6c, 0x41, 0x63, 0x15, 0x3a, 0x4f, 0x20, + 0x22, 0x23, 0x2d, 0x03, 0x0a, 0xba, 0xe9, 0xe0, 0x73, 0xfb, 0x0e, 0x03, 0x0f, 0x41, + 0x4c, 0xdd, 0xe0, 0xfc, 0xaa, 0x4a, 0x92, 0xfb, 0x96, 0xa5, 0xda, 0x48}, + {0xc7, 0x9c, 0xa5, 0x5c, 0x66, 0x8e, 0xca, 0x6e, 0xa0, 0xac, 0x38, 0x2e, 0x4b, 0x25, + 0x47, 0xa8, 0xce, 0x17, 0x1e, 0xd2, 0x08, 0xc7, 0xaf, 0x31, 0xf7, 0x4a, 0xd8, 0xca, + 0xfc, 0xd6, 0x6d, 0x67, 0x93, 0x97, 0x4c, 0xc8, 0x5d, 0x1d, 0xf6, 0x14, 0x06, 0x82, + 0x41, 0xef, 0xe3, 0xf9, 0x41, 0x99, 0xac, 0x77, 0x62, 0x34, 0x8f, 0xb8, 0xf5, 0xcd, + 0xa9, 0x79, 0x8a, 0x0e, 0xfa, 0x37, 0xc8, 0x58, 0x58, 0x90, 0xfc, 0x96, 0x85, 0x68, + 0xf9, 0x0c, 0x1b, 0xa0, 0x56, 0x7b, 0xf3, 0xbb, 0xdc, 0x1d, 0x6a, 0xd6, 0x35, 0x49, + 0x7d, 0xe7, 0xc2, 0xdc, 0x0a, 0x7f, 0xa5, 0xc6, 0xf2, 0x73, 0x4f, 0x1c}, + {0xbb, 0xa0, 0x5f, 0x30, 0xbd, 0x4f, 0x7a, 0x0e, 0xad, 0x63, 0xc6, 0x54, 0xe0, 0x4c, + 0x9d, 0x82, 0x48, 0x38, 0xe3, 0x2f, 0x83, 0xc3, 0x21, 0xf4, 0x42, 0x4c, 0xf6, 0x1b, + 0x0d, 0xc8, 0x5a, 0x79, 0x84, 0x34, 0x7c, 0xfc, 0x6e, 0x70, 0x6e, 0xb3, 0x61, 0xcf, + 0xc1, 0xc3, 0xb4, 0xc9, 0xdf, 0x73, 0xe5, 0xc7, 0x1c, 0x78, 0xc9, 0x79, 0x1d, 0xeb, + 0x5c, 0x67, 0xaf, 0x7d, 0xdb, 0x9a, 0x45, 0x70, 0xb3, 0x2b, 0xb4, 0x91, 0x49, 0xdb, + 0x91, 0x1b, 0xca, 0xdc, 0x02, 0x4b, 0x23, 0x96, 0x26, 0x57, 0xdc, 0x78, 0x8c, 0x1f, + 0xe5, 0x9e, 0xdf, 0x9f, 0xd3, 0x1f, 0xe2, 0x8c, 0x84, 0x62, 0xe1, 0x5f}, + {0x1a, 0x96, 0x94, 0xe1, 0x4f, 0x21, 0x59, 0x4e, 0x4f, 0xcd, 0x71, 0x0d, 0xc7, 0x7d, + 0xbe, 0x49, 0x2d, 0xf2, 0x50, 0x3b, 0xd2, 0xcf, 0x00, 0x93, 0x32, 0x72, 0x91, 0xfc, + 0x46, 0xd4, 0x89, 0x47, 0x08, 0xb2, 0x7c, 0x5d, 0x2d, 0x85, 0x79, 0x28, 0xe7, 0xf2, + 0x7d, 0x68, 0x70, 0xdd, 0xde, 0xb8, 0x91, 0x78, 0x68, 0x21, 0xab, 0xff, 0x0b, 0xdc, + 0x35, 0xaa, 0x7d, 0x67, 0x43, 0xc0, 0x44, 0x2b, 0x8e, 0xb7, 0x4e, 0x07, 0xab, 0x87, + 0x1c, 0x1a, 0x67, 0xf4, 0xda, 0x99, 0x8e, 0xd1, 0xc6, 0xfa, 0x67, 0x90, 0x4f, 0x48, + 0xcd, 0xbb, 0xac, 0x3e, 0xe4, 0xa4, 0xb9, 0x2b, 0xef, 0x2e, 0xc5, 0x60}, + {0xf1, 0x8b, 0xfd, 0x3b, 0xbc, 0x89, 0x5d, 0x0b, 0x1a, 0x55, 0xf3, 0xc9, 0x37, 0x92, + 0x6b, 0xb0, 0xf5, 0x28, 0x30, 0xd5, 0xb0, 0x16, 0x4c, 0x0e, 0xab, 0xca, 0xcf, 0x2c, + 0x31, 0x9c, 0xbc, 0x10, 0x11, 0x6d, 0xae, 0x7c, 0xc2, 0xc5, 0x2b, 0x70, 0xab, 0x8c, + 0xa4, 0x54, 0x9b, 0x69, 0xc7, 0x44, 0xb2, 0x2e, 0x49, 0xba, 0x56, 0x40, 0xbc, 0xef, + 0x6d, 0x67, 0xb6, 0xd9, 0x48, 0x72, 0xd7, 0x70, 0x5b, 0xa0, 0xc2, 0x3e, 0x4b, 0xe8, + 0x8a, 0xaa, 0xe0, 0x81, 0x17, 0xed, 0xf4, 0x9e, 0x69, 0x98, 0xd1, 0x85, 0x8e, 0x70, + 0xe4, 0x13, 0x45, 0x79, 0x13, 0xf4, 0x76, 0xa9, 0xd3, 0x5b, 0x75, 0x63}, + {0x53, 0x08, 0xd1, 0x2a, 0x3e, 0xa0, 0x5f, 0xb5, 0x69, 0x35, 0xe6, 0x9e, 0x90, 0x75, + 0x6f, 0x35, 0x90, 0xb8, 0x69, 0xbe, 0xfd, 0xf1, 0xf9, 0x9f, 0x84, 0x6f, 0xc1, 0x8b, + 0xc4, 0xc1, 0x8c, 0x0d, 0xb7, 0xac, 0xf1, 0x97, 0x18, 0x10, 0xc7, 0x3d, 0xd8, 0xbb, + 0x65, 0xc1, 0x5e, 0x7d, 0xda, 0x5d, 0x0f, 0x02, 0xa1, 0x0f, 0x9c, 0x5b, 0x8e, 0x50, + 0x56, 0x2a, 0xc5, 0x37, 0x17, 0x75, 0x63, 0x27, 0xa9, 0x19, 0xb4, 0x6e, 0xd3, 0x02, + 0x94, 0x02, 0xa5, 0x60, 0xb4, 0x77, 0x7e, 0x4e, 0xb4, 0xf0, 0x56, 0x49, 0x3c, 0xd4, + 0x30, 0x62, 0xa8, 0xcf, 0xe7, 0x66, 0xd1, 0x7a, 0x8a, 0xdd, 0xc2, 0x70}, + {0x0e, 0xec, 0x6f, 0x9f, 0x50, 0x94, 0x61, 0x65, 0x8d, 0x51, 0xc6, 0x46, 0xa9, 0x7e, + 0x2e, 0xee, 0x5c, 0x9b, 0xe0, 0x67, 0xf3, 0xc1, 0x33, 0x97, 0x95, 0x84, 0x94, 0x63, + 0x63, 0xac, 0x0f, 0x2e, 0x13, 0x7e, 0xed, 0xb8, 0x7d, 0x96, 0xd4, 0x91, 0x7a, 0x81, + 0x76, 0xd7, 0x0a, 0x2f, 0x25, 0x74, 0x64, 0x25, 0x85, 0x0d, 0xe0, 0x82, 0x09, 0xe4, + 0xe5, 0x3c, 0xa5, 0x16, 0x38, 0x61, 0xb8, 0x32, 0x64, 0xcd, 0x48, 0xe4, 0xbe, 0xf7, + 0xe7, 0x79, 0xd0, 0x86, 0x78, 0x08, 0x67, 0x3a, 0xc8, 0x6a, 0x2e, 0xdb, 0xe4, 0xa0, + 0xd9, 0xd4, 0x9f, 0xf8, 0x41, 0x4f, 0x5a, 0x73, 0x5c, 0x21, 0x79, 0x41}, + {0x2a, 0xed, 0xdc, 0xd7, 0xe7, 0x94, 0x70, 0x8c, 0x70, 0x9c, 0xd3, 0x47, 0xc3, 0x8a, + 0xfb, 0x97, 0x02, 0xd9, 0x06, 0xa9, 0x33, 0xe0, 0x3b, 0xe1, 0x76, 0x9d, 0xd9, 0x0c, + 0xa3, 0x44, 0x03, 0x70, 0x34, 0xcd, 0x6b, 0x28, 0xb9, 0x33, 0xae, 0xe4, 0xdc, 0xd6, + 0x9d, 0x55, 0xb6, 0x7e, 0xef, 0xb7, 0x1f, 0x8e, 0xd3, 0xb3, 0x1f, 0x14, 0x8b, 0x27, + 0x86, 0xc2, 0x41, 0x22, 0x66, 0x85, 0xfa, 0x31, 0xf4, 0x22, 0x36, 0x2e, 0x42, 0x6c, + 0x82, 0xaf, 0x2d, 0x50, 0x33, 0x98, 0x87, 0x29, 0x20, 0xc1, 0x23, 0x91, 0x38, 0x2b, + 0xe1, 0xb7, 0xc1, 0x9b, 0x89, 0x24, 0x95, 0xa9, 0x12, 0x23, 0xbb, 0x24}, + {0xc3, 0x67, 0xde, 0x32, 0x17, 0xed, 0xa8, 0xb1, 0x48, 0x49, 0x1b, 0x46, 0x18, 0x94, + 0xb4, 0x3c, 0xd2, 0xbc, 0xcf, 0x76, 0x43, 0x43, 0xbd, 0x8e, 0x08, 0x80, 0x18, 0x1e, + 0x87, 0x3e, 0xee, 0x0f, 0x6b, 0x5c, 0xf8, 0xf5, 0x2a, 0x0c, 0xf8, 0x41, 0x94, 0x67, + 0xfa, 0x04, 0xc3, 0x84, 0x72, 0x68, 0xad, 0x1b, 0xba, 0xa3, 0x99, 0xdf, 0x45, 0x89, + 0x16, 0x5d, 0xeb, 0xff, 0xf9, 0x2a, 0x1d, 0x0d, 0xdf, 0x1e, 0x62, 0x32, 0xa1, 0x8a, + 0xda, 0xa9, 0x79, 0x65, 0x22, 0x59, 0xa1, 0x22, 0xb8, 0x30, 0x93, 0xc1, 0x9a, 0xa7, + 0x7b, 0x19, 0x04, 0x40, 0x76, 0x1d, 0x53, 0x18, 0x97, 0xd7, 0xac, 0x16}, + {0x3d, 0x1d, 0x9b, 0x2d, 0xaf, 0x72, 0xdf, 0x72, 0x5a, 0x24, 0x32, 0xa4, 0x36, 0x2a, + 0x46, 0x63, 0x37, 0x96, 0xb3, 0x16, 0x79, 0xa0, 0xce, 0x3e, 0x09, 0x23, 0x30, 0xb9, + 0xf6, 0x0e, 0x3e, 0x12, 0xad, 0xb6, 0x87, 0x78, 0xc5, 0xc6, 0x59, 0xc9, 0xba, 0xfe, + 0x90, 0x5f, 0xad, 0x9e, 0xe1, 0x94, 0x04, 0xf5, 0x42, 0xa3, 0x62, 0x4e, 0xe2, 0x16, + 0x00, 0x17, 0x16, 0x18, 0x4b, 0xd3, 0x4e, 0x16, 0x9a, 0xe6, 0x2f, 0x19, 0x4c, 0xd9, + 0x7e, 0x48, 0x13, 0x15, 0x91, 0x3a, 0xea, 0x2c, 0xae, 0x61, 0x27, 0xde, 0xa4, 0xb9, + 0xd3, 0xf6, 0x7b, 0x87, 0xeb, 0xf3, 0x73, 0x10, 0xc6, 0x0f, 0xda, 0x78}, + {0x6a, 0xc6, 0x2b, 0xe5, 0x28, 0x5d, 0xf1, 0x5b, 0x8e, 0x1a, 0xf0, 0x70, 0x18, 0xe3, + 0x47, 0x2c, 0xdd, 0x8b, 0xc2, 0x06, 0xbc, 0xaf, 0x19, 0x24, 0x3a, 0x17, 0x6b, 0x25, + 0xeb, 0xde, 0x25, 0x2d, 0x94, 0x3a, 0x0c, 0x68, 0xf1, 0x80, 0x9f, 0xa2, 0xe6, 0xe7, + 0xe9, 0x1a, 0x15, 0x7e, 0xf7, 0x71, 0x73, 0x79, 0x01, 0x48, 0x58, 0xf1, 0x00, 0x11, + 0xdd, 0x8d, 0xb3, 0x16, 0xb3, 0xa4, 0x4a, 0x05, 0xb8, 0x7c, 0x26, 0x19, 0x8d, 0x46, + 0xc8, 0xdf, 0xaf, 0x4d, 0xe5, 0x66, 0x9c, 0x78, 0x28, 0x0b, 0x17, 0xec, 0x6e, 0x66, + 0x2a, 0x1d, 0xeb, 0x2a, 0x60, 0xa7, 0x7d, 0xab, 0xa6, 0x10, 0x46, 0x13}, + {0xfe, 0xb0, 0xf6, 0x8d, 0xc7, 0x8e, 0x13, 0x51, 0x1b, 0xf5, 0x75, 0xe5, 0x89, 0xda, + 0x97, 0x53, 0xb9, 0xf1, 0x7a, 0x71, 0x1d, 0x7a, 0x20, 0x09, 0x50, 0xd6, 0x20, 0x2b, + 0xba, 0xfd, 0x02, 0x21, 0x15, 0xf5, 0xd1, 0x77, 0xe7, 0x65, 0x2a, 0xcd, 0xf1, 0x60, + 0xaa, 0x8f, 0x87, 0x91, 0x89, 0x54, 0xe5, 0x06, 0xbc, 0xda, 0xbc, 0x3b, 0xb7, 0xb1, + 0xfb, 0xc9, 0x7c, 0xa9, 0xcb, 0x78, 0x48, 0x65, 0xa1, 0xe6, 0x5c, 0x05, 0x05, 0xe4, + 0x9e, 0x96, 0x29, 0xad, 0x51, 0x12, 0x68, 0xa7, 0xbc, 0x36, 0x15, 0xa4, 0x7d, 0xaa, + 0x17, 0xf5, 0x1a, 0x3a, 0xba, 0xb2, 0xec, 0x29, 0xdb, 0x25, 0xd7, 0x0a}, + {0x57, 0x24, 0x4e, 0x83, 0xb1, 0x67, 0x42, 0xdc, 0xc5, 0x1b, 0xce, 0x70, 0xb5, 0x44, + 0x75, 0xb6, 0xd7, 0x5e, 0xd1, 0xf7, 0x0b, 0x7a, 0xf0, 0x1a, 0x50, 0x36, 0xa0, 0x71, + 0xfb, 0xcf, 0xef, 0x4a, 0x85, 0x6f, 0x05, 0x9b, 0x0c, 0xbc, 0xc7, 0xfe, 0xd7, 0xff, + 0xf5, 0xe7, 0x68, 0x52, 0x7d, 0x53, 0xfa, 0xae, 0x12, 0x43, 0x62, 0xc6, 0xaf, 0x77, + 0xd9, 0x9f, 0x39, 0x02, 0x53, 0x5f, 0x67, 0x4f, 0x1e, 0x17, 0x15, 0x04, 0x36, 0x36, + 0x2d, 0xc3, 0x3b, 0x48, 0x98, 0x89, 0x11, 0xef, 0x2b, 0xcd, 0x10, 0x51, 0x94, 0xd0, + 0xad, 0x6e, 0x0a, 0x87, 0x61, 0x65, 0xa8, 0xa2, 0x72, 0xbb, 0xcc, 0x0b}, + {0xc8, 0xa9, 0xb1, 0xea, 0x2f, 0x96, 0x5e, 0x18, 0xcd, 0x7d, 0x14, 0x65, 0x35, 0xe6, + 0xe7, 0x86, 0xf2, 0x6d, 0x5b, 0xbb, 0x31, 0xe0, 0x92, 0xb0, 0x3e, 0xb7, 0xd6, 0x59, + 0xab, 0xf0, 0x24, 0x40, 0x96, 0x12, 0xfe, 0x50, 0x4c, 0x5e, 0x6d, 0x18, 0x7e, 0x9f, + 0xe8, 0xfe, 0x82, 0x7b, 0x39, 0xe0, 0xb0, 0x31, 0x70, 0x50, 0xc5, 0xf6, 0xc7, 0x3b, + 0xc2, 0x37, 0x8f, 0x10, 0x69, 0xfd, 0x78, 0x66, 0xc2, 0x63, 0x68, 0x63, 0x31, 0xfa, + 0x86, 0x15, 0xf2, 0x33, 0x2d, 0x57, 0x48, 0x8c, 0xf6, 0x07, 0xfc, 0xae, 0x9e, 0x78, + 0x9f, 0xcc, 0x73, 0x4f, 0x01, 0x47, 0xad, 0x8e, 0x10, 0xe2, 0x42, 0x2d}, + {0x9b, 0xd2, 0xdf, 0x94, 0x15, 0x13, 0xf5, 0x97, 0x6a, 0x4c, 0x3f, 0x31, 0x5d, 0x98, + 0x55, 0x61, 0x10, 0x50, 0x45, 0x08, 0x07, 0x3f, 0xa1, 0xeb, 0x22, 0xd3, 0xd2, 0xb8, + 0x08, 0x26, 0x6b, 0x67, 0x93, 0x75, 0x53, 0x0f, 0x0d, 0x7b, 0x71, 0x21, 0x4c, 0x06, + 0x1e, 0x13, 0x0b, 0x69, 0x4e, 0x91, 0x9f, 0xe0, 0x2a, 0x75, 0xae, 0x87, 0xb6, 0x1b, + 0x6e, 0x3c, 0x42, 0x9b, 0xa7, 0xf3, 0x0b, 0x42, 0x47, 0x2b, 0x5b, 0x1c, 0x65, 0xba, + 0x38, 0x81, 0x80, 0x1b, 0x1b, 0x31, 0xec, 0xb6, 0x71, 0x86, 0xb0, 0x35, 0x31, 0xbc, + 0xb1, 0x0c, 0xff, 0x7b, 0xe0, 0xf1, 0x0c, 0x9c, 0xfa, 0x2f, 0x5d, 0x74}, + {0xbd, 0xc8, 0xc9, 0x2b, 0x1e, 0x5a, 0x52, 0xbf, 0x81, 0x9d, 0x47, 0x26, 0x08, 0x26, + 0x5b, 0xea, 0xdb, 0x55, 0x01, 0xdf, 0x0e, 0xc7, 0x11, 0xd5, 0xd0, 0xf5, 0x0c, 0x96, + 0xeb, 0x3c, 0xe2, 0x1a, 0x6a, 0x4e, 0xd3, 0x21, 0x57, 0xdf, 0x36, 0x60, 0xd0, 0xb3, + 0x7b, 0x99, 0x27, 0x88, 0xdb, 0xb1, 0xfa, 0x6a, 0x75, 0xc8, 0xc3, 0x09, 0xc2, 0xd3, + 0x39, 0xc8, 0x1d, 0x4c, 0xe5, 0x5b, 0xe1, 0x06, 0x4a, 0x99, 0x32, 0x19, 0x87, 0x5d, + 0x72, 0x5b, 0xb0, 0xda, 0xb1, 0xce, 0xb5, 0x1c, 0x35, 0x32, 0x05, 0xca, 0xb7, 0xda, + 0x49, 0x15, 0xc4, 0x7d, 0xf7, 0xc1, 0x8e, 0x27, 0x61, 0xd8, 0xde, 0x58}, + {0x5c, 0xc5, 0x66, 0xf2, 0x93, 0x37, 0x17, 0xd8, 0x49, 0x4e, 0x45, 0xcc, 0xc5, 0x76, + 0xc9, 0xc8, 0xa8, 0xc3, 0x26, 0xbc, 0xf8, 0x82, 0xe3, 0x5c, 0xf9, 0xf6, 0x85, 0x54, + 0xe8, 0x9d, 0xf3, 0x2f, 0xa8, 0xc9, 0xc2, 0xb6, 0xa8, 0x5b, 0xfb, 0x2d, 0x8c, 0x59, + 0x2c, 0xf5, 0x8e, 0xef, 0xee, 0x48, 0x73, 0x15, 0x2d, 0xf1, 0x07, 0x91, 0x80, 0x33, + 0xd8, 0x5b, 0x1d, 0x53, 0x6b, 0x69, 0xba, 0x08, 0x7a, 0xc5, 0xef, 0xc3, 0xee, 0x3e, + 0xed, 0x77, 0x11, 0x48, 0xff, 0xd4, 0x17, 0x55, 0xe0, 0x04, 0xcb, 0x71, 0xa6, 0xf1, + 0x3f, 0x7a, 0x3d, 0xea, 0x54, 0xfe, 0x7c, 0x94, 0xb4, 0x33, 0x06, 0x12}, + {0x42, 0x00, 0x61, 0x91, 0x78, 0x98, 0x94, 0x0b, 0xe8, 0xfa, 0xeb, 0xec, 0x3c, 0xb1, + 0xe7, 0x4e, 0xc0, 0xa4, 0xf0, 0x94, 0x95, 0x73, 0xbe, 0x70, 0x85, 0x91, 0xd5, 0xb4, + 0x99, 0x0a, 0xd3, 0x35, 0x0a, 0x10, 0x12, 0x49, 0x47, 0x31, 0xbd, 0x82, 0x06, 0xbe, + 0x6f, 0x7e, 0x6d, 0x7b, 0x23, 0xde, 0xc6, 0x79, 0xea, 0x11, 0x19, 0x76, 0x1e, 0xe1, + 0xde, 0x3b, 0x39, 0xcb, 0xe3, 0x3b, 0x43, 0x07, 0xf4, 0x97, 0xe9, 0x5c, 0xc0, 0x44, + 0x79, 0xff, 0xa3, 0x51, 0x5c, 0xb0, 0xe4, 0x3d, 0x5d, 0x57, 0x7c, 0x84, 0x76, 0x5a, + 0xfd, 0x81, 0x33, 0x58, 0x9f, 0xda, 0xf6, 0x7a, 0xde, 0x3e, 0x87, 0x2d}, + {0x09, 0x34, 0x37, 0x43, 0x64, 0x31, 0x7a, 0x15, 0xd9, 0x81, 0xaa, 0xf4, 0xee, 0xb7, + 0xb8, 0xfa, 0x06, 0x48, 0xa6, 0xf5, 0xe6, 0xfe, 0x93, 0xb0, 0xb6, 0xa7, 0x7f, 0x70, + 0x54, 0x36, 0x77, 0x2e, 0x81, 0xf9, 0x5d, 0x4e, 0xe1, 0x02, 0x62, 0xaa, 0xf5, 0xe1, + 0x15, 0x50, 0x17, 0x59, 0x0d, 0xa2, 0x6c, 0x1d, 0xe2, 0xba, 0xd3, 0x75, 0xa2, 0x18, + 0x53, 0x02, 0x60, 0x01, 0x8a, 0x61, 0x43, 0x05, 0xc1, 0x23, 0x4c, 0x97, 0xf4, 0xbd, + 0xea, 0x0d, 0x93, 0x46, 0xce, 0x9d, 0x25, 0x0a, 0x6f, 0xaa, 0x2c, 0xba, 0x9a, 0xa2, + 0xb8, 0x2c, 0x20, 0x04, 0x0d, 0x96, 0x07, 0x2d, 0x36, 0x43, 0x14, 0x4b}, + {0x7a, 0x1f, 0x6e, 0xb6, 0xc7, 0xb7, 0xc4, 0xcc, 0x7e, 0x2f, 0x0c, 0xf5, 0x25, 0x7e, + 0x15, 0x44, 0x1c, 0xaf, 0x3e, 0x71, 0xfc, 0x6d, 0xf0, 0x3e, 0xf7, 0x63, 0xda, 0x52, + 0x67, 0x44, 0x2f, 0x58, 0xcb, 0x9c, 0x52, 0x1c, 0xe9, 0x54, 0x7c, 0x96, 0xfb, 0x35, + 0xc6, 0x64, 0x92, 0x26, 0xf6, 0x30, 0x65, 0x19, 0x12, 0x78, 0xf4, 0xaf, 0x47, 0x27, + 0x5c, 0x6f, 0xf6, 0xea, 0x18, 0x84, 0x03, 0x17, 0xe4, 0x4c, 0x32, 0x20, 0xd3, 0x7b, + 0x31, 0xc6, 0xc4, 0x8b, 0x48, 0xa4, 0xe8, 0x42, 0x10, 0xa8, 0x64, 0x13, 0x5a, 0x4e, + 0x8b, 0xf1, 0x1e, 0xb2, 0xc9, 0x8d, 0xa2, 0xcd, 0x4b, 0x1c, 0x2a, 0x0c}, + {0x47, 0x04, 0x1f, 0x6f, 0xd0, 0xc7, 0x4d, 0xd2, 0x59, 0xc0, 0x87, 0xdb, 0x3e, 0x9e, + 0x26, 0xb2, 0x8f, 0xd2, 0xb2, 0xfb, 0x72, 0x02, 0x5b, 0xd1, 0x77, 0x48, 0xf6, 0xc6, + 0xd1, 0x8b, 0x55, 0x7c, 0x45, 0x69, 0xbd, 0x69, 0x48, 0x81, 0xc4, 0xed, 0x22, 0x8d, + 0x1c, 0xbe, 0x7d, 0x90, 0x6d, 0x0d, 0xab, 0xc5, 0x5c, 0xd5, 0x12, 0xd2, 0x3b, 0xc6, + 0x83, 0xdc, 0x14, 0xa3, 0x30, 0x9b, 0x6a, 0x5a, 0x3d, 0x46, 0x96, 0xd3, 0x24, 0x15, + 0xec, 0xd0, 0xf0, 0x24, 0x5a, 0xc3, 0x8a, 0x62, 0xbb, 0x12, 0xa4, 0x5f, 0xbc, 0x1c, + 0x79, 0x3a, 0x0c, 0xa5, 0xc3, 0xaf, 0xfb, 0x0a, 0xca, 0xa5, 0x04, 0x04}, + {0xd6, 0x43, 0xa7, 0x0a, 0x07, 0x40, 0x1f, 0x8c, 0xe8, 0x5e, 0x26, 0x5b, 0xcb, 0xd0, + 0xba, 0xcc, 0xde, 0xd2, 0x8f, 0x66, 0x6b, 0x04, 0x4b, 0x57, 0x33, 0x96, 0xdd, 0xca, + 0xfd, 0x5b, 0x39, 0x46, 0xd1, 0x6f, 0x41, 0x2a, 0x1b, 0x9e, 0xbc, 0x62, 0x8b, 0x59, + 0x50, 0xe3, 0x28, 0xf7, 0xc6, 0xb5, 0x67, 0x69, 0x5d, 0x3d, 0xd8, 0x3f, 0x34, 0x04, + 0x98, 0xee, 0xf8, 0xe7, 0x16, 0x75, 0x52, 0x39, 0x9c, 0x9a, 0x5d, 0x1a, 0x2d, 0xdb, + 0x7f, 0x11, 0x2a, 0x5c, 0x00, 0xd1, 0xbc, 0x45, 0x77, 0x9c, 0xea, 0x6f, 0xd5, 0x54, + 0xf1, 0xbe, 0xd4, 0xef, 0x16, 0xd0, 0x22, 0xe8, 0x29, 0x9a, 0x57, 0x76}, + {0x17, 0x2a, 0xc0, 0x49, 0x7e, 0x8e, 0xb6, 0x45, 0x7f, 0xa3, 0xa9, 0xbc, 0xa2, 0x51, + 0xcd, 0x23, 0x1b, 0x4c, 0x22, 0xec, 0x11, 0x5f, 0xd6, 0x3e, 0xb1, 0xbd, 0x05, 0x9e, + 0xdc, 0x84, 0xa3, 0x43, 0xf2, 0x34, 0xb4, 0x52, 0x13, 0xb5, 0x3c, 0x33, 0xe1, 0x80, + 0xde, 0x93, 0x49, 0x28, 0x32, 0xd8, 0xce, 0x35, 0x0d, 0x75, 0x87, 0x28, 0x51, 0xb5, + 0xc1, 0x77, 0x27, 0x2a, 0xbb, 0x14, 0xc5, 0x02, 0x45, 0xb6, 0xf1, 0x8b, 0xda, 0xd5, + 0x4b, 0x68, 0x53, 0x4b, 0xb5, 0xf6, 0x7e, 0xd3, 0x8b, 0xfb, 0x53, 0xd2, 0xb0, 0xa9, + 0xd7, 0x16, 0x39, 0x31, 0x59, 0x80, 0x54, 0x61, 0x09, 0x92, 0x60, 0x11}, + {0xaa, 0xcf, 0xda, 0x29, 0x69, 0x16, 0x4d, 0xb4, 0x8f, 0x59, 0x13, 0x84, 0x4c, 0x9f, + 0x52, 0xda, 0x59, 0x55, 0x3d, 0x45, 0xca, 0x63, 0xef, 0xe9, 0x0b, 0x8e, 0x69, 0xc5, + 0x5b, 0x12, 0x1e, 0x35, 0xcd, 0x4d, 0x9b, 0x36, 0x16, 0x56, 0x38, 0x7a, 0x63, 0x35, + 0x5c, 0x65, 0xa7, 0x2c, 0xc0, 0x75, 0x21, 0x80, 0xf1, 0xd4, 0xf9, 0x1b, 0xc2, 0x7d, + 0x42, 0xe0, 0xe6, 0x91, 0x74, 0x7d, 0x63, 0x2f, 0xbe, 0x7b, 0xf6, 0x1a, 0x46, 0x9b, + 0xb4, 0xd4, 0x61, 0x89, 0xab, 0xc8, 0x7a, 0x03, 0x03, 0xd6, 0xfb, 0x99, 0xa6, 0xf9, + 0x9f, 0xe1, 0xde, 0x71, 0x9a, 0x2a, 0xce, 0xe7, 0x06, 0x2d, 0x18, 0x7f}, + {0xec, 0x68, 0x01, 0xab, 0x64, 0x8e, 0x7c, 0x7a, 0x43, 0xc5, 0xed, 0x15, 0x55, 0x4a, + 0x5a, 0xcb, 0xda, 0x0e, 0xcd, 0x47, 0xd3, 0x19, 0x55, 0x09, 0xb0, 0x93, 0x3e, 0x34, + 0x8c, 0xac, 0xd4, 0x67, 0x22, 0x75, 0x21, 0x8e, 0x72, 0x4b, 0x45, 0x09, 0xd8, 0xb8, + 0x84, 0xd4, 0xf4, 0xe8, 0x58, 0xaa, 0x3c, 0x90, 0x46, 0x7f, 0x4d, 0x25, 0x58, 0xd3, + 0x17, 0x52, 0x1c, 0x24, 0x43, 0xc0, 0xac, 0x44, 0x77, 0x57, 0x7a, 0x4f, 0xbb, 0x6b, + 0x7d, 0x1c, 0xe1, 0x13, 0x83, 0x91, 0xd4, 0xfe, 0x35, 0x8b, 0x84, 0x46, 0x6b, 0xc9, + 0xc6, 0xa1, 0xdc, 0x4a, 0xbd, 0x71, 0xad, 0x12, 0x83, 0x1c, 0x6d, 0x55}, + {0x82, 0x39, 0x8d, 0x0c, 0xe3, 0x40, 0xef, 0x17, 0x34, 0xfa, 0xa3, 0x15, 0x3e, 0x07, + 0xf7, 0x31, 0x6e, 0x64, 0x73, 0x07, 0xcb, 0xf3, 0x21, 0x4f, 0xff, 0x4e, 0x82, 0x1d, + 0x6d, 0x6c, 0x6c, 0x74, 0x21, 0xe8, 0x1b, 0xb1, 0x56, 0x67, 0xf0, 0x81, 0xdd, 0xf3, + 0xa3, 0x10, 0x23, 0xf8, 0xaf, 0x0f, 0x5d, 0x46, 0x99, 0x6a, 0x55, 0xd0, 0xb2, 0xf8, + 0x05, 0x7f, 0x8c, 0xcc, 0x38, 0xbe, 0x7a, 0x09, 0xa4, 0x2d, 0xa5, 0x7e, 0x87, 0xc9, + 0x49, 0x0c, 0x43, 0x1d, 0xdc, 0x9b, 0x55, 0x69, 0x43, 0x4c, 0xd2, 0xeb, 0xcc, 0xf7, + 0x09, 0x38, 0x2c, 0x02, 0xbd, 0x84, 0xee, 0x4b, 0xa3, 0x14, 0x7e, 0x57}, + {0x0a, 0x3b, 0xa7, 0x61, 0xac, 0x68, 0xe2, 0xf0, 0xf5, 0xa5, 0x91, 0x37, 0x10, 0xfa, + 0xfa, 0xf2, 0xe9, 0x00, 0x6d, 0x6b, 0x82, 0x3e, 0xe1, 0xc1, 0x42, 0x8f, 0xd7, 0x6f, + 0xe9, 0x7e, 0xfa, 0x60, 0x2b, 0xd7, 0x4d, 0xbd, 0xbe, 0xce, 0xfe, 0x94, 0x11, 0x22, + 0x0f, 0x06, 0xda, 0x4f, 0x6a, 0xf4, 0xff, 0xd1, 0xc8, 0xc0, 0x77, 0x59, 0x4a, 0x12, + 0x95, 0x92, 0x00, 0xfb, 0xb8, 0x04, 0x53, 0x70, 0xc6, 0x6e, 0x29, 0x4d, 0x35, 0x1d, + 0x3d, 0xb6, 0xd8, 0x31, 0xad, 0x5f, 0x3e, 0x05, 0xc3, 0xf3, 0xec, 0x42, 0xbd, 0xb4, + 0x8c, 0x95, 0x0b, 0x67, 0xfd, 0x53, 0x63, 0xa1, 0x0c, 0x8e, 0x39, 0x21}, + {0xf3, 0x33, 0x2b, 0x38, 0x8a, 0x05, 0xf5, 0x89, 0xb4, 0xc0, 0x48, 0xad, 0x0b, 0xba, + 0xe2, 0x5a, 0x6e, 0xb3, 0x3d, 0xa5, 0x03, 0xb5, 0x93, 0x8f, 0xe6, 0x32, 0xa2, 0x95, + 0x9d, 0xed, 0xa3, 0x5a, 0x01, 0x56, 0xb7, 0xb4, 0xf9, 0xaa, 0x98, 0x27, 0x72, 0xad, + 0x8d, 0x5c, 0x13, 0x72, 0xac, 0x5e, 0x23, 0xa0, 0xb7, 0x61, 0x61, 0xaa, 0xce, 0xd2, + 0x4e, 0x7d, 0x8f, 0xe9, 0x84, 0xb2, 0xbf, 0x1b, 0x61, 0x65, 0xd9, 0xc7, 0xe9, 0x77, + 0x67, 0x65, 0x36, 0x80, 0xc7, 0x72, 0x54, 0x12, 0x2b, 0xcb, 0xee, 0x6e, 0x50, 0xd9, + 0x99, 0x32, 0x05, 0x65, 0xcc, 0x57, 0x89, 0x5e, 0x4e, 0xe1, 0x07, 0x4a}, + {0x99, 0xf9, 0x0d, 0x98, 0xcb, 0x12, 0xe4, 0x4e, 0x71, 0xc7, 0x6e, 0x3c, 0x6f, 0xd7, + 0x15, 0xa3, 0xfd, 0x77, 0x5c, 0x92, 0xde, 0xed, 0xa5, 0xbb, 0x02, 0x34, 0x31, 0x1d, + 0x39, 0xac, 0x0b, 0x3f, 0x9b, 0xa4, 0x77, 0xc4, 0xcd, 0x58, 0x0b, 0x24, 0x17, 0xf0, + 0x47, 0x64, 0xde, 0xda, 0x38, 0xfd, 0xad, 0x6a, 0xc8, 0xa7, 0x32, 0x8d, 0x92, 0x19, + 0x81, 0xa0, 0xaf, 0x84, 0xed, 0x7a, 0xaf, 0x50, 0xe5, 0x5b, 0xf6, 0x15, 0x01, 0xde, + 0x4f, 0x6e, 0xb2, 0x09, 0x61, 0x21, 0x21, 0x26, 0x98, 0x29, 0xd9, 0xd6, 0xad, 0x0b, + 0x81, 0x05, 0x02, 0x78, 0x06, 0xd0, 0xeb, 0xba, 0x16, 0xa3, 0x21, 0x19}, + {0xfc, 0x70, 0xb8, 0xdf, 0x7e, 0x2f, 0x42, 0x89, 0xbd, 0xb3, 0x76, 0x4f, 0xeb, 0x6b, + 0x29, 0x2c, 0xf7, 0x4d, 0xc2, 0x36, 0xd4, 0xf1, 0x38, 0x07, 0xb0, 0xae, 0x73, 0xe2, + 0x41, 0xdf, 0x58, 0x64, 0x8b, 0xc1, 0xf3, 0xd9, 0x9a, 0xad, 0x5a, 0xd7, 0x9c, 0xc1, + 0xb1, 0x60, 0xef, 0x0e, 0x6a, 0x56, 0xd9, 0x0e, 0x5c, 0x25, 0xac, 0x0b, 0x9a, 0x3e, + 0xf5, 0xc7, 0x62, 0xa0, 0xec, 0x9d, 0x04, 0x7b, 0x83, 0x44, 0x44, 0x35, 0x7a, 0xe3, + 0xcb, 0xdc, 0x93, 0xbe, 0xed, 0x0f, 0x33, 0x79, 0x88, 0x75, 0x87, 0xdd, 0xc5, 0x12, + 0xc3, 0x04, 0x60, 0x78, 0x64, 0x0e, 0x95, 0xc2, 0xcb, 0xdc, 0x93, 0x60}, + {0x6d, 0x70, 0xe0, 0x85, 0x85, 0x9a, 0xf3, 0x1f, 0x33, 0x39, 0xe7, 0xb3, 0xd8, 0xa5, + 0xd0, 0x36, 0x3b, 0x45, 0x8f, 0x71, 0xe1, 0xf2, 0xb9, 0x43, 0x7c, 0xa9, 0x27, 0x48, + 0x08, 0xea, 0xd1, 0x57, 0x4b, 0x03, 0x84, 0x60, 0xbe, 0xee, 0xde, 0x6b, 0x54, 0xb8, + 0x0f, 0x78, 0xb6, 0xc2, 0x99, 0x31, 0x95, 0x06, 0x2d, 0xb6, 0xab, 0x76, 0x33, 0x97, + 0x90, 0x7d, 0x64, 0x8b, 0xc9, 0x80, 0x31, 0x6e, 0x71, 0xb0, 0x28, 0xa1, 0xe7, 0xb6, + 0x7a, 0xee, 0xaa, 0x8b, 0xa8, 0x93, 0x6d, 0x59, 0xc1, 0xa4, 0x30, 0x61, 0x21, 0xb2, + 0x82, 0xde, 0xb4, 0xf7, 0x18, 0xbd, 0x97, 0xdd, 0x9d, 0x99, 0x3e, 0x36}, + {0xc4, 0x1f, 0xee, 0x35, 0xc1, 0x43, 0xa8, 0x96, 0xcf, 0xc8, 0xe4, 0x08, 0x55, 0xb3, + 0x6e, 0x97, 0x30, 0xd3, 0x8c, 0xb5, 0x01, 0x68, 0x2f, 0xb4, 0x2b, 0x05, 0x3a, 0x69, + 0x78, 0x9b, 0xee, 0x48, 0xc6, 0xae, 0x4b, 0xe2, 0xdc, 0x48, 0x18, 0x2f, 0x60, 0xaf, + 0xbc, 0xba, 0x55, 0x72, 0x9b, 0x76, 0x31, 0xe9, 0xef, 0x3c, 0x6e, 0x3c, 0xcb, 0x90, + 0x55, 0xb3, 0xf9, 0xc6, 0x9b, 0x97, 0x1f, 0x23, 0xc6, 0xf3, 0x2a, 0xcc, 0x4b, 0xde, + 0x31, 0x5c, 0x1f, 0x8d, 0x20, 0xfe, 0x30, 0xb0, 0x4b, 0xb0, 0x66, 0xb4, 0x4f, 0xc1, + 0x09, 0x70, 0x8d, 0xb7, 0x13, 0x24, 0x79, 0x08, 0x9b, 0xfa, 0x9b, 0x07}, + {0xf4, 0x0d, 0x30, 0xda, 0x51, 0x3a, 0x90, 0xe3, 0xb0, 0x5a, 0xa9, 0x3d, 0x23, 0x64, + 0x39, 0x84, 0x80, 0x64, 0x35, 0x0b, 0x2d, 0xf1, 0x3c, 0xed, 0x94, 0x71, 0x81, 0x84, + 0xf6, 0x77, 0x8c, 0x03, 0x45, 0x42, 0xd5, 0xa2, 0x80, 0xed, 0xc9, 0xf3, 0x52, 0x39, + 0xf6, 0x77, 0x78, 0x8b, 0xa0, 0x0a, 0x75, 0x54, 0x08, 0xd1, 0x63, 0xac, 0x6d, 0xd7, + 0x6b, 0x63, 0x70, 0x94, 0x15, 0xfb, 0xf4, 0x1e, 0xec, 0x7b, 0x16, 0x5b, 0xe6, 0x5e, + 0x4e, 0x85, 0xc2, 0xcd, 0xd0, 0x96, 0x42, 0x0a, 0x59, 0x59, 0x99, 0x21, 0x10, 0x98, + 0x34, 0xdf, 0xb2, 0x72, 0x56, 0xff, 0x0b, 0x4a, 0x2a, 0xe9, 0x5e, 0x57}, + {0xcf, 0x2f, 0x18, 0x8a, 0x90, 0x80, 0xc0, 0xd4, 0xbd, 0x9d, 0x48, 0x99, 0xc2, 0x70, + 0xe1, 0x30, 0xde, 0x33, 0xf7, 0x52, 0x57, 0xbd, 0xba, 0x05, 0x00, 0xfd, 0xd3, 0x2c, + 0x11, 0xe7, 0xd4, 0x43, 0x01, 0xd8, 0xa4, 0x0a, 0x45, 0xbc, 0x46, 0x5d, 0xd8, 0xb9, + 0x33, 0xa5, 0x27, 0x12, 0xaf, 0xc3, 0xc2, 0x06, 0x89, 0x2b, 0x26, 0x3b, 0x9e, 0x38, + 0x1b, 0x58, 0x2f, 0x38, 0x7e, 0x1e, 0x0a, 0x20, 0xc5, 0x3a, 0xf9, 0xea, 0x67, 0xb9, + 0x8d, 0x51, 0xc0, 0x52, 0x66, 0x05, 0x9b, 0x98, 0xbc, 0x71, 0xf5, 0x97, 0x71, 0x56, + 0xd9, 0x85, 0x2b, 0xfe, 0x38, 0x4e, 0x1e, 0x65, 0x52, 0xca, 0x0e, 0x05}, + {0x9c, 0x0c, 0x3f, 0x45, 0xde, 0x1a, 0x43, 0xc3, 0x9b, 0x3b, 0x70, 0xff, 0x5e, 0x04, + 0xf5, 0xe9, 0x3d, 0x7b, 0x84, 0xed, 0xc9, 0x7a, 0xd9, 0xfc, 0xc6, 0xf4, 0x58, 0x1c, + 0xc2, 0xe6, 0x0e, 0x4b, 0xea, 0x68, 0xe6, 0x60, 0x76, 0x39, 0xac, 0x97, 0x97, 0xb4, + 0x3a, 0x15, 0xfe, 0xbb, 0x19, 0x9b, 0x9f, 0xa7, 0xec, 0x34, 0xb5, 0x79, 0xb1, 0x4c, + 0x57, 0xae, 0x31, 0xa1, 0x9f, 0xc0, 0x51, 0x61, 0x96, 0x5d, 0xf0, 0xfd, 0x0d, 0x5c, + 0xf5, 0x3a, 0x7a, 0xee, 0xb4, 0x2a, 0xe0, 0x2e, 0x26, 0xdd, 0x09, 0x17, 0x17, 0x12, + 0x87, 0xbb, 0xb2, 0x11, 0x0b, 0x03, 0x0f, 0x80, 0xfa, 0x24, 0xef, 0x1f}, + {0x96, 0x31, 0xa7, 0x1a, 0xfb, 0x53, 0xd6, 0x37, 0x18, 0x64, 0xd7, 0x3f, 0x30, 0x95, + 0x94, 0x0f, 0xb2, 0x17, 0x3a, 0xfb, 0x09, 0x0b, 0x20, 0xad, 0x3e, 0x61, 0xc8, 0x2f, + 0x29, 0x49, 0x4d, 0x54, 0x86, 0x6b, 0x97, 0x30, 0xf5, 0xaf, 0xd2, 0x22, 0x04, 0x46, + 0xd2, 0xc2, 0x06, 0xb8, 0x90, 0x8d, 0xe5, 0xba, 0xe5, 0x4d, 0x6c, 0x89, 0xa1, 0xdc, + 0x17, 0x0c, 0x34, 0xc8, 0xe6, 0x5f, 0x00, 0x28, 0x88, 0x86, 0x52, 0x34, 0x9f, 0xba, + 0xef, 0x6a, 0xa1, 0x7d, 0x10, 0x25, 0x94, 0xff, 0x1b, 0x5c, 0x36, 0x4b, 0xd9, 0x66, + 0xcd, 0xbb, 0x5b, 0xf7, 0xfa, 0x6d, 0x31, 0x0f, 0x93, 0x72, 0xe4, 0x72}, + {0x4f, 0x08, 0x81, 0x97, 0x8c, 0x20, 0x95, 0x26, 0xe1, 0x0e, 0x45, 0x23, 0x0b, 0x2a, + 0x50, 0xb1, 0x02, 0xde, 0xef, 0x03, 0xa6, 0xae, 0x9d, 0xfd, 0x4c, 0xa3, 0x33, 0x27, + 0x8c, 0x2e, 0x9d, 0x5a, 0x27, 0x76, 0x2a, 0xd3, 0x35, 0xf6, 0xf3, 0x07, 0xf0, 0x66, + 0x65, 0x5f, 0x86, 0x4d, 0xaa, 0x7a, 0x50, 0x44, 0xd0, 0x28, 0x97, 0xe7, 0x85, 0x3c, + 0x38, 0x64, 0xe0, 0x0f, 0x00, 0x7f, 0xee, 0x1f, 0xe5, 0xf7, 0xdb, 0x03, 0xda, 0x05, + 0x53, 0x76, 0xbd, 0xcd, 0x34, 0x14, 0x49, 0xf2, 0xda, 0xa4, 0xec, 0x88, 0x4a, 0xd2, + 0xcd, 0xd5, 0x4a, 0x7b, 0x43, 0x05, 0x04, 0xee, 0x51, 0x40, 0xf9, 0x00}, + {0xb2, 0x30, 0xd3, 0xc3, 0x23, 0x6b, 0x35, 0x8d, 0x06, 0x1b, 0x47, 0xb0, 0x9b, 0x8b, + 0x1c, 0xf2, 0x3c, 0xb8, 0x42, 0x6e, 0x6c, 0x31, 0x6c, 0xb3, 0x0d, 0xb1, 0xea, 0x8b, + 0x7e, 0x9c, 0xd7, 0x07, 0x53, 0x97, 0xaf, 0x07, 0xbb, 0x93, 0xef, 0xd7, 0xa7, 0x66, + 0xb7, 0x3d, 0xcf, 0xd0, 0x3e, 0x58, 0xc5, 0x1e, 0x0b, 0x6e, 0xbf, 0x98, 0x69, 0xce, + 0x52, 0x04, 0xd4, 0x5d, 0xd2, 0xff, 0xb7, 0x47, 0x12, 0xdd, 0x08, 0xbc, 0x9c, 0xfb, + 0xfb, 0x87, 0x9b, 0xc2, 0xee, 0xe1, 0x3a, 0x6b, 0x06, 0x8a, 0xbf, 0xc1, 0x1f, 0xdb, + 0x2b, 0x24, 0x57, 0x0d, 0xb6, 0x4b, 0xa6, 0x5e, 0xa3, 0x20, 0x35, 0x1c}, + {0x4a, 0xa3, 0xcb, 0xbc, 0xa6, 0x53, 0xd2, 0x80, 0x9b, 0x21, 0x38, 0x38, 0xa1, 0xc3, + 0x61, 0x3e, 0x96, 0xe3, 0x82, 0x98, 0x01, 0xb6, 0xc3, 0x90, 0x6f, 0xe6, 0x0e, 0x5d, + 0x77, 0x05, 0x3d, 0x1c, 0x59, 0xc0, 0x6b, 0x21, 0x40, 0x6f, 0xa8, 0xcd, 0x7e, 0xd8, + 0xbc, 0x12, 0x1d, 0x23, 0xbb, 0x1f, 0x90, 0x09, 0xc7, 0x17, 0x9e, 0x6a, 0x95, 0xb4, + 0x55, 0x2e, 0xd1, 0x66, 0x3b, 0x0c, 0x75, 0x38, 0x1a, 0xe5, 0x22, 0x94, 0x40, 0xf1, + 0x2e, 0x69, 0x71, 0xf6, 0x5d, 0x2b, 0x3c, 0xc7, 0xc0, 0xcb, 0x29, 0xe0, 0x4c, 0x74, + 0xe7, 0x4f, 0x01, 0x21, 0x7c, 0x48, 0x30, 0xd3, 0xc7, 0xe2, 0x21, 0x06}, + {0x8d, 0x83, 0x59, 0x82, 0xcc, 0x60, 0x98, 0xaf, 0xdc, 0x9a, 0x9f, 0xc6, 0xc1, 0x48, + 0xea, 0x90, 0x30, 0x1e, 0x58, 0x65, 0x37, 0x48, 0x26, 0x65, 0xbc, 0xa5, 0xd3, 0x7b, + 0x09, 0xd6, 0x07, 0x00, 0xf3, 0xf0, 0xdb, 0xb0, 0x96, 0x17, 0xae, 0xb7, 0x96, 0xe1, + 0x7c, 0xe1, 0xb9, 0xaf, 0xdf, 0x54, 0xb4, 0xa3, 0xaa, 0xe9, 0x71, 0x30, 0x92, 0x25, + 0x9d, 0x2e, 0x00, 0xa1, 0x9c, 0x58, 0x8e, 0x5d, 0x4b, 0xa9, 0x42, 0x08, 0x95, 0x1d, + 0xbf, 0xc0, 0x3e, 0x2e, 0x8f, 0x58, 0x63, 0xc3, 0xd3, 0xb2, 0xef, 0xe2, 0x51, 0xbb, + 0x38, 0x14, 0x96, 0x0a, 0x86, 0xbf, 0x1c, 0x3c, 0x78, 0xd7, 0x83, 0x15}, + {0xe1, 0x7a, 0xa2, 0x5d, 0xef, 0xa2, 0xee, 0xec, 0x74, 0x01, 0x67, 0x55, 0x14, 0x3a, + 0x7c, 0x59, 0x7a, 0x16, 0x09, 0x66, 0x12, 0x2a, 0xa6, 0xc9, 0x70, 0x8f, 0xed, 0x81, + 0x2e, 0x5f, 0x2a, 0x25, 0xc7, 0x28, 0x9d, 0xcc, 0x04, 0x47, 0x03, 0x90, 0x8f, 0xc5, + 0x2c, 0xf7, 0x9e, 0x67, 0x1b, 0x1d, 0x26, 0x87, 0x5b, 0xbe, 0x5f, 0x2b, 0xe1, 0x16, + 0x0a, 0x58, 0xc5, 0x83, 0x4e, 0x06, 0x58, 0x49, 0x0d, 0xe8, 0x66, 0x50, 0x26, 0x94, + 0x28, 0x0d, 0x6b, 0x8c, 0x7c, 0x30, 0x85, 0xf7, 0xc3, 0xfc, 0xfd, 0x12, 0x11, 0x0c, + 0x78, 0xda, 0x53, 0x1b, 0x88, 0xb3, 0x43, 0xd8, 0x0b, 0x17, 0x9c, 0x07}, + {0xff, 0x6f, 0xfa, 0x64, 0xe4, 0xec, 0x06, 0x05, 0x23, 0xe5, 0x05, 0x62, 0x1e, 0x43, + 0xe3, 0xbe, 0x42, 0xea, 0xb8, 0x51, 0x24, 0x42, 0x79, 0x35, 0x00, 0xfb, 0xc9, 0x4a, + 0xe3, 0x05, 0xec, 0x6d, 0x56, 0xd0, 0xd5, 0xc0, 0x50, 0xcd, 0xd6, 0xcd, 0x3b, 0x57, + 0x03, 0xbb, 0x6d, 0x68, 0xf7, 0x9a, 0x48, 0xef, 0xc3, 0xf3, 0x3f, 0x72, 0xa6, 0x3c, + 0xcc, 0x8a, 0x7b, 0x31, 0xd7, 0xc0, 0x68, 0x67, 0xb3, 0xc1, 0x55, 0xf1, 0xe5, 0x25, + 0xb6, 0x94, 0x91, 0x7b, 0x7b, 0x99, 0xa7, 0xf3, 0x7b, 0x41, 0x00, 0x26, 0x6b, 0x6d, + 0xdc, 0xbd, 0x2c, 0xc2, 0xf4, 0x52, 0xcd, 0xdd, 0x14, 0x5e, 0x44, 0x51}, + {0x51, 0x49, 0x14, 0x3b, 0x4b, 0x2b, 0x50, 0x57, 0xb3, 0xbc, 0x4b, 0x44, 0x6b, 0xff, + 0x67, 0x8e, 0xdb, 0x85, 0x63, 0x16, 0x27, 0x69, 0xbd, 0xb8, 0xc8, 0x95, 0x92, 0xe3, + 0x31, 0x6f, 0x18, 0x13, 0x55, 0xa4, 0xbe, 0x2b, 0xab, 0x47, 0x31, 0x89, 0x29, 0x91, + 0x07, 0x92, 0x4f, 0xa2, 0x53, 0x8c, 0xa7, 0xf7, 0x30, 0xbe, 0x48, 0xf9, 0x49, 0x4b, + 0x3d, 0xd4, 0x4f, 0x6e, 0x08, 0x90, 0xe9, 0x12, 0x2e, 0xbb, 0xdf, 0x7f, 0xb3, 0x96, + 0x0c, 0xf1, 0xf9, 0xea, 0x1c, 0x12, 0x5e, 0x93, 0x9a, 0x9f, 0x3f, 0x98, 0x5b, 0x3a, + 0xc4, 0x36, 0x11, 0xdf, 0xaf, 0x99, 0x3e, 0x5d, 0xf0, 0xe3, 0xb2, 0x77}, + {0xde, 0xc4, 0x2e, 0x9c, 0xc5, 0xa9, 0x6f, 0x29, 0xcb, 0xf3, 0x84, 0x4f, 0xbf, 0x61, + 0x8b, 0xbc, 0x08, 0xf9, 0xa8, 0x17, 0xd9, 0x06, 0x77, 0x1c, 0x5d, 0x25, 0xd3, 0x7a, + 0xfc, 0x95, 0xb7, 0x63, 0xa4, 0xb0, 0xdd, 0x12, 0x9c, 0x63, 0x98, 0xd5, 0x6b, 0x86, + 0x24, 0xc0, 0x30, 0x9f, 0xd1, 0xa5, 0x60, 0xe4, 0xfc, 0x58, 0x03, 0x2f, 0x7c, 0xd1, + 0x8a, 0x5e, 0x09, 0x2e, 0x15, 0x95, 0xa1, 0x07, 0xc8, 0x5f, 0x9e, 0x38, 0x02, 0x8f, + 0x36, 0xa8, 0x3b, 0xe4, 0x8d, 0xcf, 0x02, 0x3b, 0x43, 0x90, 0x43, 0x26, 0x41, 0xc5, + 0x5d, 0xfd, 0xa1, 0xaf, 0x37, 0x01, 0x2f, 0x03, 0x3d, 0xe8, 0x8f, 0x3e}, + {0x94, 0xa2, 0x70, 0x05, 0xb9, 0x15, 0x8b, 0x2f, 0x49, 0x45, 0x08, 0x67, 0x70, 0x42, + 0xf2, 0x94, 0x84, 0xfd, 0xbb, 0x61, 0xe1, 0x5a, 0x1c, 0xde, 0x07, 0x40, 0xac, 0x7f, + 0x79, 0x3b, 0xba, 0x75, 0x3c, 0xd1, 0xef, 0xe8, 0x8d, 0x4c, 0x70, 0x08, 0x31, 0x37, + 0xe0, 0x33, 0x8e, 0x1a, 0xc5, 0xdf, 0xe3, 0xcd, 0x60, 0x12, 0xa5, 0x5d, 0x9d, 0xa5, + 0x86, 0x8c, 0x25, 0xa6, 0x99, 0x08, 0xd6, 0x22, 0x96, 0xd1, 0xcd, 0x70, 0xc0, 0xdb, + 0x39, 0x62, 0x9a, 0x8a, 0x7d, 0x6c, 0x8b, 0x8a, 0xfe, 0x60, 0x60, 0x12, 0x40, 0xeb, + 0xbc, 0x47, 0x88, 0xb3, 0x5e, 0x9e, 0x77, 0x87, 0x7b, 0xd0, 0x04, 0x09}, + {0x9c, 0x91, 0xba, 0xdd, 0xd4, 0x1f, 0xce, 0xb4, 0xaa, 0x8d, 0x4c, 0xc7, 0x3e, 0xdb, + 0x31, 0xcf, 0x51, 0xcc, 0x86, 0xad, 0x63, 0xcc, 0x63, 0x2c, 0x07, 0xde, 0x1d, 0xbc, + 0x3f, 0x14, 0xe2, 0x43, 0xb9, 0x40, 0xf9, 0x48, 0x66, 0x2d, 0x32, 0xf4, 0x39, 0x0c, + 0x2d, 0xbd, 0x0c, 0x2f, 0x95, 0x06, 0x31, 0xf9, 0x81, 0xa0, 0xad, 0x97, 0x76, 0x16, + 0x6c, 0x2a, 0xf7, 0xba, 0xce, 0xaa, 0x40, 0x62, 0xa0, 0x95, 0xa2, 0x5b, 0x9c, 0x74, + 0x34, 0xf8, 0x5a, 0xd2, 0x37, 0xca, 0x5b, 0x7c, 0x94, 0xd6, 0x6a, 0x31, 0xc9, 0xe7, + 0xa7, 0x3b, 0xf1, 0x66, 0xac, 0x0c, 0xb4, 0x8d, 0x23, 0xaf, 0xbd, 0x56}, + {0xeb, 0x33, 0x35, 0xf5, 0xe3, 0xb9, 0x2a, 0x36, 0x40, 0x3d, 0xb9, 0x6e, 0xd5, 0x68, + 0x85, 0x33, 0x72, 0x55, 0x5a, 0x1d, 0x52, 0x14, 0x0e, 0x9e, 0x18, 0x13, 0x74, 0x83, + 0x6d, 0xa8, 0x24, 0x1d, 0xb2, 0x3b, 0x9d, 0xc1, 0x6c, 0xd3, 0x10, 0x13, 0xb9, 0x86, + 0x23, 0x62, 0xb7, 0x6b, 0x2a, 0x06, 0x5c, 0x4f, 0xa1, 0xd7, 0x91, 0x85, 0x9b, 0x7c, + 0x54, 0x57, 0x1e, 0x7e, 0x50, 0x31, 0xaa, 0x03, 0x1f, 0xce, 0xd4, 0xff, 0x48, 0x76, + 0xec, 0xf4, 0x1c, 0x8c, 0xac, 0x54, 0xf0, 0xea, 0x45, 0xe0, 0x7c, 0x35, 0x09, 0x1d, + 0x82, 0x25, 0xd2, 0x88, 0x59, 0x48, 0xeb, 0x9a, 0xdc, 0x61, 0xb2, 0x43}, + {0xbb, 0x79, 0xbb, 0x88, 0x19, 0x1e, 0x5b, 0xe5, 0x9d, 0x35, 0x7a, 0xc1, 0x7d, 0xd0, + 0x9e, 0xa0, 0x33, 0xea, 0x3d, 0x60, 0xe2, 0x2e, 0x2c, 0xb0, 0xc2, 0x6b, 0x27, 0x5b, + 0xcf, 0x55, 0x60, 0x32, 0x64, 0x13, 0x95, 0x6c, 0x8b, 0x3d, 0x51, 0x19, 0x7b, 0xf4, + 0x0b, 0x00, 0x26, 0x71, 0xfe, 0x94, 0x67, 0x95, 0x4f, 0xd5, 0xdd, 0x10, 0x8d, 0x02, + 0x64, 0x09, 0x94, 0x42, 0xe2, 0xd5, 0xb4, 0x02, 0xf2, 0x8d, 0xd1, 0x28, 0xcb, 0x55, + 0xa1, 0xb4, 0x08, 0xe5, 0x6c, 0x18, 0x46, 0x46, 0xcc, 0xea, 0x89, 0x43, 0x82, 0x6c, + 0x93, 0xf4, 0x9c, 0xc4, 0x10, 0x34, 0x5d, 0xae, 0x09, 0xc8, 0xa6, 0x27}, + {0x88, 0xb1, 0x0d, 0x1f, 0xcd, 0xeb, 0xa6, 0x8b, 0xe8, 0x5b, 0x5a, 0x67, 0x3a, 0xd7, + 0xd3, 0x37, 0x5a, 0x58, 0xf5, 0x15, 0xa3, 0xdf, 0x2e, 0xf2, 0x7e, 0xa1, 0x60, 0xff, + 0x74, 0x71, 0xb6, 0x2c, 0x54, 0x69, 0x3d, 0xc4, 0x0a, 0x27, 0x2c, 0xcd, 0xb2, 0xca, + 0x66, 0x6a, 0x57, 0x3e, 0x4a, 0xdd, 0x6c, 0x03, 0xd7, 0x69, 0x24, 0x59, 0xfa, 0x79, + 0x99, 0x25, 0x8c, 0x3d, 0x60, 0x03, 0x15, 0x22, 0xd0, 0xe1, 0x0b, 0x39, 0xf9, 0xcd, + 0xee, 0x59, 0xf1, 0xe3, 0x8c, 0x72, 0x44, 0x20, 0x42, 0xa9, 0xf4, 0xf0, 0x94, 0x7a, + 0x66, 0x1c, 0x89, 0x82, 0x36, 0xf4, 0x90, 0x38, 0xb7, 0xf4, 0x1d, 0x7b}, + {0x24, 0xa2, 0xb2, 0xb3, 0xe0, 0xf2, 0x92, 0xe4, 0x60, 0x11, 0x55, 0x2b, 0x06, 0x9e, + 0x6c, 0x7c, 0x0e, 0x7b, 0x7f, 0x0d, 0xe2, 0x8f, 0xeb, 0x15, 0x92, 0x59, 0xfc, 0x58, + 0x26, 0xef, 0xfc, 0x61, 0x8c, 0xf5, 0xf8, 0x07, 0x18, 0x22, 0x2e, 0x5f, 0xd4, 0x09, + 0x94, 0xd4, 0x9f, 0x5c, 0x55, 0xe3, 0x30, 0xa6, 0xb6, 0x1f, 0x8d, 0xa8, 0xaa, 0xb2, + 0x3d, 0xe0, 0x52, 0xd3, 0x45, 0x82, 0x69, 0x68, 0x7a, 0x18, 0x18, 0x2a, 0x85, 0x5d, + 0xb1, 0xdb, 0xd7, 0xac, 0xdd, 0x86, 0xd3, 0xaa, 0xe4, 0xf3, 0x82, 0xc4, 0xf6, 0x0f, + 0x81, 0xe2, 0xba, 0x44, 0xcf, 0x01, 0xaf, 0x3d, 0x47, 0x4c, 0xcf, 0x46}, + {0xf9, 0xe5, 0xc4, 0x9e, 0xed, 0x25, 0x65, 0x42, 0x03, 0x33, 0x90, 0x16, 0x01, 0xda, + 0x5e, 0x0e, 0xdc, 0xca, 0xe5, 0xcb, 0xf2, 0xa7, 0xb1, 0x72, 0x40, 0x5f, 0xeb, 0x14, + 0xcd, 0x7b, 0x38, 0x29, 0x40, 0x81, 0x49, 0xf1, 0xa7, 0x6e, 0x3c, 0x21, 0x54, 0x48, + 0x2b, 0x39, 0xf8, 0x7e, 0x1e, 0x7c, 0xba, 0xce, 0x29, 0x56, 0x8c, 0xc3, 0x88, 0x24, + 0xbb, 0xc5, 0x8c, 0x0d, 0xe5, 0xaa, 0x65, 0x10, 0x57, 0x0d, 0x20, 0xdf, 0x25, 0x45, + 0x2c, 0x1c, 0x4a, 0x67, 0xca, 0xbf, 0xd6, 0x2d, 0x3b, 0x5c, 0x30, 0x40, 0x83, 0xe1, + 0xb1, 0xe7, 0x07, 0x0a, 0x16, 0xe7, 0x1c, 0x4f, 0xe6, 0x98, 0xa1, 0x69}, + {0xbc, 0x78, 0x1a, 0xd9, 0xe0, 0xb2, 0x62, 0x90, 0x67, 0x96, 0x50, 0xc8, 0x9c, 0x88, + 0xc9, 0x47, 0xb8, 0x70, 0x50, 0x40, 0x66, 0x4a, 0xf5, 0x9d, 0xbf, 0xa1, 0x93, 0x24, + 0xa9, 0xe6, 0x69, 0x73, 0xed, 0xca, 0xc5, 0xdc, 0x34, 0x44, 0x01, 0xe1, 0x33, 0xfb, + 0x84, 0x3c, 0x96, 0x5d, 0xed, 0x47, 0xe7, 0xa0, 0x86, 0xed, 0x76, 0x95, 0x01, 0x70, + 0xe4, 0xf9, 0x67, 0xd2, 0x7b, 0x69, 0xb2, 0x25, 0x64, 0x68, 0x98, 0x13, 0xfb, 0x3f, + 0x67, 0x9d, 0xb8, 0xc7, 0x5d, 0x41, 0xd9, 0xfb, 0xa5, 0x3c, 0x5e, 0x3b, 0x27, 0xdf, + 0x3b, 0xcc, 0x4e, 0xe0, 0xd2, 0x4c, 0x4e, 0xb5, 0x3d, 0x68, 0x20, 0x14}, + {0x97, 0xd1, 0x9d, 0x24, 0x1e, 0xbd, 0x78, 0xb4, 0x02, 0xc1, 0x58, 0x5e, 0x00, 0x35, + 0x0c, 0x62, 0x5c, 0xac, 0xba, 0xcc, 0x2f, 0xd3, 0x02, 0xfb, 0x2d, 0xa7, 0x08, 0xf5, + 0xeb, 0x3b, 0xb6, 0x60, 0xd0, 0x5a, 0xcc, 0xc1, 0x6f, 0xbb, 0xee, 0x34, 0x8b, 0xac, + 0x46, 0x96, 0xe9, 0x0c, 0x1b, 0x6a, 0x53, 0xde, 0x6b, 0xa6, 0x49, 0xda, 0xb0, 0xd3, + 0xc1, 0x81, 0xd0, 0x61, 0x41, 0x3b, 0xe8, 0x31, 0x4f, 0x2b, 0x06, 0x9e, 0x12, 0xc7, + 0xe8, 0x97, 0xd8, 0x0a, 0x32, 0x29, 0x4f, 0x8f, 0xe4, 0x49, 0x3f, 0x68, 0x18, 0x6f, + 0x4b, 0xe1, 0xec, 0x5b, 0x17, 0x03, 0x55, 0x2d, 0xb6, 0x1e, 0xcf, 0x55}, + {0x58, 0x3d, 0xc2, 0x65, 0x10, 0x10, 0x79, 0x58, 0x9c, 0x81, 0x94, 0x50, 0x6d, 0x08, + 0x9d, 0x8b, 0xa7, 0x5f, 0xc5, 0x12, 0xa9, 0x2f, 0x40, 0xe2, 0xd4, 0x91, 0x08, 0x57, + 0x64, 0x65, 0x9a, 0x66, 0x52, 0x8c, 0xf5, 0x7d, 0xe3, 0xb5, 0x76, 0x30, 0x36, 0xcc, + 0x99, 0xe7, 0xdd, 0xb9, 0x3a, 0xd7, 0x20, 0xee, 0x13, 0x49, 0xe3, 0x1c, 0x83, 0xbd, + 0x33, 0x01, 0xba, 0x62, 0xaa, 0xfb, 0x56, 0x1a, 0xec, 0xc9, 0x9d, 0x5c, 0x50, 0x6b, + 0x3e, 0x94, 0x1a, 0x37, 0x7c, 0xa7, 0xbb, 0x57, 0x25, 0x30, 0x51, 0x76, 0x34, 0x41, + 0x56, 0xae, 0x73, 0x98, 0x5c, 0x8a, 0xc5, 0x99, 0x67, 0x83, 0xc4, 0x13}, + {0xb9, 0xe1, 0xb3, 0x5a, 0x46, 0x5d, 0x3a, 0x42, 0x61, 0x3f, 0xf1, 0xc7, 0x87, 0xc1, + 0x13, 0xfc, 0xb6, 0xb9, 0xb5, 0xec, 0x64, 0x36, 0xf8, 0x19, 0x07, 0xb6, 0x37, 0xa6, + 0x93, 0x0c, 0xf8, 0x66, 0x80, 0xd0, 0x8b, 0x5d, 0x6a, 0xfb, 0xdc, 0xc4, 0x42, 0x48, + 0x1a, 0x57, 0xec, 0xc4, 0xeb, 0xde, 0x65, 0x53, 0xe5, 0xb8, 0x83, 0xe8, 0xb2, 0xd4, + 0x27, 0xb8, 0xe5, 0xc8, 0x7d, 0xc8, 0xbd, 0x50, 0x11, 0xe1, 0xdf, 0x6e, 0x83, 0x37, + 0x6d, 0x60, 0xd9, 0xab, 0x11, 0xf0, 0x15, 0x3e, 0x35, 0x32, 0x96, 0x3b, 0xb7, 0x25, + 0xc3, 0x3a, 0xb0, 0x64, 0xae, 0xd5, 0x5f, 0x72, 0x44, 0x64, 0xd5, 0x1d}, + {0x7d, 0x12, 0x62, 0x33, 0xf8, 0x7f, 0xa4, 0x8f, 0x15, 0x7c, 0xcd, 0x71, 0xc4, 0x6a, + 0x9f, 0xbc, 0x8b, 0x0c, 0x22, 0x49, 0x43, 0x45, 0x71, 0x6e, 0x2e, 0x73, 0x9f, 0x21, + 0x12, 0x59, 0x64, 0x0e, 0x9a, 0xc8, 0xba, 0x08, 0x00, 0xe6, 0x97, 0xc2, 0xe0, 0xc3, + 0xe1, 0xea, 0x11, 0xea, 0x4c, 0x7d, 0x7c, 0x97, 0xe7, 0x9f, 0xe1, 0x8b, 0xe3, 0xf3, + 0xcd, 0x05, 0xa3, 0x63, 0x0f, 0x45, 0x3a, 0x3a, 0x27, 0x46, 0x39, 0xd8, 0x31, 0x2f, + 0x8f, 0x07, 0x10, 0xa5, 0x94, 0xde, 0x83, 0x31, 0x9d, 0x38, 0x80, 0x6f, 0x99, 0x17, + 0x6d, 0x6c, 0xe3, 0xd1, 0x7b, 0xa8, 0xa9, 0x93, 0x93, 0x8d, 0x8c, 0x31}, + {0x19, 0xfe, 0xff, 0x2a, 0x03, 0x5d, 0x74, 0xf2, 0x66, 0xdb, 0x24, 0x7f, 0x49, 0x3c, + 0x9f, 0x0c, 0xef, 0x98, 0x85, 0xba, 0xe3, 0xd3, 0x98, 0xbc, 0x14, 0x53, 0x1d, 0x9a, + 0x67, 0x7c, 0x4c, 0x22, 0x98, 0xd3, 0x1d, 0xab, 0x29, 0x9e, 0x66, 0x5d, 0x3b, 0x9e, + 0x2d, 0x34, 0x58, 0x16, 0x92, 0xfc, 0xcd, 0x73, 0x59, 0xf3, 0xfd, 0x1d, 0x85, 0x55, + 0xf6, 0x0a, 0x95, 0x25, 0xc3, 0x41, 0x9a, 0x50, 0xe9, 0x25, 0xf9, 0xa6, 0xdc, 0x6e, + 0xc0, 0xbd, 0x33, 0x1f, 0x1b, 0x64, 0xf4, 0xf3, 0x3e, 0x79, 0x89, 0x3e, 0x83, 0x9d, + 0x80, 0x12, 0xec, 0x82, 0x89, 0x13, 0xa1, 0x28, 0x23, 0xf0, 0xbf, 0x05}, + {0x0b, 0xe0, 0xca, 0x23, 0x70, 0x13, 0x32, 0x36, 0x59, 0xcf, 0xac, 0xd1, 0x0a, 0xcf, + 0x4a, 0x54, 0x88, 0x1c, 0x1a, 0xd2, 0x49, 0x10, 0x74, 0x96, 0xa7, 0x44, 0x2a, 0xfa, + 0xc3, 0x8c, 0x0b, 0x78, 0xe4, 0x12, 0xc5, 0x0d, 0xdd, 0xa0, 0x81, 0x68, 0xfe, 0xfa, + 0xa5, 0x44, 0xc8, 0x0d, 0xe7, 0x4f, 0x40, 0x52, 0x4a, 0x8f, 0x6b, 0x8e, 0x74, 0x1f, + 0xea, 0xa3, 0x01, 0xee, 0xcd, 0x77, 0x62, 0x57, 0x5f, 0x30, 0x4f, 0x23, 0xbc, 0x8a, + 0xf3, 0x1e, 0x08, 0xde, 0x05, 0x14, 0xbd, 0x7f, 0x57, 0x9a, 0x0d, 0x2a, 0xe6, 0x34, + 0x14, 0xa5, 0x82, 0x5e, 0xa1, 0xb7, 0x71, 0x62, 0x72, 0x18, 0xf4, 0x5f}, + {0x9d, 0xdb, 0x89, 0x17, 0x0c, 0x08, 0x8e, 0x39, 0xf5, 0x78, 0xe7, 0xf3, 0x25, 0x20, + 0x60, 0xa7, 0x5d, 0x03, 0xbd, 0x06, 0x4c, 0x89, 0x98, 0xfa, 0xbe, 0x66, 0xa9, 0x25, + 0xdc, 0x03, 0x6a, 0x10, 0x40, 0x95, 0xb6, 0x13, 0xe8, 0x47, 0xdb, 0xe5, 0xe1, 0x10, + 0x26, 0x43, 0x3b, 0x2a, 0x5d, 0xf3, 0x76, 0x12, 0x78, 0x38, 0xe9, 0x26, 0x1f, 0xac, + 0x69, 0xcb, 0xa0, 0xa0, 0x8c, 0xdb, 0xd4, 0x29, 0xd0, 0x53, 0x33, 0x33, 0xaf, 0x0a, + 0xad, 0xd9, 0xe5, 0x09, 0xd3, 0xac, 0xa5, 0x9d, 0x66, 0x38, 0xf0, 0xf7, 0x88, 0xc8, + 0x8a, 0x65, 0x57, 0x3c, 0xfa, 0xbe, 0x2c, 0x05, 0x51, 0x8a, 0xb3, 0x4a}, + {0x93, 0xd5, 0x68, 0x67, 0x25, 0x2b, 0x7c, 0xda, 0x13, 0xca, 0x22, 0x44, 0x57, 0xc0, + 0xc1, 0x98, 0x1d, 0xce, 0x0a, 0xca, 0xd5, 0x0b, 0xa8, 0xf1, 0x90, 0xa6, 0x88, 0xc0, + 0xad, 0xd1, 0xcd, 0x29, 0x9c, 0xc0, 0xdd, 0x5f, 0xef, 0xd1, 0xcf, 0xd6, 0xce, 0x5d, + 0x57, 0xf7, 0xfd, 0x3e, 0x2b, 0xe8, 0xc2, 0x34, 0x16, 0x20, 0x5d, 0x6b, 0xd5, 0x25, + 0x9b, 0x2b, 0xed, 0x04, 0xbb, 0xc6, 0x41, 0x30, 0x48, 0xe1, 0x56, 0xd9, 0xf9, 0xf2, + 0xf2, 0x0f, 0x2e, 0x6b, 0x35, 0x9f, 0x75, 0x97, 0xe7, 0xad, 0x5c, 0x02, 0x6c, 0x5f, + 0xbb, 0x98, 0x46, 0x1a, 0x7b, 0x9a, 0x04, 0x14, 0x68, 0xbd, 0x4b, 0x10}, + {0x67, 0xed, 0xf1, 0x68, 0x31, 0xfd, 0xf0, 0x51, 0xc2, 0x3b, 0x6f, 0xd8, 0xcd, 0x1d, + 0x81, 0x2c, 0xde, 0xf2, 0xd2, 0x04, 0x43, 0x5c, 0xdc, 0x44, 0x49, 0x71, 0x2a, 0x09, + 0x57, 0xcc, 0xe8, 0x5b, 0x63, 0xf1, 0x7f, 0xd6, 0x5f, 0x9a, 0x5d, 0xa9, 0x81, 0x56, + 0xc7, 0x4c, 0x9d, 0xe6, 0x2b, 0xe9, 0x57, 0xf2, 0x20, 0xde, 0x4c, 0x02, 0xf8, 0xb7, + 0xf5, 0x2d, 0x07, 0xfb, 0x20, 0x2a, 0x4f, 0x20, 0x79, 0xb0, 0xeb, 0x30, 0x3d, 0x3b, + 0x14, 0xc8, 0x30, 0x2e, 0x65, 0xbd, 0x5a, 0x15, 0x89, 0x75, 0x31, 0x5c, 0x6d, 0x8f, + 0x31, 0x3c, 0x3c, 0x65, 0x1f, 0x16, 0x79, 0xc2, 0x17, 0xfb, 0x70, 0x25}, + {0x75, 0x15, 0xb6, 0x2c, 0x7f, 0x36, 0xfa, 0x3e, 0x6c, 0x02, 0xd6, 0x1c, 0x76, 0x6f, + 0xf9, 0xf5, 0x62, 0x25, 0xb5, 0x65, 0x2a, 0x14, 0xc7, 0xe8, 0xcd, 0x0a, 0x03, 0x53, + 0xea, 0x65, 0xcb, 0x3d, 0x5a, 0x24, 0xb8, 0x0b, 0x55, 0xa9, 0x2e, 0x19, 0xd1, 0x50, + 0x90, 0x8f, 0xa8, 0xfb, 0xe6, 0xc8, 0x35, 0xc9, 0xa4, 0x88, 0x2d, 0xea, 0x86, 0x79, + 0x68, 0x86, 0x01, 0xde, 0x91, 0x5f, 0x1c, 0x24, 0xaa, 0x6c, 0xde, 0x40, 0x29, 0x17, + 0xd8, 0x28, 0x3a, 0x73, 0xd9, 0x22, 0xf0, 0x2c, 0xbf, 0x8f, 0xd1, 0x01, 0x5b, 0x23, + 0xdd, 0xfc, 0xd7, 0x16, 0xe5, 0xf0, 0xcd, 0x5f, 0xdd, 0x0e, 0x42, 0x08}, + {0x4a, 0xfa, 0x62, 0x83, 0xab, 0x20, 0xff, 0xcd, 0x6e, 0x3e, 0x1a, 0xe2, 0xd4, 0x18, + 0xe1, 0x57, 0x2b, 0xe6, 0x39, 0xfc, 0x17, 0x96, 0x17, 0xe3, 0xfd, 0x69, 0x17, 0xbc, + 0xef, 0x53, 0x9a, 0x0d, 0xce, 0x10, 0xf4, 0x04, 0x4e, 0xc3, 0x58, 0x03, 0x85, 0x06, + 0x6e, 0x27, 0x5a, 0x5b, 0x13, 0xb6, 0x21, 0x15, 0xb9, 0xeb, 0xc7, 0x70, 0x96, 0x5d, + 0x9c, 0x88, 0xdb, 0x21, 0xf3, 0x54, 0xd6, 0x04, 0xd5, 0xb5, 0xbd, 0xdd, 0x16, 0xc1, + 0x7d, 0x5e, 0x2d, 0xdd, 0xa5, 0x8d, 0xb6, 0xde, 0x54, 0x29, 0x92, 0xa2, 0x34, 0x33, + 0x17, 0x08, 0xb6, 0x1c, 0xd7, 0x1a, 0x99, 0x18, 0x26, 0x4f, 0x7a, 0x4a}, + {0x95, 0x5f, 0xb1, 0x5f, 0x02, 0x18, 0xa7, 0xf4, 0x8f, 0x1b, 0x5c, 0x6b, 0x34, 0x5f, + 0xf6, 0x3d, 0x12, 0x11, 0xe0, 0x00, 0x85, 0xf0, 0xfc, 0xcd, 0x48, 0x18, 0xd3, 0xdd, + 0x4c, 0x0c, 0xb5, 0x11, 0x4b, 0x2a, 0x37, 0xaf, 0x91, 0xb2, 0xc3, 0x24, 0xf2, 0x47, + 0x81, 0x71, 0x70, 0x82, 0xda, 0x93, 0xf2, 0x9e, 0x89, 0x86, 0x64, 0x85, 0x84, 0xdd, + 0x33, 0xee, 0xe0, 0x23, 0x42, 0x31, 0x96, 0x4a, 0xd6, 0xff, 0xa4, 0x08, 0x44, 0x27, + 0xe8, 0xa6, 0xd9, 0x76, 0x15, 0x9c, 0x7e, 0x17, 0x8e, 0x73, 0xf2, 0xb3, 0x02, 0x3d, + 0xb6, 0x48, 0x33, 0x77, 0x51, 0xcc, 0x6b, 0xce, 0x4d, 0xce, 0x4b, 0x4f}, + {0x84, 0x25, 0x24, 0xe2, 0x5a, 0xce, 0x1f, 0xa7, 0x9e, 0x8a, 0xf5, 0x92, 0x56, 0x72, + 0xea, 0x26, 0xf4, 0x3c, 0xea, 0x1c, 0xd7, 0x09, 0x1a, 0xd2, 0xe6, 0x01, 0x1c, 0xb7, + 0x14, 0xdd, 0xfc, 0x73, 0x6f, 0x0b, 0x9d, 0xc4, 0x6e, 0x61, 0xe2, 0x30, 0x17, 0x23, + 0xec, 0xca, 0x8f, 0x71, 0x56, 0xe4, 0xa6, 0x4f, 0x6b, 0xf2, 0x9b, 0x40, 0xeb, 0x48, + 0x37, 0x5f, 0x59, 0x61, 0xe5, 0xce, 0x42, 0x30, 0x41, 0xac, 0x9b, 0x44, 0x79, 0x70, + 0x7e, 0x42, 0x0a, 0x31, 0xe2, 0xbc, 0x6d, 0xe3, 0x5a, 0x85, 0x7c, 0x1a, 0x84, 0x5f, + 0x21, 0x76, 0xae, 0x4c, 0xd6, 0xe1, 0x9c, 0x9a, 0x0c, 0x74, 0x9e, 0x38}, + {0xce, 0xb9, 0xdc, 0x34, 0xae, 0xb3, 0xfc, 0x64, 0xad, 0xd0, 0x48, 0xe3, 0x23, 0x03, + 0x50, 0x97, 0x1b, 0x38, 0xc6, 0x62, 0x7d, 0xf0, 0xb3, 0x45, 0x88, 0x67, 0x5a, 0x46, + 0x79, 0x53, 0x54, 0x61, 0x28, 0xac, 0x0e, 0x57, 0xf6, 0x78, 0xbd, 0xc9, 0xe1, 0x9c, + 0x91, 0x27, 0x32, 0x0b, 0x5b, 0xe5, 0xed, 0x91, 0x9b, 0xa1, 0xab, 0x3e, 0xfc, 0x65, + 0x90, 0x36, 0x26, 0xd6, 0xe5, 0x25, 0xc4, 0x25, 0x6e, 0xde, 0xd7, 0xf1, 0xa6, 0x06, + 0x3e, 0x3f, 0x08, 0x23, 0x06, 0x8e, 0x27, 0x76, 0xf9, 0x3e, 0x77, 0x6c, 0x8a, 0x4e, + 0x26, 0xf6, 0x14, 0x8c, 0x59, 0x47, 0x48, 0x15, 0x89, 0xa0, 0x39, 0x65}, + {0x73, 0xf7, 0xd2, 0xc3, 0x74, 0x1f, 0xd2, 0xe9, 0x45, 0x68, 0xc4, 0x25, 0x41, 0x54, + 0x50, 0xc1, 0x33, 0x9e, 0xb9, 0xf9, 0xe8, 0x5c, 0x4e, 0x62, 0x6c, 0x18, 0xcd, 0xc5, + 0xaa, 0xe4, 0xc5, 0x11, 0x19, 0x4a, 0xbb, 0x14, 0xd4, 0xdb, 0xc4, 0xdd, 0x8e, 0x4f, + 0x42, 0x98, 0x3c, 0xbc, 0xb2, 0x19, 0x69, 0x71, 0xca, 0x36, 0xd7, 0x9f, 0xa8, 0x48, + 0x90, 0xbd, 0x19, 0xf0, 0x0e, 0x32, 0x65, 0x0f, 0xc6, 0xe0, 0xfd, 0xca, 0xb1, 0xd1, + 0x86, 0xd4, 0x81, 0x51, 0x3b, 0x16, 0xe3, 0xe6, 0x3f, 0x4f, 0x9a, 0x93, 0xf2, 0xfa, + 0x0d, 0xaf, 0xa8, 0x59, 0x2a, 0x07, 0x33, 0xec, 0xbd, 0xc7, 0xab, 0x4c}, + {0x2e, 0x0a, 0x9c, 0x08, 0x24, 0x96, 0x9e, 0x23, 0x38, 0x47, 0xfe, 0x3a, 0xc0, 0xc4, + 0x48, 0xc7, 0x2a, 0xa1, 0x4f, 0x76, 0x2a, 0xed, 0xdb, 0x17, 0x82, 0x85, 0x1c, 0x32, + 0xf0, 0x93, 0x9b, 0x63, 0x89, 0xd2, 0x78, 0x3f, 0x8f, 0x78, 0x8f, 0xc0, 0x9f, 0x4d, + 0x40, 0xa1, 0x2c, 0xa7, 0x30, 0xfe, 0x9d, 0xcc, 0x65, 0xcf, 0xfc, 0x8b, 0x77, 0xf2, + 0x21, 0x20, 0xcb, 0x5a, 0x16, 0x98, 0xe4, 0x7e, 0xc3, 0xa1, 0x11, 0x91, 0xe3, 0x08, + 0xd5, 0x7b, 0x89, 0x74, 0x90, 0x80, 0xd4, 0x90, 0x2b, 0x2b, 0x19, 0xfd, 0x72, 0xae, + 0xc2, 0xae, 0xd2, 0xe7, 0xa6, 0x02, 0xb6, 0x85, 0x3c, 0x49, 0xdf, 0x0e}, + {0x68, 0x5a, 0x9b, 0x59, 0x58, 0x81, 0xcc, 0xae, 0x0e, 0xe2, 0xad, 0xeb, 0x0f, 0x4f, + 0x57, 0xea, 0x07, 0x7f, 0xb6, 0x22, 0x74, 0x1d, 0xe4, 0x4f, 0xb4, 0x4f, 0x9d, 0x01, + 0xe3, 0x92, 0x3b, 0x40, 0x13, 0x41, 0x76, 0x84, 0xd2, 0xc4, 0x67, 0x67, 0x35, 0xf8, + 0xf5, 0xf7, 0x3f, 0x40, 0x90, 0xa0, 0xde, 0xbe, 0xe6, 0xca, 0xfa, 0xcf, 0x8f, 0x1c, + 0x69, 0xa3, 0xdf, 0xd1, 0x54, 0x0c, 0xc0, 0x04, 0xf8, 0x5c, 0x46, 0x8b, 0x81, 0x2f, + 0xc2, 0x4d, 0xf8, 0xef, 0x80, 0x14, 0x5a, 0xf3, 0xa0, 0x71, 0x57, 0xd6, 0xc7, 0x04, + 0xad, 0xbf, 0xe8, 0xae, 0xf4, 0x76, 0x61, 0xb2, 0x2a, 0xb1, 0x5b, 0x35}, + {0xf4, 0xbb, 0x93, 0x74, 0xcc, 0x64, 0x1e, 0xa7, 0xc3, 0xb0, 0xa3, 0xec, 0xd9, 0x84, + 0xbd, 0xe5, 0x85, 0xe7, 0x05, 0xfa, 0x0c, 0xc5, 0x6b, 0x0a, 0x12, 0xc3, 0x2e, 0x18, + 0x32, 0x81, 0x9b, 0x0f, 0x18, 0x73, 0x8c, 0x5a, 0xc7, 0xda, 0x01, 0xa3, 0x11, 0xaa, + 0xce, 0xb3, 0x9d, 0x03, 0x90, 0xed, 0x2d, 0x3f, 0xae, 0x3b, 0xbf, 0x7c, 0x07, 0x6f, + 0x8e, 0xad, 0x52, 0xe0, 0xf8, 0xea, 0x18, 0x75, 0x32, 0x6c, 0x7f, 0x1b, 0xc4, 0x59, + 0x88, 0xa4, 0x98, 0x32, 0x38, 0xf4, 0xbc, 0x60, 0x2d, 0x0f, 0xd9, 0xd1, 0xb1, 0xc9, + 0x29, 0xa9, 0x15, 0x18, 0xc4, 0x55, 0x17, 0xbb, 0x1b, 0x87, 0xc3, 0x47}, + {0x48, 0x4f, 0xec, 0x71, 0x97, 0x53, 0x44, 0x51, 0x6e, 0x5d, 0x8c, 0xc9, 0x7d, 0xb1, + 0x05, 0xf8, 0x6b, 0xc6, 0xc3, 0x47, 0x1a, 0xc1, 0x62, 0xf7, 0xdc, 0x99, 0x46, 0x76, + 0x85, 0x9b, 0xb8, 0x00, 0xb0, 0x66, 0x50, 0xc8, 0x50, 0x5d, 0xe6, 0xfb, 0xb0, 0x99, + 0xa2, 0xb3, 0xb0, 0xc4, 0xec, 0x62, 0xe0, 0xe8, 0x1a, 0x44, 0xea, 0x54, 0x37, 0xe5, + 0x5f, 0x8d, 0xd4, 0xe8, 0x2c, 0xa0, 0xfe, 0x08, 0xd0, 0xea, 0xde, 0x68, 0x76, 0xdd, + 0x4d, 0x82, 0x23, 0x5d, 0x68, 0x4b, 0x20, 0x45, 0x64, 0xc8, 0x65, 0xd6, 0x89, 0x5d, + 0xcd, 0xcf, 0x14, 0xb5, 0x37, 0xd5, 0x75, 0x4f, 0xa7, 0x29, 0x38, 0x47}, + {0x18, 0xc4, 0x79, 0x46, 0x75, 0xda, 0xd2, 0x82, 0xf0, 0x8d, 0x61, 0xb2, 0xd8, 0xd7, + 0x3b, 0xe6, 0x0a, 0xeb, 0x47, 0xac, 0x24, 0xef, 0x5e, 0x35, 0xb4, 0xc6, 0x33, 0x48, + 0x4c, 0x68, 0x78, 0x20, 0xc9, 0x02, 0x39, 0xad, 0x3a, 0x53, 0xd9, 0x23, 0x8f, 0x58, + 0x03, 0xef, 0xce, 0xdd, 0xc2, 0x64, 0xb4, 0x2f, 0xe1, 0xcf, 0x90, 0x73, 0x25, 0x15, + 0x90, 0xd3, 0xe4, 0x44, 0x4d, 0x8b, 0x66, 0x6c, 0x0c, 0x82, 0x78, 0x7a, 0x21, 0xcf, + 0x48, 0x3b, 0x97, 0x3e, 0x27, 0x81, 0xb2, 0x0a, 0x6a, 0xf7, 0x7b, 0xed, 0x8e, 0x8c, + 0xa7, 0x65, 0x6c, 0xa9, 0x3f, 0x43, 0x8a, 0x4f, 0x05, 0xa6, 0x11, 0x74}, + {0x6d, 0xc8, 0x9d, 0xb9, 0x32, 0x9d, 0x65, 0x4d, 0x15, 0xf1, 0x3a, 0x60, 0x75, 0xdc, + 0x4c, 0x04, 0x88, 0xe4, 0xc2, 0xdc, 0x2c, 0x71, 0x4c, 0xb3, 0xff, 0x34, 0x81, 0xfb, + 0x74, 0x65, 0x13, 0x7c, 0xb4, 0x75, 0xb1, 0x18, 0x3d, 0xe5, 0x9a, 0x57, 0x02, 0xa1, + 0x92, 0xf3, 0x59, 0x31, 0x71, 0x68, 0xf5, 0x35, 0xef, 0x1e, 0xba, 0xec, 0x55, 0x84, + 0x8f, 0x39, 0x8c, 0x45, 0x72, 0xa8, 0xc9, 0x1e, 0x9b, 0x50, 0xa2, 0x00, 0xd4, 0xa4, + 0xe6, 0xb8, 0xb4, 0x82, 0xc8, 0x0b, 0x02, 0xd7, 0x81, 0x9b, 0x61, 0x75, 0x95, 0xf1, + 0x9b, 0xcc, 0xe7, 0x57, 0x60, 0x64, 0xcd, 0xc7, 0xa5, 0x88, 0xdd, 0x3a}, + {0xf2, 0xdc, 0x35, 0xb6, 0x70, 0x57, 0x89, 0xab, 0xbc, 0x1f, 0x6c, 0xf6, 0x6c, 0xef, + 0xdf, 0x02, 0x87, 0xd1, 0xb6, 0xbe, 0x68, 0x02, 0x53, 0x85, 0x74, 0x9e, 0x87, 0xcc, + 0xfc, 0x29, 0x99, 0x24, 0x46, 0x30, 0x39, 0x59, 0xd4, 0x98, 0xc2, 0x85, 0xec, 0x59, + 0xf6, 0x5f, 0x98, 0x35, 0x7e, 0x8f, 0x3a, 0x6e, 0xf6, 0xf2, 0x2a, 0xa2, 0x2c, 0x1d, + 0x20, 0xa7, 0x06, 0xa4, 0x31, 0x11, 0xba, 0x61, 0x29, 0x90, 0x95, 0x16, 0xf1, 0xa0, + 0xd0, 0xa3, 0x89, 0xbd, 0x7e, 0xba, 0x6c, 0x6b, 0x3b, 0x02, 0x07, 0x33, 0x78, 0x26, + 0x3e, 0x5a, 0xf1, 0x7b, 0xe7, 0xec, 0xd8, 0xbb, 0x0c, 0x31, 0x20, 0x56}, + {0x43, 0xd6, 0x34, 0x49, 0x43, 0x93, 0x89, 0x52, 0xf5, 0x22, 0x12, 0xa5, 0x06, 0xf8, + 0xdb, 0xb9, 0x22, 0x1c, 0xf4, 0xc3, 0x8f, 0x87, 0x6d, 0x8f, 0x30, 0x97, 0x9d, 0x4d, + 0x2a, 0x6a, 0x67, 0x37, 0xd6, 0x85, 0xe2, 0x77, 0xf4, 0xb5, 0x46, 0x66, 0x93, 0x61, + 0x8f, 0x6c, 0x67, 0xff, 0xe8, 0x40, 0xdd, 0x94, 0xb5, 0xab, 0x11, 0x73, 0xec, 0xa6, + 0x4d, 0xec, 0x8c, 0x65, 0xf3, 0x46, 0xc8, 0x7e, 0xc7, 0x2e, 0xa2, 0x1d, 0x3f, 0x8f, + 0x5e, 0x9b, 0x13, 0xcd, 0x01, 0x6c, 0x77, 0x1d, 0x0f, 0x13, 0xb8, 0x9f, 0x98, 0xa2, + 0xcf, 0x8f, 0x4c, 0x21, 0xd5, 0x9d, 0x9b, 0x39, 0x23, 0xf7, 0xaa, 0x6d}, + {0x47, 0xbe, 0x3d, 0xeb, 0x62, 0x75, 0x3a, 0x5f, 0xb8, 0xa0, 0xbd, 0x8e, 0x54, 0x38, + 0xea, 0xf7, 0x99, 0x72, 0x74, 0x45, 0x31, 0xe5, 0xc3, 0x00, 0x51, 0xd5, 0x27, 0x16, + 0xe7, 0xe9, 0x04, 0x13, 0xa2, 0x8e, 0xad, 0xac, 0xbf, 0x04, 0x3b, 0x58, 0x84, 0xe8, + 0x8b, 0x14, 0xe8, 0x43, 0xb7, 0x29, 0xdb, 0xc5, 0x10, 0x08, 0x3b, 0x58, 0x1e, 0x2b, + 0xaa, 0xbb, 0xb3, 0x8e, 0xe5, 0x49, 0x54, 0x2b, 0xfe, 0x9c, 0xdc, 0x6a, 0xd2, 0x14, + 0x98, 0x78, 0x0b, 0xdd, 0x48, 0x8b, 0x3f, 0xab, 0x1b, 0x3c, 0x0a, 0xc6, 0x79, 0xf9, + 0xff, 0xe1, 0x0f, 0xda, 0x93, 0xd6, 0x2d, 0x7c, 0x2d, 0xde, 0x68, 0x44}, + {0x9e, 0x46, 0x19, 0x94, 0x5e, 0x35, 0xbb, 0x51, 0x54, 0xc7, 0xdd, 0x23, 0x4c, 0xdc, + 0xe6, 0x33, 0x62, 0x99, 0x7f, 0x44, 0xd6, 0xb6, 0xa5, 0x93, 0x63, 0xbd, 0x44, 0xfb, + 0x6f, 0x7c, 0xce, 0x6c, 0xce, 0x07, 0x63, 0xf8, 0xc6, 0xd8, 0x9a, 0x4b, 0x28, 0x0c, + 0x5d, 0x43, 0x31, 0x35, 0x11, 0x21, 0x2c, 0x77, 0x7a, 0x65, 0xc5, 0x66, 0xa8, 0xd4, + 0x52, 0x73, 0x24, 0x63, 0x7e, 0x42, 0xa6, 0x5d, 0xca, 0x22, 0xac, 0xde, 0x88, 0xc6, + 0x94, 0x1a, 0xf8, 0x1f, 0xae, 0xbb, 0xf7, 0x6e, 0x06, 0xb9, 0x0f, 0x58, 0x59, 0x8d, + 0x38, 0x8c, 0xad, 0x88, 0xa8, 0x2c, 0x9f, 0xe7, 0xbf, 0x9a, 0xf2, 0x58}, + {0x68, 0x3e, 0xe7, 0x8d, 0xab, 0xcf, 0x0e, 0xe9, 0xa5, 0x76, 0x7e, 0x37, 0x9f, 0x6f, + 0x03, 0x54, 0x82, 0x59, 0x01, 0xbe, 0x0b, 0x5b, 0x49, 0xf0, 0x36, 0x1e, 0xf4, 0xa7, + 0xc4, 0x29, 0x76, 0x57, 0xf6, 0xcd, 0x0e, 0x71, 0xbf, 0x64, 0x5a, 0x4b, 0x3c, 0x29, + 0x2c, 0x46, 0x38, 0xe5, 0x4c, 0xb1, 0xb9, 0x3a, 0x0b, 0xd5, 0x56, 0xd0, 0x43, 0x36, + 0x70, 0x48, 0x5b, 0x18, 0x24, 0x37, 0xf9, 0x6a, 0x88, 0xa8, 0xc6, 0x09, 0x45, 0x02, + 0x20, 0x32, 0x73, 0x89, 0x55, 0x4b, 0x13, 0x36, 0xe0, 0xd2, 0x9f, 0x28, 0x33, 0x3c, + 0x23, 0x36, 0xe2, 0x83, 0x8f, 0xc1, 0xae, 0x0c, 0xbb, 0x25, 0x1f, 0x70}, + {0xed, 0x6c, 0x61, 0xe4, 0xf8, 0xb0, 0xa8, 0xc3, 0x7d, 0xa8, 0x25, 0x9e, 0x0e, 0x66, + 0x00, 0xf7, 0x9c, 0xa5, 0xbc, 0xf4, 0x1f, 0x06, 0xe3, 0x61, 0xe9, 0x0b, 0xc4, 0xbd, + 0xbf, 0x92, 0x0c, 0x2e, 0x13, 0xc1, 0xbe, 0x7c, 0xd9, 0xf6, 0x18, 0x9d, 0xe4, 0xdb, + 0xbf, 0x74, 0xe6, 0x06, 0x4a, 0x84, 0xd6, 0x60, 0x4e, 0xac, 0x22, 0xb5, 0xf5, 0x20, + 0x51, 0x5e, 0x95, 0x50, 0xc0, 0x5b, 0x0a, 0x72, 0x35, 0x5a, 0x80, 0x9b, 0x43, 0x09, + 0x3f, 0x0c, 0xfc, 0xab, 0x42, 0x62, 0x37, 0x8b, 0x4e, 0xe8, 0x46, 0x93, 0x22, 0x5c, + 0xf3, 0x17, 0x14, 0x69, 0xec, 0xf0, 0x4e, 0x14, 0xbb, 0x9c, 0x9b, 0x0e}, + {0xad, 0x20, 0x57, 0xfb, 0x8f, 0xd4, 0xba, 0xfb, 0x0e, 0x0d, 0xf9, 0xdb, 0x6b, 0x91, + 0x81, 0xee, 0xbf, 0x43, 0x55, 0x63, 0x52, 0x31, 0x81, 0xd4, 0xd8, 0x7b, 0x33, 0x3f, + 0xeb, 0x04, 0x11, 0x22, 0xee, 0xbe, 0xb1, 0x5d, 0xd5, 0x9b, 0xee, 0x8d, 0xb9, 0x3f, + 0x72, 0x0a, 0x37, 0xab, 0xc3, 0xc9, 0x91, 0xd7, 0x68, 0x1c, 0xbf, 0xf1, 0xa8, 0x44, + 0xde, 0x3c, 0xfd, 0x1c, 0x19, 0x44, 0x6d, 0x36, 0x14, 0x8c, 0xbc, 0xf2, 0x43, 0x17, + 0x3c, 0x9e, 0x3b, 0x6c, 0x85, 0xb5, 0xfc, 0x26, 0xda, 0x2e, 0x97, 0xfb, 0xa7, 0x68, + 0x0e, 0x2f, 0xb8, 0xcc, 0x44, 0x32, 0x59, 0xbc, 0xe6, 0xa4, 0x67, 0x41}, + {0x00, 0x27, 0xf6, 0x76, 0x28, 0x9d, 0x3b, 0x64, 0xeb, 0x68, 0x76, 0x0e, 0x40, 0x9d, + 0x1d, 0x5d, 0x84, 0x06, 0xfc, 0x21, 0x03, 0x43, 0x4b, 0x1b, 0x6a, 0x24, 0x55, 0x22, + 0x7e, 0xbb, 0x38, 0x79, 0xee, 0x8f, 0xce, 0xf8, 0x65, 0x26, 0xbe, 0xc2, 0x2c, 0xd6, + 0x80, 0xe8, 0x14, 0xff, 0x67, 0xe9, 0xee, 0x4e, 0x36, 0x2f, 0x7e, 0x6e, 0x2e, 0xf1, + 0xf6, 0xd2, 0x7e, 0xcb, 0x70, 0x33, 0xb3, 0x34, 0xcc, 0xd6, 0x81, 0x86, 0xee, 0x91, + 0xc5, 0xcd, 0x53, 0xa7, 0x85, 0xed, 0x9c, 0x10, 0x02, 0xce, 0x83, 0x88, 0x80, 0x58, + 0xc1, 0x85, 0x74, 0xed, 0xe4, 0x65, 0xfe, 0x2d, 0x6e, 0xfc, 0x76, 0x11}, + {0x9b, 0x61, 0x9c, 0x5b, 0xd0, 0x6c, 0xaf, 0xb4, 0x80, 0x84, 0xa5, 0xb2, 0xf4, 0xc9, + 0xdf, 0x2d, 0xc4, 0x4d, 0xe9, 0xeb, 0x02, 0xa5, 0x4f, 0x3d, 0x34, 0x5f, 0x7d, 0x67, + 0x4c, 0x3a, 0xfc, 0x08, 0xb8, 0x0e, 0x77, 0x49, 0x89, 0xe2, 0x90, 0xdb, 0xa3, 0x40, + 0xf4, 0xac, 0x2a, 0xcc, 0xfb, 0x98, 0x9b, 0x87, 0xd7, 0xde, 0xfe, 0x4f, 0x35, 0x21, + 0xb6, 0x06, 0x69, 0xf2, 0x54, 0x3e, 0x6a, 0x1f, 0xea, 0x34, 0x07, 0xd3, 0x99, 0xc1, + 0xa4, 0x60, 0xd6, 0x5c, 0x16, 0x31, 0xb6, 0x85, 0xc0, 0x40, 0x95, 0x82, 0x59, 0xf7, + 0x23, 0x3e, 0x33, 0xe2, 0xd1, 0x00, 0xb9, 0x16, 0x01, 0xad, 0x2f, 0x4f}, + {0x54, 0x4e, 0xae, 0x94, 0x41, 0xb2, 0xbe, 0x44, 0x6c, 0xef, 0x57, 0x18, 0x51, 0x1c, + 0x54, 0x5f, 0x98, 0x04, 0x8d, 0x36, 0x2d, 0x6b, 0x1e, 0xa6, 0xab, 0xf7, 0x2e, 0x97, + 0xa4, 0x84, 0x54, 0x44, 0x38, 0xb6, 0x3b, 0xb7, 0x1d, 0xd9, 0x2c, 0x96, 0x08, 0x9c, + 0x12, 0xfc, 0xaa, 0x77, 0x05, 0xe6, 0x89, 0x16, 0xb6, 0xf3, 0x39, 0x9b, 0x61, 0x6f, + 0x81, 0xee, 0x44, 0x29, 0x5f, 0x99, 0x51, 0x34, 0x7c, 0x7d, 0xea, 0x9f, 0xd0, 0xfc, + 0x52, 0x91, 0xf6, 0x5c, 0x93, 0xb0, 0x94, 0x6c, 0x81, 0x4a, 0x40, 0x5c, 0x28, 0x47, + 0xaa, 0x9a, 0x8e, 0x25, 0xb7, 0x93, 0x28, 0x04, 0xa6, 0x9c, 0xb8, 0x10}, + {0x9c, 0x28, 0x18, 0x97, 0x49, 0x47, 0x59, 0x3d, 0x26, 0x3f, 0x53, 0x24, 0xc5, 0xf8, + 0xeb, 0x12, 0x15, 0xef, 0xc3, 0x14, 0xcb, 0xbf, 0x62, 0x02, 0x8e, 0x51, 0xb7, 0x77, + 0xd5, 0x78, 0xb8, 0x20, 0x6e, 0xf0, 0x45, 0x5a, 0xbe, 0x41, 0x39, 0x75, 0x65, 0x5f, + 0x9c, 0x6d, 0xed, 0xae, 0x7c, 0xd0, 0xb6, 0x51, 0xff, 0x72, 0x9c, 0x6b, 0x77, 0x11, + 0xa9, 0x4d, 0x0d, 0xef, 0xd9, 0xd1, 0xd2, 0x17, 0x6a, 0x3e, 0x3f, 0x07, 0x18, 0xaf, + 0xf2, 0x27, 0x69, 0x10, 0x52, 0xd7, 0x19, 0xe5, 0x3f, 0xfd, 0x22, 0x00, 0xa6, 0x3c, + 0x2c, 0xb7, 0xe3, 0x22, 0xa7, 0xc6, 0x65, 0xcc, 0x63, 0x4f, 0x21, 0x72}, + {0x93, 0xa6, 0x07, 0x53, 0x40, 0x7f, 0xe3, 0xb4, 0x95, 0x67, 0x33, 0x2f, 0xd7, 0x14, + 0xa7, 0xab, 0x99, 0x10, 0x76, 0x73, 0xa7, 0xd0, 0xfb, 0xd6, 0xc9, 0xcb, 0x71, 0x81, + 0xc5, 0x48, 0xdf, 0x5f, 0xc9, 0x29, 0x3b, 0xf4, 0xb9, 0xb7, 0x9d, 0x1d, 0x75, 0x8f, + 0x51, 0x4f, 0x4a, 0x82, 0x05, 0xd6, 0xc4, 0x9d, 0x2f, 0x31, 0xbd, 0x72, 0xc0, 0xf2, + 0xb0, 0x45, 0x15, 0x5a, 0x85, 0xac, 0x24, 0x1f, 0xaa, 0x05, 0x95, 0x8e, 0x32, 0x08, + 0xd6, 0x24, 0xee, 0x20, 0x14, 0x0c, 0xd1, 0xc1, 0x48, 0x47, 0xa2, 0x25, 0xfb, 0x06, + 0x5c, 0xe4, 0xff, 0xc7, 0xe6, 0x95, 0xe3, 0x2a, 0x9e, 0x73, 0xba, 0x00}, + {0xd6, 0x90, 0x87, 0x5c, 0xde, 0x98, 0x2e, 0x59, 0xdf, 0xa2, 0xc2, 0x45, 0xd3, 0xb7, + 0xbf, 0xe5, 0x22, 0x99, 0xb4, 0xf9, 0x60, 0x3b, 0x5a, 0x11, 0xf3, 0x78, 0xad, 0x67, + 0x3e, 0x3a, 0x28, 0x03, 0x26, 0xbb, 0x88, 0xea, 0xf5, 0x26, 0x44, 0xae, 0xfb, 0x3b, + 0x97, 0x84, 0xd9, 0x79, 0x06, 0x36, 0x50, 0x4e, 0x69, 0x26, 0x0c, 0x03, 0x9f, 0x5c, + 0x26, 0xd2, 0x18, 0xd5, 0xe7, 0x7d, 0x29, 0x72, 0x39, 0xb9, 0x0c, 0xbe, 0xc7, 0x1d, + 0x24, 0x48, 0x80, 0x30, 0x63, 0x8b, 0x4d, 0x9b, 0xf1, 0x32, 0x08, 0x93, 0x28, 0x02, + 0x0d, 0xc9, 0xdf, 0xd3, 0x45, 0x19, 0x27, 0x46, 0x68, 0x29, 0xe1, 0x05}, + {0x5a, 0x49, 0x9c, 0x2d, 0xb3, 0xee, 0x82, 0xba, 0x7c, 0xb9, 0x2b, 0xf1, 0xfc, 0xc8, + 0xef, 0xce, 0xe0, 0xd1, 0xb5, 0x93, 0xae, 0xab, 0x2d, 0xb0, 0x9b, 0x8d, 0x69, 0x13, + 0x9c, 0x0c, 0xc0, 0x39, 0x50, 0x45, 0x2c, 0x24, 0xc8, 0xbb, 0xbf, 0xad, 0xd9, 0x81, + 0x30, 0xd0, 0xec, 0x0c, 0xc8, 0xbc, 0x92, 0xdf, 0xc8, 0xf5, 0xa6, 0x66, 0x35, 0x84, + 0x4c, 0xce, 0x58, 0x82, 0xd3, 0x25, 0xcf, 0x78, 0x68, 0x9d, 0x48, 0x31, 0x8e, 0x6b, + 0xae, 0x15, 0x87, 0xf0, 0x2b, 0x9c, 0xab, 0x1c, 0x85, 0xaa, 0x05, 0xfa, 0x4e, 0xf0, + 0x97, 0x5a, 0xa7, 0xc9, 0x32, 0xf8, 0x3f, 0x6b, 0x07, 0x52, 0x6b, 0x00}, + {0x1c, 0x78, 0x95, 0x9d, 0xe1, 0xcf, 0xe0, 0x29, 0xe2, 0x10, 0x63, 0x96, 0x18, 0xdf, + 0x81, 0xb6, 0x39, 0x6b, 0x51, 0x70, 0xd3, 0x39, 0xdf, 0x57, 0x22, 0x61, 0xc7, 0x3b, + 0x44, 0xe3, 0x57, 0x4d, 0x2d, 0x08, 0xce, 0xb9, 0x16, 0x7e, 0xcb, 0xf5, 0x29, 0xbc, + 0x7a, 0x41, 0x4c, 0xf1, 0x07, 0x34, 0xab, 0xa7, 0xf4, 0x2b, 0xce, 0x6b, 0xb3, 0xd4, + 0xce, 0x75, 0x9f, 0x1a, 0x56, 0xe9, 0xe2, 0x7d, 0xcb, 0x5e, 0xa5, 0xb6, 0xf4, 0xd4, + 0x70, 0xde, 0x99, 0xdb, 0x85, 0x5d, 0x7f, 0x52, 0x01, 0x48, 0x81, 0x9a, 0xee, 0xd3, + 0x40, 0xc4, 0xc9, 0xdb, 0xed, 0x29, 0x60, 0x1a, 0xaf, 0x90, 0x2a, 0x6b}, + {0x97, 0x1e, 0xe6, 0x9a, 0xfc, 0xf4, 0x23, 0x69, 0xd1, 0x5f, 0x3f, 0xe0, 0x1d, 0x28, + 0x35, 0x57, 0x2d, 0xd1, 0xed, 0xe6, 0x43, 0xae, 0x64, 0xa7, 0x4a, 0x3e, 0x2d, 0xd1, + 0xe9, 0xf4, 0xd8, 0x5f, 0x0a, 0xd8, 0xb2, 0x5b, 0x24, 0xf3, 0xeb, 0x77, 0x9b, 0x07, + 0xb9, 0x2f, 0x47, 0x1b, 0x30, 0xd8, 0x33, 0x73, 0xee, 0x4c, 0xf2, 0xe6, 0x47, 0xc6, + 0x09, 0x21, 0x6c, 0x27, 0xc8, 0x12, 0x58, 0x46, 0xd9, 0x62, 0x10, 0x2a, 0xb2, 0xbe, + 0x43, 0x4d, 0x16, 0xdc, 0x31, 0x38, 0x75, 0xfb, 0x65, 0x70, 0xd7, 0x68, 0x29, 0xde, + 0x7b, 0x4a, 0x0d, 0x18, 0x90, 0x67, 0xb1, 0x1c, 0x2b, 0x2c, 0xb3, 0x05}, + {0xfd, 0xa8, 0x4d, 0xd2, 0xcc, 0x5e, 0xc0, 0xc8, 0x83, 0xef, 0xdf, 0x05, 0xac, 0x1a, + 0xcf, 0xa1, 0x61, 0xcd, 0xf9, 0x7d, 0xf2, 0xef, 0xbe, 0xdb, 0x99, 0x1e, 0x47, 0x7b, + 0xa3, 0x56, 0x55, 0x3b, 0x95, 0x81, 0xd5, 0x7a, 0x2c, 0xa4, 0xfc, 0xf7, 0xcc, 0xf3, + 0x33, 0x43, 0x6e, 0x28, 0x14, 0x32, 0x9d, 0x97, 0x0b, 0x34, 0x0d, 0x9d, 0xc2, 0xb6, + 0xe1, 0x07, 0x73, 0x56, 0x48, 0x1a, 0x77, 0x31, 0x82, 0xd4, 0x4d, 0xe1, 0x24, 0xc5, + 0xb0, 0x32, 0xb6, 0xa4, 0x2b, 0x1a, 0x54, 0x51, 0xb3, 0xed, 0xf3, 0x5a, 0x2b, 0x28, + 0x48, 0x60, 0xd1, 0xa3, 0xeb, 0x36, 0x73, 0x7a, 0xd2, 0x79, 0xc0, 0x4f}, + {0x7f, 0x2f, 0xbf, 0x89, 0xb0, 0x38, 0xc9, 0x51, 0xa7, 0xe9, 0xdf, 0x02, 0x65, 0xbd, + 0x97, 0x24, 0x53, 0xe4, 0x80, 0x78, 0x9c, 0xc0, 0xff, 0xff, 0x92, 0x8e, 0xf9, 0xca, + 0xce, 0x67, 0x45, 0x12, 0x0d, 0xc5, 0x86, 0x0c, 0x44, 0x8b, 0x34, 0xdc, 0x51, 0xe6, + 0x94, 0xcc, 0xc9, 0xcb, 0x37, 0x13, 0xb9, 0x3c, 0x3e, 0x64, 0x4d, 0xf7, 0x22, 0x64, + 0x08, 0xcd, 0xe3, 0xba, 0xc2, 0x70, 0x11, 0x24, 0xb4, 0x73, 0xc4, 0x0a, 0x86, 0xab, + 0xf9, 0x3f, 0x35, 0xe4, 0x13, 0x01, 0xee, 0x1d, 0x91, 0xf0, 0xaf, 0xc4, 0xc6, 0xeb, + 0x60, 0x50, 0xe7, 0x4a, 0x0d, 0x00, 0x87, 0x6c, 0x96, 0x12, 0x86, 0x3f}, + {0xde, 0x0d, 0x2a, 0x78, 0xc9, 0x0c, 0x9a, 0x55, 0x85, 0x83, 0x71, 0xea, 0xb2, 0xcd, + 0x1d, 0x55, 0x8c, 0x23, 0xef, 0x31, 0x5b, 0x86, 0x62, 0x7f, 0x3d, 0x61, 0x73, 0x79, + 0x76, 0xa7, 0x4a, 0x50, 0x13, 0x8d, 0x04, 0x36, 0xfa, 0xfc, 0x18, 0x9c, 0xdd, 0x9d, + 0x89, 0x73, 0xb3, 0x9d, 0x15, 0x29, 0xaa, 0xd0, 0x92, 0x9f, 0x0b, 0x35, 0x9f, 0xdc, + 0xd4, 0x19, 0x8a, 0x87, 0xee, 0x7e, 0xf5, 0x26, 0xb1, 0xef, 0x87, 0x56, 0xd5, 0x2c, + 0xab, 0x0c, 0x7b, 0xf1, 0x7a, 0x24, 0x62, 0xd1, 0x80, 0x51, 0x67, 0x24, 0x5a, 0x4f, + 0x34, 0x5a, 0xc1, 0x85, 0x69, 0x30, 0xba, 0x9d, 0x3d, 0x94, 0x41, 0x40}, + {0x96, 0xcc, 0xeb, 0x43, 0xba, 0xee, 0xc0, 0xc3, 0xaf, 0x9c, 0xea, 0x26, 0x9c, 0x9c, + 0x74, 0x8d, 0xc6, 0xcc, 0x77, 0x1c, 0xee, 0x95, 0xfa, 0xd9, 0x0f, 0x34, 0x84, 0x76, + 0xd9, 0xa1, 0x20, 0x14, 0xdd, 0xaa, 0x6c, 0xa2, 0x43, 0x77, 0x21, 0x4b, 0xce, 0xb7, + 0x8a, 0x64, 0x24, 0xb4, 0xa6, 0x47, 0xe3, 0xc9, 0xfb, 0x03, 0x7a, 0x4f, 0x1d, 0xcb, + 0x19, 0xd0, 0x00, 0x98, 0x42, 0x31, 0xd9, 0x12, 0x4f, 0x59, 0x37, 0xd3, 0x99, 0x77, + 0xc6, 0x00, 0x7b, 0xa4, 0x3a, 0xb2, 0x40, 0x51, 0x3c, 0x5e, 0x95, 0xf3, 0x5f, 0xe3, + 0x54, 0x28, 0x18, 0x44, 0x12, 0xa0, 0x59, 0x43, 0x31, 0x92, 0x4f, 0x1b}, + {0x51, 0x09, 0x15, 0x89, 0x9d, 0x10, 0x5c, 0x3e, 0x6a, 0x69, 0xe9, 0x2d, 0x91, 0xfa, + 0xce, 0x39, 0x20, 0x30, 0x5f, 0x97, 0x3f, 0xe4, 0xea, 0x20, 0xae, 0x2d, 0x13, 0x7f, + 0x2a, 0x57, 0x9b, 0x23, 0xb1, 0x66, 0x98, 0xa4, 0x30, 0x30, 0xcf, 0x33, 0x59, 0x48, + 0x5f, 0x21, 0xd2, 0x73, 0x1f, 0x25, 0xf6, 0xf4, 0xde, 0x51, 0x40, 0xaa, 0x82, 0xab, + 0xf6, 0x23, 0x9a, 0x6f, 0xd5, 0x91, 0xf1, 0x5f, 0x68, 0x90, 0x2d, 0xac, 0x33, 0xd4, + 0x9e, 0x81, 0x23, 0x85, 0xc9, 0x5f, 0x79, 0xab, 0x83, 0x28, 0x3d, 0xeb, 0x93, 0x55, + 0x80, 0x72, 0x45, 0xef, 0xcb, 0x36, 0x8f, 0x75, 0x6a, 0x52, 0x0c, 0x02}, + {0xbc, 0xdb, 0xd8, 0x9e, 0xf8, 0x34, 0x98, 0x77, 0x6c, 0xa4, 0x7c, 0xdc, 0xf9, 0xaa, + 0xf2, 0xc8, 0x74, 0xb0, 0xe1, 0xa3, 0xdc, 0x4c, 0x52, 0xa9, 0x77, 0x38, 0x31, 0x15, + 0x46, 0xcc, 0xaa, 0x02, 0x89, 0xcc, 0x42, 0xf0, 0x59, 0xef, 0x31, 0xe9, 0xb6, 0x4b, + 0x12, 0x8e, 0x9d, 0x9c, 0x58, 0x2c, 0x97, 0x59, 0xc7, 0xae, 0x8a, 0xe1, 0xc8, 0xad, + 0x0c, 0xc5, 0x02, 0x56, 0x0a, 0xfe, 0x2c, 0x45, 0xdf, 0x77, 0x78, 0x64, 0xa0, 0xf7, + 0xa0, 0x86, 0x9f, 0x7c, 0x60, 0x0e, 0x27, 0x64, 0xc4, 0xbb, 0xc9, 0x11, 0xfb, 0xf1, + 0x25, 0xea, 0x17, 0xab, 0x7b, 0x87, 0x4b, 0x30, 0x7b, 0x7d, 0xfb, 0x4c}, + {0xfe, 0x75, 0x9b, 0xb8, 0x6c, 0x3d, 0xb4, 0x72, 0x80, 0xdc, 0x6a, 0x9c, 0xd9, 0x94, + 0xc6, 0x54, 0x9f, 0x4c, 0xe3, 0x3e, 0x37, 0xaa, 0xc3, 0xb8, 0x64, 0x53, 0x07, 0x39, + 0x2b, 0x62, 0xb4, 0x14, 0x12, 0xef, 0x89, 0x97, 0xc2, 0x99, 0x86, 0xe2, 0x0d, 0x19, + 0x57, 0xdf, 0x71, 0xcd, 0x6e, 0x2b, 0xd0, 0x70, 0xc9, 0xec, 0x57, 0xc8, 0x43, 0xc3, + 0xc5, 0x3a, 0x4d, 0x43, 0xbc, 0x4c, 0x1d, 0x5b, 0x26, 0x9f, 0x0a, 0xcc, 0x15, 0x26, + 0xfb, 0xb6, 0xe5, 0xcc, 0x8d, 0xb8, 0x2b, 0x0e, 0x4f, 0x3a, 0x05, 0xa7, 0x69, 0x33, + 0x8b, 0x49, 0x01, 0x13, 0xd1, 0x2d, 0x59, 0x58, 0x12, 0xf7, 0x98, 0x2f}, + {0x56, 0x9e, 0x0f, 0xb5, 0x4c, 0xa7, 0x94, 0x0c, 0x20, 0x13, 0x8e, 0x8e, 0xa9, 0xf4, + 0x1f, 0x5b, 0x67, 0x0f, 0x30, 0x82, 0x21, 0xcc, 0x2a, 0x9a, 0xf9, 0xaa, 0x06, 0xd8, + 0x49, 0xe2, 0x6a, 0x3a, 0x01, 0xa7, 0x54, 0x4f, 0x44, 0xae, 0x12, 0x2e, 0xde, 0xd7, + 0xcb, 0xa9, 0xf0, 0x3e, 0xfe, 0xfc, 0xe0, 0x5d, 0x83, 0x75, 0x0d, 0x89, 0xbf, 0xce, + 0x54, 0x45, 0x61, 0xe7, 0xe9, 0x62, 0x80, 0x1d, 0x5a, 0x7c, 0x90, 0xa9, 0x85, 0xda, + 0x7a, 0x65, 0x62, 0x0f, 0xb9, 0x91, 0xb5, 0xa8, 0x0e, 0x1a, 0xe9, 0xb4, 0x34, 0xdf, + 0xfb, 0x1d, 0x0e, 0x8d, 0xf3, 0x5f, 0xf2, 0xae, 0xe8, 0x8c, 0x8b, 0x29}, + {0xb2, 0x0c, 0xf7, 0xef, 0x53, 0x79, 0x92, 0x2a, 0x76, 0x70, 0x15, 0x79, 0x2a, 0xc9, + 0x89, 0x4b, 0x6a, 0xcf, 0xa7, 0x30, 0x7a, 0x45, 0x18, 0x94, 0x85, 0xe4, 0x5c, 0x4d, + 0x40, 0xa8, 0xb8, 0x34, 0xde, 0x65, 0x21, 0x0a, 0xea, 0x72, 0x7a, 0x83, 0xf6, 0x79, + 0xcf, 0x0b, 0xb4, 0x07, 0xab, 0x3f, 0x70, 0xae, 0x38, 0x77, 0xc7, 0x36, 0x16, 0x52, + 0xdc, 0xd7, 0xa7, 0x03, 0x18, 0x27, 0xa6, 0x6b, 0x35, 0x33, 0x69, 0x83, 0xb5, 0xec, + 0x6e, 0xc2, 0xfd, 0xfe, 0xb5, 0x63, 0xdf, 0x13, 0xa8, 0xd5, 0x73, 0x25, 0xb2, 0xa4, + 0x9a, 0xaa, 0x93, 0xa2, 0x6a, 0x1c, 0x5e, 0x46, 0xdd, 0x2b, 0xd6, 0x71}, + {0x80, 0xdf, 0x78, 0xd3, 0x28, 0xcc, 0x33, 0x65, 0xb4, 0xa4, 0x0f, 0x0a, 0x79, 0x43, + 0xdb, 0xf6, 0x5a, 0xda, 0x01, 0xf7, 0xf9, 0x5f, 0x64, 0xe3, 0xa4, 0x2b, 0x17, 0xf3, + 0x17, 0xf3, 0xd5, 0x74, 0xf5, 0x5e, 0xf7, 0xb1, 0xda, 0xb5, 0x2d, 0xcd, 0xf5, 0x65, + 0xb0, 0x16, 0xcf, 0x95, 0x7f, 0xd7, 0x85, 0xf0, 0x49, 0x3f, 0xea, 0x1f, 0x57, 0x14, + 0x3d, 0x2b, 0x2b, 0x26, 0x21, 0x36, 0x33, 0x1c, 0x81, 0xca, 0xd9, 0x67, 0x54, 0xe5, + 0x6f, 0xa8, 0x37, 0x8c, 0x29, 0x2b, 0x75, 0x7c, 0x8b, 0x39, 0x3b, 0x62, 0xac, 0xe3, + 0x92, 0x08, 0x6d, 0xda, 0x8c, 0xd9, 0xe9, 0x47, 0x45, 0xcc, 0xeb, 0x4a}, + {0xc9, 0x01, 0x6d, 0x27, 0x1b, 0x07, 0xf0, 0x12, 0x70, 0x8c, 0xc4, 0x86, 0xc5, 0xba, + 0xb8, 0xe7, 0xa9, 0xfb, 0xd6, 0x71, 0x9b, 0x12, 0x08, 0x53, 0x92, 0xb7, 0x3d, 0x5a, + 0xf9, 0xfb, 0x88, 0x5d, 0x10, 0xb6, 0x54, 0x73, 0x9e, 0x8d, 0x40, 0x0b, 0x6e, 0x5b, + 0xa8, 0x5b, 0x53, 0x32, 0x6b, 0x80, 0x07, 0xa2, 0x58, 0x4a, 0x03, 0x3a, 0xe6, 0xdb, + 0x2c, 0xdf, 0xa1, 0xc9, 0xdd, 0xd9, 0x3b, 0x17, 0xdf, 0x72, 0x58, 0xfe, 0x1e, 0x0f, + 0x50, 0x2b, 0xc1, 0x18, 0x39, 0xd4, 0x2e, 0x58, 0xd6, 0x58, 0xe0, 0x3a, 0x67, 0xc9, + 0x8e, 0x27, 0xed, 0xe6, 0x19, 0xa3, 0x9e, 0xb1, 0x13, 0xcd, 0xe1, 0x06}, + {0x23, 0x6f, 0x16, 0x6f, 0x51, 0xad, 0xd0, 0x40, 0xbe, 0x6a, 0xab, 0x1f, 0x93, 0x32, + 0x8e, 0x11, 0x8e, 0x08, 0x4d, 0xa0, 0x14, 0x5e, 0xe3, 0x3f, 0x66, 0x62, 0xe1, 0x26, + 0x35, 0x60, 0x80, 0x30, 0x53, 0x03, 0x5b, 0x9e, 0x62, 0xaf, 0x2b, 0x47, 0x47, 0x04, + 0x8d, 0x27, 0x90, 0x0b, 0xaa, 0x3b, 0x27, 0xbf, 0x43, 0x96, 0x46, 0x5f, 0x78, 0x0c, + 0x13, 0x7b, 0x83, 0x8d, 0x1a, 0x6a, 0x3a, 0x7f, 0x0b, 0x80, 0x3d, 0x5d, 0x39, 0x44, + 0xe6, 0xf7, 0xf6, 0xed, 0x01, 0xc9, 0x55, 0xd5, 0xa8, 0x95, 0x39, 0x63, 0x2c, 0x59, + 0x30, 0x78, 0xcd, 0x68, 0x7e, 0x30, 0x51, 0x2e, 0xed, 0xfd, 0xd0, 0x30}, + {0xb3, 0x33, 0x12, 0xf2, 0x1a, 0x4d, 0x59, 0xe0, 0x9c, 0x4d, 0xcc, 0xf0, 0x8e, 0xe7, + 0xdb, 0x1b, 0x77, 0x9a, 0x49, 0x8f, 0x7f, 0x18, 0x65, 0x69, 0x68, 0x98, 0x09, 0x2c, + 0x20, 0x14, 0x92, 0x0a, 0x50, 0x47, 0xb8, 0x68, 0x1e, 0x97, 0xb4, 0x9c, 0xcf, 0xbb, + 0x64, 0x66, 0x29, 0x72, 0x95, 0xa0, 0x2b, 0x41, 0xfa, 0x72, 0x26, 0xe7, 0x8d, 0x5c, + 0xd9, 0x89, 0xc5, 0x51, 0x43, 0x08, 0x15, 0x46, 0x2e, 0xa0, 0xb9, 0xae, 0xc0, 0x19, + 0x90, 0xbc, 0xae, 0x4c, 0x03, 0x16, 0x0d, 0x11, 0xc7, 0x55, 0xec, 0x32, 0x99, 0x65, + 0x01, 0xf5, 0x6d, 0x0e, 0xfe, 0x5d, 0xca, 0x95, 0x28, 0x0d, 0xca, 0x3b}, + {0xa4, 0x62, 0x5d, 0x3c, 0xbc, 0x31, 0xf0, 0x40, 0x60, 0x7a, 0xf0, 0xcf, 0x3e, 0x8b, + 0xfc, 0x19, 0x45, 0xb5, 0x0f, 0x13, 0xa2, 0x3d, 0x18, 0x98, 0xcd, 0x13, 0x8f, 0xae, + 0xdd, 0xde, 0x31, 0x56, 0xbf, 0x01, 0xcc, 0x9e, 0xb6, 0x8e, 0x68, 0x9c, 0x6f, 0x89, + 0x44, 0xa6, 0xad, 0x83, 0xbc, 0xf0, 0xe2, 0x9f, 0x7a, 0x5f, 0x5f, 0x95, 0x2d, 0xca, + 0x41, 0x82, 0xf2, 0x8d, 0x03, 0xb4, 0xa8, 0x4e, 0x02, 0xd2, 0xca, 0xf1, 0x0a, 0x46, + 0xed, 0x2a, 0x83, 0xee, 0x8c, 0xa4, 0x05, 0x53, 0x30, 0x46, 0x5f, 0x1a, 0xf1, 0x49, + 0x45, 0x77, 0x21, 0x91, 0x63, 0xa4, 0x2c, 0x54, 0x30, 0x09, 0xce, 0x24}, + {0x06, 0xc1, 0x06, 0xfd, 0xf5, 0x90, 0xe8, 0x1f, 0xf2, 0x10, 0x88, 0x5d, 0x35, 0x68, + 0xc4, 0xb5, 0x3e, 0xaf, 0x8c, 0x6e, 0xfe, 0x08, 0x78, 0x82, 0x4b, 0xd7, 0x06, 0x8a, + 0xc2, 0xe3, 0xd4, 0x41, 0x85, 0x0b, 0xf3, 0xfd, 0x55, 0xa1, 0xcf, 0x3f, 0xa4, 0x2e, + 0x37, 0x36, 0x8e, 0x16, 0xf7, 0xd2, 0x44, 0xf8, 0x92, 0x64, 0xde, 0x64, 0xe0, 0xb2, + 0x80, 0x42, 0x4f, 0x32, 0xa7, 0x28, 0x99, 0x54, 0x2e, 0x1a, 0xee, 0x63, 0xa7, 0x32, + 0x6e, 0xf2, 0xea, 0xfd, 0x5f, 0xd2, 0xb7, 0xe4, 0x91, 0xae, 0x69, 0x4d, 0x7f, 0xd1, + 0x3b, 0xd3, 0x3b, 0xbc, 0x6a, 0xff, 0xdc, 0xc0, 0xde, 0x66, 0x1b, 0x49}, + {0xa7, 0x32, 0xea, 0xc7, 0x3d, 0xb1, 0xf5, 0x98, 0x98, 0xdb, 0x16, 0x7e, 0xcc, 0xf8, + 0xd5, 0xe3, 0x47, 0xd9, 0xf8, 0xcb, 0x52, 0xbf, 0x0a, 0xac, 0xac, 0xe4, 0x5e, 0xc8, + 0xd0, 0x38, 0xf3, 0x08, 0xa1, 0x64, 0xda, 0xd0, 0x8e, 0x4a, 0xf0, 0x75, 0x4b, 0x28, + 0xe2, 0x67, 0xaf, 0x2c, 0x22, 0xed, 0xa4, 0x7b, 0x7b, 0x1f, 0x79, 0xa3, 0x34, 0x82, + 0x67, 0x8b, 0x01, 0xb7, 0xb0, 0xb8, 0xf6, 0x4c, 0xbd, 0x73, 0x1a, 0x99, 0x21, 0xa8, + 0x83, 0xc3, 0x7a, 0x0c, 0x32, 0xdf, 0x01, 0xbc, 0x27, 0xab, 0x63, 0x70, 0x77, 0x84, + 0x1b, 0x33, 0x3d, 0xc1, 0x99, 0x8a, 0x07, 0xeb, 0x82, 0x4a, 0x0d, 0x53}, + {0x25, 0x48, 0xf9, 0xe1, 0x30, 0x36, 0x4c, 0x00, 0x5a, 0x53, 0xab, 0x8c, 0x26, 0x78, + 0x2d, 0x7e, 0x8b, 0xff, 0x84, 0xcc, 0x23, 0x23, 0x48, 0xc7, 0xb9, 0x70, 0x17, 0x10, + 0x3f, 0x75, 0xea, 0x65, 0x9e, 0xbf, 0x9a, 0x6c, 0x45, 0x73, 0x69, 0x6d, 0x80, 0xa8, + 0x00, 0x49, 0xfc, 0xb2, 0x7f, 0x25, 0x50, 0xb8, 0xcf, 0xc8, 0x12, 0xf4, 0xac, 0x2b, + 0x5b, 0xbd, 0xbf, 0x0c, 0xe0, 0xe7, 0xb3, 0x0d, 0x63, 0x63, 0x09, 0xe2, 0x3e, 0xfc, + 0x66, 0x3d, 0x6b, 0xcb, 0xb5, 0x61, 0x7f, 0x2c, 0xd6, 0x81, 0x1a, 0x3b, 0x44, 0x13, + 0x42, 0x04, 0xbe, 0x0f, 0xdb, 0xa1, 0xe1, 0x21, 0x19, 0xec, 0xa4, 0x02}, + {0xa2, 0xb8, 0x24, 0x3b, 0x9a, 0x25, 0xe6, 0x5c, 0xb8, 0xa0, 0xaf, 0x45, 0xcc, 0x7a, + 0x57, 0xb8, 0x37, 0x70, 0xa0, 0x8b, 0xe8, 0xe6, 0xcb, 0xcc, 0xbf, 0x09, 0x78, 0x12, + 0x51, 0x3c, 0x14, 0x3d, 0x5f, 0x79, 0xcf, 0xf1, 0x62, 0x61, 0xc8, 0xf5, 0xf2, 0x57, + 0xee, 0x26, 0x19, 0x86, 0x8c, 0x11, 0x78, 0x35, 0x06, 0x1c, 0x85, 0x24, 0x21, 0x17, + 0xcf, 0x7f, 0x06, 0xec, 0x5d, 0x2b, 0xd1, 0x36, 0x57, 0x45, 0x15, 0x79, 0x91, 0x27, + 0x6d, 0x12, 0x0a, 0x3a, 0x78, 0xfc, 0x5c, 0x8f, 0xe4, 0xd5, 0xac, 0x9b, 0x17, 0xdf, + 0xe8, 0xb6, 0xbd, 0x36, 0x59, 0x28, 0xa8, 0x5b, 0x88, 0x17, 0xf5, 0x2e}, + {0xdc, 0xae, 0x58, 0x8c, 0x4e, 0x97, 0x37, 0x46, 0xa4, 0x41, 0xf0, 0xab, 0xfb, 0x22, + 0xef, 0xb9, 0x8a, 0x71, 0x80, 0xe9, 0x56, 0xd9, 0x85, 0xe1, 0xa6, 0xa8, 0x43, 0xb1, + 0xfa, 0x78, 0x1b, 0x2f, 0x51, 0x2f, 0x5b, 0x30, 0xfb, 0xbf, 0xee, 0x96, 0xb8, 0x96, + 0x95, 0x88, 0xad, 0x38, 0xf9, 0xd3, 0x25, 0xdd, 0xd5, 0x46, 0xc7, 0x2d, 0xf5, 0xf0, + 0x95, 0x00, 0x3a, 0xbb, 0x90, 0x82, 0x96, 0x57, 0x01, 0xe1, 0x20, 0x0a, 0x43, 0xb8, + 0x1a, 0xf7, 0x47, 0xec, 0xf0, 0x24, 0x8d, 0x65, 0x93, 0xf3, 0xd1, 0xee, 0xe2, 0x6e, + 0xa8, 0x09, 0x75, 0xcf, 0xe1, 0xa3, 0x2a, 0xdc, 0x35, 0x3e, 0xc4, 0x7d}, + {0xc3, 0xd9, 0x7d, 0x88, 0x65, 0x66, 0x96, 0x85, 0x55, 0x53, 0xb0, 0x4b, 0x31, 0x9b, + 0x0f, 0xc9, 0xb1, 0x79, 0x20, 0xef, 0xf8, 0x8d, 0xe0, 0xc6, 0x2f, 0xc1, 0x8c, 0x75, + 0x16, 0x20, 0xf7, 0x7e, 0x18, 0x97, 0x3e, 0x27, 0x5c, 0x2a, 0x78, 0x5a, 0x94, 0xfd, + 0x4e, 0x5e, 0x99, 0xc6, 0x76, 0x35, 0x3e, 0x7d, 0x23, 0x1f, 0x05, 0xd8, 0x2e, 0x0f, + 0x99, 0x0a, 0xd5, 0x82, 0x1d, 0xb8, 0x4f, 0x04, 0xd9, 0xe3, 0x07, 0xa9, 0xc5, 0x18, + 0xdf, 0xc1, 0x59, 0x63, 0x4c, 0xce, 0x1d, 0x37, 0xb3, 0x57, 0x49, 0xbb, 0x01, 0xb2, + 0x34, 0x45, 0x70, 0xca, 0x2e, 0xdd, 0x30, 0x9c, 0x3f, 0x82, 0x79, 0x7f}, + {0xe8, 0x13, 0xb5, 0xa3, 0x39, 0xd2, 0x34, 0x83, 0xd8, 0xa8, 0x1f, 0xb9, 0xd4, 0x70, + 0x36, 0xc1, 0x33, 0xbd, 0x90, 0xf5, 0x36, 0x41, 0xb5, 0x12, 0xb4, 0xd9, 0x84, 0xd7, + 0x73, 0x03, 0x4e, 0x0a, 0xba, 0x87, 0xf5, 0x68, 0xf0, 0x1f, 0x9c, 0x6a, 0xde, 0xc8, + 0x50, 0x00, 0x4e, 0x89, 0x27, 0x08, 0xe7, 0x5b, 0xed, 0x7d, 0x55, 0x99, 0xbf, 0x3c, + 0xf0, 0xd6, 0x06, 0x1c, 0x43, 0xb0, 0xa9, 0x64, 0x19, 0x29, 0x7d, 0x5b, 0xa1, 0xd6, + 0xb3, 0x2e, 0x35, 0x82, 0x3a, 0xd5, 0xa0, 0xf6, 0xb4, 0xb0, 0x47, 0x5d, 0xa4, 0x89, + 0x43, 0xce, 0x56, 0x71, 0x6c, 0x34, 0x18, 0xce, 0x0a, 0x7d, 0x1a, 0x07}, + {0x0b, 0xba, 0x87, 0xc8, 0xaa, 0x2d, 0x07, 0xd3, 0xee, 0x62, 0xa5, 0xbf, 0x05, 0x29, + 0x26, 0x01, 0x8b, 0x76, 0xef, 0xc0, 0x02, 0x30, 0x54, 0xcf, 0x9c, 0x7e, 0xea, 0x46, + 0x71, 0xcc, 0x3b, 0x2c, 0x31, 0x44, 0xe1, 0x20, 0x52, 0x35, 0x0c, 0xcc, 0x41, 0x51, + 0xb1, 0x09, 0x07, 0x95, 0x65, 0x0d, 0x36, 0x5f, 0x9d, 0x20, 0x1b, 0x62, 0xf5, 0x9a, + 0xd3, 0x55, 0x77, 0x61, 0xf7, 0xbc, 0x69, 0x7c, 0x5f, 0x29, 0xe8, 0x04, 0xeb, 0xd7, + 0xf0, 0x07, 0x7d, 0xf3, 0x50, 0x2f, 0x25, 0x18, 0xdb, 0x10, 0xd7, 0x98, 0x17, 0x17, + 0xa3, 0xa9, 0x51, 0xe9, 0x1d, 0xa5, 0xac, 0x22, 0x73, 0x9a, 0x5a, 0x6f}, + {0xc5, 0xc6, 0x41, 0x2f, 0x0c, 0x00, 0xa1, 0x8b, 0x9b, 0xfb, 0xfe, 0x0c, 0xc1, 0x79, + 0x9f, 0xc4, 0x9f, 0x1c, 0xc5, 0x3c, 0x70, 0x47, 0xfa, 0x4e, 0xca, 0xaf, 0x47, 0xe1, + 0xa2, 0x21, 0x4e, 0x49, 0xbe, 0x44, 0xd9, 0xa3, 0xeb, 0xd4, 0x29, 0xe7, 0x9e, 0xaf, + 0x78, 0x80, 0x40, 0x09, 0x9e, 0x8d, 0x03, 0x9c, 0x86, 0x47, 0x7a, 0x56, 0x25, 0x45, + 0x24, 0x3b, 0x8d, 0xee, 0x80, 0x96, 0xab, 0x02, 0x9a, 0x0d, 0xe5, 0xdd, 0x85, 0x8a, + 0xa4, 0xef, 0x49, 0xa2, 0xb9, 0x0f, 0x4e, 0x22, 0x9a, 0x21, 0xd9, 0xf6, 0x1e, 0xd9, + 0x1d, 0x1f, 0x09, 0xfa, 0x34, 0xbb, 0x46, 0xea, 0xcb, 0x76, 0x5d, 0x6b}, + {0x94, 0xd9, 0x0c, 0xec, 0x6c, 0x55, 0x57, 0x88, 0xba, 0x1d, 0xd0, 0x5c, 0x6f, 0xdc, + 0x72, 0x64, 0x77, 0xb4, 0x42, 0x8f, 0x14, 0x69, 0x01, 0xaf, 0x54, 0x73, 0x27, 0x85, + 0xf6, 0x33, 0xe3, 0x0a, 0x22, 0x25, 0x78, 0x1e, 0x17, 0x41, 0xf9, 0xe0, 0xd3, 0x36, + 0x69, 0x03, 0x74, 0xae, 0xe6, 0xf1, 0x46, 0xc7, 0xfc, 0xd0, 0xa2, 0x3e, 0x8b, 0x40, + 0x3e, 0x31, 0xdd, 0x03, 0x9c, 0x86, 0xfb, 0x16, 0x62, 0x09, 0xb6, 0x33, 0x97, 0x19, + 0x8e, 0x28, 0x33, 0xe1, 0xab, 0xd8, 0xb4, 0x72, 0xfc, 0x24, 0x3e, 0xd0, 0x91, 0x09, + 0xed, 0xf7, 0x11, 0x48, 0x75, 0xd0, 0x70, 0x8f, 0x8b, 0xe3, 0x81, 0x3f}, + {0xfe, 0xaf, 0xd9, 0x7e, 0xcc, 0x0f, 0x91, 0x7f, 0x4b, 0x87, 0x65, 0x24, 0xa1, 0xb8, + 0x5c, 0x54, 0x04, 0x47, 0x0c, 0x4b, 0xd2, 0x7e, 0x39, 0xa8, 0x93, 0x09, 0xf5, 0x04, + 0xc1, 0x0f, 0x51, 0x50, 0x24, 0xc8, 0x17, 0x5f, 0x35, 0x7f, 0xdb, 0x0a, 0xa4, 0x99, + 0x42, 0xd7, 0xc3, 0x23, 0xb9, 0x74, 0xf7, 0xea, 0xf8, 0xcb, 0x8b, 0x3e, 0x7c, 0xd5, + 0x3d, 0xdc, 0xde, 0x4c, 0xd3, 0xe2, 0xd3, 0x0a, 0x9d, 0x24, 0x6e, 0x33, 0xc5, 0x0f, + 0x0c, 0x6f, 0xd9, 0xcf, 0x31, 0xc3, 0x19, 0xde, 0x5e, 0x74, 0x1c, 0xfe, 0xee, 0x09, + 0x00, 0xfd, 0xd6, 0xf2, 0xbe, 0x1e, 0xfa, 0xf0, 0x8b, 0x15, 0x7c, 0x12}, + {0xa2, 0x79, 0x98, 0x2e, 0x42, 0x7c, 0x19, 0xf6, 0x47, 0x36, 0xca, 0x52, 0xd4, 0xdd, + 0x4a, 0xa4, 0xcb, 0xac, 0x4e, 0x4b, 0xc1, 0x3f, 0x41, 0x9b, 0x68, 0x4f, 0xef, 0x07, + 0x7d, 0xf8, 0x4e, 0x35, 0x74, 0xb9, 0x51, 0xae, 0xc4, 0x8f, 0xa2, 0xde, 0x96, 0xfe, + 0x4d, 0x74, 0xd3, 0x73, 0x99, 0x1d, 0xa8, 0x48, 0x38, 0x87, 0x0b, 0x68, 0x40, 0x62, + 0x95, 0xdf, 0x67, 0xd1, 0x79, 0x24, 0xd8, 0x4e, 0x75, 0xd9, 0xc5, 0x60, 0x22, 0xb5, + 0xe3, 0xfe, 0xb8, 0xb0, 0x41, 0xeb, 0xfc, 0x2e, 0x35, 0x50, 0x3c, 0x65, 0xf6, 0xa9, + 0x30, 0xac, 0x08, 0x88, 0x6d, 0x23, 0x39, 0x05, 0xd2, 0x92, 0x2d, 0x30}, + {0x3d, 0x28, 0xa4, 0xbc, 0xa2, 0xc1, 0x13, 0x78, 0xd9, 0x3d, 0x86, 0xa1, 0x91, 0xf0, + 0x62, 0xed, 0x86, 0xfa, 0x68, 0xc2, 0xb8, 0xbc, 0xc7, 0xae, 0x4c, 0xae, 0x1c, 0x6f, + 0xb7, 0xd3, 0xe5, 0x10, 0x77, 0xf1, 0xe0, 0xe4, 0xb6, 0x6f, 0xbc, 0x2d, 0x93, 0x6a, + 0xbd, 0xa4, 0x29, 0xbf, 0xe1, 0x04, 0xe8, 0xf6, 0x7a, 0x78, 0xd4, 0x66, 0x19, 0x5e, + 0x60, 0xd0, 0x26, 0xb4, 0x5e, 0x5f, 0xdc, 0x0e, 0x67, 0x8e, 0xda, 0x53, 0xd6, 0xbf, + 0x53, 0x54, 0x41, 0xf6, 0xa9, 0x24, 0xec, 0x1e, 0xdc, 0xe9, 0x23, 0x8a, 0x57, 0x03, + 0x3b, 0x26, 0x87, 0xbf, 0x72, 0xba, 0x1c, 0x36, 0x51, 0x6c, 0xb4, 0x45}, + {0xa1, 0x7f, 0x4f, 0x31, 0xbf, 0x2a, 0x40, 0xa9, 0x50, 0xf4, 0x8c, 0x8e, 0xdc, 0xf1, + 0x57, 0xe2, 0x84, 0xbe, 0xa8, 0x23, 0x4b, 0xd5, 0xbb, 0x1d, 0x3b, 0x71, 0xcb, 0x6d, + 0xa3, 0xbf, 0x77, 0x21, 0xe4, 0xe3, 0x7f, 0x8a, 0xdd, 0x4d, 0x9d, 0xce, 0x30, 0x0e, + 0x62, 0x76, 0x56, 0x64, 0x13, 0xab, 0x58, 0x99, 0x0e, 0xb3, 0x7b, 0x4f, 0x59, 0x4b, + 0xdf, 0x29, 0x12, 0x32, 0xef, 0x0a, 0x1c, 0x5c, 0x8f, 0xdb, 0x79, 0xfa, 0xbc, 0x1b, + 0x08, 0x37, 0xb3, 0x59, 0x5f, 0xc2, 0x1e, 0x81, 0x48, 0x60, 0x87, 0x24, 0x83, 0x9c, + 0x65, 0x76, 0x7a, 0x08, 0xbb, 0xb5, 0x8a, 0x7d, 0x38, 0x19, 0xe6, 0x4a}, + {0x2e, 0xa3, 0x44, 0x53, 0xaa, 0xf6, 0xdb, 0x8d, 0x78, 0x40, 0x1b, 0xb4, 0xb4, 0xea, + 0x88, 0x7d, 0x60, 0x0d, 0x13, 0x4a, 0x97, 0xeb, 0xb0, 0x5e, 0x03, 0x3e, 0xbf, 0x17, + 0x1b, 0xd9, 0x00, 0x1a, 0x83, 0xfb, 0x5b, 0x98, 0x44, 0x7e, 0x11, 0x61, 0x36, 0x31, + 0x96, 0x71, 0x2a, 0x46, 0xe0, 0xfc, 0x4b, 0x90, 0x25, 0xd4, 0x48, 0x34, 0xac, 0x83, + 0x64, 0x3d, 0xa4, 0x5b, 0xbe, 0x5a, 0x68, 0x75, 0xb2, 0xf2, 0x61, 0xeb, 0x33, 0x09, + 0x96, 0x6e, 0x52, 0x49, 0xff, 0xc9, 0xa8, 0x0f, 0x3d, 0x54, 0x69, 0x65, 0xf6, 0x7a, + 0x10, 0x75, 0x72, 0xdf, 0xaa, 0xe6, 0xb0, 0x23, 0xb6, 0x29, 0x55, 0x13}, + {0x18, 0xd5, 0xd1, 0xad, 0xd7, 0xdb, 0xf0, 0x18, 0x11, 0x1f, 0xc1, 0xcf, 0x88, 0x78, + 0x9f, 0x97, 0x9b, 0x75, 0x14, 0x71, 0xf0, 0xe1, 0x32, 0x87, 0x01, 0x3a, 0xca, 0x65, + 0x1a, 0xb8, 0xb5, 0x79, 0xfe, 0x83, 0x2e, 0xe2, 0xbc, 0x16, 0xc7, 0xf5, 0xc1, 0x85, + 0x09, 0xe8, 0x19, 0xeb, 0x2b, 0xb4, 0xae, 0x4a, 0x25, 0x14, 0x37, 0xa6, 0x9d, 0xec, + 0x13, 0xa6, 0x90, 0x15, 0x05, 0xea, 0x72, 0x59, 0x11, 0x78, 0x8f, 0xdc, 0x20, 0xac, + 0xd4, 0x0f, 0xa8, 0x4f, 0x4d, 0xac, 0x94, 0xd2, 0x9a, 0x9a, 0x34, 0x04, 0x36, 0xb3, + 0x64, 0x2d, 0x1b, 0xc0, 0xdb, 0x3b, 0x5f, 0x90, 0x95, 0x9c, 0x7e, 0x4f}, + {0x2e, 0x30, 0x81, 0x57, 0xbc, 0x4b, 0x67, 0x62, 0x0f, 0xdc, 0xad, 0x89, 0x39, 0x0f, + 0x52, 0xd8, 0xc6, 0xd9, 0xfb, 0x53, 0xae, 0x99, 0x29, 0x8c, 0x4c, 0x8e, 0x63, 0x2e, + 0xd9, 0x3a, 0x99, 0x31, 0xfe, 0x99, 0x52, 0x35, 0x3d, 0x44, 0xc8, 0x71, 0xd7, 0xea, + 0xeb, 0xdb, 0x1c, 0x3b, 0xcd, 0x8b, 0x66, 0x94, 0xa4, 0xf1, 0x9e, 0x49, 0x92, 0x80, + 0xc8, 0xad, 0x44, 0xa1, 0xc4, 0xee, 0x42, 0x19, 0x92, 0x49, 0x23, 0xae, 0x19, 0x53, + 0xac, 0x7d, 0x92, 0x3e, 0xea, 0x0c, 0x91, 0x3d, 0x1b, 0x2c, 0x22, 0x11, 0x3c, 0x25, + 0x94, 0xe4, 0x3c, 0x55, 0x75, 0xca, 0xf9, 0x4e, 0x31, 0x65, 0x0a, 0x2a}, + {0xc2, 0x27, 0xf9, 0xf7, 0x7f, 0x93, 0xb7, 0x2d, 0x35, 0xa6, 0xd0, 0x17, 0x06, 0x1f, + 0x74, 0xdb, 0x76, 0xaf, 0x55, 0x11, 0xa2, 0xf3, 0x82, 0x59, 0xed, 0x2d, 0x7c, 0x64, + 0x18, 0xe2, 0xf6, 0x4c, 0x3a, 0x79, 0x1c, 0x3c, 0xcd, 0x1a, 0x36, 0xcf, 0x3b, 0xbc, + 0x35, 0x5a, 0xac, 0xbc, 0x9e, 0x2f, 0xab, 0xa6, 0xcd, 0xa8, 0xe9, 0x60, 0xe8, 0x60, + 0x13, 0x1a, 0xea, 0x6d, 0x9b, 0xc3, 0x5d, 0x05, 0xb6, 0x5b, 0x8d, 0xc2, 0x7c, 0x22, + 0x19, 0xb1, 0xab, 0xff, 0x4d, 0x77, 0xbc, 0x4e, 0xe2, 0x07, 0x89, 0x2c, 0xa3, 0xe4, + 0xce, 0x78, 0x3c, 0xa8, 0xb6, 0x24, 0xaa, 0x10, 0x77, 0x30, 0x1a, 0x12}, + {0x97, 0x4a, 0x03, 0x9f, 0x5e, 0x5d, 0xdb, 0xe4, 0x2d, 0xbc, 0x34, 0x30, 0x09, 0xfc, + 0x53, 0xe1, 0xb1, 0xd3, 0x51, 0x95, 0x91, 0x46, 0x05, 0x46, 0x2d, 0xe5, 0x40, 0x7a, + 0x6c, 0xc7, 0x3f, 0x33, 0xc9, 0x83, 0x74, 0xc7, 0x3e, 0x71, 0x59, 0xd6, 0xaf, 0x96, + 0x2b, 0xb8, 0x77, 0xe0, 0xbf, 0x88, 0xd3, 0xbc, 0x97, 0x10, 0x23, 0x28, 0x9e, 0x28, + 0x9b, 0x3a, 0xed, 0x6c, 0x4a, 0xb9, 0x7b, 0x52, 0x2e, 0x48, 0x5b, 0x99, 0x2a, 0x99, + 0x3d, 0x56, 0x01, 0x38, 0x38, 0x6e, 0x7c, 0xd0, 0x05, 0x34, 0xe5, 0xd8, 0x64, 0x2f, + 0xde, 0x35, 0x50, 0x48, 0xf7, 0xa9, 0xa7, 0x20, 0x9b, 0x06, 0x89, 0x6b}, + {0x0d, 0x22, 0x70, 0x62, 0x41, 0xa0, 0x2a, 0x81, 0x4e, 0x5b, 0x24, 0xf9, 0xfa, 0x89, + 0x5a, 0x99, 0x05, 0xef, 0x72, 0x50, 0xce, 0xc4, 0xad, 0xff, 0x73, 0xeb, 0x73, 0xaa, + 0x03, 0x21, 0xbc, 0x23, 0x77, 0xdb, 0xc7, 0xb5, 0x8c, 0xfa, 0x82, 0x40, 0x55, 0xc1, + 0x34, 0xc7, 0xf8, 0x86, 0x86, 0x06, 0x7e, 0xa5, 0xe7, 0xf6, 0xd9, 0xc8, 0xe6, 0x29, + 0xcf, 0x9b, 0x63, 0xa7, 0x08, 0xd3, 0x73, 0x04, 0x05, 0x9e, 0x58, 0x03, 0x26, 0x79, + 0xee, 0xca, 0x92, 0xc4, 0xdc, 0x46, 0x12, 0x42, 0x4b, 0x2b, 0x4f, 0xa9, 0x01, 0xe6, + 0x74, 0xef, 0xa1, 0x02, 0x1a, 0x34, 0x04, 0xde, 0xbf, 0x73, 0x2f, 0x10}, + {0xc6, 0x45, 0x57, 0x7f, 0xab, 0xb9, 0x18, 0xeb, 0x90, 0xc6, 0x87, 0x57, 0xee, 0x8a, + 0x3a, 0x02, 0xa9, 0xaf, 0xf7, 0x2d, 0xda, 0x12, 0x27, 0xb7, 0x3d, 0x01, 0x5c, 0xea, + 0x25, 0x7d, 0x59, 0x36, 0x9a, 0x1c, 0x51, 0xb5, 0xe0, 0xda, 0xb4, 0xa2, 0x06, 0xff, + 0xff, 0x2b, 0x29, 0x60, 0xc8, 0x7a, 0x34, 0x42, 0x50, 0xf5, 0x5d, 0x37, 0x1f, 0x98, + 0x2d, 0xa1, 0x4e, 0xda, 0x25, 0xd7, 0x6b, 0x3f, 0xac, 0x58, 0x60, 0x10, 0x7b, 0x8d, + 0x4d, 0x73, 0x5f, 0x90, 0xc6, 0x6f, 0x9e, 0x57, 0x40, 0xd9, 0x2d, 0x93, 0x02, 0x92, + 0xf9, 0xf8, 0x66, 0x64, 0xd0, 0xd6, 0x60, 0xda, 0x19, 0xcc, 0x7e, 0x7b}, + {0x0d, 0x69, 0x5c, 0x69, 0x3c, 0x37, 0xc2, 0x78, 0x6e, 0x90, 0x42, 0x06, 0x66, 0x2e, + 0x25, 0xdd, 0xd2, 0x2b, 0xe1, 0x4a, 0x44, 0x44, 0x1d, 0x95, 0x56, 0x39, 0x74, 0x01, + 0x76, 0xad, 0x35, 0x42, 0x9b, 0xfa, 0x7c, 0xa7, 0x51, 0x4a, 0xae, 0x6d, 0x50, 0x86, + 0xa3, 0xe7, 0x54, 0x36, 0x26, 0x82, 0xdb, 0x82, 0x2d, 0x8f, 0xcd, 0xff, 0xbb, 0x09, + 0xba, 0xca, 0xf5, 0x1b, 0x66, 0xdc, 0xbe, 0x03, 0xf5, 0x75, 0x89, 0x07, 0x0d, 0xcb, + 0x58, 0x62, 0x98, 0xf2, 0x89, 0x91, 0x54, 0x42, 0x29, 0x49, 0xe4, 0x6e, 0xe3, 0xe2, + 0x23, 0xb4, 0xca, 0xa0, 0xa1, 0x66, 0xf0, 0xcd, 0xb0, 0xe2, 0x7c, 0x0e}, + {0xa3, 0x85, 0x8c, 0xc4, 0x3a, 0x64, 0x94, 0xc4, 0xad, 0x39, 0x61, 0x3c, 0xf4, 0x1d, + 0x36, 0xfd, 0x48, 0x4d, 0xe9, 0x3a, 0xdd, 0x17, 0xdb, 0x09, 0x4a, 0x67, 0xb4, 0x8f, + 0x5d, 0x0a, 0x6e, 0x66, 0xf9, 0x70, 0x4b, 0xd9, 0xdf, 0xfe, 0xa6, 0xfe, 0x2d, 0xba, + 0xfc, 0xc1, 0x51, 0xc0, 0x30, 0xf1, 0x89, 0xab, 0x2f, 0x7f, 0x7e, 0xd4, 0x82, 0x48, + 0xb5, 0xee, 0xec, 0x8a, 0x13, 0x56, 0x52, 0x61, 0x0d, 0xcb, 0x70, 0x48, 0x4e, 0xf6, + 0xbb, 0x2a, 0x6b, 0x8b, 0x45, 0xaa, 0xf0, 0xbc, 0x65, 0xcd, 0x5d, 0x98, 0xe8, 0x75, + 0xba, 0x4e, 0xbe, 0x9a, 0xe4, 0xde, 0x14, 0xd5, 0x10, 0xc8, 0x0b, 0x7f}, + {0x6f, 0x13, 0xf4, 0x26, 0xa4, 0x6b, 0x00, 0xb9, 0x35, 0x30, 0xe0, 0x57, 0x9e, 0x36, + 0x67, 0x8d, 0x28, 0x3c, 0x46, 0x4f, 0xd9, 0xdf, 0xc8, 0xcb, 0xf5, 0xdb, 0xee, 0xf8, + 0xbc, 0x8d, 0x1f, 0x0d, 0xa0, 0x13, 0x72, 0x73, 0xad, 0x9d, 0xac, 0x83, 0x98, 0x2e, + 0xf7, 0x2e, 0xba, 0xf8, 0xf6, 0x9f, 0x57, 0x69, 0xec, 0x43, 0xdd, 0x2e, 0x1e, 0x31, + 0x75, 0xab, 0xc5, 0xde, 0x7d, 0x90, 0x3a, 0x1d, 0xdc, 0x81, 0xd0, 0x3e, 0x31, 0x93, + 0x16, 0xba, 0x80, 0x34, 0x1b, 0x85, 0xad, 0x9f, 0x32, 0x29, 0xcb, 0x21, 0x03, 0x03, + 0x3c, 0x01, 0x28, 0x01, 0xe3, 0xfd, 0x1b, 0xa3, 0x44, 0x1b, 0x01, 0x00}, + {0x0c, 0x6c, 0xc6, 0x3f, 0x6c, 0xa0, 0xdf, 0x3f, 0xd2, 0x0d, 0xd6, 0x4d, 0x8e, 0xe3, + 0x40, 0x5d, 0x71, 0x4d, 0x8e, 0x26, 0x38, 0x8b, 0xe3, 0x7a, 0xe1, 0x57, 0x83, 0x6e, + 0x91, 0x8d, 0xc4, 0x3a, 0x5c, 0xa7, 0x0a, 0x6a, 0x69, 0x1f, 0x56, 0x16, 0x6a, 0xbd, + 0x52, 0x58, 0x5c, 0x72, 0xbf, 0xc1, 0xad, 0x66, 0x79, 0x9a, 0x7f, 0xdd, 0xa8, 0x11, + 0x26, 0x10, 0x85, 0xd2, 0xa2, 0x88, 0xd9, 0x63, 0x2e, 0x23, 0xbd, 0xaf, 0x53, 0x07, + 0x12, 0x00, 0x83, 0xf6, 0xd8, 0xfd, 0xb8, 0xce, 0x2b, 0xe9, 0x91, 0x2b, 0xe7, 0x84, + 0xb3, 0x69, 0x16, 0xf8, 0x66, 0xa0, 0x68, 0x23, 0x2b, 0xd5, 0xfa, 0x33}, + {0x16, 0x1e, 0xe4, 0xc5, 0xc6, 0x49, 0x06, 0x54, 0x35, 0x77, 0x3f, 0x33, 0x30, 0x64, + 0xf8, 0x0a, 0x46, 0xe7, 0x05, 0xf3, 0xd2, 0xfc, 0xac, 0xb2, 0xa7, 0xdc, 0x56, 0xa2, + 0x29, 0xf4, 0xc0, 0x16, 0xe8, 0xcf, 0x22, 0xc4, 0xd0, 0xc8, 0x2c, 0x8d, 0xcb, 0x3a, + 0xa1, 0x05, 0x7b, 0x4f, 0x2b, 0x07, 0x6f, 0xa5, 0xf6, 0xec, 0xe6, 0xb6, 0xfe, 0xa3, + 0xe2, 0x71, 0x0a, 0xb9, 0xcc, 0x55, 0xc3, 0x3c, 0x31, 0x91, 0x3e, 0x90, 0x43, 0x94, + 0xb6, 0xe9, 0xce, 0x37, 0x56, 0x7a, 0xcb, 0x94, 0xa4, 0xb8, 0x44, 0x92, 0xba, 0xba, + 0xa4, 0xd1, 0x7c, 0xc8, 0x68, 0x75, 0xae, 0x6b, 0x42, 0xaf, 0x1e, 0x63}, + {0x9f, 0xfe, 0x66, 0xda, 0x10, 0x04, 0xe9, 0xb3, 0xa6, 0xe5, 0x16, 0x6c, 0x52, 0x4b, + 0xdd, 0x85, 0x83, 0xbf, 0xf9, 0x1e, 0x61, 0x97, 0x3d, 0xbc, 0xb5, 0x19, 0xa9, 0x1e, + 0x8b, 0x64, 0x99, 0x55, 0xe8, 0x0d, 0x70, 0xa3, 0xb9, 0x75, 0xd9, 0x47, 0x52, 0x05, + 0xf8, 0xe2, 0xfb, 0xc5, 0x80, 0x72, 0xe1, 0x5d, 0xe4, 0x32, 0x27, 0x8f, 0x65, 0x53, + 0xb5, 0x80, 0x5f, 0x66, 0x7f, 0x2c, 0x1f, 0x43, 0x19, 0x7b, 0x8f, 0x85, 0x44, 0x63, + 0x02, 0xd6, 0x4a, 0x51, 0xea, 0xa1, 0x2f, 0x35, 0xab, 0x14, 0xd7, 0xa9, 0x90, 0x20, + 0x1a, 0x44, 0x00, 0x89, 0x26, 0x3b, 0x25, 0x91, 0x5f, 0x71, 0x04, 0x7b}, + {0x43, 0xae, 0xf6, 0xac, 0x28, 0xbd, 0xed, 0x83, 0xb4, 0x7a, 0x5c, 0x7d, 0x8b, 0x7c, + 0x35, 0x86, 0x44, 0x2c, 0xeb, 0xb7, 0x69, 0x47, 0x40, 0xc0, 0x3f, 0x58, 0xf6, 0xc2, + 0xf5, 0x7b, 0xb3, 0x59, 0xc6, 0xba, 0xe6, 0xc4, 0x80, 0xc2, 0x76, 0xb3, 0x0b, 0x9b, + 0x1d, 0x6d, 0xdd, 0xd3, 0x0e, 0x97, 0x44, 0xf9, 0x0b, 0x45, 0x58, 0x95, 0x9a, 0xb0, + 0x23, 0xe2, 0xcd, 0x57, 0xfa, 0xac, 0xd0, 0x48, 0x71, 0xe6, 0xab, 0x7d, 0xe4, 0x26, + 0x0f, 0xb6, 0x37, 0x3a, 0x2f, 0x62, 0x97, 0xa1, 0xd1, 0xf1, 0x94, 0x03, 0x96, 0xe9, + 0x7e, 0xce, 0x08, 0x42, 0xdb, 0x3b, 0x6d, 0x33, 0x91, 0x41, 0x23, 0x16}, + {0xf6, 0x7f, 0x26, 0xf6, 0xde, 0x99, 0xe4, 0xb9, 0x43, 0x08, 0x2c, 0x74, 0x7b, 0xca, + 0x72, 0x77, 0xb1, 0xf2, 0xa4, 0xe9, 0x3f, 0x15, 0xa0, 0x23, 0x06, 0x50, 0xd0, 0xd5, + 0xec, 0xdf, 0xdf, 0x2c, 0x40, 0x86, 0xf3, 0x1f, 0xd6, 0x9c, 0x49, 0xdd, 0xa0, 0x25, + 0x36, 0x06, 0xc3, 0x9b, 0xcd, 0x29, 0xc3, 0x3d, 0xd7, 0x3d, 0x02, 0xd8, 0xe2, 0x51, + 0x31, 0x92, 0x3b, 0x20, 0x7a, 0x70, 0x25, 0x4a, 0x6a, 0xed, 0xf6, 0x53, 0x8a, 0x66, + 0xb7, 0x2a, 0xa1, 0x70, 0xd1, 0x1d, 0x58, 0x42, 0x42, 0x30, 0x61, 0x01, 0xe2, 0x3a, + 0x4c, 0x14, 0x00, 0x40, 0xfc, 0x49, 0x8e, 0x24, 0x6d, 0x89, 0x21, 0x57}, + {0xae, 0x1b, 0x18, 0xfd, 0x17, 0x55, 0x6e, 0x0b, 0xb4, 0x63, 0xb9, 0x2b, 0x9f, 0x62, + 0x22, 0x90, 0x25, 0x46, 0x06, 0x32, 0xe9, 0xbc, 0x09, 0x55, 0xda, 0x13, 0x3c, 0xf6, + 0x74, 0xdd, 0x8e, 0x57, 0x4e, 0xda, 0xd0, 0xa1, 0x91, 0x50, 0x5d, 0x28, 0x08, 0x3e, + 0xfe, 0xb5, 0xa7, 0x6f, 0xaa, 0x4b, 0xb3, 0x93, 0x93, 0xe1, 0x7c, 0x17, 0xe5, 0x63, + 0xfd, 0x30, 0xb0, 0xc4, 0xaf, 0x35, 0xc9, 0x03, 0x3d, 0x0c, 0x2b, 0x49, 0xc6, 0x76, + 0x72, 0x99, 0xfc, 0x05, 0xe2, 0xdf, 0xc4, 0xc2, 0xcc, 0x47, 0x3c, 0x3a, 0x62, 0xdd, + 0x84, 0x9b, 0xd2, 0xdc, 0xa2, 0xc7, 0x88, 0x02, 0x59, 0xab, 0xc2, 0x3e}, + {0xb9, 0x7b, 0xd8, 0xe4, 0x7b, 0xd2, 0xa0, 0xa1, 0xed, 0x1a, 0x39, 0x61, 0xeb, 0x4d, + 0x8b, 0xa9, 0x83, 0x9b, 0xcb, 0x73, 0xd0, 0xdd, 0xa0, 0x99, 0xce, 0xca, 0x0f, 0x20, + 0x5a, 0xc2, 0xd5, 0x2d, 0xcb, 0xd1, 0x32, 0xae, 0x09, 0x3a, 0x21, 0xa7, 0xd5, 0xc2, + 0xf5, 0x40, 0xdf, 0x87, 0x2b, 0x0f, 0x29, 0xab, 0x1e, 0xe8, 0xc6, 0xa4, 0xae, 0x0b, + 0x5e, 0xac, 0xdb, 0x6a, 0x6c, 0xf6, 0x1b, 0x0e, 0x7e, 0x88, 0x2c, 0x79, 0xe9, 0xd5, + 0xab, 0xe2, 0x5d, 0x6d, 0x92, 0xcb, 0x18, 0x00, 0x02, 0x1a, 0x1e, 0x5f, 0xae, 0xba, + 0xcd, 0x69, 0xba, 0xbf, 0x5f, 0x8f, 0xe8, 0x5a, 0xb3, 0x48, 0x05, 0x73}, + {0xee, 0xb8, 0xa8, 0xcb, 0xa3, 0x51, 0x35, 0xc4, 0x16, 0x5f, 0x11, 0xb2, 0x1d, 0x6f, + 0xa2, 0x65, 0x50, 0x38, 0x8c, 0xab, 0x52, 0x4f, 0x0f, 0x76, 0xca, 0xb8, 0x1d, 0x41, + 0x3b, 0x44, 0x43, 0x30, 0x34, 0xe3, 0xd6, 0xa1, 0x4b, 0x09, 0x5b, 0x80, 0x19, 0x3f, + 0x35, 0x09, 0x77, 0xf1, 0x3e, 0xbf, 0x2b, 0x70, 0x22, 0x06, 0xcb, 0x06, 0x3f, 0x42, + 0xdd, 0x45, 0x78, 0xd8, 0x77, 0x22, 0x5a, 0x58, 0x62, 0x89, 0xd4, 0x33, 0x82, 0x5f, + 0x8a, 0xa1, 0x7f, 0x25, 0x78, 0xec, 0xb5, 0xc4, 0x98, 0x66, 0xff, 0x41, 0x3e, 0x37, + 0xa5, 0x6f, 0x8e, 0xa7, 0x1f, 0x98, 0xef, 0x50, 0x89, 0x27, 0x56, 0x76}, + {0xc0, 0xc8, 0x1f, 0xd5, 0x59, 0xcf, 0xc3, 0x38, 0xf2, 0xb6, 0x06, 0x05, 0xfd, 0xd2, + 0xed, 0x9b, 0x8f, 0x0e, 0x57, 0xab, 0x9f, 0x10, 0xbf, 0x26, 0xa6, 0x46, 0xb8, 0xc1, + 0xa8, 0x60, 0x41, 0x3f, 0x9d, 0xcf, 0x86, 0xea, 0xa3, 0x73, 0x70, 0xe1, 0xdc, 0x5f, + 0x15, 0x07, 0xb7, 0xfb, 0x8c, 0x3a, 0x8e, 0x8a, 0x83, 0x31, 0xfc, 0xe7, 0x53, 0x48, + 0x16, 0xf6, 0x13, 0xb6, 0x84, 0xf4, 0xbb, 0x28, 0x7c, 0x6c, 0x13, 0x6f, 0x5c, 0x2f, + 0x61, 0xf2, 0xbe, 0x11, 0xdd, 0xf6, 0x07, 0xd1, 0xea, 0xaf, 0x33, 0x6f, 0xde, 0x13, + 0xd2, 0x9a, 0x7e, 0x52, 0x5d, 0xf7, 0x88, 0x81, 0x35, 0xcb, 0x79, 0x1e}, + {0xf1, 0xe3, 0xf7, 0xee, 0xc3, 0x36, 0x34, 0x01, 0xf8, 0x10, 0x9e, 0xfe, 0x7f, 0x6a, + 0x8b, 0x82, 0xfc, 0xde, 0xf9, 0xbc, 0xe5, 0x08, 0xf9, 0x7f, 0x31, 0x38, 0x3b, 0x3a, + 0x1b, 0x95, 0xd7, 0x65, 0x81, 0x81, 0xe0, 0xf5, 0xd8, 0x53, 0xe9, 0x77, 0xd9, 0xde, + 0x9d, 0x29, 0x44, 0x0c, 0xa5, 0x84, 0xe5, 0x25, 0x45, 0x86, 0x0c, 0x2d, 0x6c, 0xdc, + 0xf4, 0xf2, 0xd1, 0x39, 0x2d, 0xb5, 0x8a, 0x47, 0x59, 0xd1, 0x52, 0x92, 0xd3, 0xa4, + 0xa6, 0x66, 0x07, 0xc8, 0x1a, 0x87, 0xbc, 0xe1, 0xdd, 0xe5, 0x6f, 0xc9, 0xc1, 0xa6, + 0x40, 0x6b, 0x2c, 0xb8, 0x14, 0x22, 0x21, 0x1a, 0x41, 0x7a, 0xd8, 0x16}, + {0x15, 0x62, 0x06, 0x42, 0x5a, 0x7e, 0xbd, 0xb3, 0xc1, 0x24, 0x5a, 0x0c, 0xcd, 0xe3, + 0x9b, 0x87, 0xb7, 0x94, 0xf9, 0xd6, 0xb1, 0x5d, 0xc0, 0x57, 0xa6, 0x8c, 0xf3, 0x65, + 0x81, 0x7c, 0xf8, 0x28, 0x83, 0x05, 0x4e, 0xd5, 0xe2, 0xd5, 0xa4, 0xfb, 0xfa, 0x99, + 0xbd, 0x2e, 0xd7, 0xaf, 0x1f, 0xe2, 0x8f, 0x77, 0xe9, 0x6e, 0x73, 0xc2, 0x7a, 0x49, + 0xde, 0x6d, 0x5a, 0x7a, 0x57, 0x0b, 0x99, 0x1f, 0xd6, 0xf7, 0xe8, 0x1b, 0xad, 0x4e, + 0x34, 0xa3, 0x8f, 0x79, 0xea, 0xac, 0xeb, 0x50, 0x1e, 0x7d, 0x52, 0xe0, 0x0d, 0x52, + 0x9e, 0x56, 0xc6, 0x77, 0x3e, 0x6d, 0x4d, 0x53, 0xe1, 0x2f, 0x88, 0x45}, + {0xd6, 0x83, 0x79, 0x75, 0x5d, 0x34, 0x69, 0x66, 0xa6, 0x11, 0xaa, 0x17, 0x11, 0xed, + 0xb6, 0x62, 0x8f, 0x12, 0x5e, 0x98, 0x57, 0x18, 0xdd, 0x7d, 0xdd, 0xf6, 0x26, 0xf6, + 0xb8, 0xe5, 0x8f, 0x68, 0xe4, 0x6f, 0x3c, 0x94, 0x29, 0x99, 0xac, 0xd8, 0xa2, 0x92, + 0x83, 0xa3, 0x61, 0xf1, 0xf9, 0xb5, 0xf3, 0x9a, 0xc8, 0xbe, 0x13, 0xdb, 0x99, 0x26, + 0x74, 0xf0, 0x05, 0xe4, 0x3c, 0x84, 0xcf, 0x7d, 0xc0, 0x32, 0x47, 0x4a, 0x48, 0xd6, + 0x90, 0x6c, 0x99, 0x32, 0x56, 0xca, 0xfd, 0x43, 0x21, 0xd5, 0xe1, 0xc6, 0x5d, 0x91, + 0xc3, 0x28, 0xbe, 0xb3, 0x1b, 0x19, 0x27, 0x73, 0x7e, 0x68, 0x39, 0x67}, + {0xa6, 0x75, 0x56, 0x38, 0x14, 0x20, 0x78, 0xef, 0xe8, 0xa9, 0xfd, 0xaa, 0x30, 0x9f, + 0x64, 0xa2, 0xcb, 0xa8, 0xdf, 0x5c, 0x50, 0xeb, 0xd1, 0x4c, 0xb3, 0xc0, 0x4d, 0x1d, + 0xba, 0x5a, 0x11, 0x46, 0xc0, 0x1a, 0x0c, 0xc8, 0x9d, 0xcc, 0x6d, 0xa6, 0x36, 0xa4, + 0x38, 0x1b, 0xf4, 0x5c, 0xa0, 0x97, 0xc6, 0xd7, 0xdb, 0x95, 0xbe, 0xf3, 0xeb, 0xa7, + 0xab, 0x7d, 0x7e, 0x8d, 0xf6, 0xb8, 0xa0, 0x7d, 0x76, 0xda, 0xb5, 0xc3, 0x53, 0x19, + 0x0f, 0xd4, 0x9b, 0x9e, 0x11, 0x21, 0x73, 0x6f, 0xac, 0x1d, 0x60, 0x59, 0xb2, 0xfe, + 0x21, 0x60, 0xcc, 0x03, 0x4b, 0x4b, 0x67, 0x83, 0x7e, 0x88, 0x5f, 0x5a}, + {0x11, 0x3d, 0xa1, 0x70, 0xcf, 0x01, 0x63, 0x8f, 0xc4, 0xd0, 0x0d, 0x35, 0x15, 0xb8, + 0xce, 0xcf, 0x7e, 0xa4, 0xbc, 0xa4, 0xd4, 0x97, 0x02, 0xf7, 0x34, 0x14, 0x4d, 0xe4, + 0x56, 0xb6, 0x69, 0x36, 0xb9, 0x43, 0xa6, 0xa0, 0xd3, 0x28, 0x96, 0x9e, 0x64, 0x20, + 0xc3, 0xe6, 0x00, 0xcb, 0xc3, 0xb5, 0x32, 0xec, 0x2d, 0x7c, 0x89, 0x02, 0x53, 0x9b, + 0x0c, 0xc7, 0xd1, 0xd5, 0xe2, 0x7a, 0xe3, 0x43, 0x33, 0xe1, 0xa6, 0xed, 0x06, 0x3f, + 0x7e, 0x38, 0xc0, 0x3a, 0xa1, 0x99, 0x51, 0x1d, 0x30, 0x67, 0x11, 0x38, 0x26, 0x36, + 0xf8, 0xd8, 0x5a, 0xbd, 0xbe, 0xe9, 0xd5, 0x4f, 0xcd, 0xe6, 0x21, 0x6a}, + {0x5f, 0xe6, 0x46, 0x30, 0x0a, 0x17, 0xc6, 0xf1, 0x24, 0x35, 0xd2, 0x00, 0x2a, 0x2a, + 0x71, 0x58, 0x55, 0xb7, 0x82, 0x8c, 0x3c, 0xbd, 0xdb, 0x69, 0x57, 0xff, 0x95, 0xa1, + 0xf1, 0xf9, 0x6b, 0x58, 0xe3, 0xb2, 0x99, 0x66, 0x12, 0x29, 0x41, 0xef, 0x01, 0x13, + 0x8d, 0x70, 0x47, 0x08, 0xd3, 0x71, 0xbd, 0xb0, 0x82, 0x11, 0xd0, 0x32, 0x54, 0x32, + 0x36, 0x8b, 0x1e, 0x00, 0x07, 0x1b, 0x37, 0x45, 0x0b, 0x79, 0xf8, 0x5e, 0x8d, 0x08, + 0xdb, 0xa6, 0xe5, 0x37, 0x09, 0x61, 0xdc, 0xf0, 0x78, 0x52, 0xb8, 0x6e, 0xa1, 0x61, + 0xd2, 0x49, 0x03, 0xac, 0x79, 0x21, 0xe5, 0x90, 0x37, 0xb0, 0xaf, 0x0e}, + {0x2f, 0x04, 0x48, 0x37, 0xc1, 0x55, 0x05, 0x96, 0x11, 0xaa, 0x0b, 0x82, 0xe6, 0x41, + 0x9a, 0x21, 0x0c, 0x6d, 0x48, 0x73, 0x38, 0xf7, 0x81, 0x1c, 0x61, 0xc6, 0x02, 0x5a, + 0x67, 0xcc, 0x9a, 0x30, 0x1d, 0xae, 0x75, 0x0f, 0x5e, 0x80, 0x40, 0x51, 0x30, 0xcc, + 0x62, 0x26, 0xe3, 0xfb, 0x02, 0xec, 0x6d, 0x39, 0x92, 0xea, 0x1e, 0xdf, 0xeb, 0x2c, + 0xb3, 0x5b, 0x43, 0xc5, 0x44, 0x33, 0xae, 0x44, 0xee, 0x43, 0xa5, 0xbb, 0xb9, 0x89, + 0xf2, 0x9c, 0x42, 0x71, 0xc9, 0x5a, 0x9d, 0x0e, 0x76, 0xf3, 0xaa, 0x60, 0x93, 0x4f, + 0xc6, 0xe5, 0x82, 0x1d, 0x8f, 0x67, 0x94, 0x7f, 0x1b, 0x22, 0xd5, 0x62}, + {0x6d, 0x93, 0xd0, 0x18, 0x9c, 0x29, 0x4c, 0x52, 0x0c, 0x1a, 0x0c, 0x8a, 0x6c, 0xb5, + 0x6b, 0xc8, 0x31, 0x86, 0x4a, 0xdb, 0x2e, 0x05, 0x75, 0xa3, 0x62, 0x45, 0x75, 0xbc, + 0xe4, 0xfd, 0x0e, 0x5c, 0x3c, 0x7a, 0xf7, 0x3a, 0x26, 0xd4, 0x85, 0x75, 0x4d, 0x14, + 0xe9, 0xfe, 0x11, 0x7b, 0xae, 0xdf, 0x3d, 0x19, 0xf7, 0x59, 0x80, 0x70, 0x06, 0xa5, + 0x37, 0x20, 0x92, 0x83, 0x53, 0x9a, 0xf2, 0x14, 0xf5, 0xd7, 0xb2, 0x25, 0xdc, 0x7e, + 0x71, 0xdf, 0x40, 0x30, 0xb5, 0x99, 0xdb, 0x70, 0xf9, 0x21, 0x62, 0x4c, 0xed, 0xc3, + 0xb7, 0x34, 0x92, 0xda, 0x3e, 0x09, 0xee, 0x7b, 0x5c, 0x36, 0x72, 0x5e}, + {0x7f, 0x21, 0x71, 0x45, 0x07, 0xfc, 0x5b, 0x57, 0x5b, 0xd9, 0x94, 0x06, 0x5d, 0x67, + 0x79, 0x37, 0x33, 0x1e, 0x19, 0xf4, 0xbb, 0x37, 0x0a, 0x9a, 0xbc, 0xea, 0xb4, 0x47, + 0x4c, 0x10, 0xf1, 0x77, 0x3e, 0xb3, 0x08, 0x2f, 0x06, 0x39, 0x93, 0x7d, 0xbe, 0x32, + 0x9f, 0xdf, 0xe5, 0x59, 0x96, 0x5b, 0xfd, 0xbd, 0x9e, 0x1f, 0xad, 0x3d, 0xff, 0xac, + 0xb7, 0x49, 0x73, 0xcb, 0x55, 0x05, 0xb2, 0x70, 0x4c, 0x2c, 0x11, 0x55, 0xc5, 0x13, + 0x51, 0xbe, 0xcd, 0x1f, 0x88, 0x9a, 0x3a, 0x42, 0x88, 0x66, 0x47, 0x3b, 0x50, 0x5e, + 0x85, 0x77, 0x66, 0x44, 0x4a, 0x40, 0x06, 0x4a, 0x8f, 0x39, 0x34, 0x0e}, + {0xe8, 0xbd, 0xce, 0x3e, 0xd9, 0x22, 0x7d, 0xb6, 0x07, 0x2f, 0x82, 0x27, 0x41, 0xe8, + 0xb3, 0x09, 0x8d, 0x6d, 0x5b, 0xb0, 0x1f, 0xa6, 0x3f, 0x74, 0x72, 0x23, 0x36, 0x8a, + 0x36, 0x05, 0x54, 0x5e, 0x28, 0x19, 0x4b, 0x3e, 0x09, 0x0b, 0x93, 0x18, 0x40, 0xf6, + 0xf3, 0x73, 0x0e, 0xe1, 0xe3, 0x7d, 0x6f, 0x5d, 0x39, 0x73, 0xda, 0x17, 0x32, 0xf4, + 0x3e, 0x9c, 0x37, 0xca, 0xd6, 0xde, 0x8a, 0x6f, 0x9a, 0xb2, 0xb7, 0xfd, 0x3d, 0x12, + 0x40, 0xe3, 0x91, 0xb2, 0x1a, 0xa2, 0xe1, 0x97, 0x7b, 0x48, 0x9e, 0x94, 0xe6, 0xfd, + 0x02, 0x7d, 0x96, 0xf9, 0x97, 0xde, 0xd3, 0xc8, 0x2e, 0xe7, 0x0d, 0x78}, + {0xbc, 0xe7, 0x9a, 0x08, 0x45, 0x85, 0xe2, 0x0a, 0x06, 0x4d, 0x7f, 0x1c, 0xcf, 0xde, + 0x8d, 0x38, 0xb8, 0x11, 0x48, 0x0a, 0x51, 0x15, 0xac, 0x38, 0xe4, 0x8c, 0x92, 0x71, + 0xf6, 0x8b, 0xb2, 0x0e, 0x72, 0x27, 0xf4, 0x00, 0xf3, 0xea, 0x1f, 0x67, 0xaa, 0x41, + 0x8c, 0x2a, 0x2a, 0xeb, 0x72, 0x8f, 0x92, 0x32, 0x37, 0x97, 0xd7, 0x7f, 0xa1, 0x29, + 0xa6, 0x87, 0xb5, 0x32, 0xad, 0xc6, 0xef, 0x1d, 0xa7, 0x95, 0x51, 0xef, 0x1a, 0xbe, + 0x5b, 0xaf, 0xed, 0x15, 0x7b, 0x91, 0x77, 0x12, 0x8c, 0x14, 0x2e, 0xda, 0xe5, 0x7a, + 0xfb, 0xf7, 0x91, 0x29, 0x67, 0x28, 0xdd, 0xf8, 0x1b, 0x20, 0x7d, 0x46}, + {0xad, 0x4f, 0xef, 0x74, 0x9a, 0x91, 0xfe, 0x95, 0xa2, 0x08, 0xa3, 0xf6, 0xec, 0x7b, + 0x82, 0x3a, 0x01, 0x7b, 0xa4, 0x09, 0xd3, 0x01, 0x4e, 0x96, 0x97, 0xc7, 0xa3, 0x5b, + 0x4f, 0x3c, 0xc4, 0x71, 0xa9, 0xe7, 0x7a, 0x56, 0xbd, 0xf4, 0x1e, 0xbc, 0xbd, 0x98, + 0x44, 0xd6, 0xb2, 0x4c, 0x62, 0x3f, 0xc8, 0x4e, 0x1f, 0x2c, 0xd2, 0x64, 0x10, 0xe4, + 0x01, 0x40, 0x38, 0xba, 0xa5, 0xc5, 0xf9, 0x2e, 0xcd, 0x74, 0x9e, 0xfa, 0xf6, 0x6d, + 0xfd, 0xb6, 0x7a, 0x26, 0xaf, 0xe4, 0xbc, 0x78, 0x82, 0xf1, 0x0e, 0x99, 0xef, 0xf1, + 0xd0, 0xb3, 0x55, 0x82, 0x93, 0xf2, 0xc5, 0x90, 0xa3, 0x8c, 0x75, 0x5a}, + {0x95, 0x24, 0x46, 0xd9, 0x10, 0x27, 0xb7, 0xa2, 0x03, 0x50, 0x7d, 0xd5, 0xd2, 0xc6, + 0xa8, 0x3a, 0xca, 0x87, 0xb4, 0xa0, 0xbf, 0x00, 0xd4, 0xe3, 0xec, 0x72, 0xeb, 0xb3, + 0x44, 0xe2, 0xba, 0x2d, 0x94, 0xdc, 0x61, 0x1d, 0x8b, 0x91, 0xe0, 0x8c, 0x66, 0x30, + 0x81, 0x9a, 0x46, 0x36, 0xed, 0x8d, 0xd3, 0xaa, 0xe8, 0xaf, 0x29, 0xa8, 0xe6, 0xd4, + 0x3f, 0xd4, 0x39, 0xf6, 0x27, 0x80, 0x73, 0x0a, 0xcc, 0xe1, 0xff, 0x57, 0x2f, 0x4a, + 0x0f, 0x98, 0x43, 0x98, 0x83, 0xe1, 0x0d, 0x0d, 0x67, 0x00, 0xfd, 0x15, 0xfb, 0x49, + 0x4a, 0x3f, 0x5c, 0x10, 0x9c, 0xa6, 0x26, 0x51, 0x63, 0xca, 0x98, 0x26}, + {0x78, 0xba, 0xb0, 0x32, 0x88, 0x31, 0x65, 0xe7, 0x8b, 0xff, 0x5c, 0x92, 0xf7, 0x31, + 0x18, 0x38, 0xcc, 0x1f, 0x29, 0xa0, 0x91, 0x1b, 0xa8, 0x08, 0x07, 0xeb, 0xca, 0x49, + 0xcc, 0x3d, 0xb4, 0x1f, 0x0e, 0xd9, 0x3d, 0x5e, 0x2f, 0x70, 0x3d, 0x2e, 0x86, 0x53, + 0xd2, 0xe4, 0x18, 0x09, 0x3f, 0x9e, 0x6a, 0xa9, 0x4d, 0x02, 0xf6, 0x3e, 0x77, 0x5e, + 0x32, 0x33, 0xfa, 0x4a, 0x0c, 0x4b, 0x00, 0x3c, 0x2b, 0xb8, 0xf4, 0x06, 0xac, 0x46, + 0xa9, 0x9a, 0xf3, 0xc4, 0x06, 0xa8, 0xa5, 0x84, 0xa2, 0x1c, 0x87, 0x47, 0xcd, 0xc6, + 0x5f, 0x26, 0xd3, 0x3e, 0x17, 0xd2, 0x1f, 0xcd, 0x01, 0xfd, 0x43, 0x6b}, + {0x44, 0xc5, 0x97, 0x46, 0x4b, 0x5d, 0xa7, 0xc7, 0xbf, 0xff, 0x0f, 0xdf, 0x48, 0xf8, + 0xfd, 0x15, 0x5a, 0x78, 0x46, 0xaa, 0xeb, 0xb9, 0x68, 0x28, 0x14, 0xf7, 0x52, 0x5b, + 0x10, 0xd7, 0x68, 0x5a, 0xf3, 0x0e, 0x76, 0x3e, 0x58, 0x42, 0xc7, 0xb5, 0x90, 0xb9, + 0x0a, 0xee, 0xb9, 0x52, 0xdc, 0x75, 0x3f, 0x92, 0x2b, 0x07, 0xc2, 0x27, 0x14, 0xbf, + 0xf0, 0xd9, 0xf0, 0x6f, 0x2d, 0x0b, 0x42, 0x73, 0x06, 0x1e, 0x85, 0x9e, 0xcb, 0xf6, + 0x2c, 0xaf, 0xc4, 0x38, 0x22, 0xc6, 0x13, 0x39, 0x59, 0x8f, 0x73, 0xf3, 0xfb, 0x99, + 0x96, 0xb8, 0x8a, 0xda, 0x9e, 0xbc, 0x34, 0xea, 0x2f, 0x63, 0xb5, 0x3d}, + {0xd8, 0xd9, 0x5d, 0xf7, 0x2b, 0xee, 0x6e, 0xf4, 0xa5, 0x59, 0x67, 0x39, 0xf6, 0xb1, + 0x17, 0x0d, 0x73, 0x72, 0x9e, 0x49, 0x31, 0xd1, 0xf2, 0x1b, 0x13, 0x5f, 0xd7, 0x49, + 0xdf, 0x1a, 0x32, 0x04, 0xd5, 0x25, 0x98, 0x82, 0xb1, 0x90, 0x49, 0x2e, 0x91, 0x89, + 0x9a, 0x3e, 0x87, 0xeb, 0xea, 0xed, 0xf8, 0x4a, 0x70, 0x4c, 0x39, 0x3d, 0xf0, 0xee, + 0x0e, 0x2b, 0xdf, 0x95, 0xa4, 0x7e, 0x19, 0x59, 0xae, 0x5a, 0xe5, 0xe4, 0x19, 0x60, + 0xe1, 0x04, 0xe9, 0x92, 0x2f, 0x7e, 0x7a, 0x43, 0x7b, 0xe7, 0xa4, 0x9a, 0x15, 0x6f, + 0xc1, 0x2d, 0xce, 0xc7, 0xc0, 0x0c, 0xd7, 0xf4, 0xc1, 0xfd, 0xea, 0x45}, + {0x2b, 0xd7, 0x45, 0x80, 0x85, 0x01, 0x84, 0x69, 0x51, 0x06, 0x2f, 0xcf, 0xa2, 0xfa, + 0x22, 0x4c, 0xc6, 0x2d, 0x22, 0x6b, 0x65, 0x36, 0x1a, 0x94, 0xde, 0xda, 0x62, 0x03, + 0xc8, 0xeb, 0x5e, 0x5a, 0xed, 0xb1, 0xcc, 0xcf, 0x24, 0x46, 0x0e, 0xb6, 0x95, 0x03, + 0x5c, 0xbd, 0x92, 0xc2, 0xdb, 0x59, 0xc9, 0x81, 0x04, 0xdc, 0x1d, 0x9d, 0xa0, 0x31, + 0x40, 0xd9, 0x56, 0x5d, 0xea, 0xce, 0x73, 0x3f, 0xc6, 0x8d, 0x4e, 0x0a, 0xd1, 0xbf, + 0xa7, 0xb7, 0x39, 0xb3, 0xc9, 0x44, 0x7e, 0x00, 0x57, 0xbe, 0xfa, 0xae, 0x57, 0x15, + 0x7f, 0x20, 0xc1, 0x60, 0xdb, 0x18, 0x62, 0x26, 0x91, 0x88, 0x05, 0x26}, + {0x04, 0xff, 0x60, 0x83, 0xa6, 0x04, 0xf7, 0x59, 0xf4, 0xe6, 0x61, 0x76, 0xde, 0x3f, + 0xd9, 0xc3, 0x51, 0x35, 0x87, 0x12, 0x73, 0x2a, 0x1b, 0x83, 0x57, 0x5d, 0x61, 0x4e, + 0x2e, 0x0c, 0xad, 0x54, 0x42, 0xe5, 0x76, 0xc6, 0x3c, 0x8e, 0x81, 0x4c, 0xad, 0xcc, + 0xce, 0x03, 0x93, 0x2c, 0x42, 0x5e, 0x08, 0x9f, 0x12, 0xb4, 0xca, 0xcc, 0x07, 0xec, + 0xb8, 0x43, 0x44, 0xb2, 0x10, 0xfa, 0xed, 0x0d, 0x2a, 0x52, 0x2b, 0xb8, 0xd5, 0x67, + 0x3b, 0xee, 0xeb, 0xc1, 0xa5, 0x9f, 0x46, 0x63, 0xf1, 0x36, 0xd3, 0x9f, 0xc1, 0x6e, + 0xf2, 0xd2, 0xb4, 0xa5, 0x08, 0x94, 0x7a, 0xa7, 0xba, 0xb2, 0xec, 0x62}, + {0x3d, 0x2b, 0x15, 0x61, 0x52, 0x79, 0xed, 0xe5, 0xd1, 0xd7, 0xdd, 0x0e, 0x7d, 0x35, + 0x62, 0x49, 0x71, 0x4c, 0x6b, 0xb9, 0xd0, 0xc8, 0x82, 0x74, 0xbe, 0xd8, 0x66, 0xa9, + 0x19, 0xf9, 0x59, 0x2e, 0x74, 0x28, 0xb6, 0xaf, 0x36, 0x28, 0x07, 0x92, 0xa5, 0x04, + 0xe1, 0x79, 0x85, 0x5e, 0xcd, 0x5f, 0x4a, 0xa1, 0x30, 0xc6, 0xad, 0x01, 0xad, 0x5a, + 0x98, 0x3f, 0x66, 0x75, 0x50, 0x3d, 0x91, 0x61, 0xda, 0x31, 0x32, 0x1a, 0x36, 0x2d, + 0xc6, 0x0d, 0x70, 0x02, 0x20, 0x94, 0x32, 0x58, 0x47, 0xfa, 0xce, 0x94, 0x95, 0x3f, + 0x51, 0x01, 0xd8, 0x02, 0x5c, 0x5d, 0xc0, 0x31, 0xa1, 0xc2, 0xdb, 0x3d}, + {0x4b, 0xc5, 0x5e, 0xce, 0xf9, 0x0f, 0xdc, 0x9a, 0x0d, 0x13, 0x2f, 0x8c, 0x6b, 0x2a, + 0x9c, 0x03, 0x15, 0x95, 0xf8, 0xf0, 0xc7, 0x07, 0x80, 0x02, 0x6b, 0xb3, 0x04, 0xac, + 0x14, 0x83, 0x96, 0x78, 0x14, 0xbb, 0x96, 0x27, 0xa2, 0x57, 0xaa, 0xf3, 0x21, 0xda, + 0x07, 0x9b, 0xb7, 0xba, 0x3a, 0x88, 0x1c, 0x39, 0xa0, 0x31, 0x18, 0xe2, 0x4b, 0xe5, + 0xf9, 0x05, 0x32, 0xd8, 0x38, 0xfb, 0xe7, 0x5e, 0x8e, 0x6a, 0x44, 0x41, 0xcb, 0xfd, + 0x8d, 0x53, 0xf9, 0x37, 0x49, 0x43, 0xa9, 0xfd, 0xac, 0xa5, 0x78, 0x8c, 0x3c, 0x26, + 0x8d, 0x90, 0xaf, 0x46, 0x09, 0x0d, 0xca, 0x9b, 0x3c, 0x63, 0xd0, 0x61}, + {0x66, 0x25, 0xdb, 0xff, 0x35, 0x49, 0x74, 0x63, 0xbb, 0x68, 0x0b, 0x78, 0x89, 0x6b, + 0xbd, 0xc5, 0x03, 0xec, 0x3e, 0x55, 0x80, 0x32, 0x1b, 0x6f, 0xf5, 0xd7, 0xae, 0x47, + 0xd8, 0x5f, 0x96, 0x6e, 0xdf, 0x73, 0xfc, 0xf8, 0xbc, 0x28, 0xa3, 0xad, 0xfc, 0x37, + 0xf0, 0xa6, 0x5d, 0x69, 0x84, 0xee, 0x09, 0xa9, 0xc2, 0x38, 0xdb, 0xb4, 0x7f, 0x63, + 0xdc, 0x7b, 0x06, 0xf8, 0x2d, 0xac, 0x23, 0x5b, 0x7b, 0x52, 0x80, 0xee, 0x53, 0xb9, + 0xd2, 0x9a, 0x8d, 0x6d, 0xde, 0xfa, 0xaa, 0x19, 0x8f, 0xe8, 0xcf, 0x82, 0x0e, 0x15, + 0x04, 0x17, 0x71, 0x0e, 0xdc, 0xde, 0x95, 0xdd, 0xb9, 0xbb, 0xb9, 0x79}, + {0xc2, 0x26, 0x31, 0x6a, 0x40, 0x55, 0xb3, 0xeb, 0x93, 0xc3, 0xc8, 0x68, 0xa8, 0x83, + 0x63, 0xd2, 0x82, 0x7a, 0xb9, 0xe5, 0x29, 0x64, 0x0c, 0x6c, 0x47, 0x21, 0xfd, 0xc9, + 0x58, 0xf1, 0x65, 0x50, 0x74, 0x73, 0x9f, 0x8e, 0xae, 0x7d, 0x99, 0xd1, 0x16, 0x08, + 0xbb, 0xcf, 0xf8, 0xa2, 0x32, 0xa0, 0x0a, 0x5f, 0x44, 0x6d, 0x12, 0xba, 0x6c, 0xcd, + 0x34, 0xb8, 0xcc, 0x0a, 0x46, 0x11, 0xa8, 0x1b, 0x54, 0x99, 0x42, 0x0c, 0xfb, 0x69, + 0x81, 0x70, 0x67, 0xcf, 0x6e, 0xd7, 0xac, 0x00, 0x46, 0xe1, 0xba, 0x45, 0xe6, 0x70, + 0x8a, 0xb9, 0xaa, 0x2e, 0xf2, 0xfa, 0xa4, 0x58, 0x9e, 0xf3, 0x81, 0x39}, + {0x93, 0x0a, 0x23, 0x59, 0x75, 0x8a, 0xfb, 0x18, 0x5d, 0xf4, 0xe6, 0x60, 0x69, 0x8f, + 0x16, 0x1d, 0xb5, 0x3c, 0xa9, 0x14, 0x45, 0xa9, 0x85, 0x3a, 0xfd, 0xd0, 0xac, 0x05, + 0x37, 0x08, 0xdc, 0x38, 0xde, 0x6f, 0xe6, 0x6d, 0xa5, 0xdf, 0x45, 0xc8, 0x3a, 0x48, + 0x40, 0x2c, 0x00, 0xa5, 0x52, 0xe1, 0x32, 0xf6, 0xb4, 0xc7, 0x63, 0xe1, 0xd2, 0xe9, + 0x65, 0x1b, 0xbc, 0xdc, 0x2e, 0x45, 0xf4, 0x30, 0x40, 0x97, 0x75, 0xc5, 0x82, 0x27, + 0x6d, 0x85, 0xcc, 0xbe, 0x9c, 0xf9, 0x69, 0x45, 0x13, 0xfa, 0x71, 0x4e, 0xea, 0xc0, + 0x73, 0xfc, 0x44, 0x88, 0x69, 0x24, 0x3f, 0x59, 0x1a, 0x9a, 0x2d, 0x63}, + {0xa6, 0xcb, 0x07, 0xb8, 0x15, 0x6b, 0xbb, 0xf6, 0xd7, 0xf0, 0x54, 0xbc, 0xdf, 0xc7, + 0x23, 0x18, 0x0b, 0x67, 0x29, 0x6e, 0x03, 0x97, 0x1d, 0xbb, 0x57, 0x4a, 0xed, 0x47, + 0x88, 0xf4, 0x24, 0x0b, 0xa7, 0x84, 0x0c, 0xed, 0x11, 0xfd, 0x09, 0xbf, 0x3a, 0x69, + 0x9f, 0x0d, 0x81, 0x71, 0xf0, 0x63, 0x79, 0x87, 0xcf, 0x57, 0x2d, 0x8c, 0x90, 0x21, + 0xa2, 0x4b, 0xf6, 0x8a, 0xf2, 0x7d, 0x5a, 0x3a, 0xc7, 0xea, 0x1b, 0x51, 0xbe, 0xd4, + 0xda, 0xdc, 0xf2, 0xcc, 0x26, 0xed, 0x75, 0x80, 0x53, 0xa4, 0x65, 0x9a, 0x5f, 0x00, + 0x9f, 0xff, 0x9c, 0xe1, 0x63, 0x1f, 0x48, 0x75, 0x44, 0xf7, 0xfc, 0x34}, + {0xca, 0x67, 0x97, 0x78, 0x4c, 0xe0, 0x97, 0xc1, 0x7d, 0x46, 0xd9, 0x38, 0xcb, 0x4d, + 0x71, 0xb8, 0xa8, 0x5f, 0xf9, 0x83, 0x82, 0x88, 0xde, 0x55, 0xf7, 0x63, 0xfa, 0x4d, + 0x16, 0xdc, 0x3b, 0x3d, 0x98, 0xaa, 0xcf, 0x78, 0xab, 0x1d, 0xbb, 0xa5, 0xf2, 0x72, + 0x0b, 0x19, 0x67, 0xa2, 0xed, 0x5c, 0x8e, 0x60, 0x92, 0x0a, 0x11, 0xc9, 0x09, 0x93, + 0xb0, 0x74, 0xb3, 0x2f, 0x04, 0xa3, 0x19, 0x01, 0x7d, 0x17, 0xc2, 0xe8, 0x9c, 0xd8, + 0xa2, 0x67, 0xc1, 0xd0, 0x95, 0x68, 0xf6, 0xa5, 0x9d, 0x66, 0xb0, 0xa2, 0x82, 0xb2, + 0xe5, 0x98, 0x65, 0xf5, 0x73, 0x0a, 0xe2, 0xed, 0xf1, 0x88, 0xc0, 0x56}, + {0x17, 0x6e, 0xa8, 0x10, 0x11, 0x3d, 0x6d, 0x33, 0xfa, 0xb2, 0x75, 0x0b, 0x32, 0x88, + 0xf3, 0xd7, 0x88, 0x29, 0x07, 0x25, 0x76, 0x33, 0x15, 0xf9, 0x87, 0x8b, 0x10, 0x99, + 0x6b, 0x4c, 0x67, 0x09, 0x02, 0x8f, 0xf3, 0x24, 0xac, 0x5f, 0x1b, 0x58, 0xbd, 0x0c, + 0xe3, 0xba, 0xfe, 0xe9, 0x0b, 0xa9, 0xf0, 0x92, 0xcf, 0x8a, 0x02, 0x69, 0x21, 0x9a, + 0x8f, 0x03, 0x59, 0x83, 0xa4, 0x7e, 0x8b, 0x03, 0xf8, 0x6f, 0x31, 0x99, 0x21, 0xf8, + 0x4e, 0x9f, 0x4f, 0x8d, 0xa7, 0xea, 0x82, 0xd2, 0x49, 0x2f, 0x74, 0x31, 0xef, 0x5a, + 0xab, 0xa5, 0x71, 0x09, 0x65, 0xeb, 0x69, 0x59, 0x02, 0x31, 0x5e, 0x6e}, + {0xfb, 0x93, 0xe5, 0x87, 0xf5, 0x62, 0x6c, 0xb1, 0x71, 0x3e, 0x5d, 0xca, 0xde, 0xed, + 0x99, 0x49, 0x6d, 0x3e, 0xcc, 0x14, 0xe0, 0xc1, 0x91, 0xb4, 0xa8, 0xdb, 0xa8, 0x89, + 0x47, 0x11, 0xf5, 0x08, 0x22, 0x62, 0x06, 0x63, 0x0e, 0xfb, 0x04, 0x33, 0x3f, 0xba, + 0xac, 0x87, 0x89, 0x06, 0x35, 0xfb, 0xa3, 0x61, 0x10, 0x8c, 0x77, 0x24, 0x19, 0xbd, + 0x20, 0x86, 0x83, 0xd1, 0x43, 0xad, 0x58, 0x30, 0xd0, 0x63, 0x76, 0xe5, 0xfd, 0x0f, + 0x3c, 0x32, 0x10, 0xa6, 0x2e, 0xa2, 0x38, 0xdf, 0xc3, 0x05, 0x9a, 0x4f, 0x99, 0xac, + 0xbd, 0x8a, 0xc7, 0xbd, 0x99, 0xdc, 0xe3, 0xef, 0xa4, 0x9f, 0x54, 0x26}, + {0xd6, 0xf9, 0x6b, 0x1e, 0x46, 0x5a, 0x1d, 0x74, 0x81, 0xa5, 0x77, 0x77, 0xfc, 0xb3, + 0x05, 0x23, 0xd9, 0xd3, 0x74, 0x64, 0xa2, 0x74, 0x55, 0xd4, 0xff, 0xe0, 0x01, 0x64, + 0xdc, 0xe1, 0x26, 0x19, 0x6e, 0x66, 0x3f, 0xaf, 0x49, 0x85, 0x46, 0xdb, 0xa5, 0x0e, + 0x4a, 0xf1, 0x04, 0xcf, 0x7f, 0xd7, 0x47, 0x0c, 0xba, 0xa4, 0xf7, 0x3f, 0xf2, 0x3d, + 0x85, 0x3c, 0xce, 0x32, 0xe1, 0xdf, 0x10, 0x3a, 0xa0, 0xce, 0x17, 0xea, 0x8a, 0x4e, + 0x7f, 0xe0, 0xfd, 0xc1, 0x1f, 0x3a, 0x46, 0x15, 0xd5, 0x2f, 0xf1, 0xc0, 0xf2, 0x31, + 0xfd, 0x22, 0x53, 0x17, 0x15, 0x5d, 0x1e, 0x86, 0x1d, 0xd0, 0xa1, 0x1f}, + {0x32, 0x98, 0x59, 0x7d, 0x94, 0x55, 0x80, 0xcc, 0x20, 0x55, 0xf1, 0x37, 0xda, 0x56, + 0x46, 0x1e, 0x20, 0x93, 0x05, 0x4e, 0x74, 0xf7, 0xf6, 0x99, 0x33, 0xcf, 0x75, 0x6a, + 0xbc, 0x63, 0x35, 0x77, 0xab, 0x94, 0xdf, 0xd1, 0x00, 0xac, 0xdc, 0x38, 0xe9, 0x0d, + 0x08, 0xd1, 0xdd, 0x2b, 0x71, 0x2e, 0x62, 0xe2, 0xd5, 0xfd, 0x3e, 0xe9, 0x13, 0x7f, + 0xe5, 0x01, 0x9a, 0xee, 0x18, 0xed, 0xfc, 0x73, 0xb3, 0x9c, 0x13, 0x63, 0x08, 0xe9, + 0xb1, 0x06, 0xcd, 0x3e, 0xa0, 0xc5, 0x67, 0xda, 0x93, 0xa4, 0x32, 0x89, 0x63, 0xad, + 0xc8, 0xce, 0x77, 0x8d, 0x44, 0x4f, 0x86, 0x1b, 0x70, 0x6b, 0x42, 0x1f}, + {0x01, 0x1c, 0x91, 0x41, 0x4c, 0x26, 0xc9, 0xef, 0x25, 0x2c, 0xa2, 0x17, 0xb8, 0xb7, + 0xa3, 0xf1, 0x47, 0x14, 0x0f, 0xf3, 0x6b, 0xda, 0x75, 0x58, 0x90, 0xb0, 0x31, 0x1d, + 0x27, 0xf5, 0x1a, 0x4e, 0x52, 0x25, 0xa1, 0x91, 0xc8, 0x35, 0x7e, 0xf1, 0x76, 0x9c, + 0x5e, 0x57, 0x53, 0x81, 0x6b, 0xb7, 0x3e, 0x72, 0x9b, 0x0d, 0x6f, 0x40, 0x83, 0xfa, + 0x38, 0xe4, 0xa7, 0x3f, 0x1b, 0xbb, 0x76, 0x0b, 0x9b, 0x93, 0x92, 0x7f, 0xf9, 0xc1, + 0xb8, 0x08, 0x6e, 0xab, 0x44, 0xd4, 0xcb, 0x71, 0x67, 0xbe, 0x17, 0x80, 0xbb, 0x99, + 0x63, 0x64, 0xe5, 0x22, 0x55, 0xa9, 0x72, 0xb7, 0x1e, 0xd6, 0x6d, 0x7b}, + {0x92, 0x3d, 0xf3, 0x50, 0xe8, 0xc1, 0xad, 0xb7, 0xcf, 0xd5, 0x8c, 0x60, 0x4f, 0xfa, + 0x98, 0x79, 0xdb, 0x5b, 0xfc, 0x8d, 0xbd, 0x2d, 0x96, 0xad, 0x4f, 0x2f, 0x1d, 0xaf, + 0xce, 0x9b, 0x3e, 0x70, 0xc7, 0xd2, 0x01, 0xab, 0xf9, 0xab, 0x30, 0x57, 0x18, 0x3b, + 0x14, 0x40, 0xdc, 0x76, 0xfb, 0x16, 0x81, 0xb2, 0xcb, 0xa0, 0x65, 0xbe, 0x6c, 0x86, + 0xfe, 0x6a, 0xff, 0x9b, 0x65, 0x9b, 0xfa, 0x53, 0x55, 0x54, 0x88, 0x94, 0xe9, 0xc8, + 0x14, 0x6c, 0xe5, 0xd4, 0xae, 0x65, 0x66, 0x5d, 0x3a, 0x84, 0xf1, 0x5a, 0xd6, 0xbc, + 0x3e, 0xb7, 0x1b, 0x18, 0x50, 0x1f, 0xc6, 0xc4, 0xe5, 0x93, 0x8d, 0x39}, + {0xf3, 0x48, 0xe2, 0x33, 0x67, 0xd1, 0x4b, 0x1c, 0x5f, 0x0a, 0xbf, 0x15, 0x87, 0x12, + 0x9e, 0xbd, 0x76, 0x03, 0x0b, 0xa1, 0xf0, 0x8c, 0x3f, 0xd4, 0x13, 0x1b, 0x19, 0xdf, + 0x5d, 0x9b, 0xb0, 0x53, 0xf2, 0xe3, 0xe7, 0xd2, 0x60, 0x7c, 0x87, 0xc3, 0xb1, 0x8b, + 0x82, 0x30, 0xa0, 0xaa, 0x34, 0x3b, 0x38, 0xf1, 0x9e, 0x73, 0xe7, 0x26, 0x3e, 0x28, + 0x77, 0x05, 0xc3, 0x02, 0x90, 0x9c, 0x9c, 0x69, 0xcc, 0xf1, 0x46, 0x59, 0x23, 0xa7, + 0x06, 0xf3, 0x7d, 0xd9, 0xe5, 0xcc, 0xb5, 0x18, 0x17, 0x92, 0x75, 0xe9, 0xb4, 0x81, + 0x47, 0xd2, 0xcd, 0x28, 0x07, 0xd9, 0xcd, 0x6f, 0x0c, 0xf3, 0xca, 0x51}, + {0x0a, 0xe0, 0x74, 0x76, 0x42, 0xa7, 0x0b, 0xa6, 0xf3, 0x7b, 0x7a, 0xa1, 0x70, 0x85, + 0x0e, 0x63, 0xcc, 0x24, 0x33, 0xcf, 0x3d, 0x56, 0x58, 0x37, 0xaa, 0xfd, 0x83, 0x23, + 0x29, 0xaa, 0x04, 0x55, 0xc7, 0x54, 0xac, 0x18, 0x9a, 0xf9, 0x7a, 0x73, 0x0f, 0xb3, + 0x1c, 0xc5, 0xdc, 0x78, 0x33, 0x90, 0xc7, 0x0c, 0xe1, 0x4c, 0x33, 0xbc, 0x89, 0x2b, + 0x9a, 0xe9, 0xf8, 0x89, 0xc1, 0x29, 0xae, 0x12, 0xcf, 0x01, 0x0d, 0x1f, 0xcb, 0xc0, + 0x9e, 0xa9, 0xae, 0xf7, 0x34, 0x3a, 0xcc, 0xef, 0xd1, 0x0d, 0x22, 0x4e, 0x9c, 0xd0, + 0x21, 0x75, 0xca, 0x55, 0xea, 0xa5, 0xeb, 0x58, 0xe9, 0x4f, 0xd1, 0x5f}, + {0x2c, 0xab, 0x45, 0x28, 0xdf, 0x2d, 0xdc, 0xb5, 0x93, 0xe9, 0x7f, 0x0a, 0xb1, 0x91, + 0x94, 0x06, 0x46, 0xe3, 0x02, 0x40, 0xd6, 0xf3, 0xaa, 0x4d, 0xd1, 0x74, 0x64, 0x58, + 0x6e, 0xf2, 0x3f, 0x09, 0x8e, 0xcb, 0x93, 0xbf, 0x5e, 0xfe, 0x42, 0x3c, 0x5f, 0x56, + 0xd4, 0x36, 0x51, 0xa8, 0xdf, 0xbe, 0xe8, 0x20, 0x42, 0x88, 0x9e, 0x85, 0xf0, 0xe0, + 0x28, 0xd1, 0x25, 0x07, 0x96, 0x3f, 0xd7, 0x7d, 0x29, 0x98, 0x05, 0x68, 0xfe, 0x24, + 0x0d, 0xb1, 0xe5, 0x23, 0xaf, 0xdb, 0x72, 0x06, 0x73, 0x75, 0x29, 0xac, 0x57, 0xb4, + 0x3a, 0x25, 0x67, 0x13, 0xa4, 0x70, 0xb4, 0x86, 0xbc, 0xbc, 0x59, 0x2f}, + {0x5f, 0x13, 0x17, 0x99, 0x42, 0x7d, 0x84, 0x83, 0xd7, 0x03, 0x7d, 0x56, 0x1f, 0x91, + 0x1b, 0xad, 0xd1, 0xaa, 0x77, 0xbe, 0xd9, 0x48, 0x77, 0x7e, 0x4a, 0xaf, 0x51, 0x2e, + 0x2e, 0xb4, 0x58, 0x54, 0x01, 0xc3, 0x91, 0xb6, 0x60, 0xd5, 0x41, 0x70, 0x1e, 0xe7, + 0xd7, 0xad, 0x3f, 0x1b, 0x20, 0x85, 0x85, 0x55, 0x33, 0x11, 0x63, 0xe1, 0xc2, 0x16, + 0xb1, 0x28, 0x08, 0x01, 0x3d, 0x5e, 0xa5, 0x2a, 0x4f, 0x44, 0x07, 0x0c, 0xe6, 0x92, + 0x51, 0xed, 0x10, 0x1d, 0x42, 0x74, 0x2d, 0x4e, 0xc5, 0x42, 0x64, 0xc8, 0xb5, 0xfd, + 0x82, 0x4c, 0x2b, 0x35, 0x64, 0x86, 0x76, 0x8a, 0x4a, 0x00, 0xe9, 0x13}, + {0xdb, 0xce, 0x2f, 0x83, 0x45, 0x88, 0x9d, 0x73, 0x63, 0xf8, 0x6b, 0xae, 0xc9, 0xd6, + 0x38, 0xfa, 0xf7, 0xfe, 0x4f, 0xb7, 0xca, 0x0d, 0xbc, 0x32, 0x5e, 0xe4, 0xbc, 0x14, + 0x88, 0x7e, 0x93, 0x73, 0x7f, 0x87, 0x3b, 0x19, 0xc9, 0x00, 0x2e, 0xbb, 0x6b, 0x50, + 0xdc, 0xe0, 0x90, 0xa8, 0xe3, 0xec, 0x9f, 0x64, 0xde, 0x36, 0xc0, 0xb7, 0xf3, 0xec, + 0x1a, 0x9e, 0xde, 0x98, 0x08, 0x04, 0x46, 0x5f, 0x8d, 0xf4, 0x7b, 0x29, 0x16, 0x71, + 0x03, 0xb9, 0x34, 0x68, 0xf0, 0xd4, 0x22, 0x3b, 0xd1, 0xa9, 0xc6, 0xbd, 0x96, 0x46, + 0x57, 0x15, 0x97, 0xe1, 0x35, 0xe8, 0xd5, 0x91, 0xe8, 0xa4, 0xf8, 0x2c}, + {0x67, 0x0f, 0x11, 0x07, 0x87, 0xfd, 0x93, 0x6d, 0x49, 0xb5, 0x38, 0x7c, 0xd3, 0x09, + 0x4c, 0xdd, 0x86, 0x6a, 0x73, 0xc2, 0x4c, 0x6a, 0xb1, 0x7c, 0x09, 0x2a, 0x25, 0x58, + 0x6e, 0xbd, 0x49, 0x20, 0xa2, 0x6b, 0xd0, 0x17, 0x7e, 0x48, 0xb5, 0x2c, 0x6b, 0x19, + 0x50, 0x39, 0x1c, 0x38, 0xd2, 0x24, 0x30, 0x8a, 0x97, 0x85, 0x81, 0x9c, 0x65, 0xd7, + 0xf6, 0xa4, 0xd6, 0x91, 0x28, 0x7f, 0x6f, 0x7a, 0x49, 0xef, 0x9a, 0x6a, 0x8d, 0xfd, + 0x09, 0x7d, 0x0b, 0xb9, 0x3d, 0x5b, 0xbe, 0x60, 0xee, 0xf0, 0xd4, 0xbf, 0x9e, 0x51, + 0x2c, 0xb5, 0x21, 0x4c, 0x1d, 0x94, 0x45, 0xc5, 0xdf, 0xaa, 0x11, 0x60}, + {0x3c, 0xf8, 0x95, 0xcf, 0x6d, 0x92, 0x67, 0x5f, 0x71, 0x90, 0x28, 0x71, 0x61, 0x85, + 0x7e, 0x7c, 0x5b, 0x7a, 0x8f, 0x99, 0xf3, 0xe7, 0xa1, 0xd6, 0xe0, 0xf9, 0x62, 0x0b, + 0x1b, 0xcc, 0xc5, 0x6f, 0x90, 0xf8, 0xcb, 0x02, 0xc8, 0xd0, 0xde, 0x63, 0xaa, 0x6a, + 0xff, 0x0d, 0xca, 0x98, 0xd0, 0xfb, 0x99, 0xed, 0xb6, 0xb9, 0xfd, 0x0a, 0x4d, 0x62, + 0x1e, 0x0b, 0x34, 0x79, 0xb7, 0x18, 0xce, 0x69, 0xcb, 0x79, 0x98, 0xb2, 0x28, 0x55, + 0xef, 0xd1, 0x92, 0x90, 0x7e, 0xd4, 0x3c, 0xae, 0x1a, 0xdd, 0x52, 0x23, 0x9f, 0x18, + 0x42, 0x04, 0x7e, 0x12, 0xf1, 0x01, 0x71, 0xe5, 0x3a, 0x6b, 0x59, 0x15}, + {0xa2, 0x79, 0x91, 0x3f, 0xd2, 0x39, 0x27, 0x46, 0xcf, 0xdd, 0xd6, 0x97, 0x31, 0x12, + 0x83, 0xff, 0x8a, 0x14, 0xf2, 0x53, 0xb5, 0xde, 0x07, 0x13, 0xda, 0x4d, 0x5f, 0x7b, + 0x68, 0x37, 0x22, 0x0d, 0xca, 0x24, 0x51, 0x7e, 0x16, 0x31, 0xff, 0x09, 0xdf, 0x45, + 0xc7, 0xd9, 0x8b, 0x15, 0xe4, 0x0b, 0xe5, 0x56, 0xf5, 0x7e, 0x22, 0x7d, 0x2b, 0x29, + 0x38, 0xd1, 0xb6, 0xaf, 0x41, 0xe2, 0xa4, 0x3a, 0xf5, 0x05, 0x33, 0x2a, 0xbf, 0x38, + 0xc1, 0x2c, 0xc3, 0x26, 0xe9, 0xa2, 0x8f, 0x3f, 0x58, 0x48, 0xeb, 0xd2, 0x49, 0x55, + 0xa2, 0xb1, 0x3a, 0x08, 0x6c, 0xa3, 0x87, 0x46, 0x6e, 0xaa, 0xfc, 0x32}, + {0xf5, 0x9a, 0x7d, 0xc5, 0x8d, 0x6e, 0xc5, 0x7b, 0xf2, 0xbd, 0xf0, 0x9d, 0xed, 0xd2, + 0x0b, 0x3e, 0xa3, 0xe4, 0xef, 0x22, 0xde, 0x14, 0xc0, 0xaa, 0x5c, 0x6a, 0xbd, 0xfe, + 0xce, 0xe9, 0x27, 0x46, 0xdf, 0xcc, 0x87, 0x27, 0x73, 0xa4, 0x07, 0x32, 0xf8, 0xe3, + 0x13, 0xf2, 0x08, 0x19, 0xe3, 0x17, 0x4e, 0x96, 0x0d, 0xf6, 0xd7, 0xec, 0xb2, 0xd5, + 0xe9, 0x0b, 0x60, 0xc2, 0x36, 0x63, 0x6f, 0x74, 0x1c, 0x97, 0x6c, 0xab, 0x45, 0xf3, + 0x4a, 0x3f, 0x1f, 0x73, 0x43, 0x99, 0x72, 0xeb, 0x88, 0xe2, 0x6d, 0x18, 0x44, 0x03, + 0x8a, 0x6a, 0x59, 0x33, 0x93, 0x62, 0xd6, 0x7e, 0x00, 0x17, 0x49, 0x7b}, + {0x64, 0xb0, 0x84, 0xab, 0x5c, 0xfb, 0x85, 0x2d, 0x14, 0xbc, 0xf3, 0x89, 0xd2, 0x10, + 0x78, 0x49, 0x0c, 0xce, 0x15, 0x7b, 0x44, 0xdc, 0x6a, 0x47, 0x7b, 0xfd, 0x44, 0xf8, + 0x76, 0xa3, 0x2b, 0x12, 0xdd, 0xa2, 0x53, 0xdd, 0x28, 0x1b, 0x34, 0x54, 0x3f, 0xfc, + 0x42, 0xdf, 0x5b, 0x90, 0x17, 0xaa, 0xf4, 0xf8, 0xd2, 0x4d, 0xd9, 0x92, 0xf5, 0x0f, + 0x7d, 0xd3, 0x8c, 0xe0, 0x0f, 0x62, 0x03, 0x1d, 0x54, 0xe5, 0xb4, 0xa2, 0xcd, 0x32, + 0x02, 0xc2, 0x7f, 0x18, 0x5d, 0x11, 0x42, 0xfd, 0xd0, 0x9e, 0xd9, 0x79, 0xd4, 0x7d, + 0xbe, 0xb4, 0xab, 0x2e, 0x4c, 0xec, 0x68, 0x2b, 0xf5, 0x0b, 0xc7, 0x02}, + {0xbb, 0x2f, 0x0b, 0x5d, 0x4b, 0xec, 0x87, 0xa2, 0xca, 0x82, 0x48, 0x07, 0x90, 0x57, + 0x5c, 0x41, 0x5c, 0x81, 0xd0, 0xc1, 0x1e, 0xa6, 0x44, 0xe0, 0xe0, 0xf5, 0x9e, 0x40, + 0x0a, 0x4f, 0x33, 0x26, 0xe1, 0x72, 0x8d, 0x45, 0xbf, 0x32, 0xe5, 0xac, 0xb5, 0x3c, + 0xb7, 0x7c, 0xe0, 0x68, 0xe7, 0x5b, 0xe7, 0xbd, 0x8b, 0xee, 0x94, 0x7d, 0xcf, 0x56, + 0x03, 0x3a, 0xb4, 0xfe, 0xe3, 0x97, 0x06, 0x6b, 0xc0, 0xa3, 0x62, 0xdf, 0x4a, 0xf0, + 0xc8, 0xb6, 0x5d, 0xa4, 0x6d, 0x07, 0xef, 0x00, 0xf0, 0x3e, 0xa9, 0xd2, 0xf0, 0x49, + 0x58, 0xb9, 0x9c, 0x9c, 0xae, 0x2f, 0x1b, 0x44, 0x43, 0x7f, 0xc3, 0x1c}, + {0x4f, 0x32, 0xc7, 0x5c, 0x5a, 0x56, 0x8f, 0x50, 0x22, 0xa9, 0x06, 0xe5, 0xc0, 0xc4, + 0x61, 0xd0, 0x19, 0xac, 0x45, 0x5c, 0xdb, 0xab, 0x18, 0xfb, 0x4a, 0x31, 0x80, 0x03, + 0xc1, 0x09, 0x68, 0x6c, 0xb9, 0xae, 0xce, 0xc9, 0xf1, 0x56, 0x66, 0xd7, 0x6a, 0x65, + 0xe5, 0x18, 0xf8, 0x15, 0x5b, 0x1c, 0x34, 0x23, 0x4c, 0x84, 0x32, 0x28, 0xe7, 0x26, + 0x38, 0x68, 0x19, 0x2f, 0x77, 0x6f, 0x34, 0x3a, 0xc8, 0x6a, 0xda, 0xe2, 0x12, 0x51, + 0xd5, 0xd2, 0xed, 0x51, 0xe8, 0xb1, 0x31, 0x03, 0xbd, 0xe9, 0x62, 0x72, 0xc6, 0x8e, + 0xdd, 0x46, 0x07, 0x96, 0xd0, 0xc5, 0xf7, 0x6e, 0x9f, 0x1b, 0x91, 0x05}, + {0xbb, 0x0e, 0xdf, 0xf5, 0x83, 0x99, 0x33, 0xc1, 0xac, 0x4c, 0x2c, 0x51, 0x8f, 0x75, + 0xf3, 0xc0, 0xe1, 0x98, 0xb3, 0x0b, 0x0a, 0x13, 0xf1, 0x2c, 0x62, 0x0c, 0x27, 0xaa, + 0xf9, 0xec, 0x3c, 0x6b, 0xef, 0xea, 0x2e, 0x51, 0xf3, 0xac, 0x49, 0x53, 0x49, 0xcb, + 0xc1, 0x1c, 0xd3, 0x41, 0xc1, 0x20, 0x8d, 0x68, 0x9a, 0xa9, 0x07, 0x0c, 0x18, 0x24, + 0x17, 0x2d, 0x4b, 0xc6, 0xd1, 0xf9, 0x5e, 0x55, 0x08, 0xbd, 0x73, 0x3b, 0xba, 0x70, + 0xa7, 0x36, 0x0c, 0xbf, 0xaf, 0xa3, 0x08, 0xef, 0x4a, 0x62, 0xf2, 0x46, 0x09, 0xb4, + 0x98, 0xff, 0x37, 0x57, 0x9d, 0x74, 0x81, 0x33, 0xe1, 0x4d, 0x5f, 0x67}, + {0xfc, 0x82, 0x17, 0x6b, 0x03, 0x52, 0x2c, 0x0e, 0xb4, 0x83, 0xad, 0x6c, 0x81, 0x6c, + 0x81, 0x64, 0x3e, 0x07, 0x64, 0x69, 0xd9, 0xbd, 0xdc, 0xd0, 0x20, 0xc5, 0x64, 0x01, + 0xf7, 0x9d, 0xd9, 0x13, 0x1d, 0xb3, 0xda, 0x3b, 0xd9, 0xf6, 0x2f, 0xa1, 0xfe, 0x2d, + 0x65, 0x9d, 0x0f, 0xd8, 0x25, 0x07, 0x87, 0x94, 0xbe, 0x9a, 0xf3, 0x4f, 0x9c, 0x01, + 0x43, 0x3c, 0xcd, 0x82, 0xb8, 0x50, 0xf4, 0x60, 0xca, 0xc0, 0xe5, 0x21, 0xc3, 0x5e, + 0x4b, 0x01, 0xa2, 0xbf, 0x19, 0xd7, 0xc9, 0x69, 0xcb, 0x4f, 0xa0, 0x23, 0x00, 0x75, + 0x18, 0x1c, 0x5f, 0x4e, 0x80, 0xac, 0xed, 0x55, 0x9e, 0xde, 0x06, 0x1c}, + {0xe2, 0xc4, 0x3e, 0xa3, 0xd6, 0x7a, 0x0f, 0x99, 0x8e, 0xe0, 0x2e, 0xbe, 0x38, 0xf9, + 0x08, 0x66, 0x15, 0x45, 0x28, 0x63, 0xc5, 0x43, 0xa1, 0x9c, 0x0d, 0xb6, 0x2d, 0xec, + 0x1f, 0x8a, 0xf3, 0x4c, 0xaa, 0x69, 0x6d, 0xff, 0x40, 0x2b, 0xd5, 0xff, 0xbb, 0x49, + 0x40, 0xdc, 0x18, 0x0b, 0x53, 0x34, 0x97, 0x98, 0x4d, 0xa3, 0x2f, 0x5c, 0x4a, 0x5e, + 0x2d, 0xba, 0x32, 0x7d, 0x8e, 0x6f, 0x09, 0x78, 0xe7, 0x5c, 0xfa, 0x0d, 0x65, 0xaa, + 0xaa, 0xa0, 0x8c, 0x47, 0xb5, 0x48, 0x2a, 0x9e, 0xc4, 0xf9, 0x5b, 0x72, 0x03, 0x70, + 0x7d, 0xcc, 0x09, 0x4f, 0xbe, 0x1a, 0x09, 0x26, 0x3a, 0xad, 0x3c, 0x37}, + {0x7c, 0xf5, 0xc9, 0x82, 0x4d, 0x63, 0x94, 0xb2, 0x36, 0x45, 0x93, 0x24, 0xe1, 0xfd, + 0xcb, 0x1f, 0x5a, 0xdb, 0x8c, 0x41, 0xb3, 0x4d, 0x9c, 0x9e, 0xfc, 0x19, 0x44, 0x45, + 0xd9, 0xf3, 0x40, 0x00, 0xad, 0xbb, 0xdd, 0x89, 0xfb, 0xa8, 0xbe, 0xf1, 0xcb, 0xae, + 0xae, 0x61, 0xbc, 0x2c, 0xcb, 0x3b, 0x9d, 0x8d, 0x9b, 0x1f, 0xbb, 0xa7, 0x58, 0x8f, + 0x86, 0xa6, 0x12, 0x51, 0xda, 0x7e, 0x54, 0x21, 0xd3, 0x86, 0x59, 0xfd, 0x39, 0xe9, + 0xfd, 0xde, 0x0c, 0x38, 0x0a, 0x51, 0x89, 0x2c, 0x27, 0xf4, 0xb9, 0x19, 0x31, 0xbb, + 0x07, 0xa4, 0x2b, 0xb7, 0xf4, 0x4d, 0x25, 0x4a, 0x33, 0x0a, 0x55, 0x63}, + {0x37, 0xcf, 0x69, 0xb5, 0xed, 0xd6, 0x07, 0x65, 0xe1, 0x2e, 0xa5, 0x0c, 0xb0, 0x29, + 0x84, 0x17, 0x5d, 0xd6, 0x6b, 0xeb, 0x90, 0x00, 0x7c, 0xea, 0x51, 0x8f, 0xf7, 0xda, + 0xc7, 0x62, 0xea, 0x3e, 0x49, 0x7b, 0x54, 0x72, 0x45, 0x58, 0xba, 0x9b, 0xe0, 0x08, + 0xc4, 0xe2, 0xfa, 0xc6, 0x05, 0xf3, 0x8d, 0xf1, 0x34, 0xc7, 0x69, 0xfa, 0xe8, 0x60, + 0x7a, 0x76, 0x7d, 0xaa, 0xaf, 0x2b, 0xa9, 0x39, 0x4e, 0x27, 0x93, 0xe6, 0x13, 0xc7, + 0x24, 0x9d, 0x75, 0xd3, 0xdb, 0x68, 0x77, 0x85, 0x63, 0x5f, 0x9a, 0xb3, 0x8a, 0xeb, + 0x60, 0x55, 0x52, 0x70, 0xcd, 0xc4, 0xc9, 0x65, 0x06, 0x6a, 0x43, 0x68}, + {0x27, 0x3f, 0x2f, 0x20, 0xe8, 0x35, 0x02, 0xbc, 0xb0, 0x75, 0xf9, 0x64, 0xe2, 0x00, + 0x5c, 0xc7, 0x16, 0x24, 0x8c, 0xa3, 0xd5, 0xe9, 0xa4, 0x91, 0xf9, 0x89, 0xb7, 0x8a, + 0xf6, 0xe7, 0xb6, 0x17, 0x7c, 0x10, 0x20, 0xe8, 0x17, 0xd3, 0x56, 0x1e, 0x65, 0xe9, + 0x0a, 0x84, 0x44, 0x68, 0x26, 0xc5, 0x7a, 0xfc, 0x0f, 0x32, 0xc6, 0xa1, 0xe0, 0xc1, + 0x72, 0x14, 0x61, 0x91, 0x9c, 0x66, 0x73, 0x53, 0x57, 0x52, 0x0e, 0x9a, 0xab, 0x14, + 0x28, 0x5d, 0xfc, 0xb3, 0xca, 0xc9, 0x84, 0x20, 0x8f, 0x90, 0xca, 0x1e, 0x2d, 0x5b, + 0x88, 0xf5, 0xca, 0xaf, 0x11, 0x7d, 0xf8, 0x78, 0xa6, 0xb5, 0xb4, 0x1c}, + {0x6c, 0xfc, 0x4a, 0x39, 0x6b, 0xc0, 0x64, 0xb6, 0xb1, 0x5f, 0xda, 0x98, 0x24, 0xde, + 0x88, 0x0c, 0x34, 0xd8, 0xca, 0x4b, 0x16, 0x03, 0x8d, 0x4f, 0xa2, 0x34, 0x74, 0xde, + 0x78, 0xca, 0x0b, 0x33, 0xe7, 0x07, 0xa0, 0xa2, 0x62, 0xaa, 0x74, 0x6b, 0xb1, 0xc7, + 0x71, 0xf0, 0xb0, 0xe0, 0x11, 0xf3, 0x23, 0xe2, 0x0b, 0x00, 0x38, 0xe4, 0x07, 0x57, + 0xac, 0x6e, 0xef, 0x82, 0x2d, 0xfd, 0xc0, 0x2d, 0x4e, 0x74, 0x19, 0x11, 0x84, 0xff, + 0x2e, 0x98, 0x24, 0x47, 0x07, 0x2b, 0x96, 0x5e, 0x69, 0xf9, 0xfb, 0x53, 0xc9, 0xbf, + 0x4f, 0xc1, 0x8a, 0xc5, 0xf5, 0x1c, 0x9f, 0x36, 0x1b, 0xbe, 0x31, 0x3c}, + {0xee, 0x8a, 0x94, 0x08, 0x4d, 0x86, 0xf4, 0xb0, 0x6f, 0x1c, 0xba, 0x91, 0xee, 0x19, + 0xdc, 0x07, 0x58, 0xa1, 0xac, 0xa6, 0xae, 0xcd, 0x75, 0x79, 0xbb, 0xd4, 0x62, 0x42, + 0x13, 0x61, 0x0b, 0x33, 0x72, 0x42, 0xcb, 0xf9, 0x93, 0xbc, 0x68, 0xc1, 0x98, 0xdb, + 0xce, 0xc7, 0x1f, 0x71, 0xb8, 0xae, 0x7a, 0x8d, 0xac, 0x34, 0xaa, 0x52, 0x0e, 0x7f, + 0xbb, 0x55, 0x7d, 0x7e, 0x09, 0xc1, 0xce, 0x41, 0x8a, 0x80, 0x6d, 0xa2, 0xd7, 0x19, + 0x96, 0xf7, 0x6d, 0x15, 0x9e, 0x1d, 0x9e, 0xd4, 0x1f, 0xbb, 0x27, 0xdf, 0xa1, 0xdb, + 0x6c, 0xc3, 0xd7, 0x73, 0x7d, 0x77, 0x28, 0x1f, 0xd9, 0x4c, 0xb4, 0x26}, + {0x75, 0x74, 0x38, 0x8f, 0x47, 0x48, 0xf0, 0x51, 0x3c, 0xcb, 0xbe, 0x9c, 0xf4, 0xbc, + 0x5d, 0xb2, 0x55, 0x20, 0x9f, 0xd9, 0x44, 0x12, 0xab, 0x9a, 0xd6, 0xa5, 0x10, 0x1c, + 0x6c, 0x9e, 0x70, 0x2c, 0x83, 0x03, 0x73, 0x62, 0x93, 0xf2, 0xb7, 0xe1, 0x2c, 0x8a, + 0xca, 0xeb, 0xff, 0x79, 0x52, 0x4b, 0x14, 0x13, 0xd4, 0xbf, 0x8a, 0x77, 0xfc, 0xda, + 0x0f, 0x61, 0x72, 0x9c, 0x14, 0x10, 0xeb, 0x7d, 0x7a, 0xee, 0x66, 0x87, 0x6a, 0xaf, + 0x62, 0xcb, 0x0e, 0xcd, 0x53, 0x55, 0x04, 0xec, 0xcb, 0x66, 0xb5, 0xe4, 0x0b, 0x0f, + 0x38, 0x01, 0x80, 0x58, 0xea, 0xe2, 0x2c, 0xf6, 0x9f, 0x8e, 0xe6, 0x08}, + {0xad, 0x30, 0xc1, 0x4b, 0x0a, 0x50, 0xad, 0x34, 0x9c, 0xd4, 0x0b, 0x3d, 0x49, 0xdb, + 0x38, 0x8d, 0xbe, 0x89, 0x0a, 0x50, 0x98, 0x3d, 0x5c, 0xa2, 0x09, 0x3b, 0xba, 0xee, + 0x87, 0x3f, 0x1f, 0x2f, 0xf9, 0xf2, 0xb8, 0x0a, 0xd5, 0x09, 0x2d, 0x2f, 0xdf, 0x23, + 0x59, 0xc5, 0x8d, 0x21, 0xb9, 0xac, 0xb9, 0x6c, 0x76, 0x73, 0x26, 0x34, 0x8f, 0x4a, + 0xf5, 0x19, 0xf7, 0x38, 0xd7, 0x3b, 0xb1, 0x4c, 0x4a, 0xb6, 0x15, 0xe5, 0x75, 0x8c, + 0x84, 0xf7, 0x38, 0x90, 0x4a, 0xdb, 0xba, 0x01, 0x95, 0xa5, 0x50, 0x1b, 0x75, 0x3f, + 0x3f, 0x31, 0x0d, 0xc2, 0xe8, 0x2e, 0xae, 0xc0, 0x53, 0xe3, 0xa1, 0x19}, + {0xc3, 0x05, 0xfa, 0xba, 0x60, 0x75, 0x1c, 0x7d, 0x61, 0x5e, 0xe5, 0xc6, 0xa0, 0xa0, + 0xe1, 0xb3, 0x73, 0x64, 0xd6, 0xc0, 0x18, 0x97, 0x52, 0xe3, 0x86, 0x34, 0x0c, 0xc2, + 0x11, 0x6b, 0x54, 0x41, 0xbd, 0xbd, 0x96, 0xd5, 0xcd, 0x72, 0x21, 0xb4, 0x40, 0xfc, + 0xee, 0x98, 0x43, 0x45, 0xe0, 0x93, 0xb5, 0x09, 0x41, 0xb4, 0x47, 0x53, 0xb1, 0x9f, + 0x34, 0xae, 0x66, 0x02, 0x99, 0xd3, 0x6b, 0x73, 0xb4, 0xb3, 0x34, 0x93, 0x50, 0x2d, + 0x53, 0x85, 0x73, 0x65, 0x81, 0x60, 0x4b, 0x11, 0xfd, 0x46, 0x75, 0x83, 0x5c, 0x42, + 0x30, 0x5f, 0x5f, 0xcc, 0x5c, 0xab, 0x7f, 0xb8, 0xa2, 0x95, 0x22, 0x41}, + {0xe9, 0xd6, 0x7e, 0xf5, 0x88, 0x9b, 0xc9, 0x19, 0x25, 0xc8, 0xf8, 0x6d, 0x26, 0xcb, + 0x93, 0x53, 0x73, 0xd2, 0x0a, 0xb3, 0x13, 0x32, 0xee, 0x5c, 0x34, 0x2e, 0x2d, 0xb5, + 0xeb, 0x53, 0xe1, 0x14, 0xc6, 0xea, 0x93, 0xe2, 0x61, 0x52, 0x65, 0x2e, 0xdb, 0xac, + 0x33, 0x21, 0x03, 0x92, 0x5a, 0x84, 0x6b, 0x99, 0x00, 0x79, 0xcb, 0x75, 0x09, 0x46, + 0x80, 0xdd, 0x5a, 0x19, 0x8d, 0xbb, 0x60, 0x07, 0x8a, 0x81, 0xe6, 0xcd, 0x17, 0x1a, + 0x3e, 0x41, 0x84, 0xa0, 0x69, 0xed, 0xa9, 0x6d, 0x15, 0x57, 0xb1, 0xcc, 0xca, 0x46, + 0x8f, 0x26, 0xbf, 0x2c, 0xf2, 0xc5, 0x3a, 0xc3, 0x9b, 0xbe, 0x34, 0x6b}, + {0xb2, 0xc0, 0x78, 0x3a, 0x64, 0x2f, 0xdf, 0xf3, 0x7c, 0x02, 0x2e, 0xf2, 0x1e, 0x97, + 0x3e, 0x4c, 0xa3, 0xb5, 0xc1, 0x49, 0x5e, 0x1c, 0x7d, 0xec, 0x2d, 0xdd, 0x22, 0x09, + 0x8f, 0xc1, 0x12, 0x20, 0xd3, 0xf2, 0x71, 0x65, 0x65, 0x69, 0xfc, 0x11, 0x7a, 0x73, + 0x0e, 0x53, 0x45, 0xe8, 0xc9, 0xc6, 0x35, 0x50, 0xfe, 0xd4, 0xa2, 0xe7, 0x3a, 0xe3, + 0x0b, 0xd3, 0x6d, 0x2e, 0xb6, 0xc7, 0xb9, 0x01, 0x29, 0x9d, 0xc8, 0x5a, 0xe5, 0x55, + 0x0b, 0x88, 0x63, 0xa7, 0xa0, 0x45, 0x1f, 0x24, 0x83, 0x14, 0x1f, 0x6c, 0xe7, 0xc2, + 0xdf, 0xef, 0x36, 0x3d, 0xe8, 0xad, 0x4b, 0x4e, 0x78, 0x5b, 0xaf, 0x08}, + {0x33, 0x25, 0x1f, 0x88, 0xdc, 0x99, 0x34, 0x28, 0xb6, 0x23, 0x93, 0x77, 0xda, 0x25, + 0x05, 0x9d, 0xf4, 0x41, 0x34, 0x67, 0xfb, 0xdd, 0x7a, 0x89, 0x8d, 0x16, 0x3a, 0x16, + 0x71, 0x9d, 0xb7, 0x32, 0x4b, 0x2c, 0xcc, 0x89, 0xd2, 0x14, 0x73, 0xe2, 0x8d, 0x17, + 0x87, 0xa2, 0x11, 0xbd, 0xe4, 0x4b, 0xce, 0x64, 0x33, 0xfa, 0xd6, 0x28, 0xd5, 0x18, + 0x6e, 0x82, 0xd9, 0xaf, 0xd5, 0xc1, 0x23, 0x64, 0x6a, 0xb3, 0xfc, 0xed, 0xd9, 0xf8, + 0x85, 0xcc, 0xf9, 0xe5, 0x46, 0x37, 0x8f, 0xc2, 0xbc, 0x22, 0xcd, 0xd3, 0xe5, 0xf9, + 0x38, 0xe3, 0x9d, 0xe4, 0xcc, 0x2d, 0x3e, 0xc1, 0xfb, 0x5e, 0x0a, 0x48}, + {0x71, 0x20, 0x62, 0x01, 0x0b, 0xe7, 0x51, 0x0b, 0xc5, 0xaf, 0x1d, 0x8b, 0xcf, 0x05, + 0xb5, 0x06, 0xcd, 0xab, 0x5a, 0xef, 0x61, 0xb0, 0x6b, 0x2c, 0x31, 0xbf, 0xb7, 0x0c, + 0x60, 0x27, 0xaa, 0x47, 0x1f, 0x22, 0xce, 0x42, 0xe4, 0x4c, 0x61, 0xb6, 0x28, 0x39, + 0x05, 0x4c, 0xcc, 0x9d, 0x19, 0x6e, 0x03, 0xbe, 0x1c, 0xdc, 0xa4, 0xb4, 0x3f, 0x66, + 0x06, 0x8e, 0x1c, 0x69, 0x47, 0x1d, 0xb3, 0x24, 0xc3, 0xf8, 0x15, 0xc0, 0xed, 0x1e, + 0x54, 0x2a, 0x7c, 0x3f, 0x69, 0x7c, 0x7e, 0xfe, 0xa4, 0x11, 0xd6, 0x78, 0xa2, 0x4e, + 0x13, 0x66, 0xaf, 0xf0, 0x94, 0xa0, 0xdd, 0x14, 0x5d, 0x58, 0x5b, 0x54}, + {0x0f, 0x3a, 0xd4, 0xa0, 0x5e, 0x27, 0xbf, 0x67, 0xbe, 0xee, 0x9b, 0x08, 0x34, 0x8e, + 0xe6, 0xad, 0x2e, 0xe7, 0x79, 0xd4, 0x4c, 0x13, 0x89, 0x42, 0x54, 0x54, 0xba, 0x32, + 0xc3, 0xf9, 0x62, 0x0f, 0xe1, 0x21, 0xb3, 0xe3, 0xd0, 0xe4, 0x04, 0x62, 0x95, 0x1e, + 0xff, 0x28, 0x7a, 0x63, 0xaa, 0x3b, 0x9e, 0xbd, 0x99, 0x5b, 0xfd, 0xcf, 0x0c, 0x0b, + 0x71, 0xd0, 0xc8, 0x64, 0x3e, 0xdc, 0x22, 0x4d, 0x39, 0x5f, 0x3b, 0xd6, 0x89, 0x65, + 0xb4, 0xfc, 0x61, 0xcf, 0xcb, 0x57, 0x3f, 0x6a, 0xae, 0x5c, 0x05, 0xfa, 0x3a, 0x95, + 0xd2, 0xc2, 0xba, 0xfe, 0x36, 0x14, 0x37, 0x36, 0x1a, 0xa0, 0x0f, 0x1c}, + {0xff, 0x3d, 0x94, 0x22, 0xb6, 0x04, 0xc6, 0xd2, 0xa0, 0xb3, 0xcf, 0x44, 0xce, 0xbe, + 0x8c, 0xbc, 0x78, 0x86, 0x80, 0x97, 0xf3, 0x4f, 0x25, 0x5d, 0xbf, 0xa6, 0x1c, 0x3b, + 0x4f, 0x61, 0xa3, 0x0f, 0x50, 0x6a, 0x93, 0x8c, 0x0e, 0x2b, 0x08, 0x69, 0xb6, 0xc5, + 0xda, 0xc1, 0x35, 0xa0, 0xc9, 0xf9, 0x34, 0xb6, 0xdf, 0xc4, 0x54, 0x3e, 0xb7, 0x6f, + 0x40, 0xc1, 0x2b, 0x1d, 0x9b, 0x41, 0x05, 0x40, 0xf0, 0x82, 0xbe, 0xb9, 0xbd, 0xfe, + 0x03, 0xa0, 0x90, 0xac, 0x44, 0x3a, 0xaf, 0xc1, 0x89, 0x20, 0x8e, 0xfa, 0x54, 0x19, + 0x91, 0x9f, 0x49, 0xf8, 0x42, 0xab, 0x40, 0xef, 0x8a, 0x21, 0xba, 0x1f}, + {0x3e, 0xf5, 0xc8, 0xfa, 0x48, 0x94, 0x54, 0xab, 0x41, 0x37, 0xa6, 0x7b, 0x9a, 0xe8, + 0xf6, 0x81, 0x01, 0x5e, 0x2b, 0x6c, 0x7d, 0x6c, 0xfd, 0x74, 0x42, 0x6e, 0xc8, 0xa8, + 0xca, 0x3a, 0x2e, 0x39, 0x94, 0x01, 0x7b, 0x3e, 0x04, 0x57, 0x3e, 0x4f, 0x7f, 0xaf, + 0xda, 0x08, 0xee, 0x3e, 0x1d, 0xa8, 0xf1, 0xde, 0xdc, 0x99, 0xab, 0xc6, 0x39, 0xc8, + 0xd5, 0x61, 0x77, 0xff, 0x13, 0x5d, 0x53, 0x6c, 0xaf, 0x35, 0x8a, 0x3e, 0xe9, 0x34, + 0xbd, 0x4c, 0x16, 0xe8, 0x87, 0x58, 0x44, 0x81, 0x07, 0x2e, 0xab, 0xb0, 0x9a, 0xf2, + 0x76, 0x9c, 0x31, 0x19, 0x3b, 0xc1, 0x0a, 0xd5, 0xe4, 0x7f, 0xe1, 0x25}, + {0x76, 0xf6, 0x04, 0x1e, 0xd7, 0x9b, 0x28, 0x0a, 0x95, 0x0f, 0x42, 0xd6, 0x52, 0x1c, + 0x8e, 0x20, 0xab, 0x1f, 0x69, 0x34, 0xb0, 0xd8, 0x86, 0x51, 0x51, 0xb3, 0x9f, 0x2a, + 0x44, 0x51, 0x57, 0x25, 0xa7, 0x21, 0xf1, 0x76, 0xf5, 0x7f, 0x5f, 0x91, 0xe3, 0x87, + 0xcd, 0x2f, 0x27, 0x32, 0x4a, 0xc3, 0x26, 0xe5, 0x1b, 0x4d, 0xde, 0x2f, 0xba, 0xcc, + 0x9b, 0x89, 0x69, 0x89, 0x8f, 0x82, 0xba, 0x6b, 0x01, 0x39, 0xfe, 0x90, 0x66, 0xbc, + 0xd1, 0xe2, 0xd5, 0x7a, 0x99, 0xa0, 0x18, 0x4a, 0xb5, 0x4c, 0xd4, 0x60, 0x84, 0xaf, + 0x14, 0x69, 0x1d, 0x97, 0xe4, 0x7b, 0x6b, 0x7f, 0x4f, 0x50, 0x9d, 0x55}, + {0xd5, 0x54, 0xeb, 0xb3, 0x78, 0x83, 0x73, 0xa7, 0x7c, 0x3c, 0x55, 0xa5, 0x66, 0xd3, + 0x69, 0x1d, 0xba, 0x00, 0x28, 0xf9, 0x62, 0xcf, 0x26, 0x0a, 0x17, 0x32, 0x7e, 0x80, + 0xd5, 0x12, 0xab, 0x01, 0xfd, 0x66, 0xd2, 0xf6, 0xe7, 0x91, 0x48, 0x9c, 0x1b, 0x78, + 0x07, 0x03, 0x9b, 0xa1, 0x44, 0x07, 0x3b, 0xe2, 0x61, 0x60, 0x1d, 0x8f, 0x38, 0x88, + 0x0e, 0xd5, 0x4b, 0x35, 0xa3, 0xa6, 0x3e, 0x12, 0x96, 0x2d, 0xe3, 0x41, 0x90, 0x18, + 0x8d, 0x11, 0x48, 0x58, 0x31, 0xd8, 0xc2, 0xe3, 0xed, 0xb9, 0xd9, 0x45, 0x32, 0xd8, + 0x71, 0x42, 0xab, 0x1e, 0x54, 0xa1, 0x18, 0xc9, 0xe2, 0x61, 0x39, 0x4a}, + {0xa0, 0xbb, 0xe6, 0xf8, 0xe0, 0x3b, 0xdc, 0x71, 0x0a, 0xe3, 0xff, 0x7e, 0x34, 0xf8, + 0xce, 0xd6, 0x6a, 0x47, 0x3a, 0xe1, 0x5f, 0x42, 0x92, 0xa9, 0x63, 0xb7, 0x1d, 0xfb, + 0xe3, 0xbc, 0xd6, 0x2c, 0x1e, 0x3f, 0x23, 0xf3, 0x44, 0xd6, 0x27, 0x03, 0x16, 0xf0, + 0xfc, 0x34, 0x0e, 0x26, 0x9a, 0x49, 0x79, 0xb9, 0xda, 0xf2, 0x16, 0xa7, 0xb5, 0x83, + 0x1f, 0x11, 0xd4, 0x9b, 0xad, 0xee, 0xac, 0x68, 0x10, 0xc2, 0xd7, 0xf3, 0x0e, 0xc9, + 0xb4, 0x38, 0x0c, 0x04, 0xad, 0xb7, 0x24, 0x6e, 0x8e, 0x30, 0x23, 0x3e, 0xe7, 0xb7, + 0xf1, 0xd9, 0x60, 0x38, 0x97, 0xf5, 0x08, 0xb5, 0xd5, 0x60, 0x57, 0x59}, + {0x97, 0x63, 0xaa, 0x04, 0xe1, 0xbf, 0x29, 0x61, 0xcb, 0xfc, 0xa7, 0xa4, 0x08, 0x00, + 0x96, 0x8f, 0x58, 0x94, 0x90, 0x7d, 0x89, 0xc0, 0x8b, 0x3f, 0xa9, 0x91, 0xb2, 0xdc, + 0x3e, 0xa4, 0x9f, 0x70, 0x90, 0x27, 0x02, 0xfd, 0xeb, 0xcb, 0x2a, 0x88, 0x60, 0x57, + 0x11, 0xc4, 0x05, 0x33, 0xaf, 0x89, 0xf4, 0x73, 0x34, 0x7d, 0xe3, 0x92, 0xf4, 0x65, + 0x2b, 0x5a, 0x51, 0x54, 0xdf, 0xc5, 0xb2, 0x2c, 0xca, 0x2a, 0xfd, 0x63, 0x8c, 0x5d, + 0x0a, 0xeb, 0xff, 0x4e, 0x69, 0x2e, 0x66, 0xc1, 0x2b, 0xd2, 0x3a, 0xb0, 0xcb, 0xf8, + 0x6e, 0xf3, 0x23, 0x27, 0x1f, 0x13, 0xc8, 0xf0, 0xec, 0x29, 0xf0, 0x70}, + {0x33, 0x3e, 0xed, 0x2e, 0xb3, 0x07, 0x13, 0x46, 0xe7, 0x81, 0x55, 0xa4, 0x33, 0x2f, + 0x04, 0xae, 0x66, 0x03, 0x5f, 0x19, 0xd3, 0x49, 0x44, 0xc9, 0x58, 0x48, 0x31, 0x6c, + 0x8a, 0x5d, 0x7d, 0x0b, 0xb9, 0xb0, 0x10, 0x5e, 0xaa, 0xaf, 0x6a, 0x2a, 0xa9, 0x1a, + 0x04, 0xef, 0x70, 0xa3, 0xf0, 0x78, 0x1f, 0xd6, 0x3a, 0xaa, 0x77, 0xfb, 0x3e, 0x77, + 0xe1, 0xd9, 0x4b, 0xa7, 0xa2, 0xa5, 0xec, 0x44, 0x43, 0xd5, 0x95, 0x7b, 0x32, 0x48, + 0xd4, 0x25, 0x1d, 0x0f, 0x34, 0xa3, 0x00, 0x83, 0xd3, 0x70, 0x2b, 0xc5, 0xe1, 0x60, + 0x1c, 0x53, 0x1c, 0xde, 0xe4, 0xe9, 0x7d, 0x2c, 0x51, 0x24, 0x22, 0x27}, + {0x2e, 0x34, 0xc5, 0x49, 0xaf, 0x92, 0xbc, 0x1a, 0xd0, 0xfa, 0xe6, 0xb2, 0x11, 0xd8, + 0xee, 0xff, 0x29, 0x4e, 0xc8, 0xfc, 0x8d, 0x8c, 0xa2, 0xef, 0x43, 0xc5, 0x4c, 0xa4, + 0x18, 0xdf, 0xb5, 0x11, 0xfc, 0x75, 0xa9, 0x42, 0x8a, 0xbb, 0x7b, 0xbf, 0x58, 0xa3, + 0xad, 0x96, 0x77, 0x39, 0x5c, 0x8c, 0x48, 0xaa, 0xed, 0xcd, 0x6f, 0xc7, 0x7f, 0xe2, + 0xa6, 0x20, 0xbc, 0xf6, 0xd7, 0x5f, 0x73, 0x19, 0x66, 0x42, 0xc8, 0x42, 0xd0, 0x90, + 0xab, 0xe3, 0x7e, 0x54, 0x19, 0x7f, 0x0f, 0x8e, 0x84, 0xeb, 0xb9, 0x97, 0xa4, 0x65, + 0xd0, 0xa1, 0x03, 0x25, 0x5f, 0x89, 0xdf, 0x91, 0x11, 0x91, 0xef, 0x0f}}; diff --git a/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_donna_basepoint_table.h b/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_donna_basepoint_table.h new file mode 100644 index 0000000000..9c0cdfc0cf --- /dev/null +++ b/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_donna_basepoint_table.h @@ -0,0 +1,2 @@ +/* multiples of the base point in packed {ysubx, xaddy, t2d} form */ +extern const uint8_t ALIGN(16) ge25519_niels_base_multiples[256][96]; diff --git a/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_donna_impl_base.c b/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_donna_impl_base.c new file mode 100644 index 0000000000..778f245788 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_donna_impl_base.c @@ -0,0 +1,829 @@ +#include +#include "ed25519_donna.h" +#include "../memzero.h" + +/* sqrt(x) is such an integer y that 0 <= y <= p - 1, y % 2 = 0, and y^2 = x (mod p). */ +/* d = -121665 / 121666 */ +#if !defined(NDEBUG) +static const bignum25519 ALIGN(16) fe_d = { + 0x35978a3, + 0x0d37284, + 0x3156ebd, + 0x06a0a0e, + 0x001c029, + 0x179e898, + 0x3a03cbb, + 0x1ce7198, + 0x2e2b6ff, + 0x1480db3}; /* d */ +#endif +static const bignum25519 ALIGN(16) fe_sqrtm1 = { + 0x20ea0b0, + 0x186c9d2, + 0x08f189d, + 0x035697f, + 0x0bd0c60, + 0x1fbd7a7, + 0x2804c9e, + 0x1e16569, + 0x004fc1d, + 0x0ae0c92}; /* sqrt(-1) */ +//static const bignum25519 ALIGN(16) fe_d2 = { +// 0x2b2f159, 0x1a6e509, 0x22add7a, 0x0d4141d, 0x0038052, 0x0f3d130, 0x3407977, 0x19ce331, 0x1c56dff, 0x0901b67}; /* 2 * d */ + +/* A = 2 * (1 - d) / (1 + d) = 486662 */ +static const bignum25519 ALIGN(16) fe_ma2 = { + 0x33de3c9, + 0x1fff236, + 0x3ffffff, + 0x1ffffff, + 0x3ffffff, + 0x1ffffff, + 0x3ffffff, + 0x1ffffff, + 0x3ffffff, + 0x1ffffff}; /* -A^2 */ +static const bignum25519 ALIGN(16) fe_ma = { + 0x3f892e7, + 0x1ffffff, + 0x3ffffff, + 0x1ffffff, + 0x3ffffff, + 0x1ffffff, + 0x3ffffff, + 0x1ffffff, + 0x3ffffff, + 0x1ffffff}; /* -A */ +static const bignum25519 ALIGN(16) fe_fffb1 = { + 0x1e3bdff, + 0x025a2b3, + 0x18e5bab, + 0x0ba36ac, + 0x0b9afed, + 0x004e61c, + 0x31d645f, + 0x09d1bea, + 0x102529e, + 0x0063810}; /* sqrt(-2 * A * (A + 2)) */ +static const bignum25519 ALIGN(16) fe_fffb2 = { + 0x383650d, + 0x066df27, + 0x10405a4, + 0x1cfdd48, + 0x2b887f2, + 0x1e9a041, + 0x1d7241f, + 0x0612dc5, + 0x35fba5d, + 0x0cbe787}; /* sqrt(2 * A * (A + 2)) */ +static const bignum25519 ALIGN(16) fe_fffb3 = { + 0x0cfd387, + 0x1209e3a, + 0x3bad4fc, + 0x18ad34d, + 0x2ff6c02, + 0x0f25d12, + 0x15cdfe0, + 0x0e208ed, + 0x32eb3df, + 0x062d7bb}; /* sqrt(-sqrt(-1) * A * (A + 2)) */ +static const bignum25519 ALIGN(16) fe_fffb4 = { + 0x2b39186, + 0x14640ed, + 0x14930a7, + 0x04509fa, + 0x3b91bf0, + 0x0f7432e, + 0x07a443f, + 0x17f24d8, + 0x031067d, + 0x0690fcc}; /* sqrt(sqrt(-1) * A * (A + 2)) */ + +/* + Timing safe memory compare +*/ +int ed25519_verify(const unsigned char* x, const unsigned char* y, size_t len) { + size_t differentbits = 0; + while(len--) differentbits |= (*x++ ^ *y++); + return (int)(1 & ((differentbits - 1) >> 8)); +} + +/* + conversions +*/ + +void ge25519_p1p1_to_partial(ge25519* r, const ge25519_p1p1* p) { + curve25519_mul(r->x, p->x, p->t); + curve25519_mul(r->y, p->y, p->z); + curve25519_mul(r->z, p->z, p->t); +} + +void ge25519_p1p1_to_full(ge25519* r, const ge25519_p1p1* p) { + curve25519_mul(r->x, p->x, p->t); + curve25519_mul(r->y, p->y, p->z); + curve25519_mul(r->z, p->z, p->t); + curve25519_mul(r->t, p->x, p->y); +} + +void ge25519_full_to_pniels(ge25519_pniels* p, const ge25519* r) { + curve25519_sub(p->ysubx, r->y, r->x); + curve25519_add(p->xaddy, r->y, r->x); + curve25519_copy(p->z, r->z); + curve25519_mul(p->t2d, r->t, ge25519_ec2d); +} + +/* + adding & doubling +*/ + +void ge25519_double_p1p1(ge25519_p1p1* r, const ge25519* p) { + bignum25519 a = {0}, b = {0}, c = {0}; + + curve25519_square(a, p->x); + curve25519_square(b, p->y); + curve25519_square(c, p->z); + curve25519_add_reduce(c, c, c); + curve25519_add(r->x, p->x, p->y); + curve25519_square(r->x, r->x); + curve25519_add(r->y, b, a); + curve25519_sub(r->z, b, a); + curve25519_sub_after_basic(r->x, r->x, r->y); + curve25519_sub_after_basic(r->t, c, r->z); +} + +#ifndef ED25519_NO_PRECOMP +void ge25519_nielsadd2_p1p1( + ge25519_p1p1* r, + const ge25519* p, + const ge25519_niels* q, + unsigned char signbit) { + const bignum25519* qb = (const bignum25519*)q; + bignum25519* rb = (bignum25519*)r; + bignum25519 a = {0}, b = {0}, c = {0}; + + curve25519_sub(a, p->y, p->x); + curve25519_add(b, p->y, p->x); + curve25519_mul(a, a, qb[signbit]); /* x for +, y for - */ + curve25519_mul(r->x, b, qb[signbit ^ 1]); /* y for +, x for - */ + curve25519_add(r->y, r->x, a); + curve25519_sub(r->x, r->x, a); + curve25519_mul(c, p->t, q->t2d); + curve25519_add_reduce(r->t, p->z, p->z); + curve25519_copy(r->z, r->t); + curve25519_add(rb[2 + signbit], rb[2 + signbit], c); /* z for +, t for - */ + curve25519_sub(rb[2 + (signbit ^ 1)], rb[2 + (signbit ^ 1)], c); /* t for +, z for - */ +} +#endif + +void ge25519_pnielsadd_p1p1( + ge25519_p1p1* r, + const ge25519* p, + const ge25519_pniels* q, + unsigned char signbit) { + const bignum25519* qb = (const bignum25519*)q; + bignum25519* rb = (bignum25519*)r; + bignum25519 a = {0}, b = {0}, c = {0}; + + curve25519_sub(a, p->y, p->x); + curve25519_add(b, p->y, p->x); + curve25519_mul(a, a, qb[signbit]); /* ysubx for +, xaddy for - */ + curve25519_mul(r->x, b, qb[signbit ^ 1]); /* xaddy for +, ysubx for - */ + curve25519_add(r->y, r->x, a); + curve25519_sub(r->x, r->x, a); + curve25519_mul(c, p->t, q->t2d); + curve25519_mul(r->t, p->z, q->z); + curve25519_add_reduce(r->t, r->t, r->t); + curve25519_copy(r->z, r->t); + curve25519_add(rb[2 + signbit], rb[2 + signbit], c); /* z for +, t for - */ + curve25519_sub(rb[2 + (signbit ^ 1)], rb[2 + (signbit ^ 1)], c); /* t for +, z for - */ +} + +void ge25519_double_partial(ge25519* r, const ge25519* p) { + ge25519_p1p1 t = {0}; + ge25519_double_p1p1(&t, p); + ge25519_p1p1_to_partial(r, &t); +} + +void ge25519_double(ge25519* r, const ge25519* p) { + ge25519_p1p1 t = {0}; + ge25519_double_p1p1(&t, p); + ge25519_p1p1_to_full(r, &t); +} + +void ge25519_nielsadd2(ge25519* r, const ge25519_niels* q) { + bignum25519 a = {0}, b = {0}, c = {0}, e = {0}, f = {0}, g = {0}, h = {0}; + + curve25519_sub(a, r->y, r->x); + curve25519_add(b, r->y, r->x); + curve25519_mul(a, a, q->ysubx); + curve25519_mul(e, b, q->xaddy); + curve25519_add(h, e, a); + curve25519_sub(e, e, a); + curve25519_mul(c, r->t, q->t2d); + curve25519_add(f, r->z, r->z); + curve25519_add_after_basic(g, f, c); + curve25519_sub_after_basic(f, f, c); + curve25519_mul(r->x, e, f); + curve25519_mul(r->y, h, g); + curve25519_mul(r->z, g, f); + curve25519_mul(r->t, e, h); +} + +void ge25519_pnielsadd(ge25519_pniels* r, const ge25519* p, const ge25519_pniels* q) { + bignum25519 a = {0}, b = {0}, c = {0}, x = {0}, y = {0}, z = {0}, t = {0}; + + curve25519_sub(a, p->y, p->x); + curve25519_add(b, p->y, p->x); + curve25519_mul(a, a, q->ysubx); + curve25519_mul(x, b, q->xaddy); + curve25519_add(y, x, a); + curve25519_sub(x, x, a); + curve25519_mul(c, p->t, q->t2d); + curve25519_mul(t, p->z, q->z); + curve25519_add(t, t, t); + curve25519_add_after_basic(z, t, c); + curve25519_sub_after_basic(t, t, c); + curve25519_mul(r->xaddy, x, t); + curve25519_mul(r->ysubx, y, z); + curve25519_mul(r->z, z, t); + curve25519_mul(r->t2d, x, y); + curve25519_copy(y, r->ysubx); + curve25519_sub(r->ysubx, r->ysubx, r->xaddy); + curve25519_add(r->xaddy, r->xaddy, y); + curve25519_mul(r->t2d, r->t2d, ge25519_ec2d); +} + +/* + pack & unpack +*/ + +void ge25519_pack(unsigned char r[32], const ge25519* p) { + bignum25519 tx = {0}, ty = {0}, zi = {0}; + unsigned char parity[32] = {0}; + curve25519_recip(zi, p->z); + curve25519_mul(tx, p->x, zi); + curve25519_mul(ty, p->y, zi); + curve25519_contract(r, ty); + curve25519_contract(parity, tx); + r[31] ^= ((parity[0] & 1) << 7); +} + +int ge25519_unpack_negative_vartime(ge25519* r, const unsigned char p[32]) { + const unsigned char zero[32] = {0}; + const bignum25519 one = {1}; + unsigned char parity = p[31] >> 7; + unsigned char check[32] = {0}; + bignum25519 t = {0}, root = {0}, num = {0}, den = {0}, d3 = {0}; + + curve25519_expand(r->y, p); + curve25519_copy(r->z, one); + curve25519_square(num, r->y); /* x = y^2 */ + curve25519_mul(den, num, ge25519_ecd); /* den = dy^2 */ + curve25519_sub_reduce(num, num, r->z); /* x = y^1 - 1 */ + curve25519_add(den, den, r->z); /* den = dy^2 + 1 */ + + /* Computation of sqrt(num/den) */ + /* 1.: computation of num^((p-5)/8)*den^((7p-35)/8) = (num*den^7)^((p-5)/8) */ + curve25519_square(t, den); + curve25519_mul(d3, t, den); + curve25519_square(r->x, d3); + curve25519_mul(r->x, r->x, den); + curve25519_mul(r->x, r->x, num); + curve25519_pow_two252m3(r->x, r->x); + + /* 2. computation of r->x = num * den^3 * (num*den^7)^((p-5)/8) */ + curve25519_mul(r->x, r->x, d3); + curve25519_mul(r->x, r->x, num); + + /* 3. Check if either of the roots works: */ + curve25519_square(t, r->x); + curve25519_mul(t, t, den); + curve25519_sub_reduce(root, t, num); + curve25519_contract(check, root); + if(!ed25519_verify(check, zero, 32)) { + curve25519_add_reduce(t, t, num); + curve25519_contract(check, t); + if(!ed25519_verify(check, zero, 32)) return 0; + curve25519_mul(r->x, r->x, ge25519_sqrtneg1); + } + + curve25519_contract(check, r->x); + if((check[0] & 1) == parity) { + curve25519_copy(t, r->x); + curve25519_neg(r->x, t); + } + curve25519_mul(r->t, r->x, r->y); + return 1; +} + +/* + scalarmults +*/ + +void ge25519_set_neutral(ge25519* r) { + memzero(r, sizeof(ge25519)); + r->y[0] = 1; + r->z[0] = 1; +} + +#define S1_SWINDOWSIZE 5 +#define S1_TABLE_SIZE (1 << (S1_SWINDOWSIZE - 2)) +#ifdef ED25519_NO_PRECOMP +#define S2_SWINDOWSIZE 5 +#else +#define S2_SWINDOWSIZE 7 +#endif +#define S2_TABLE_SIZE (1 << (S2_SWINDOWSIZE - 2)) + +/* computes [s1]p1 + [s2]base */ +void ge25519_double_scalarmult_vartime( + ge25519* r, + const ge25519* p1, + const bignum256modm s1, + const bignum256modm s2) { + signed char slide1[256] = {0}, slide2[256] = {0}; + ge25519_pniels pre1[S1_TABLE_SIZE] = {0}; +#ifdef ED25519_NO_PRECOMP + ge25519_pniels pre2[S2_TABLE_SIZE] = {0}; +#endif + ge25519 dp = {0}; + ge25519_p1p1 t = {0}; + int32_t i = 0; + + memzero(&t, sizeof(ge25519_p1p1)); + contract256_slidingwindow_modm(slide1, s1, S1_SWINDOWSIZE); + contract256_slidingwindow_modm(slide2, s2, S2_SWINDOWSIZE); + + ge25519_double(&dp, p1); + ge25519_full_to_pniels(pre1, p1); + for(i = 0; i < S1_TABLE_SIZE - 1; i++) ge25519_pnielsadd(&pre1[i + 1], &dp, &pre1[i]); + +#ifdef ED25519_NO_PRECOMP + ge25519_double(&dp, &ge25519_basepoint); + ge25519_full_to_pniels(pre2, &ge25519_basepoint); + for(i = 0; i < S2_TABLE_SIZE - 1; i++) ge25519_pnielsadd(&pre2[i + 1], &dp, &pre2[i]); +#endif + + ge25519_set_neutral(r); + + i = 255; + while((i >= 0) && !(slide1[i] | slide2[i])) i--; + + for(; i >= 0; i--) { + ge25519_double_p1p1(&t, r); + + if(slide1[i]) { + ge25519_p1p1_to_full(r, &t); + ge25519_pnielsadd_p1p1( + &t, r, &pre1[abs(slide1[i]) / 2], (unsigned char)slide1[i] >> 7); + } + + if(slide2[i]) { + ge25519_p1p1_to_full(r, &t); +#ifdef ED25519_NO_PRECOMP + ge25519_pnielsadd_p1p1( + &t, r, &pre2[abs(slide2[i]) / 2], (unsigned char)slide2[i] >> 7); +#else + ge25519_nielsadd2_p1p1( + &t, + r, + &ge25519_niels_sliding_multiples[abs(slide2[i]) / 2], + (unsigned char)slide2[i] >> 7); +#endif + } + + ge25519_p1p1_to_partial(r, &t); + } + curve25519_mul(r->t, t.x, t.y); + memzero(slide1, sizeof(slide1)); + memzero(slide2, sizeof(slide2)); +} + +/* computes [s1]p1 + [s2]p2 */ +#if USE_MONERO +void ge25519_double_scalarmult_vartime2( + ge25519* r, + const ge25519* p1, + const bignum256modm s1, + const ge25519* p2, + const bignum256modm s2) { + signed char slide1[256] = {0}, slide2[256] = {0}; + ge25519_pniels pre1[S1_TABLE_SIZE] = {0}; + ge25519_pniels pre2[S1_TABLE_SIZE] = {0}; + ge25519 dp = {0}; + ge25519_p1p1 t = {0}; + int32_t i = 0; + + memzero(&t, sizeof(ge25519_p1p1)); + contract256_slidingwindow_modm(slide1, s1, S1_SWINDOWSIZE); + contract256_slidingwindow_modm(slide2, s2, S1_SWINDOWSIZE); + + ge25519_double(&dp, p1); + ge25519_full_to_pniels(pre1, p1); + for(i = 0; i < S1_TABLE_SIZE - 1; i++) ge25519_pnielsadd(&pre1[i + 1], &dp, &pre1[i]); + + ge25519_double(&dp, p2); + ge25519_full_to_pniels(pre2, p2); + for(i = 0; i < S1_TABLE_SIZE - 1; i++) ge25519_pnielsadd(&pre2[i + 1], &dp, &pre2[i]); + + ge25519_set_neutral(r); + + i = 255; + while((i >= 0) && !(slide1[i] | slide2[i])) i--; + + for(; i >= 0; i--) { + ge25519_double_p1p1(&t, r); + + if(slide1[i]) { + ge25519_p1p1_to_full(r, &t); + ge25519_pnielsadd_p1p1( + &t, r, &pre1[abs(slide1[i]) / 2], (unsigned char)slide1[i] >> 7); + } + + if(slide2[i]) { + ge25519_p1p1_to_full(r, &t); + ge25519_pnielsadd_p1p1( + &t, r, &pre2[abs(slide2[i]) / 2], (unsigned char)slide2[i] >> 7); + } + + ge25519_p1p1_to_partial(r, &t); + } + curve25519_mul(r->t, t.x, t.y); + memzero(slide1, sizeof(slide1)); + memzero(slide2, sizeof(slide2)); +} +#endif + +/* + * The following conditional move stuff uses conditional moves. + * I will check on which compilers this works, and provide suitable + * workarounds for those where it doesn't. + * + * This works on gcc 4.x and above with -O3. Don't use -O2, this will + * cause the code to not generate conditional moves. Don't use any -march= + * with less than i686 on x86 + */ +static void ge25519_cmove_stride4(long* r, long* p, long* pos, long* n, int stride) { + long x0 = r[0], x1 = r[1], x2 = r[2], x3 = r[3], y0 = 0, y1 = 0, y2 = 0, y3 = 0; + for(; p < n; p += stride) { + volatile int flag = (p == pos); + y0 = p[0]; + y1 = p[1]; + y2 = p[2]; + y3 = p[3]; + x0 = flag ? y0 : x0; + x1 = flag ? y1 : x1; + x2 = flag ? y2 : x2; + x3 = flag ? y3 : x3; + } + r[0] = x0; + r[1] = x1; + r[2] = x2; + r[3] = x3; +} +#define HAS_CMOVE_STRIDE4 + +static void ge25519_cmove_stride4b(long* r, long* p, long* pos, long* n, int stride) { + long x0 = p[0], x1 = p[1], x2 = p[2], x3 = p[3], y0 = 0, y1 = 0, y2 = 0, y3 = 0; + for(p += stride; p < n; p += stride) { + volatile int flag = (p == pos); + y0 = p[0]; + y1 = p[1]; + y2 = p[2]; + y3 = p[3]; + x0 = flag ? y0 : x0; + x1 = flag ? y1 : x1; + x2 = flag ? y2 : x2; + x3 = flag ? y3 : x3; + } + r[0] = x0; + r[1] = x1; + r[2] = x2; + r[3] = x3; +} +#define HAS_CMOVE_STRIDE4B + +void ge25519_move_conditional_pniels_array( + ge25519_pniels* r, + const ge25519_pniels* p, + int pos, + int n) { +#ifdef HAS_CMOVE_STRIDE4B + size_t i = 0; + for(i = 0; i < sizeof(ge25519_pniels) / sizeof(long); i += 4) { + ge25519_cmove_stride4b( + ((long*)r) + i, + ((long*)p) + i, + ((long*)(p + pos)) + i, + ((long*)(p + n)) + i, + sizeof(ge25519_pniels) / sizeof(long)); + } +#else + size_t i = 0; + for(i = 0; i < n; i++) { + ge25519_move_conditional_pniels(r, p + i, pos == i); + } +#endif +} + +void ge25519_move_conditional_niels_array(ge25519_niels* r, const uint8_t p[8][96], int pos, int n) { + size_t i = 0; + for(i = 0; i < 96 / sizeof(long); i += 4) { + ge25519_cmove_stride4( + ((long*)r) + i, + ((long*)p) + i, + ((long*)(p + pos)) + i, + ((long*)(p + n)) + i, + 96 / sizeof(long)); + } +} + +/* computes [s1]p1, constant time */ +void ge25519_scalarmult(ge25519* r, const ge25519* p1, const bignum256modm s1) { + signed char slide1[64] = {0}; + ge25519_pniels pre1[9] = {0}; + ge25519_pniels pre = {0}; + ge25519 d1 = {0}; + ge25519_p1p1 t = {0}; + int32_t i = 0; + + contract256_window4_modm(slide1, s1); + + ge25519_full_to_pniels(pre1 + 1, p1); + ge25519_double(&d1, p1); + + ge25519_set_neutral(r); + ge25519_full_to_pniels(pre1, r); + + ge25519_full_to_pniels(pre1 + 2, &d1); + for(i = 1; i < 7; i++) { + ge25519_pnielsadd(&pre1[i + 2], &d1, &pre1[i]); + } + + for(i = 63; i >= 0; i--) { + int k = abs(slide1[i]); + ge25519_double_partial(r, r); + ge25519_double_partial(r, r); + ge25519_double_partial(r, r); + ge25519_double_p1p1(&t, r); + ge25519_move_conditional_pniels_array(&pre, pre1, k, 9); + ge25519_p1p1_to_full(r, &t); + ge25519_pnielsadd_p1p1(&t, r, &pre, (unsigned char)slide1[i] >> 7); + ge25519_p1p1_to_partial(r, &t); + } + curve25519_mul(r->t, t.x, t.y); + memzero(slide1, sizeof(slide1)); +} + +void ge25519_scalarmult_base_choose_niels( + ge25519_niels* t, + const uint8_t table[256][96], + uint32_t pos, + signed char b) { + bignum25519 neg = {0}; + uint32_t sign = (uint32_t)((unsigned char)b >> 7); + uint32_t mask = ~(sign - 1); + uint32_t u = (b + mask) ^ mask; + + /* ysubx, xaddy, t2d in packed form. initialize to ysubx = 1, xaddy = 1, t2d = 0 */ + uint8_t packed[96] = {0}; + packed[0] = 1; + packed[32] = 1; + + ge25519_move_conditional_niels_array((ge25519_niels*)packed, &table[pos * 8], u - 1, 8); + + /* expand in to t */ + curve25519_expand(t->ysubx, packed + 0); + curve25519_expand(t->xaddy, packed + 32); + curve25519_expand(t->t2d, packed + 64); + + /* adjust for sign */ + curve25519_swap_conditional(t->ysubx, t->xaddy, sign); + curve25519_neg(neg, t->t2d); + curve25519_swap_conditional(t->t2d, neg, sign); +} + +/* computes [s]basepoint */ +void ge25519_scalarmult_base_niels( + ge25519* r, + const uint8_t basepoint_table[256][96], + const bignum256modm s) { + signed char b[64] = {0}; + uint32_t i = 0; + ge25519_niels t = {0}; + + contract256_window4_modm(b, s); + + ge25519_scalarmult_base_choose_niels(&t, basepoint_table, 0, b[1]); + curve25519_sub_reduce(r->x, t.xaddy, t.ysubx); + curve25519_add_reduce(r->y, t.xaddy, t.ysubx); + memzero(r->z, sizeof(bignum25519)); + curve25519_copy(r->t, t.t2d); + r->z[0] = 2; + for(i = 3; i < 64; i += 2) { + ge25519_scalarmult_base_choose_niels(&t, basepoint_table, i / 2, b[i]); + ge25519_nielsadd2(r, &t); + } + ge25519_double_partial(r, r); + ge25519_double_partial(r, r); + ge25519_double_partial(r, r); + ge25519_double(r, r); + ge25519_scalarmult_base_choose_niels(&t, basepoint_table, 0, b[0]); + curve25519_mul(t.t2d, t.t2d, ge25519_ecd); + ge25519_nielsadd2(r, &t); + for(i = 2; i < 64; i += 2) { + ge25519_scalarmult_base_choose_niels(&t, basepoint_table, i / 2, b[i]); + ge25519_nielsadd2(r, &t); + } +} + +int ge25519_check(const ge25519* r) { + /* return (z % q != 0 and + x * y % q == z * t % q and + (y * y - x * x - z * z - ed25519.d * t * t) % q == 0) + */ + + bignum25519 z = {0}, lhs = {0}, rhs = {0}, tmp = {0}, res = {0}; + curve25519_reduce(z, r->z); + + curve25519_mul(lhs, r->x, r->y); + curve25519_mul(rhs, r->z, r->t); + curve25519_sub_reduce(lhs, lhs, rhs); + + curve25519_square(res, r->y); + curve25519_square(tmp, r->x); + curve25519_sub_reduce(res, res, tmp); + curve25519_square(tmp, r->z); + curve25519_sub_reduce(res, res, tmp); + curve25519_square(tmp, r->t); + curve25519_mul(tmp, tmp, ge25519_ecd); + curve25519_sub_reduce(res, res, tmp); + + const int c1 = curve25519_isnonzero(z); + const int c2 = curve25519_isnonzero(lhs); + const int c3 = curve25519_isnonzero(res); + return c1 & (c2 ^ 0x1) & (c3 ^ 0x1); +} + +int ge25519_eq(const ge25519* a, const ge25519* b) { + int eq = 1; + bignum25519 t1 = {0}, t2 = {0}; + + eq &= ge25519_check(a); + eq &= ge25519_check(b); + + curve25519_mul(t1, a->x, b->z); + curve25519_mul(t2, b->x, a->z); + curve25519_sub_reduce(t1, t1, t2); + eq &= curve25519_isnonzero(t1) ^ 1; + + curve25519_mul(t1, a->y, b->z); + curve25519_mul(t2, b->y, a->z); + curve25519_sub_reduce(t1, t1, t2); + eq &= curve25519_isnonzero(t1) ^ 1; + + return eq; +} + +void ge25519_copy(ge25519* dst, const ge25519* src) { + curve25519_copy(dst->x, src->x); + curve25519_copy(dst->y, src->y); + curve25519_copy(dst->z, src->z); + curve25519_copy(dst->t, src->t); +} + +void ge25519_set_base(ge25519* r) { + ge25519_copy(r, &ge25519_basepoint); +} + +void ge25519_mul8(ge25519* r, const ge25519* t) { + ge25519_double_partial(r, t); + ge25519_double_partial(r, r); + ge25519_double(r, r); +} + +void ge25519_neg_partial(ge25519* r) { + curve25519_neg(r->x, r->x); +} + +void ge25519_neg_full(ge25519* r) { + curve25519_neg(r->x, r->x); + curve25519_neg(r->t, r->t); +} + +void ge25519_reduce(ge25519* r, const ge25519* t) { + curve25519_reduce(r->x, t->x); + curve25519_reduce(r->y, t->y); + curve25519_reduce(r->z, t->z); + curve25519_reduce(r->t, t->t); +} + +void ge25519_norm(ge25519* r, const ge25519* t) { + bignum25519 zinv = {0}; + curve25519_recip(zinv, t->z); + curve25519_mul(r->x, t->x, zinv); + curve25519_mul(r->y, t->y, zinv); + curve25519_mul(r->t, r->x, r->y); + curve25519_set(r->z, 1); +} + +void ge25519_add(ge25519* r, const ge25519* p, const ge25519* q, unsigned char signbit) { + ge25519_pniels P_ni = {0}; + ge25519_p1p1 P_11 = {0}; + + ge25519_full_to_pniels(&P_ni, q); + ge25519_pnielsadd_p1p1(&P_11, p, &P_ni, signbit); + ge25519_p1p1_to_full(r, &P_11); +} + +void ge25519_fromfe_frombytes_vartime(ge25519* r, const unsigned char* s) { + bignum25519 u = {0}, v = {0}, w = {0}, x = {0}, y = {0}, z = {0}; + unsigned char sign = 0; + + curve25519_expand_reduce(u, s); + + curve25519_square(v, u); + curve25519_add_reduce(v, v, v); /* 2 * u^2 */ + curve25519_set(w, 1); + curve25519_add_reduce(w, v, w); /* w = 2 * u^2 + 1 */ + + curve25519_square(x, w); /* w^2 */ + curve25519_mul(y, fe_ma2, v); /* -2 * A^2 * u^2 */ + curve25519_add_reduce(x, x, y); /* x = w^2 - 2 * A^2 * u^2 */ + + curve25519_divpowm1(r->x, w, x); /* (w / x)^(m + 1) */ + curve25519_square(y, r->x); + curve25519_mul(x, y, x); + curve25519_sub_reduce(y, w, x); + curve25519_copy(z, fe_ma); + + if(curve25519_isnonzero(y)) { + curve25519_add_reduce(y, w, x); + if(curve25519_isnonzero(y)) { + goto negative; + } else { + curve25519_mul(r->x, r->x, fe_fffb1); + } + } else { + curve25519_mul(r->x, r->x, fe_fffb2); + } + curve25519_mul(r->x, r->x, u); /* u * sqrt(2 * A * (A + 2) * w / x) */ + curve25519_mul(z, z, v); /* -2 * A * u^2 */ + sign = 0; + goto setsign; +negative: + curve25519_mul(x, x, fe_sqrtm1); + curve25519_sub_reduce(y, w, x); + if(curve25519_isnonzero(y)) { + assert((curve25519_add_reduce(y, w, x), !curve25519_isnonzero(y))); + curve25519_mul(r->x, r->x, fe_fffb3); + } else { + curve25519_mul(r->x, r->x, fe_fffb4); + } + /* r->x = sqrt(A * (A + 2) * w / x) */ + /* z = -A */ + sign = 1; +setsign: + if(curve25519_isnegative(r->x) != sign) { + assert(curve25519_isnonzero(r->x)); + curve25519_neg(r->x, r->x); + } + curve25519_add_reduce(r->z, z, w); + curve25519_sub_reduce(r->y, z, w); + curve25519_mul(r->x, r->x, r->z); + + // Partial form, saving from T coord computation . + // Later is mul8 discarding T anyway. + // rt = ((rx * ry % q) * inv(rz)) % q + // curve25519_mul(x, r->x, r->y); + // curve25519_recip(z, r->z); + // curve25519_mul(r->t, x, z); + +#if !defined(NDEBUG) + { + bignum25519 check_x = {0}, check_y = {0}, check_iz = {0}, check_v = {0}; + curve25519_recip(check_iz, r->z); + curve25519_mul(check_x, r->x, check_iz); + curve25519_mul(check_y, r->y, check_iz); + curve25519_square(check_x, check_x); + curve25519_square(check_y, check_y); + curve25519_mul(check_v, check_x, check_y); + curve25519_mul(check_v, fe_d, check_v); + curve25519_add_reduce(check_v, check_v, check_x); + curve25519_sub_reduce(check_v, check_v, check_y); + curve25519_set(check_x, 1); + curve25519_add_reduce(check_v, check_v, check_x); + assert(!curve25519_isnonzero(check_v)); + } +#endif +} + +int ge25519_unpack_vartime(ge25519* r, const unsigned char* s) { + int res = ge25519_unpack_negative_vartime(r, s); + ge25519_neg_full(r); + return res; +} + +void ge25519_scalarmult_base_wrapper(ge25519* r, const bignum256modm s) { + ge25519_scalarmult_base_niels(r, ge25519_niels_base_multiples, s); +} diff --git a/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_donna_impl_base.h b/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_donna_impl_base.h new file mode 100644 index 0000000000..8e690ef801 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_donna_impl_base.h @@ -0,0 +1,127 @@ +/* + Timing safe memory compare +*/ +int ed25519_verify(const unsigned char* x, const unsigned char* y, size_t len); + +/* + conversions +*/ + +void ge25519_p1p1_to_partial(ge25519* r, const ge25519_p1p1* p); + +void ge25519_p1p1_to_full(ge25519* r, const ge25519_p1p1* p); + +void ge25519_full_to_pniels(ge25519_pniels* p, const ge25519* r); + +/* + adding & doubling +*/ + +void ge25519_double_p1p1(ge25519_p1p1* r, const ge25519* p); + +#ifndef ED25519_NO_PRECOMP +void ge25519_nielsadd2_p1p1( + ge25519_p1p1* r, + const ge25519* p, + const ge25519_niels* q, + unsigned char signbit); +#endif + +/* computes [s1]p1 + [s2]p2 */ +//#if USE_MONERO +void ge25519_double_scalarmult_vartime2( + ge25519* r, + const ge25519* p1, + const bignum256modm s1, + const ge25519* p2, + const bignum256modm s2); +//#endif + +void ge25519_pnielsadd_p1p1( + ge25519_p1p1* r, + const ge25519* p, + const ge25519_pniels* q, + unsigned char signbit); + +void ge25519_double_partial(ge25519* r, const ge25519* p); + +void ge25519_double(ge25519* r, const ge25519* p); + +void ge25519_nielsadd2(ge25519* r, const ge25519_niels* q); + +void ge25519_pnielsadd(ge25519_pniels* r, const ge25519* p, const ge25519_pniels* q); + +/* + pack & unpack +*/ + +void ge25519_pack(unsigned char r[32], const ge25519* p); + +int ge25519_unpack_negative_vartime(ge25519* r, const unsigned char p[32]); + +/* + scalarmults +*/ + +void ge25519_set_neutral(ge25519* r); + +/* computes [s1]p1 + [s2]base */ +void ge25519_double_scalarmult_vartime( + ge25519* r, + const ge25519* p1, + const bignum256modm s1, + const bignum256modm s2); + +/* computes [s1]p1, constant time */ +void ge25519_scalarmult(ge25519* r, const ge25519* p1, const bignum256modm s1); + +void ge25519_scalarmult_base_choose_niels( + ge25519_niels* t, + const uint8_t table[256][96], + uint32_t pos, + signed char b); + +/* computes [s]basepoint */ +void ge25519_scalarmult_base_niels( + ge25519* r, + const uint8_t basepoint_table[256][96], + const bignum256modm s); + +/* check if r is on curve */ +int ge25519_check(const ge25519* r); + +/* a == b */ +int ge25519_eq(const ge25519* a, const ge25519* b); + +/* copies one point to another */ +void ge25519_copy(ge25519* dst, const ge25519* src); + +/* sets B point to r */ +void ge25519_set_base(ge25519* r); + +/* 8*P */ +void ge25519_mul8(ge25519* r, const ge25519* t); + +/* -P */ +void ge25519_neg_partial(ge25519* r); + +/* -P */ +void ge25519_neg_full(ge25519* r); + +/* reduce all coords */ +void ge25519_reduce(ge25519* r, const ge25519* t); + +/* normalizes coords. (x, y, 1, x*y) */ +void ge25519_norm(ge25519* r, const ge25519* t); + +/* Simple addition */ +void ge25519_add(ge25519* r, const ge25519* a, const ge25519* b, unsigned char signbit); + +/* point from bytes, used in H_p() */ +void ge25519_fromfe_frombytes_vartime(ge25519* r, const unsigned char* s); + +/* point from bytes */ +int ge25519_unpack_vartime(ge25519* r, const unsigned char* s); + +/* aG, wrapper for niels base mult. */ +void ge25519_scalarmult_base_wrapper(ge25519* r, const bignum256modm s); diff --git a/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_donna_portable.h b/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_donna_portable.h new file mode 100644 index 0000000000..d344c9e2ae --- /dev/null +++ b/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_donna_portable.h @@ -0,0 +1,22 @@ +#define mul32x32_64(a, b) (((uint64_t)(a)) * (b)) + +#include +#include +#include + +#define DONNA_INLINE +#undef ALIGN +#define ALIGN(x) __attribute__((aligned(x))) + +static inline void U32TO8_LE(unsigned char* p, const uint32_t v) { + p[0] = (unsigned char)(v); + p[1] = (unsigned char)(v >> 8); + p[2] = (unsigned char)(v >> 16); + p[3] = (unsigned char)(v >> 24); +} + +static inline uint32_t U8TO32_LE(const unsigned char* p) { + return ( + ((uint32_t)(p[0])) | ((uint32_t)(p[1]) << 8) | ((uint32_t)(p[2]) << 16) | + ((uint32_t)(p[3]) << 24)); +} diff --git a/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_hash_custom.h b/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_hash_custom.h new file mode 100644 index 0000000000..7bae63ed1f --- /dev/null +++ b/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_hash_custom.h @@ -0,0 +1,23 @@ +/* + a custom hash must have a 512bit digest and implement: + + struct ed25519_hash_context; + + void ed25519_hash_init(ed25519_hash_context *ctx); + void ed25519_hash_update(ed25519_hash_context *ctx, const uint8_t *in, size_t inlen); + void ed25519_hash_final(ed25519_hash_context *ctx, uint8_t *hash); + void ed25519_hash(uint8_t *hash, const uint8_t *in, size_t inlen); +*/ + +#ifndef ED25519_HASH_CUSTOM +#define ED25519_HASH_CUSTOM + +#include "../sha2.h" + +#define ed25519_hash_context SHA512_CTX +#define ed25519_hash_init(ctx) sha512_Init(ctx) +#define ed25519_hash_update(ctx, in, inlen) sha512_Update((ctx), (in), (inlen)) +#define ed25519_hash_final(ctx, hash) sha512_Final((ctx), (hash)) +#define ed25519_hash(hash, in, inlen) sha512_Raw((in), (inlen), (hash)) + +#endif // ED25519_HASH_CUSTOM diff --git a/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_hash_custom_keccak.h b/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_hash_custom_keccak.h new file mode 100644 index 0000000000..74a986824a --- /dev/null +++ b/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_hash_custom_keccak.h @@ -0,0 +1,29 @@ +/* + a custom hash must have a 512bit digest and implement: + + struct ed25519_hash_context; + + void ed25519_hash_init(ed25519_hash_context *ctx); + void ed25519_hash_update(ed25519_hash_context *ctx, const uint8_t *in, size_t inlen); + void ed25519_hash_final(ed25519_hash_context *ctx, uint8_t *hash); + void ed25519_hash(uint8_t *hash, const uint8_t *in, size_t inlen); +*/ + +#include "../options.h" + +#if USE_KECCAK + +#ifndef ED25519_HASH_CUSTOM +#define ED25519_HASH_CUSTOM + +#include "../sha3.h" + +#define ed25519_hash_context SHA3_CTX +#define ed25519_hash_init(ctx) keccak_512_Init(ctx) +#define ed25519_hash_update(ctx, in, inlen) keccak_Update((ctx), (in), (inlen)) +#define ed25519_hash_final(ctx, hash) keccak_Final((ctx), (hash)) +#define ed25519_hash(hash, in, inlen) keccak_512((in), (inlen), (hash)) + +#endif // ED25519_HASH_CUSTOM + +#endif // USE_KECCAK diff --git a/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_hash_custom_sha3.h b/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_hash_custom_sha3.h new file mode 100644 index 0000000000..f857821fad --- /dev/null +++ b/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_hash_custom_sha3.h @@ -0,0 +1,23 @@ +/* + a custom hash must have a 512bit digest and implement: + + struct ed25519_hash_context; + + void ed25519_hash_init(ed25519_hash_context *ctx); + void ed25519_hash_update(ed25519_hash_context *ctx, const uint8_t *in, size_t inlen); + void ed25519_hash_final(ed25519_hash_context *ctx, uint8_t *hash); + void ed25519_hash(uint8_t *hash, const uint8_t *in, size_t inlen); +*/ + +#ifndef ED25519_HASH_CUSTOM +#define ED25519_HASH_CUSTOM + +#include "../sha3.h" + +#define ed25519_hash_context SHA3_CTX +#define ed25519_hash_init(ctx) sha3_512_Init(ctx) +#define ed25519_hash_update(ctx, in, inlen) sha3_Update((ctx), (in), (inlen)) +#define ed25519_hash_final(ctx, hash) sha3_Final((ctx), (hash)) +#define ed25519_hash(hash, in, inlen) sha3_512((in), (inlen), (hash)) + +#endif // ED25519_HASH_CUSTOM diff --git a/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_keccak.c b/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_keccak.c new file mode 100644 index 0000000000..7b33b6bfe5 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_keccak.c @@ -0,0 +1,14 @@ +#include "../options.h" + +#if USE_KECCAK + +#include + +#include "ed25519_keccak.h" +#include "ed25519_hash_custom_keccak.h" + +#define ED25519_SUFFIX _keccak + +#include "ed25519.c" + +#endif // USE_KECCAK \ No newline at end of file diff --git a/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_keccak.h b/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_keccak.h new file mode 100644 index 0000000000..4c315a75ea --- /dev/null +++ b/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_keccak.h @@ -0,0 +1,38 @@ +#include "../options.h" + +#if USE_KECCAK + +#ifndef ED25519_KECCAK_H +#define ED25519_KECCAK_H + +#include "ed25519.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +void ed25519_publickey_keccak(const ed25519_secret_key sk, ed25519_public_key pk); + +int ed25519_sign_open_keccak( + const unsigned char* m, + size_t mlen, + const ed25519_public_key pk, + const ed25519_signature RS); +void ed25519_sign_keccak( + const unsigned char* m, + size_t mlen, + const ed25519_secret_key sk, + ed25519_signature RS); + +int ed25519_scalarmult_keccak( + ed25519_public_key res, + const ed25519_secret_key sk, + const ed25519_public_key pk); + +#if defined(__cplusplus) +} +#endif + +#endif // ED25519_KECCAK_H + +#endif // USE_KECCAK \ No newline at end of file diff --git a/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_sha3.c b/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_sha3.c new file mode 100644 index 0000000000..e172ef874e --- /dev/null +++ b/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_sha3.c @@ -0,0 +1,8 @@ +#include + +#include "ed25519_sha3.h" +#include "ed25519_hash_custom_sha3.h" + +#define ED25519_SUFFIX _sha3 + +#include "ed25519.c" diff --git a/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_sha3.h b/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_sha3.h new file mode 100644 index 0000000000..1085c1b20d --- /dev/null +++ b/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_sha3.h @@ -0,0 +1,32 @@ +#ifndef ED25519_SHA3_H +#define ED25519_SHA3_H + +#include "ed25519.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +void ed25519_publickey_sha3(const ed25519_secret_key sk, ed25519_public_key pk); + +int ed25519_sign_open_sha3( + const unsigned char* m, + size_t mlen, + const ed25519_public_key pk, + const ed25519_signature RS); +void ed25519_sign_sha3( + const unsigned char* m, + size_t mlen, + const ed25519_secret_key sk, + ed25519_signature RS); + +int ed25519_scalarmult_sha3( + ed25519_public_key res, + const ed25519_secret_key sk, + const ed25519_public_key pk); + +#if defined(__cplusplus) +} +#endif + +#endif // ED25519_SHA3_H diff --git a/applications/external/flipbip/lib/crypto/ed25519_donna/modm_donna_32bit.c b/applications/external/flipbip/lib/crypto/ed25519_donna/modm_donna_32bit.c new file mode 100644 index 0000000000..6489ea20c7 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/ed25519_donna/modm_donna_32bit.c @@ -0,0 +1,800 @@ +/* + Public domain by Andrew M. +*/ + +#include "ed25519_donna.h" + +/* + Arithmetic modulo the group order n = 2^252 + 27742317777372353535851937790883648493 = 7237005577332262213973186563042994240857116359379907606001950938285454250989 + + k = 32 + b = 1 << 8 = 256 + m = 2^252 + 27742317777372353535851937790883648493 = 0x1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3ed + mu = floor( b^(k*2) / m ) = 0xfffffffffffffffffffffffffffffffeb2106215d086329a7ed9ce5a30a2c131b +*/ + +static const bignum256modm modm_m = { + 0x1cf5d3ed, + 0x20498c69, + 0x2f79cd65, + 0x37be77a8, + 0x00000014, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00001000}; + +static const bignum256modm modm_mu = { + 0x0a2c131b, + 0x3673968c, + 0x06329a7e, + 0x01885742, + 0x3fffeb21, + 0x3fffffff, + 0x3fffffff, + 0x3fffffff, + 0x000fffff}; + +static bignum256modm_element_t lt_modm(bignum256modm_element_t a, bignum256modm_element_t b) { + return (a - b) >> 31; +} + +/* see HAC, Alg. 14.42 Step 4 */ +void reduce256_modm(bignum256modm r) { + bignum256modm t = {0}; + bignum256modm_element_t b = 0, pb = 0, mask = 0; + + /* t = r - m */ + pb = 0; + pb += modm_m[0]; + b = lt_modm(r[0], pb); + t[0] = (r[0] - pb + (b << 30)); + pb = b; + pb += modm_m[1]; + b = lt_modm(r[1], pb); + t[1] = (r[1] - pb + (b << 30)); + pb = b; + pb += modm_m[2]; + b = lt_modm(r[2], pb); + t[2] = (r[2] - pb + (b << 30)); + pb = b; + pb += modm_m[3]; + b = lt_modm(r[3], pb); + t[3] = (r[3] - pb + (b << 30)); + pb = b; + pb += modm_m[4]; + b = lt_modm(r[4], pb); + t[4] = (r[4] - pb + (b << 30)); + pb = b; + pb += modm_m[5]; + b = lt_modm(r[5], pb); + t[5] = (r[5] - pb + (b << 30)); + pb = b; + pb += modm_m[6]; + b = lt_modm(r[6], pb); + t[6] = (r[6] - pb + (b << 30)); + pb = b; + pb += modm_m[7]; + b = lt_modm(r[7], pb); + t[7] = (r[7] - pb + (b << 30)); + pb = b; + pb += modm_m[8]; + b = lt_modm(r[8], pb); + t[8] = (r[8] - pb + (b << 16)); + + /* keep r if r was smaller than m */ + mask = b - 1; + r[0] ^= mask & (r[0] ^ t[0]); + r[1] ^= mask & (r[1] ^ t[1]); + r[2] ^= mask & (r[2] ^ t[2]); + r[3] ^= mask & (r[3] ^ t[3]); + r[4] ^= mask & (r[4] ^ t[4]); + r[5] ^= mask & (r[5] ^ t[5]); + r[6] ^= mask & (r[6] ^ t[6]); + r[7] ^= mask & (r[7] ^ t[7]); + r[8] ^= mask & (r[8] ^ t[8]); +} + +/* + Barrett reduction, see HAC, Alg. 14.42 + + Instead of passing in x, pre-process in to q1 and r1 for efficiency +*/ +void barrett_reduce256_modm(bignum256modm r, const bignum256modm q1, const bignum256modm r1) { + bignum256modm q3 = {0}, r2 = {0}; + uint64_t c = 0; + bignum256modm_element_t f = 0, b = 0, pb = 0; + + /* q1 = x >> 248 = 264 bits = 9 30 bit elements + q2 = mu * q1 + q3 = (q2 / 256(32+1)) = q2 / (2^8)^(32+1) = q2 >> 264 */ + c = mul32x32_64(modm_mu[0], q1[7]) + mul32x32_64(modm_mu[1], q1[6]) + + mul32x32_64(modm_mu[2], q1[5]) + mul32x32_64(modm_mu[3], q1[4]) + + mul32x32_64(modm_mu[4], q1[3]) + mul32x32_64(modm_mu[5], q1[2]) + + mul32x32_64(modm_mu[6], q1[1]) + mul32x32_64(modm_mu[7], q1[0]); + c >>= 30; + c += mul32x32_64(modm_mu[0], q1[8]) + mul32x32_64(modm_mu[1], q1[7]) + + mul32x32_64(modm_mu[2], q1[6]) + mul32x32_64(modm_mu[3], q1[5]) + + mul32x32_64(modm_mu[4], q1[4]) + mul32x32_64(modm_mu[5], q1[3]) + + mul32x32_64(modm_mu[6], q1[2]) + mul32x32_64(modm_mu[7], q1[1]) + + mul32x32_64(modm_mu[8], q1[0]); + f = (bignum256modm_element_t)c; + q3[0] = (f >> 24) & 0x3f; + c >>= 30; + c += mul32x32_64(modm_mu[1], q1[8]) + mul32x32_64(modm_mu[2], q1[7]) + + mul32x32_64(modm_mu[3], q1[6]) + mul32x32_64(modm_mu[4], q1[5]) + + mul32x32_64(modm_mu[5], q1[4]) + mul32x32_64(modm_mu[6], q1[3]) + + mul32x32_64(modm_mu[7], q1[2]) + mul32x32_64(modm_mu[8], q1[1]); + f = (bignum256modm_element_t)c; + q3[0] |= (f << 6) & 0x3fffffff; + q3[1] = (f >> 24) & 0x3f; + c >>= 30; + c += mul32x32_64(modm_mu[2], q1[8]) + mul32x32_64(modm_mu[3], q1[7]) + + mul32x32_64(modm_mu[4], q1[6]) + mul32x32_64(modm_mu[5], q1[5]) + + mul32x32_64(modm_mu[6], q1[4]) + mul32x32_64(modm_mu[7], q1[3]) + + mul32x32_64(modm_mu[8], q1[2]); + f = (bignum256modm_element_t)c; + q3[1] |= (f << 6) & 0x3fffffff; + q3[2] = (f >> 24) & 0x3f; + c >>= 30; + c += mul32x32_64(modm_mu[3], q1[8]) + mul32x32_64(modm_mu[4], q1[7]) + + mul32x32_64(modm_mu[5], q1[6]) + mul32x32_64(modm_mu[6], q1[5]) + + mul32x32_64(modm_mu[7], q1[4]) + mul32x32_64(modm_mu[8], q1[3]); + f = (bignum256modm_element_t)c; + q3[2] |= (f << 6) & 0x3fffffff; + q3[3] = (f >> 24) & 0x3f; + c >>= 30; + c += mul32x32_64(modm_mu[4], q1[8]) + mul32x32_64(modm_mu[5], q1[7]) + + mul32x32_64(modm_mu[6], q1[6]) + mul32x32_64(modm_mu[7], q1[5]) + + mul32x32_64(modm_mu[8], q1[4]); + f = (bignum256modm_element_t)c; + q3[3] |= (f << 6) & 0x3fffffff; + q3[4] = (f >> 24) & 0x3f; + c >>= 30; + c += mul32x32_64(modm_mu[5], q1[8]) + mul32x32_64(modm_mu[6], q1[7]) + + mul32x32_64(modm_mu[7], q1[6]) + mul32x32_64(modm_mu[8], q1[5]); + f = (bignum256modm_element_t)c; + q3[4] |= (f << 6) & 0x3fffffff; + q3[5] = (f >> 24) & 0x3f; + c >>= 30; + c += mul32x32_64(modm_mu[6], q1[8]) + mul32x32_64(modm_mu[7], q1[7]) + + mul32x32_64(modm_mu[8], q1[6]); + f = (bignum256modm_element_t)c; + q3[5] |= (f << 6) & 0x3fffffff; + q3[6] = (f >> 24) & 0x3f; + c >>= 30; + c += mul32x32_64(modm_mu[7], q1[8]) + mul32x32_64(modm_mu[8], q1[7]); + f = (bignum256modm_element_t)c; + q3[6] |= (f << 6) & 0x3fffffff; + q3[7] = (f >> 24) & 0x3f; + c >>= 30; + c += mul32x32_64(modm_mu[8], q1[8]); + f = (bignum256modm_element_t)c; + q3[7] |= (f << 6) & 0x3fffffff; + q3[8] = (bignum256modm_element_t)(c >> 24); + + /* r1 = (x mod 256^(32+1)) = x mod (2^8)(32+1) = x & ((1 << 264) - 1) + r2 = (q3 * m) mod (256^(32+1)) = (q3 * m) & ((1 << 264) - 1) */ + c = mul32x32_64(modm_m[0], q3[0]); + r2[0] = (bignum256modm_element_t)(c & 0x3fffffff); + c >>= 30; + c += mul32x32_64(modm_m[0], q3[1]) + mul32x32_64(modm_m[1], q3[0]); + r2[1] = (bignum256modm_element_t)(c & 0x3fffffff); + c >>= 30; + c += mul32x32_64(modm_m[0], q3[2]) + mul32x32_64(modm_m[1], q3[1]) + + mul32x32_64(modm_m[2], q3[0]); + r2[2] = (bignum256modm_element_t)(c & 0x3fffffff); + c >>= 30; + c += mul32x32_64(modm_m[0], q3[3]) + mul32x32_64(modm_m[1], q3[2]) + + mul32x32_64(modm_m[2], q3[1]) + mul32x32_64(modm_m[3], q3[0]); + r2[3] = (bignum256modm_element_t)(c & 0x3fffffff); + c >>= 30; + c += mul32x32_64(modm_m[0], q3[4]) + mul32x32_64(modm_m[1], q3[3]) + + mul32x32_64(modm_m[2], q3[2]) + mul32x32_64(modm_m[3], q3[1]) + + mul32x32_64(modm_m[4], q3[0]); + r2[4] = (bignum256modm_element_t)(c & 0x3fffffff); + c >>= 30; + c += mul32x32_64(modm_m[0], q3[5]) + mul32x32_64(modm_m[1], q3[4]) + + mul32x32_64(modm_m[2], q3[3]) + mul32x32_64(modm_m[3], q3[2]) + + mul32x32_64(modm_m[4], q3[1]) + mul32x32_64(modm_m[5], q3[0]); + r2[5] = (bignum256modm_element_t)(c & 0x3fffffff); + c >>= 30; + c += mul32x32_64(modm_m[0], q3[6]) + mul32x32_64(modm_m[1], q3[5]) + + mul32x32_64(modm_m[2], q3[4]) + mul32x32_64(modm_m[3], q3[3]) + + mul32x32_64(modm_m[4], q3[2]) + mul32x32_64(modm_m[5], q3[1]) + + mul32x32_64(modm_m[6], q3[0]); + r2[6] = (bignum256modm_element_t)(c & 0x3fffffff); + c >>= 30; + c += mul32x32_64(modm_m[0], q3[7]) + mul32x32_64(modm_m[1], q3[6]) + + mul32x32_64(modm_m[2], q3[5]) + mul32x32_64(modm_m[3], q3[4]) + + mul32x32_64(modm_m[4], q3[3]) + mul32x32_64(modm_m[5], q3[2]) + + mul32x32_64(modm_m[6], q3[1]) + mul32x32_64(modm_m[7], q3[0]); + r2[7] = (bignum256modm_element_t)(c & 0x3fffffff); + c >>= 30; + c += mul32x32_64(modm_m[0], q3[8]) + mul32x32_64(modm_m[1], q3[7]) + + mul32x32_64(modm_m[2], q3[6]) + mul32x32_64(modm_m[3], q3[5]) + + mul32x32_64(modm_m[4], q3[4]) + mul32x32_64(modm_m[5], q3[3]) + + mul32x32_64(modm_m[6], q3[2]) + mul32x32_64(modm_m[7], q3[1]) + + mul32x32_64(modm_m[8], q3[0]); + r2[8] = (bignum256modm_element_t)(c & 0xffffff); + + /* r = r1 - r2 + if (r < 0) r += (1 << 264) */ + pb = 0; + pb += r2[0]; + b = lt_modm(r1[0], pb); + r[0] = (r1[0] - pb + (b << 30)); + pb = b; + pb += r2[1]; + b = lt_modm(r1[1], pb); + r[1] = (r1[1] - pb + (b << 30)); + pb = b; + pb += r2[2]; + b = lt_modm(r1[2], pb); + r[2] = (r1[2] - pb + (b << 30)); + pb = b; + pb += r2[3]; + b = lt_modm(r1[3], pb); + r[3] = (r1[3] - pb + (b << 30)); + pb = b; + pb += r2[4]; + b = lt_modm(r1[4], pb); + r[4] = (r1[4] - pb + (b << 30)); + pb = b; + pb += r2[5]; + b = lt_modm(r1[5], pb); + r[5] = (r1[5] - pb + (b << 30)); + pb = b; + pb += r2[6]; + b = lt_modm(r1[6], pb); + r[6] = (r1[6] - pb + (b << 30)); + pb = b; + pb += r2[7]; + b = lt_modm(r1[7], pb); + r[7] = (r1[7] - pb + (b << 30)); + pb = b; + pb += r2[8]; + b = lt_modm(r1[8], pb); + r[8] = (r1[8] - pb + (b << 24)); + + reduce256_modm(r); + reduce256_modm(r); +} + +/* addition modulo m */ +void add256_modm(bignum256modm r, const bignum256modm x, const bignum256modm y) { + bignum256modm_element_t c = 0; + + c = x[0] + y[0]; + r[0] = c & 0x3fffffff; + c >>= 30; + c += x[1] + y[1]; + r[1] = c & 0x3fffffff; + c >>= 30; + c += x[2] + y[2]; + r[2] = c & 0x3fffffff; + c >>= 30; + c += x[3] + y[3]; + r[3] = c & 0x3fffffff; + c >>= 30; + c += x[4] + y[4]; + r[4] = c & 0x3fffffff; + c >>= 30; + c += x[5] + y[5]; + r[5] = c & 0x3fffffff; + c >>= 30; + c += x[6] + y[6]; + r[6] = c & 0x3fffffff; + c >>= 30; + c += x[7] + y[7]; + r[7] = c & 0x3fffffff; + c >>= 30; + c += x[8] + y[8]; + r[8] = c; + + reduce256_modm(r); +} + +/* -x modulo m */ +void neg256_modm(bignum256modm r, const bignum256modm x) { + bignum256modm_element_t b = 0, pb = 0; + + /* r = m - x */ + pb = 0; + pb += x[0]; + b = lt_modm(modm_m[0], pb); + r[0] = (modm_m[0] - pb + (b << 30)); + pb = b; + pb += x[1]; + b = lt_modm(modm_m[1], pb); + r[1] = (modm_m[1] - pb + (b << 30)); + pb = b; + pb += x[2]; + b = lt_modm(modm_m[2], pb); + r[2] = (modm_m[2] - pb + (b << 30)); + pb = b; + pb += x[3]; + b = lt_modm(modm_m[3], pb); + r[3] = (modm_m[3] - pb + (b << 30)); + pb = b; + pb += x[4]; + b = lt_modm(modm_m[4], pb); + r[4] = (modm_m[4] - pb + (b << 30)); + pb = b; + pb += x[5]; + b = lt_modm(modm_m[5], pb); + r[5] = (modm_m[5] - pb + (b << 30)); + pb = b; + pb += x[6]; + b = lt_modm(modm_m[6], pb); + r[6] = (modm_m[6] - pb + (b << 30)); + pb = b; + pb += x[7]; + b = lt_modm(modm_m[7], pb); + r[7] = (modm_m[7] - pb + (b << 30)); + pb = b; + pb += x[8]; + b = lt_modm(modm_m[8], pb); + r[8] = (modm_m[8] - pb + (b << 16)); + + // if x==0, reduction is required + reduce256_modm(r); +} + +/* consts for subtraction, > p */ +/* Emilia Kasper trick, https://www.imperialviolet.org/2010/12/04/ecc.html */ +static const uint32_t twoP[] = { + 0x5cf5d3ed, + 0x60498c68, + 0x6f79cd64, + 0x77be77a7, + 0x40000013, + 0x3fffffff, + 0x3fffffff, + 0x3fffffff, + 0xfff}; + +/* subtraction x-y % m */ +void sub256_modm(bignum256modm r, const bignum256modm x, const bignum256modm y) { + bignum256modm_element_t c = 0; + c = twoP[0] + x[0] - y[0]; + r[0] = c & 0x3fffffff; + c >>= 30; + c += twoP[1] + x[1] - y[1]; + r[1] = c & 0x3fffffff; + c >>= 30; + c += twoP[2] + x[2] - y[2]; + r[2] = c & 0x3fffffff; + c >>= 30; + c += twoP[3] + x[3] - y[3]; + r[3] = c & 0x3fffffff; + c >>= 30; + c += twoP[4] + x[4] - y[4]; + r[4] = c & 0x3fffffff; + c >>= 30; + c += twoP[5] + x[5] - y[5]; + r[5] = c & 0x3fffffff; + c >>= 30; + c += twoP[6] + x[6] - y[6]; + r[6] = c & 0x3fffffff; + c >>= 30; + c += twoP[7] + x[7] - y[7]; + r[7] = c & 0x3fffffff; + c >>= 30; + c += twoP[8] + x[8] - y[8]; + r[8] = c; + reduce256_modm(r); +} + +/* multiplication modulo m */ +void mul256_modm(bignum256modm r, const bignum256modm x, const bignum256modm y) { + bignum256modm r1 = {0}, q1 = {0}; + uint64_t c = 0; + bignum256modm_element_t f = 0; + + /* r1 = (x mod 256^(32+1)) = x mod (2^8)(31+1) = x & ((1 << 264) - 1) + q1 = x >> 248 = 264 bits = 9 30 bit elements */ + c = mul32x32_64(x[0], y[0]); + f = (bignum256modm_element_t)c; + r1[0] = (f & 0x3fffffff); + c >>= 30; + c += mul32x32_64(x[0], y[1]) + mul32x32_64(x[1], y[0]); + f = (bignum256modm_element_t)c; + r1[1] = (f & 0x3fffffff); + c >>= 30; + c += mul32x32_64(x[0], y[2]) + mul32x32_64(x[1], y[1]) + mul32x32_64(x[2], y[0]); + f = (bignum256modm_element_t)c; + r1[2] = (f & 0x3fffffff); + c >>= 30; + c += mul32x32_64(x[0], y[3]) + mul32x32_64(x[1], y[2]) + mul32x32_64(x[2], y[1]) + + mul32x32_64(x[3], y[0]); + f = (bignum256modm_element_t)c; + r1[3] = (f & 0x3fffffff); + c >>= 30; + c += mul32x32_64(x[0], y[4]) + mul32x32_64(x[1], y[3]) + mul32x32_64(x[2], y[2]) + + mul32x32_64(x[3], y[1]) + mul32x32_64(x[4], y[0]); + f = (bignum256modm_element_t)c; + r1[4] = (f & 0x3fffffff); + c >>= 30; + c += mul32x32_64(x[0], y[5]) + mul32x32_64(x[1], y[4]) + mul32x32_64(x[2], y[3]) + + mul32x32_64(x[3], y[2]) + mul32x32_64(x[4], y[1]) + mul32x32_64(x[5], y[0]); + f = (bignum256modm_element_t)c; + r1[5] = (f & 0x3fffffff); + c >>= 30; + c += mul32x32_64(x[0], y[6]) + mul32x32_64(x[1], y[5]) + mul32x32_64(x[2], y[4]) + + mul32x32_64(x[3], y[3]) + mul32x32_64(x[4], y[2]) + mul32x32_64(x[5], y[1]) + + mul32x32_64(x[6], y[0]); + f = (bignum256modm_element_t)c; + r1[6] = (f & 0x3fffffff); + c >>= 30; + c += mul32x32_64(x[0], y[7]) + mul32x32_64(x[1], y[6]) + mul32x32_64(x[2], y[5]) + + mul32x32_64(x[3], y[4]) + mul32x32_64(x[4], y[3]) + mul32x32_64(x[5], y[2]) + + mul32x32_64(x[6], y[1]) + mul32x32_64(x[7], y[0]); + f = (bignum256modm_element_t)c; + r1[7] = (f & 0x3fffffff); + c >>= 30; + c += mul32x32_64(x[0], y[8]) + mul32x32_64(x[1], y[7]) + mul32x32_64(x[2], y[6]) + + mul32x32_64(x[3], y[5]) + mul32x32_64(x[4], y[4]) + mul32x32_64(x[5], y[3]) + + mul32x32_64(x[6], y[2]) + mul32x32_64(x[7], y[1]) + mul32x32_64(x[8], y[0]); + f = (bignum256modm_element_t)c; + r1[8] = (f & 0x00ffffff); + q1[0] = (f >> 8) & 0x3fffff; + c >>= 30; + c += mul32x32_64(x[1], y[8]) + mul32x32_64(x[2], y[7]) + mul32x32_64(x[3], y[6]) + + mul32x32_64(x[4], y[5]) + mul32x32_64(x[5], y[4]) + mul32x32_64(x[6], y[3]) + + mul32x32_64(x[7], y[2]) + mul32x32_64(x[8], y[1]); + f = (bignum256modm_element_t)c; + q1[0] = (q1[0] | (f << 22)) & 0x3fffffff; + q1[1] = (f >> 8) & 0x3fffff; + c >>= 30; + c += mul32x32_64(x[2], y[8]) + mul32x32_64(x[3], y[7]) + mul32x32_64(x[4], y[6]) + + mul32x32_64(x[5], y[5]) + mul32x32_64(x[6], y[4]) + mul32x32_64(x[7], y[3]) + + mul32x32_64(x[8], y[2]); + f = (bignum256modm_element_t)c; + q1[1] = (q1[1] | (f << 22)) & 0x3fffffff; + q1[2] = (f >> 8) & 0x3fffff; + c >>= 30; + c += mul32x32_64(x[3], y[8]) + mul32x32_64(x[4], y[7]) + mul32x32_64(x[5], y[6]) + + mul32x32_64(x[6], y[5]) + mul32x32_64(x[7], y[4]) + mul32x32_64(x[8], y[3]); + f = (bignum256modm_element_t)c; + q1[2] = (q1[2] | (f << 22)) & 0x3fffffff; + q1[3] = (f >> 8) & 0x3fffff; + c >>= 30; + c += mul32x32_64(x[4], y[8]) + mul32x32_64(x[5], y[7]) + mul32x32_64(x[6], y[6]) + + mul32x32_64(x[7], y[5]) + mul32x32_64(x[8], y[4]); + f = (bignum256modm_element_t)c; + q1[3] = (q1[3] | (f << 22)) & 0x3fffffff; + q1[4] = (f >> 8) & 0x3fffff; + c >>= 30; + c += mul32x32_64(x[5], y[8]) + mul32x32_64(x[6], y[7]) + mul32x32_64(x[7], y[6]) + + mul32x32_64(x[8], y[5]); + f = (bignum256modm_element_t)c; + q1[4] = (q1[4] | (f << 22)) & 0x3fffffff; + q1[5] = (f >> 8) & 0x3fffff; + c >>= 30; + c += mul32x32_64(x[6], y[8]) + mul32x32_64(x[7], y[7]) + mul32x32_64(x[8], y[6]); + f = (bignum256modm_element_t)c; + q1[5] = (q1[5] | (f << 22)) & 0x3fffffff; + q1[6] = (f >> 8) & 0x3fffff; + c >>= 30; + c += mul32x32_64(x[7], y[8]) + mul32x32_64(x[8], y[7]); + f = (bignum256modm_element_t)c; + q1[6] = (q1[6] | (f << 22)) & 0x3fffffff; + q1[7] = (f >> 8) & 0x3fffff; + c >>= 30; + c += mul32x32_64(x[8], y[8]); + f = (bignum256modm_element_t)c; + q1[7] = (q1[7] | (f << 22)) & 0x3fffffff; + q1[8] = (f >> 8) & 0x3fffff; + + barrett_reduce256_modm(r, q1, r1); +} + +void expand256_modm(bignum256modm out, const unsigned char* in, size_t len) { + unsigned char work[64] = {0}; + bignum256modm_element_t x[16] = {0}; + bignum256modm q1 = {0}; + + memcpy(work, in, len); + x[0] = U8TO32_LE(work + 0); + x[1] = U8TO32_LE(work + 4); + x[2] = U8TO32_LE(work + 8); + x[3] = U8TO32_LE(work + 12); + x[4] = U8TO32_LE(work + 16); + x[5] = U8TO32_LE(work + 20); + x[6] = U8TO32_LE(work + 24); + x[7] = U8TO32_LE(work + 28); + x[8] = U8TO32_LE(work + 32); + x[9] = U8TO32_LE(work + 36); + x[10] = U8TO32_LE(work + 40); + x[11] = U8TO32_LE(work + 44); + x[12] = U8TO32_LE(work + 48); + x[13] = U8TO32_LE(work + 52); + x[14] = U8TO32_LE(work + 56); + x[15] = U8TO32_LE(work + 60); + + /* r1 = (x mod 256^(32+1)) = x mod (2^8)(31+1) = x & ((1 << 264) - 1) */ + out[0] = (x[0]) & 0x3fffffff; + out[1] = ((x[0] >> 30) | (x[1] << 2)) & 0x3fffffff; + out[2] = ((x[1] >> 28) | (x[2] << 4)) & 0x3fffffff; + out[3] = ((x[2] >> 26) | (x[3] << 6)) & 0x3fffffff; + out[4] = ((x[3] >> 24) | (x[4] << 8)) & 0x3fffffff; + out[5] = ((x[4] >> 22) | (x[5] << 10)) & 0x3fffffff; + out[6] = ((x[5] >> 20) | (x[6] << 12)) & 0x3fffffff; + out[7] = ((x[6] >> 18) | (x[7] << 14)) & 0x3fffffff; + out[8] = ((x[7] >> 16) | (x[8] << 16)) & 0x00ffffff; + + /* 8*31 = 248 bits, no need to reduce */ + if(len < 32) return; + + /* q1 = x >> 248 = 264 bits = 9 30 bit elements */ + q1[0] = ((x[7] >> 24) | (x[8] << 8)) & 0x3fffffff; + q1[1] = ((x[8] >> 22) | (x[9] << 10)) & 0x3fffffff; + q1[2] = ((x[9] >> 20) | (x[10] << 12)) & 0x3fffffff; + q1[3] = ((x[10] >> 18) | (x[11] << 14)) & 0x3fffffff; + q1[4] = ((x[11] >> 16) | (x[12] << 16)) & 0x3fffffff; + q1[5] = ((x[12] >> 14) | (x[13] << 18)) & 0x3fffffff; + q1[6] = ((x[13] >> 12) | (x[14] << 20)) & 0x3fffffff; + q1[7] = ((x[14] >> 10) | (x[15] << 22)) & 0x3fffffff; + q1[8] = ((x[15] >> 8)); + + barrett_reduce256_modm(out, q1, out); +} + +void expand_raw256_modm(bignum256modm out, const unsigned char in[32]) { + bignum256modm_element_t x[8] = {0}; + + x[0] = U8TO32_LE(in + 0); + x[1] = U8TO32_LE(in + 4); + x[2] = U8TO32_LE(in + 8); + x[3] = U8TO32_LE(in + 12); + x[4] = U8TO32_LE(in + 16); + x[5] = U8TO32_LE(in + 20); + x[6] = U8TO32_LE(in + 24); + x[7] = U8TO32_LE(in + 28); + + out[0] = (x[0]) & 0x3fffffff; + out[1] = ((x[0] >> 30) | (x[1] << 2)) & 0x3fffffff; + out[2] = ((x[1] >> 28) | (x[2] << 4)) & 0x3fffffff; + out[3] = ((x[2] >> 26) | (x[3] << 6)) & 0x3fffffff; + out[4] = ((x[3] >> 24) | (x[4] << 8)) & 0x3fffffff; + out[5] = ((x[4] >> 22) | (x[5] << 10)) & 0x3fffffff; + out[6] = ((x[5] >> 20) | (x[6] << 12)) & 0x3fffffff; + out[7] = ((x[6] >> 18) | (x[7] << 14)) & 0x3fffffff; + out[8] = ((x[7] >> 16)) & 0x0000ffff; +} + +int is_reduced256_modm(const bignum256modm in) { + int i = 0; + uint32_t res1 = 0; + uint32_t res2 = 0; + for(i = 8; i >= 0; i--) { + res1 = (res1 << 1) | (in[i] < modm_m[i]); + res2 = (res2 << 1) | (in[i] > modm_m[i]); + } + return res1 > res2; +} + +void contract256_modm(unsigned char out[32], const bignum256modm in) { + U32TO8_LE(out + 0, (in[0]) | (in[1] << 30)); + U32TO8_LE(out + 4, (in[1] >> 2) | (in[2] << 28)); + U32TO8_LE(out + 8, (in[2] >> 4) | (in[3] << 26)); + U32TO8_LE(out + 12, (in[3] >> 6) | (in[4] << 24)); + U32TO8_LE(out + 16, (in[4] >> 8) | (in[5] << 22)); + U32TO8_LE(out + 20, (in[5] >> 10) | (in[6] << 20)); + U32TO8_LE(out + 24, (in[6] >> 12) | (in[7] << 18)); + U32TO8_LE(out + 28, (in[7] >> 14) | (in[8] << 16)); +} + +void contract256_window4_modm(signed char r[64], const bignum256modm in) { + char carry = 0; + signed char* quads = r; + bignum256modm_element_t i = 0, j = 0, v = 0; + + for(i = 0; i < 8; i += 2) { + v = in[i]; + for(j = 0; j < 7; j++) { + *quads++ = (v & 15); + v >>= 4; + } + v |= (in[i + 1] << 2); + for(j = 0; j < 8; j++) { + *quads++ = (v & 15); + v >>= 4; + } + } + v = in[8]; + *quads++ = (v & 15); + v >>= 4; + *quads++ = (v & 15); + v >>= 4; + *quads++ = (v & 15); + v >>= 4; + *quads++ = (v & 15); + v >>= 4; + + /* making it signed */ + carry = 0; + for(i = 0; i < 63; i++) { + r[i] += carry; + r[i + 1] += (r[i] >> 4); + r[i] &= 15; + carry = (r[i] >> 3); + r[i] -= (carry << 4); + } + r[63] += carry; +} + +void contract256_slidingwindow_modm(signed char r[256], const bignum256modm s, int windowsize) { + int i = 0, j = 0, k = 0, b = 0; + int m = (1 << (windowsize - 1)) - 1, soplen = 256; + signed char* bits = r; + bignum256modm_element_t v = 0; + + /* first put the binary expansion into r */ + for(i = 0; i < 8; i++) { + v = s[i]; + for(j = 0; j < 30; j++, v >>= 1) *bits++ = (v & 1); + } + v = s[8]; + for(j = 0; j < 16; j++, v >>= 1) *bits++ = (v & 1); + + /* Making it sliding window */ + for(j = 0; j < soplen; j++) { + if(!r[j]) continue; + + for(b = 1; (b < (soplen - j)) && (b <= 6); b++) { + if((r[j] + (r[j + b] << b)) <= m) { + r[j] += r[j + b] << b; + r[j + b] = 0; + } else if((r[j] - (r[j + b] << b)) >= -m) { + r[j] -= r[j + b] << b; + for(k = j + b; k < soplen; k++) { + if(!r[k]) { + r[k] = 1; + break; + } + r[k] = 0; + } + } else if(r[j + b]) { + break; + } + } + } +} + +void set256_modm(bignum256modm r, uint64_t v) { + r[0] = (bignum256modm_element_t)(v & 0x3fffffff); + v >>= 30; + r[1] = (bignum256modm_element_t)(v & 0x3fffffff); + v >>= 30; + r[2] = (bignum256modm_element_t)(v & 0x3fffffff); + r[3] = 0; + r[4] = 0; + r[5] = 0; + r[6] = 0; + r[7] = 0; + r[8] = 0; +} + +int get256_modm(uint64_t* v, const bignum256modm r) { + *v = 0; + int con1 = 0; + +#define NONZ(x) ((((((int64_t)(x)) - 1) >> 32) + 1) & 1) + bignum256modm_element_t c = 0; + c = r[0]; + *v += (uint64_t)c & 0x3fffffff; + c >>= 30; // 30 + c += r[1]; + *v += ((uint64_t)c & 0x3fffffff) << 30; + c >>= 30; // 60 + c += r[2]; + *v += ((uint64_t)c & 0xf) << 60; + con1 |= NONZ(c >> 4); + c >>= 30; // 64 bits + c += r[3]; + con1 |= NONZ(c); + c >>= 30; + c += r[4]; + con1 |= NONZ(c); + c >>= 30; + c += r[5]; + con1 |= NONZ(c); + c >>= 30; + c += r[6]; + con1 |= NONZ(c); + c >>= 30; + c += r[7]; + con1 |= NONZ(c); + c >>= 30; + c += r[8]; + con1 |= NONZ(c); + c >>= 30; + con1 |= NONZ(c); +#undef NONZ + + return con1 ^ 1; +} + +int eq256_modm(const bignum256modm x, const bignum256modm y) { + size_t differentbits = 0; + int len = bignum256modm_limb_size; + while(len--) { + differentbits |= (*x++ ^ *y++); + } + return (int)(1 & ((differentbits - 1) >> bignum256modm_bits_per_limb)); +} + +int cmp256_modm(const bignum256modm x, const bignum256modm y) { + int len = 2 * bignum256modm_limb_size; + uint32_t a_gt = 0; + uint32_t b_gt = 0; + + // 16B chunks + while(len--) { + const uint32_t ln = (const uint32_t)len; + const uint32_t a = (x[ln >> 1] >> 16 * (ln & 1)) & 0xffff; + const uint32_t b = (y[ln >> 1] >> 16 * (ln & 1)) & 0xffff; + + const uint32_t limb_a_gt = ((b - a) >> 16) & 1; + const uint32_t limb_b_gt = ((a - b) >> 16) & 1; + a_gt |= limb_a_gt & ~b_gt; + b_gt |= limb_b_gt & ~a_gt; + } + + return a_gt - b_gt; +} + +int iszero256_modm(const bignum256modm x) { + size_t differentbits = 0; + int len = bignum256modm_limb_size; + while(len--) { + differentbits |= (*x++); + } + return (int)(1 & ((differentbits - 1) >> bignum256modm_bits_per_limb)); +} + +void copy256_modm(bignum256modm r, const bignum256modm x) { + r[0] = x[0]; + r[1] = x[1]; + r[2] = x[2]; + r[3] = x[3]; + r[4] = x[4]; + r[5] = x[5]; + r[6] = x[6]; + r[7] = x[7]; + r[8] = x[8]; +} + +int check256_modm(const bignum256modm x) { + int ok = 1; + bignum256modm t = {0}, z = {0}; + + ok &= iszero256_modm(x) ^ 1; + barrett_reduce256_modm(t, z, x); + ok &= eq256_modm(t, x); + return ok; +} + +void mulsub256_modm( + bignum256modm r, + const bignum256modm a, + const bignum256modm b, + const bignum256modm c) { + //(cc - aa * bb) % l + bignum256modm t = {0}; + mul256_modm(t, a, b); + sub256_modm(r, c, t); +} + +void muladd256_modm( + bignum256modm r, + const bignum256modm a, + const bignum256modm b, + const bignum256modm c) { + //(cc + aa * bb) % l + bignum256modm t = {0}; + mul256_modm(t, a, b); + add256_modm(r, c, t); +} diff --git a/applications/external/flipbip/lib/crypto/ed25519_donna/modm_donna_32bit.h b/applications/external/flipbip/lib/crypto/ed25519_donna/modm_donna_32bit.h new file mode 100644 index 0000000000..021edc19eb --- /dev/null +++ b/applications/external/flipbip/lib/crypto/ed25519_donna/modm_donna_32bit.h @@ -0,0 +1,87 @@ +/* + Public domain by Andrew M. +*/ + +/* + Arithmetic modulo the group order n = 2^252 + 27742317777372353535851937790883648493 = 7237005577332262213973186563042994240857116359379907606001950938285454250989 + + k = 32 + b = 1 << 8 = 256 + m = 2^252 + 27742317777372353535851937790883648493 = 0x1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3ed + mu = floor( b^(k*2) / m ) = 0xfffffffffffffffffffffffffffffffeb2106215d086329a7ed9ce5a30a2c131b +*/ + +#define bignum256modm_bits_per_limb 30 +#define bignum256modm_limb_size 9 + +typedef uint32_t bignum256modm_element_t; +typedef bignum256modm_element_t bignum256modm[9]; + +/* see HAC, Alg. 14.42 Step 4 */ +void reduce256_modm(bignum256modm r); + +/* + Barrett reduction, see HAC, Alg. 14.42 + + Instead of passing in x, pre-process in to q1 and r1 for efficiency +*/ +void barrett_reduce256_modm(bignum256modm r, const bignum256modm q1, const bignum256modm r1); + +/* addition modulo m */ +void add256_modm(bignum256modm r, const bignum256modm x, const bignum256modm y); + +/* -x modulo m */ +void neg256_modm(bignum256modm r, const bignum256modm x); + +/* subtraction x-y modulo m */ +void sub256_modm(bignum256modm r, const bignum256modm x, const bignum256modm y); + +/* multiplication modulo m */ +void mul256_modm(bignum256modm r, const bignum256modm x, const bignum256modm y); + +void expand256_modm(bignum256modm out, const unsigned char* in, size_t len); + +void expand_raw256_modm(bignum256modm out, const unsigned char in[32]); + +int is_reduced256_modm(const bignum256modm in); + +void contract256_modm(unsigned char out[32], const bignum256modm in); + +void contract256_window4_modm(signed char r[64], const bignum256modm in); + +void contract256_slidingwindow_modm(signed char r[256], const bignum256modm s, int windowsize); + +/* 64bit uint to scalar value */ +void set256_modm(bignum256modm r, uint64_t v); + +/* scalar value to 64bit uint */ +int get256_modm(uint64_t* v, const bignum256modm r); + +/* equality test on two reduced scalar values */ +int eq256_modm(const bignum256modm x, const bignum256modm y); + +/* comparison of two reduced scalar values */ +int cmp256_modm(const bignum256modm x, const bignum256modm y); + +/* scalar null check, has to be reduced */ +int iszero256_modm(const bignum256modm x); + +/* simple copy, no reduction */ +void copy256_modm(bignum256modm r, const bignum256modm x); + +/* check if nonzero && same after reduction */ +int check256_modm(const bignum256modm x); + +/* (cc - aa * bb) % l */ +void mulsub256_modm( + bignum256modm r, + const bignum256modm a, + const bignum256modm b, + const bignum256modm c); + +/* (cc + aa * bb) % l */ +void muladd256_modm( + bignum256modm r, + const bignum256modm a, + const bignum256modm b, + const bignum256modm c); diff --git a/applications/external/flipbip/lib/crypto/groestl.c b/applications/external/flipbip/lib/crypto/groestl.c new file mode 100644 index 0000000000..a77da80f4b --- /dev/null +++ b/applications/external/flipbip/lib/crypto/groestl.c @@ -0,0 +1,755 @@ +/* Groestl hash from https://github.com/Groestlcoin/vanitygen + * Trezor adaptation by Yura Pakhuchiy . */ +/* + * Groestl implementation. + * + * ==========================(LICENSE BEGIN)============================ + * + * Copyright (c) 2007-2010 Projet RNRT SAPHIR + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * ===========================(LICENSE END)============================= + * + * @author Thomas Pornin + */ + +#include +#include + +#include "groestl_internal.h" +#include "groestl.h" +#include "memzero.h" + +#define C32e(x) \ + ((SPH_C32(x) >> 24) | ((SPH_C32(x) >> 8) & SPH_C32(0x0000FF00)) | \ + ((SPH_C32(x) << 8) & SPH_C32(0x00FF0000)) | ((SPH_C32(x) << 24) & SPH_C32(0xFF000000))) +#define dec32e_aligned sph_dec32le_aligned +#define enc32e sph_enc32le +#define B32_0(x) ((x)&0xFF) +#define B32_1(x) (((x) >> 8) & 0xFF) +#define B32_2(x) (((x) >> 16) & 0xFF) +#define B32_3(x) ((x) >> 24) + +#define R32u(u, d) SPH_T32(((u) << 16) | ((d) >> 16)) +#define R32d(u, d) SPH_T32(((u) >> 16) | ((d) << 16)) + +#define PC32up(j, r) ((sph_u32)((j) + (r))) +#define PC32dn(j, r) 0 +#define QC32up(j, r) SPH_C32(0xFFFFFFFF) +#define QC32dn(j, r) (((sph_u32)(r) << 24) ^ SPH_T32(~((sph_u32)(j) << 24))) + +#define C64e(x) \ + ((SPH_C64(x) >> 56) | ((SPH_C64(x) >> 40) & SPH_C64(0x000000000000FF00)) | \ + ((SPH_C64(x) >> 24) & SPH_C64(0x0000000000FF0000)) | \ + ((SPH_C64(x) >> 8) & SPH_C64(0x00000000FF000000)) | \ + ((SPH_C64(x) << 8) & SPH_C64(0x000000FF00000000)) | \ + ((SPH_C64(x) << 24) & SPH_C64(0x0000FF0000000000)) | \ + ((SPH_C64(x) << 40) & SPH_C64(0x00FF000000000000)) | \ + ((SPH_C64(x) << 56) & SPH_C64(0xFF00000000000000))) +#define dec64e_aligned sph_dec64le_aligned +#define enc64e sph_enc64le +#define B64_0(x) ((x)&0xFF) +#define B64_1(x) (((x) >> 8) & 0xFF) +#define B64_2(x) (((x) >> 16) & 0xFF) +#define B64_3(x) (((x) >> 24) & 0xFF) +#define B64_4(x) (((x) >> 32) & 0xFF) +#define B64_5(x) (((x) >> 40) & 0xFF) +#define B64_6(x) (((x) >> 48) & 0xFF) +#define B64_7(x) ((x) >> 56) +#define R64 SPH_ROTL64 +#define PC64(j, r) ((sph_u64)((j) + (r))) +#define QC64(j, r) (((sph_u64)(r) << 56) ^ SPH_T64(~((sph_u64)(j) << 56))) + +static const sph_u32 T0up[] = { + C32e(0xc632f4a5), C32e(0xf86f9784), C32e(0xee5eb099), C32e(0xf67a8c8d), C32e(0xffe8170d), + C32e(0xd60adcbd), C32e(0xde16c8b1), C32e(0x916dfc54), C32e(0x6090f050), C32e(0x02070503), + C32e(0xce2ee0a9), C32e(0x56d1877d), C32e(0xe7cc2b19), C32e(0xb513a662), C32e(0x4d7c31e6), + C32e(0xec59b59a), C32e(0x8f40cf45), C32e(0x1fa3bc9d), C32e(0x8949c040), C32e(0xfa689287), + C32e(0xefd03f15), C32e(0xb29426eb), C32e(0x8ece40c9), C32e(0xfbe61d0b), C32e(0x416e2fec), + C32e(0xb31aa967), C32e(0x5f431cfd), C32e(0x456025ea), C32e(0x23f9dabf), C32e(0x535102f7), + C32e(0xe445a196), C32e(0x9b76ed5b), C32e(0x75285dc2), C32e(0xe1c5241c), C32e(0x3dd4e9ae), + C32e(0x4cf2be6a), C32e(0x6c82ee5a), C32e(0x7ebdc341), C32e(0xf5f30602), C32e(0x8352d14f), + C32e(0x688ce45c), C32e(0x515607f4), C32e(0xd18d5c34), C32e(0xf9e11808), C32e(0xe24cae93), + C32e(0xab3e9573), C32e(0x6297f553), C32e(0x2a6b413f), C32e(0x081c140c), C32e(0x9563f652), + C32e(0x46e9af65), C32e(0x9d7fe25e), C32e(0x30487828), C32e(0x37cff8a1), C32e(0x0a1b110f), + C32e(0x2febc4b5), C32e(0x0e151b09), C32e(0x247e5a36), C32e(0x1badb69b), C32e(0xdf98473d), + C32e(0xcda76a26), C32e(0x4ef5bb69), C32e(0x7f334ccd), C32e(0xea50ba9f), C32e(0x123f2d1b), + C32e(0x1da4b99e), C32e(0x58c49c74), C32e(0x3446722e), C32e(0x3641772d), C32e(0xdc11cdb2), + C32e(0xb49d29ee), C32e(0x5b4d16fb), C32e(0xa4a501f6), C32e(0x76a1d74d), C32e(0xb714a361), + C32e(0x7d3449ce), C32e(0x52df8d7b), C32e(0xdd9f423e), C32e(0x5ecd9371), C32e(0x13b1a297), + C32e(0xa6a204f5), C32e(0xb901b868), C32e(0x00000000), C32e(0xc1b5742c), C32e(0x40e0a060), + C32e(0xe3c2211f), C32e(0x793a43c8), C32e(0xb69a2ced), C32e(0xd40dd9be), C32e(0x8d47ca46), + C32e(0x671770d9), C32e(0x72afdd4b), C32e(0x94ed79de), C32e(0x98ff67d4), C32e(0xb09323e8), + C32e(0x855bde4a), C32e(0xbb06bd6b), C32e(0xc5bb7e2a), C32e(0x4f7b34e5), C32e(0xedd73a16), + C32e(0x86d254c5), C32e(0x9af862d7), C32e(0x6699ff55), C32e(0x11b6a794), C32e(0x8ac04acf), + C32e(0xe9d93010), C32e(0x040e0a06), C32e(0xfe669881), C32e(0xa0ab0bf0), C32e(0x78b4cc44), + C32e(0x25f0d5ba), C32e(0x4b753ee3), C32e(0xa2ac0ef3), C32e(0x5d4419fe), C32e(0x80db5bc0), + C32e(0x0580858a), C32e(0x3fd3ecad), C32e(0x21fedfbc), C32e(0x70a8d848), C32e(0xf1fd0c04), + C32e(0x63197adf), C32e(0x772f58c1), C32e(0xaf309f75), C32e(0x42e7a563), C32e(0x20705030), + C32e(0xe5cb2e1a), C32e(0xfdef120e), C32e(0xbf08b76d), C32e(0x8155d44c), C32e(0x18243c14), + C32e(0x26795f35), C32e(0xc3b2712f), C32e(0xbe8638e1), C32e(0x35c8fda2), C32e(0x88c74fcc), + C32e(0x2e654b39), C32e(0x936af957), C32e(0x55580df2), C32e(0xfc619d82), C32e(0x7ab3c947), + C32e(0xc827efac), C32e(0xba8832e7), C32e(0x324f7d2b), C32e(0xe642a495), C32e(0xc03bfba0), + C32e(0x19aab398), C32e(0x9ef668d1), C32e(0xa322817f), C32e(0x44eeaa66), C32e(0x54d6827e), + C32e(0x3bdde6ab), C32e(0x0b959e83), C32e(0x8cc945ca), C32e(0xc7bc7b29), C32e(0x6b056ed3), + C32e(0x286c443c), C32e(0xa72c8b79), C32e(0xbc813de2), C32e(0x1631271d), C32e(0xad379a76), + C32e(0xdb964d3b), C32e(0x649efa56), C32e(0x74a6d24e), C32e(0x1436221e), C32e(0x92e476db), + C32e(0x0c121e0a), C32e(0x48fcb46c), C32e(0xb88f37e4), C32e(0x9f78e75d), C32e(0xbd0fb26e), + C32e(0x43692aef), C32e(0xc435f1a6), C32e(0x39dae3a8), C32e(0x31c6f7a4), C32e(0xd38a5937), + C32e(0xf274868b), C32e(0xd5835632), C32e(0x8b4ec543), C32e(0x6e85eb59), C32e(0xda18c2b7), + C32e(0x018e8f8c), C32e(0xb11dac64), C32e(0x9cf16dd2), C32e(0x49723be0), C32e(0xd81fc7b4), + C32e(0xacb915fa), C32e(0xf3fa0907), C32e(0xcfa06f25), C32e(0xca20eaaf), C32e(0xf47d898e), + C32e(0x476720e9), C32e(0x10382818), C32e(0x6f0b64d5), C32e(0xf0738388), C32e(0x4afbb16f), + C32e(0x5cca9672), C32e(0x38546c24), C32e(0x575f08f1), C32e(0x732152c7), C32e(0x9764f351), + C32e(0xcbae6523), C32e(0xa125847c), C32e(0xe857bf9c), C32e(0x3e5d6321), C32e(0x96ea7cdd), + C32e(0x611e7fdc), C32e(0x0d9c9186), C32e(0x0f9b9485), C32e(0xe04bab90), C32e(0x7cbac642), + C32e(0x712657c4), C32e(0xcc29e5aa), C32e(0x90e373d8), C32e(0x06090f05), C32e(0xf7f40301), + C32e(0x1c2a3612), C32e(0xc23cfea3), C32e(0x6a8be15f), C32e(0xaebe10f9), C32e(0x69026bd0), + C32e(0x17bfa891), C32e(0x9971e858), C32e(0x3a536927), C32e(0x27f7d0b9), C32e(0xd9914838), + C32e(0xebde3513), C32e(0x2be5ceb3), C32e(0x22775533), C32e(0xd204d6bb), C32e(0xa9399070), + C32e(0x07878089), C32e(0x33c1f2a7), C32e(0x2decc1b6), C32e(0x3c5a6622), C32e(0x15b8ad92), + C32e(0xc9a96020), C32e(0x875cdb49), C32e(0xaab01aff), C32e(0x50d88878), C32e(0xa52b8e7a), + C32e(0x03898a8f), C32e(0x594a13f8), C32e(0x09929b80), C32e(0x1a233917), C32e(0x651075da), + C32e(0xd7845331), C32e(0x84d551c6), C32e(0xd003d3b8), C32e(0x82dc5ec3), C32e(0x29e2cbb0), + C32e(0x5ac39977), C32e(0x1e2d3311), C32e(0x7b3d46cb), C32e(0xa8b71ffc), C32e(0x6d0c61d6), + C32e(0x2c624e3a)}; + +static const sph_u32 T0dn[] = { + C32e(0xf497a5c6), C32e(0x97eb84f8), C32e(0xb0c799ee), C32e(0x8cf78df6), C32e(0x17e50dff), + C32e(0xdcb7bdd6), C32e(0xc8a7b1de), C32e(0xfc395491), C32e(0xf0c05060), C32e(0x05040302), + C32e(0xe087a9ce), C32e(0x87ac7d56), C32e(0x2bd519e7), C32e(0xa67162b5), C32e(0x319ae64d), + C32e(0xb5c39aec), C32e(0xcf05458f), C32e(0xbc3e9d1f), C32e(0xc0094089), C32e(0x92ef87fa), + C32e(0x3fc515ef), C32e(0x267febb2), C32e(0x4007c98e), C32e(0x1ded0bfb), C32e(0x2f82ec41), + C32e(0xa97d67b3), C32e(0x1cbefd5f), C32e(0x258aea45), C32e(0xda46bf23), C32e(0x02a6f753), + C32e(0xa1d396e4), C32e(0xed2d5b9b), C32e(0x5deac275), C32e(0x24d91ce1), C32e(0xe97aae3d), + C32e(0xbe986a4c), C32e(0xeed85a6c), C32e(0xc3fc417e), C32e(0x06f102f5), C32e(0xd11d4f83), + C32e(0xe4d05c68), C32e(0x07a2f451), C32e(0x5cb934d1), C32e(0x18e908f9), C32e(0xaedf93e2), + C32e(0x954d73ab), C32e(0xf5c45362), C32e(0x41543f2a), C32e(0x14100c08), C32e(0xf6315295), + C32e(0xaf8c6546), C32e(0xe2215e9d), C32e(0x78602830), C32e(0xf86ea137), C32e(0x11140f0a), + C32e(0xc45eb52f), C32e(0x1b1c090e), C32e(0x5a483624), C32e(0xb6369b1b), C32e(0x47a53ddf), + C32e(0x6a8126cd), C32e(0xbb9c694e), C32e(0x4cfecd7f), C32e(0xbacf9fea), C32e(0x2d241b12), + C32e(0xb93a9e1d), C32e(0x9cb07458), C32e(0x72682e34), C32e(0x776c2d36), C32e(0xcda3b2dc), + C32e(0x2973eeb4), C32e(0x16b6fb5b), C32e(0x0153f6a4), C32e(0xd7ec4d76), C32e(0xa37561b7), + C32e(0x49face7d), C32e(0x8da47b52), C32e(0x42a13edd), C32e(0x93bc715e), C32e(0xa2269713), + C32e(0x0457f5a6), C32e(0xb86968b9), C32e(0x00000000), C32e(0x74992cc1), C32e(0xa0806040), + C32e(0x21dd1fe3), C32e(0x43f2c879), C32e(0x2c77edb6), C32e(0xd9b3bed4), C32e(0xca01468d), + C32e(0x70ced967), C32e(0xdde44b72), C32e(0x7933de94), C32e(0x672bd498), C32e(0x237be8b0), + C32e(0xde114a85), C32e(0xbd6d6bbb), C32e(0x7e912ac5), C32e(0x349ee54f), C32e(0x3ac116ed), + C32e(0x5417c586), C32e(0x622fd79a), C32e(0xffcc5566), C32e(0xa7229411), C32e(0x4a0fcf8a), + C32e(0x30c910e9), C32e(0x0a080604), C32e(0x98e781fe), C32e(0x0b5bf0a0), C32e(0xccf04478), + C32e(0xd54aba25), C32e(0x3e96e34b), C32e(0x0e5ff3a2), C32e(0x19bafe5d), C32e(0x5b1bc080), + C32e(0x850a8a05), C32e(0xec7ead3f), C32e(0xdf42bc21), C32e(0xd8e04870), C32e(0x0cf904f1), + C32e(0x7ac6df63), C32e(0x58eec177), C32e(0x9f4575af), C32e(0xa5846342), C32e(0x50403020), + C32e(0x2ed11ae5), C32e(0x12e10efd), C32e(0xb7656dbf), C32e(0xd4194c81), C32e(0x3c301418), + C32e(0x5f4c3526), C32e(0x719d2fc3), C32e(0x3867e1be), C32e(0xfd6aa235), C32e(0x4f0bcc88), + C32e(0x4b5c392e), C32e(0xf93d5793), C32e(0x0daaf255), C32e(0x9de382fc), C32e(0xc9f4477a), + C32e(0xef8bacc8), C32e(0x326fe7ba), C32e(0x7d642b32), C32e(0xa4d795e6), C32e(0xfb9ba0c0), + C32e(0xb3329819), C32e(0x6827d19e), C32e(0x815d7fa3), C32e(0xaa886644), C32e(0x82a87e54), + C32e(0xe676ab3b), C32e(0x9e16830b), C32e(0x4503ca8c), C32e(0x7b9529c7), C32e(0x6ed6d36b), + C32e(0x44503c28), C32e(0x8b5579a7), C32e(0x3d63e2bc), C32e(0x272c1d16), C32e(0x9a4176ad), + C32e(0x4dad3bdb), C32e(0xfac85664), C32e(0xd2e84e74), C32e(0x22281e14), C32e(0x763fdb92), + C32e(0x1e180a0c), C32e(0xb4906c48), C32e(0x376be4b8), C32e(0xe7255d9f), C32e(0xb2616ebd), + C32e(0x2a86ef43), C32e(0xf193a6c4), C32e(0xe372a839), C32e(0xf762a431), C32e(0x59bd37d3), + C32e(0x86ff8bf2), C32e(0x56b132d5), C32e(0xc50d438b), C32e(0xebdc596e), C32e(0xc2afb7da), + C32e(0x8f028c01), C32e(0xac7964b1), C32e(0x6d23d29c), C32e(0x3b92e049), C32e(0xc7abb4d8), + C32e(0x1543faac), C32e(0x09fd07f3), C32e(0x6f8525cf), C32e(0xea8fafca), C32e(0x89f38ef4), + C32e(0x208ee947), C32e(0x28201810), C32e(0x64ded56f), C32e(0x83fb88f0), C32e(0xb1946f4a), + C32e(0x96b8725c), C32e(0x6c702438), C32e(0x08aef157), C32e(0x52e6c773), C32e(0xf3355197), + C32e(0x658d23cb), C32e(0x84597ca1), C32e(0xbfcb9ce8), C32e(0x637c213e), C32e(0x7c37dd96), + C32e(0x7fc2dc61), C32e(0x911a860d), C32e(0x941e850f), C32e(0xabdb90e0), C32e(0xc6f8427c), + C32e(0x57e2c471), C32e(0xe583aacc), C32e(0x733bd890), C32e(0x0f0c0506), C32e(0x03f501f7), + C32e(0x3638121c), C32e(0xfe9fa3c2), C32e(0xe1d45f6a), C32e(0x1047f9ae), C32e(0x6bd2d069), + C32e(0xa82e9117), C32e(0xe8295899), C32e(0x6974273a), C32e(0xd04eb927), C32e(0x48a938d9), + C32e(0x35cd13eb), C32e(0xce56b32b), C32e(0x55443322), C32e(0xd6bfbbd2), C32e(0x904970a9), + C32e(0x800e8907), C32e(0xf266a733), C32e(0xc15ab62d), C32e(0x6678223c), C32e(0xad2a9215), + C32e(0x608920c9), C32e(0xdb154987), C32e(0x1a4fffaa), C32e(0x88a07850), C32e(0x8e517aa5), + C32e(0x8a068f03), C32e(0x13b2f859), C32e(0x9b128009), C32e(0x3934171a), C32e(0x75cada65), + C32e(0x53b531d7), C32e(0x5113c684), C32e(0xd3bbb8d0), C32e(0x5e1fc382), C32e(0xcb52b029), + C32e(0x99b4775a), C32e(0x333c111e), C32e(0x46f6cb7b), C32e(0x1f4bfca8), C32e(0x61dad66d), + C32e(0x4e583a2c)}; + +static const sph_u32 T1up[] = { + C32e(0xc6c632f4), C32e(0xf8f86f97), C32e(0xeeee5eb0), C32e(0xf6f67a8c), C32e(0xffffe817), + C32e(0xd6d60adc), C32e(0xdede16c8), C32e(0x91916dfc), C32e(0x606090f0), C32e(0x02020705), + C32e(0xcece2ee0), C32e(0x5656d187), C32e(0xe7e7cc2b), C32e(0xb5b513a6), C32e(0x4d4d7c31), + C32e(0xecec59b5), C32e(0x8f8f40cf), C32e(0x1f1fa3bc), C32e(0x898949c0), C32e(0xfafa6892), + C32e(0xefefd03f), C32e(0xb2b29426), C32e(0x8e8ece40), C32e(0xfbfbe61d), C32e(0x41416e2f), + C32e(0xb3b31aa9), C32e(0x5f5f431c), C32e(0x45456025), C32e(0x2323f9da), C32e(0x53535102), + C32e(0xe4e445a1), C32e(0x9b9b76ed), C32e(0x7575285d), C32e(0xe1e1c524), C32e(0x3d3dd4e9), + C32e(0x4c4cf2be), C32e(0x6c6c82ee), C32e(0x7e7ebdc3), C32e(0xf5f5f306), C32e(0x838352d1), + C32e(0x68688ce4), C32e(0x51515607), C32e(0xd1d18d5c), C32e(0xf9f9e118), C32e(0xe2e24cae), + C32e(0xabab3e95), C32e(0x626297f5), C32e(0x2a2a6b41), C32e(0x08081c14), C32e(0x959563f6), + C32e(0x4646e9af), C32e(0x9d9d7fe2), C32e(0x30304878), C32e(0x3737cff8), C32e(0x0a0a1b11), + C32e(0x2f2febc4), C32e(0x0e0e151b), C32e(0x24247e5a), C32e(0x1b1badb6), C32e(0xdfdf9847), + C32e(0xcdcda76a), C32e(0x4e4ef5bb), C32e(0x7f7f334c), C32e(0xeaea50ba), C32e(0x12123f2d), + C32e(0x1d1da4b9), C32e(0x5858c49c), C32e(0x34344672), C32e(0x36364177), C32e(0xdcdc11cd), + C32e(0xb4b49d29), C32e(0x5b5b4d16), C32e(0xa4a4a501), C32e(0x7676a1d7), C32e(0xb7b714a3), + C32e(0x7d7d3449), C32e(0x5252df8d), C32e(0xdddd9f42), C32e(0x5e5ecd93), C32e(0x1313b1a2), + C32e(0xa6a6a204), C32e(0xb9b901b8), C32e(0x00000000), C32e(0xc1c1b574), C32e(0x4040e0a0), + C32e(0xe3e3c221), C32e(0x79793a43), C32e(0xb6b69a2c), C32e(0xd4d40dd9), C32e(0x8d8d47ca), + C32e(0x67671770), C32e(0x7272afdd), C32e(0x9494ed79), C32e(0x9898ff67), C32e(0xb0b09323), + C32e(0x85855bde), C32e(0xbbbb06bd), C32e(0xc5c5bb7e), C32e(0x4f4f7b34), C32e(0xededd73a), + C32e(0x8686d254), C32e(0x9a9af862), C32e(0x666699ff), C32e(0x1111b6a7), C32e(0x8a8ac04a), + C32e(0xe9e9d930), C32e(0x04040e0a), C32e(0xfefe6698), C32e(0xa0a0ab0b), C32e(0x7878b4cc), + C32e(0x2525f0d5), C32e(0x4b4b753e), C32e(0xa2a2ac0e), C32e(0x5d5d4419), C32e(0x8080db5b), + C32e(0x05058085), C32e(0x3f3fd3ec), C32e(0x2121fedf), C32e(0x7070a8d8), C32e(0xf1f1fd0c), + C32e(0x6363197a), C32e(0x77772f58), C32e(0xafaf309f), C32e(0x4242e7a5), C32e(0x20207050), + C32e(0xe5e5cb2e), C32e(0xfdfdef12), C32e(0xbfbf08b7), C32e(0x818155d4), C32e(0x1818243c), + C32e(0x2626795f), C32e(0xc3c3b271), C32e(0xbebe8638), C32e(0x3535c8fd), C32e(0x8888c74f), + C32e(0x2e2e654b), C32e(0x93936af9), C32e(0x5555580d), C32e(0xfcfc619d), C32e(0x7a7ab3c9), + C32e(0xc8c827ef), C32e(0xbaba8832), C32e(0x32324f7d), C32e(0xe6e642a4), C32e(0xc0c03bfb), + C32e(0x1919aab3), C32e(0x9e9ef668), C32e(0xa3a32281), C32e(0x4444eeaa), C32e(0x5454d682), + C32e(0x3b3bdde6), C32e(0x0b0b959e), C32e(0x8c8cc945), C32e(0xc7c7bc7b), C32e(0x6b6b056e), + C32e(0x28286c44), C32e(0xa7a72c8b), C32e(0xbcbc813d), C32e(0x16163127), C32e(0xadad379a), + C32e(0xdbdb964d), C32e(0x64649efa), C32e(0x7474a6d2), C32e(0x14143622), C32e(0x9292e476), + C32e(0x0c0c121e), C32e(0x4848fcb4), C32e(0xb8b88f37), C32e(0x9f9f78e7), C32e(0xbdbd0fb2), + C32e(0x4343692a), C32e(0xc4c435f1), C32e(0x3939dae3), C32e(0x3131c6f7), C32e(0xd3d38a59), + C32e(0xf2f27486), C32e(0xd5d58356), C32e(0x8b8b4ec5), C32e(0x6e6e85eb), C32e(0xdada18c2), + C32e(0x01018e8f), C32e(0xb1b11dac), C32e(0x9c9cf16d), C32e(0x4949723b), C32e(0xd8d81fc7), + C32e(0xacacb915), C32e(0xf3f3fa09), C32e(0xcfcfa06f), C32e(0xcaca20ea), C32e(0xf4f47d89), + C32e(0x47476720), C32e(0x10103828), C32e(0x6f6f0b64), C32e(0xf0f07383), C32e(0x4a4afbb1), + C32e(0x5c5cca96), C32e(0x3838546c), C32e(0x57575f08), C32e(0x73732152), C32e(0x979764f3), + C32e(0xcbcbae65), C32e(0xa1a12584), C32e(0xe8e857bf), C32e(0x3e3e5d63), C32e(0x9696ea7c), + C32e(0x61611e7f), C32e(0x0d0d9c91), C32e(0x0f0f9b94), C32e(0xe0e04bab), C32e(0x7c7cbac6), + C32e(0x71712657), C32e(0xcccc29e5), C32e(0x9090e373), C32e(0x0606090f), C32e(0xf7f7f403), + C32e(0x1c1c2a36), C32e(0xc2c23cfe), C32e(0x6a6a8be1), C32e(0xaeaebe10), C32e(0x6969026b), + C32e(0x1717bfa8), C32e(0x999971e8), C32e(0x3a3a5369), C32e(0x2727f7d0), C32e(0xd9d99148), + C32e(0xebebde35), C32e(0x2b2be5ce), C32e(0x22227755), C32e(0xd2d204d6), C32e(0xa9a93990), + C32e(0x07078780), C32e(0x3333c1f2), C32e(0x2d2decc1), C32e(0x3c3c5a66), C32e(0x1515b8ad), + C32e(0xc9c9a960), C32e(0x87875cdb), C32e(0xaaaab01a), C32e(0x5050d888), C32e(0xa5a52b8e), + C32e(0x0303898a), C32e(0x59594a13), C32e(0x0909929b), C32e(0x1a1a2339), C32e(0x65651075), + C32e(0xd7d78453), C32e(0x8484d551), C32e(0xd0d003d3), C32e(0x8282dc5e), C32e(0x2929e2cb), + C32e(0x5a5ac399), C32e(0x1e1e2d33), C32e(0x7b7b3d46), C32e(0xa8a8b71f), C32e(0x6d6d0c61), + C32e(0x2c2c624e)}; + +static const sph_u32 T1dn[] = { + C32e(0xa5f497a5), C32e(0x8497eb84), C32e(0x99b0c799), C32e(0x8d8cf78d), C32e(0x0d17e50d), + C32e(0xbddcb7bd), C32e(0xb1c8a7b1), C32e(0x54fc3954), C32e(0x50f0c050), C32e(0x03050403), + C32e(0xa9e087a9), C32e(0x7d87ac7d), C32e(0x192bd519), C32e(0x62a67162), C32e(0xe6319ae6), + C32e(0x9ab5c39a), C32e(0x45cf0545), C32e(0x9dbc3e9d), C32e(0x40c00940), C32e(0x8792ef87), + C32e(0x153fc515), C32e(0xeb267feb), C32e(0xc94007c9), C32e(0x0b1ded0b), C32e(0xec2f82ec), + C32e(0x67a97d67), C32e(0xfd1cbefd), C32e(0xea258aea), C32e(0xbfda46bf), C32e(0xf702a6f7), + C32e(0x96a1d396), C32e(0x5bed2d5b), C32e(0xc25deac2), C32e(0x1c24d91c), C32e(0xaee97aae), + C32e(0x6abe986a), C32e(0x5aeed85a), C32e(0x41c3fc41), C32e(0x0206f102), C32e(0x4fd11d4f), + C32e(0x5ce4d05c), C32e(0xf407a2f4), C32e(0x345cb934), C32e(0x0818e908), C32e(0x93aedf93), + C32e(0x73954d73), C32e(0x53f5c453), C32e(0x3f41543f), C32e(0x0c14100c), C32e(0x52f63152), + C32e(0x65af8c65), C32e(0x5ee2215e), C32e(0x28786028), C32e(0xa1f86ea1), C32e(0x0f11140f), + C32e(0xb5c45eb5), C32e(0x091b1c09), C32e(0x365a4836), C32e(0x9bb6369b), C32e(0x3d47a53d), + C32e(0x266a8126), C32e(0x69bb9c69), C32e(0xcd4cfecd), C32e(0x9fbacf9f), C32e(0x1b2d241b), + C32e(0x9eb93a9e), C32e(0x749cb074), C32e(0x2e72682e), C32e(0x2d776c2d), C32e(0xb2cda3b2), + C32e(0xee2973ee), C32e(0xfb16b6fb), C32e(0xf60153f6), C32e(0x4dd7ec4d), C32e(0x61a37561), + C32e(0xce49face), C32e(0x7b8da47b), C32e(0x3e42a13e), C32e(0x7193bc71), C32e(0x97a22697), + C32e(0xf50457f5), C32e(0x68b86968), C32e(0x00000000), C32e(0x2c74992c), C32e(0x60a08060), + C32e(0x1f21dd1f), C32e(0xc843f2c8), C32e(0xed2c77ed), C32e(0xbed9b3be), C32e(0x46ca0146), + C32e(0xd970ced9), C32e(0x4bdde44b), C32e(0xde7933de), C32e(0xd4672bd4), C32e(0xe8237be8), + C32e(0x4ade114a), C32e(0x6bbd6d6b), C32e(0x2a7e912a), C32e(0xe5349ee5), C32e(0x163ac116), + C32e(0xc55417c5), C32e(0xd7622fd7), C32e(0x55ffcc55), C32e(0x94a72294), C32e(0xcf4a0fcf), + C32e(0x1030c910), C32e(0x060a0806), C32e(0x8198e781), C32e(0xf00b5bf0), C32e(0x44ccf044), + C32e(0xbad54aba), C32e(0xe33e96e3), C32e(0xf30e5ff3), C32e(0xfe19bafe), C32e(0xc05b1bc0), + C32e(0x8a850a8a), C32e(0xadec7ead), C32e(0xbcdf42bc), C32e(0x48d8e048), C32e(0x040cf904), + C32e(0xdf7ac6df), C32e(0xc158eec1), C32e(0x759f4575), C32e(0x63a58463), C32e(0x30504030), + C32e(0x1a2ed11a), C32e(0x0e12e10e), C32e(0x6db7656d), C32e(0x4cd4194c), C32e(0x143c3014), + C32e(0x355f4c35), C32e(0x2f719d2f), C32e(0xe13867e1), C32e(0xa2fd6aa2), C32e(0xcc4f0bcc), + C32e(0x394b5c39), C32e(0x57f93d57), C32e(0xf20daaf2), C32e(0x829de382), C32e(0x47c9f447), + C32e(0xacef8bac), C32e(0xe7326fe7), C32e(0x2b7d642b), C32e(0x95a4d795), C32e(0xa0fb9ba0), + C32e(0x98b33298), C32e(0xd16827d1), C32e(0x7f815d7f), C32e(0x66aa8866), C32e(0x7e82a87e), + C32e(0xabe676ab), C32e(0x839e1683), C32e(0xca4503ca), C32e(0x297b9529), C32e(0xd36ed6d3), + C32e(0x3c44503c), C32e(0x798b5579), C32e(0xe23d63e2), C32e(0x1d272c1d), C32e(0x769a4176), + C32e(0x3b4dad3b), C32e(0x56fac856), C32e(0x4ed2e84e), C32e(0x1e22281e), C32e(0xdb763fdb), + C32e(0x0a1e180a), C32e(0x6cb4906c), C32e(0xe4376be4), C32e(0x5de7255d), C32e(0x6eb2616e), + C32e(0xef2a86ef), C32e(0xa6f193a6), C32e(0xa8e372a8), C32e(0xa4f762a4), C32e(0x3759bd37), + C32e(0x8b86ff8b), C32e(0x3256b132), C32e(0x43c50d43), C32e(0x59ebdc59), C32e(0xb7c2afb7), + C32e(0x8c8f028c), C32e(0x64ac7964), C32e(0xd26d23d2), C32e(0xe03b92e0), C32e(0xb4c7abb4), + C32e(0xfa1543fa), C32e(0x0709fd07), C32e(0x256f8525), C32e(0xafea8faf), C32e(0x8e89f38e), + C32e(0xe9208ee9), C32e(0x18282018), C32e(0xd564ded5), C32e(0x8883fb88), C32e(0x6fb1946f), + C32e(0x7296b872), C32e(0x246c7024), C32e(0xf108aef1), C32e(0xc752e6c7), C32e(0x51f33551), + C32e(0x23658d23), C32e(0x7c84597c), C32e(0x9cbfcb9c), C32e(0x21637c21), C32e(0xdd7c37dd), + C32e(0xdc7fc2dc), C32e(0x86911a86), C32e(0x85941e85), C32e(0x90abdb90), C32e(0x42c6f842), + C32e(0xc457e2c4), C32e(0xaae583aa), C32e(0xd8733bd8), C32e(0x050f0c05), C32e(0x0103f501), + C32e(0x12363812), C32e(0xa3fe9fa3), C32e(0x5fe1d45f), C32e(0xf91047f9), C32e(0xd06bd2d0), + C32e(0x91a82e91), C32e(0x58e82958), C32e(0x27697427), C32e(0xb9d04eb9), C32e(0x3848a938), + C32e(0x1335cd13), C32e(0xb3ce56b3), C32e(0x33554433), C32e(0xbbd6bfbb), C32e(0x70904970), + C32e(0x89800e89), C32e(0xa7f266a7), C32e(0xb6c15ab6), C32e(0x22667822), C32e(0x92ad2a92), + C32e(0x20608920), C32e(0x49db1549), C32e(0xff1a4fff), C32e(0x7888a078), C32e(0x7a8e517a), + C32e(0x8f8a068f), C32e(0xf813b2f8), C32e(0x809b1280), C32e(0x17393417), C32e(0xda75cada), + C32e(0x3153b531), C32e(0xc65113c6), C32e(0xb8d3bbb8), C32e(0xc35e1fc3), C32e(0xb0cb52b0), + C32e(0x7799b477), C32e(0x11333c11), C32e(0xcb46f6cb), C32e(0xfc1f4bfc), C32e(0xd661dad6), + C32e(0x3a4e583a)}; + +#define DECL_STATE_SMALL sph_u32 H[16] = {0}; + +#define READ_STATE_SMALL(sc) \ + do { \ + memcpy(H, (sc)->state.narrow, sizeof H); \ + } while(0) + +#define WRITE_STATE_SMALL(sc) \ + do { \ + memcpy((sc)->state.narrow, H, sizeof H); \ + } while(0) + +#define XCAT(x, y) XCAT_(x, y) +#define XCAT_(x, y) x##y + +#define RSTT(d0, d1, a, b0, b1, b2, b3, b4, b5, b6, b7) \ + do { \ + t[d0] = T0up[B32_0(a[b0])] ^ T1up[B32_1(a[b1])] ^ T2up[B32_2(a[b2])] ^ \ + T3up[B32_3(a[b3])] ^ T0dn[B32_0(a[b4])] ^ T1dn[B32_1(a[b5])] ^ \ + T2dn[B32_2(a[b6])] ^ T3dn[B32_3(a[b7])]; \ + t[d1] = T0dn[B32_0(a[b0])] ^ T1dn[B32_1(a[b1])] ^ T2dn[B32_2(a[b2])] ^ \ + T3dn[B32_3(a[b3])] ^ T0up[B32_0(a[b4])] ^ T1up[B32_1(a[b5])] ^ \ + T2up[B32_2(a[b6])] ^ T3up[B32_3(a[b7])]; \ + } while(0) + +#define ROUND_SMALL_P(a, r) \ + do { \ + sph_u32 t[16]; \ + a[0x0] ^= PC32up(0x00, r); \ + a[0x1] ^= PC32dn(0x00, r); \ + a[0x2] ^= PC32up(0x10, r); \ + a[0x3] ^= PC32dn(0x10, r); \ + a[0x4] ^= PC32up(0x20, r); \ + a[0x5] ^= PC32dn(0x20, r); \ + a[0x6] ^= PC32up(0x30, r); \ + a[0x7] ^= PC32dn(0x30, r); \ + a[0x8] ^= PC32up(0x40, r); \ + a[0x9] ^= PC32dn(0x40, r); \ + a[0xA] ^= PC32up(0x50, r); \ + a[0xB] ^= PC32dn(0x50, r); \ + a[0xC] ^= PC32up(0x60, r); \ + a[0xD] ^= PC32dn(0x60, r); \ + a[0xE] ^= PC32up(0x70, r); \ + a[0xF] ^= PC32dn(0x70, r); \ + RSTT(0x0, 0x1, a, 0x0, 0x2, 0x4, 0x6, 0x9, 0xB, 0xD, 0xF); \ + RSTT(0x2, 0x3, a, 0x2, 0x4, 0x6, 0x8, 0xB, 0xD, 0xF, 0x1); \ + RSTT(0x4, 0x5, a, 0x4, 0x6, 0x8, 0xA, 0xD, 0xF, 0x1, 0x3); \ + RSTT(0x6, 0x7, a, 0x6, 0x8, 0xA, 0xC, 0xF, 0x1, 0x3, 0x5); \ + RSTT(0x8, 0x9, a, 0x8, 0xA, 0xC, 0xE, 0x1, 0x3, 0x5, 0x7); \ + RSTT(0xA, 0xB, a, 0xA, 0xC, 0xE, 0x0, 0x3, 0x5, 0x7, 0x9); \ + RSTT(0xC, 0xD, a, 0xC, 0xE, 0x0, 0x2, 0x5, 0x7, 0x9, 0xB); \ + RSTT(0xE, 0xF, a, 0xE, 0x0, 0x2, 0x4, 0x7, 0x9, 0xB, 0xD); \ + memcpy(a, t, sizeof t); \ + } while(0) + +#define ROUND_SMALL_Q(a, r) \ + do { \ + sph_u32 t[16]; \ + a[0x0] ^= QC32up(0x00, r); \ + a[0x1] ^= QC32dn(0x00, r); \ + a[0x2] ^= QC32up(0x10, r); \ + a[0x3] ^= QC32dn(0x10, r); \ + a[0x4] ^= QC32up(0x20, r); \ + a[0x5] ^= QC32dn(0x20, r); \ + a[0x6] ^= QC32up(0x30, r); \ + a[0x7] ^= QC32dn(0x30, r); \ + a[0x8] ^= QC32up(0x40, r); \ + a[0x9] ^= QC32dn(0x40, r); \ + a[0xA] ^= QC32up(0x50, r); \ + a[0xB] ^= QC32dn(0x50, r); \ + a[0xC] ^= QC32up(0x60, r); \ + a[0xD] ^= QC32dn(0x60, r); \ + a[0xE] ^= QC32up(0x70, r); \ + a[0xF] ^= QC32dn(0x70, r); \ + RSTT(0x0, 0x1, a, 0x2, 0x6, 0xA, 0xE, 0x1, 0x5, 0x9, 0xD); \ + RSTT(0x2, 0x3, a, 0x4, 0x8, 0xC, 0x0, 0x3, 0x7, 0xB, 0xF); \ + RSTT(0x4, 0x5, a, 0x6, 0xA, 0xE, 0x2, 0x5, 0x9, 0xD, 0x1); \ + RSTT(0x6, 0x7, a, 0x8, 0xC, 0x0, 0x4, 0x7, 0xB, 0xF, 0x3); \ + RSTT(0x8, 0x9, a, 0xA, 0xE, 0x2, 0x6, 0x9, 0xD, 0x1, 0x5); \ + RSTT(0xA, 0xB, a, 0xC, 0x0, 0x4, 0x8, 0xB, 0xF, 0x3, 0x7); \ + RSTT(0xC, 0xD, a, 0xE, 0x2, 0x6, 0xA, 0xD, 0x1, 0x5, 0x9); \ + RSTT(0xE, 0xF, a, 0x0, 0x4, 0x8, 0xC, 0xF, 0x3, 0x7, 0xB); \ + memcpy(a, t, sizeof t); \ + } while(0) + +#define PERM_SMALL_P(a) \ + do { \ + int r; \ + for(r = 0; r < 10; r++) ROUND_SMALL_P(a, r); \ + } while(0) + +#define PERM_SMALL_Q(a) \ + do { \ + int r; \ + for(r = 0; r < 10; r++) ROUND_SMALL_Q(a, r); \ + } while(0) + +#define COMPRESS_SMALL \ + do { \ + sph_u32 g[16], m[16]; \ + size_t u; \ + for(u = 0; u < 16; u++) { \ + m[u] = dec32e_aligned(buf + (u << 2)); \ + g[u] = m[u] ^ H[u]; \ + } \ + PERM_SMALL_P(g); \ + PERM_SMALL_Q(m); \ + for(u = 0; u < 16; u++) H[u] ^= g[u] ^ m[u]; \ + } while(0) + +#define FINAL_SMALL \ + do { \ + sph_u32 x[16]; \ + size_t u; \ + memcpy(x, H, sizeof x); \ + PERM_SMALL_P(x); \ + for(u = 0; u < 16; u++) H[u] ^= x[u]; \ + } while(0) + +#define DECL_STATE_BIG sph_u32 H[32] = {0}; + +#define READ_STATE_BIG(sc) \ + do { \ + memcpy(H, (sc)->state.narrow, sizeof H); \ + } while(0) + +#define WRITE_STATE_BIG(sc) \ + do { \ + memcpy((sc)->state.narrow, H, sizeof H); \ + } while(0) + +#define RBTT(d0, d1, a, b0, b1, b2, b3, b4, b5, b6, b7) \ + do { \ + sph_u32 fu2 = T0up[B32_2(a[b2])]; \ + sph_u32 fd2 = T0dn[B32_2(a[b2])]; \ + sph_u32 fu3 = T1up[B32_3(a[b3])]; \ + sph_u32 fd3 = T1dn[B32_3(a[b3])]; \ + sph_u32 fu6 = T0up[B32_2(a[b6])]; \ + sph_u32 fd6 = T0dn[B32_2(a[b6])]; \ + sph_u32 fu7 = T1up[B32_3(a[b7])]; \ + sph_u32 fd7 = T1dn[B32_3(a[b7])]; \ + t[d0] = T0up[B32_0(a[b0])] ^ T1up[B32_1(a[b1])] ^ R32u(fu2, fd2) ^ R32u(fu3, fd3) ^ \ + T0dn[B32_0(a[b4])] ^ T1dn[B32_1(a[b5])] ^ R32d(fu6, fd6) ^ R32d(fu7, fd7); \ + t[d1] = T0dn[B32_0(a[b0])] ^ T1dn[B32_1(a[b1])] ^ R32d(fu2, fd2) ^ R32d(fu3, fd3) ^ \ + T0up[B32_0(a[b4])] ^ T1up[B32_1(a[b5])] ^ R32u(fu6, fd6) ^ R32u(fu7, fd7); \ + } while(0) + +#define ROUND_BIG_P(a, r) \ + do { \ + sph_u32 t[32]; \ + size_t u; \ + a[0x00] ^= PC32up(0x00, r); \ + a[0x01] ^= PC32dn(0x00, r); \ + a[0x02] ^= PC32up(0x10, r); \ + a[0x03] ^= PC32dn(0x10, r); \ + a[0x04] ^= PC32up(0x20, r); \ + a[0x05] ^= PC32dn(0x20, r); \ + a[0x06] ^= PC32up(0x30, r); \ + a[0x07] ^= PC32dn(0x30, r); \ + a[0x08] ^= PC32up(0x40, r); \ + a[0x09] ^= PC32dn(0x40, r); \ + a[0x0A] ^= PC32up(0x50, r); \ + a[0x0B] ^= PC32dn(0x50, r); \ + a[0x0C] ^= PC32up(0x60, r); \ + a[0x0D] ^= PC32dn(0x60, r); \ + a[0x0E] ^= PC32up(0x70, r); \ + a[0x0F] ^= PC32dn(0x70, r); \ + a[0x10] ^= PC32up(0x80, r); \ + a[0x11] ^= PC32dn(0x80, r); \ + a[0x12] ^= PC32up(0x90, r); \ + a[0x13] ^= PC32dn(0x90, r); \ + a[0x14] ^= PC32up(0xA0, r); \ + a[0x15] ^= PC32dn(0xA0, r); \ + a[0x16] ^= PC32up(0xB0, r); \ + a[0x17] ^= PC32dn(0xB0, r); \ + a[0x18] ^= PC32up(0xC0, r); \ + a[0x19] ^= PC32dn(0xC0, r); \ + a[0x1A] ^= PC32up(0xD0, r); \ + a[0x1B] ^= PC32dn(0xD0, r); \ + a[0x1C] ^= PC32up(0xE0, r); \ + a[0x1D] ^= PC32dn(0xE0, r); \ + a[0x1E] ^= PC32up(0xF0, r); \ + a[0x1F] ^= PC32dn(0xF0, r); \ + for(u = 0; u < 32; u += 8) { \ + RBTT( \ + u + 0x00, \ + (u + 0x01) & 0x1F, \ + a, \ + u + 0x00, \ + (u + 0x02) & 0x1F, \ + (u + 0x04) & 0x1F, \ + (u + 0x06) & 0x1F, \ + (u + 0x09) & 0x1F, \ + (u + 0x0B) & 0x1F, \ + (u + 0x0D) & 0x1F, \ + (u + 0x17) & 0x1F); \ + RBTT( \ + u + 0x02, \ + (u + 0x03) & 0x1F, \ + a, \ + u + 0x02, \ + (u + 0x04) & 0x1F, \ + (u + 0x06) & 0x1F, \ + (u + 0x08) & 0x1F, \ + (u + 0x0B) & 0x1F, \ + (u + 0x0D) & 0x1F, \ + (u + 0x0F) & 0x1F, \ + (u + 0x19) & 0x1F); \ + RBTT( \ + u + 0x04, \ + (u + 0x05) & 0x1F, \ + a, \ + u + 0x04, \ + (u + 0x06) & 0x1F, \ + (u + 0x08) & 0x1F, \ + (u + 0x0A) & 0x1F, \ + (u + 0x0D) & 0x1F, \ + (u + 0x0F) & 0x1F, \ + (u + 0x11) & 0x1F, \ + (u + 0x1B) & 0x1F); \ + RBTT( \ + u + 0x06, \ + (u + 0x07) & 0x1F, \ + a, \ + u + 0x06, \ + (u + 0x08) & 0x1F, \ + (u + 0x0A) & 0x1F, \ + (u + 0x0C) & 0x1F, \ + (u + 0x0F) & 0x1F, \ + (u + 0x11) & 0x1F, \ + (u + 0x13) & 0x1F, \ + (u + 0x1D) & 0x1F); \ + } \ + memcpy(a, t, sizeof t); \ + } while(0) + +#define ROUND_BIG_Q(a, r) \ + do { \ + sph_u32 t[32]; \ + size_t u; \ + a[0x00] ^= QC32up(0x00, r); \ + a[0x01] ^= QC32dn(0x00, r); \ + a[0x02] ^= QC32up(0x10, r); \ + a[0x03] ^= QC32dn(0x10, r); \ + a[0x04] ^= QC32up(0x20, r); \ + a[0x05] ^= QC32dn(0x20, r); \ + a[0x06] ^= QC32up(0x30, r); \ + a[0x07] ^= QC32dn(0x30, r); \ + a[0x08] ^= QC32up(0x40, r); \ + a[0x09] ^= QC32dn(0x40, r); \ + a[0x0A] ^= QC32up(0x50, r); \ + a[0x0B] ^= QC32dn(0x50, r); \ + a[0x0C] ^= QC32up(0x60, r); \ + a[0x0D] ^= QC32dn(0x60, r); \ + a[0x0E] ^= QC32up(0x70, r); \ + a[0x0F] ^= QC32dn(0x70, r); \ + a[0x10] ^= QC32up(0x80, r); \ + a[0x11] ^= QC32dn(0x80, r); \ + a[0x12] ^= QC32up(0x90, r); \ + a[0x13] ^= QC32dn(0x90, r); \ + a[0x14] ^= QC32up(0xA0, r); \ + a[0x15] ^= QC32dn(0xA0, r); \ + a[0x16] ^= QC32up(0xB0, r); \ + a[0x17] ^= QC32dn(0xB0, r); \ + a[0x18] ^= QC32up(0xC0, r); \ + a[0x19] ^= QC32dn(0xC0, r); \ + a[0x1A] ^= QC32up(0xD0, r); \ + a[0x1B] ^= QC32dn(0xD0, r); \ + a[0x1C] ^= QC32up(0xE0, r); \ + a[0x1D] ^= QC32dn(0xE0, r); \ + a[0x1E] ^= QC32up(0xF0, r); \ + a[0x1F] ^= QC32dn(0xF0, r); \ + for(u = 0; u < 32; u += 8) { \ + RBTT( \ + u + 0x00, \ + (u + 0x01) & 0x1F, \ + a, \ + (u + 0x02) & 0x1F, \ + (u + 0x06) & 0x1F, \ + (u + 0x0A) & 0x1F, \ + (u + 0x16) & 0x1F, \ + (u + 0x01) & 0x1F, \ + (u + 0x05) & 0x1F, \ + (u + 0x09) & 0x1F, \ + (u + 0x0D) & 0x1F); \ + RBTT( \ + u + 0x02, \ + (u + 0x03) & 0x1F, \ + a, \ + (u + 0x04) & 0x1F, \ + (u + 0x08) & 0x1F, \ + (u + 0x0C) & 0x1F, \ + (u + 0x18) & 0x1F, \ + (u + 0x03) & 0x1F, \ + (u + 0x07) & 0x1F, \ + (u + 0x0B) & 0x1F, \ + (u + 0x0F) & 0x1F); \ + RBTT( \ + u + 0x04, \ + (u + 0x05) & 0x1F, \ + a, \ + (u + 0x06) & 0x1F, \ + (u + 0x0A) & 0x1F, \ + (u + 0x0E) & 0x1F, \ + (u + 0x1A) & 0x1F, \ + (u + 0x05) & 0x1F, \ + (u + 0x09) & 0x1F, \ + (u + 0x0D) & 0x1F, \ + (u + 0x11) & 0x1F); \ + RBTT( \ + u + 0x06, \ + (u + 0x07) & 0x1F, \ + a, \ + (u + 0x08) & 0x1F, \ + (u + 0x0C) & 0x1F, \ + (u + 0x10) & 0x1F, \ + (u + 0x1C) & 0x1F, \ + (u + 0x07) & 0x1F, \ + (u + 0x0B) & 0x1F, \ + (u + 0x0F) & 0x1F, \ + (u + 0x13) & 0x1F); \ + } \ + memcpy(a, t, sizeof t); \ + } while(0) + +#define PERM_BIG_P(a) \ + do { \ + int r; \ + for(r = 0; r < 14; r++) ROUND_BIG_P(a, r); \ + } while(0) + +#define PERM_BIG_Q(a) \ + do { \ + int r; \ + for(r = 0; r < 14; r++) ROUND_BIG_Q(a, r); \ + } while(0) + +#define COMPRESS_BIG \ + do { \ + sph_u32 g[32], m[32]; \ + size_t uu; \ + for(uu = 0; uu < 32; uu++) { \ + m[uu] = dec32e_aligned(buf + (uu << 2)); \ + g[uu] = m[uu] ^ H[uu]; \ + } \ + PERM_BIG_P(g); \ + PERM_BIG_Q(m); \ + for(uu = 0; uu < 32; uu++) H[uu] ^= g[uu] ^ m[uu]; \ + } while(0) + +#define FINAL_BIG \ + do { \ + sph_u32 x[32]; \ + size_t uu; \ + memcpy(x, H, sizeof x); \ + PERM_BIG_P(x); \ + for(uu = 0; uu < 32; uu++) H[uu] ^= x[uu]; \ + } while(0) + +static void groestl_big_init(sph_groestl_big_context* sc, unsigned out_size) { + size_t u = 0; + + sc->ptr = 0; + for(u = 0; u < 31; u++) sc->state.narrow[u] = 0; + sc->state.narrow[31] = ((sph_u32)(out_size & 0xFF) << 24) | + ((sph_u32)(out_size & 0xFF00) << 8); + sc->count = 0; +} + +static void groestl_big_core(sph_groestl_big_context* sc, const void* data, size_t len) { + if(len == 0) { + return; + } + + unsigned char* buf = NULL; + size_t ptr = 0; + DECL_STATE_BIG + + buf = sc->buf; + ptr = sc->ptr; + if(len < (sizeof sc->buf) - ptr) { + memcpy(buf + ptr, data, len); + ptr += len; + sc->ptr = ptr; + return; + } + + READ_STATE_BIG(sc); + while(len > 0) { + size_t clen = 0; + + clen = (sizeof sc->buf) - ptr; + if(clen > len) clen = len; + memcpy(buf + ptr, data, clen); + ptr += clen; + data = (const unsigned char*)data + clen; + len -= clen; + if(ptr == sizeof sc->buf) { + COMPRESS_BIG; + sc->count++; + ptr = 0; + } + } + WRITE_STATE_BIG(sc); + sc->ptr = ptr; +} + +static void groestl_big_close( + sph_groestl_big_context* sc, + unsigned ub, + unsigned n, + void* dst, + size_t out_len) { + unsigned char pad[136] = {0}; + size_t ptr = 0, pad_len = 0, u2 = 0; + sph_u64 count = 0; + unsigned z = 0; + DECL_STATE_BIG + + ptr = sc->ptr; + z = 0x80 >> n; + pad[0] = ((ub & -z) | z) & 0xFF; + if(ptr < 120) { + pad_len = 128 - ptr; + count = SPH_T64(sc->count + 1); + } else { + pad_len = 256 - ptr; + count = SPH_T64(sc->count + 2); + } + memzero(pad + 1, pad_len - 9); + sph_enc64be(pad + pad_len - 8, count); + groestl_big_core(sc, pad, pad_len); + READ_STATE_BIG(sc); + FINAL_BIG; + for(u2 = 0; u2 < 16; u2++) enc32e(pad + (u2 << 2), H[u2 + 16]); + memcpy(dst, pad + 64 - out_len, out_len); + groestl_big_init(sc, (unsigned)out_len << 3); +} + +void groestl512_Init(void* cc) { + groestl_big_init((sph_groestl_big_context*)cc, 512); +} + +void groestl512_Update(void* cc, const void* data, size_t len) { + groestl_big_core((sph_groestl_big_context*)cc, data, len); +} + +void groestl512_Final(void* cc, void* dst) { + groestl_big_close((sph_groestl_big_context*)cc, 0, 0, dst, 64); +} + +void groestl512_DoubleTrunc(void* cc, void* dst) { + char buf[64] = {0}; + + groestl512_Final(cc, buf); + groestl512_Update(cc, buf, sizeof(buf)); + groestl512_Final(cc, buf); + memcpy(dst, buf, 32); +} diff --git a/applications/external/flipbip/lib/crypto/groestl.h b/applications/external/flipbip/lib/crypto/groestl.h new file mode 100644 index 0000000000..a4871deff2 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/groestl.h @@ -0,0 +1,95 @@ +/* Groestl hash from https://github.com/Groestlcoin/vanitygen + * Trezor adaptation by Yura Pakhuchiy . */ +/** + * Groestl interface. This code implements Groestl with the recommended + * parameters for SHA-3, with outputs of 224, 256, 384 and 512 bits. + * + * ==========================(LICENSE BEGIN)============================ + * + * Copyright (c) 2007-2010 Projet RNRT SAPHIR + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * ===========================(LICENSE END)============================= + * + * @file sph_groestl.h + * @author Thomas Pornin + */ + +#ifndef GROESTL_H__ +#define GROESTL_H__ + +#include + +/** + * This structure is a context for Groestl-384 and Groestl-512 computations: + * it contains the intermediate values and some data from the last + * entered block. Once a Groestl computation has been performed, the + * context can be reused for another computation. + * + * The contents of this structure are private. A running Groestl + * computation can be cloned by copying the context (e.g. with a simple + * memcpy()). + */ +typedef struct { + unsigned char buf[128]; /* first field, for alignment */ + size_t ptr; + union { + uint64_t wide[16]; + uint32_t narrow[32]; + } state; + uint64_t count; +} sph_groestl_big_context; + +typedef sph_groestl_big_context GROESTL512_CTX; + +/** + * Initialize a Groestl-512 context. This process performs no memory allocation. + * + * @param cc the Groestl-512 context (pointer to a + * GROESTL512_CTX) + */ +void groestl512_Init(void* cc); + +/** + * Process some data bytes. It is acceptable that len is zero + * (in which case this function does nothing). + * + * @param cc the Groestl-512 context + * @param data the input data + * @param len the input data length (in bytes) + */ +void groestl512_Update(void* cc, const void* data, size_t len); + +/** + * Terminate the current Groestl-512 computation and output the result into + * the provided buffer. The destination buffer must be wide enough to + * accomodate the result (64 bytes). The context is automatically + * reinitialized. + * + * @param cc the Groestl-512 context + * @param dst the destination buffer + */ +void groestl512_Final(void* cc, void* dst); + +/* Calculate double Groestl-512 hash and truncate it to 256-bits. */ +void groestl512_DoubleTrunc(void* cc, void* dst); + +#endif diff --git a/applications/external/flipbip/lib/crypto/groestl_internal.h b/applications/external/flipbip/lib/crypto/groestl_internal.h new file mode 100644 index 0000000000..3859ff5326 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/groestl_internal.h @@ -0,0 +1,461 @@ +/* Groestl hash from https://github.com/Groestlcoin/vanitygen + * Trezor adaptation by Yura Pakhuchiy . */ +/** + * Basic type definitions. + * + * This header file defines the generic integer types that will be used + * for the implementation of hash functions; it also contains helper + * functions which encode and decode multi-byte integer values, using + * either little-endian or big-endian conventions. + * + * This file contains a compile-time test on the size of a byte + * (the unsigned char C type). If bytes are not octets, + * i.e. if they do not have a size of exactly 8 bits, then compilation + * is aborted. Architectures where bytes are not octets are relatively + * rare, even in the embedded devices market. We forbid non-octet bytes + * because there is no clear convention on how octet streams are encoded + * on such systems. + * + * ==========================(LICENSE BEGIN)============================ + * + * Copyright (c) 2007-2010 Projet RNRT SAPHIR + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * ===========================(LICENSE END)============================= + * + * @file sph_types.h + * @author Thomas Pornin + */ + +#ifndef GROESTL_INTERNAL_H__ +#define GROESTL_INTERNAL_H__ + +#include + +/* + * All our I/O functions are defined over octet streams. We do not know + * how to handle input data if bytes are not octets. + */ +#if CHAR_BIT != 8 +#error This code requires 8-bit bytes +#endif + +#if defined __STDC__ && __STDC_VERSION__ >= 199901L + +#include + +typedef uint32_t sph_u32; +typedef int32_t sph_s32; +typedef uint64_t sph_u64; +typedef int64_t sph_s64; + +#define SPH_C32(x) ((sph_u32)(x)) +#define SPH_C64(x) ((sph_u64)(x)) + +#else +#error We need at least C99 compiler +#endif + +#define SPH_T32(x) ((x)&SPH_C32(0xFFFFFFFF)) +#define SPH_ROTL32(x, n) SPH_T32(((x) << (n)) | ((x) >> (32 - (n)))) +#define SPH_ROTR32(x, n) SPH_ROTL32(x, (32 - (n))) + +#define SPH_T64(x) ((x)&SPH_C64(0xFFFFFFFFFFFFFFFF)) +#define SPH_ROTL64(x, n) SPH_T64(((x) << (n)) | ((x) >> (64 - (n)))) +#define SPH_ROTR64(x, n) SPH_ROTL64(x, (64 - (n))) + +/* + * 32-bit x86, aka "i386 compatible". + */ +#if defined __i386__ || defined _M_IX86 + +#define SPH_DETECT_LITTLE_ENDIAN 1 +#define SPH_DETECT_BIG_ENDIAN 0 + +/* + * 64-bit x86, hereafter known as "amd64". + */ +#elif defined __x86_64 || defined _M_X64 + +#define SPH_DETECT_LITTLE_ENDIAN 1 +#define SPH_DETECT_BIG_ENDIAN 0 + +/* + * ARM, little-endian. + */ +#elif defined __arm__ && __ARMEL__ + +#define SPH_DETECT_LITTLE_ENDIAN 1 +#define SPH_DETECT_BIG_ENDIAN 0 + +/* + * ARM64, little-endian. + */ +#elif defined __aarch64__ + +#define SPH_DETECT_LITTLE_ENDIAN 1 +#define SPH_DETECT_BIG_ENDIAN 0 + +#endif + +#if defined SPH_DETECT_LITTLE_ENDIAN && !defined SPH_LITTLE_ENDIAN +#define SPH_LITTLE_ENDIAN SPH_DETECT_LITTLE_ENDIAN +#endif +#if defined SPH_DETECT_BIG_ENDIAN && !defined SPH_BIG_ENDIAN +#define SPH_BIG_ENDIAN SPH_DETECT_BIG_ENDIAN +#endif + +static inline sph_u32 sph_bswap32(sph_u32 x) { + x = SPH_T32((x << 16) | (x >> 16)); + x = ((x & SPH_C32(0xFF00FF00)) >> 8) | ((x & SPH_C32(0x00FF00FF)) << 8); + return x; +} + +/** + * Byte-swap a 64-bit value. + * + * @param x the input value + * @return the byte-swapped value + */ +static inline sph_u64 sph_bswap64(sph_u64 x) { + x = SPH_T64((x << 32) | (x >> 32)); + x = ((x & SPH_C64(0xFFFF0000FFFF0000)) >> 16) | ((x & SPH_C64(0x0000FFFF0000FFFF)) << 16); + x = ((x & SPH_C64(0xFF00FF00FF00FF00)) >> 8) | ((x & SPH_C64(0x00FF00FF00FF00FF)) << 8); + return x; +} + +static inline void sph_enc16be(void* dst, unsigned val) { + ((unsigned char*)dst)[0] = (val >> 8); + ((unsigned char*)dst)[1] = val; +} + +static inline unsigned sph_dec16be(const void* src) { + return ((unsigned)(((const unsigned char*)src)[0]) << 8) | + (unsigned)(((const unsigned char*)src)[1]); +} + +static inline void sph_enc16le(void* dst, unsigned val) { + ((unsigned char*)dst)[0] = val; + ((unsigned char*)dst)[1] = val >> 8; +} + +static inline unsigned sph_dec16le(const void* src) { + return (unsigned)(((const unsigned char*)src)[0]) | + ((unsigned)(((const unsigned char*)src)[1]) << 8); +} + +/** + * Encode a 32-bit value into the provided buffer (big endian convention). + * + * @param dst the destination buffer + * @param val the 32-bit value to encode + */ +static inline void sph_enc32be(void* dst, sph_u32 val) { + ((unsigned char*)dst)[0] = (val >> 24); + ((unsigned char*)dst)[1] = (val >> 16); + ((unsigned char*)dst)[2] = (val >> 8); + ((unsigned char*)dst)[3] = val; +} + +/** + * Encode a 32-bit value into the provided buffer (big endian convention). + * The destination buffer must be properly aligned. + * + * @param dst the destination buffer (32-bit aligned) + * @param val the value to encode + */ +static inline void sph_enc32be_aligned(void* dst, sph_u32 val) { +#if SPH_LITTLE_ENDIAN + *(sph_u32*)dst = sph_bswap32(val); +#elif SPH_BIG_ENDIAN + *(sph_u32*)dst = val; +#else + ((unsigned char*)dst)[0] = (val >> 24); + ((unsigned char*)dst)[1] = (val >> 16); + ((unsigned char*)dst)[2] = (val >> 8); + ((unsigned char*)dst)[3] = val; +#endif +} + +/** + * Decode a 32-bit value from the provided buffer (big endian convention). + * + * @param src the source buffer + * @return the decoded value + */ +static inline sph_u32 sph_dec32be(const void* src) { + return ((sph_u32)(((const unsigned char*)src)[0]) << 24) | + ((sph_u32)(((const unsigned char*)src)[1]) << 16) | + ((sph_u32)(((const unsigned char*)src)[2]) << 8) | + (sph_u32)(((const unsigned char*)src)[3]); +} + +/** + * Decode a 32-bit value from the provided buffer (big endian convention). + * The source buffer must be properly aligned. + * + * @param src the source buffer (32-bit aligned) + * @return the decoded value + */ +static inline sph_u32 sph_dec32be_aligned(const void* src) { +#if SPH_LITTLE_ENDIAN + return sph_bswap32(*(const sph_u32*)src); +#elif SPH_BIG_ENDIAN + return *(const sph_u32*)src; +#else + return ((sph_u32)(((const unsigned char*)src)[0]) << 24) | + ((sph_u32)(((const unsigned char*)src)[1]) << 16) | + ((sph_u32)(((const unsigned char*)src)[2]) << 8) | + (sph_u32)(((const unsigned char*)src)[3]); +#endif +} + +/** + * Encode a 32-bit value into the provided buffer (little endian convention). + * + * @param dst the destination buffer + * @param val the 32-bit value to encode + */ +static inline void sph_enc32le(void* dst, sph_u32 val) { + ((unsigned char*)dst)[0] = val; + ((unsigned char*)dst)[1] = (val >> 8); + ((unsigned char*)dst)[2] = (val >> 16); + ((unsigned char*)dst)[3] = (val >> 24); +} + +/** + * Encode a 32-bit value into the provided buffer (little endian convention). + * The destination buffer must be properly aligned. + * + * @param dst the destination buffer (32-bit aligned) + * @param val the value to encode + */ +static inline void sph_enc32le_aligned(void* dst, sph_u32 val) { +#if SPH_LITTLE_ENDIAN + *(sph_u32*)dst = val; +#elif SPH_BIG_ENDIAN + *(sph_u32*)dst = sph_bswap32(val); +#else + ((unsigned char*)dst)[0] = val; + ((unsigned char*)dst)[1] = (val >> 8); + ((unsigned char*)dst)[2] = (val >> 16); + ((unsigned char*)dst)[3] = (val >> 24); +#endif +} + +/** + * Decode a 32-bit value from the provided buffer (little endian convention). + * + * @param src the source buffer + * @return the decoded value + */ +static inline sph_u32 sph_dec32le(const void* src) { + return (sph_u32)(((const unsigned char*)src)[0]) | + ((sph_u32)(((const unsigned char*)src)[1]) << 8) | + ((sph_u32)(((const unsigned char*)src)[2]) << 16) | + ((sph_u32)(((const unsigned char*)src)[3]) << 24); +} + +/** + * Decode a 32-bit value from the provided buffer (little endian convention). + * The source buffer must be properly aligned. + * + * @param src the source buffer (32-bit aligned) + * @return the decoded value + */ +static inline sph_u32 sph_dec32le_aligned(const void* src) { +#if SPH_LITTLE_ENDIAN + return *(const sph_u32*)src; +#elif SPH_BIG_ENDIAN + return sph_bswap32(*(const sph_u32*)src); +#else + return (sph_u32)(((const unsigned char*)src)[0]) | + ((sph_u32)(((const unsigned char*)src)[1]) << 8) | + ((sph_u32)(((const unsigned char*)src)[2]) << 16) | + ((sph_u32)(((const unsigned char*)src)[3]) << 24); +#endif +} + +/** + * Encode a 64-bit value into the provided buffer (big endian convention). + * + * @param dst the destination buffer + * @param val the 64-bit value to encode + */ +static inline void sph_enc64be(void* dst, sph_u64 val) { + ((unsigned char*)dst)[0] = (val >> 56); + ((unsigned char*)dst)[1] = (val >> 48); + ((unsigned char*)dst)[2] = (val >> 40); + ((unsigned char*)dst)[3] = (val >> 32); + ((unsigned char*)dst)[4] = (val >> 24); + ((unsigned char*)dst)[5] = (val >> 16); + ((unsigned char*)dst)[6] = (val >> 8); + ((unsigned char*)dst)[7] = val; +} + +/** + * Encode a 64-bit value into the provided buffer (big endian convention). + * The destination buffer must be properly aligned. + * + * @param dst the destination buffer (64-bit aligned) + * @param val the value to encode + */ +static inline void sph_enc64be_aligned(void* dst, sph_u64 val) { +#if SPH_LITTLE_ENDIAN + *(sph_u64*)dst = sph_bswap64(val); +#elif SPH_BIG_ENDIAN + *(sph_u64*)dst = val; +#else + ((unsigned char*)dst)[0] = (val >> 56); + ((unsigned char*)dst)[1] = (val >> 48); + ((unsigned char*)dst)[2] = (val >> 40); + ((unsigned char*)dst)[3] = (val >> 32); + ((unsigned char*)dst)[4] = (val >> 24); + ((unsigned char*)dst)[5] = (val >> 16); + ((unsigned char*)dst)[6] = (val >> 8); + ((unsigned char*)dst)[7] = val; +#endif +} + +/** + * Decode a 64-bit value from the provided buffer (big endian convention). + * + * @param src the source buffer + * @return the decoded value + */ +static inline sph_u64 sph_dec64be(const void* src) { + return ((sph_u64)(((const unsigned char*)src)[0]) << 56) | + ((sph_u64)(((const unsigned char*)src)[1]) << 48) | + ((sph_u64)(((const unsigned char*)src)[2]) << 40) | + ((sph_u64)(((const unsigned char*)src)[3]) << 32) | + ((sph_u64)(((const unsigned char*)src)[4]) << 24) | + ((sph_u64)(((const unsigned char*)src)[5]) << 16) | + ((sph_u64)(((const unsigned char*)src)[6]) << 8) | + (sph_u64)(((const unsigned char*)src)[7]); +} + +/** + * Decode a 64-bit value from the provided buffer (big endian convention). + * The source buffer must be properly aligned. + * + * @param src the source buffer (64-bit aligned) + * @return the decoded value + */ +static inline sph_u64 sph_dec64be_aligned(const void* src) { +#if SPH_LITTLE_ENDIAN + return sph_bswap64(*(const sph_u64*)src); +#elif SPH_BIG_ENDIAN + return *(const sph_u64*)src; +#else + return ((sph_u64)(((const unsigned char*)src)[0]) << 56) | + ((sph_u64)(((const unsigned char*)src)[1]) << 48) | + ((sph_u64)(((const unsigned char*)src)[2]) << 40) | + ((sph_u64)(((const unsigned char*)src)[3]) << 32) | + ((sph_u64)(((const unsigned char*)src)[4]) << 24) | + ((sph_u64)(((const unsigned char*)src)[5]) << 16) | + ((sph_u64)(((const unsigned char*)src)[6]) << 8) | + (sph_u64)(((const unsigned char*)src)[7]); +#endif +} + +/** + * Encode a 64-bit value into the provided buffer (little endian convention). + * + * @param dst the destination buffer + * @param val the 64-bit value to encode + */ +static inline void sph_enc64le(void* dst, sph_u64 val) { + ((unsigned char*)dst)[0] = val; + ((unsigned char*)dst)[1] = (val >> 8); + ((unsigned char*)dst)[2] = (val >> 16); + ((unsigned char*)dst)[3] = (val >> 24); + ((unsigned char*)dst)[4] = (val >> 32); + ((unsigned char*)dst)[5] = (val >> 40); + ((unsigned char*)dst)[6] = (val >> 48); + ((unsigned char*)dst)[7] = (val >> 56); +} + +/** + * Encode a 64-bit value into the provided buffer (little endian convention). + * The destination buffer must be properly aligned. + * + * @param dst the destination buffer (64-bit aligned) + * @param val the value to encode + */ +static inline void sph_enc64le_aligned(void* dst, sph_u64 val) { +#if SPH_LITTLE_ENDIAN + *(sph_u64*)dst = val; +#elif SPH_BIG_ENDIAN + *(sph_u64*)dst = sph_bswap64(val); +#else + ((unsigned char*)dst)[0] = val; + ((unsigned char*)dst)[1] = (val >> 8); + ((unsigned char*)dst)[2] = (val >> 16); + ((unsigned char*)dst)[3] = (val >> 24); + ((unsigned char*)dst)[4] = (val >> 32); + ((unsigned char*)dst)[5] = (val >> 40); + ((unsigned char*)dst)[6] = (val >> 48); + ((unsigned char*)dst)[7] = (val >> 56); +#endif +} + +/** + * Decode a 64-bit value from the provided buffer (little endian convention). + * + * @param src the source buffer + * @return the decoded value + */ +static inline sph_u64 sph_dec64le(const void* src) { + return (sph_u64)(((const unsigned char*)src)[0]) | + ((sph_u64)(((const unsigned char*)src)[1]) << 8) | + ((sph_u64)(((const unsigned char*)src)[2]) << 16) | + ((sph_u64)(((const unsigned char*)src)[3]) << 24) | + ((sph_u64)(((const unsigned char*)src)[4]) << 32) | + ((sph_u64)(((const unsigned char*)src)[5]) << 40) | + ((sph_u64)(((const unsigned char*)src)[6]) << 48) | + ((sph_u64)(((const unsigned char*)src)[7]) << 56); +} + +/** + * Decode a 64-bit value from the provided buffer (little endian convention). + * The source buffer must be properly aligned. + * + * @param src the source buffer (64-bit aligned) + * @return the decoded value + */ +static inline sph_u64 sph_dec64le_aligned(const void* src) { +#if SPH_LITTLE_ENDIAN + return *(const sph_u64*)src; +#elif SPH_BIG_ENDIAN + return sph_bswap64(*(const sph_u64*)src); +#else + return (sph_u64)(((const unsigned char*)src)[0]) | + ((sph_u64)(((const unsigned char*)src)[1]) << 8) | + ((sph_u64)(((const unsigned char*)src)[2]) << 16) | + ((sph_u64)(((const unsigned char*)src)[3]) << 24) | + ((sph_u64)(((const unsigned char*)src)[4]) << 32) | + ((sph_u64)(((const unsigned char*)src)[5]) << 40) | + ((sph_u64)(((const unsigned char*)src)[6]) << 48) | + ((sph_u64)(((const unsigned char*)src)[7]) << 56); +#endif +} + +#endif diff --git a/applications/external/flipbip/lib/crypto/hasher.c b/applications/external/flipbip/lib/crypto/hasher.c new file mode 100644 index 0000000000..799ea63351 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/hasher.c @@ -0,0 +1,164 @@ +/** + * Copyright (c) 2017 Saleem Rashid + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "hasher.h" +#include "ripemd160.h" + +const uint32_t sha256_initial_tapsighash_state[8] = { + 0xf504a425UL, + 0xd7f8783bUL, + 0x1363868aUL, + 0xe3e55658UL, + 0x6eee945dUL, + 0xbc7888ddUL, + 0x02a6e2c3UL, + 0x1873fe9fUL, +}; + +void hasher_InitParam(Hasher* hasher, HasherType type, const void* param, uint32_t param_size) { + hasher->type = type; + hasher->param = param; + hasher->param_size = param_size; + + switch(hasher->type) { + case HASHER_SHA2: + case HASHER_SHA2D: + case HASHER_SHA2_RIPEMD: + sha256_Init(&hasher->ctx.sha2); + break; + case HASHER_SHA2_TAPSIGHASH: + sha256_Init_ex(&hasher->ctx.sha2, sha256_initial_tapsighash_state, 512); + break; + case HASHER_SHA3: +#if USE_KECCAK + case HASHER_SHA3K: +#endif + sha3_256_Init(&hasher->ctx.sha3); + break; + case HASHER_BLAKE: + case HASHER_BLAKED: + case HASHER_BLAKE_RIPEMD: + blake256_Init(&hasher->ctx.blake); + break; + case HASHER_GROESTLD_TRUNC: + groestl512_Init(&hasher->ctx.groestl); + break; + case HASHER_BLAKE2B: + blake2b_Init(&hasher->ctx.blake2b, 32); + break; + case HASHER_BLAKE2B_PERSONAL: + blake2b_InitPersonal(&hasher->ctx.blake2b, 32, hasher->param, hasher->param_size); + break; + } +} + +void hasher_Init(Hasher* hasher, HasherType type) { + hasher_InitParam(hasher, type, NULL, 0); +} + +void hasher_Reset(Hasher* hasher) { + hasher_InitParam(hasher, hasher->type, hasher->param, hasher->param_size); +} + +void hasher_Update(Hasher* hasher, const uint8_t* data, size_t length) { + switch(hasher->type) { + case HASHER_SHA2: + case HASHER_SHA2D: + case HASHER_SHA2_RIPEMD: + case HASHER_SHA2_TAPSIGHASH: + sha256_Update(&hasher->ctx.sha2, data, length); + break; + case HASHER_SHA3: +#if USE_KECCAK + case HASHER_SHA3K: +#endif + sha3_Update(&hasher->ctx.sha3, data, length); + break; + case HASHER_BLAKE: + case HASHER_BLAKED: + case HASHER_BLAKE_RIPEMD: + blake256_Update(&hasher->ctx.blake, data, length); + break; + case HASHER_GROESTLD_TRUNC: + groestl512_Update(&hasher->ctx.groestl, data, length); + break; + case HASHER_BLAKE2B: + case HASHER_BLAKE2B_PERSONAL: + blake2b_Update(&hasher->ctx.blake2b, data, length); + break; + } +} + +void hasher_Final(Hasher* hasher, uint8_t hash[HASHER_DIGEST_LENGTH]) { + switch(hasher->type) { + case HASHER_SHA2: + case HASHER_SHA2_TAPSIGHASH: + sha256_Final(&hasher->ctx.sha2, hash); + break; + case HASHER_SHA2D: + sha256_Final(&hasher->ctx.sha2, hash); + hasher_Raw(HASHER_SHA2, hash, HASHER_DIGEST_LENGTH, hash); + break; + case HASHER_SHA2_RIPEMD: + sha256_Final(&hasher->ctx.sha2, hash); + ripemd160(hash, HASHER_DIGEST_LENGTH, hash); + break; + case HASHER_SHA3: + sha3_Final(&hasher->ctx.sha3, hash); + break; +#if USE_KECCAK + case HASHER_SHA3K: + keccak_Final(&hasher->ctx.sha3, hash); + break; +#endif + case HASHER_BLAKE: + blake256_Final(&hasher->ctx.blake, hash); + break; + case HASHER_BLAKED: + blake256_Final(&hasher->ctx.blake, hash); + hasher_Raw(HASHER_BLAKE, hash, HASHER_DIGEST_LENGTH, hash); + break; + case HASHER_BLAKE_RIPEMD: + blake256_Final(&hasher->ctx.blake, hash); + ripemd160(hash, HASHER_DIGEST_LENGTH, hash); + break; + case HASHER_GROESTLD_TRUNC: + groestl512_DoubleTrunc(&hasher->ctx.groestl, hash); + break; + case HASHER_BLAKE2B: + case HASHER_BLAKE2B_PERSONAL: + blake2b_Final(&hasher->ctx.blake2b, hash, 32); + break; + } +} + +void hasher_Raw( + HasherType type, + const uint8_t* data, + size_t length, + uint8_t hash[HASHER_DIGEST_LENGTH]) { + Hasher hasher = {0}; + + hasher_Init(&hasher, type); + hasher_Update(&hasher, data, length); + hasher_Final(&hasher, hash); +} diff --git a/applications/external/flipbip/lib/crypto/hasher.h b/applications/external/flipbip/lib/crypto/hasher.h new file mode 100644 index 0000000000..5130df4160 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/hasher.h @@ -0,0 +1,85 @@ +/** + * Copyright (c) 2017 Saleem Rashid + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __HASHER_H__ +#define __HASHER_H__ + +#include +#include + +#include "blake256.h" +#include "blake2b.h" +#include "groestl.h" +#include "sha2.h" +#include "sha3.h" + +#define HASHER_DIGEST_LENGTH 32 + +typedef enum { + HASHER_SHA2, + HASHER_SHA2D, + HASHER_SHA2_RIPEMD, + HASHER_SHA2_TAPSIGHASH, + + HASHER_SHA3, +#if USE_KECCAK + HASHER_SHA3K, +#endif + + HASHER_BLAKE, + HASHER_BLAKED, + HASHER_BLAKE_RIPEMD, + + HASHER_GROESTLD_TRUNC, /* Double Groestl512 hasher truncated to 256 bits */ + + HASHER_BLAKE2B, + HASHER_BLAKE2B_PERSONAL, +} HasherType; + +typedef struct { + HasherType type; + + union { + SHA256_CTX sha2; // for HASHER_SHA2{,D} + SHA3_CTX sha3; // for HASHER_SHA3{,K} + BLAKE256_CTX blake; // for HASHER_BLAKE{,D} + GROESTL512_CTX groestl; // for HASHER_GROESTLD_TRUNC + BLAKE2B_CTX blake2b; // for HASHER_BLAKE2B{,_PERSONAL} + } ctx; + + const void* param; + uint32_t param_size; +} Hasher; + +void hasher_InitParam(Hasher* hasher, HasherType type, const void* param, uint32_t param_size); +void hasher_Init(Hasher* hasher, HasherType type); +void hasher_Reset(Hasher* hasher); +void hasher_Update(Hasher* hasher, const uint8_t* data, size_t length); +void hasher_Final(Hasher* hasher, uint8_t hash[HASHER_DIGEST_LENGTH]); + +void hasher_Raw( + HasherType type, + const uint8_t* data, + size_t length, + uint8_t hash[HASHER_DIGEST_LENGTH]); + +#endif diff --git a/applications/external/flipbip/lib/crypto/hmac.c b/applications/external/flipbip/lib/crypto/hmac.c new file mode 100644 index 0000000000..1ee02711ae --- /dev/null +++ b/applications/external/flipbip/lib/crypto/hmac.c @@ -0,0 +1,186 @@ +/** + * Copyright (c) 2013-2014 Tomas Dzetkulic + * Copyright (c) 2013-2014 Pavol Rusnak + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include + +#include "hmac.h" +#include "memzero.h" +#include "options.h" + +void hmac_sha256_Init(HMAC_SHA256_CTX* hctx, const uint8_t* key, const uint32_t keylen) { + static CONFIDENTIAL uint8_t i_key_pad[SHA256_BLOCK_LENGTH]; + memzero(i_key_pad, SHA256_BLOCK_LENGTH); + if(keylen > SHA256_BLOCK_LENGTH) { + sha256_Raw(key, keylen, i_key_pad); + } else { + memcpy(i_key_pad, key, keylen); + } + for(int i = 0; i < SHA256_BLOCK_LENGTH; i++) { + hctx->o_key_pad[i] = i_key_pad[i] ^ 0x5c; + i_key_pad[i] ^= 0x36; + } + sha256_Init(&(hctx->ctx)); + sha256_Update(&(hctx->ctx), i_key_pad, SHA256_BLOCK_LENGTH); + memzero(i_key_pad, sizeof(i_key_pad)); +} + +void hmac_sha256_Update(HMAC_SHA256_CTX* hctx, const uint8_t* msg, const uint32_t msglen) { + sha256_Update(&(hctx->ctx), msg, msglen); +} + +void hmac_sha256_Final(HMAC_SHA256_CTX* hctx, uint8_t* hmac) { + sha256_Final(&(hctx->ctx), hmac); + sha256_Init(&(hctx->ctx)); + sha256_Update(&(hctx->ctx), hctx->o_key_pad, SHA256_BLOCK_LENGTH); + sha256_Update(&(hctx->ctx), hmac, SHA256_DIGEST_LENGTH); + sha256_Final(&(hctx->ctx), hmac); + memzero(hctx, sizeof(HMAC_SHA256_CTX)); +} + +void hmac_sha256( + const uint8_t* key, + const uint32_t keylen, + const uint8_t* msg, + const uint32_t msglen, + uint8_t* hmac) { + static CONFIDENTIAL HMAC_SHA256_CTX hctx; + hmac_sha256_Init(&hctx, key, keylen); + hmac_sha256_Update(&hctx, msg, msglen); + hmac_sha256_Final(&hctx, hmac); +} + +void hmac_sha256_prepare( + const uint8_t* key, + const uint32_t keylen, + uint32_t* opad_digest, + uint32_t* ipad_digest) { + static CONFIDENTIAL uint32_t key_pad[SHA256_BLOCK_LENGTH / sizeof(uint32_t)]; + + memzero(key_pad, sizeof(key_pad)); + if(keylen > SHA256_BLOCK_LENGTH) { + static CONFIDENTIAL SHA256_CTX context; + sha256_Init(&context); + sha256_Update(&context, key, keylen); + sha256_Final(&context, (uint8_t*)key_pad); + } else { + memcpy(key_pad, key, keylen); + } + + /* compute o_key_pad and its digest */ + for(int i = 0; i < SHA256_BLOCK_LENGTH / (int)sizeof(uint32_t); i++) { + uint32_t data = 0; +#if BYTE_ORDER == LITTLE_ENDIAN + REVERSE32(key_pad[i], data); +#else + data = key_pad[i]; +#endif + key_pad[i] = data ^ 0x5c5c5c5c; + } + sha256_Transform(sha256_initial_hash_value, key_pad, opad_digest); + + /* convert o_key_pad to i_key_pad and compute its digest */ + for(int i = 0; i < SHA256_BLOCK_LENGTH / (int)sizeof(uint32_t); i++) { + key_pad[i] = key_pad[i] ^ 0x5c5c5c5c ^ 0x36363636; + } + sha256_Transform(sha256_initial_hash_value, key_pad, ipad_digest); + memzero(key_pad, sizeof(key_pad)); +} + +void hmac_sha512_Init(HMAC_SHA512_CTX* hctx, const uint8_t* key, const uint32_t keylen) { + static CONFIDENTIAL uint8_t i_key_pad[SHA512_BLOCK_LENGTH]; + memzero(i_key_pad, SHA512_BLOCK_LENGTH); + if(keylen > SHA512_BLOCK_LENGTH) { + sha512_Raw(key, keylen, i_key_pad); + } else { + memcpy(i_key_pad, key, keylen); + } + for(int i = 0; i < SHA512_BLOCK_LENGTH; i++) { + hctx->o_key_pad[i] = i_key_pad[i] ^ 0x5c; + i_key_pad[i] ^= 0x36; + } + sha512_Init(&(hctx->ctx)); + sha512_Update(&(hctx->ctx), i_key_pad, SHA512_BLOCK_LENGTH); + memzero(i_key_pad, sizeof(i_key_pad)); +} + +void hmac_sha512_Update(HMAC_SHA512_CTX* hctx, const uint8_t* msg, const uint32_t msglen) { + sha512_Update(&(hctx->ctx), msg, msglen); +} + +void hmac_sha512_Final(HMAC_SHA512_CTX* hctx, uint8_t* hmac) { + sha512_Final(&(hctx->ctx), hmac); + sha512_Init(&(hctx->ctx)); + sha512_Update(&(hctx->ctx), hctx->o_key_pad, SHA512_BLOCK_LENGTH); + sha512_Update(&(hctx->ctx), hmac, SHA512_DIGEST_LENGTH); + sha512_Final(&(hctx->ctx), hmac); + memzero(hctx, sizeof(HMAC_SHA512_CTX)); +} + +void hmac_sha512( + const uint8_t* key, + const uint32_t keylen, + const uint8_t* msg, + const uint32_t msglen, + uint8_t* hmac) { + HMAC_SHA512_CTX hctx = {0}; + hmac_sha512_Init(&hctx, key, keylen); + hmac_sha512_Update(&hctx, msg, msglen); + hmac_sha512_Final(&hctx, hmac); +} + +void hmac_sha512_prepare( + const uint8_t* key, + const uint32_t keylen, + uint64_t* opad_digest, + uint64_t* ipad_digest) { + static CONFIDENTIAL uint64_t key_pad[SHA512_BLOCK_LENGTH / sizeof(uint64_t)]; + + memzero(key_pad, sizeof(key_pad)); + if(keylen > SHA512_BLOCK_LENGTH) { + static CONFIDENTIAL SHA512_CTX context; + sha512_Init(&context); + sha512_Update(&context, key, keylen); + sha512_Final(&context, (uint8_t*)key_pad); + } else { + memcpy(key_pad, key, keylen); + } + + /* compute o_key_pad and its digest */ + for(int i = 0; i < SHA512_BLOCK_LENGTH / (int)sizeof(uint64_t); i++) { + uint64_t data = 0; +#if BYTE_ORDER == LITTLE_ENDIAN + REVERSE64(key_pad[i], data); +#else + data = key_pad[i]; +#endif + key_pad[i] = data ^ 0x5c5c5c5c5c5c5c5c; + } + sha512_Transform(sha512_initial_hash_value, key_pad, opad_digest); + + /* convert o_key_pad to i_key_pad and compute its digest */ + for(int i = 0; i < SHA512_BLOCK_LENGTH / (int)sizeof(uint64_t); i++) { + key_pad[i] = key_pad[i] ^ 0x5c5c5c5c5c5c5c5c ^ 0x3636363636363636; + } + sha512_Transform(sha512_initial_hash_value, key_pad, ipad_digest); + memzero(key_pad, sizeof(key_pad)); +} diff --git a/applications/external/flipbip/lib/crypto/hmac.h b/applications/external/flipbip/lib/crypto/hmac.h new file mode 100644 index 0000000000..21a4f8da67 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/hmac.h @@ -0,0 +1,70 @@ +/** + * Copyright (c) 2013-2014 Tomas Dzetkulic + * Copyright (c) 2013-2014 Pavol Rusnak + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT HMAC_SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __HMAC_H__ +#define __HMAC_H__ + +#include +#include "sha2.h" + +typedef struct _HMAC_SHA256_CTX { + uint8_t o_key_pad[SHA256_BLOCK_LENGTH]; + SHA256_CTX ctx; +} HMAC_SHA256_CTX; + +typedef struct _HMAC_SHA512_CTX { + uint8_t o_key_pad[SHA512_BLOCK_LENGTH]; + SHA512_CTX ctx; +} HMAC_SHA512_CTX; + +void hmac_sha256_Init(HMAC_SHA256_CTX* hctx, const uint8_t* key, const uint32_t keylen); +void hmac_sha256_Update(HMAC_SHA256_CTX* hctx, const uint8_t* msg, const uint32_t msglen); +void hmac_sha256_Final(HMAC_SHA256_CTX* hctx, uint8_t* hmac); +void hmac_sha256( + const uint8_t* key, + const uint32_t keylen, + const uint8_t* msg, + const uint32_t msglen, + uint8_t* hmac); +void hmac_sha256_prepare( + const uint8_t* key, + const uint32_t keylen, + uint32_t* opad_digest, + uint32_t* ipad_digest); + +void hmac_sha512_Init(HMAC_SHA512_CTX* hctx, const uint8_t* key, const uint32_t keylen); +void hmac_sha512_Update(HMAC_SHA512_CTX* hctx, const uint8_t* msg, const uint32_t msglen); +void hmac_sha512_Final(HMAC_SHA512_CTX* hctx, uint8_t* hmac); +void hmac_sha512( + const uint8_t* key, + const uint32_t keylen, + const uint8_t* msg, + const uint32_t msglen, + uint8_t* hmac); +void hmac_sha512_prepare( + const uint8_t* key, + const uint32_t keylen, + uint64_t* opad_digest, + uint64_t* ipad_digest); + +#endif diff --git a/applications/external/flipbip/lib/crypto/hmac_drbg.c b/applications/external/flipbip/lib/crypto/hmac_drbg.c new file mode 100644 index 0000000000..3115c11f22 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/hmac_drbg.c @@ -0,0 +1,141 @@ +/** + * Copyright (c) 2019 Andrew R. Kozlik + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "hmac_drbg.h" +#include +#include "memzero.h" +#include "sha2.h" + +static void update_k( + HMAC_DRBG_CTX* ctx, + uint8_t domain, + const uint8_t* data1, + size_t len1, + const uint8_t* data2, + size_t len2) { + // Computes K = HMAC(K, V || domain || data1 || data 2). + + // First hash operation of HMAC. + uint32_t h[SHA256_BLOCK_LENGTH / sizeof(uint32_t)] = {0}; + if(len1 + len2 == 0) { + ctx->v[8] = 0x00800000; + ctx->v[15] = (SHA256_BLOCK_LENGTH + SHA256_DIGEST_LENGTH + 1) * 8; + sha256_Transform(ctx->idig, ctx->v, h); + ctx->v[8] = 0x80000000; + ctx->v[15] = (SHA256_BLOCK_LENGTH + SHA256_DIGEST_LENGTH) * 8; + } else { + SHA256_CTX sha_ctx = {0}; + memcpy(sha_ctx.state, ctx->idig, SHA256_DIGEST_LENGTH); + for(size_t i = 0; i < SHA256_DIGEST_LENGTH / sizeof(uint32_t); i++) { +#if BYTE_ORDER == LITTLE_ENDIAN + REVERSE32(ctx->v[i], sha_ctx.buffer[i]); +#else + sha_ctx.buffer[i] = ctx->v[i]; +#endif + } + ((uint8_t*)sha_ctx.buffer)[SHA256_DIGEST_LENGTH] = domain; + sha_ctx.bitcount = (SHA256_BLOCK_LENGTH + SHA256_DIGEST_LENGTH + 1) * 8; + sha256_Update(&sha_ctx, data1, len1); + sha256_Update(&sha_ctx, data2, len2); + sha256_Final(&sha_ctx, (uint8_t*)h); +#if BYTE_ORDER == LITTLE_ENDIAN + for(size_t i = 0; i < SHA256_DIGEST_LENGTH / sizeof(uint32_t); i++) REVERSE32(h[i], h[i]); +#endif + } + + // Second hash operation of HMAC. + h[8] = 0x80000000; + h[15] = (SHA256_BLOCK_LENGTH + SHA256_DIGEST_LENGTH) * 8; + sha256_Transform(ctx->odig, h, h); + + // Precompute the inner digest and outer digest of K. + h[8] = 0; + h[15] = 0; + for(size_t i = 0; i < SHA256_BLOCK_LENGTH / sizeof(uint32_t); i++) { + h[i] ^= 0x36363636; + } + sha256_Transform(sha256_initial_hash_value, h, ctx->idig); + + for(size_t i = 0; i < SHA256_BLOCK_LENGTH / sizeof(uint32_t); i++) { + h[i] = h[i] ^ 0x36363636 ^ 0x5c5c5c5c; + } + sha256_Transform(sha256_initial_hash_value, h, ctx->odig); + memzero(h, sizeof(h)); +} + +static void update_v(HMAC_DRBG_CTX* ctx) { + sha256_Transform(ctx->idig, ctx->v, ctx->v); + sha256_Transform(ctx->odig, ctx->v, ctx->v); +} + +void hmac_drbg_init( + HMAC_DRBG_CTX* ctx, + const uint8_t* entropy, + size_t entropy_len, + const uint8_t* nonce, + size_t nonce_len) { + uint32_t h[SHA256_BLOCK_LENGTH / sizeof(uint32_t)] = {0}; + + // Precompute the inner digest and outer digest of K = 0x00 ... 0x00. + memset(h, 0x36, sizeof(h)); + sha256_Transform(sha256_initial_hash_value, h, ctx->idig); + memset(h, 0x5c, sizeof(h)); + sha256_Transform(sha256_initial_hash_value, h, ctx->odig); + + // Let V = 0x01 ... 0x01. + memset(ctx->v, 1, SHA256_DIGEST_LENGTH); + for(size_t i = 9; i < 15; i++) ctx->v[i] = 0; + ctx->v[8] = 0x80000000; + ctx->v[15] = (SHA256_BLOCK_LENGTH + SHA256_DIGEST_LENGTH) * 8; + + hmac_drbg_reseed(ctx, entropy, entropy_len, nonce, nonce_len); + + memzero(h, sizeof(h)); +} + +void hmac_drbg_reseed( + HMAC_DRBG_CTX* ctx, + const uint8_t* entropy, + size_t len, + const uint8_t* addin, + size_t addin_len) { + update_k(ctx, 0, entropy, len, addin, addin_len); + update_v(ctx); + if(len == 0) return; + update_k(ctx, 1, entropy, len, addin, addin_len); + update_v(ctx); +} + +void hmac_drbg_generate(HMAC_DRBG_CTX* ctx, uint8_t* buf, size_t len) { + size_t i = 0; + while(i < len) { + update_v(ctx); + for(size_t j = 0; j < 8 && i < len; j++) { + uint32_t r = ctx->v[j]; + for(int k = 24; k >= 0 && i < len; k -= 8) { + buf[i++] = (r >> k) & 0xFF; + } + } + } + update_k(ctx, 0, NULL, 0, NULL, 0); + update_v(ctx); +} diff --git a/applications/external/flipbip/lib/crypto/hmac_drbg.h b/applications/external/flipbip/lib/crypto/hmac_drbg.h new file mode 100644 index 0000000000..f6d3f3732f --- /dev/null +++ b/applications/external/flipbip/lib/crypto/hmac_drbg.h @@ -0,0 +1,51 @@ +/** + * Copyright (c) 2019 Andrew R. Kozlik + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __HMAC_DRBG_H__ +#define __HMAC_DRBG_H__ + +#include "sha2.h" +#include + +// HMAC based Deterministic Random Bit Generator with SHA-256 + +typedef struct _HMAC_DRBG_CTX { + uint32_t odig[SHA256_DIGEST_LENGTH / sizeof(uint32_t)]; + uint32_t idig[SHA256_DIGEST_LENGTH / sizeof(uint32_t)]; + uint32_t v[SHA256_BLOCK_LENGTH / sizeof(uint32_t)]; +} HMAC_DRBG_CTX; + +void hmac_drbg_init( + HMAC_DRBG_CTX* ctx, + const uint8_t* buf, + size_t len, + const uint8_t* nonce, + size_t nonce_len); +void hmac_drbg_reseed( + HMAC_DRBG_CTX* ctx, + const uint8_t* buf, + size_t len, + const uint8_t* addin, + size_t addin_len); +void hmac_drbg_generate(HMAC_DRBG_CTX* ctx, uint8_t* buf, size_t len); + +#endif diff --git a/applications/external/flipbip/lib/crypto/memzero.c b/applications/external/flipbip/lib/crypto/memzero.c new file mode 100644 index 0000000000..64866ee56c --- /dev/null +++ b/applications/external/flipbip/lib/crypto/memzero.c @@ -0,0 +1,74 @@ +#ifndef __STDC_WANT_LIB_EXT1__ +#define __STDC_WANT_LIB_EXT1__ 1 // C11's bounds-checking interface. +#endif +#include + +#ifdef _WIN32 +#include +#endif + +#ifdef __unix__ +#include +#include +#endif + +// C11's bounds-checking interface. +#if defined(__STDC_LIB_EXT1__) +#define HAVE_MEMSET_S 1 +#endif + +// GNU C Library version 2.25 or later. +#if defined(__GLIBC__) && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 25)) +#define HAVE_EXPLICIT_BZERO 1 +#endif + +// Newlib +#if defined(__NEWLIB__) +#define HAVE_EXPLICIT_BZERO 1 +#endif + +// FreeBSD version 11.0 or later. +#if defined(__FreeBSD__) && __FreeBSD_version >= 1100037 +#define HAVE_EXPLICIT_BZERO 1 +#endif + +// OpenBSD version 5.5 or later. +#if defined(__OpenBSD__) && OpenBSD >= 201405 +#define HAVE_EXPLICIT_BZERO 1 +#endif + +// NetBSD version 7.2 or later. +#if defined(__NetBSD__) && __NetBSD_Version__ >= 702000000 +#define HAVE_EXPLICIT_MEMSET 1 +#endif + +// Adapted from +// https://github.com/jedisct1/libsodium/blob/1647f0d53ae0e370378a9195477e3df0a792408f/src/libsodium/sodium/utils.c#L102-L130 + +void memzero(void* const pnt, const size_t len) { +#ifdef _WIN32 + SecureZeroMemory(pnt, len); +#elif defined(HAVE_MEMSET_S) + memset_s(pnt, (rsize_t)len, 0, (rsize_t)len); +// #elif defined(HAVE_EXPLICIT_BZERO) +// explicit_bzero(pnt, len); +#elif defined(HAVE_EXPLICIT_MEMSET) + explicit_memset(pnt, 0, len); +#else + volatile unsigned char* volatile pnt_ = (volatile unsigned char* volatile)pnt; + size_t i = (size_t)0U; + + while(i < len) { + pnt_[i++] = 0U; + } +#endif + + // explicitly mark the memory as overwritten for the Clang MemorySanitizer + // this is only included at compile time if MemorySanitizer is enabled and + // should not come with any downsides during regular builds +#if defined(__has_feature) +#if __has_feature(memory_sanitizer) + memset(pnt, 0, len); +#endif +#endif +} diff --git a/applications/external/flipbip/lib/crypto/memzero.h b/applications/external/flipbip/lib/crypto/memzero.h new file mode 100644 index 0000000000..0a959fbc2d --- /dev/null +++ b/applications/external/flipbip/lib/crypto/memzero.h @@ -0,0 +1,8 @@ +#ifndef __MEMZERO_H__ +#define __MEMZERO_H__ + +#include + +void memzero(void* const pnt, const size_t len); + +#endif diff --git a/applications/external/flipbip/lib/crypto/monero/base58.c b/applications/external/flipbip/lib/crypto/monero/base58.c new file mode 100644 index 0000000000..1ca9dfafd9 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/monero/base58.c @@ -0,0 +1,297 @@ +// Copyright (c) 2014-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote +// developers + +#if USE_MONERO + +#include "base58.h" +#include +#include +#include +#include +#include "../base58.h" +#include "../byte_order.h" +#include "int_util.h" +#include "../sha2.h" + +const size_t alphabet_size = 58; // sizeof(b58digits_ordered) - 1; +const size_t full_encoded_block_size = 11; +const size_t encoded_block_sizes[] = {0, 2, 3, 5, 6, 7, 9, 10, full_encoded_block_size}; +const size_t full_block_size = sizeof(encoded_block_sizes) / sizeof(encoded_block_sizes[0]) - 1; +const size_t addr_checksum_size = 4; +const size_t max_bin_data_size = 72; +const int decoded_block_sizes[] = {0, -1, 1, 2, -1, 3, 4, 5, -1, 6, 7, 8}; +#define reverse_alphabet(letter) ((int8_t)b58digits_map[(int)letter]) + +uint64_t uint_8be_to_64(const uint8_t* data, size_t size) { + assert(1 <= size && size <= sizeof(uint64_t)); + + uint64_t res = 0; + switch(9 - size) { + case 1: + res |= *data++; /* FALLTHRU */ + case 2: + res <<= 8; + res |= *data++; /* FALLTHRU */ + case 3: + res <<= 8; + res |= *data++; /* FALLTHRU */ + case 4: + res <<= 8; + res |= *data++; /* FALLTHRU */ + case 5: + res <<= 8; + res |= *data++; /* FALLTHRU */ + case 6: + res <<= 8; + res |= *data++; /* FALLTHRU */ + case 7: + res <<= 8; + res |= *data++; /* FALLTHRU */ + case 8: + res <<= 8; + res |= *data; + break; + default: + assert(false); + } + + return res; +} + +void uint_64_to_8be(uint64_t num, size_t size, uint8_t* data) { + assert(1 <= size && size <= sizeof(uint64_t)); + +#if BYTE_ORDER == LITTLE_ENDIAN + uint64_t num_be = SWAP64(num); +#else + uint64_t num_be = num; +#endif + memcpy(data, (uint8_t*)(&num_be) + sizeof(uint64_t) - size, size); +} + +void encode_block(const char* block, size_t size, char* res) { + assert(1 <= size && size <= full_block_size); + + uint64_t num = uint_8be_to_64((uint8_t*)(block), size); + int i = ((int)(encoded_block_sizes[size])) - 1; + while(0 <= i) { + uint64_t remainder = num % alphabet_size; + num /= alphabet_size; + res[i] = b58digits_ordered[remainder]; + --i; + } +} + +bool decode_block(const char* block, size_t size, char* res) { + assert(1 <= size && size <= full_encoded_block_size); + + int res_size = decoded_block_sizes[size]; + if(res_size <= 0) { + return false; // Invalid block size + } + + uint64_t res_num = 0; + uint64_t order = 1; + for(size_t i = size - 1; i < size; --i) { + if(block[i] & 0x80) { + return false; // Invalid symbol + } + int digit = reverse_alphabet(block[i]); + if(digit < 0) { + return false; // Invalid symbol + } + + uint64_t product_hi = 0; + uint64_t tmp = res_num + mul128(order, (uint64_t)digit, &product_hi); + if(tmp < res_num || 0 != product_hi) { + return false; // Overflow + } + + res_num = tmp; + // The original code comment for the order multiplication says + // "Never overflows, 58^10 < 2^64" + // This is incorrect since it overflows on the 11th iteration + // However, there is no negative impact since the result is unused + order *= alphabet_size; + } + + if((size_t)res_size < full_block_size && (UINT64_C(1) << (8 * res_size)) <= res_num) + return false; // Overflow + + uint_64_to_8be(res_num, res_size, (uint8_t*)(res)); + + return true; +} + +bool xmr_base58_encode(char* b58, size_t* b58sz, const void* data, size_t binsz) { + if(binsz == 0) { + if(b58sz) { + *b58sz = 0; + } + return true; + } + + const char* data_bin = data; + size_t full_block_count = binsz / full_block_size; + size_t last_block_size = binsz % full_block_size; + size_t res_size = + full_block_count * full_encoded_block_size + encoded_block_sizes[last_block_size]; + + if(b58sz) { + if(res_size > *b58sz) { + return false; + } + *b58sz = res_size; + } + + for(size_t i = 0; i < full_block_count; ++i) { + encode_block( + data_bin + i * full_block_size, full_block_size, b58 + i * full_encoded_block_size); + } + + if(0 < last_block_size) { + encode_block( + data_bin + full_block_count * full_block_size, + last_block_size, + b58 + full_block_count * full_encoded_block_size); + } + + return true; +} + +bool xmr_base58_decode(const char* b58, size_t b58sz, void* data, size_t* binsz) { + if(b58sz == 0) { + *binsz = 0; + return true; + } + + size_t full_block_count = b58sz / full_encoded_block_size; + size_t last_block_size = b58sz % full_encoded_block_size; + int last_block_decoded_size = decoded_block_sizes[last_block_size]; + if(last_block_decoded_size < 0) { + *binsz = 0; + return false; // Invalid enc length + } + + size_t data_size = full_block_count * full_block_size + last_block_decoded_size; + if(*binsz < data_size) { + *binsz = 0; + return false; + } + + char* data_bin = data; + for(size_t i = 0; i < full_block_count; ++i) { + if(!decode_block( + b58 + i * full_encoded_block_size, + full_encoded_block_size, + data_bin + i * full_block_size)) { + *binsz = 0; + return false; + } + } + + if(0 < last_block_size) { + if(!decode_block( + b58 + full_block_count * full_encoded_block_size, + last_block_size, + data_bin + full_block_count * full_block_size)) { + *binsz = 0; + return false; + } + } + + *binsz = data_size; + return true; +} + +int xmr_base58_addr_encode_check( + uint64_t tag, + const uint8_t* data, + size_t binsz, + char* b58, + size_t b58sz) { + if(binsz > max_bin_data_size || tag > 127) { // tag varint + return false; + } + + size_t b58size = b58sz; + uint8_t buf[(binsz + 1) + HASHER_DIGEST_LENGTH]; + memset(buf, 0, sizeof(buf)); + uint8_t* hash = buf + binsz + 1; + buf[0] = (uint8_t)tag; + memcpy(buf + 1, data, binsz); + hasher_Raw(HASHER_SHA3K, buf, binsz + 1, hash); + + bool r = xmr_base58_encode(b58, &b58size, buf, binsz + 1 + addr_checksum_size); + return (int)(!r ? 0 : b58size); +} + +int xmr_base58_addr_decode_check( + const char* addr, + size_t sz, + uint64_t* tag, + void* data, + size_t datalen) { + size_t buflen = 1 + max_bin_data_size + addr_checksum_size; + uint8_t buf[buflen]; + memset(buf, 0, sizeof(buf)); + uint8_t hash[HASHER_DIGEST_LENGTH] = {0}; + + if(!xmr_base58_decode(addr, sz, buf, &buflen)) { + return 0; + } + + if(buflen <= addr_checksum_size + 1) { + return 0; + } + + size_t res_size = buflen - addr_checksum_size - 1; + if(datalen < res_size) { + return 0; + } + + hasher_Raw(HASHER_SHA3K, buf, buflen - addr_checksum_size, hash); + if(memcmp(hash, buf + buflen - addr_checksum_size, addr_checksum_size) != 0) { + return 0; + } + + *tag = buf[0]; + if(*tag > 127) { + return false; // varint + } + + memcpy(data, buf + 1, res_size); + return (int)res_size; +} + +#endif // USE_MONERO \ No newline at end of file diff --git a/applications/external/flipbip/lib/crypto/monero/base58.h b/applications/external/flipbip/lib/crypto/monero/base58.h new file mode 100644 index 0000000000..adcafadd17 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/monero/base58.h @@ -0,0 +1,60 @@ +// Copyright (c) 2014-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote +// developers + +#if USE_MONERO + +#ifndef __XMR_BASE58_H__ +#define __XMR_BASE58_H__ + +#include +#include "../hasher.h" +#include "../options.h" + +int xmr_base58_addr_encode_check( + uint64_t tag, + const uint8_t* data, + size_t binsz, + char* b58, + size_t b58sz); +int xmr_base58_addr_decode_check( + const char* addr, + size_t sz, + uint64_t* tag, + void* data, + size_t datalen); +bool xmr_base58_encode(char* b58, size_t* b58sz, const void* data, size_t binsz); +bool xmr_base58_decode(const char* b58, size_t b58sz, void* data, size_t* binsz); + +#endif + +#endif // USE_MONERO \ No newline at end of file diff --git a/applications/external/flipbip/lib/crypto/monero/int_util.h b/applications/external/flipbip/lib/crypto/monero/int_util.h new file mode 100644 index 0000000000..fb627b62e3 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/monero/int_util.h @@ -0,0 +1,81 @@ +// Copyright (c) 2014-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote +// developers + +#if USE_MONERO + +#pragma once + +#include +#include + +static inline uint64_t hi_dword(uint64_t val) { + return val >> 32; +} + +static inline uint64_t lo_dword(uint64_t val) { + return val & 0xFFFFFFFF; +} + +static inline uint64_t mul128(uint64_t multiplier, uint64_t multiplicand, uint64_t* product_hi) { + // multiplier = ab = a * 2^32 + b + // multiplicand = cd = c * 2^32 + d + // ab * cd = a * c * 2^64 + (a * d + b * c) * 2^32 + b * d + uint64_t a = hi_dword(multiplier); + uint64_t b = lo_dword(multiplier); + uint64_t c = hi_dword(multiplicand); + uint64_t d = lo_dword(multiplicand); + + uint64_t ac = a * c; + uint64_t ad = a * d; + uint64_t bc = b * c; + uint64_t bd = b * d; + + uint64_t adbc = ad + bc; + uint64_t adbc_carry = adbc < ad ? 1 : 0; + + // multiplier * multiplicand = product_hi * 2^64 + product_lo + uint64_t product_lo = bd + (adbc << 32); + uint64_t product_lo_carry = product_lo < bd ? 1 : 0; + *product_hi = ac + (adbc >> 32) + (adbc_carry << 32) + product_lo_carry; + assert(ac <= *product_hi); + + return product_lo; +} + +#define SWAP64(x) \ + ((((uint64_t)(x)&0x00000000000000ff) << 56) | (((uint64_t)(x)&0x000000000000ff00) << 40) | \ + (((uint64_t)(x)&0x0000000000ff0000) << 24) | (((uint64_t)(x)&0x00000000ff000000) << 8) | \ + (((uint64_t)(x)&0x000000ff00000000) >> 8) | (((uint64_t)(x)&0x0000ff0000000000) >> 24) | \ + (((uint64_t)(x)&0x00ff000000000000) >> 40) | (((uint64_t)(x)&0xff00000000000000) >> 56)) + +#endif // USE_MONERO \ No newline at end of file diff --git a/applications/external/flipbip/lib/crypto/monero/monero.h b/applications/external/flipbip/lib/crypto/monero/monero.h new file mode 100644 index 0000000000..96ff9dd5c7 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/monero/monero.h @@ -0,0 +1,24 @@ +// +// Created by Dusan Klinec on 10/05/2018. +// + +#if USE_MONERO + +#ifndef TREZOR_CRYPTO_MONERO_H +#define TREZOR_CRYPTO_MONERO_H + +#if !USE_MONERO +#error "Compile with -DUSE_MONERO=1" +#endif + +#if !USE_KECCAK +#error "Compile with -DUSE_KECCAK=1" +#endif + +#include "base58.h" +#include "serialize.h" +#include "xmr.h" + +#endif // TREZOR_CRYPTO_MONERO_H + +#endif // USE_MONERO diff --git a/applications/external/flipbip/lib/crypto/monero/serialize.c b/applications/external/flipbip/lib/crypto/monero/serialize.c new file mode 100644 index 0000000000..0023c9699d --- /dev/null +++ b/applications/external/flipbip/lib/crypto/monero/serialize.c @@ -0,0 +1,57 @@ +// +// Created by Dusan Klinec on 02/05/2018. +// + +#if USE_MONERO + +#include "serialize.h" + +int xmr_size_varint(uint64_t num) { + int ctr = 1; + while(num >= 0x80) { + ++ctr; + num >>= 7; + } + return ctr; +} + +int xmr_write_varint(uint8_t* buff, size_t buff_size, uint64_t num) { + unsigned ctr = 0; + while(num >= 0x80 && ctr < buff_size) { + *buff = (uint8_t)(((num)&0x7f) | 0x80); + ++buff; + ++ctr; + num >>= 7; + } + + /* writes the last one to dest */ + if(ctr < buff_size) { + *buff = (uint8_t)num; + ++ctr; + } + return ctr <= buff_size ? (int)ctr : -1; +} + +int xmr_read_varint(uint8_t* buff, size_t buff_size, uint64_t* val) { + unsigned read = 0; + int finished_ok = 0; + *val = 0; + + for(int shift = 0; read < buff_size; shift += 7, ++read) { + uint8_t byte = buff[read]; + if((byte == 0 && shift != 0) || (shift >= 63 && byte > 1)) { + return -1; + } + + *val |= (uint64_t)(byte & 0x7f) << shift; + + /* If there is no next */ + if((byte & 0x80) == 0) { + finished_ok = 1; + break; + } + } + return finished_ok ? (int)read + 1 : -2; +} + +#endif // USE_MONERO diff --git a/applications/external/flipbip/lib/crypto/monero/serialize.h b/applications/external/flipbip/lib/crypto/monero/serialize.h new file mode 100644 index 0000000000..9f89036943 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/monero/serialize.h @@ -0,0 +1,19 @@ +// +// Created by Dusan Klinec on 02/05/2018. +// + +#if USE_MONERO + +#ifndef TREZOR_XMR_SERIALIZE_H +#define TREZOR_XMR_SERIALIZE_H + +#include +#include + +int xmr_size_varint(uint64_t num); +int xmr_write_varint(uint8_t* buff, size_t buff_size, uint64_t num); +int xmr_read_varint(uint8_t* buff, size_t buff_size, uint64_t* val); + +#endif // TREZOR_XMR_SERIALIZE_H + +#endif // USE_MONERO diff --git a/applications/external/flipbip/lib/crypto/monero/xmr.c b/applications/external/flipbip/lib/crypto/monero/xmr.c new file mode 100644 index 0000000000..917200624d --- /dev/null +++ b/applications/external/flipbip/lib/crypto/monero/xmr.c @@ -0,0 +1,202 @@ +// +// Created by Dusan Klinec on 10/05/2018. +// + +#if USE_MONERO + +#include "xmr.h" +#include "../byte_order.h" +#include "int_util.h" +#include "../rand.h" +#include "serialize.h" + +const ge25519 ALIGN(16) xmr_h = { + {0x1861ec7, + 0x1ceac77, + 0x2f11626, + 0x1f261d3, + 0x346107c, + 0x06d8c4a, + 0x254201d, + 0x1675c09, + 0x1301c3f, + 0x0211d73}, + {0x326feb4, + 0x12e30cc, + 0x0cf54b4, + 0x1117305, + 0x318f5d5, + 0x06cf754, + 0x2e578a1, + 0x1daf058, + 0x34430a1, + 0x04410e9}, + {0x0fde4d2, + 0x0774049, + 0x22ca951, + 0x05aec2b, + 0x07a36a5, + 0x1394f13, + 0x3c5385c, + 0x1adb924, + 0x2b6c581, + 0x0a55fa4}, + {0x24517f7, + 0x05ee936, + 0x3acf5d9, + 0x14b08aa, + 0x3363738, + 0x1051745, + 0x360601e, + 0x0f3f2c9, + 0x1ead2cd, + 0x1d3e3df}}; + +void ge25519_set_xmr_h(ge25519* r) { + ge25519_copy(r, &xmr_h); +} + +void xmr_random_scalar(bignum256modm m) { + unsigned char buff[32] = {0}; + random_buffer(buff, sizeof(buff)); + expand256_modm(m, buff, sizeof(buff)); +} + +void xmr_fast_hash(uint8_t* hash, const void* data, size_t length) { + hasher_Raw(HASHER_SHA3K, data, length, hash); +} + +void xmr_hasher_init(Hasher* hasher) { + hasher_Init(hasher, HASHER_SHA3K); +} + +void xmr_hasher_update(Hasher* hasher, const void* data, size_t length) { + hasher_Update(hasher, data, length); +} + +void xmr_hasher_final(Hasher* hasher, uint8_t* hash) { + hasher_Final(hasher, hash); +} + +void xmr_hasher_copy(Hasher* dst, const Hasher* src) { + memcpy(dst, src, sizeof(Hasher)); +} + +void xmr_hash_to_scalar(bignum256modm r, const void* data, size_t length) { + uint8_t hash[HASHER_DIGEST_LENGTH] = {0}; + hasher_Raw(HASHER_SHA3K, data, length, hash); + expand256_modm(r, hash, HASHER_DIGEST_LENGTH); +} + +void xmr_hash_to_ec(ge25519* P, const void* data, size_t length) { + ge25519 point2 = {0}; + uint8_t hash[HASHER_DIGEST_LENGTH] = {0}; + hasher_Raw(HASHER_SHA3K, data, length, hash); + + ge25519_fromfe_frombytes_vartime(&point2, hash); + ge25519_mul8(P, &point2); +} + +void xmr_derivation_to_scalar(bignum256modm s, const ge25519* p, uint32_t output_index) { + uint8_t buff[32 + 8] = {0}; + ge25519_pack(buff, p); + int written = xmr_write_varint(buff + 32, 8, output_index); + xmr_hash_to_scalar(s, buff, 32u + written); +} + +void xmr_generate_key_derivation(ge25519* r, const ge25519* A, const bignum256modm b) { + ge25519 bA = {0}; + ge25519_scalarmult(&bA, A, b); + ge25519_mul8(r, &bA); +} + +void xmr_derive_private_key( + bignum256modm s, + const ge25519* deriv, + uint32_t idx, + const bignum256modm base) { + xmr_derivation_to_scalar(s, deriv, idx); + add256_modm(s, s, base); +} + +void xmr_derive_public_key(ge25519* r, const ge25519* deriv, uint32_t idx, const ge25519* base) { + bignum256modm s = {0}; + ge25519 p2 = {0}; + + xmr_derivation_to_scalar(s, deriv, idx); + ge25519_scalarmult_base_niels(&p2, ge25519_niels_base_multiples, s); + ge25519_add(r, base, &p2, 0); +} + +void xmr_add_keys2(ge25519* r, const bignum256modm a, const bignum256modm b, const ge25519* B) { + // aG + bB, G is basepoint + ge25519 aG = {0}, bB = {0}; + ge25519_scalarmult_base_niels(&aG, ge25519_niels_base_multiples, a); + ge25519_scalarmult(&bB, B, b); + ge25519_add(r, &aG, &bB, 0); +} + +void xmr_add_keys2_vartime( + ge25519* r, + const bignum256modm a, + const bignum256modm b, + const ge25519* B) { + // aG + bB, G is basepoint + ge25519_double_scalarmult_vartime(r, B, b, a); +} + +void xmr_add_keys3( + ge25519* r, + const bignum256modm a, + const ge25519* A, + const bignum256modm b, + const ge25519* B) { + // aA + bB + ge25519 aA = {0}, bB = {0}; + ge25519_scalarmult(&aA, A, a); + ge25519_scalarmult(&bB, B, b); + ge25519_add(r, &aA, &bB, 0); +} + +void xmr_add_keys3_vartime( + ge25519* r, + const bignum256modm a, + const ge25519* A, + const bignum256modm b, + const ge25519* B) { + // aA + bB + ge25519_double_scalarmult_vartime2(r, A, a, B, b); +} + +void xmr_get_subaddress_secret_key( + bignum256modm r, + uint32_t major, + uint32_t minor, + const bignum256modm m) { + const char prefix[] = "SubAddr"; + unsigned char buff[32] = {0}; + contract256_modm(buff, m); + + char data[sizeof(prefix) + sizeof(buff) + 2 * sizeof(uint32_t)] = {0}; + memcpy(data, prefix, sizeof(prefix)); + memcpy(data + sizeof(prefix), buff, sizeof(buff)); + +#if BYTE_ORDER == BIG_ENDIAN + REVERSE32(major, major); + REVERSE32(minor, minor); +#endif + + memcpy(data + sizeof(prefix) + sizeof(buff), &major, sizeof(uint32_t)); + memcpy(data + sizeof(prefix) + sizeof(buff) + sizeof(uint32_t), &minor, sizeof(uint32_t)); + + xmr_hash_to_scalar(r, data, sizeof(data)); +} + +void xmr_gen_c(ge25519* r, const bignum256modm a, uint64_t amount) { + // C = aG + bH + bignum256modm b = {0}; + set256_modm(b, amount); + xmr_add_keys2(r, a, b, &xmr_h); +} + +#endif // USE_MONERO diff --git a/applications/external/flipbip/lib/crypto/monero/xmr.h b/applications/external/flipbip/lib/crypto/monero/xmr.h new file mode 100644 index 0000000000..16c2aec5e2 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/monero/xmr.h @@ -0,0 +1,93 @@ +// +// Created by Dusan Klinec on 10/05/2018. +// + +#if USE_MONERO + +#ifndef TREZOR_CRYPTO_XMR_H +#define TREZOR_CRYPTO_XMR_H + +#include "../ed25519_donna/ed25519_donna.h" +#include "../hasher.h" + +extern const ge25519 ALIGN(16) xmr_h; + +typedef unsigned char xmr_key_t[32]; + +typedef struct xmr_ctkey { + xmr_key_t dest; + xmr_key_t mask; +} xmr_ctkey_t; + +/* sets H point to r */ +void ge25519_set_xmr_h(ge25519* r); + +/* random scalar value */ +void xmr_random_scalar(bignum256modm m); + +/* cn_fast_hash */ +void xmr_fast_hash(uint8_t* hash, const void* data, size_t length); + +/* incremental hashing wrappers */ +void xmr_hasher_init(Hasher* hasher); +void xmr_hasher_update(Hasher* hasher, const void* data, size_t length); +void xmr_hasher_final(Hasher* hasher, uint8_t* hash); +void xmr_hasher_copy(Hasher* dst, const Hasher* src); + +/* H_s(buffer) */ +void xmr_hash_to_scalar(bignum256modm r, const void* data, size_t length); + +/* H_p(buffer) */ +void xmr_hash_to_ec(ge25519* P, const void* data, size_t length); + +/* derivation to scalar value */ +void xmr_derivation_to_scalar(bignum256modm s, const ge25519* p, uint32_t output_index); + +/* derivation */ +void xmr_generate_key_derivation(ge25519* r, const ge25519* A, const bignum256modm b); + +/* H_s(derivation || varint(output_index)) + base */ +void xmr_derive_private_key( + bignum256modm s, + const ge25519* deriv, + uint32_t idx, + const bignum256modm base); + +/* H_s(derivation || varint(output_index))G + base */ +void xmr_derive_public_key(ge25519* r, const ge25519* deriv, uint32_t idx, const ge25519* base); + +/* aG + bB, G is basepoint */ +void xmr_add_keys2(ge25519* r, const bignum256modm a, const bignum256modm b, const ge25519* B); +void xmr_add_keys2_vartime( + ge25519* r, + const bignum256modm a, + const bignum256modm b, + const ge25519* B); + +/* aA + bB */ +void xmr_add_keys3( + ge25519* r, + const bignum256modm a, + const ge25519* A, + const bignum256modm b, + const ge25519* B); +void xmr_add_keys3_vartime( + ge25519* r, + const bignum256modm a, + const ge25519* A, + const bignum256modm b, + const ge25519* B); + +/* subaddress secret */ +void xmr_get_subaddress_secret_key( + bignum256modm r, + uint32_t major, + uint32_t minor, + const bignum256modm m); + +/* Generates Pedersen commitment C = aG + bH */ +void xmr_gen_c(ge25519* r, const bignum256modm a, uint64_t amount); + +#endif // TREZOR_CRYPTO_XMR_H + +#endif // USE_MONERO diff --git a/applications/external/flipbip/lib/crypto/nem.c b/applications/external/flipbip/lib/crypto/nem.c new file mode 100644 index 0000000000..391c1e6fca --- /dev/null +++ b/applications/external/flipbip/lib/crypto/nem.c @@ -0,0 +1,603 @@ +/** + * Copyright (c) 2017 Saleem Rashid + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, E1PRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "options.h" + +#if USE_NEM + +#include "nem.h" + +#include + +#include "base32.h" +#include "ed25519_donna/ed25519_keccak.h" +#include "memzero.h" +#include "ripemd160.h" +#include "sha3.h" + +#define CAN_WRITE(NEEDED) ((ctx->offset + (NEEDED)) <= ctx->size) + +#define SERIALIZE_U32(DATA) \ + do { \ + if(!nem_write_u32(ctx, (DATA))) return false; \ + } while(0) +#define SERIALIZE_U64(DATA) \ + do { \ + if(!nem_write_u64(ctx, (DATA))) return false; \ + } while(0) +#define SERIALIZE_TAGGED(DATA, LENGTH) \ + do { \ + if(!nem_write_tagged(ctx, (DATA), (LENGTH))) return false; \ + } while(0) + +const char* nem_network_name(uint8_t network) { + switch(network) { + case NEM_NETWORK_MAINNET: + return "NEM Mainnet"; + case NEM_NETWORK_TESTNET: + return "NEM Testnet"; + case NEM_NETWORK_MIJIN: + return "Mijin"; + default: + return NULL; + } +} + +static inline bool + nem_write_checked(nem_transaction_ctx* ctx, const uint8_t* data, uint32_t length) { + if(!CAN_WRITE(length)) { + return false; + } + + memcpy(&ctx->buffer[ctx->offset], data, length); + ctx->offset += length; + return true; +} + +static inline bool nem_write_u32(nem_transaction_ctx* ctx, uint32_t data) { + if(!CAN_WRITE(4)) { + return false; + } + + ctx->buffer[ctx->offset++] = (data >> 0) & 0xff; + ctx->buffer[ctx->offset++] = (data >> 8) & 0xff; + ctx->buffer[ctx->offset++] = (data >> 16) & 0xff; + ctx->buffer[ctx->offset++] = (data >> 24) & 0xff; + + return true; +} + +static inline bool nem_write_u64(nem_transaction_ctx* ctx, uint64_t data) { + SERIALIZE_U32((data >> 0) & 0xffffffff); + SERIALIZE_U32((data >> 32) & 0xffffffff); + + return true; +} + +static inline bool + nem_write_tagged(nem_transaction_ctx* ctx, const uint8_t* data, uint32_t length) { + SERIALIZE_U32(length); + + return nem_write_checked(ctx, data, length); +} + +static inline bool + nem_write_mosaic_str(nem_transaction_ctx* ctx, const char* name, const char* value) { + uint32_t name_length = strlen(name); + uint32_t value_length = strlen(value); + + SERIALIZE_U32(sizeof(uint32_t) + name_length + sizeof(uint32_t) + value_length); + SERIALIZE_TAGGED((const uint8_t*)name, name_length); + SERIALIZE_TAGGED((const uint8_t*)value, value_length); + + return true; +} + +static inline bool nem_write_mosaic_bool(nem_transaction_ctx* ctx, const char* name, bool value) { + return nem_write_mosaic_str(ctx, name, value ? "true" : "false"); +} + +static inline bool + nem_write_mosaic_u64(nem_transaction_ctx* ctx, const char* name, uint64_t value) { + char buffer[21] = {0}; + + if(bn_format_uint64(value, NULL, NULL, 0, 0, false, 0, buffer, sizeof(buffer)) == 0) { + return false; + } + + return nem_write_mosaic_str(ctx, name, buffer); +} + +void nem_get_address_raw(const ed25519_public_key public_key, uint8_t version, uint8_t* address) { + uint8_t hash[SHA3_256_DIGEST_LENGTH] = {0}; + + /* 1. Perform 256-bit Sha3 on the public key */ + keccak_256(public_key, sizeof(ed25519_public_key), hash); + + /* 2. Perform 160-bit Ripemd of hash resulting from step 1. */ + ripemd160(hash, SHA3_256_DIGEST_LENGTH, &address[1]); + + /* 3. Prepend version byte to Ripemd hash (either 0x68 or 0x98) */ + address[0] = version; + + /* 4. Perform 256-bit Sha3 on the result, take the first four bytes as a + * checksum */ + keccak_256(address, 1 + RIPEMD160_DIGEST_LENGTH, hash); + + /* 5. Concatenate output of step 3 and the checksum from step 4 */ + memcpy(&address[1 + RIPEMD160_DIGEST_LENGTH], hash, 4); + + memzero(hash, sizeof(hash)); +} + +bool nem_get_address(const ed25519_public_key public_key, uint8_t version, char* address) { + uint8_t pubkeyhash[NEM_ADDRESS_SIZE_RAW] = {0}; + + nem_get_address_raw(public_key, version, pubkeyhash); + + char* ret = base32_encode( + pubkeyhash, sizeof(pubkeyhash), address, NEM_ADDRESS_SIZE + 1, BASE32_ALPHABET_RFC4648); + + memzero(pubkeyhash, sizeof(pubkeyhash)); + return (ret != NULL); +} + +bool nem_validate_address_raw(const uint8_t* address, uint8_t network) { + if(!nem_network_name(network) || address[0] != network) { + return false; + } + + uint8_t hash[SHA3_256_DIGEST_LENGTH] = {0}; + + keccak_256(address, 1 + RIPEMD160_DIGEST_LENGTH, hash); + bool valid = (memcmp(&address[1 + RIPEMD160_DIGEST_LENGTH], hash, 4) == 0); + + memzero(hash, sizeof(hash)); + return valid; +} + +bool nem_validate_address(const char* address, uint8_t network) { + uint8_t pubkeyhash[NEM_ADDRESS_SIZE_RAW] = {0}; + + if(strlen(address) != NEM_ADDRESS_SIZE) { + return false; + } + + uint8_t* ret = base32_decode( + address, NEM_ADDRESS_SIZE, pubkeyhash, sizeof(pubkeyhash), BASE32_ALPHABET_RFC4648); + bool valid = (ret != NULL) && nem_validate_address_raw(pubkeyhash, network); + + memzero(pubkeyhash, sizeof(pubkeyhash)); + return valid; +} + +void nem_transaction_start( + nem_transaction_ctx* ctx, + const ed25519_public_key public_key, + uint8_t* buffer, + size_t size) { + memcpy(ctx->public_key, public_key, sizeof(ctx->public_key)); + + ctx->buffer = buffer; + ctx->offset = 0; + ctx->size = size; +} + +size_t nem_transaction_end( + nem_transaction_ctx* ctx, + const ed25519_secret_key private_key, + ed25519_signature signature) { + if(private_key != NULL && signature != NULL) { + ed25519_sign_keccak(ctx->buffer, ctx->offset, private_key, signature); + } + + return ctx->offset; +} + +bool nem_transaction_write_common( + nem_transaction_ctx* ctx, + uint32_t type, + uint32_t version, + uint32_t timestamp, + const ed25519_public_key signer, + uint64_t fee, + uint32_t deadline) { + SERIALIZE_U32(type); + SERIALIZE_U32(version); + SERIALIZE_U32(timestamp); + SERIALIZE_TAGGED(signer, sizeof(ed25519_public_key)); + SERIALIZE_U64(fee); + SERIALIZE_U32(deadline); + + return true; +} + +bool nem_transaction_create_transfer( + nem_transaction_ctx* ctx, + uint8_t network, + uint32_t timestamp, + const ed25519_public_key signer, + uint64_t fee, + uint32_t deadline, + const char* recipient, + uint64_t amount, + const uint8_t* payload, + uint32_t length, + bool encrypted, + uint32_t mosaics) { + if(!signer) { + signer = ctx->public_key; + } + + if(!payload) { + length = 0; + } + + bool ret = nem_transaction_write_common( + ctx, + NEM_TRANSACTION_TYPE_TRANSFER, + (uint32_t)network << 24 | (mosaics ? 2 : 1), + timestamp, + signer, + fee, + deadline); + if(!ret) return false; + + SERIALIZE_TAGGED((const uint8_t*)recipient, NEM_ADDRESS_SIZE); + SERIALIZE_U64(amount); + + if(length) { + SERIALIZE_U32(sizeof(uint32_t) + sizeof(uint32_t) + length); + SERIALIZE_U32(encrypted ? 0x02 : 0x01); + SERIALIZE_TAGGED(payload, length); + } else { + SERIALIZE_U32(0); + } + + if(mosaics) { + SERIALIZE_U32(mosaics); + } + + return true; +} + +bool nem_transaction_write_mosaic( + nem_transaction_ctx* ctx, + const char* namespace, + const char* mosaic, + uint64_t quantity) { + size_t namespace_length = strlen(namespace); + size_t mosaic_length = strlen(mosaic); + size_t identifier_length = + sizeof(uint32_t) + namespace_length + sizeof(uint32_t) + mosaic_length; + + SERIALIZE_U32(sizeof(uint32_t) + sizeof(uint64_t) + identifier_length); + SERIALIZE_U32(identifier_length); + SERIALIZE_TAGGED((const uint8_t*)namespace, namespace_length); + SERIALIZE_TAGGED((const uint8_t*)mosaic, mosaic_length); + SERIALIZE_U64(quantity); + + return true; +} + +bool nem_transaction_create_multisig( + nem_transaction_ctx* ctx, + uint8_t network, + uint32_t timestamp, + const ed25519_public_key signer, + uint64_t fee, + uint32_t deadline, + const nem_transaction_ctx* inner) { + if(!signer) { + signer = ctx->public_key; + } + + bool ret = nem_transaction_write_common( + ctx, + NEM_TRANSACTION_TYPE_MULTISIG, + (uint32_t)network << 24 | 1, + timestamp, + signer, + fee, + deadline); + if(!ret) return false; + + SERIALIZE_TAGGED(inner->buffer, inner->offset); + + return true; +} + +bool nem_transaction_create_multisig_signature( + nem_transaction_ctx* ctx, + uint8_t network, + uint32_t timestamp, + const ed25519_public_key signer, + uint64_t fee, + uint32_t deadline, + const nem_transaction_ctx* inner) { + if(!signer) { + signer = ctx->public_key; + } + + bool ret = nem_transaction_write_common( + ctx, + NEM_TRANSACTION_TYPE_MULTISIG_SIGNATURE, + (uint32_t)network << 24 | 1, + timestamp, + signer, + fee, + deadline); + if(!ret) return false; + + char address[NEM_ADDRESS_SIZE + 1] = {0}; + nem_get_address(inner->public_key, network, address); + + uint8_t hash[SHA3_256_DIGEST_LENGTH] = {0}; + keccak_256(inner->buffer, inner->offset, hash); + + SERIALIZE_U32(sizeof(uint32_t) + SHA3_256_DIGEST_LENGTH); + SERIALIZE_TAGGED(hash, SHA3_256_DIGEST_LENGTH); + SERIALIZE_TAGGED((const uint8_t*)address, NEM_ADDRESS_SIZE); + + return true; +} + +bool nem_transaction_create_provision_namespace( + nem_transaction_ctx* ctx, + uint8_t network, + uint32_t timestamp, + const ed25519_public_key signer, + uint64_t fee, + uint32_t deadline, + const char* namespace, + const char* parent, + const char* rental_sink, + uint64_t rental_fee) { + if(!signer) { + signer = ctx->public_key; + } + + bool ret = nem_transaction_write_common( + ctx, + NEM_TRANSACTION_TYPE_PROVISION_NAMESPACE, + (uint32_t)network << 24 | 1, + timestamp, + signer, + fee, + deadline); + if(!ret) return false; + + if(parent) { + SERIALIZE_TAGGED((const uint8_t*)rental_sink, NEM_ADDRESS_SIZE); + SERIALIZE_U64(rental_fee); + SERIALIZE_TAGGED((const uint8_t*)namespace, strlen(namespace)); + SERIALIZE_TAGGED((const uint8_t*)parent, strlen(parent)); + } else { + SERIALIZE_TAGGED((const uint8_t*)rental_sink, NEM_ADDRESS_SIZE); + SERIALIZE_U64(rental_fee); + SERIALIZE_TAGGED((const uint8_t*)namespace, strlen(namespace)); + SERIALIZE_U32(0xffffffff); + } + + return true; +} + +bool nem_transaction_create_mosaic_creation( + nem_transaction_ctx* ctx, + uint8_t network, + uint32_t timestamp, + const ed25519_public_key signer, + uint64_t fee, + uint32_t deadline, + const char* namespace, + const char* mosaic, + const char* description, + uint32_t divisibility, + uint64_t supply, + bool mutable_supply, + bool transferable, + uint32_t levy_type, + uint64_t levy_fee, + const char* levy_address, + const char* levy_namespace, + const char* levy_mosaic, + const char* creation_sink, + uint64_t creation_fee) { + if(!signer) { + signer = ctx->public_key; + } + + bool ret = nem_transaction_write_common( + ctx, + NEM_TRANSACTION_TYPE_MOSAIC_CREATION, + (uint32_t)network << 24 | 1, + timestamp, + signer, + fee, + deadline); + if(!ret) return false; + + size_t namespace_length = strlen(namespace); + size_t mosaic_length = strlen(mosaic); + size_t identifier_length = + sizeof(uint32_t) + namespace_length + sizeof(uint32_t) + mosaic_length; + + // This length will be rewritten later on + nem_transaction_ctx state = {0}; + memcpy(&state, ctx, sizeof(state)); + + SERIALIZE_U32(0); + SERIALIZE_TAGGED(signer, sizeof(ed25519_public_key)); + SERIALIZE_U32(identifier_length); + SERIALIZE_TAGGED((const uint8_t*)namespace, namespace_length); + SERIALIZE_TAGGED((const uint8_t*)mosaic, mosaic_length); + SERIALIZE_TAGGED((const uint8_t*)description, strlen(description)); + SERIALIZE_U32(4); // Number of properties + + if(!nem_write_mosaic_u64(ctx, "divisibility", divisibility)) return false; + if(!nem_write_mosaic_u64(ctx, "initialSupply", supply)) return false; + if(!nem_write_mosaic_bool(ctx, "supplyMutable", mutable_supply)) return false; + if(!nem_write_mosaic_bool(ctx, "transferable", transferable)) return false; + + if(levy_type) { + size_t levy_namespace_length = strlen(levy_namespace); + size_t levy_mosaic_length = strlen(levy_mosaic); + size_t levy_identifier_length = + sizeof(uint32_t) + levy_namespace_length + sizeof(uint32_t) + levy_mosaic_length; + + SERIALIZE_U32( + sizeof(uint32_t) + sizeof(uint32_t) + NEM_ADDRESS_SIZE + sizeof(uint32_t) + + levy_identifier_length + sizeof(uint64_t)); + SERIALIZE_U32(levy_type); + SERIALIZE_TAGGED((const uint8_t*)levy_address, NEM_ADDRESS_SIZE); + SERIALIZE_U32(levy_identifier_length); + SERIALIZE_TAGGED((const uint8_t*)levy_namespace, levy_namespace_length); + SERIALIZE_TAGGED((const uint8_t*)levy_mosaic, levy_mosaic_length); + SERIALIZE_U64(levy_fee); + } else { + SERIALIZE_U32(0); + } + + // Rewrite length + nem_write_u32(&state, ctx->offset - state.offset - sizeof(uint32_t)); + + SERIALIZE_TAGGED((const uint8_t*)creation_sink, NEM_ADDRESS_SIZE); + SERIALIZE_U64(creation_fee); + + return true; +} + +bool nem_transaction_create_mosaic_supply_change( + nem_transaction_ctx* ctx, + uint8_t network, + uint32_t timestamp, + const ed25519_public_key signer, + uint64_t fee, + uint32_t deadline, + const char* namespace, + const char* mosaic, + uint32_t type, + uint64_t delta) { + if(!signer) { + signer = ctx->public_key; + } + + bool ret = nem_transaction_write_common( + ctx, + NEM_TRANSACTION_TYPE_MOSAIC_SUPPLY_CHANGE, + (uint32_t)network << 24 | 1, + timestamp, + signer, + fee, + deadline); + if(!ret) return false; + + size_t namespace_length = strlen(namespace); + size_t mosaic_length = strlen(mosaic); + size_t identifier_length = + sizeof(uint32_t) + namespace_length + sizeof(uint32_t) + mosaic_length; + + SERIALIZE_U32(identifier_length); + SERIALIZE_TAGGED((const uint8_t*)namespace, namespace_length); + SERIALIZE_TAGGED((const uint8_t*)mosaic, mosaic_length); + SERIALIZE_U32(type); + SERIALIZE_U64(delta); + + return true; +} + +bool nem_transaction_create_aggregate_modification( + nem_transaction_ctx* ctx, + uint8_t network, + uint32_t timestamp, + const ed25519_public_key signer, + uint64_t fee, + uint32_t deadline, + uint32_t modifications, + bool relative_change) { + if(!signer) { + signer = ctx->public_key; + } + + bool ret = nem_transaction_write_common( + ctx, + NEM_TRANSACTION_TYPE_AGGREGATE_MODIFICATION, + (uint32_t)network << 24 | (relative_change ? 2 : 1), + timestamp, + signer, + fee, + deadline); + if(!ret) return false; + + SERIALIZE_U32(modifications); + + return true; +} + +bool nem_transaction_write_cosignatory_modification( + nem_transaction_ctx* ctx, + uint32_t type, + const ed25519_public_key cosignatory) { + SERIALIZE_U32(sizeof(uint32_t) + sizeof(uint32_t) + sizeof(ed25519_public_key)); + SERIALIZE_U32(type); + SERIALIZE_TAGGED(cosignatory, sizeof(ed25519_public_key)); + + return true; +} + +bool nem_transaction_write_minimum_cosignatories(nem_transaction_ctx* ctx, int32_t relative_change) { + SERIALIZE_U32(sizeof(uint32_t)); + SERIALIZE_U32((uint32_t)relative_change); + + return true; +} + +bool nem_transaction_create_importance_transfer( + nem_transaction_ctx* ctx, + uint8_t network, + uint32_t timestamp, + const ed25519_public_key signer, + uint64_t fee, + uint32_t deadline, + uint32_t mode, + const ed25519_public_key remote) { + if(!signer) { + signer = ctx->public_key; + } + + bool ret = nem_transaction_write_common( + ctx, + NEM_TRANSACTION_TYPE_IMPORTANCE_TRANSFER, + (uint32_t)network << 24 | 1, + timestamp, + signer, + fee, + deadline); + if(!ret) return false; + + SERIALIZE_U32(mode); + SERIALIZE_TAGGED(remote, sizeof(ed25519_public_key)); + + return true; +} + +#endif // USE_NEM diff --git a/applications/external/flipbip/lib/crypto/nem.h b/applications/external/flipbip/lib/crypto/nem.h new file mode 100644 index 0000000000..38b7530d7a --- /dev/null +++ b/applications/external/flipbip/lib/crypto/nem.h @@ -0,0 +1,216 @@ +/** + * Copyright (c) 2017 Saleem Rashid + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "../options.h" + +#if USE_NEM + +#ifndef __NEM_H__ +#define __NEM_H__ + +#include +#include +#include + +#include "bip32.h" +#include "ed25519_donna/ed25519.h" + +#define NEM_LEVY_PERCENTILE_DIVISOR 4 +#define NEM_MAX_DIVISIBILITY 6 +#define NEM_MAX_SUPPLY 9000000000 + +#define NEM_NETWORK_MAINNET 0x68 +#define NEM_NETWORK_TESTNET 0x98 +#define NEM_NETWORK_MIJIN 0x60 + +#define NEM_ADDRESS_SIZE 40 +#define NEM_ADDRESS_SIZE_RAW 25 + +#define NEM_TRANSACTION_TYPE_TRANSFER 0x0101 +#define NEM_TRANSACTION_TYPE_IMPORTANCE_TRANSFER 0x0801 +#define NEM_TRANSACTION_TYPE_AGGREGATE_MODIFICATION 0x1001 +#define NEM_TRANSACTION_TYPE_MULTISIG_SIGNATURE 0x1002 +#define NEM_TRANSACTION_TYPE_MULTISIG 0x1004 +#define NEM_TRANSACTION_TYPE_PROVISION_NAMESPACE 0x2001 +#define NEM_TRANSACTION_TYPE_MOSAIC_CREATION 0x4001 +#define NEM_TRANSACTION_TYPE_MOSAIC_SUPPLY_CHANGE 0x4002 + +#define NEM_SALT_SIZE sizeof(ed25519_public_key) + +#define NEM_ENCRYPTED_SIZE(size) (((size) + AES_BLOCK_SIZE) / AES_BLOCK_SIZE * AES_BLOCK_SIZE) +#define NEM_ENCRYPTED_PAYLOAD_SIZE(size) \ + (AES_BLOCK_SIZE + NEM_SALT_SIZE + NEM_ENCRYPTED_SIZE(size)) + +#define _NEM_PADDING_SIZE(buffer, size) ((buffer)[(size)-1]) +#define NEM_PADDING_SIZE(buffer, size) \ + (_NEM_PADDING_SIZE(buffer, size) > (size) ? (size) : _NEM_PADDING_SIZE(buffer, size)) + +#define NEM_DECRYPTED_SIZE(buffer, size) ((size)-NEM_PADDING_SIZE(buffer, size)) + +typedef struct { + ed25519_public_key public_key; + uint8_t* buffer; + size_t offset; + size_t size; +} nem_transaction_ctx; + +const char* nem_network_name(uint8_t network); + +void nem_get_address_raw(const ed25519_public_key public_key, uint8_t version, uint8_t* address); +bool nem_get_address(const ed25519_public_key public_key, uint8_t version, char* address); + +bool nem_validate_address_raw(const uint8_t* address, uint8_t network); +bool nem_validate_address(const char* address, uint8_t network); + +void nem_transaction_start( + nem_transaction_ctx* ctx, + const ed25519_public_key public_key, + uint8_t* buffer, + size_t size); +size_t nem_transaction_end( + nem_transaction_ctx* ctx, + const ed25519_secret_key private_key, + ed25519_signature signature); + +bool nem_transaction_write_common( + nem_transaction_ctx* context, + uint32_t type, + uint32_t version, + uint32_t timestamp, + const ed25519_public_key signer, + uint64_t fee, + uint32_t deadline); + +bool nem_transaction_create_transfer( + nem_transaction_ctx* context, + uint8_t network, + uint32_t timestamp, + const ed25519_public_key signer, + uint64_t fee, + uint32_t deadline, + const char* recipient, + uint64_t amount, + const uint8_t* payload, + uint32_t length, + bool encrypted, + uint32_t mosaics); + +bool nem_transaction_write_mosaic( + nem_transaction_ctx* ctx, + const char* namespace, + const char* mosaic, + uint64_t quantity); + +bool nem_transaction_create_multisig( + nem_transaction_ctx* ctx, + uint8_t network, + uint32_t timestamp, + const ed25519_public_key signer, + uint64_t fee, + uint32_t deadline, + const nem_transaction_ctx* inner); + +bool nem_transaction_create_multisig_signature( + nem_transaction_ctx* ctx, + uint8_t network, + uint32_t timestamp, + const ed25519_public_key signer, + uint64_t fee, + uint32_t deadline, + const nem_transaction_ctx* inner); + +bool nem_transaction_create_provision_namespace( + nem_transaction_ctx* ctx, + uint8_t network, + uint32_t timestamp, + const ed25519_public_key signer, + uint64_t fee, + uint32_t deadline, + const char* namespace, + const char* parent, + const char* rental_sink, + uint64_t rental_fee); + +bool nem_transaction_create_mosaic_creation( + nem_transaction_ctx* ctx, + uint8_t network, + uint32_t timestamp, + const ed25519_public_key signer, + uint64_t fee, + uint32_t deadline, + const char* namespace, + const char* mosaic, + const char* description, + uint32_t divisibility, + uint64_t supply, + bool mutable_supply, + bool transferable, + uint32_t levy_type, + uint64_t levy_fee, + const char* levy_address, + const char* levy_namespace, + const char* levy_mosaic, + const char* creation_sink, + uint64_t creation_fee); + +bool nem_transaction_create_mosaic_supply_change( + nem_transaction_ctx* ctx, + uint8_t network, + uint32_t timestamp, + const ed25519_public_key signer, + uint64_t fee, + uint32_t deadline, + const char* namespace, + const char* mosaic, + uint32_t type, + uint64_t delta); + +bool nem_transaction_create_aggregate_modification( + nem_transaction_ctx* ctx, + uint8_t network, + uint32_t timestamp, + const ed25519_public_key signer, + uint64_t fee, + uint32_t deadline, + uint32_t modifications, + bool relative_change); + +bool nem_transaction_write_cosignatory_modification( + nem_transaction_ctx* ctx, + uint32_t type, + const ed25519_public_key cosignatory); + +bool nem_transaction_write_minimum_cosignatories(nem_transaction_ctx* ctx, int32_t relative_change); + +bool nem_transaction_create_importance_transfer( + nem_transaction_ctx* ctx, + uint8_t network, + uint32_t timestamp, + const ed25519_public_key signer, + uint64_t fee, + uint32_t deadline, + uint32_t mode, + const ed25519_public_key remote); + +#endif + +#endif // USE_NEM diff --git a/applications/external/flipbip/lib/crypto/nist256p1.c b/applications/external/flipbip/lib/crypto/nist256p1.c new file mode 100644 index 0000000000..51dc569cd0 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/nist256p1.c @@ -0,0 +1,114 @@ +/** + * Copyright (c) 2013-2014 Tomas Dzetkulic + * Copyright (c) 2013-2014 Pavol Rusnak + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "nist256p1.h" + +const ecdsa_curve nist256p1 = { + /* .prime */ {/*.val =*/{ + 0x1fffffff, + 0x1fffffff, + 0x1fffffff, + 0x000001ff, + 0x00000000, + 0x00000000, + 0x00040000, + 0x1fe00000, + 0xffffff}}, + + /* G */ + {/*.x =*/{/*.val =*/{ + 0x1898c296, + 0x0509ca2e, + 0x1acce83d, + 0x06fb025b, + 0x040f2770, + 0x1372b1d2, + 0x091fe2f3, + 0x1e5c2588, + 0x6b17d1}}, + /*.y =*/{/*.val =*/{ + 0x17bf51f5, + 0x1db20341, + 0x0c57b3b2, + 0x1c66aed6, + 0x19e162bc, + 0x15a53e07, + 0x1e6e3b9f, + 0x1c5fc34f, + 0x4fe342}}}, + + /* order */ + {/*.val =*/{ + 0x1c632551, + 0x1dce5617, + 0x05e7a13c, + 0x0df55b4e, + 0x1ffffbce, + 0x1fffffff, + 0x0003ffff, + 0x1fe00000, + 0xffffff}}, + + /* order_half */ + {/*.val =*/{ + 0x1e3192a8, + 0x0ee72b0b, + 0x02f3d09e, + 0x06faada7, + 0x1ffffde7, + 0x1fffffff, + 0x0001ffff, + 0x1ff00000, + 0x7fffff}}, + + /* a */ -3, + + /* b */ + {/*.val =*/{ + 0x07d2604b, + 0x1e71e1f1, + 0x14ec3d8e, + 0x1a0d6198, + 0x086bc651, + 0x1eaabb4c, + 0x0f9ecfae, + 0x1b154752, + 0x005ac635}} + +#if USE_PRECOMPUTED_CP + , + /* cp */ + { +#include "nist256p1.table" + } +#endif +}; + +const curve_info nist256p1_info = { + .bip32_name = "Nist256p1 seed", + .params = &nist256p1, + .hasher_base58 = HASHER_SHA2D, + .hasher_sign = HASHER_SHA2D, + .hasher_pubkey = HASHER_SHA2_RIPEMD, + .hasher_script = HASHER_SHA2, +}; diff --git a/applications/external/flipbip/lib/crypto/nist256p1.h b/applications/external/flipbip/lib/crypto/nist256p1.h new file mode 100644 index 0000000000..02d04025ad --- /dev/null +++ b/applications/external/flipbip/lib/crypto/nist256p1.h @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2013-2014 Tomas Dzetkulic + * Copyright (c) 2013-2014 Pavol Rusnak + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __NIST256P1_H__ +#define __NIST256P1_H__ + +#include + +#include "bip32.h" +#include "ecdsa.h" + +extern const ecdsa_curve nist256p1; +extern const curve_info nist256p1_info; + +#endif diff --git a/applications/external/flipbip/lib/crypto/nist256p1.table b/applications/external/flipbip/lib/crypto/nist256p1.table new file mode 100644 index 0000000000..6be01b4d62 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/nist256p1.table @@ -0,0 +1,1664 @@ + { + /* 1*16^0*G: */ + {{{0x1898c296, 0x0509ca2e, 0x1acce83d, 0x06fb025b, 0x040f2770, 0x1372b1d2, 0x091fe2f3, 0x1e5c2588, 0x6b17d1}}, + {{0x17bf51f5, 0x1db20341, 0x0c57b3b2, 0x1c66aed6, 0x19e162bc, 0x15a53e07, 0x1e6e3b9f, 0x1c5fc34f, 0x4fe342}}}, + /* 3*16^0*G: */ + {{{0x06e7fd6c, 0x1a0b30de, 0x0b6a617e, 0x0d6e43df, 0x1f165e6c, 0x17ca8ea5, 0x091323df, 0x1a34c661, 0x5ecbe4}}, + {{0x027d5032, 0x13cd893d, 0x13ee0f66, 0x15606c70, 0x0a2ecd82, 0x03670d32, 0x1df8dd2c, 0x0189331f, 0x873464}}}, + /* 5*16^0*G: */ + {{{0x03d033ed, 0x0aaa506e, 0x16f94908, 0x1905fa3e, 0x08fdfef8, 0x042b0433, 0x034b5e13, 0x0f4a2a28, 0x51590b}}, + {{0x1da16da4, 0x0e85da27, 0x16022234, 0x025e01a9, 0x079260d0, 0x1f9b5fc5, 0x09f62b86, 0x1512094e, 0xe0c17d}}}, + /* 7*16^0*G: */ + {{{0x1187b2a3, 0x00314381, 0x03fbd6cc, 0x13f17150, 0x1fb607ef, 0x18333e00, 0x0d1896ec, 0x0df417ef, 0x8e533b}}, + {{0x01f400b4, 0x0af0d436, 0x0106c871, 0x0e6c6796, 0x1900053c, 0x0fc1d37a, 0x00d9b41a, 0x17bc0663, 0x73eb1d}}}, + /* 9*16^0*G: */ + {{{0x10949ee0, 0x1cf4525c, 0x1b7e2cf5, 0x15971858, 0x1f8729e0, 0x1c6a8eb8, 0x0dc61e24, 0x16dfdbe1, 0xea68d7}}, + {{0x0dd048fa, 0x02d11252, 0x17a08ffa, 0x029fd549, 0x0a0c84d7, 0x054b2547, 0x139e1c05, 0x192e593f, 0x2a2744}}}, + /* 11*16^0*G: */ + {{{0x14bc21d1, 0x199c8e9b, 0x14122fd0, 0x085da04a, 0x01cda167, 0x1bced861, 0x116418e0, 0x16f10769, 0x3ed113}}, + {{0x082a3740, 0x17c777e7, 0x062276b8, 0x1a09b4bd, 0x0c68a090, 0x01d7d27a, 0x02889321, 0x13599899, 0x909920}}}, + /* 13*16^0*G: */ + {{{0x06072c01, 0x070aecea, 0x1ab562a6, 0x1c5096cb, 0x0e2fc792, 0x0ef96c2f, 0x05698601, 0x0f5c1589, 0x177c83}}, + {{0x0fc7bfd8, 0x021ddf17, 0x1ed37ce7, 0x1c298743, 0x14e7226e, 0x08d6da07, 0x15628902, 0x19a9d7d4, 0x63bb58}}}, + /* 15*16^0*G: */ + {{{0x059b9d5f, 0x1b34631f, 0x0e83bc58, 0x075f25bc, 0x08265ae0, 0x1bc4ccc4, 0x0b9eb7ec, 0x18d2e357, 0xf0454d}}, + {{0x0d034f36, 0x1f2ce6f0, 0x0d7e8fd1, 0x16439ceb, 0x043e62a3, 0x0a728fcb, 0x147d3996, 0x1c6b25c5, 0xb5b93e}}} + }, + { + /* 1*16^1*G: */ + {{{0x01277c6e, 0x0f5a3c3f, 0x1b280e29, 0x10725dfe, 0x0315fcd2, 0x0e314c1b, 0x06162e08, 0x02714d68, 0x76a94d}}, + {{0x0b8c5110, 0x14eeeb92, 0x11e2ea83, 0x1340081f, 0x08720859, 0x10daf08f, 0x1839b2c2, 0x0c2683e4, 0xa985fe}}}, + /* 3*16^1*G: */ + {{{0x1e4536ca, 0x1fdcee34, 0x0806986a, 0x1d252c14, 0x0cda11c2, 0x1a2df038, 0x07b23339, 0x01c924a7, 0x9482fb}}, + {{0x158cc1c8, 0x1d6c31df, 0x01efeec7, 0x1abc52ae, 0x14e63f63, 0x11c653a9, 0x1fe46975, 0x14e8be2a, 0x351d9c}}}, + /* 5*16^1*G: */ + {{{0x15ecff13, 0x0d8ed714, 0x1418cf12, 0x0c9439b7, 0x01eeb637, 0x0d28a984, 0x04656e0d, 0x182f5d26, 0xb2e1b7}}, + {{0x12187d44, 0x1dd24c4d, 0x17a8b9a8, 0x1435302f, 0x158c387d, 0x10d1556b, 0x0f33c8ce, 0x0262747d, 0xe6c044}}}, + /* 7*16^1*G: */ + {{{0x0ef87286, 0x05dd99a7, 0x08c2790e, 0x07d0fbf8, 0x0ee34070, 0x0bbee5e3, 0x0f0b2cd0, 0x05cfe36d, 0xb43346}}, + {{0x041496d9, 0x16023fb4, 0x0b3a92de, 0x1c4100d4, 0x12e6f9dc, 0x073960b4, 0x17f8e0fd, 0x0a4dda56, 0xa0da54}}}, + /* 9*16^1*G: */ + {{{0x19794381, 0x1cc35ad8, 0x0c31d806, 0x02b10ebd, 0x07b19189, 0x1482daab, 0x0933061b, 0x19c9a7b4, 0xa47420}}, + {{0x02769c52, 0x0b671d3f, 0x1dab1e1a, 0x0b811b73, 0x07afff3a, 0x1c9b0ccb, 0x1f839cf7, 0x1bef7c37, 0x2ebbff}}}, + /* 11*16^1*G: */ + {{{0x09a5185a, 0x03a23324, 0x179bcafb, 0x0e15913e, 0x1a3195b8, 0x0a009586, 0x05217fb3, 0x03373a12, 0x5ba7a}}, + {{0x01de3a4e, 0x0a1e4470, 0x016b8005, 0x0c441792, 0x1d48e760, 0x011f1271, 0x025b919e, 0x16bd0583, 0x30e0d2}}}, + /* 13*16^1*G: */ + {{{0x199b85fe, 0x151cc5af, 0x0e977f56, 0x1dfd355c, 0x1e0b79c0, 0x06c8c041, 0x0bf5a5e6, 0x193a1bfc, 0xb29f74}}, + {{0x0da604dc, 0x1d3214bb, 0x1d67a036, 0x1ec7a617, 0x1c6d92de, 0x18974b6b, 0x1e175741, 0x1874d852, 0xa3ce5b}}}, + /* 15*16^1*G: */ + {{{0x07e030ef, 0x1b3a2b74, 0x1543a589, 0x16560af9, 0x02b08576, 0x1f81f924, 0x13d41db2, 0x15517237, 0x99299b}}, + {{0x18d9aa9a, 0x0c735bee, 0x10021113, 0x0258933e, 0x0aa7957c, 0x0080d367, 0x1862fcce, 0x1a6667f1, 0x5cc2e3}}} + }, + { + /* 1*16^2*G: */ + {{{0x12d0441b, 0x0d971af8, 0x1a95930b, 0x1a16e21a, 0x1ed61190, 0x08a94301, 0x19661fff, 0x14760122, 0x34a2d4}}, + {{0x1e93b146, 0x01273c22, 0x1776076d, 0x0ecd73bd, 0x17fc0e7d, 0x1882373b, 0x0f08af29, 0x0d4a743c, 0xbeaaed}}}, + /* 3*16^2*G: */ + {{{0x1da08424, 0x0725719d, 0x1d912a77, 0x1f6f56e9, 0x123ed3ab, 0x02ffd4ae, 0x06a86d63, 0x00f5b86b, 0xa98b0a}}, + {{0x0831800c, 0x10943d07, 0x17d60b4d, 0x17d01741, 0x11320752, 0x0f46470e, 0x0d9b94c6, 0x08e44933, 0x4754c6}}}, + /* 5*16^2*G: */ + {{{0x083c2d6a, 0x096bf2f4, 0x0f4a6ced, 0x1f231b03, 0x0696dd22, 0x15bc642d, 0x0b17486d, 0x013911b5, 0xa034f0}}, + {{0x02c30e72, 0x019b7796, 0x099f9f3d, 0x0c06be2c, 0x0eb06be0, 0x1c2a2e76, 0x107ec336, 0x06d95133, 0xfe1b00}}}, + /* 7*16^2*G: */ + {{{0x08be8c78, 0x0b528ee9, 0x1e4f935c, 0x12d9f056, 0x0e9a14a0, 0x115a04b1, 0x100681db, 0x1fe633a8, 0xb52226}}, + {{0x11686903, 0x019b3e52, 0x0d63fcc8, 0x1eff91cb, 0x0851a819, 0x1b34bf49, 0x1503286c, 0x193ea73e, 0x701ee0}}}, + /* 9*16^2*G: */ + {{{0x0058b5a4, 0x0207f205, 0x0c65e6dc, 0x08c9f0f2, 0x04713de4, 0x07667e66, 0x16cf65a3, 0x0c30d1c0, 0xa3e388}}, + {{0x07113aef, 0x19836c87, 0x1c68a523, 0x1d08b76e, 0x1e68f3ce, 0x1359d74b, 0x04b0e123, 0x110fbe1a, 0xcfd457}}}, + /* 11*16^2*G: */ + {{{0x04a08314, 0x02faf648, 0x11f7fde8, 0x1c6db65b, 0x1197a82f, 0x055cd110, 0x14e4ccf3, 0x094263f4, 0x209954}}, + {{0x07573ccc, 0x1fc64e2f, 0x1f3fa5b9, 0x0dcb4e6d, 0x048dfd40, 0x1472e5c8, 0x0c6243ee, 0x1fa55467, 0xa5cf3d}}}, + /* 13*16^2*G: */ + {{{0x09078c18, 0x017fca57, 0x1b7a4a54, 0x018795dd, 0x1d39b99e, 0x1739556f, 0x11c0c23d, 0x0275981d, 0x751d65}}, + {{0x0ece0d67, 0x0c6b74ae, 0x1816b1e6, 0x12918397, 0x02a4ef00, 0x05d7b515, 0x056214d5, 0x1fb94948, 0xba9c3f}}}, + /* 15*16^2*G: */ + {{{0x1e86d900, 0x167e6ae4, 0x08f6d124, 0x1dc13a2f, 0x03b36405, 0x0184501d, 0x00445ed2, 0x044b88f0, 0x5be0f5}}, + {{0x158a5755, 0x066eb4df, 0x0cd3d6b1, 0x02e4a483, 0x1423df92, 0x11d273da, 0x108b4d1f, 0x1cdbfe58, 0x83d1d9}}} + }, + { + /* 1*16^3*G: */ + {{{0x0245573f, 0x03bf46d5, 0x1f42993c, 0x0cede5e9, 0x16bd2508, 0x04b39736, 0x193665de, 0x1a59e0d3, 0xe716ae}}, + {{0x069218d1, 0x02fe135a, 0x01f26cd4, 0x1a1be5f4, 0x1a851d13, 0x183343dc, 0x0aad644a, 0x1cd29f8e, 0x353663}}}, + /* 3*16^3*G: */ + {{{0x057008ac, 0x1cc3c983, 0x0a7b7edf, 0x1c3d9dc6, 0x1a22d19c, 0x0ac5260e, 0x13a19395, 0x11adea0f, 0xd2bf89}}, + {{0x127beb55, 0x088a9a53, 0x1fc1e620, 0x19c2156b, 0x10c56679, 0x0aed9fd4, 0x1ea8fe06, 0x196b4d6e, 0x69c0b1}}}, + /* 5*16^3*G: */ + {{{0x182f1580, 0x086be54b, 0x080fadff, 0x09bbdc48, 0x064f8b0e, 0x0b9c8f98, 0x19821fe9, 0x09dcb079, 0x4d8830}}, + {{0x1616edd7, 0x007d8b6a, 0x1ac78443, 0x19477ad1, 0x16518aaf, 0x00044c7e, 0x0db3a35a, 0x0e13560c, 0x28a94e}}}, + /* 7*16^3*G: */ + {{{0x00c38e11, 0x1c261ed7, 0x1b89ae97, 0x0c8437d4, 0x1af9e715, 0x1cc90172, 0x1bacc99c, 0x04f91f81, 0x17c727}}, + {{0x0e90decd, 0x0c1b35dd, 0x0336b8f3, 0x07328b4c, 0x1802e7e5, 0x17e67480, 0x0cf10180, 0x1e9fccc9, 0x95c5a4}}}, + /* 9*16^3*G: */ + {{{0x032f36ea, 0x0aafec57, 0x0d5070f6, 0x1e8d4e52, 0x0b8b3cd4, 0x18e7e426, 0x02a2118e, 0x15588e8a, 0x7e03fd}}, + {{0x0a68b8af, 0x16611fe4, 0x140275a2, 0x1d2f3498, 0x0af99419, 0x158b80c2, 0x03699a39, 0x1e240245, 0xdcf6d5}}}, + /* 11*16^3*G: */ + {{{0x1953baa9, 0x01f29536, 0x18637a35, 0x0b5d0c93, 0x0b214261, 0x1d60ee50, 0x1c1d858d, 0x0d096379, 0x8ce6da}}, + {{0x152689e5, 0x073ae147, 0x0a4d075e, 0x0730d76d, 0x1b56807f, 0x1de48030, 0x082f9a6c, 0x171f5262, 0x3d9f4f}}}, + /* 13*16^3*G: */ + {{{0x0f22a96a, 0x15c6c29d, 0x1aae3e52, 0x03753b38, 0x0e2b008b, 0x1aa4badd, 0x15288294, 0x06081b80, 0xb570e7}}, + {{0x085d1b00, 0x199fb30e, 0x15732732, 0x1376f4b0, 0x0cfbda7c, 0x1bb4dae0, 0x1dfc0a30, 0x0c71ee3c, 0xdcd8a2}}}, + /* 15*16^3*G: */ + {{{0x189a7509, 0x12075ca2, 0x1ea01527, 0x0e172c83, 0x1f20de12, 0x0d1f3ae3, 0x10ec1284, 0x04797572, 0x44d943}}, + {{0x00437234, 0x10515cdb, 0x137449b8, 0x07c3fbe2, 0x193586f9, 0x1de693bd, 0x1c280d08, 0x10010803, 0xb2c5b3}}} + }, + { + /* 1*16^4*G: */ + {{{0x0eade3c4, 0x1f4232e3, 0x014a8140, 0x156e9392, 0x186b4714, 0x1219a072, 0x04363971, 0x0de9d23d, 0xa01836}}, + {{0x0b26e8d0, 0x02e21013, 0x1853cdfd, 0x0e509c88, 0x18369d5f, 0x12bc1a4e, 0x0c59f1b3, 0x02e28221, 0xe2bbec}}}, + /* 3*16^4*G: */ + {{{0x19e0af06, 0x10e6f562, 0x1b65635b, 0x07314ac5, 0x1055c92f, 0x1dad36a2, 0x059142f4, 0x001711b1, 0x9cdf1f}}, + {{0x0216ade7, 0x0bd196db, 0x1f2951a3, 0x0a45b832, 0x194b0828, 0x05de6e46, 0x15993656, 0x1fda7e55, 0x916d25}}}, + /* 5*16^4*G: */ + {{{0x1305097e, 0x09b95c9c, 0x0ee94660, 0x0c2e1d08, 0x0448338d, 0x0d9682a0, 0x0fb6adac, 0x1aba34cc, 0x45bfd9}}, + {{0x108ddc04, 0x05d02c74, 0x14b05655, 0x173a677f, 0x0e908683, 0x0182a386, 0x14aeeb62, 0x0250ab5f, 0x9d1dc}}}, + /* 7*16^4*G: */ + {{{0x163bd6df, 0x098f8d05, 0x19abb0f3, 0x184785ba, 0x15e6fd1f, 0x0fce0144, 0x04ed9c7a, 0x02ac8ecd, 0xb599ad}}, + {{0x0efa09ac, 0x150e751c, 0x04d2d163, 0x0189ed22, 0x1c4166a4, 0x1b05c591, 0x0cc918dc, 0x1bd89f56, 0x6979d1}}}, + /* 9*16^4*G: */ + {{{0x07142d62, 0x1c309f99, 0x0ef09b6b, 0x14d46bf0, 0x054c11d9, 0x158913f4, 0x103a4998, 0x1e2ce655, 0xf4dca1}}, + {{0x1a618fc9, 0x04f66603, 0x134f5feb, 0x0f359dfb, 0x1c659bb9, 0x1c0dc8d5, 0x151b527c, 0x1cb69699, 0x551167}}}, + /* 11*16^4*G: */ + {{{0x015fae61, 0x1658ffa3, 0x0e57e087, 0x0b8fb89e, 0x0bb5a7f1, 0x1120738b, 0x0d9b6713, 0x141cdc13, 0xf07278}}, + {{0x096376f9, 0x1e865ba4, 0x0dfaaf3d, 0x0e0e2b70, 0x0f7d42c2, 0x10410c4c, 0x1a97df5e, 0x056294e3, 0x1a681b}}}, + /* 13*16^4*G: */ + {{{0x0466591f, 0x027020a9, 0x11a35a83, 0x0e5d51ff, 0x058ccbe1, 0x15a99dd4, 0x16c3c50e, 0x0af5e141, 0x1fc524}}, + {{0x196f8cdb, 0x077f7ab9, 0x1b1e0891, 0x11958465, 0x14e1629f, 0x1b7f2da1, 0x1047a7b1, 0x070113d7, 0x489c18}}}, + /* 15*16^4*G: */ + {{{0x122a8ebc, 0x12e85c61, 0x1e59c544, 0x039b6e31, 0x056d5a29, 0x05ad9865, 0x17d3f5fc, 0x16634918, 0xfa2501}}, + {{0x0a8a775b, 0x06c93dad, 0x0f20cc60, 0x11c11cd9, 0x1a8cbea5, 0x0f330d37, 0x0588ce14, 0x1629f0b1, 0x71c932}}} + }, + { + /* 1*16^5*G: */ + {{{0x1d96dff1, 0x1bee765b, 0x157f3fa3, 0x08638355, 0x198d530e, 0x105ab866, 0x153ffbda, 0x10a283fc, 0xec738}}, + {{0x1c7b552c, 0x16420d63, 0x1b5e2aa7, 0x04c99d0f, 0x052511d5, 0x0277ac03, 0x1d7646b3, 0x09d0f5d0, 0xd6224f}}}, + /* 3*16^5*G: */ + {{{0x088d58e9, 0x0e192558, 0x18c60e14, 0x14b838c9, 0x0a7b6e94, 0x12353e21, 0x0a1ba64a, 0x1fb8e0c9, 0x96dac3}}, + {{0x0ebebc5e, 0x01a49895, 0x01f9b8e0, 0x17d13729, 0x0c439685, 0x024a49c1, 0x06b615b3, 0x1e75a8d8, 0xcb1faf}}}, + /* 5*16^5*G: */ + {{{0x0db29f92, 0x0a956899, 0x11ecb162, 0x03a4e372, 0x18f811d2, 0x0e1bc575, 0x0c4a8417, 0x079d629e, 0xe297b2}}, + {{0x05e58ddb, 0x0794a645, 0x1b505058, 0x079d770b, 0x19149122, 0x0dd5dc66, 0x02d2d203, 0x041f196e, 0xe13725}}}, + /* 7*16^5*G: */ + {{{0x0ad88c33, 0x0ca1dbdc, 0x1d1af2bf, 0x15c729b2, 0x0da97d91, 0x1e490692, 0x12d9ac1a, 0x071f6572, 0x1cd223}}, + {{0x048fb1b2, 0x14753c21, 0x12879258, 0x1ca262bd, 0x0bc2713f, 0x1205589b, 0x02c25b21, 0x1f071569, 0xfc3acd}}}, + /* 9*16^5*G: */ + {{{0x1b26aa73, 0x09d644e1, 0x18e8383d, 0x0fc23618, 0x11ee0cdf, 0x16986ffd, 0x0eff2c72, 0x15b73d3f, 0xf462d7}}, + {{0x18479e73, 0x02f560bb, 0x140b3289, 0x11c14600, 0x13c7a49e, 0x1d253439, 0x0c50354e, 0x034f068a, 0x406a0d}}}, + /* 11*16^5*G: */ + {{{0x1cd015e3, 0x170f0155, 0x194089cf, 0x01d2b2fc, 0x15168af9, 0x1f59e544, 0x12bdd6f6, 0x04ba7ee1, 0xe0f689}}, + {{0x12157cce, 0x15126a16, 0x0a4daef6, 0x116a723c, 0x0c77c55b, 0x14b6393a, 0x0aa54d89, 0x0621c907, 0x8531e}}}, + /* 13*16^5*G: */ + {{{0x0bb76b12, 0x1362188a, 0x0649da47, 0x1cecee7c, 0x15a00ea8, 0x1598957b, 0x15ff0760, 0x182aa57e, 0x28e4ad}}, + {{0x0c4747bd, 0x1f229d3f, 0x058a3fd5, 0x014c1e2e, 0x0a3f703a, 0x1b2db5cf, 0x06cfd392, 0x09dfb340, 0x14d74c}}}, + /* 15*16^5*G: */ + {{{0x076ff697, 0x1fac00ff, 0x01d918a2, 0x16d10ca4, 0x097c6369, 0x16d5d9d0, 0x017b49c7, 0x04f29750, 0x85a0ba}}, + {{0x12142721, 0x04f6a6d2, 0x02962e4c, 0x12fff4f2, 0x1aa551de, 0x0869ee76, 0x0929551e, 0x0c3d587c, 0xadf32e}}} + }, + { + /* 1*16^6*G: */ + {{{0x0392d805, 0x192e2ee7, 0x1501750d, 0x1500d30e, 0x1449aa87, 0x06d57d51, 0x0f5e9295, 0x19e98d52, 0xf8f5dc}}, + {{0x0cda02fa, 0x1330ca8b, 0x0850ee80, 0x07b4c94a, 0x1327351f, 0x1f19b230, 0x0150e274, 0x19ecdac6, 0xe58176}}}, + /* 3*16^6*G: */ + {{{0x1fa046f7, 0x0ea598b3, 0x01cc2746, 0x021e7204, 0x02d45171, 0x05644a37, 0x0ea53821, 0x0950cb10, 0xe12c8e}}, + {{0x012646ad, 0x1d2ad145, 0x0c464d14, 0x1809c226, 0x126f6dd0, 0x1f6a9c98, 0x0bcd0cec, 0x0c21fb34, 0x7cec04}}}, + /* 5*16^6*G: */ + {{{0x02853c43, 0x0d893f46, 0x08b919ae, 0x0cd8af5c, 0x13236481, 0x1177f1e8, 0x0824f423, 0x0e82f2d2, 0x394bd4}}, + {{0x0064469d, 0x0bc14665, 0x03f3c32c, 0x1ece25b2, 0x00767d52, 0x1fe178eb, 0x1ae481f8, 0x0a42a3b8, 0x2b4d6d}}}, + /* 7*16^6*G: */ + {{{0x1c722e94, 0x016eb9cf, 0x0162587b, 0x102072da, 0x004334ed, 0x132b62ca, 0x0ba51171, 0x1be71bd0, 0xca8538}}, + {{0x1049a527, 0x105316f8, 0x02c9a90e, 0x12a75149, 0x19f12f20, 0x189350fd, 0x170c479c, 0x085d73c0, 0x3b27fc}}}, + /* 9*16^6*G: */ + {{{0x1cad6309, 0x18ba314e, 0x0e7fd221, 0x143f85e4, 0x07b3dd31, 0x1a312653, 0x0dd686ed, 0x0b3e46af, 0x663e1a}}, + {{0x09981f28, 0x19435d1c, 0x1ad4af54, 0x0fa88805, 0x0f918b90, 0x00c1e58e, 0x030f040b, 0x07700cd5, 0x292fb7}}}, + /* 11*16^6*G: */ + {{{0x02c23d03, 0x13ad9229, 0x11ffc924, 0x03609b1f, 0x1eeab3ba, 0x1611b83d, 0x19c25b0d, 0x1ce60c6c, 0xa2d9ef}}, + {{0x1085db74, 0x01726027, 0x0e77d144, 0x134fd2b0, 0x01b92ea0, 0x021b6388, 0x0e3c554c, 0x05199083, 0x1cc852}}}, + /* 13*16^6*G: */ + {{{0x07d4c6d1, 0x1aafcb38, 0x167ffec5, 0x059aa335, 0x135d0f66, 0x085a939f, 0x07bf82e4, 0x05691635, 0x5657e1}}, + {{0x0106bdec, 0x0181f94c, 0x14b05062, 0x1346b428, 0x06a0abff, 0x1c8799d7, 0x07e6b3ec, 0x1e971ba9, 0x72b5be}}}, + /* 15*16^6*G: */ + {{{0x1933564a, 0x032a6eaa, 0x18ebd13e, 0x1169f5db, 0x18d2b7a6, 0x16e333b6, 0x00042193, 0x02ba815c, 0x5b6862}}, + {{0x0204ef63, 0x1af822c4, 0x1e21a7ce, 0x15dc4510, 0x195de392, 0x062a68ce, 0x19154b4c, 0x0d39e744, 0x76b5ea}}} + }, + { + /* 1*16^7*G: */ + {{{0x0f922dbd, 0x025118bb, 0x064863e6, 0x0c622c3f, 0x171b91ca, 0x1556c726, 0x1cc4fe17, 0x17ffa9b5, 0x6d28b6}}, + {{0x00ef3cff, 0x055ff815, 0x1368a651, 0x09d3e170, 0x14a1c4c2, 0x10d30e65, 0x0be903ef, 0x00a283ba, 0xaf39d9}}}, + /* 3*16^7*G: */ + {{{0x1b168b64, 0x0c86ffcc, 0x01b2102d, 0x07a0558b, 0x0467ea15, 0x0482fe0a, 0x0f774f7c, 0x0f4ff7fc, 0x7cd315}}, + {{0x1abbb16b, 0x0b60695a, 0x180b8d61, 0x189b36e5, 0x1fafb13a, 0x073c5e6d, 0x1188815a, 0x0422e525, 0x9ca08d}}}, + /* 5*16^7*G: */ + {{{0x06ef29e8, 0x158e37d0, 0x166bb8ec, 0x18aa9613, 0x1356264d, 0x018ccea2, 0x03115ac9, 0x1b6be5bf, 0xed860b}}, + {{0x19b90f8f, 0x00d0e24d, 0x036a8cd4, 0x061eb85b, 0x17bb1e76, 0x1b59a020, 0x1ff5f1af, 0x086ebab4, 0xb837e7}}}, + /* 7*16^7*G: */ + {{{0x1636e617, 0x0fc8c59f, 0x1c73d667, 0x19a07783, 0x12c9d47d, 0x0e173f13, 0x090dcd78, 0x11fa3a42, 0x6868cc}}, + {{0x1cbb3d27, 0x046ee79f, 0x0e1335c7, 0x1c619148, 0x13f0a60f, 0x081c3c98, 0x079acf16, 0x14b7237f, 0xeed008}}}, + /* 9*16^7*G: */ + {{{0x113e2a34, 0x1ced1530, 0x0e8478b1, 0x1a5ccb9c, 0x1d318291, 0x1a4f8857, 0x1880b172, 0x0c1d591f, 0xec03b3}}, + {{0x184566d6, 0x01121f75, 0x084dade9, 0x102adccb, 0x122728aa, 0x0f108090, 0x010defbf, 0x1f6c140b, 0x86020f}}}, + /* 11*16^7*G: */ + {{{0x087c1b18, 0x1570672e, 0x12646897, 0x1c596a90, 0x056fa101, 0x01ad2c88, 0x04379a8c, 0x1473cf17, 0xa79464}}, + {{0x1b94809a, 0x15ac284b, 0x158a086f, 0x11bd38e2, 0x089f60d9, 0x0bc3cf4d, 0x03309269, 0x1c7ee50b, 0x1361f4}}}, + /* 13*16^7*G: */ + {{{0x1ed10d26, 0x0a714636, 0x19657d96, 0x157df053, 0x10ccc5a9, 0x0fdb4a79, 0x131e43a4, 0x101551f7, 0x544ef2}}, + {{0x16dafcc2, 0x1e127b93, 0x0b01ab8b, 0x1adf2edc, 0x170d3683, 0x0eff46c6, 0x0b3a75ac, 0x13b88b58, 0x95b91c}}}, + /* 15*16^7*G: */ + {{{0x17ad18e6, 0x04a3cfc5, 0x0f2b5789, 0x1a7cf31a, 0x0ff9b57a, 0x1e4235a5, 0x18853794, 0x110be698, 0xe2c26c}}, + {{0x0554cfad, 0x0d7f28d5, 0x0607792b, 0x0a2b94e3, 0x1ba113bc, 0x1337508b, 0x06214738, 0x0509e910, 0xb2121b}}} + }, + { + /* 1*16^8*G: */ + {{{0x185a5943, 0x12d4f110, 0x1977ed8e, 0x12326cb8, 0x071da1ab, 0x15991316, 0x1e248595, 0x0815e455, 0x7fe36b}}, + {{0x099ca101, 0x0868a963, 0x02bc84b5, 0x07ab0cf7, 0x0a6f174b, 0x1a0203ee, 0x18927c27, 0x0b04b6c6, 0xe697d4}}}, + /* 3*16^8*G: */ + {{{0x1f0922a8, 0x153c048e, 0x173e365d, 0x01d3a4a8, 0x18b808e9, 0x072b0117, 0x15ce0249, 0x080b69c5, 0x4b656a}}, + {{0x199a80bb, 0x0d6e562c, 0x0143da53, 0x0773c573, 0x0c4d6d47, 0x06612aec, 0x00e48341, 0x03a25cef, 0xee1ea3}}}, + /* 5*16^8*G: */ + {{{0x09d07e9e, 0x0827c443, 0x09830dad, 0x1cf943ed, 0x18e5ea09, 0x15f07d9e, 0x04f64f6c, 0x0dd7acbc, 0x9d7895}}, + {{0x03d8c8ee, 0x06eb0ad6, 0x0531ecbd, 0x0145b477, 0x010ce746, 0x144e0e99, 0x0335950a, 0x0dde91d5, 0xd5149e}}}, + /* 7*16^8*G: */ + {{{0x1dc4950f, 0x13ba8433, 0x0e5081d9, 0x03655b7a, 0x16316574, 0x1423726d, 0x1ea55c8d, 0x0e5c26a7, 0x4acb12}}, + {{0x03d8b4df, 0x16372223, 0x019ab159, 0x1c85016b, 0x1e29fdf9, 0x0c7b6638, 0x16b633ca, 0x01f6e879, 0x726cd2}}}, + /* 9*16^8*G: */ + {{{0x0d7e8cbd, 0x0063d3df, 0x01f605bd, 0x0cff15e0, 0x1464a2a6, 0x09aa8640, 0x1ed08d11, 0x17e34d62, 0x640c5e}}, + {{0x1ec5a5db, 0x171481e5, 0x1fe6c081, 0x0d3f6319, 0x0ab9eba0, 0x0375b80f, 0x14bcaba7, 0x1fb0539f, 0xd6ae88}}}, + /* 11*16^8*G: */ + {{{0x1e86bddc, 0x18502583, 0x1738f57d, 0x04d72f2e, 0x16b9bf1d, 0x116486ed, 0x0cc91455, 0x16368509, 0x8bba04}}, + {{0x09e349c4, 0x111e2d0f, 0x065b97d4, 0x19fbc9e8, 0x1437ac6a, 0x1c90b34c, 0x1afd966f, 0x18138411, 0xc34099}}}, + /* 13*16^8*G: */ + {{{0x0257f582, 0x0b873c02, 0x10e89634, 0x1213d5bd, 0x05fe41f9, 0x0a1fbe2d, 0x1f88429a, 0x089fd9a6, 0x8b0a57}}, + {{0x17372d4b, 0x00bff6f9, 0x0581d992, 0x168d74f6, 0x17d4656d, 0x0ad09bf1, 0x1ce598a8, 0x09d81ad8, 0x9e57a4}}}, + /* 15*16^8*G: */ + {{{0x185e797e, 0x18a24fcc, 0x0f0c5a0f, 0x1c449e5c, 0x078a6f06, 0x17a49ecb, 0x025c7e15, 0x12f84c8f, 0xa13ff}}, + {{0x139d666f, 0x193d5cfa, 0x0030e4a0, 0x1ede14eb, 0x1dc3ff27, 0x00c747d0, 0x0f5645f6, 0x09644b90, 0xeb1a96}}} + }, + { + /* 1*16^9*G: */ + {{{0x1dde3445, 0x17fa7f0f, 0x0ba9e05e, 0x1753bb45, 0x0519e76b, 0x0ff3ff93, 0x079a14dc, 0x0709ae0c, 0x6965b6}}, + {{0x18855113, 0x0621c4f0, 0x17e6765f, 0x19cc0a34, 0x12dbef47, 0x04b31c39, 0x1444c979, 0x1c738904, 0xd1bcda}}}, + /* 3*16^9*G: */ + {{{0x088acfc5, 0x0f207cb4, 0x1a25e27d, 0x1eb1366c, 0x11a71b1e, 0x1eedea04, 0x1f6809f9, 0x03bbceb8, 0xe1cbc5}}, + {{0x0249a225, 0x1e2ff6ad, 0x0fee948e, 0x065257dd, 0x1e23ce7d, 0x1b27fedc, 0x0bb6cc49, 0x12f9bcfb, 0xec6783}}}, + /* 5*16^9*G: */ + {{{0x0107ccc6, 0x0c7529ff, 0x0f208a1d, 0x112fc2a8, 0x100481d3, 0x0e9422d4, 0x0a85cc7a, 0x0f2e2271, 0x9c3f26}}, + {{0x1dea9152, 0x003ca669, 0x1e6c61a1, 0x11fa9d2b, 0x06eca51a, 0x1afa5f9f, 0x16722f8d, 0x036c96a3, 0xdaee0c}}}, + /* 7*16^9*G: */ + {{{0x15a449e7, 0x0e2f5a1c, 0x0f0af908, 0x054069ca, 0x0922b2fa, 0x160bd8a0, 0x15c9e5be, 0x0aef8d65, 0x5c30d9}}, + {{0x0e2677d0, 0x1875240d, 0x139e439d, 0x1c80a787, 0x192d0b75, 0x12fbcce2, 0x0e48592d, 0x16b611c3, 0x8025e3}}}, + /* 9*16^9*G: */ + {{{0x0b8ceb07, 0x12ac6014, 0x1b89c73e, 0x124a57ff, 0x0d995537, 0x11583f54, 0x1a0abf93, 0x046191d2, 0xaa3bbf}}, + {{0x1fd83984, 0x0d423174, 0x0cfe7ae6, 0x036578f5, 0x162ec3b7, 0x187f2648, 0x09a11372, 0x161383d8, 0xe6320c}}}, + /* 11*16^9*G: */ + {{{0x03bfd1f1, 0x00752aac, 0x1595ccb9, 0x0db86a2b, 0x001943a3, 0x07e4c8a0, 0x0605e798, 0x0a226668, 0xab4a32}}, + {{0x08e1c233, 0x089cb086, 0x07a633c8, 0x1b26484f, 0x1e057a57, 0x1de82097, 0x19a2d3ac, 0x17606d90, 0xea6c64}}}, + /* 13*16^9*G: */ + {{{0x0b1d6d3f, 0x02036bc0, 0x1baee242, 0x0ecd149d, 0x1a8105f3, 0x1fae6c52, 0x15dfdccd, 0x0b3bbdab, 0xdba9b2}}, + {{0x09cd8133, 0x01749414, 0x0af64f31, 0x1b3788a2, 0x1871448b, 0x0ca7a7f5, 0x0ded6b2a, 0x07eaea6b, 0xb3862d}}}, + /* 15*16^9*G: */ + {{{0x11fa2a6a, 0x1cd01c01, 0x0392c959, 0x183ab0b6, 0x1cdfe34f, 0x0ba158d1, 0x1d141eb7, 0x1885d304, 0x1998f4}}, + {{0x06415142, 0x03f6da5e, 0x1c962d58, 0x180470f6, 0x0a1e94c6, 0x1c0045bc, 0x08425c03, 0x0da3215f, 0x2ebb41}}} + }, + { + /* 1*16^10*G: */ + {{{0x0d9aefbd, 0x0c7e5362, 0x0e55dd49, 0x1c8f64e7, 0x043dc1ef, 0x0f86a0de, 0x15d8cb2a, 0x03918cd3, 0xfbc34}}, + {{0x13e71ca0, 0x09db754e, 0x02a7fc7b, 0x160d3c40, 0x1d3d3950, 0x058ea4ab, 0x18ff9005, 0x0c65e6c1, 0xbd8022}}}, + /* 3*16^10*G: */ + {{{0x09434837, 0x1cb6fff7, 0x08306ee1, 0x0628170c, 0x1b06dadb, 0x0e37fda7, 0x0bb6c4d0, 0x1578950f, 0xd76d18}}, + {{0x1b215b4c, 0x1d5027cd, 0x0df33093, 0x08b1ceeb, 0x1933290a, 0x010a7bd5, 0x19137839, 0x1465db2c, 0xf6bc4d}}}, + /* 5*16^10*G: */ + {{{0x1c07f222, 0x1744f90c, 0x183f9f40, 0x0438c758, 0x153d1c5e, 0x0e8d0f8b, 0x1d813d20, 0x0a0c2cff, 0xd5c0c6}}, + {{0x0c6ffd57, 0x177b48f0, 0x004ea1b8, 0x07ea34f1, 0x175b9baf, 0x063bfa4f, 0x02143378, 0x10c102f7, 0x15a30b}}}, + /* 7*16^10*G: */ + {{{0x0e93bfa6, 0x1238b512, 0x084d8a92, 0x1a52b413, 0x09fe0d39, 0x05d335a6, 0x18b39527, 0x09c948de, 0x734c36}}, + {{0x18d10774, 0x037d3ccc, 0x00a5f13f, 0x026c4112, 0x05f48eca, 0x00f1a906, 0x141277a6, 0x007554f3, 0x99515}}}, + /* 9*16^10*G: */ + {{{0x1fa194ae, 0x075a8bfa, 0x0152bb3c, 0x00523b34, 0x0b149064, 0x0ece954f, 0x0a24045d, 0x1b40f6cd, 0x79a3d9}}, + {{0x1fcf634b, 0x1e32f4e4, 0x1e6f1353, 0x084be65e, 0x103d86bd, 0x18dc2c57, 0x06cd2cd9, 0x194e4a96, 0x84e1db}}}, + /* 11*16^10*G: */ + {{{0x01e8b7fe, 0x16483001, 0x016c9a9a, 0x01c5c2ef, 0x098e05dd, 0x06556e7e, 0x160a28f9, 0x0129ab60, 0x3393fd}}, + {{0x023c6821, 0x00a12210, 0x06e52dc3, 0x0d661515, 0x0668d8e5, 0x1576ce2d, 0x1ae0babf, 0x17d90cf7, 0x130437}}}, + /* 13*16^10*G: */ + {{{0x01528cf6, 0x04abceab, 0x141a53a8, 0x004a15ec, 0x10a52e6a, 0x02c3772d, 0x1f85786d, 0x10c268c4, 0xa16b28}}, + {{0x1ce93f3b, 0x05c907cf, 0x132004e0, 0x07f79027, 0x150f4349, 0x16bcec08, 0x166644f3, 0x15d0a6f5, 0xf40598}}}, + /* 15*16^10*G: */ + {{{0x0c8db2e0, 0x0885335c, 0x035cd60d, 0x197b82be, 0x074f6473, 0x04d5d4ad, 0x1d32fdb2, 0x04031c68, 0xd317ce}}, + {{0x074bc9a9, 0x0d9a5159, 0x0e183ce9, 0x04e9a045, 0x19136caa, 0x01f471cb, 0x112e670b, 0x01521270, 0xd61a21}}} + }, + { + /* 1*16^11*G: */ + {{{0x03fe67b2, 0x1718cbd5, 0x0b8cce31, 0x117fce14, 0x123b234a, 0x15cdd4b9, 0x1772f199, 0x086ee790, 0x6608c2}}, + {{0x05b47a28, 0x091ff2e7, 0x1150c206, 0x162b3e81, 0x0e03fc22, 0x1206d3a3, 0x05a211bd, 0x17d8a438, 0xa1a916}}}, + /* 3*16^11*G: */ + {{{0x0bc73e02, 0x1f375569, 0x194c4b04, 0x0cc29dd6, 0x18631849, 0x0c08beab, 0x108c81aa, 0x1c54d8bb, 0xf0956a}}, + {{0x0611219d, 0x0967b4cc, 0x0ba515cb, 0x1a091b77, 0x19356672, 0x02c57941, 0x00f1db88, 0x0c02289c, 0x433fe6}}}, + /* 5*16^11*G: */ + {{{0x1c5fe45c, 0x0f116f28, 0x1f03d65f, 0x05035070, 0x1283488b, 0x124ebe7b, 0x183294bc, 0x1d479454, 0xdb98eb}}, + {{0x1ef45a22, 0x05380e21, 0x12038711, 0x06409f54, 0x12c8ce98, 0x094eec48, 0x168b7854, 0x016eb7b0, 0xe0c0f3}}}, + /* 7*16^11*G: */ + {{{0x02128eac, 0x1acf306f, 0x0bc732eb, 0x0eb1ab99, 0x051690c4, 0x159dbeb5, 0x17d086e9, 0x02c15bb4, 0xa73c0}}, + {{0x04f1180d, 0x02c948eb, 0x1576728e, 0x0eb3e2ee, 0x10a7c793, 0x1b6eca7a, 0x0e329572, 0x0ac089d0, 0x443fda}}}, + /* 9*16^11*G: */ + {{{0x03ccbc30, 0x12d4998b, 0x0d258631, 0x1fcba43a, 0x197249ed, 0x1bb33d04, 0x1c8dc3b4, 0x1868f969, 0x378720}}, + {{0x1315729e, 0x110193f8, 0x0d23e11a, 0x19f2aba2, 0x19b6d16b, 0x05605553, 0x0e324c46, 0x1739ee4a, 0x8f1bdd}}}, + /* 11*16^11*G: */ + {{{0x03bb1b6b, 0x12546f2a, 0x1f0afea9, 0x1fde65e8, 0x1a621575, 0x08c6082a, 0x09867660, 0x03b6f163, 0xfe8fd2}}, + {{0x10eaaf93, 0x1daecdd1, 0x0afe2265, 0x09bb9fe1, 0x113c00f1, 0x074afad6, 0x18dda78a, 0x15d864ad, 0x791e22}}}, + /* 13*16^11*G: */ + {{{0x144b7cea, 0x002acce5, 0x1b3e1f7a, 0x08d28122, 0x1dcca474, 0x00113c59, 0x18bba081, 0x1aaf3f6f, 0x2c331f}}, + {{0x15d1c9c2, 0x1e15a356, 0x0c2501df, 0x04a23fe2, 0x1d650619, 0x19ec61de, 0x19dbf3cf, 0x0399c0c3, 0x20567b}}}, + /* 15*16^11*G: */ + {{{0x0112278c, 0x1c94d9bc, 0x03668563, 0x02c93a15, 0x04b66dd0, 0x1a643fc2, 0x0c828d22, 0x0d88ca34, 0x509bc0}}, + {{0x0e178f66, 0x043ae4c4, 0x0d7e517b, 0x0bfd0dce, 0x164570f6, 0x1a78bdc7, 0x1bb05d83, 0x1856e448, 0xb4b168}}} + }, + { + /* 1*16^12*G: */ + {{{0x17e55104, 0x175d7c00, 0x03a71c70, 0x1506bf77, 0x1561cf73, 0x09e1a6c5, 0x1cdd8f7a, 0x0a44f6f0, 0xd8de76}}, + {{0x052e08cd, 0x10177c07, 0x1036c6ca, 0x0e7f9c32, 0x0f924c2f, 0x114568ee, 0x0b457131, 0x0cb7c27e, 0x2fd294}}}, + /* 3*16^12*G: */ + {{{0x0987bfd3, 0x09ce163f, 0x110a131a, 0x0a8e5fe9, 0x1d19187c, 0x1df57d6e, 0x18cd5477, 0x0df7aa31, 0xe3a578}}, + {{0x0cc23e7d, 0x1698db64, 0x0a38205f, 0x19e5d9b2, 0x0210ca3d, 0x1cffa56c, 0x039a11de, 0x012a6b08, 0xf74bad}}}, + /* 5*16^12*G: */ + {{{0x02d5056e, 0x13ca8e91, 0x11e81f2d, 0x196b4fc4, 0x05e480ed, 0x0ffe74b7, 0x03504549, 0x083d94a4, 0x42a970}}, + {{0x10f7db11, 0x020a6e45, 0x07f47f52, 0x1c907add, 0x03b26b64, 0x16e935ab, 0x1c19bb0d, 0x0cd9e010, 0xf2fc7f}}}, + /* 7*16^12*G: */ + {{{0x13dafd3b, 0x13a2fc8e, 0x12ce55b1, 0x1133e05b, 0x179d66fb, 0x16176b29, 0x1933f88c, 0x036384f2, 0xd6ca7}}, + {{0x1a6fe6e8, 0x156f8a10, 0x021dc6b5, 0x049157dd, 0x1de58a02, 0x0a501383, 0x04f67f92, 0x1671fbc5, 0xa85130}}}, + /* 9*16^12*G: */ + {{{0x11131f8b, 0x15fce3ff, 0x0883537d, 0x08d2c19f, 0x11fb4696, 0x116fce21, 0x0f65f530, 0x0f5b0cb4, 0x5edca}}, + {{0x1a6f779f, 0x167858cd, 0x1a275243, 0x04a00be1, 0x071be335, 0x0b8daf81, 0x1af00727, 0x03c91d10, 0x9e5888}}}, + /* 11*16^12*G: */ + {{{0x0b597e4d, 0x047cfa0f, 0x1f468c67, 0x09e64bce, 0x11c11497, 0x02d19dbb, 0x0290486d, 0x0ac178f4, 0x5a13c0}}, + {{0x041856e0, 0x1bf2434f, 0x04d9992b, 0x1d0f56b2, 0x173ce7bd, 0x0c507916, 0x1ffd47f4, 0x027121f2, 0xc9e73c}}}, + /* 13*16^12*G: */ + {{{0x0260faf0, 0x153c4b0a, 0x1a564d76, 0x17a68b2f, 0x1272ea2b, 0x070d3407, 0x19d50c51, 0x0ac02f6d, 0x96b256}}, + {{0x0366d412, 0x07212907, 0x1f53d6b0, 0x177f30e0, 0x199e1890, 0x072b99df, 0x12b002b6, 0x1400366f, 0xdcf8a6}}}, + /* 15*16^12*G: */ + {{{0x0ad13276, 0x04d0df25, 0x1010f1e9, 0x06a1d5d3, 0x171a3ca6, 0x15959c3b, 0x18909bc4, 0x0218839b, 0xb9719}}, + {{0x0a84dadb, 0x19c79a10, 0x1c3eb8ec, 0x1af25304, 0x0811f593, 0x122d9dfb, 0x1bef538b, 0x0dde00dd, 0xac2848}}} + }, + { + /* 1*16^13*G: */ + {{{0x071e5c83, 0x1535e490, 0x10a82fbb, 0x04fe330a, 0x0e5b18bd, 0x02db952c, 0x1cfc82a1, 0x082a04da, 0x54ccc9}}, + {{0x140916a1, 0x1e8477b8, 0x03b925b3, 0x1c1798bb, 0x0bf22929, 0x038aed69, 0x14c8ea3e, 0x08b68a28, 0x1c433f}}}, + /* 3*16^13*G: */ + {{{0x0f76167c, 0x02f1b115, 0x1d19ce53, 0x1e845988, 0x0d9f22f2, 0x163496e6, 0x1e54e708, 0x1b96e7df, 0x87582e}}, + {{0x0f4d7ff6, 0x037ddb0b, 0x065efa1b, 0x051e0cee, 0x036a8880, 0x0161e1b9, 0x09eff784, 0x06a15ac9, 0x75b94}}}, + /* 5*16^13*G: */ + {{{0x14de6611, 0x1391f743, 0x0087ec11, 0x0cae7297, 0x0f11e33f, 0x1b9b1ab3, 0x1b6c7096, 0x1943e4e6, 0x4a107a}}, + {{0x18a805fe, 0x1ac75f72, 0x084cab00, 0x1aa8f4b8, 0x0052e075, 0x12747b80, 0x1ce4d339, 0x0a200c7d, 0x809b26}}}, + /* 7*16^13*G: */ + {{{0x0dcfecb0, 0x05d27683, 0x0a3611bf, 0x07fa3a61, 0x149f6e98, 0x0706a4df, 0x1f259528, 0x166e555b, 0xfd402e}}, + {{0x0da08de8, 0x02c9f9ef, 0x19979e44, 0x1698497b, 0x1a86b1a4, 0x131c4dd5, 0x088ba698, 0x061bb4c7, 0xd6b340}}}, + /* 9*16^13*G: */ + {{{0x154659ed, 0x1a6eb07a, 0x17659b96, 0x1c7a4432, 0x1d0a07fa, 0x0703ff64, 0x145577ee, 0x08c8c30e, 0x467bb3}}, + {{0x0800cf83, 0x1903e859, 0x004f8026, 0x17821b9a, 0x02cd3701, 0x06ac36dc, 0x03a287cb, 0x1a0b552f, 0xd01eff}}}, + /* 11*16^13*G: */ + {{{0x1c97e515, 0x1324c256, 0x021697fb, 0x09ad8673, 0x0c7e5691, 0x0db1874f, 0x0391b21e, 0x11f19bb8, 0xcf66b9}}, + {{0x006c8a51, 0x0c10a754, 0x0400cea5, 0x00e8b1b3, 0x06a7b33f, 0x1a081a76, 0x04847096, 0x0f72088c, 0xa7071}}}, + /* 13*16^13*G: */ + {{{0x186154e5, 0x1d295080, 0x0be8374e, 0x0054cd35, 0x06770864, 0x04a9d6a4, 0x08d1d472, 0x18c1f7f5, 0x3d9252}}, + {{0x1603a8a2, 0x1fe49fb4, 0x1f75c1dd, 0x03f6faf8, 0x14cf3aea, 0x19a144b5, 0x15ff1124, 0x1b9a5f53, 0x3d0d01}}}, + /* 15*16^13*G: */ + {{{0x0a4a8daa, 0x1db1db15, 0x029728f4, 0x00c01de5, 0x0968ce51, 0x1ada47a5, 0x1a71d83a, 0x164d112e, 0x2bbe38}}, + {{0x1236e49b, 0x1043ba74, 0x1adb9f5f, 0x056749c7, 0x00850990, 0x1f7c8fcb, 0x0dbd78b3, 0x1ec01a76, 0xf85707}}} + }, + { + /* 1*16^14*G: */ + {{{0x02d32936, 0x187dbb89, 0x011fc070, 0x19a05c65, 0x17f38e40, 0x03e8a89e, 0x11f67db3, 0x0b2f0294, 0xc5440c}}, + {{0x07cabfd4, 0x1217d1ba, 0x19079766, 0x0da50c63, 0x0f4acd8a, 0x0f0e91f6, 0x1e9cbb70, 0x174707c3, 0xd27ee9}}}, + /* 3*16^14*G: */ + {{{0x1989a8e1, 0x0c280f63, 0x1b92f75f, 0x1b2de6e9, 0x0fe0a7c1, 0x1023cdce, 0x1e6dd403, 0x188b9bf4, 0x3e4d03}}, + {{0x041c240a, 0x03c89c42, 0x0f59d026, 0x1e31abe7, 0x0a719ffa, 0x0be56be5, 0x11d6ab04, 0x1a102b4c, 0x12f744}}}, + /* 5*16^14*G: */ + {{{0x19bb7902, 0x09cd4686, 0x0df1cfda, 0x04d8e50d, 0x05e9fd8c, 0x124f4a24, 0x00d66a68, 0x09367ac2, 0x1b684}}, + {{0x003ee653, 0x0bfa258f, 0x00d9a0b3, 0x164c08e3, 0x11581fce, 0x0c72e1b6, 0x10f82fc6, 0x143d26f3, 0xe59063}}}, + /* 7*16^14*G: */ + {{{0x0371d9fe, 0x14dab6a6, 0x0675aef0, 0x166ad833, 0x13a4cf04, 0x0ad3e1d5, 0x1288cd65, 0x16359993, 0x952c3c}}, + {{0x0189a13f, 0x1b673692, 0x0231d7b5, 0x08c9e230, 0x0e5d0664, 0x089b7b76, 0x1c1a9f7e, 0x08defac5, 0x59b985}}}, + /* 9*16^14*G: */ + {{{0x0bc2c885, 0x03ffe3b4, 0x19395f21, 0x03dceea4, 0x1cde5155, 0x002285cb, 0x1ab21626, 0x1c2b62cd, 0xdfcb4e}}, + {{0x1345d92e, 0x0ea53218, 0x0afa5d5d, 0x0e7128f7, 0x00f411c7, 0x1e136416, 0x0e854f13, 0x12aaa0c2, 0x536a23}}}, + /* 11*16^14*G: */ + {{{0x1510208e, 0x1c5c295f, 0x15e50e1c, 0x199c5b09, 0x097bbdb8, 0x179d023e, 0x00322a1e, 0x18137cca, 0x157966}}, + {{0x0d207357, 0x10777128, 0x1690f7f5, 0x1f8c8865, 0x0be07008, 0x1bdbddc6, 0x193aaf7f, 0x0b56e244, 0x40446d}}}, + /* 13*16^14*G: */ + {{{0x0b96fe13, 0x04fe65f4, 0x014baa07, 0x0495a769, 0x16e23e49, 0x147fc09f, 0x042b5b86, 0x078963d3, 0xc6b21e}}, + {{0x17bb1417, 0x0035cb57, 0x19b4b5a8, 0x0aa122f3, 0x128a2ff6, 0x1c0f1a75, 0x1523952d, 0x1b186669, 0x335bcd}}}, + /* 15*16^14*G: */ + {{{0x03936565, 0x1b2c0cd6, 0x1fe931f0, 0x0f66db2e, 0x122d997a, 0x054ea9ca, 0x05bc2d2f, 0x188f18e5, 0xa231e4}}, + {{0x15ec5f19, 0x172b5031, 0x1c5509a2, 0x0adc012d, 0x1cf942ba, 0x07634401, 0x1a470365, 0x117d8ff7, 0x80e0c9}}} + }, + { + /* 1*16^15*G: */ + {{{0x1066fd48, 0x1cfa0b95, 0x05c560ef, 0x0e203971, 0x0dca61c3, 0x0dcbd35d, 0x07141b1e, 0x0f4844fe, 0x241c56}}, + {{0x12857b08, 0x1be9633c, 0x08d9815f, 0x10e2715d, 0x003a48ea, 0x00be0219, 0x152e4d8e, 0x127a8605, 0x40a62d}}}, + /* 3*16^15*G: */ + {{{0x0df9591d, 0x10fbedc8, 0x0d320aa1, 0x18758485, 0x07218dce, 0x09e25599, 0x03a72e83, 0x07704d2f, 0xde2fd2}}, + {{0x0457ad84, 0x070cb9e8, 0x100da92d, 0x15143f11, 0x12ebbda9, 0x1bf6425c, 0x0fcc17b2, 0x02676c48, 0x400d71}}}, + /* 5*16^15*G: */ + {{{0x1562282c, 0x15412a57, 0x1ef0ddcb, 0x1e75f271, 0x11340f02, 0x04581270, 0x0f7664e5, 0x16060999, 0x8df889}}, + {{0x01d195cd, 0x12b55ecb, 0x1e6ec55c, 0x1ee0899d, 0x0f35e247, 0x0f318c45, 0x1bb5b1d0, 0x0ce640b9, 0x74525b}}}, + /* 7*16^15*G: */ + {{{0x0dddb2dd, 0x0ea944a8, 0x0b0369be, 0x10c99b98, 0x0f245078, 0x1c0678a9, 0x03e0007e, 0x119b3170, 0xa0fd75}}, + {{0x13ede6b2, 0x1eca7fc3, 0x10269f1f, 0x19d2df12, 0x08f311c8, 0x0fe989d8, 0x0357e1a4, 0x06b8266d, 0x53e5d8}}}, + /* 9*16^15*G: */ + {{{0x0f542e36, 0x0465d502, 0x0d0570b8, 0x05ff5f42, 0x135d84e2, 0x0933ca31, 0x03d9f796, 0x108e5a34, 0x3170c5}}, + {{0x163288b5, 0x1623ad77, 0x066f86f1, 0x1eead2b3, 0x1773d006, 0x0849ff5c, 0x1f88dd45, 0x025f874f, 0xb20836}}}, + /* 11*16^15*G: */ + {{{0x1c7548e9, 0x0cd91d2e, 0x04b5531e, 0x1b500e11, 0x03fe5d8d, 0x00b4a783, 0x180a76d2, 0x152145a7, 0x92fab0}}, + {{0x0917758f, 0x03896682, 0x0b421223, 0x01b8d1de, 0x079ffc8a, 0x18a70613, 0x1af3d0c5, 0x0d019648, 0x55e7b4}}}, + /* 13*16^15*G: */ + {{{0x1498f7f8, 0x06c0285c, 0x104588b8, 0x1ecfa64c, 0x08712c4d, 0x108d8c96, 0x145e742f, 0x17c3006a, 0x91b065}}, + {{0x1f23195b, 0x03a06cf1, 0x0258e78f, 0x18f684af, 0x1e264df2, 0x19a4800b, 0x1883fe7f, 0x0eff6ce2, 0x35b6f}}}, + /* 15*16^15*G: */ + {{{0x0060e322, 0x0ee5f712, 0x113452d4, 0x1b8e6f53, 0x0b9923ec, 0x034ba44d, 0x0cea70e4, 0x09995939, 0x8e4a1f}}, + {{0x104619d7, 0x110c1e6c, 0x13eff813, 0x01531b2a, 0x07bc4fb0, 0x0f692037, 0x1dd4bec1, 0x0bd6651a, 0x4936b7}}} + }, + { + /* 1*16^16*G: */ + {{{0x0e14db63, 0x073ae5a4, 0x1947dfa4, 0x1277555a, 0x025de294, 0x0c971937, 0x0a961249, 0x17850235, 0xfa822}}, + {{0x1f462ee7, 0x008922a2, 0x1fa0bd79, 0x034ca0a1, 0x1188b34b, 0x0a5e59ef, 0x0035bd2b, 0x1d1ebb75, 0xbff44a}}}, + /* 3*16^16*G: */ + {{{0x1db3cdec, 0x096229fb, 0x0a3afd5d, 0x1e742564, 0x04bc8dbe, 0x122f3df5, 0x1d739659, 0x0c9ff225, 0x85b2c0}}, + {{0x0a03f81f, 0x1a684102, 0x133e3823, 0x101669cc, 0x06d00dc9, 0x1d1697e6, 0x14d98f1f, 0x11e73a22, 0xf64b27}}}, + /* 5*16^16*G: */ + {{{0x0607b030, 0x0452d724, 0x01359cca, 0x15fec7cb, 0x0f29c24d, 0x1dd6760b, 0x119de147, 0x0ed88042, 0x110b03}}, + {{0x13617c3a, 0x01d50895, 0x0a61d27d, 0x1c2aadf6, 0x1c70c87b, 0x1c4fc230, 0x19cd31ba, 0x10a631dc, 0x84432b}}}, + /* 7*16^16*G: */ + {{{0x15a76f08, 0x076ec0e3, 0x0efb5395, 0x0be4a717, 0x0aaf8329, 0x1092158e, 0x075c53db, 0x0893ec8e, 0x18784e}}, + {{0x0b824993, 0x19cb02eb, 0x02894c82, 0x1ae94f4c, 0x1e671fc9, 0x147ed638, 0x0b9c5dde, 0x0fe943c3, 0x8d76ed}}}, + /* 9*16^16*G: */ + {{{0x1a6fad81, 0x14719e4f, 0x032c0fb3, 0x06cae918, 0x0037d9c3, 0x16ebb81d, 0x1ae6bbd5, 0x1c0fa0bc, 0x58a2f9}}, + {{0x1b109594, 0x00030af1, 0x0c02e095, 0x1765a65a, 0x1a6bd798, 0x017c38bf, 0x038306da, 0x18b58aac, 0x6ab64}}}, + /* 11*16^16*G: */ + {{{0x1eb52583, 0x191788fc, 0x03304ebe, 0x15339d82, 0x1676fea8, 0x16dd93c7, 0x0e8903b9, 0x1b99cfd7, 0x1ef020}}, + {{0x1a3f17ae, 0x0d93c0cb, 0x04b532d5, 0x0601f2e0, 0x095306ac, 0x1607a5cb, 0x12c025fd, 0x164b4f42, 0x594476}}}, + /* 13*16^16*G: */ + {{{0x10a1958f, 0x0e11f60f, 0x0d5990ed, 0x136a48df, 0x1a32c4f3, 0x0a267d5f, 0x084e5774, 0x0c783ad4, 0x4e4a81}}, + {{0x08636f8a, 0x01d9fa8a, 0x110f59a9, 0x0810bf65, 0x06fa8400, 0x051f714e, 0x03440cfd, 0x0b6f3d19, 0x2574a4}}}, + /* 15*16^16*G: */ + {{{0x04935db5, 0x013ad423, 0x1af6c3ba, 0x0f304c38, 0x0519e281, 0x1f076aca, 0x04138cc9, 0x02d5bac2, 0xf6966a}}, + {{0x1d166838, 0x11a41b60, 0x006fc04e, 0x08a74688, 0x0755e2d7, 0x172b961e, 0x16cb0697, 0x00f52063, 0xba0de3}}} + }, + { + /* 1*16^17*G: */ + {{{0x176a6987, 0x07a0982d, 0x1690ffda, 0x10356887, 0x01b3f2b4, 0x14c46bf7, 0x0551f771, 0x1af53313, 0x54bc18}}, + {{0x1b9aae49, 0x122be682, 0x1cec467f, 0x171da976, 0x1e2fd52e, 0x1c28dd39, 0x0bcdce46, 0x02423cdd, 0x4b2c8c}}}, + /* 3*16^17*G: */ + {{{0x1d1fd820, 0x05fa3faf, 0x1d9d400e, 0x0f0a8c90, 0x1271b788, 0x08113158, 0x14aaa18d, 0x1ed01838, 0xbafbbc}}, + {{0x1bf074f0, 0x176d6a90, 0x07f2b5ba, 0x18694246, 0x1c0fde81, 0x1fad644d, 0x1604b39e, 0x0f93b69b, 0x148799}}}, + /* 5*16^17*G: */ + {{{0x14299b7e, 0x08b84094, 0x01b8343a, 0x0f3e5a13, 0x020ec24e, 0x1dfe3ae1, 0x07f8a8f0, 0x1ee20671, 0x9b1938}}, + {{0x047b84de, 0x0bd942ba, 0x1ae08cb1, 0x1bd0a3f0, 0x03ec90ac, 0x14c70e55, 0x0a0cc503, 0x0dde2e20, 0xfb12aa}}}, + /* 7*16^17*G: */ + {{{0x110cc4c4, 0x0a083a06, 0x094f683f, 0x1a6b4589, 0x01b2cb71, 0x0fee033b, 0x1641f0e0, 0x10b9802e, 0xa67719}}, + {{0x1c976570, 0x1079d91b, 0x1fcc2530, 0x08aee74d, 0x19c3dbc7, 0x0b300f2e, 0x1663ca6f, 0x10beea1b, 0x855061}}}, + /* 9*16^17*G: */ + {{{0x14242a28, 0x0084016a, 0x0f29dc55, 0x1796424a, 0x14eca455, 0x17bc25bb, 0x1f0427a2, 0x0a6d61d5, 0x88b7ed}}, + {{0x194ed8ce, 0x1cb40f63, 0x00f8d1fe, 0x0cd4391f, 0x1bd934e3, 0x1cee9ac3, 0x11791fef, 0x040c48a6, 0x22b053}}}, + /* 11*16^17*G: */ + {{{0x1bc1a94c, 0x0b0a2764, 0x1c76be00, 0x0e3a567c, 0x08757516, 0x1958112f, 0x0f4814b5, 0x002550d6, 0x37114}}, + {{0x0fc2073c, 0x00605b67, 0x110920e3, 0x03186c55, 0x05335c85, 0x10d2a568, 0x0e43be30, 0x07aa3bae, 0x4d1d69}}}, + /* 13*16^17*G: */ + {{{0x077f88ba, 0x1de8f04c, 0x06fb4dbb, 0x100ef3dd, 0x02cc3509, 0x11974275, 0x04f8d2d6, 0x09913085, 0xcc9821}}, + {{0x07885278, 0x123177e4, 0x0d531382, 0x0c1cfdde, 0x163abeff, 0x053233be, 0x1ea44531, 0x1d1ca4b5, 0x4ebb4}}}, + /* 15*16^17*G: */ + {{{0x1d70187c, 0x0b98ca6f, 0x19031fc5, 0x1590c536, 0x15bf8751, 0x12f01487, 0x043e6053, 0x00534854, 0xad3ae}}, + {{0x1fc775a7, 0x0ffe7a59, 0x18a83d9f, 0x09f6102c, 0x1b33dd03, 0x0af82fd5, 0x1b8de171, 0x1a8eb108, 0x2af040}}} + }, + { + /* 1*16^18*G: */ + {{{0x10aae231, 0x0557d68b, 0x1e5adf18, 0x0970a4f3, 0x1756f519, 0x0411c933, 0x0fca17c9, 0x0d32ec3c, 0x1d35c9}}, + {{0x0cd6ac71, 0x033831d6, 0x0699bc56, 0x0b1548e5, 0x1f810edf, 0x055b1175, 0x008c7598, 0x16c5bec1, 0xc7226c}}}, + /* 3*16^18*G: */ + {{{0x0cfa3fac, 0x1069ff0c, 0x0ae064f3, 0x1b0d3f86, 0x1803023a, 0x1da2eb06, 0x0d338b3a, 0x08f4da44, 0xdf3d40}}, + {{0x0fc7d419, 0x03136c9c, 0x020a5d7d, 0x0c79c92d, 0x19dbfafd, 0x10f94e07, 0x036c92a8, 0x0a453fa8, 0x48fa6f}}}, + /* 5*16^18*G: */ + {{{0x1f1322d4, 0x16c92d70, 0x13372eeb, 0x14d28095, 0x1f822cb0, 0x16265188, 0x0513a879, 0x0d563f2c, 0xdbb2af}}, + {{0x0a8c5311, 0x12495a7a, 0x1f8e97ea, 0x1f92f0f6, 0x13f9d9a9, 0x068f6b21, 0x186a86d8, 0x1725e26a, 0xebae75}}}, + /* 7*16^18*G: */ + {{{0x01138f7b, 0x034d19e5, 0x1cba3cbc, 0x03927c5b, 0x15f0bc4c, 0x05133fcd, 0x1f532bea, 0x0b2fe4c3, 0x1dba7b}}, + {{0x039bf268, 0x0416c63d, 0x008cb037, 0x0dcd6f33, 0x007f5813, 0x08035dc0, 0x1f18eb86, 0x07cbbcfd, 0x6aaf7d}}}, + /* 9*16^18*G: */ + {{{0x179a70af, 0x0d43bb80, 0x1937ca23, 0x1a853536, 0x105c6ec9, 0x1416afa9, 0x0674c6ef, 0x0884c4d2, 0x348a2e}}, + {{0x07b66e82, 0x142430a4, 0x184f96b2, 0x0f858b6f, 0x10105b2a, 0x1de70011, 0x1a0231d1, 0x0f3eab47, 0xa1b9fa}}}, + /* 11*16^18*G: */ + {{{0x04d3a201, 0x1a3b6bb6, 0x06e7bbf1, 0x124d49fb, 0x0df5f36e, 0x07dfc98d, 0x1872a1a9, 0x0a333393, 0x3a4ec8}}, + {{0x08635f89, 0x0c2cb7bb, 0x0f3556f7, 0x063cb5ee, 0x14a8b27d, 0x1e08d23b, 0x0b79a780, 0x1f05ec4c, 0xc99a2e}}}, + /* 13*16^18*G: */ + {{{0x1c978567, 0x17d7397e, 0x1707e607, 0x1b4d1081, 0x1f60be15, 0x1aeb17f8, 0x115e13a4, 0x1b10669a, 0xb1ba52}}, + {{0x162dc291, 0x1fd24151, 0x1f35029d, 0x08d96175, 0x1b78fd9a, 0x1e7b89ed, 0x0e9df17d, 0x18f50d28, 0xa46bae}}}, + /* 15*16^18*G: */ + {{{0x12350da1, 0x11477528, 0x1d7c10f4, 0x0298ef82, 0x12c2f194, 0x1bdcb4b0, 0x0bf49c62, 0x07b1de55, 0xae6eb}}, + {{0x10a14bc3, 0x19f04f9b, 0x10f692f1, 0x08d1e1da, 0x189b566d, 0x1a7bfe20, 0x12b8b740, 0x13f6c00d, 0x99cdee}}} + }, + { + /* 1*16^19*G: */ + {{{0x150e4f5f, 0x0b957543, 0x19f83995, 0x0b95354a, 0x0f29acbf, 0x187bd501, 0x0bbce23f, 0x0b30896b, 0x55d9a9}}, + {{0x1ca97db0, 0x02c75bb5, 0x0b46c572, 0x10218c67, 0x0ec524c9, 0x0ba7de64, 0x080dd9b5, 0x1354bb5a, 0x69cb7f}}}, + /* 3*16^19*G: */ + {{{0x1eb142e9, 0x0354c43f, 0x01ff3ef0, 0x0dd60c8c, 0x0d480c17, 0x0341c7d7, 0x1845e536, 0x1d7c8de7, 0x4b0043}}, + {{0x02c68552, 0x0596296b, 0x04962201, 0x06521e74, 0x02d870f2, 0x04231b11, 0x106b6c5e, 0x047461b5, 0x173ccf}}}, + /* 5*16^19*G: */ + {{{0x17f1aa96, 0x16ea5738, 0x1fd9207d, 0x0f4bee69, 0x063c513d, 0x06e5db9a, 0x1f08c9ca, 0x0f3255ad, 0x79ead2}}, + {{0x0b0a3fb7, 0x05d38e72, 0x0ce556a0, 0x09ae2223, 0x16542de8, 0x01c9ab12, 0x0afc69b9, 0x19ff755b, 0xa95de9}}}, + /* 7*16^19*G: */ + {{{0x193a9ae6, 0x002e5397, 0x1dfe2b72, 0x01540b38, 0x1743f8ee, 0x0f1b18d6, 0x073ad49e, 0x0a1e49b4, 0x318e00}}, + {{0x1c447d79, 0x025ab4a6, 0x14ea3b86, 0x06dfb75d, 0x04bd0945, 0x1e528f28, 0x0a67d345, 0x022339d2, 0x2792ee}}}, + /* 9*16^19*G: */ + {{{0x1d30585a, 0x1bca2da6, 0x14052544, 0x1f143546, 0x0a4b495f, 0x197a4860, 0x10eee200, 0x0e3a5e3c, 0x6bbc54}}, + {{0x148b05f3, 0x17cbf779, 0x1e7ab65a, 0x00850205, 0x026be09f, 0x1158af91, 0x07c12a7b, 0x136182a5, 0x467b18}}}, + /* 11*16^19*G: */ + {{{0x00136275, 0x1127c046, 0x01dd4d49, 0x0f8002e7, 0x00d78a64, 0x0d487bbb, 0x144626d4, 0x18c2183f, 0x940148}}, + {{0x1997aa47, 0x0b1d088a, 0x13869097, 0x1675cac0, 0x1d6695d4, 0x06255a82, 0x13ddea89, 0x0be26cf4, 0x6967f9}}}, + /* 13*16^19*G: */ + {{{0x1f0fe850, 0x1d09ee9c, 0x0469ef70, 0x1a4617ca, 0x1f98e864, 0x164946d2, 0x127246d5, 0x124c0736, 0xc41833}}, + {{0x1d38666d, 0x1ac7a235, 0x0682bc04, 0x14fd93c0, 0x1d558065, 0x0aea1357, 0x1debae02, 0x19391a81, 0x3537fe}}}, + /* 15*16^19*G: */ + {{{0x14937fbe, 0x1f2209bb, 0x146e9bb3, 0x009bbbe0, 0x0f546c0b, 0x0000d7cb, 0x15c38305, 0x1ec4f8cf, 0x2baba3}}, + {{0x173a1df4, 0x1c872aba, 0x0b204424, 0x1b896321, 0x0abc9ec2, 0x1866c927, 0x0e235e0e, 0x0cb64411, 0x77e5c7}}} + }, + { + /* 1*16^20*G: */ + {{{0x0351964c, 0x069e96f2, 0x1504b075, 0x12486ede, 0x15c08346, 0x1e50c2ba, 0x11feb96a, 0x0b37c518, 0x6e29f9}}, + {{0x163fd88f, 0x0c4125ea, 0x02fed8c4, 0x0818a4f4, 0x0246def6, 0x163660c2, 0x0bd9414b, 0x13ea01e6, 0x34565d}}}, + /* 3*16^20*G: */ + {{{0x0c0e49cc, 0x1ca8081c, 0x1150034f, 0x065b50b7, 0x140ed412, 0x046f65db, 0x1dbb760a, 0x152f12e1, 0xd691d4}}, + {{0x100f4152, 0x085da60e, 0x1410fcb7, 0x17c3a055, 0x00c52ac5, 0x1edabb1f, 0x0e5fdfee, 0x10e96f1e, 0x7a79e7}}}, + /* 5*16^20*G: */ + {{{0x0b9b930a, 0x0c3b5cf9, 0x0c3d63cf, 0x026a548a, 0x1bc49deb, 0x1befbd3d, 0x1f177b96, 0x08d45c1a, 0x2a5d68}}, + {{0x1b2caeca, 0x17f9a2f9, 0x09c5a161, 0x16e686bc, 0x0ab58ea5, 0x181c81e2, 0x1db79733, 0x012d0ec8, 0xdc3d7c}}}, + /* 7*16^20*G: */ + {{{0x016959ef, 0x13ee5e94, 0x076d66be, 0x13a0ace8, 0x15df8767, 0x18a09713, 0x1498bc10, 0x0d471376, 0x876449}}, + {{0x00cd5010, 0x10188e5f, 0x1e78fc59, 0x0d5a82e5, 0x1961f285, 0x0093cb76, 0x1ff6782d, 0x1ac3a005, 0x599b69}}}, + /* 9*16^20*G: */ + {{{0x1aef0f84, 0x1ca04e71, 0x071d6e58, 0x16a0d50e, 0x1b8cab0b, 0x1fd60bd6, 0x06c4cf78, 0x1790248e, 0x94d393}}, + {{0x178ba7c1, 0x0d730dc9, 0x0b3c4aa1, 0x1e804ca1, 0x19a07dd3, 0x1e1c3591, 0x0fc87872, 0x169d696c, 0x5a826a}}}, + /* 11*16^20*G: */ + {{{0x0ffed1b7, 0x0a2abc27, 0x12a8ed3b, 0x17a24cac, 0x0bd2ee2d, 0x04b8169a, 0x18b745d4, 0x141113c9, 0x4a72b5}}, + {{0x1601dc5f, 0x0f94fec4, 0x1366116d, 0x0c971d8e, 0x0ea9685e, 0x1fe023e4, 0x038b230c, 0x1d4943a4, 0x3531e6}}}, + /* 13*16^20*G: */ + {{{0x10467317, 0x1021f92e, 0x16461a80, 0x03b883b1, 0x07900914, 0x13797d6f, 0x18569e19, 0x1f8b46e3, 0xd7f01c}}, + {{0x0f7d014e, 0x05cabeae, 0x1fef6257, 0x002e86d2, 0x1ef5728a, 0x10a0360a, 0x109bb1cd, 0x1b30ee4d, 0x888dbb}}}, + /* 15*16^20*G: */ + {{{0x1dea02c1, 0x1ebac853, 0x1d021f0e, 0x17736f8e, 0x11206e4f, 0x1fcec8f1, 0x1c6efa02, 0x173eef86, 0x7e50a0}}, + {{0x0d45c201, 0x1e4a36ff, 0x0386ca0c, 0x07269e2b, 0x19517742, 0x178eedc5, 0x0a7185b1, 0x0789c1fc, 0xc3405d}}} + }, + { + /* 1*16^21*G: */ + {{{0x0cb71280, 0x053ea088, 0x1158c44a, 0x0384b350, 0x1458ec14, 0x17783cb7, 0x1b67003c, 0x13d657fd, 0xff046a}}, + {{0x1ec33919, 0x161938fa, 0x0a121e9f, 0x16dcdd7a, 0x0fcc9012, 0x16edeea6, 0x085c2807, 0x159812a7, 0x432f55}}}, + /* 3*16^21*G: */ + {{{0x13ca3ce3, 0x193c1551, 0x1a9df897, 0x1630cb56, 0x0231fef2, 0x1e0fa989, 0x134c6a56, 0x0ba29a7b, 0xf717c4}}, + {{0x10ed88e9, 0x04a666da, 0x05db7c1a, 0x07c57bb8, 0x1ee55af5, 0x0a768556, 0x04b72a6d, 0x0ed1932a, 0x422c40}}}, + /* 5*16^21*G: */ + {{{0x1b28cd88, 0x1b42186a, 0x0a6f66b0, 0x1c12ea05, 0x1b32c738, 0x1ab13fc8, 0x0c830bfa, 0x169e66e1, 0x60e269}}, + {{0x0be2066c, 0x14dd4521, 0x1211ac38, 0x0ac2718d, 0x0df76024, 0x08c44b94, 0x02a58248, 0x0b4caf3e, 0xfe0017}}}, + /* 7*16^21*G: */ + {{{0x061cc594, 0x09d8c994, 0x1414dac5, 0x17ba2e09, 0x06855964, 0x1d02fe5a, 0x05242a96, 0x143cce73, 0x799297}}, + {{0x0f8d309e, 0x14888490, 0x0b1ba427, 0x150d5c17, 0x1e50127d, 0x088c3c4f, 0x06c3841a, 0x119aa479, 0x132ff1}}}, + /* 9*16^21*G: */ + {{{0x172d0aba, 0x04623d12, 0x1f86c5b7, 0x03215b8e, 0x1241dd8d, 0x0775aa80, 0x01c1e7b8, 0x09dfc850, 0x2d9be9}}, + {{0x11fa0876, 0x07b98fd9, 0x0dc439be, 0x038cf311, 0x1d91bbad, 0x1c9a5b0c, 0x0288a57b, 0x16bd3d13, 0xa9708d}}}, + /* 11*16^21*G: */ + {{{0x155e8732, 0x0603da41, 0x1b478c78, 0x0c5dbca0, 0x0c243cfa, 0x03298a49, 0x0859832f, 0x13d91048, 0x212703}}, + {{0x00cacc07, 0x0c91afa3, 0x15ef5a35, 0x07e7e775, 0x10a5cd69, 0x1cedddbf, 0x16a80652, 0x0de475c6, 0x85219c}}}, + /* 13*16^21*G: */ + {{{0x12083347, 0x0489a067, 0x0694c898, 0x15c7ccee, 0x0b42b9e1, 0x07ad5d01, 0x03ae5eb4, 0x09657b1c, 0xfab5b5}}, + {{0x1d38fd2f, 0x0664e6c1, 0x09a6bea7, 0x1c6f4c2d, 0x03eafc05, 0x15791305, 0x0540f21e, 0x02c30da8, 0x5758d1}}}, + /* 15*16^21*G: */ + {{{0x1f572554, 0x1b3930a7, 0x1d36adcf, 0x085c8e31, 0x0a56ff38, 0x0b3b85bf, 0x1f295e42, 0x0ae4f2e3, 0x685d27}}, + {{0x00979e03, 0x18d7b685, 0x1d33292d, 0x15545581, 0x0377cc58, 0x0e4020f0, 0x17f01b8e, 0x18441568, 0xa82f89}}} + }, + { + /* 1*16^22*G: */ + {{{0x05852e50, 0x1e859266, 0x15c33171, 0x18fcc79e, 0x07eff8b2, 0x1511a4f7, 0x016307e6, 0x1bffd576, 0xe486c7}}, + {{0x0ecf107d, 0x15d1e56d, 0x04baf619, 0x08c7ac67, 0x1e05a694, 0x052e4fa8, 0x04ba7ba2, 0x1daac0d4, 0x51fd75}}}, + /* 3*16^22*G: */ + {{{0x1c881907, 0x1355322a, 0x002c76b1, 0x190ce6b6, 0x00415142, 0x02e63357, 0x0df25904, 0x1771c5ff, 0x4acf44}}, + {{0x163cdafc, 0x0935a2b7, 0x1612c94a, 0x0a565ef9, 0x05457993, 0x142d6885, 0x01ecd510, 0x1fc1ffab, 0xad99a8}}}, + /* 5*16^22*G: */ + {{{0x1439383f, 0x15d7034b, 0x0068aaa0, 0x1f4ac56c, 0x14eec34f, 0x11496411, 0x192775ea, 0x10683114, 0x4ddf22}}, + {{0x03d52337, 0x197d40c1, 0x07e09a58, 0x0ba08a26, 0x00c6b3dd, 0x174be9c6, 0x00f5d2ae, 0x0f61b231, 0xe71c62}}}, + /* 7*16^22*G: */ + {{{0x1822b4f4, 0x16e440aa, 0x1977e3c9, 0x0a75f9f0, 0x1f39ba53, 0x14f03cbd, 0x1e784cd3, 0x0f226f30, 0x58b065}}, + {{0x0c60fbca, 0x103d4108, 0x13949659, 0x0940edf8, 0x0836c1ca, 0x12af8131, 0x10dc09f2, 0x14828b45, 0x55a98a}}}, + /* 9*16^22*G: */ + {{{0x14fe39b0, 0x195e8861, 0x0d37b452, 0x02e79ae9, 0x01f358a3, 0x020e119c, 0x14a5d8e9, 0x0b14c966, 0xa345e0}}, + {{0x0baf79dc, 0x184a405d, 0x0da77d4d, 0x18d4eb56, 0x1d949ce8, 0x02ad947c, 0x1c3e47c2, 0x14dab954, 0x7c6dd5}}}, + /* 11*16^22*G: */ + {{{0x126695fb, 0x1df03dcc, 0x0eb7b84f, 0x01773fc8, 0x0ccb6afd, 0x05c3a36b, 0x0b20806b, 0x10603214, 0x3a10bf}}, + {{0x04eb7e47, 0x0ae39595, 0x1514a21d, 0x1fd4d887, 0x065c5f28, 0x1c243445, 0x1c55d880, 0x167d8344, 0xb5d585}}}, + /* 13*16^22*G: */ + {{{0x1d2c8614, 0x00d474b3, 0x16116589, 0x012c14c0, 0x12876a7b, 0x1ad61848, 0x10cf2973, 0x1f592020, 0x12fbdd}}, + {{0x18b0b174, 0x03bde0ea, 0x067d257c, 0x04b04738, 0x0cfb54d4, 0x08a14669, 0x01a0cff6, 0x1a5204f0, 0xa5ff20}}}, + /* 15*16^22*G: */ + {{{0x0d9aeeaf, 0x1fbb2181, 0x16bf2194, 0x00f29940, 0x19a3005d, 0x11d0fd7c, 0x01ddee2b, 0x0b34572f, 0x8bece8}}, + {{0x05f9eb5e, 0x0d06d1e4, 0x02603dd5, 0x04071edf, 0x1c368bb2, 0x0885877f, 0x07f3eee2, 0x1898a0ca, 0x35f933}}} + }, + { + /* 1*16^23*G: */ + {{{0x0b519178, 0x05b5c761, 0x0f47f161, 0x17333148, 0x0ee0ab63, 0x067c5af1, 0x10c33f82, 0x0976bca0, 0xf41d7f}}, + {{0x0a6a3551, 0x1d0b32ee, 0x06787e69, 0x135986d2, 0x02347ab4, 0x16f1fd54, 0x035bc411, 0x17d7b35f, 0xe6a669}}}, + /* 3*16^23*G: */ + {{{0x1e3501d9, 0x138989f0, 0x187f22c6, 0x1059c376, 0x13e611be, 0x0b48eb16, 0x19fccccb, 0x0fdac5a9, 0xd65f82}}, + {{0x0b55759e, 0x1ca23637, 0x1cc2f86a, 0x02755aba, 0x187fc61c, 0x1e528b35, 0x03f94ded, 0x1f9e1352, 0xf24b60}}}, + /* 5*16^23*G: */ + {{{0x0f2ed84e, 0x1b17f120, 0x18ba84b2, 0x129be6bf, 0x0c0ade79, 0x045abc16, 0x10e17b9b, 0x16dbd60b, 0x55a9b5}}, + {{0x146495a3, 0x1cee2a82, 0x08363c15, 0x1f69ae25, 0x1f43ae8f, 0x068c876b, 0x06aca45d, 0x12d593e3, 0xc4f221}}}, + /* 7*16^23*G: */ + {{{0x075495a2, 0x1e967c38, 0x0f002ca6, 0x1c3d6c91, 0x08bc49fd, 0x0b05312a, 0x07687dfb, 0x0db8fb6e, 0x79de57}}, + {{0x0aec6fb8, 0x01646355, 0x04e9d1a0, 0x09ea12c9, 0x079c6831, 0x16d723ee, 0x0809e179, 0x01cd630d, 0x3b2d3d}}}, + /* 9*16^23*G: */ + {{{0x02d1e7bb, 0x17495664, 0x0465933b, 0x07ff206a, 0x1e55524c, 0x19b4acab, 0x12db8992, 0x0642c480, 0x8fe04e}}, + {{0x1a62e84f, 0x13acd686, 0x011aa336, 0x02c8c8de, 0x1b34e5b9, 0x0752bd46, 0x163f975b, 0x0a045ec6, 0x3987db}}}, + /* 11*16^23*G: */ + {{{0x09f7c360, 0x00d25763, 0x00217084, 0x19aaefc9, 0x024e24ef, 0x0531b469, 0x03c542a5, 0x012afdbf, 0x3af499}}, + {{0x087d7a7b, 0x1cee1553, 0x18225734, 0x0a9bac4e, 0x13c33485, 0x03241739, 0x04754c7e, 0x061a763b, 0xd17060}}}, + /* 13*16^23*G: */ + {{{0x170373be, 0x0d291d73, 0x18f4ed4e, 0x1a0c4424, 0x07c4e213, 0x0179ff11, 0x01db0696, 0x0b8c4790, 0x31a7e0}}, + {{0x090950a8, 0x16bc3c87, 0x08950c23, 0x105732a3, 0x1db1e8ce, 0x05571a11, 0x199c086c, 0x1e7f1a3b, 0xc23d2e}}}, + /* 15*16^23*G: */ + {{{0x0b923b45, 0x0130876e, 0x0f892f6d, 0x153f7fe2, 0x18458a33, 0x09a316ec, 0x08fb4554, 0x09026ada, 0x740a66}}, + {{0x11ecaa5f, 0x0609f1c2, 0x121202ea, 0x0f32720f, 0x006cfa89, 0x03c453ab, 0x08fcbd39, 0x1184b9aa, 0x7f6d04}}} + }, + { + /* 1*16^24*G: */ + {{{0x1512218e, 0x025549cb, 0x1280506a, 0x0a4360e9, 0x0e902e9a, 0x059d0c51, 0x1e995e20, 0x0cc254ce, 0x4a5b50}}, + {{0x0c4f3840, 0x1f56d3d2, 0x189b6742, 0x1b62a833, 0x07d40626, 0x027df0b1, 0x07c71098, 0x039d5811, 0xeb1346}}}, + /* 3*16^24*G: */ + {{{0x118473fd, 0x177a0712, 0x05cce454, 0x1204d78d, 0x0a9e3bbb, 0x155ccb94, 0x0e8214a4, 0x06466631, 0x106406}}, + {{0x185cf805, 0x1d30b173, 0x08740e6b, 0x1c321a18, 0x1883ccfb, 0x014f32c2, 0x0b63239e, 0x10477174, 0x9c6832}}}, + /* 5*16^24*G: */ + {{{0x053f30f3, 0x0b3f7643, 0x088e374d, 0x047da5db, 0x03f212b4, 0x0086aa04, 0x1efd7b69, 0x11fe0be4, 0x38c8ad}}, + {{0x0be9217b, 0x03073a4f, 0x1677cde8, 0x07f53052, 0x1641f658, 0x0b2a6ede, 0x045bce42, 0x0f0edb96, 0x83c261}}}, + /* 7*16^24*G: */ + {{{0x16ef1584, 0x01f3488d, 0x04832fd5, 0x1345471f, 0x1184277a, 0x1df1fe9a, 0x0c256163, 0x179d0c17, 0x6fcb8c}}, + {{0x1d3110f2, 0x1e4fbe10, 0x0290c970, 0x05edad25, 0x0459d599, 0x0411d618, 0x15db5530, 0x07b16e43, 0xa8f1a8}}}, + /* 9*16^24*G: */ + {{{0x062255ef, 0x07a89a02, 0x1901e33a, 0x0e3a68ce, 0x1b0ee50d, 0x0e486f22, 0x0af5cd45, 0x1727eedc, 0x7811d0}}, + {{0x1be3252e, 0x0c5f7cd8, 0x150dda29, 0x1da0a275, 0x05132f51, 0x174a00dc, 0x11e95300, 0x06e176b5, 0x99bb76}}}, + /* 11*16^24*G: */ + {{{0x153d2ea7, 0x1f088789, 0x1ea19424, 0x1a104747, 0x16f2a9ee, 0x194eafc0, 0x033831ed, 0x1289516a, 0xc3583d}}, + {{0x0b667bbd, 0x18c1f4f0, 0x17c710ca, 0x124b4ca2, 0x1cccdd6c, 0x113f7c4f, 0x043d6591, 0x1812723d, 0x53967a}}}, + /* 13*16^24*G: */ + {{{0x102e3099, 0x16ef8702, 0x01005963, 0x04274cc5, 0x0d00d53f, 0x180c5dbd, 0x1407ce83, 0x05079bdc, 0xd591cf}}, + {{0x055ac5af, 0x1de13b2a, 0x045bf58c, 0x05dff116, 0x1c5825cf, 0x19869a85, 0x1b913944, 0x19f88f1c, 0x87ebe5}}}, + /* 15*16^24*G: */ + {{{0x12a940a5, 0x1932b231, 0x0539ac4a, 0x06bd6b48, 0x1e8714a4, 0x00f0d27c, 0x17130049, 0x0c25e646, 0xa0aa0d}}, + {{0x0b950422, 0x1fc41534, 0x146fe4e4, 0x1239bb4f, 0x01f3e6c6, 0x067cd00c, 0x10a066cd, 0x174edeec, 0x22340e}}} + }, + { + /* 1*16^25*G: */ + {{{0x008e2df0, 0x1146c1b8, 0x0a397dd9, 0x0764ae86, 0x00f5032c, 0x14efc5df, 0x065404b0, 0x017bc557, 0x2eb391}}, + {{0x1274eaae, 0x08866276, 0x1d97d242, 0x01a241d9, 0x0999f954, 0x1e9a46d2, 0x0ce9df4d, 0x0466e8e9, 0x3f29c0}}}, + /* 3*16^25*G: */ + {{{0x082867f0, 0x1815c25b, 0x06428e6f, 0x084dc436, 0x100f0a21, 0x08b53c04, 0x1388aaaf, 0x111cc98f, 0xf6e9db}}, + {{0x0f1861ea, 0x1ad9d788, 0x1c2d88d1, 0x08374bf2, 0x0d5b1270, 0x1dbb7460, 0x0dd20764, 0x016f5a55, 0x53ca79}}}, + /* 5*16^25*G: */ + {{{0x120d6102, 0x16e821c7, 0x114e5026, 0x1aa6f146, 0x19a5ef06, 0x0adcdb0c, 0x1275e170, 0x070ec1c8, 0xfd1ddb}}, + {{0x0e003b7c, 0x1053248d, 0x144e60f6, 0x1c322422, 0x1b700163, 0x0f8fbc41, 0x0e2bb6a8, 0x0e720a0c, 0xcb54b8}}}, + /* 7*16^25*G: */ + {{{0x02c77cb7, 0x1fb9a0ee, 0x17056dbc, 0x1b281205, 0x0698fef6, 0x139f32f7, 0x00767f92, 0x1844b332, 0x61a273}}, + {{0x1ddb25ed, 0x0a308fc0, 0x0b87dd21, 0x0b5b34e1, 0x10cc9c5c, 0x10cfaf75, 0x0f4fd3a8, 0x0669a75a, 0x5bbacd}}}, + /* 9*16^25*G: */ + {{{0x177d8976, 0x0cc6ab71, 0x03ca4b6e, 0x0e7471c6, 0x104b55e1, 0x164c114e, 0x06d932c0, 0x0cbdeec0, 0xcd2e8e}}, + {{0x0867bc22, 0x0b4b7a0e, 0x10a30144, 0x1dbc1e6a, 0x0ff68f60, 0x074796a3, 0x0c7ff0c7, 0x06c46854, 0xf58ead}}}, + /* 11*16^25*G: */ + {{{0x0c67c998, 0x04d98361, 0x13c6e198, 0x160fd547, 0x04c259a9, 0x0f545218, 0x1bed0089, 0x13870447, 0x9bd61f}}, + {{0x08199514, 0x1a057ce1, 0x1092c630, 0x1b383d20, 0x050fa927, 0x104b4b4a, 0x1d71723c, 0x01322d8d, 0x77b204}}}, + /* 13*16^25*G: */ + {{{0x0aafa568, 0x122f0bdf, 0x07889d9a, 0x1af52ee0, 0x1a016b4c, 0x13d2088b, 0x1dd44ab8, 0x09ef2e0e, 0x7afaeb}}, + {{0x01c1f2df, 0x16a9d17c, 0x14e408cf, 0x1cd28653, 0x1365a972, 0x0a09a820, 0x09f62574, 0x03267f7a, 0xc6efe6}}}, + /* 15*16^25*G: */ + {{{0x0e59ddeb, 0x1f381f28, 0x07a62a2d, 0x1cc5395a, 0x10c3b483, 0x0a60a4b5, 0x0be41876, 0x044fc482, 0xd9a002}}, + {{0x0f0af5a4, 0x19c9ffc0, 0x17c63397, 0x05517956, 0x10581856, 0x07c521b3, 0x08b10f18, 0x1f276f40, 0x975eb2}}} + }, + { + /* 1*16^26*G: */ + {{{0x1ecca7e0, 0x19cd2f51, 0x10cccfb1, 0x05931ece, 0x19428a7d, 0x119a9126, 0x08303fbd, 0x078b8f25, 0x7ef2ee}}, + {{0x152ac094, 0x015916ea, 0x0f4f480c, 0x0428a1bf, 0x009db81b, 0x1fa8eaf3, 0x004693d9, 0x04e61598, 0xafb686}}}, + /* 3*16^26*G: */ + {{{0x033f0ffa, 0x179ac1ed, 0x001e905c, 0x09958c23, 0x1c641cae, 0x12bb64ec, 0x0b35d892, 0x04e2d10f, 0x370457}}, + {{0x00fa35ac, 0x1f69fe33, 0x02fcbf17, 0x147d6f55, 0x1ff4c57b, 0x1a5dc44a, 0x1d3e106e, 0x18c34e3c, 0x5c6408}}}, + /* 5*16^26*G: */ + {{{0x12603513, 0x037cc329, 0x1280254f, 0x0c9b8d02, 0x1b697f6c, 0x15216045, 0x0dc8b47a, 0x1ca8531b, 0xc2a979}}, + {{0x1c3505b9, 0x0baad961, 0x05451265, 0x0ccdbe90, 0x1ce7e8cc, 0x0040c9ad, 0x1721509e, 0x0943cdd6, 0xef9dd7}}}, + /* 7*16^26*G: */ + {{{0x19fdee3e, 0x1237fd8e, 0x0c0b8079, 0x1f2ee2d8, 0x1afb9681, 0x18ae6d8b, 0x07845cae, 0x0d563f8b, 0xc9f418}}, + {{0x0ced22e7, 0x0f33662e, 0x1637d3d4, 0x02a638f6, 0x1214344e, 0x18a15c41, 0x0d0437dc, 0x0fe79674, 0x9420a}}}, + /* 9*16^26*G: */ + {{{0x0814f217, 0x0659f33a, 0x15b3febc, 0x147ef70e, 0x1b6a4a9d, 0x00127c1d, 0x095ac228, 0x0b700900, 0x27dcf5}}, + {{0x0cf2ed4c, 0x064a5ac4, 0x07f0efc5, 0x1d1f5632, 0x02a380a4, 0x1d584a50, 0x17b82f22, 0x09ea91ab, 0xbe18f5}}}, + /* 11*16^26*G: */ + {{{0x1b9e310c, 0x0d1527ea, 0x133f529d, 0x19cb7370, 0x19187313, 0x125619ca, 0x092e7234, 0x00764add, 0x3b8b02}}, + {{0x0d19fc9b, 0x0a0b302d, 0x0cc160e6, 0x1f009705, 0x117a6a8f, 0x171bbe20, 0x056f585d, 0x09d6066c, 0x91117c}}}, + /* 13*16^26*G: */ + {{{0x07615b36, 0x18f0f15d, 0x1cf057e3, 0x1fe3d18f, 0x0c3372a4, 0x12037c8a, 0x00e84170, 0x118b7982, 0xa5e739}}, + {{0x09136d80, 0x0cc171ca, 0x0d84f013, 0x1f8326e1, 0x1f8c547f, 0x06f7950a, 0x1339190e, 0x11b92e9c, 0x1f3ff5}}}, + /* 15*16^26*G: */ + {{{0x0d3e10db, 0x1bd82197, 0x0e1c566e, 0x065c3591, 0x161fa477, 0x021ffa75, 0x0caf7b81, 0x06ab592a, 0xaad218}}, + {{0x0fa05fb3, 0x1a9ad5eb, 0x072e89d8, 0x064ee566, 0x18b4a720, 0x0f147eaf, 0x18ece9f7, 0x1dd75f05, 0x793b1e}}} + }, + { + /* 1*16^27*G: */ + {{{0x0c49e853, 0x180f8487, 0x1d781299, 0x16a6a532, 0x1a77aa9c, 0x12aa75af, 0x0bad5e00, 0x0c842c81, 0xe5141}}, + {{0x16405cb2, 0x1b71e89b, 0x127b8d8c, 0x10728321, 0x1030df56, 0x1a529d48, 0x11a49e3a, 0x1d4cb20a, 0xcf331c}}}, + /* 3*16^27*G: */ + {{{0x1dc6afad, 0x0511f8e0, 0x1e03d1b1, 0x07b2452b, 0x0c6771bd, 0x092fb260, 0x1c977a3b, 0x0cecb959, 0x977ba0}}, + {{0x1d7faa09, 0x0b166e28, 0x16868553, 0x0b42483b, 0x1d9992f7, 0x1d506840, 0x1e8a20cf, 0x0875db71, 0x1bc3bc}}}, + /* 5*16^27*G: */ + {{{0x1d81983b, 0x0fba2ebe, 0x03aade19, 0x041fa05f, 0x1facc102, 0x1b03b8ff, 0x0af930f2, 0x0c254247, 0x3bc328}}, + {{0x11a637b6, 0x0ccf9428, 0x0ee1236f, 0x0910b51f, 0x00a7f433, 0x182e5578, 0x0880942c, 0x0dbda191, 0xa6b582}}}, + /* 7*16^27*G: */ + {{{0x17c8537b, 0x0fbb60ff, 0x07e0185d, 0x19596e88, 0x16c21ca8, 0x17f0e57c, 0x12b146eb, 0x1d1d3d80, 0x67cba4}}, + {{0x185726da, 0x075a82b9, 0x100fdb87, 0x1e087da0, 0x1cc047bd, 0x030f97d8, 0x06128787, 0x1dbd4391, 0x5b65ed}}}, + /* 9*16^27*G: */ + {{{0x0b6a1226, 0x14114672, 0x15194e4d, 0x13d39156, 0x0f3922af, 0x1eb43234, 0x064c681c, 0x14daae93, 0xc5958e}}, + {{0x066961d4, 0x00803446, 0x08422e47, 0x0edb7d74, 0x1bd82968, 0x0cb8cea2, 0x175fb2f8, 0x105ee1ef, 0xa031a8}}}, + /* 11*16^27*G: */ + {{{0x13713a52, 0x06f1fc2d, 0x0f1505f9, 0x0eb718d0, 0x05fa025c, 0x0061e3a5, 0x129f07f3, 0x1f05c84b, 0x83f07b}}, + {{0x1fb0ffb6, 0x088939fc, 0x1ed174a8, 0x0b6ec1c3, 0x1d3cfcab, 0x0a648963, 0x0ee4125e, 0x02144b71, 0xf2ec4f}}}, + /* 13*16^27*G: */ + {{{0x01ba21b0, 0x16cd0489, 0x109954e6, 0x0bb1a401, 0x00076d85, 0x00343757, 0x0b539bda, 0x09f36b48, 0x938dba}}, + {{0x0d5b4325, 0x1a6ccaf2, 0x0ada5f0d, 0x0377dc7e, 0x0b90bcbf, 0x16dee89b, 0x1959ba6a, 0x15135eea, 0x48afc0}}}, + /* 15*16^27*G: */ + {{{0x1f2b24f1, 0x09f24d31, 0x19daf3ed, 0x1d8c9ebf, 0x09779cfa, 0x0d5679da, 0x079cea07, 0x16ed9406, 0x7efa89}}, + {{0x0f8e4b83, 0x12ee2ea5, 0x15291575, 0x15be758a, 0x0ab211db, 0x09117e0b, 0x1bfde33b, 0x1d8c3e95, 0xa99ff3}}} + }, + { + /* 1*16^28*G: */ + {{{0x02f2b734, 0x034cdfcf, 0x007499fc, 0x0776b6aa, 0x0445779c, 0x13c3788b, 0x066818d2, 0x0533dd99, 0x224a02}}, + {{0x11ec7fdf, 0x007ac2a4, 0x11ebf421, 0x0e096ce7, 0x17fff07b, 0x0456c38e, 0x0ad05268, 0x1a536da4, 0xfa41a8}}}, + /* 3*16^28*G: */ + {{{0x1d254033, 0x0f497a3a, 0x091c1eb2, 0x0d050185, 0x0f240783, 0x0fd3445b, 0x1167f36b, 0x1dc2a79d, 0xe63502}}, + {{0x08f05e3f, 0x1455444b, 0x1f3cd81f, 0x02b41897, 0x1ce482c9, 0x14078384, 0x1846b5d3, 0x0f63ec43, 0xa7c583}}}, + /* 5*16^28*G: */ + {{{0x01e08f33, 0x1cb04d7f, 0x060b328a, 0x1ac63b97, 0x000e2b1f, 0x02e299f8, 0x1a1c4aad, 0x184e5d72, 0x5648de}}, + {{0x0269f1c9, 0x1776cd62, 0x045e5449, 0x0577e5c0, 0x1a2693e7, 0x1ae326bb, 0x010fd1e3, 0x0e0e11ee, 0x95fddc}}}, + /* 7*16^28*G: */ + {{{0x1ab98c43, 0x1dc1005f, 0x0fa2a938, 0x14aeeafc, 0x08f03c5b, 0x17c765f6, 0x149a5454, 0x13e6701d, 0x1941c1}}, + {{0x02abe217, 0x1d1086d1, 0x17d58e63, 0x0c5a7b71, 0x18c53d8f, 0x0d95247d, 0x0adb8adb, 0x068b2bb6, 0x8e66b8}}}, + /* 9*16^28*G: */ + {{{0x02d8c2f4, 0x13386882, 0x07006279, 0x064373ef, 0x18a0bd73, 0x1def1b26, 0x0f59fe9c, 0x063a2169, 0xfc5864}}, + {{0x1cfd274a, 0x08c6447f, 0x10ce2d00, 0x05960bd7, 0x1b4ab29e, 0x013cf35e, 0x055928cf, 0x0a59d15b, 0x99fade}}}, + /* 11*16^28*G: */ + {{{0x11ff0f18, 0x1583bf8e, 0x12690b44, 0x0179ce0a, 0x0d6c207e, 0x1d67cd79, 0x077aa6fe, 0x14d6189e, 0xee993b}}, + {{0x050782ad, 0x009c3b65, 0x03cb2c60, 0x0b901bee, 0x1bcfeb43, 0x0eac6742, 0x0cb07db1, 0x1b28af1b, 0xd87fd4}}}, + /* 13*16^28*G: */ + {{{0x0fdde3fc, 0x03d3fbaa, 0x18a33279, 0x11113c0a, 0x131fbe5a, 0x0e20d297, 0x090a3683, 0x0f55d3fc, 0xba4951}}, + {{0x0fcb7d01, 0x1f7e9197, 0x0a4b2aa8, 0x1c3903c6, 0x1359adfd, 0x121bb009, 0x16542d3b, 0x1c39528a, 0xa88fb6}}}, + /* 15*16^28*G: */ + {{{0x1d14da68, 0x0449517b, 0x14805cde, 0x0983f09b, 0x012b54a6, 0x07c139bb, 0x06e9378b, 0x02e8455e, 0xd3398f}}, + {{0x15a90e3d, 0x183b18da, 0x10514e8f, 0x0f4f245a, 0x0b020afc, 0x0968f451, 0x1583adc9, 0x1a9a3fa7, 0xa34ad4}}} + }, + { + /* 1*16^29*G: */ + {{{0x1789b84d, 0x13032d6b, 0x010738ba, 0x1abdc9a2, 0x093fe167, 0x0888dab2, 0x0d3336d7, 0x028ae6e9, 0x4a89a6}}, + {{0x018e3ea8, 0x1cdbebf8, 0x1c46667b, 0x1c500c68, 0x077e6a72, 0x154bcff3, 0x1570230b, 0x10fda901, 0x45b04e}}}, + /* 3*16^29*G: */ + {{{0x02e14c34, 0x13b591ba, 0x0c9d64db, 0x18bd7c0e, 0x177c9834, 0x19873c09, 0x1e4a8aa6, 0x09150e1d, 0xcc9f96}}, + {{0x14be556b, 0x0490abd4, 0x031f8162, 0x122fe305, 0x089db299, 0x1319edf3, 0x05bd0c45, 0x0596bf19, 0x829bff}}}, + /* 5*16^29*G: */ + {{{0x15de4837, 0x1ef3642c, 0x1979d3a1, 0x050d905b, 0x18a758d4, 0x060e856c, 0x106bd3d6, 0x143f55a0, 0x5c618d}}, + {{0x034a7dee, 0x05c2c1db, 0x089e32b0, 0x06cc92c8, 0x19671ecd, 0x071c24fe, 0x067622e0, 0x02f79e74, 0x4c7191}}}, + /* 7*16^29*G: */ + {{{0x16635396, 0x0c49a132, 0x029b0309, 0x15c2b46d, 0x1c11ef2c, 0x116ef338, 0x1a8b9617, 0x0d522b40, 0xab8a88}}, + {{0x1fc92c23, 0x11c7d351, 0x14b5ab5f, 0x0c0b07ef, 0x0d5abb93, 0x1fc1245e, 0x0c6559a3, 0x0f8fb508, 0xe773cf}}}, + /* 9*16^29*G: */ + {{{0x01df49c3, 0x0121f2c4, 0x01a68d6b, 0x11210cda, 0x1604aa9e, 0x125c5b4a, 0x0e337b19, 0x0a284830, 0x62b7c8}}, + {{0x16341c21, 0x1f1b407f, 0x1f2d5426, 0x0a76c270, 0x122db8fb, 0x0b5746a4, 0x0cd56522, 0x042a55c0, 0xa71ed6}}}, + /* 11*16^29*G: */ + {{{0x044f47d0, 0x178a3717, 0x07c00bce, 0x13ebbd29, 0x1c9791b5, 0x0f8434ef, 0x0717e5d4, 0x17f48b91, 0x4897bd}}, + {{0x0e1320bf, 0x0f280d56, 0x1c046560, 0x1b400351, 0x1093f526, 0x1654a0bd, 0x0e30d3a3, 0x01ef5f82, 0xe77dc2}}}, + /* 13*16^29*G: */ + {{{0x11581bd3, 0x10de74db, 0x0641e1b9, 0x14631bec, 0x02f2ecbf, 0x103ded39, 0x15db1147, 0x16aac90c, 0xeb1cfc}}, + {{0x1cd98a09, 0x1e746cc2, 0x0f469916, 0x19e6338b, 0x16e3cb39, 0x002f5f26, 0x14814110, 0x0eae3f29, 0xfd47ce}}}, + /* 15*16^29*G: */ + {{{0x073a3111, 0x00915edb, 0x132063e4, 0x105c82f6, 0x176a018b, 0x05617392, 0x18270355, 0x177c91c3, 0xa1fd8b}}, + {{0x18c704ef, 0x043f3285, 0x15445a6e, 0x18928b7f, 0x116c0c7e, 0x1f03de04, 0x015a6f5d, 0x11469461, 0xad215d}}} + }, + { + /* 1*16^30*G: */ + {{{0x12bd05a0, 0x01c64253, 0x07f2034d, 0x0466fa16, 0x11f90ba8, 0x1ccaf9b6, 0x0173b70b, 0x06c74631, 0xe5e892}}, + {{0x01a69f5d, 0x09b6f15f, 0x14266bb2, 0x0732b739, 0x15c3eca7, 0x1580f3cd, 0x1f484c07, 0x1c9b4370, 0x77439d}}}, + /* 3*16^30*G: */ + {{{0x01467d6b, 0x184a9408, 0x0892d453, 0x1ae252a5, 0x0f1d8357, 0x0308b216, 0x13d74406, 0x1bf286b9, 0x5d2393}}, + {{0x11bc5458, 0x1e339e35, 0x011cea01, 0x0e0f4ea2, 0x0f46d72a, 0x0c2d96ad, 0x1df5eb2f, 0x1e4c7fa1, 0xe66e63}}}, + /* 5*16^30*G: */ + {{{0x1d159f7a, 0x058f49e4, 0x10b9643c, 0x127539e4, 0x1873fecf, 0x1d95e97f, 0x04fceb73, 0x14a75571, 0x453657}}, + {{0x0a02fb78, 0x0e115b84, 0x07769766, 0x0937a9d0, 0x1c7286f9, 0x18489d00, 0x171768bb, 0x1ff10047, 0xbfb5ae}}}, + /* 7*16^30*G: */ + {{{0x146cb42a, 0x0f6f6f9e, 0x08e424cc, 0x0a50a74e, 0x173e7bc0, 0x16f5509e, 0x11193452, 0x1960f609, 0x435b54}}, + {{0x1af72dd0, 0x1f126f6e, 0x0e5269ad, 0x1898f286, 0x0585d5ed, 0x12a660f0, 0x086927d2, 0x063c8e31, 0xd726c0}}}, + /* 9*16^30*G: */ + {{{0x1d2fc263, 0x0beca0d8, 0x19755eea, 0x1f027cb6, 0x1da0e89c, 0x023e2709, 0x15f867ef, 0x033c29db, 0x1805b8}}, + {{0x10870ec7, 0x132385a7, 0x147b2bc9, 0x1835f1ca, 0x131489c8, 0x0d5435e5, 0x05c56163, 0x05012870, 0x101f64}}}, + /* 11*16^30*G: */ + {{{0x0076d1df, 0x1158db86, 0x1fe86ce6, 0x1c410284, 0x15f45f41, 0x1d45153b, 0x053d0185, 0x086cda63, 0xc73aaf}}, + {{0x05ad6605, 0x13cea8b7, 0x024dc834, 0x16af4a3b, 0x0dcfef75, 0x00df1dde, 0x05bbe738, 0x0d3d99f2, 0x9201ec}}}, + /* 13*16^30*G: */ + {{{0x05a745c7, 0x1b41fbcc, 0x1fab01f4, 0x144e7182, 0x152c1bc8, 0x0db57b0e, 0x1b49dc62, 0x11efab86, 0xe3ddee}}, + {{0x178efcc2, 0x1cf84a03, 0x1409ec68, 0x1185e2f7, 0x1d8f47c2, 0x0dc3553d, 0x0e1c6f94, 0x1a723265, 0x487199}}}, + /* 15*16^30*G: */ + {{{0x0d4170b3, 0x06399f42, 0x0e8d61fd, 0x0882adf8, 0x0a1d5401, 0x1508c360, 0x0796bda2, 0x1b8406be, 0x45c78d}}, + {{0x111faa1d, 0x177d1f6a, 0x08331cc5, 0x008cc0c8, 0x13a512cb, 0x14e8a27d, 0x1032c386, 0x08c471ab, 0x11af99}}} + }, + { + /* 1*16^31*G: */ + {{{0x1ab5c2cf, 0x0abccc97, 0x0c213788, 0x05e1843f, 0x090a4531, 0x03a0827d, 0x1d41c79d, 0x0863c443, 0xe4107e}}, + {{0x0b955c2b, 0x014201f9, 0x1ec90434, 0x1125b9fe, 0x02b323c7, 0x03343a0e, 0x1268e523, 0x1cd9ee03, 0x1e5f11}}}, + /* 3*16^31*G: */ + {{{0x09b67b7e, 0x1dadb1e0, 0x070f5216, 0x16f65722, 0x01142766, 0x0b80ef5e, 0x1df6ab3e, 0x0cbdc2f3, 0x85df9a}}, + {{0x030a36e3, 0x059c34f4, 0x0f1ba962, 0x15293a3f, 0x0297386f, 0x1eaf7f87, 0x0c22edad, 0x154735ec, 0xa6b4b7}}}, + /* 5*16^31*G: */ + {{{0x0b3510d3, 0x13e7ef30, 0x0875b904, 0x062cce09, 0x1292885d, 0x10c32e16, 0x1422a362, 0x1fcff3f9, 0x23c493}}, + {{0x1ae89a47, 0x1a317621, 0x0e76f5c4, 0x13c6bc4d, 0x1e6d8f79, 0x0ecf277f, 0x108c309d, 0x153c8682, 0xadf456}}}, + /* 7*16^31*G: */ + {{{0x0b49a71f, 0x06348c04, 0x089f0e6b, 0x0dd6d8c8, 0x035ddac3, 0x09f5d579, 0x03b77966, 0x016629ba, 0x94406d}}, + {{0x10b249a9, 0x18e0ca26, 0x18f3e511, 0x04e4c394, 0x1e897d0e, 0x1e484837, 0x05e73481, 0x0fd0c9be, 0x61aae7}}}, + /* 9*16^31*G: */ + {{{0x00e50c32, 0x0e6f19e3, 0x11a1cff9, 0x0212e5d1, 0x08d3877f, 0x1b6fadf7, 0x1050cdc4, 0x087bd10d, 0x3c2ebf}}, + {{0x0a93c124, 0x117c48be, 0x1a326627, 0x1a118735, 0x028e7be2, 0x1c9e9017, 0x06719496, 0x14d8bd07, 0xa703d9}}}, + /* 11*16^31*G: */ + {{{0x03a2355a, 0x041ecef6, 0x1812641c, 0x0a37e6b8, 0x15f12996, 0x12dc4f62, 0x1ae7ce47, 0x1ae4f281, 0x987536}}, + {{0x1bd05276, 0x063ae260, 0x0eb7337c, 0x1ed8f8d0, 0x17b48b85, 0x18e0a4f2, 0x05c90f82, 0x1ace9e22, 0x12f19a}}}, + /* 13*16^31*G: */ + {{{0x12f3ec22, 0x0782a3e2, 0x0a3e29ac, 0x10db9ee3, 0x0ddddd1f, 0x1c58bf79, 0x11a3c674, 0x08134988, 0xd515ac}}, + {{0x05a2863b, 0x10a31759, 0x06e01bb2, 0x0afbe006, 0x0bd464cd, 0x12420029, 0x0cb87a97, 0x02844893, 0x51c048}}}, + /* 15*16^31*G: */ + {{{0x1971c857, 0x0cd0f4d2, 0x19824fe9, 0x133cdb57, 0x1cf3ddec, 0x1e61c5b0, 0x02eb8914, 0x0930bcb6, 0x70b9d}}, + {{0x0ac8dfc1, 0x1ce5b1ee, 0x15186abf, 0x107b43d8, 0x13cfb33a, 0x0857231b, 0x09421ae0, 0x037fe96c, 0xed8046}}} + }, + { + /* 1*16^32*G: */ + {{{0x1789bd85, 0x1e427e4e, 0x05fab0d5, 0x0bfefb85, 0x0766efc3, 0x17eac463, 0x199fee60, 0x137ddb6b, 0x447d73}}, + {{0x12e25b32, 0x03f19e4b, 0x1eb94003, 0x09372b4f, 0x0aff73d3, 0x0eca9d25, 0x07bb84ba, 0x15706826, 0x2d4825}}}, + /* 3*16^32*G: */ + {{{0x1ea6db68, 0x1f0ccc76, 0x098cb09c, 0x15b0ac10, 0x0f4f6ddf, 0x06fcb2ef, 0x05fd62c5, 0x07c07940, 0xf8b653}}, + {{0x0e760da9, 0x0de92d85, 0x17283b5a, 0x1ae1bb38, 0x03ec66bb, 0x16ed2855, 0x1218bc11, 0x1ebd888a, 0xc30f4e}}}, + /* 5*16^32*G: */ + {{{0x1924e753, 0x16aea75f, 0x1e1c9f19, 0x02e60e59, 0x1fb755f0, 0x18c394f6, 0x11f1523b, 0x1a6ab050, 0xf35289}}, + {{0x0b13a20c, 0x122dae17, 0x0b43c12a, 0x05f8ae52, 0x01bd8c56, 0x0450da87, 0x0fee4f7c, 0x03d8bd82, 0x75c178}}}, + /* 7*16^32*G: */ + {{{0x1018017e, 0x1cae5b39, 0x17c8f5c8, 0x083fffad, 0x14e01af1, 0x0c2fdb39, 0x1c5920d0, 0x0e9b4882, 0xcfd06b}}, + {{0x0e0dff50, 0x0573df26, 0x1de5dde8, 0x0060f0d6, 0x07950003, 0x19cac3ed, 0x044e040e, 0x1536e575, 0xb647b7}}}, + /* 9*16^32*G: */ + {{{0x192db860, 0x0640c82b, 0x06891ec1, 0x11251065, 0x1dbe9810, 0x0b68478a, 0x1344544b, 0x09895abb, 0x755e7}}, + {{0x00d09849, 0x0b3006dd, 0x109dde9f, 0x06e8c99f, 0x15bc2b29, 0x196c11c0, 0x19374926, 0x14ea75b3, 0xfe16af}}}, + /* 11*16^32*G: */ + {{{0x0a26ba6b, 0x0f7b4aaf, 0x1684b0ea, 0x1945492d, 0x0bbdedd2, 0x0606bf58, 0x05a5a284, 0x09986e59, 0x88fba8}}, + {{0x044972f8, 0x020a3c0f, 0x033f73a0, 0x153c51c0, 0x0788d484, 0x0d22da8a, 0x183f499b, 0x0c93a737, 0x512aef}}}, + /* 13*16^32*G: */ + {{{0x14720cab, 0x0e245214, 0x0e6c5f8e, 0x1d4ba82e, 0x06f2c1a1, 0x1b73aaae, 0x0fae3943, 0x197d80ab, 0xe1a0ac}}, + {{0x14b185c9, 0x16a29fab, 0x000953a9, 0x1454e3a1, 0x0bcbf084, 0x1c183bf7, 0x015060db, 0x1f6fd319, 0xe07968}}}, + /* 15*16^32*G: */ + {{{0x0c26c42a, 0x14147eff, 0x0c46ed8a, 0x17f6fc8d, 0x1e6a0249, 0x1ac498f3, 0x11f9436c, 0x10dcd4e7, 0xae93b}}, + {{0x19f808ca, 0x08319a1d, 0x0ab6d924, 0x1473ddeb, 0x00b37278, 0x11f6e0f1, 0x184ea50b, 0x1ba28f0f, 0x7028f6}}} + }, + { + /* 1*16^33*G: */ + {{{0x137de736, 0x15ec5d60, 0x02338907, 0x11aac30d, 0x0c18ea2f, 0x0a15c66f, 0x1cfa24dd, 0x02929399, 0x9022e3}}, + {{0x04c42ecc, 0x077ae042, 0x1a9d95fd, 0x126cd889, 0x0ce087f4, 0x1d822913, 0x0e519b42, 0x09e52094, 0x2fae5e}}}, + /* 3*16^33*G: */ + {{{0x109b01f5, 0x0b8365c6, 0x1cf01464, 0x1e6a3064, 0x1857af73, 0x169f8d7b, 0x0517ec3c, 0x1c60edfd, 0x6e5872}}, + {{0x0bcd5fde, 0x183ac6cd, 0x06a169f0, 0x02f3a9c1, 0x0f0ebc13, 0x0ea579df, 0x05c60330, 0x05d91aee, 0x4213c0}}}, + /* 5*16^33*G: */ + {{{0x0fe45b1f, 0x09cdba3f, 0x185a30ad, 0x02abf65c, 0x1df827bd, 0x0f1a260b, 0x1d412bf4, 0x1634bb47, 0x292220}}, + {{0x134bb026, 0x1f205c5a, 0x17504117, 0x06886099, 0x18d7ff7c, 0x1caffd74, 0x16bb8df1, 0x0a657e2e, 0xe316a0}}}, + /* 7*16^33*G: */ + {{{0x03abd540, 0x14283315, 0x059b790f, 0x0080ca96, 0x13a340e8, 0x07c39084, 0x1c2b0c89, 0x0900f489, 0x3644e1}}, + {{0x155da631, 0x0e37f0e6, 0x1be85378, 0x0ef9db4c, 0x1e293001, 0x02984fd0, 0x05d8470a, 0x0a1b784f, 0xec5a91}}}, + /* 9*16^33*G: */ + {{{0x09142ccb, 0x117e90f4, 0x01fb78c6, 0x0414b58d, 0x1b4824c9, 0x1c7a4c9d, 0x00e3956c, 0x15c2ff9b, 0xedfe7d}}, + {{0x0c593d75, 0x1ca8e98d, 0x14450bae, 0x08856d3a, 0x1beb81da, 0x0e95a7aa, 0x1c858615, 0x0245c1b5, 0xc88692}}}, + /* 11*16^33*G: */ + {{{0x0a41b208, 0x1ed38e32, 0x189c003c, 0x0e8eb9bd, 0x00de8e7f, 0x00135e75, 0x1a5661c5, 0x11680b34, 0x531196}}, + {{0x048e4b69, 0x08f0fcf0, 0x0da1dfda, 0x183b8048, 0x1da113b3, 0x0c0f19d9, 0x05e29b19, 0x0567b845, 0xb78d73}}}, + /* 13*16^33*G: */ + {{{0x095daae7, 0x1e80dae2, 0x1c0cef2c, 0x0dcdd8f8, 0x0e1d5af4, 0x15fb7ab0, 0x1653de88, 0x1e45ff74, 0xf65dd1}}, + {{0x02556738, 0x0a72d4ce, 0x0b366ca3, 0x0c301f3d, 0x04cd0eb0, 0x1f31be7e, 0x1ba0839b, 0x0419c36c, 0x9d71b8}}}, + /* 15*16^33*G: */ + {{{0x1afcf1ee, 0x1f7e0af1, 0x01a6846a, 0x155008e4, 0x007c60af, 0x1a405554, 0x0922d06f, 0x0fdb995c, 0xa6670e}}, + {{0x00c67fc8, 0x1c43855f, 0x0f3ce28d, 0x07312392, 0x1ac131ef, 0x1b97a4ab, 0x0bce8e57, 0x19274a38, 0xa7fd14}}} + }, + { + /* 1*16^34*G: */ + {{{0x04cd3397, 0x008a2e0a, 0x1457ad5d, 0x09820c32, 0x16deddc3, 0x16795a8a, 0x1c8e24e1, 0x00833db4, 0x73baff}}, + {{0x1adcb8e4, 0x0f79509c, 0x0984d250, 0x1df259f0, 0x1825e779, 0x08f460c7, 0x117da803, 0x0c692ef5, 0x1e97de}}}, + /* 3*16^34*G: */ + {{{0x161fb913, 0x1587ca90, 0x14c4a5df, 0x0048c2ad, 0x0d04e3e1, 0x046f225f, 0x0860d10e, 0x01867cc1, 0x45f833}}, + {{0x0c15c3bc, 0x06a40be0, 0x1f0cdcdc, 0x1a10f3ed, 0x07760f06, 0x003b6c5d, 0x01bbe03a, 0x0db7fad2, 0xc212cf}}}, + /* 5*16^34*G: */ + {{{0x0e393654, 0x177cb48f, 0x1b75f1d6, 0x1a97c3e7, 0x15991965, 0x100e45e6, 0x16ad97d6, 0x09359af7, 0x5544dc}}, + {{0x0f53206f, 0x1b085dfc, 0x15639cc8, 0x1dfd2e07, 0x15192241, 0x02dadc49, 0x152b0130, 0x112a10ff, 0xed3e85}}}, + /* 7*16^34*G: */ + {{{0x08a15dfd, 0x1f9acca1, 0x1ea79544, 0x1b75804b, 0x0f695741, 0x176aad71, 0x1dcd2cf4, 0x120c33dc, 0x2757b7}}, + {{0x1042c341, 0x04742067, 0x09c55b7f, 0x112f1479, 0x1500d176, 0x1a909e6a, 0x04b97325, 0x1aaa9856, 0xdf46d2}}}, + /* 9*16^34*G: */ + {{{0x1f840a5b, 0x1f6fa135, 0x0b52613b, 0x195b4ba7, 0x03ccd148, 0x10e6608a, 0x0f236610, 0x0fbca1c5, 0xe87243}}, + {{0x1377e86a, 0x0272c2a8, 0x0e59192e, 0x1468d9f2, 0x08bf5ac3, 0x048fb312, 0x185964ef, 0x11224e52, 0xf984bc}}}, + /* 11*16^34*G: */ + {{{0x136afd6a, 0x0ab54dc5, 0x1e078f52, 0x03f8a142, 0x1334a926, 0x1b6af379, 0x1700e4bb, 0x15749aee, 0xc56cdc}}, + {{0x13f87c26, 0x10435f77, 0x0331c8f9, 0x090764ad, 0x14ef566f, 0x0f58bfac, 0x01c334bb, 0x1c5e70d6, 0xd16056}}}, + /* 13*16^34*G: */ + {{{0x0cfd2cd0, 0x0d6cc069, 0x070304c7, 0x08266883, 0x0abb1239, 0x142c1f24, 0x0a1a73f4, 0x0e71e7fe, 0xd3d6e6}}, + {{0x194ae2dc, 0x02bce3bb, 0x1dbe3c53, 0x1f4ad185, 0x1e59b001, 0x147fb9be, 0x0da14db6, 0x0bf9a2b3, 0xff448f}}}, + /* 15*16^34*G: */ + {{{0x1ab7a64c, 0x0ac442ae, 0x026ade82, 0x011ad474, 0x1d406565, 0x196b911b, 0x101ec0c4, 0x110adc8b, 0x88f977}}, + {{0x1575f103, 0x02f4c708, 0x1b499ce8, 0x06012442, 0x09e5836c, 0x0d792bcb, 0x11d0d14c, 0x1ba8d6ab, 0x4a745c}}} + }, + { + /* 1*16^35*G: */ + {{{0x1fa4e33c, 0x1172fd98, 0x02632cc3, 0x077d8f16, 0x0fb98268, 0x023614bb, 0x16ef25d1, 0x17234984, 0x9cf646}}, + {{0x0e0d4563, 0x0e22f030, 0x10580c86, 0x00b04fd7, 0x01f319e2, 0x0712c5c1, 0x0a247902, 0x09b83ecb, 0x37b062}}}, + /* 3*16^35*G: */ + {{{0x06bf1e67, 0x0c5b0c66, 0x172bd8fa, 0x0cce93fc, 0x04e0f4c5, 0x129c13bb, 0x126675e9, 0x1bc2a36c, 0x83cb43}}, + {{0x099acc97, 0x13f74598, 0x1445a7a8, 0x0884597b, 0x018f8287, 0x00373122, 0x1be3bec6, 0x1449731e, 0xbce28c}}}, + /* 5*16^35*G: */ + {{{0x1c0f057e, 0x1856ba46, 0x154f7608, 0x10c50e03, 0x1022484e, 0x07e0af12, 0x02300cd0, 0x1cac19d6, 0x3ff3b3}}, + {{0x0817965e, 0x0a0fbed5, 0x1c05d88b, 0x0046dd88, 0x07843a01, 0x08b82bc3, 0x1e3dbdff, 0x0de776ca, 0x7f17ad}}}, + /* 7*16^35*G: */ + {{{0x125e69f1, 0x088a5a01, 0x08af2d45, 0x0f51e5a8, 0x0af99636, 0x0ef0b9eb, 0x00ff7686, 0x05bb1ffb, 0x6e9edb}}, + {{0x002b7e9b, 0x1070bf1a, 0x07ca06dc, 0x04e8a8f3, 0x1bff61c7, 0x0b55b2f9, 0x153aacd5, 0x02d9dff2, 0xc08222}}}, + /* 9*16^35*G: */ + {{{0x0288f038, 0x19297b35, 0x17fe082f, 0x0ed129d6, 0x02d32f08, 0x00cef376, 0x112fbeaf, 0x1d009883, 0x5ee280}}, + {{0x16f1ee6e, 0x02d55c35, 0x19b1bd07, 0x0f067531, 0x1eec011d, 0x0c37f664, 0x0e4a1301, 0x1f28cefc, 0xbcd969}}}, + /* 11*16^35*G: */ + {{{0x00c708c8, 0x05f992b6, 0x1c2a1aa8, 0x08609e5e, 0x0288c2c3, 0x1b2ec8ff, 0x15cdb7f8, 0x0dc0b840, 0xe1f016}}, + {{0x1896ed38, 0x18c6b9d9, 0x0d6802b9, 0x0abe45df, 0x13016fb6, 0x1195f451, 0x0d481111, 0x07d22d87, 0xe64765}}}, + /* 13*16^35*G: */ + {{{0x076edefb, 0x10784e52, 0x039f575f, 0x117b0020, 0x1c7badd5, 0x0d5a14bc, 0x1171fc48, 0x10f57ec6, 0x280896}}, + {{0x1d1b0ae0, 0x17a2b914, 0x00e4848b, 0x06360f7c, 0x141c44dd, 0x0cf5ec82, 0x064699f8, 0x1e67a766, 0x5d071c}}}, + /* 15*16^35*G: */ + {{{0x1344897a, 0x096ccde7, 0x1309a774, 0x1da60eb4, 0x1edab7b9, 0x0f429212, 0x132dc161, 0x1bc50320, 0xeb15b0}}, + {{0x05bfe7ee, 0x0cef41e7, 0x1f42e0ab, 0x0d3165f2, 0x12f85814, 0x157c66b9, 0x01c42262, 0x02d384cc, 0x96cdd}}} + }, + { + /* 1*16^36*G: */ + {{{0x123b716d, 0x137d6e02, 0x13869ae0, 0x0712cdee, 0x0df9e0d2, 0x03c9c68c, 0x14d3a297, 0x1c717194, 0xf81f5b}}, + {{0x12632401, 0x120017a8, 0x01e0cc11, 0x0eb0a075, 0x00328660, 0x094e9c07, 0x1b7c755b, 0x065383e0, 0xdc7f49}}}, + /* 3*16^36*G: */ + {{{0x19d19d8a, 0x062b4281, 0x09548561, 0x024f12a3, 0x1490a3b4, 0x161ae0b6, 0x18e16bdb, 0x0a1f6250, 0xa60084}}, + {{0x190d2c4f, 0x0caedd9a, 0x0995cf7f, 0x1c011f4e, 0x1ca84f0f, 0x0a2f3df4, 0x11cb23db, 0x180cf46f, 0x7b4b79}}}, + /* 5*16^36*G: */ + {{{0x096cac9f, 0x14c3d9ca, 0x11379c89, 0x0719d4b8, 0x10e1c59e, 0x04bcc7f5, 0x0d531930, 0x1632cfbe, 0xde5382}}, + {{0x0fa473d3, 0x070417ed, 0x1610455f, 0x07c528e7, 0x19f2bc2e, 0x1804c8e7, 0x06158b0e, 0x0f16d392, 0x9c47b6}}}, + /* 7*16^36*G: */ + {{{0x06807a23, 0x0594b1bc, 0x097fcf9f, 0x0040a2d4, 0x14ec8400, 0x1ccea88c, 0x04214d12, 0x0100fe55, 0xa7227b}}, + {{0x14115894, 0x0238cc1e, 0x0a337247, 0x1f02af6e, 0x075abe7b, 0x023c9050, 0x07a1176a, 0x04ba8ba7, 0xf55075}}}, + /* 9*16^36*G: */ + {{{0x044d14c0, 0x13cbfe10, 0x0a3cd796, 0x1956bf43, 0x01d39005, 0x1f39d1b3, 0x1659196a, 0x11a84688, 0xcd2b2e}}, + {{0x02496315, 0x0b056791, 0x0b33b3a9, 0x16de1973, 0x0f671fdd, 0x0f76ed60, 0x02893541, 0x1bf3610e, 0xf13012}}}, + /* 11*16^36*G: */ + {{{0x199a1566, 0x17a75891, 0x08a3da59, 0x18cab5fe, 0x19dd8f25, 0x1e4dc1ef, 0x1fad33be, 0x0991e1be, 0x11b2e8}}, + {{0x1c56bb73, 0x0d259e02, 0x16025a16, 0x16ab0819, 0x005cc824, 0x0b4c3cba, 0x05d7410b, 0x13b79446, 0x610376}}}, + /* 13*16^36*G: */ + {{{0x1ba6f34a, 0x084f5946, 0x1171be1f, 0x10d41fc4, 0x11485312, 0x1051f7cc, 0x01c1d676, 0x052be574, 0xfb6d19}}, + {{0x0bcf0452, 0x1af9e411, 0x1a16c7e6, 0x19f45b92, 0x191b9ecf, 0x07ea6253, 0x1b7678e2, 0x053af7e8, 0xf29b2d}}}, + /* 15*16^36*G: */ + {{{0x0a435427, 0x06516950, 0x1936d41d, 0x0021304f, 0x06651a69, 0x13b286c8, 0x0f042abd, 0x19820782, 0xff429f}}, + {{0x19853824, 0x12822869, 0x0e8368a3, 0x17dca694, 0x06876205, 0x04e42b0c, 0x16c25350, 0x0cd149d9, 0x2addf9}}} + }, + { + /* 1*16^37*G: */ + {{{0x07354b7a, 0x16ce3f3c, 0x045725e3, 0x1d94ad87, 0x0de1d022, 0x1a31f29e, 0x01208e5f, 0x0e9aefc1, 0x856854}}, + {{0x116e04c6, 0x0aa2015e, 0x15bf6f62, 0x1123b83f, 0x1728706a, 0x089d9537, 0x1edbbaf2, 0x16a17eb0, 0x20b50e}}}, + /* 3*16^37*G: */ + {{{0x1b26165d, 0x1b663fd3, 0x1dc4dfab, 0x07442bec, 0x092dea71, 0x090497e6, 0x199527a7, 0x18ea5647, 0x617344}}, + {{0x06b2a286, 0x04dfee5c, 0x1c5c6931, 0x13582dc6, 0x020b2989, 0x0885e8c1, 0x0d6926df, 0x02f486f0, 0xe99016}}}, + /* 5*16^37*G: */ + {{{0x10442770, 0x10dfbb32, 0x006eb440, 0x11ef4dc2, 0x0fc2e901, 0x00ed4f20, 0x088b6813, 0x1dcabf6d, 0xf8ac03}}, + {{0x1b00c82d, 0x11432788, 0x0107408e, 0x0c057f2a, 0x06d17a97, 0x03a8d9a7, 0x1a3a90db, 0x0cab33a3, 0x5a358e}}}, + /* 7*16^37*G: */ + {{{0x165460de, 0x1f036c37, 0x0fddee3e, 0x07f1a155, 0x08ea60c7, 0x1e5f1866, 0x1719b1b2, 0x16a4b792, 0x8a731f}}, + {{0x1f8dc207, 0x0c2cd0ef, 0x1423b8fa, 0x0cfc78a4, 0x04dc4358, 0x0aa5ffd8, 0x0c663d4a, 0x18bc556c, 0x585855}}}, + /* 9*16^37*G: */ + {{{0x151634a5, 0x0155d6fd, 0x1b549399, 0x1214f06b, 0x1967f3cf, 0x15d0166b, 0x0892fc29, 0x0c26551e, 0xa20b60}}, + {{0x1226afad, 0x1609bcaf, 0x1517b7c9, 0x08eafc79, 0x1315bc67, 0x0ff41b9d, 0x0676b4f5, 0x13ed2fb7, 0x9c43ce}}}, + /* 11*16^37*G: */ + {{{0x13ba54a8, 0x02191225, 0x0eaa5c1c, 0x164c6ac0, 0x1cc0ab58, 0x0761a7e2, 0x1c26450e, 0x127e24ac, 0x16f1f}}, + {{0x064f2889, 0x0be30fd3, 0x08ed39bb, 0x15e4ea8d, 0x0e658d93, 0x188df24e, 0x055dbca6, 0x0822b12a, 0x8bcd3}}}, + /* 13*16^37*G: */ + {{{0x07c16d94, 0x177249e7, 0x0a117f77, 0x103a1540, 0x0c31fe25, 0x1eb3e667, 0x11e23023, 0x0ce17a06, 0xe61586}}, + {{0x114810ab, 0x1a768cd5, 0x0910eefe, 0x0b9a3c8f, 0x0f0ee4e6, 0x15c9fa5b, 0x12fa316c, 0x15d3ce24, 0x65ca03}}}, + /* 15*16^37*G: */ + {{{0x13ce728d, 0x0b6e5332, 0x1c7342f3, 0x1b20fc50, 0x05347f4c, 0x04510b64, 0x08995568, 0x01671aad, 0xdcd37f}}, + {{0x17cacbcb, 0x0be89b4c, 0x076afae3, 0x19a68da5, 0x0d6f3caa, 0x1db159e3, 0x1061cb0d, 0x1aef9b49, 0x6574db}}} + }, + { + /* 1*16^38*G: */ + {{{0x12378c16, 0x0e36e0d8, 0x05588ab7, 0x0c0eaa8c, 0x1597d9e3, 0x1296b5fc, 0x0c46cc67, 0x0b382567, 0x1136b7}}, + {{0x13488127, 0x0facde85, 0x147338de, 0x095451ce, 0x1ecb2961, 0x15e307f4, 0x1f7427c2, 0x19e8a2d1, 0x7dec0f}}}, + /* 3*16^38*G: */ + {{{0x1b299740, 0x18736c13, 0x0f6c4d25, 0x1e8cfae2, 0x0ad48f40, 0x089bc9fd, 0x0cdb312b, 0x16e39ba8, 0x53893e}}, + {{0x1fff7509, 0x0f5378d9, 0x1f7a3354, 0x0265de43, 0x1dc8dd8a, 0x0714753a, 0x0f60e107, 0x0fd290bf, 0x27728a}}}, + /* 5*16^38*G: */ + {{{0x138000fc, 0x059e4ab3, 0x1c0ef04c, 0x1c4ee0f7, 0x0e5604e1, 0x1b5d78fd, 0x089d5f8d, 0x1baea99b, 0xdd64e9}}, + {{0x1df4ac12, 0x1d50e286, 0x07923e2f, 0x0ba04572, 0x079ffbe4, 0x0f1f0ce5, 0x0b25b7e2, 0x1d618526, 0x432193}}}, + /* 7*16^38*G: */ + {{{0x13878c4a, 0x1c10a8b9, 0x1470157b, 0x07a0790a, 0x11b21d88, 0x0e307254, 0x145bcb1a, 0x150fdffa, 0xff9845}}, + {{0x1488df68, 0x1d3423eb, 0x173faf1a, 0x066e8d6c, 0x1e8ddf1f, 0x12476ffa, 0x0a3e3f62, 0x1e949c48, 0x835b9a}}}, + /* 9*16^38*G: */ + {{{0x00f51524, 0x054dc40a, 0x0be1bf23, 0x1d6d42b7, 0x093de5fa, 0x1d184229, 0x0273a1d9, 0x1d17722c, 0x954e13}}, + {{0x05e30a69, 0x0fed9eca, 0x079f2f7a, 0x1d486228, 0x0cbcfa9e, 0x1121a3cf, 0x010764f1, 0x07ff2548, 0xecc836}}}, + /* 11*16^38*G: */ + {{{0x04f77287, 0x1bcc6a9e, 0x0c3678c7, 0x12a786d2, 0x00497412, 0x0862e0ec, 0x0e4f7f35, 0x1edf483f, 0xb2217e}}, + {{0x04e7b5da, 0x1d0253b9, 0x17a4f2e5, 0x13b8e738, 0x0c843d70, 0x1e7c469c, 0x0e8ad77e, 0x1c19cf9e, 0xfb5153}}}, + /* 13*16^38*G: */ + {{{0x05c723e9, 0x17a9cb64, 0x09e05c62, 0x0e775535, 0x15c351f9, 0x175748ae, 0x16b448b1, 0x162f6f37, 0x3c3950}}, + {{0x16834763, 0x11c3bafb, 0x1241ab97, 0x1b596af8, 0x0a8b0b01, 0x1dbf50c3, 0x036ed252, 0x0c441222, 0xb99b73}}}, + /* 15*16^38*G: */ + {{{0x1c18abeb, 0x18beb711, 0x09758a8d, 0x1b9a320e, 0x0a944d59, 0x097c225d, 0x136b477b, 0x0599f2c3, 0x803c7a}}, + {{0x029bfa78, 0x121cdf87, 0x19c8735a, 0x18887854, 0x14409ed7, 0x078b4e25, 0x04a1ac6b, 0x0814f2dd, 0x83fb50}}} + }, + { + /* 1*16^39*G: */ + {{{0x143e832a, 0x03b1778c, 0x01b7dc27, 0x0a15602f, 0x1f18e07e, 0x19d412c4, 0x146a43d5, 0x1174f854, 0xd2bf2}}, + {{0x1b20d37c, 0x0131d78a, 0x15451192, 0x193b72c0, 0x0e7ed27e, 0x10854a5a, 0x02b1c21e, 0x086277a0, 0xcac3f}}}, + /* 3*16^39*G: */ + {{{0x112c9666, 0x0f5625e0, 0x1ff37cd8, 0x118d86cb, 0x1531b1cf, 0x061adbec, 0x00b3f66f, 0x0bd72cff, 0x34e5e8}}, + {{0x192a3666, 0x0d0c29e4, 0x18e949ad, 0x0fdac783, 0x046330f4, 0x12bae65e, 0x0dae0a11, 0x06264434, 0xc0ce68}}}, + /* 5*16^39*G: */ + {{{0x11209502, 0x0b295f1a, 0x16499970, 0x02e4004b, 0x1e154594, 0x09f7848c, 0x018e9b12, 0x198b3e9b, 0x727362}}, + {{0x17042b0d, 0x13be8e9e, 0x09d82ef1, 0x1a6ff376, 0x11d20a18, 0x05c61674, 0x0d627c40, 0x04537575, 0x17b0f4}}}, + /* 7*16^39*G: */ + {{{0x0447f959, 0x0af3c945, 0x1c74da11, 0x0fb57504, 0x1eee9f94, 0x0a625da4, 0x13b25ce8, 0x0c00a94d, 0xa0c3f2}}, + {{0x168b671f, 0x01ce9244, 0x149cb26c, 0x0aa804b1, 0x08208b1b, 0x060865b1, 0x113ce0be, 0x0121c965, 0x508c73}}}, + /* 9*16^39*G: */ + {{{0x1b37e1d3, 0x10fcf812, 0x193788c0, 0x0c37c279, 0x1fd04107, 0x019df20a, 0x09e9032d, 0x063ef2b9, 0x7e81b2}}, + {{0x0155681a, 0x1afe5132, 0x10b8380f, 0x1097e563, 0x07c4e4ec, 0x04b67736, 0x144c497a, 0x0361d37d, 0xf3855a}}}, + /* 11*16^39*G: */ + {{{0x16c051a0, 0x04bb8afa, 0x16cf9b71, 0x1e5248de, 0x12abddcd, 0x0c736d87, 0x1b6128db, 0x038fb004, 0x8035e2}}, + {{0x12d0dd12, 0x00f5d97d, 0x02d88d58, 0x0fb8c613, 0x1d318b3e, 0x1f341bde, 0x0fbcdd76, 0x14896f45, 0xb8810}}}, + /* 13*16^39*G: */ + {{{0x1f12c69e, 0x1006184f, 0x194658f3, 0x0b5deb12, 0x0fcecafe, 0x18008102, 0x14cc1aeb, 0x1bfac314, 0x196908}}, + {{0x1d114831, 0x0998c820, 0x1ee21ae3, 0x05c66e3f, 0x1054eb6b, 0x0ef56e90, 0x18102fb8, 0x0d65f22d, 0x3f65bf}}}, + /* 15*16^39*G: */ + {{{0x0b89c5ed, 0x04c700fe, 0x1e9e31f9, 0x0a619ef2, 0x10f3577b, 0x10e90856, 0x0abd1b9b, 0x1d712c34, 0xdb77fc}}, + {{0x0d25b46e, 0x0ff8e3f7, 0x0266be96, 0x0d8f56d1, 0x1ad411f1, 0x1cdf264c, 0x173bb3cc, 0x070e39dc, 0x7fd1dc}}} + }, + { + /* 1*16^40*G: */ + {{{0x17f82f2a, 0x174e3aef, 0x1f7d0eab, 0x186b0e95, 0x113269e4, 0x16fa1b9b, 0x185fd588, 0x0acdd8e6, 0x8a535f}}, + {{0x023094b7, 0x0fcd0561, 0x031d9a71, 0x0a670c99, 0x092bfcde, 0x140c842d, 0x0f5cdf80, 0x108d1611, 0x455c0}}}, + /* 3*16^40*G: */ + {{{0x0b348fa0, 0x18a790bd, 0x0550777e, 0x1c48b20a, 0x0b4bce0f, 0x1191b612, 0x00b70a88, 0x07bbbd71, 0x86eac9}}, + {{0x0da51cee, 0x171c04aa, 0x13fba293, 0x0db2c6a3, 0x146716c2, 0x17cf46b7, 0x1635690d, 0x0a797789, 0x948f38}}}, + /* 5*16^40*G: */ + {{{0x19222c03, 0x17a0ffe4, 0x197840de, 0x19cefd0f, 0x1f407948, 0x1ebc242c, 0x0ab8fd79, 0x175f3f67, 0x8bf09e}}, + {{0x0a72bb54, 0x0a2fba17, 0x08387528, 0x1d81c3bc, 0x1ba309c9, 0x18edf3f2, 0x09cced22, 0x15fc5c4f, 0x509cba}}}, + /* 7*16^40*G: */ + {{{0x11ae9300, 0x029d160e, 0x1120a02d, 0x188e08eb, 0x1735b5e1, 0x05d6d179, 0x1f18644c, 0x1976fce1, 0xe85e2d}}, + {{0x1546e25e, 0x1506fee8, 0x030c6edc, 0x0fc30bbf, 0x02707deb, 0x1dadc11e, 0x02ff1ee9, 0x14daa39c, 0x451aaf}}}, + /* 9*16^40*G: */ + {{{0x05260cb8, 0x092eaab0, 0x0c854bc9, 0x1e95019d, 0x1dbf6836, 0x13ed0dd3, 0x1e0a8fc0, 0x1e451925, 0x3f5fb0}}, + {{0x1852c964, 0x17da5a20, 0x17b0cc9c, 0x1d0ea3f8, 0x183f2fa3, 0x0f0a9b33, 0x061c38e3, 0x1b5b4933, 0xc55834}}}, + /* 11*16^40*G: */ + {{{0x1a1cd60f, 0x15222216, 0x0c24ba92, 0x0d315398, 0x0002b9f9, 0x083a5a6d, 0x06595ebb, 0x045631b3, 0x336856}}, + {{0x0fd57d67, 0x1fb9bb28, 0x142e2c92, 0x1eb49978, 0x1af175fe, 0x06006f53, 0x1366ea16, 0x13de248f, 0xd42f50}}}, + /* 13*16^40*G: */ + {{{0x17576342, 0x029db75d, 0x06488abc, 0x19110673, 0x179d95b2, 0x1cec4b04, 0x0203df43, 0x0b811e00, 0x4813eb}}, + {{0x17376316, 0x060aaf5c, 0x1aa413d9, 0x1b8cfaa0, 0x1524aca2, 0x0b424719, 0x0903d980, 0x1a846748, 0x043f}}}, + /* 15*16^40*G: */ + {{{0x1f69b2be, 0x1b38b8ef, 0x04447027, 0x03ee9db8, 0x06e56ba4, 0x16ddd71c, 0x05ebc4c8, 0x1f34b5d3, 0x80c3f1}}, + {{0x0102d2f5, 0x0825cbe2, 0x0dea2fe2, 0x16e966b9, 0x15a9bf14, 0x113b2d8e, 0x1a14a603, 0x0814013b, 0xa9321}}} + }, + { + /* 1*16^41*G: */ + {{{0x0476c81d, 0x041bfa7f, 0x194f57a7, 0x06061d33, 0x0a366b7a, 0x06939ed4, 0x08066bdf, 0x1bfb9683, 0xad6090}}, + {{0x0e6705dd, 0x0c55e427, 0x0c1237e0, 0x1fb86c9e, 0x1d8a8393, 0x1ae1662d, 0x047ab335, 0x1ba91e99, 0x77b5d1}}}, + /* 3*16^41*G: */ + {{{0x02725490, 0x11239be7, 0x12d66402, 0x06480c47, 0x1863c4ac, 0x15299d84, 0x05f28ab6, 0x176e35ad, 0xd90b45}}, + {{0x0feb1299, 0x07c27c25, 0x1119b32c, 0x180f7fe7, 0x0cbd80cd, 0x1ab439bc, 0x143c2762, 0x11d83766, 0x332692}}}, + /* 5*16^41*G: */ + {{{0x1e933668, 0x1b59d32b, 0x1aeafc29, 0x05b099f0, 0x106befb0, 0x1c9d7d0f, 0x1f1eb014, 0x02e62427, 0xac2592}}, + {{0x12776fb5, 0x173bfb39, 0x07879e85, 0x0c83138d, 0x0ed7c6f8, 0x009f75ec, 0x02ea143b, 0x070e1b75, 0x6c79a0}}}, + /* 7*16^41*G: */ + {{{0x128de7e3, 0x130be090, 0x1aa65b66, 0x08558757, 0x031bf868, 0x07ded3da, 0x0ea21cc8, 0x095d2f3e, 0x7bc337}}, + {{0x0e6e5e29, 0x1d059975, 0x10333c19, 0x05b67369, 0x1acbd55f, 0x1bd73725, 0x19778031, 0x048c10a9, 0xb431f0}}}, + /* 9*16^41*G: */ + {{{0x035e057d, 0x1659191c, 0x162696d2, 0x1e21b5ed, 0x1119329c, 0x0397e1a0, 0x0ac9a2f2, 0x128a75c0, 0xbf9c14}}, + {{0x0da20fea, 0x1ab25941, 0x1711e6db, 0x173c3038, 0x1a542440, 0x189f82e3, 0x0f83ce14, 0x13e4be47, 0x92acb8}}}, + /* 11*16^41*G: */ + {{{0x021d6584, 0x1566be10, 0x0cf67974, 0x00bac887, 0x1014ef27, 0x1ed1ad6f, 0x1e2a5ba5, 0x0736af09, 0x55bf08}}, + {{0x0ed744ea, 0x0ab27a14, 0x19696a03, 0x1c284055, 0x07dcf089, 0x1fb5d45e, 0x133c9eb2, 0x067c96c3, 0x3d9807}}}, + /* 13*16^41*G: */ + {{{0x01068c5e, 0x0b4efb72, 0x195cf437, 0x0bdcdc97, 0x1d4872a4, 0x10a73c0a, 0x15467cab, 0x02ca66f1, 0xfcf24e}}, + {{0x07c19d75, 0x10fa29f9, 0x052156dd, 0x0ed49650, 0x0e7aee91, 0x0f10dac9, 0x1f8d719d, 0x1ca66dff, 0x186bb0}}}, + /* 15*16^41*G: */ + {{{0x020da860, 0x1becaf83, 0x12744022, 0x08a35490, 0x11f2f843, 0x1b29a1ec, 0x1fb287f6, 0x1a05ea2c, 0x7bc280}}, + {{0x07aac76d, 0x11488208, 0x1058cbaf, 0x03345fb9, 0x0e6e0f2e, 0x15fd382f, 0x07978989, 0x0ef777a6, 0xb33f6d}}} + }, + { + /* 1*16^42*G: */ + {{{0x13c4a205, 0x097961b4, 0x042a1229, 0x15bf13ea, 0x129fcde1, 0x0ab83adc, 0x0f139199, 0x0a2c60b7, 0xb6e06c}}, + {{0x1db36d05, 0x1bfa6c32, 0x0aa7e1d4, 0x13283350, 0x0d73b63f, 0x189373cf, 0x0fd71787, 0x0f843664, 0xbdb427}}}, + /* 3*16^42*G: */ + {{{0x19e27907, 0x17c10fb7, 0x167935a4, 0x15a96711, 0x1bd68771, 0x0eaeb7ef, 0x1139ace5, 0x07f08483, 0xed9e4}}, + {{0x0e78c4fb, 0x09fa7a83, 0x0e86417c, 0x0a39fd71, 0x00e0ce91, 0x07ec7589, 0x0d1fd6f0, 0x095fed64, 0xb5af87}}}, + /* 5*16^42*G: */ + {{{0x15b38f54, 0x1682b929, 0x0bc1f38d, 0x150c1cb9, 0x0e1f92e2, 0x146da47f, 0x1df71549, 0x0200edb1, 0x57a7e4}}, + {{0x045f2809, 0x1d3c0f31, 0x01fae9d7, 0x0edc7352, 0x0dd21dfd, 0x0511de41, 0x14906532, 0x00791d95, 0xfd7f0f}}}, + /* 7*16^42*G: */ + {{{0x07a79593, 0x17f5dfcd, 0x17125e51, 0x14e493a4, 0x05cf3347, 0x0d92c665, 0x16fbd4b6, 0x1c5e7deb, 0x24799a}}, + {{0x10920d55, 0x11c46bae, 0x1ac4e635, 0x086c3f37, 0x1e300999, 0x08d4e9b1, 0x0deccb0f, 0x04c5d90f, 0x9436b5}}}, + /* 9*16^42*G: */ + {{{0x0bf5abdc, 0x010b75a8, 0x02d94198, 0x19a75f7a, 0x15d60456, 0x08e58406, 0x009bb4c4, 0x18d1098e, 0xf15017}}, + {{0x0def85b7, 0x1aef15c5, 0x0e15612d, 0x18ceb84f, 0x1e232cbd, 0x1a0f1fe2, 0x0aa3b360, 0x03858be0, 0xdea7ef}}}, + /* 11*16^42*G: */ + {{{0x1e78e9b1, 0x0f92958f, 0x10507a1a, 0x11cdb89d, 0x0dfcc897, 0x018fba89, 0x1aa9b83e, 0x01f13697, 0xe196e2}}, + {{0x0a77eed7, 0x00bab0fe, 0x1b4d7d11, 0x1a02257d, 0x0ed9a908, 0x045f3b59, 0x1698a990, 0x10bf0350, 0xa5d66d}}}, + /* 13*16^42*G: */ + {{{0x1b1cb64b, 0x17719f2b, 0x02b0f55d, 0x13ca4ac3, 0x1ed14d60, 0x1e6b8a9c, 0x0c0bce5f, 0x1bcd8360, 0x7779f5}}, + {{0x1aba3aab, 0x070c68e5, 0x0aa54cf6, 0x10528479, 0x0e3fae2a, 0x189a53d1, 0x1afab7ea, 0x07e8e987, 0x9a842}}}, + /* 15*16^42*G: */ + {{{0x11d1b492, 0x16c2c3b4, 0x0669bd4f, 0x00c31840, 0x08ce8dfa, 0x16cd5759, 0x0bc4797d, 0x097c8474, 0x8605b9}}, + {{0x18ac6598, 0x18ebbdfb, 0x07a49715, 0x06a4da90, 0x1d1a8ee2, 0x170610a1, 0x1d63cfbd, 0x050fbcea, 0xa8e561}}} + }, + { + /* 1*16^43*G: */ + {{{0x09aa52df, 0x06afa725, 0x0a989fcf, 0x08a56368, 0x1ece618c, 0x00c4ecc8, 0x0fddb6f1, 0x192fec11, 0x45a511}}, + {{0x125ec16c, 0x1a95e890, 0x0a55739e, 0x03364fa4, 0x05ad25a9, 0x19bfe5b1, 0x0db4ff8c, 0x18ee7d53, 0x73be0e}}}, + /* 3*16^43*G: */ + {{{0x0e75bc7f, 0x0b3a9f7d, 0x1dfec7d9, 0x0e2e1b6d, 0x14c7b95c, 0x07890be6, 0x0d0e3bd0, 0x09fce572, 0xb57ac}}, + {{0x0972e9a9, 0x078b96e8, 0x127f1881, 0x12d81c80, 0x094fab1f, 0x1a67d2bf, 0x0ed7ca30, 0x104ef53e, 0xceedbf}}}, + /* 5*16^43*G: */ + {{{0x033125ff, 0x1ba19e7c, 0x16a05084, 0x0aaf60b4, 0x0ae99354, 0x016ce50e, 0x05233d1a, 0x1f6dc97a, 0x1178b3}}, + {{0x18486abd, 0x007cb84e, 0x195346fa, 0x115b9a11, 0x10ed10dc, 0x131bf518, 0x056ce0b7, 0x1d53757b, 0xdfd697}}}, + /* 7*16^43*G: */ + {{{0x1662d955, 0x19b4ae67, 0x033913ce, 0x09ad0b69, 0x15693844, 0x1dbf4693, 0x041fe2a0, 0x104df29d, 0x8ac7a0}}, + {{0x046b3dae, 0x03516df3, 0x1b04e36f, 0x09038b7a, 0x19ad9e3f, 0x1291a65a, 0x0c73275f, 0x1241e664, 0xc80f40}}}, + /* 9*16^43*G: */ + {{{0x088803e5, 0x06c3cd6f, 0x0d1972df, 0x156f9c1a, 0x09e02cc1, 0x1d43802b, 0x08446adf, 0x050b3bbe, 0xc0f48b}}, + {{0x076619ee, 0x14991014, 0x00a3b6e9, 0x1c9c0e17, 0x0d7d3932, 0x12393f30, 0x08da7269, 0x16df1079, 0x3d4326}}}, + /* 11*16^43*G: */ + {{{0x1e1f515c, 0x0b6b384c, 0x194e6bea, 0x09146442, 0x1b8c0e2b, 0x047087fb, 0x19b68067, 0x01e06a2e, 0x2ce870}}, + {{0x052eed6e, 0x08c9b24c, 0x10f54b25, 0x13b9a7c5, 0x15d2ca7a, 0x17a17bf7, 0x129eeb2c, 0x09e76bd8, 0x73879f}}}, + /* 13*16^43*G: */ + {{{0x04dcf274, 0x0d51bbaf, 0x0b3a8911, 0x0400d059, 0x0ca1d807, 0x0dd87ebe, 0x04245178, 0x0c3b96f8, 0xfac442}}, + {{0x112f0472, 0x1b4c3007, 0x11652c58, 0x004f8c4e, 0x097bd732, 0x11eaea77, 0x02a1f31c, 0x18c2acd5, 0x9713fd}}}, + /* 15*16^43*G: */ + {{{0x1dc39d21, 0x1af25b55, 0x06b71a0a, 0x0e7d7a81, 0x12813683, 0x0f21d0cc, 0x18011964, 0x02cb6807, 0xf891e6}}, + {{0x05ca579f, 0x0c8470be, 0x11809535, 0x12fd89ea, 0x1c43b73a, 0x1c54716d, 0x13b462ba, 0x162577bf, 0x129f53}}} + }, + { + /* 1*16^44*G: */ + {{{0x1076f57b, 0x0678d5cc, 0x12b181ca, 0x170dc79d, 0x0a53f030, 0x11a80e79, 0x1e72e49f, 0x0ac91018, 0x20e118}}, + {{0x0da7afe6, 0x0c1df6dd, 0x186dc7a1, 0x01b93681, 0x1e9979fe, 0x01f1fdb6, 0x1bdcc6f7, 0x0a55886f, 0xff67b3}}}, + /* 3*16^44*G: */ + {{{0x0b661da4, 0x04f1d76a, 0x0a129721, 0x14a358c9, 0x01a21940, 0x1fec8183, 0x1b3a3df1, 0x0b770cc0, 0xffca2a}}, + {{0x0f9b8a5a, 0x0c241af5, 0x1a1fdbe2, 0x1f0f5bca, 0x06c8c478, 0x04d0a47a, 0x172294f3, 0x1c656e39, 0xce1cb0}}}, + /* 5*16^44*G: */ + {{{0x1c8cfd22, 0x0560f09c, 0x0daf960f, 0x160ce36c, 0x1354a4e2, 0x1a8a86d6, 0x1aad8d94, 0x1dbff822, 0xff209c}}, + {{0x13bc5535, 0x1c4d8a74, 0x1a12e508, 0x1e972592, 0x1f1c80d4, 0x00e92667, 0x1df881f5, 0x12e8d3a1, 0xad79f3}}}, + /* 7*16^44*G: */ + {{{0x0f4e983e, 0x003f2c2c, 0x05fddf3a, 0x1cdd90e0, 0x14b156d9, 0x0040699d, 0x0ea56181, 0x05a160f3, 0x33d5f0}}, + {{0x11ea37a9, 0x0b86dc3c, 0x18c3255b, 0x035524b2, 0x1a1eb7a7, 0x0b0b2add, 0x1e11cf0f, 0x0eda7388, 0x1eb5a}}}, + /* 9*16^44*G: */ + {{{0x01e52a73, 0x14ad3cfb, 0x01686f2d, 0x0622d92e, 0x15e65d0e, 0x1a6cd6dd, 0x1a42a7a5, 0x07b4c4c2, 0x8f1bd2}}, + {{0x0bea8f2e, 0x1ed0a28f, 0x06142a15, 0x0a05cc96, 0x1eade590, 0x0f2183a1, 0x02e2e1da, 0x0c7275b5, 0x4c7a8f}}}, + /* 11*16^44*G: */ + {{{0x1d2bc078, 0x0217f617, 0x1cfbd696, 0x00ec0a53, 0x080aec4a, 0x0c5bed6c, 0x1fcfb98a, 0x1fdef6c6, 0x963bc9}}, + {{0x1afb8657, 0x0ed918fa, 0x10e8f09f, 0x180851e8, 0x0a4f1c5e, 0x128494c0, 0x188a028f, 0x07f9a3ce, 0x7ed338}}}, + /* 13*16^44*G: */ + {{{0x0fb608ad, 0x038f3b75, 0x03f64e9a, 0x00aca9b1, 0x0bc49748, 0x16ee5be4, 0x011398c6, 0x15b0c3b5, 0x89fe6d}}, + {{0x064d4dad, 0x00a0373c, 0x1c2a1ed8, 0x13aa27c1, 0x1df39aea, 0x1effdb92, 0x03f21ed5, 0x10463e9d, 0x289827}}}, + /* 15*16^44*G: */ + {{{0x19ae4a29, 0x18c4c0d8, 0x08ca82c1, 0x1fb4dd44, 0x06984df2, 0x1d62708b, 0x1d654435, 0x0fe77af2, 0xc34804}}, + {{0x0eb09235, 0x04acd626, 0x126bafcd, 0x05d33e0a, 0x1ae9a842, 0x06e90706, 0x11584f6a, 0x1e40ad43, 0x584781}}} + }, + { + /* 1*16^45*G: */ + {{{0x19273a8f, 0x08a29744, 0x13f552b7, 0x1a8f1cd2, 0x1dac93fd, 0x163fefeb, 0x09ec0c63, 0x1f0e4740, 0x5c9cc4}}, + {{0x1ce80d6e, 0x0ed6534a, 0x06b2ad6b, 0x006ceb42, 0x0af964f0, 0x0c4e9b84, 0x0966a09d, 0x0f43bfda, 0x84efe0}}}, + /* 3*16^45*G: */ + {{{0x0883e382, 0x0464c2a2, 0x154dbce3, 0x009f9dea, 0x07431d06, 0x001ca900, 0x01716f89, 0x12577bfb, 0x5ac8e1}}, + {{0x1dfeaadc, 0x09b9ecde, 0x13674b94, 0x0dd9427a, 0x03976de7, 0x1ff9784b, 0x1200e723, 0x00098f51, 0xfcb7e5}}}, + /* 5*16^45*G: */ + {{{0x0f01a3e8, 0x052183bf, 0x120253af, 0x16ca865c, 0x07362c6e, 0x0ea2706b, 0x0460b545, 0x1316f224, 0x99dc06}}, + {{0x00f61114, 0x14322ff2, 0x1e3ca514, 0x0ce069af, 0x00044b7a, 0x0388b8ec, 0x0af1a5eb, 0x1ba47730, 0x67c69c}}}, + /* 7*16^45*G: */ + {{{0x0cd535ab, 0x01fbd802, 0x1d9370ce, 0x09b107d0, 0x1b9f3772, 0x01abe7e7, 0x18591009, 0x0c31c080, 0xabe2f3}}, + {{0x117b9c1a, 0x0388d9a2, 0x0b237664, 0x1cf43187, 0x1f7957fd, 0x1f959016, 0x0a4f7836, 0x0996eab6, 0x4f02d6}}}, + /* 9*16^45*G: */ + {{{0x0909970c, 0x1a5b359b, 0x19b93836, 0x11b74b33, 0x0099e451, 0x1d8fbbf3, 0x1c84df1b, 0x1af1873c, 0x227cd0}}, + {{0x1f809727, 0x02d25718, 0x0e67b10a, 0x01d87efd, 0x15defa21, 0x043a0e7f, 0x04761f5b, 0x0e390327, 0x2225e7}}}, + /* 11*16^45*G: */ + {{{0x09e65b59, 0x0cd6fe4c, 0x113fddf3, 0x02045efa, 0x1053b7a4, 0x14985466, 0x16da09fb, 0x10415db8, 0x363146}}, + {{0x09b4c2cf, 0x0050b213, 0x116dba72, 0x0792076b, 0x07fc1c14, 0x1c7c9011, 0x0a4a3a09, 0x0c42f12e, 0x1d87db}}}, + /* 13*16^45*G: */ + {{{0x0d4c2506, 0x0bd8ac5e, 0x07a7ebc0, 0x18bb8fe3, 0x11fec5b6, 0x14670c4e, 0x028f9d29, 0x16cd0d63, 0xf65ed6}}, + {{0x1913dfac, 0x0296e129, 0x15950af3, 0x11df8699, 0x0e7bd412, 0x0e17e9bb, 0x0ba14957, 0x0d065175, 0xd6d0bc}}}, + /* 15*16^45*G: */ + {{{0x1b47a80b, 0x0f27cba9, 0x0925d5e0, 0x0f8b4cc8, 0x1dba8ff9, 0x0e13b7d5, 0x0d5ca776, 0x1f423ec2, 0x66a0de}}, + {{0x1f795e8b, 0x1b8cafc7, 0x1bb74803, 0x014850a4, 0x0f474c23, 0x0f92b0d7, 0x09072b63, 0x0dbc6f59, 0x3e24aa}}} + }, + { + /* 1*16^46*G: */ + {{{0x08523eb3, 0x1eecd599, 0x1b1b12b9, 0x1474eaaa, 0x1cde3351, 0x0c22279e, 0x16ad1ab0, 0x1d7f1516, 0xbaffd4}}, + {{0x15acf387, 0x0bc18066, 0x122dbb86, 0x02399f7e, 0x0c09c245, 0x0759bcad, 0x1a0c00ea, 0x18bb792b, 0xfa93d}}}, + /* 3*16^46*G: */ + {{{0x14737641, 0x004cda77, 0x10b84eb4, 0x158182cd, 0x03fb71af, 0x1a891b2b, 0x07b5dde2, 0x04488391, 0x91b445}}, + {{0x0101bbe0, 0x1289594f, 0x01df40fc, 0x0fb8ccdc, 0x1b428c9d, 0x11ad5817, 0x05d8b04c, 0x17a6ffb5, 0x3f4463}}}, + /* 5*16^46*G: */ + {{{0x06dd01df, 0x1515d5c1, 0x03fbee71, 0x1dfeeca7, 0x09165743, 0x1363e434, 0x01ea8dfa, 0x0bbd05b6, 0x296d13}}, + {{0x0a3a0dc8, 0x0a869905, 0x199bd812, 0x15ec31cb, 0x177cadff, 0x094bbb63, 0x1790ae1c, 0x1bb25c28, 0x14054e}}}, + /* 7*16^46*G: */ + {{{0x1650f5dd, 0x13f37836, 0x046cb231, 0x1fc24843, 0x04a9654f, 0x0178f8cf, 0x08c63c4f, 0x1e7226cf, 0x4b0942}}, + {{0x1aeddc6d, 0x1752de63, 0x19ce98b0, 0x18cf05c9, 0x1c25f023, 0x0080ab09, 0x0f038157, 0x14c6cd95, 0x7432aa}}}, + /* 9*16^46*G: */ + {{{0x04e4a96f, 0x11baf478, 0x1956c51e, 0x10c5686a, 0x0e4af188, 0x0dc7e269, 0x046a0cfb, 0x14c5fd98, 0x36377}}, + {{0x11e08cdb, 0x1d7dbf02, 0x0244c9a2, 0x13184286, 0x06263840, 0x062abc2a, 0x1e0e364c, 0x03a6a9fb, 0xa1de19}}}, + /* 11*16^46*G: */ + {{{0x079b5a59, 0x00fe9f0b, 0x1a798a85, 0x03d13d64, 0x03251c62, 0x16ab84ec, 0x058af2ef, 0x1ee61ebc, 0xa1041a}}, + {{0x0c5514a3, 0x0695a011, 0x0b19b676, 0x00d21c3d, 0x02afbfb3, 0x086c39de, 0x0c650899, 0x0d551eb1, 0xad4217}}}, + /* 13*16^46*G: */ + {{{0x0ddd597b, 0x1746d836, 0x015d637a, 0x1262e199, 0x12007d88, 0x0d687cf4, 0x191c0cdc, 0x15a163ca, 0xda2167}}, + {{0x1de7fa04, 0x140b93f1, 0x13e7d189, 0x1c54a428, 0x0ebf6cdd, 0x180753cc, 0x14d87fd9, 0x16c4a8dd, 0xa21992}}}, + /* 15*16^46*G: */ + {{{0x13f7dbb0, 0x09a51115, 0x0fca7026, 0x1c47b84d, 0x0f29df4e, 0x1dc390b2, 0x19e2f218, 0x1846fed8, 0x1c3fdb}}, + {{0x12e16d32, 0x1265ee4b, 0x083e2b75, 0x0c8c7000, 0x118d41f0, 0x129ca525, 0x004fc2ba, 0x0206f253, 0x39260f}}} + }, + { + /* 1*16^47*G: */ + {{{0x1a111101, 0x1a9046c5, 0x16cf17e0, 0x1e1634ed, 0x04c96479, 0x11a692bf, 0x1d9bb48a, 0x0131f9da, 0x6ad2a}}, + {{0x1fb37ef4, 0x0d3dd4ea, 0x03a26bb0, 0x053b056e, 0x162a0de4, 0x000ddcf5, 0x18d56693, 0x038b1f0b, 0x2d5b8b}}}, + /* 3*16^47*G: */ + {{{0x102bef6f, 0x17e1c23b, 0x0c7b90dd, 0x1c9e308a, 0x0d475bba, 0x05eb35ec, 0x15c813de, 0x0aad8779, 0xf1a2ca}}, + {{0x0f6c1ca3, 0x0d968fec, 0x154ad004, 0x08fd503f, 0x00168b0b, 0x0a0bee01, 0x04fb7d15, 0x15e09106, 0xb39b59}}}, + /* 5*16^47*G: */ + {{{0x00455367, 0x030147e5, 0x1584f820, 0x09e59049, 0x11851e6c, 0x00b2c75f, 0x00c3b864, 0x093fc770, 0xe924ad}}, + {{0x08257e92, 0x1e8c67c1, 0x099d4ad7, 0x1463549f, 0x1bb6f52d, 0x029c9eb7, 0x1b55d482, 0x12f34287, 0xfcc97d}}}, + /* 7*16^47*G: */ + {{{0x02f8f1e7, 0x08f849ca, 0x147e78ba, 0x0954ba9b, 0x122f68dd, 0x09a882fd, 0x09be802e, 0x0fb8bee0, 0xf49d9c}}, + {{0x114d9972, 0x1114558c, 0x135ea0e4, 0x0002789c, 0x0f67901b, 0x09d9dcca, 0x12b9ab97, 0x08407c75, 0xf5585d}}}, + /* 9*16^47*G: */ + {{{0x1b219a79, 0x19d4b3bf, 0x0609e9de, 0x19a882fd, 0x189e65c4, 0x01aabaa8, 0x1522c38f, 0x007c8d53, 0x28b04d}}, + {{0x102dbe24, 0x05fbe6c8, 0x0012f97e, 0x0cd99f0c, 0x0206b861, 0x12b90c1f, 0x1c51673f, 0x13cb4299, 0xf658da}}}, + /* 11*16^47*G: */ + {{{0x17774af9, 0x14116ce2, 0x1182b62c, 0x0d74ca22, 0x0efb54c1, 0x01c94435, 0x1a4f5a14, 0x17cf983f, 0x3f4766}}, + {{0x1d4de990, 0x1e6bfb26, 0x0dcc9bfd, 0x1299fbf3, 0x05f511ca, 0x1c483737, 0x12e8eac5, 0x1ad4e663, 0xcc810d}}}, + /* 13*16^47*G: */ + {{{0x08e57705, 0x020cf8f2, 0x1356639e, 0x1d6ff590, 0x1361721c, 0x0d5a0eb7, 0x19e47cab, 0x00581f1d, 0xe3249e}}, + {{0x18caec0c, 0x0a58cf41, 0x1ce10882, 0x128bae2c, 0x06b5a501, 0x1c60f924, 0x141f72dd, 0x10e026d2, 0xa66665}}}, + /* 15*16^47*G: */ + {{{0x125b93ac, 0x1c5e757d, 0x01fe34b1, 0x0404a20b, 0x1b4e917e, 0x13b49efd, 0x1872f7e7, 0x017bf6f2, 0xa68958}}, + {{0x0f589aab, 0x11e8e26b, 0x113f4eba, 0x03f02fb3, 0x19ff2fcf, 0x1780af82, 0x00faa9fc, 0x12969e0f, 0x4657ca}}} + }, + { + /* 1*16^48*G: */ + {{{0x113728be, 0x07907fd9, 0x11ae529b, 0x072b2347, 0x15fc5964, 0x1fc1a218, 0x09d89cdb, 0x0ef4f092, 0xa6d396}}, + {{0x0357f5f4, 0x15d5c19e, 0x010166fc, 0x15241845, 0x1ecdf824, 0x1d5e9693, 0x00599ae2, 0x0e936171, 0x674f84}}}, + /* 3*16^48*G: */ + {{{0x0f15d864, 0x02a5ab4e, 0x13234f30, 0x0dfb8d43, 0x0cf35240, 0x1673df13, 0x0c36bf23, 0x1af8bbdb, 0x7ef66}}, + {{0x140907fe, 0x0312a13b, 0x1392a2d5, 0x0e1c7639, 0x1505e9f4, 0x062910fe, 0x1a941b50, 0x0bb713bc, 0x2db332}}}, + /* 5*16^48*G: */ + {{{0x17c7c05e, 0x14a8a1e0, 0x19505ab1, 0x07972a59, 0x08c0bb28, 0x1397baf9, 0x118053ce, 0x0bf80db8, 0x82df4e}}, + {{0x18fff26b, 0x0b09e816, 0x0b6cc02e, 0x0bb28d08, 0x1cbd1cf1, 0x0b10890b, 0x08289d48, 0x193192c8, 0xe4e188}}}, + /* 7*16^48*G: */ + {{{0x140368b0, 0x0570ea0a, 0x1ba52760, 0x09845f1f, 0x07a62132, 0x0dc69c72, 0x11a9f679, 0x13782561, 0x261efc}}, + {{0x1deb011f, 0x1d692acd, 0x06d74c04, 0x0817c3c7, 0x1ff797e3, 0x02966b27, 0x13a7f722, 0x1b7c70df, 0x8a9d2a}}}, + /* 9*16^48*G: */ + {{{0x15eb8034, 0x0819f4a8, 0x1a3292bc, 0x1666bead, 0x1692df30, 0x1f2cecf3, 0x1e4526a9, 0x1aef4584, 0x6d48df}}, + {{0x1ab0ce30, 0x039843d2, 0x0fa2587a, 0x0421d454, 0x14763080, 0x1cb24f02, 0x04bcf579, 0x08a2cbba, 0x3cb472}}}, + /* 11*16^48*G: */ + {{{0x140535c8, 0x08a1efe2, 0x036c4fad, 0x014ac619, 0x14e6f65f, 0x11fda7e2, 0x048e9244, 0x03cf7731, 0x93a5c0}}, + {{0x1d59b844, 0x04aba041, 0x16fb7ff1, 0x02c40926, 0x1a5c166a, 0x021ac70a, 0x0bd305aa, 0x12093018, 0x2d440e}}}, + /* 13*16^48*G: */ + {{{0x1e2047ba, 0x130d2b34, 0x0c3d94a8, 0x0e0932d7, 0x07031e54, 0x10700beb, 0x0aeecd76, 0x0522c24e, 0x3fb0b9}}, + {{0x1db24158, 0x1ff66a76, 0x1c0274d5, 0x0415cee2, 0x06dc86c4, 0x110e4cb3, 0x1e5329c9, 0x1cd042fb, 0x9d467a}}}, + /* 15*16^48*G: */ + {{{0x02df71c1, 0x05ededd7, 0x0edc8e80, 0x030d7d5f, 0x1e4381c3, 0x1dd4ef19, 0x0f5741d8, 0x073c11d0, 0xdab094}}, + {{0x04c3a1e3, 0x039a4209, 0x0d138eee, 0x0c661949, 0x00b3d6e9, 0x14379069, 0x13bce16b, 0x03ca89c3, 0x763cbc}}} + }, + { + /* 1*16^49*G: */ + {{{0x0514c6fd, 0x177b17fa, 0x0ac04f9b, 0x10769a1b, 0x15936fd6, 0x0dab887f, 0x1380cf53, 0x1001139e, 0xac25da}}, + {{0x05830541, 0x05a9cbb8, 0x0efcde98, 0x1307d048, 0x1338c810, 0x1498950d, 0x11ee20f6, 0x130b9689, 0xebc69d}}}, + /* 3*16^49*G: */ + {{{0x12fd0e3d, 0x0ca0e3f1, 0x09eb5820, 0x03c9b8a8, 0x05547e63, 0x04338f0b, 0x122ed35d, 0x0d893747, 0xeea7e6}}, + {{0x191d5868, 0x0208fb46, 0x0678f304, 0x175b4460, 0x17d985ac, 0x1f93df4d, 0x0984a210, 0x0b73f112, 0x83fed4}}}, + /* 5*16^49*G: */ + {{{0x0ccb63b3, 0x0cf8a793, 0x10239744, 0x1ffcd888, 0x1c67b7dd, 0x0a59aa01, 0x06f6eb46, 0x1a7d27c4, 0x9a3de2}}, + {{0x1df93fcf, 0x0e2994bb, 0x16089800, 0x003b3fde, 0x13c4c49c, 0x139b7740, 0x0b22027c, 0x120c2222, 0x2f809b}}}, + /* 7*16^49*G: */ + {{{0x163f1117, 0x07651a6e, 0x1fdb62f9, 0x12dee174, 0x1adaf348, 0x0698091f, 0x0b6c1440, 0x1e96a772, 0xa7c382}}, + {{0x1934cb9a, 0x091062f0, 0x1129a330, 0x1d6edda7, 0x1cfb5ae4, 0x1bbbb82e, 0x1e201167, 0x022cec37, 0x1b91e5}}}, + /* 9*16^49*G: */ + {{{0x06774a96, 0x05e40e2d, 0x0e38fa14, 0x063fb230, 0x0f497e82, 0x1dfe41d3, 0x084e1c50, 0x1319b0c2, 0x555aa9}}, + {{0x06db0e34, 0x0a04b96c, 0x189be9a7, 0x1aba9791, 0x0bbb89a0, 0x00f389a8, 0x11a66751, 0x0d1a40f7, 0xb05328}}}, + /* 11*16^49*G: */ + {{{0x0f7dac7d, 0x17bf779d, 0x0904848c, 0x0e572422, 0x1c369165, 0x0bc7c6bb, 0x0b5ed633, 0x0b66914f, 0x1a42f}}, + {{0x0195e46e, 0x1c4a518c, 0x13aa5ac9, 0x15e52651, 0x19216172, 0x1caa5c5f, 0x1e04d25f, 0x070aa40e, 0x957a9c}}}, + /* 13*16^49*G: */ + {{{0x002afc36, 0x015d0ea1, 0x0c1c74f8, 0x1bddaa28, 0x1d3a3134, 0x04f78da2, 0x18f4e96c, 0x06fd60b9, 0x4b47f5}}, + {{0x0f8133ff, 0x144fbb53, 0x17ef68d3, 0x1597d364, 0x1f573345, 0x037d0746, 0x1c30b72c, 0x0073390b, 0xf2fc45}}}, + /* 15*16^49*G: */ + {{{0x17d17f68, 0x15f971e3, 0x02eb61aa, 0x0b43bf97, 0x0418f791, 0x0b7a9b57, 0x033b5594, 0x1398a49d, 0x6b3dec}}, + {{0x09232402, 0x1d73d106, 0x1732da33, 0x0552d54d, 0x15d4747f, 0x00da0b66, 0x07bc1426, 0x06ffbdfb, 0xb86539}}} + }, + { + /* 1*16^50*G: */ + {{{0x0fd20bae, 0x0ea71bd1, 0x0d2a0455, 0x06ace5ab, 0x1343a260, 0x1d090bb6, 0x136409ee, 0x1db8779f, 0x285250}}, + {{0x14e0ab97, 0x0ef22ad2, 0x1bfcc8fe, 0x163459e3, 0x0c1716e9, 0x02568823, 0x1aa0fdca, 0x10de95af, 0x7866c0}}}, + /* 3*16^50*G: */ + {{{0x0636a50b, 0x0443b55d, 0x0dd465b3, 0x1dec2d57, 0x0baf65d2, 0x1d097e3c, 0x1d7160db, 0x0ca8bab4, 0xf1e3c5}}, + {{0x0b3128b6, 0x1bbc8a75, 0x0b5e9bb7, 0x1f4aeda4, 0x1f3136b7, 0x1533fb52, 0x139db1cd, 0x0f4dc3df, 0x12b884}}}, + /* 5*16^50*G: */ + {{{0x0e583340, 0x0bf8990a, 0x0d3cec94, 0x1836d6ba, 0x1228cf45, 0x06d5fd4d, 0x129db61f, 0x13903c26, 0x584d72}}, + {{0x14fb24b9, 0x17b72f4b, 0x05301c1c, 0x0ee14cc9, 0x0affa8f1, 0x1e2c9818, 0x02af34c1, 0x148ac1b0, 0x2fdd80}}}, + /* 7*16^50*G: */ + {{{0x0235809b, 0x1641f6f0, 0x1ae05ce4, 0x0e5be16b, 0x03c453c5, 0x0146e11c, 0x1df478b8, 0x001906fb, 0xbaaeae}}, + {{0x0154fd62, 0x0b0ec52e, 0x14b9f973, 0x18788543, 0x1f299835, 0x183de5a4, 0x0e02d288, 0x1067e649, 0x325788}}}, + /* 9*16^50*G: */ + {{{0x0d612268, 0x10021620, 0x17b405bd, 0x1eb3be14, 0x0b8b906c, 0x0f7d21ca, 0x0c69944e, 0x0c6c1842, 0x6c7e4}}, + {{0x060166a0, 0x05a5b009, 0x0b9c262f, 0x1b14b4f0, 0x053ca238, 0x03ae717a, 0x0335d1ff, 0x0bbee5bb, 0xcb6ad5}}}, + /* 11*16^50*G: */ + {{{0x012fbdc8, 0x0a1d1adc, 0x1038a8ef, 0x1c419545, 0x1a36db89, 0x1663db88, 0x10f96f0b, 0x1bd57acc, 0x64131}}, + {{0x09f99380, 0x09ff984d, 0x1ec08297, 0x15c4d163, 0x17598603, 0x006c9a4a, 0x00a3cace, 0x15865ace, 0x882c7f}}}, + /* 13*16^50*G: */ + {{{0x0bac9f32, 0x0f580032, 0x1a26c19d, 0x104398b2, 0x16400443, 0x00b7f0cd, 0x08de2859, 0x15984eb8, 0x366bb7}}, + {{0x1c85d47d, 0x17872e7d, 0x1c09a290, 0x19ca180f, 0x1cfc01fd, 0x01d5c6b0, 0x1c193c1e, 0x0e10f0b5, 0x7d107b}}}, + /* 15*16^50*G: */ + {{{0x06dd27eb, 0x1e5a9294, 0x0ed588c0, 0x18ab86f6, 0x032ecb98, 0x0871cddf, 0x1001ac9f, 0x04af98e7, 0xb767db}}, + {{0x0f65dac4, 0x0a90d23e, 0x1803b505, 0x0e016890, 0x1d85b64b, 0x05c0b5cc, 0x072d73ab, 0x1864c245, 0xdc1308}}} + }, + { + /* 1*16^51*G: */ + {{{0x1e07e4f1, 0x18f9d151, 0x17099f05, 0x0a28fba1, 0x0890e1fc, 0x15e03f7c, 0x19be0637, 0x00c5da06, 0xc472c1}}, + {{0x06b0daa9, 0x18e341ec, 0x0ad76295, 0x076f63c4, 0x067d885f, 0x15ad426b, 0x03a590ca, 0x00328bee, 0x41820e}}}, + /* 3*16^51*G: */ + {{{0x02f8acb1, 0x1b4a9a55, 0x0fc14fd0, 0x1acaa8c2, 0x0c31eb39, 0x1bd25c76, 0x0f5a1786, 0x0732a8bc, 0x86f88f}}, + {{0x05098a19, 0x05821688, 0x05949c45, 0x14c57dd2, 0x1b1214ee, 0x0bc7dac8, 0x035acdf2, 0x0d34d047, 0x2ccfdd}}}, + /* 5*16^51*G: */ + {{{0x1cadab66, 0x041bbdb7, 0x14d580b6, 0x0d46787b, 0x1f9a0e1c, 0x134b306c, 0x19d1f7dd, 0x03f93291, 0xa2a3be}}, + {{0x0c1aeb12, 0x1a509242, 0x002b497b, 0x1bd14a69, 0x15ee1fb2, 0x1f6ba691, 0x199c1941, 0x1cd6a497, 0xa93bd8}}}, + /* 7*16^51*G: */ + {{{0x007c25bf, 0x04e91099, 0x0b6025ee, 0x178eef7c, 0x080be82f, 0x09233be0, 0x16e4d9f8, 0x1daadcf1, 0x354e7c}}, + {{0x1a864211, 0x091d1cca, 0x082a8854, 0x0ead960b, 0x1e5585fd, 0x0c1252e7, 0x01c39baf, 0x1d377328, 0x629d62}}}, + /* 9*16^51*G: */ + {{{0x15e1dad8, 0x13ec1703, 0x112f4b92, 0x014f0b0d, 0x19f498b8, 0x07b90604, 0x016de6d7, 0x155ebbd0, 0xc751c9}}, + {{0x0e4afc8b, 0x09b59b09, 0x02a300b4, 0x112e1474, 0x1a4ffe6f, 0x00feb6e4, 0x178afad3, 0x06ff8f33, 0xcb9864}}}, + /* 11*16^51*G: */ + {{{0x099d9ba2, 0x1890d908, 0x0aa9d5f4, 0x0cafad2f, 0x19ca80cf, 0x09435a87, 0x0085e2ad, 0x08373a3a, 0xaee9cc}}, + {{0x1ed2ad58, 0x06b0241d, 0x05d2439e, 0x1cd1c4d4, 0x0b1f5312, 0x1dfd8063, 0x0dbdefa5, 0x005b6a54, 0xb72cf3}}}, + /* 13*16^51*G: */ + {{{0x094d208d, 0x1ff84124, 0x1ce46918, 0x1b51fe1f, 0x1f0cefb0, 0x1057958f, 0x1b15affd, 0x08d4f225, 0xd0d3f}}, + {{0x1d45d1b1, 0x117a9fbc, 0x0776aff5, 0x0781a34e, 0x09ff0b21, 0x06d32fc8, 0x05a6c11d, 0x1015c3ee, 0xdc371b}}}, + /* 15*16^51*G: */ + {{{0x19cfd890, 0x17411640, 0x01c45171, 0x0d86e6f8, 0x041e1cb3, 0x0c1c3f2a, 0x14d7d1f3, 0x0d68826e, 0x778d14}}, + {{0x16f057f2, 0x0d43fd3e, 0x1482b2b2, 0x148a911d, 0x0cd3796c, 0x18c6cbc3, 0x0bdca949, 0x02676023, 0x3c85ba}}} + }, + { + /* 1*16^52*G: */ + {{{0x039bb85f, 0x10784e1c, 0x14398b0c, 0x03f60d40, 0x13458010, 0x0164cd6a, 0x0cad55d6, 0x11a2ccc8, 0x55d539}}, + {{0x0fed936f, 0x1fb188c2, 0x0cf6787d, 0x1ad4fe30, 0x0a72ad90, 0x154f475d, 0x18b41671, 0x12093ff1, 0x576e22}}}, + /* 3*16^52*G: */ + {{{0x12ea285a, 0x03f15c90, 0x0e443470, 0x0383cdef, 0x0a7a39f0, 0x02c6eee0, 0x022de160, 0x011029f1, 0x1bb464}}, + {{0x107dc702, 0x006453cc, 0x0bea53b7, 0x05469732, 0x0a4fc1e5, 0x0cdc495a, 0x0496903f, 0x17f5f5c2, 0x8a3016}}}, + /* 5*16^52*G: */ + {{{0x1539785a, 0x08094806, 0x0a5fc051, 0x11a0ed70, 0x07a548c8, 0x1f133c31, 0x053b825c, 0x0e1c05f4, 0x4bc4e}}, + {{0x0a62e3bb, 0x1cc823ae, 0x0c9d70d4, 0x002ee4f4, 0x1fb4f877, 0x10e79f9e, 0x130e97d6, 0x04971969, 0x5729f2}}}, + /* 7*16^52*G: */ + {{{0x0114cfb5, 0x096a5d8f, 0x1569a8c4, 0x0c313547, 0x10876d13, 0x1cf0dbef, 0x05cd61dc, 0x1fb83f10, 0xd3e492}}, + {{0x097f7045, 0x075cd52b, 0x1d53bbef, 0x11e0dc8c, 0x1fea39c8, 0x133b5f8a, 0x122c7fb8, 0x014e7f18, 0x3604a7}}}, + /* 9*16^52*G: */ + {{{0x0c55ddff, 0x1133a5dd, 0x152de1c6, 0x1a367ec9, 0x1c1791da, 0x091887e6, 0x094e2939, 0x0ab0c508, 0xbebfc2}}, + {{0x1ea2f303, 0x0a6d8651, 0x02ea9c1b, 0x15045aca, 0x0576c3cc, 0x08e25bbb, 0x133a28e8, 0x0cb812c0, 0x850de2}}}, + /* 11*16^52*G: */ + {{{0x099ead51, 0x0d7a0f26, 0x164893a4, 0x06a87443, 0x184715da, 0x098e8b03, 0x1580beca, 0x1b8e7a41, 0x36e984}}, + {{0x0e0e5e3e, 0x17dcea13, 0x10a02bf1, 0x177d82e0, 0x12b203ba, 0x0fe4d671, 0x017d2fef, 0x08e437e9, 0x143771}}}, + /* 13*16^52*G: */ + {{{0x03093df0, 0x145bbdd9, 0x1d91935e, 0x1c4902ce, 0x193af785, 0x0a3caa6b, 0x12bbcc54, 0x00087ec0, 0x91b675}}, + {{0x12cc9f14, 0x06f461be, 0x12bd9fd7, 0x0d0bf1f3, 0x01c5d933, 0x1d6d4b71, 0x00351df1, 0x03cb0494, 0x7fedb}}}, + /* 15*16^52*G: */ + {{{0x1b7b8dc4, 0x11b3dc37, 0x06ce1228, 0x09260515, 0x02fac5b3, 0x1f0d01b0, 0x00f9f125, 0x18f43891, 0xc5d9c3}}, + {{0x1b2df0ea, 0x0b24bc68, 0x1f524dbb, 0x11ee5fa3, 0x10af3d0d, 0x01c302e2, 0x1e796023, 0x1c4b9fb6, 0x3e59b9}}} + }, + { + /* 1*16^53*G: */ + {{{0x12513926, 0x116de4e1, 0x1b217b31, 0x0d1281df, 0x03383cc2, 0x1d5925fe, 0x1c9a53fa, 0x08c79410, 0x884286}}, + {{0x0fb8a54d, 0x17214155, 0x16f05683, 0x1d6245d7, 0x01207ce0, 0x06d9b2ec, 0x1569a598, 0x1380385b, 0xf77842}}}, + /* 3*16^53*G: */ + {{{0x0520f1f1, 0x095d60cb, 0x1fdf693b, 0x03693cc8, 0x16f4ad7f, 0x1e8b4667, 0x00675697, 0x06deede1, 0x3bad9d}}, + {{0x1698df4d, 0x10cb8392, 0x0d9c961a, 0x1303449d, 0x00e80499, 0x1d3ecef6, 0x1966f367, 0x1c67c49c, 0x49f99d}}}, + /* 5*16^53*G: */ + {{{0x1185ae66, 0x18f22545, 0x09e0f7c2, 0x182db7df, 0x0118ba6e, 0x0fa9e892, 0x11b59e9f, 0x1635b618, 0xa6b3a4}}, + {{0x0bfbf21b, 0x1f5aa9fc, 0x0c92375e, 0x1e14bbeb, 0x1e8d8bcb, 0x148e6cb4, 0x188c4d86, 0x1aec7112, 0xcb6e8}}}, + /* 7*16^53*G: */ + {{{0x0c630e3a, 0x0b426727, 0x1cfc70aa, 0x00c182ff, 0x110c1f61, 0x11d3ec3f, 0x1293889f, 0x0b4d9222, 0x1f848f}}, + {{0x06ca610a, 0x176cffb4, 0x0d65c92b, 0x0724d7fc, 0x1a4264ba, 0x041e8d4e, 0x058e18a7, 0x0599a0df, 0xa02855}}}, + /* 9*16^53*G: */ + {{{0x041bcd10, 0x0ff79e6e, 0x0d138553, 0x12aaec8b, 0x02eb211b, 0x133be365, 0x0d622c68, 0x097d2938, 0xcfd1e5}}, + {{0x0a62e732, 0x000af5cd, 0x154e596b, 0x1322ba10, 0x12fafacd, 0x08dead82, 0x02ee715d, 0x0bec012b, 0xf4e31f}}}, + /* 11*16^53*G: */ + {{{0x028da5fb, 0x1d30ec91, 0x01c39c93, 0x097ebc55, 0x1291b90e, 0x0af1f3b8, 0x19544b1e, 0x01808d93, 0x4b3e73}}, + {{0x1c169e48, 0x1f8531c5, 0x08d1caef, 0x07423938, 0x0b48c65a, 0x1d03dc70, 0x03a7a032, 0x09b446cc, 0x8c4096}}}, + /* 13*16^53*G: */ + {{{0x1f894780, 0x0748d2c4, 0x06bb176e, 0x139946b1, 0x0d8f737e, 0x1c52a5a1, 0x1f9c7552, 0x1bee8871, 0xafe915}}, + {{0x1511d444, 0x1cb4aa9e, 0x102bd14b, 0x1d17ce75, 0x12e36909, 0x01a1199f, 0x1a1aa52d, 0x0811a408, 0x3caec3}}}, + /* 15*16^53*G: */ + {{{0x1182fa6e, 0x0d18fe5e, 0x00722bfd, 0x0c65bad3, 0x1c9e7c0a, 0x11d1b69a, 0x1215619a, 0x05021ff5, 0xc0548}}, + {{0x061cd145, 0x15e7f55b, 0x1db407db, 0x0cc8b096, 0x1602bf5e, 0x05c4a32e, 0x14cf46aa, 0x195a1e3e, 0x2f9cd2}}} + }, + { + /* 1*16^54*G: */ + {{{0x14441fbb, 0x105aead6, 0x1b44578f, 0x150a414b, 0x125559ea, 0x062af6dc, 0x0751fed8, 0x09c43bc4, 0xb4958c}}, + {{0x0add7118, 0x03f1e4fa, 0x118e1053, 0x13ec265a, 0x0b7e12db, 0x00fde1c6, 0x03bf9701, 0x17f32cc8, 0xfcd6e7}}}, + /* 3*16^54*G: */ + {{{0x171f07f1, 0x176b4261, 0x15c31296, 0x19db6e61, 0x0684b878, 0x105b2303, 0x11348cba, 0x0bcf6408, 0xb3a43f}}, + {{0x001dfda5, 0x1e6917a7, 0x12c3b314, 0x10ef419b, 0x15ee7ec7, 0x1139b4cb, 0x1ae1060c, 0x0b6491fa, 0x34c64f}}}, + /* 5*16^54*G: */ + {{{0x0b559d49, 0x1be4bd23, 0x1726d13f, 0x0368d21b, 0x008b148e, 0x17fec260, 0x052b0998, 0x0d348d7c, 0x452e98}}, + {{0x1a0d6ba7, 0x03e30236, 0x07b9095d, 0x050ad876, 0x1ee52598, 0x02bdb3b0, 0x0343d700, 0x11e32bf0, 0x8c5e8a}}}, + /* 7*16^54*G: */ + {{{0x13ce94da, 0x1bd33f5a, 0x00a79814, 0x049e84ad, 0x074ee8bb, 0x106e1d62, 0x0aed4737, 0x1a918dac, 0x19a7f5}}, + {{0x1025e7bb, 0x182238b1, 0x097ac0dc, 0x04a60d5c, 0x0a2e8fb6, 0x08ea2100, 0x170cbda9, 0x14f5260e, 0xa2504b}}}, + /* 9*16^54*G: */ + {{{0x147dc697, 0x0b4b6636, 0x1856e6ee, 0x1c315f6b, 0x06fa417e, 0x18595afe, 0x1370047a, 0x004149e6, 0xbdaf5b}}, + {{0x06a479e2, 0x088e5f3c, 0x0de91dee, 0x045cf10b, 0x1aa08551, 0x1af23dab, 0x0db233d5, 0x0d97bf50, 0x3cec0c}}}, + /* 11*16^54*G: */ + {{{0x0ce05e10, 0x1005c8db, 0x1841880a, 0x1ef93e87, 0x070db8ae, 0x1bff4267, 0x0576ae12, 0x1d2be73a, 0x265dba}}, + {{0x171324f4, 0x1bf80c58, 0x19319fff, 0x07e7267a, 0x15dcb066, 0x0745ab65, 0x1eb7cecf, 0x1a134606, 0x58df63}}}, + /* 13*16^54*G: */ + {{{0x13fe5938, 0x08ade2ad, 0x0db5f37b, 0x10607b6c, 0x068669ec, 0x04ea9d2b, 0x0e5a27dd, 0x07e2fccc, 0xc43e08}}, + {{0x183732f3, 0x13cbab76, 0x1101d1dd, 0x0460e2c4, 0x0402eab6, 0x0181d5e2, 0x1160d424, 0x12473f79, 0x54c602}}}, + /* 15*16^54*G: */ + {{{0x1dd52975, 0x18e387bc, 0x0d106030, 0x1e1fa60d, 0x066ba2bc, 0x086d3bd9, 0x121996b1, 0x0e0147f0, 0x7868f1}}, + {{0x1c626fac, 0x00b76a81, 0x05a4110c, 0x0df3d94d, 0x1276c68f, 0x10e36d88, 0x1b8444fe, 0x19e6242f, 0xe89097}}} + }, + { + /* 1*16^55*G: */ + {{{0x0e12e1df, 0x0127321b, 0x1d87412b, 0x0ffa16fa, 0x0027cd8a, 0x1f89d9a3, 0x0ad904d2, 0x12d11d26, 0xd0e091}}, + {{0x1fd28fbe, 0x132a26dc, 0x11ae37da, 0x19897b30, 0x1f867544, 0x105b48ed, 0x114ad3ad, 0x0b3fcfa2, 0x69c9a}}}, + /* 3*16^55*G: */ + {{{0x084aa098, 0x186c2880, 0x1b8f80ae, 0x02028152, 0x1fa8509c, 0x1ed65fe0, 0x03ace629, 0x0a942661, 0xb517a4}}, + {{0x0540efbf, 0x0025acfa, 0x0911ff58, 0x0916a8d2, 0x06fa3a4d, 0x1f17d879, 0x1e6983a8, 0x0fa183f0, 0xa3d87}}}, + /* 5*16^55*G: */ + {{{0x0744bfa1, 0x0cad6552, 0x04d90f5b, 0x0da4f9c1, 0x1e387cc2, 0x13896c79, 0x1bd9ef08, 0x07096a2c, 0xf8ec14}}, + {{0x12b65f6d, 0x14927319, 0x04001831, 0x06f58b87, 0x00f610a6, 0x07d934eb, 0x0698c8da, 0x164227f7, 0x761134}}}, + /* 7*16^55*G: */ + {{{0x1227a4bb, 0x1161df49, 0x03667cbd, 0x0d63e01f, 0x0f2e64be, 0x075690ea, 0x0b9e539d, 0x0f1b6f7f, 0x320cff}}, + {{0x10f3d2d4, 0x00e64835, 0x18be5c16, 0x0e46e813, 0x16299604, 0x0b512a7f, 0x1a4aadde, 0x1a80e550, 0xaf9fe8}}}, + /* 9*16^55*G: */ + {{{0x1c2ca683, 0x1adad2f2, 0x0569cdce, 0x19e6bc15, 0x1426a206, 0x0ee65aa1, 0x16145fb7, 0x0f8d4f5d, 0xc08de}}, + {{0x1db5f259, 0x12036dab, 0x1a9a31a4, 0x11af6fc1, 0x00e79c3c, 0x14ce6fe7, 0x1866df20, 0x10abd42d, 0xddb76d}}}, + /* 11*16^55*G: */ + {{{0x052ae5cd, 0x033d67c1, 0x1f75e187, 0x0ca5f5e9, 0x0390995b, 0x1bd22672, 0x10f4639b, 0x0d5a188f, 0xd1f8c7}}, + {{0x1e6d2dda, 0x15cbde1f, 0x027d3f1f, 0x15d02ad3, 0x1203239b, 0x0bd80fb0, 0x000ab1e6, 0x18cc241d, 0x74d45d}}}, + /* 13*16^55*G: */ + {{{0x0bdc603f, 0x1c803355, 0x17ff96ad, 0x1acb9acf, 0x020d8c96, 0x1f63133b, 0x03024f8c, 0x0d27e712, 0xa6cb83}}, + {{0x096befcc, 0x16701f06, 0x1985cd72, 0x1d82d498, 0x10b72fb1, 0x0ded2628, 0x0bf23cb6, 0x1c8c3e79, 0xd823c8}}}, + /* 15*16^55*G: */ + {{{0x02c374b0, 0x0f1d3097, 0x1c36d28a, 0x166b316a, 0x04ef0bf5, 0x04b8a921, 0x0c84dafb, 0x123d4d86, 0x8a6c9c}}, + {{0x178c08bd, 0x1fbe7c6d, 0x03d3560e, 0x0a69e868, 0x132a0461, 0x042ee480, 0x1ebde69e, 0x09ecb9bf, 0xe4bc7f}}} + }, + { + /* 1*16^56*G: */ + {{{0x0895df07, 0x1381f887, 0x01daf61a, 0x0be7f403, 0x08ffefd7, 0x03738670, 0x1fbbad6c, 0x0a84f07b, 0x68f6b8}}, + {{0x18712655, 0x063b7c53, 0x042fdfe4, 0x0527a5e6, 0x05028cf5, 0x0226fed2, 0x139bef20, 0x17525c81, 0xcbe1fe}}}, + /* 3*16^56*G: */ + {{{0x1aa89692, 0x00c7d119, 0x1308c239, 0x1611adf2, 0x0a776713, 0x1320920c, 0x1d37a65b, 0x0e302cd1, 0x3bdfca}}, + {{0x1a510308, 0x1ededa18, 0x18bbc4e4, 0x1818c68b, 0x05333c7d, 0x10a8d76d, 0x1ee12509, 0x0d0aca2c, 0xa721f}}}, + /* 5*16^56*G: */ + {{{0x037ea1d7, 0x1cd91a16, 0x06ab9341, 0x126cb1f1, 0x19e4fb23, 0x02a928c6, 0x0158d4ca, 0x12b6ea42, 0xaca8ac}}, + {{0x1ba973a2, 0x0f7d8824, 0x0e4d7a77, 0x1b7fb882, 0x12189e1e, 0x0625c943, 0x108250c1, 0x0cfbaeb9, 0x7f12c5}}}, + /* 7*16^56*G: */ + {{{0x13245b7f, 0x0d93c0e6, 0x165fefd7, 0x1acd2d20, 0x05311a37, 0x10d7cc6c, 0x103881b0, 0x009b6ccf, 0xfa9047}}, + {{0x0ddf6bef, 0x1c19ef8a, 0x13c751fb, 0x1dd9a4c4, 0x1c189cb9, 0x11f6a078, 0x1a90b5be, 0x06ce9506, 0x1a1c8c}}}, + /* 9*16^56*G: */ + {{{0x17f0953a, 0x10fbc7eb, 0x0550e6ad, 0x122907fe, 0x092a8184, 0x1c70e97d, 0x163359ca, 0x15723eaf, 0x1d431a}}, + {{0x1d68f260, 0x1e2cebb4, 0x1c1f9f05, 0x1c535210, 0x1fae0f48, 0x0641e70a, 0x087af14c, 0x1877e322, 0x8d667}}}, + /* 11*16^56*G: */ + {{{0x05227c70, 0x1188b89b, 0x08fa9c16, 0x17d56667, 0x1c60ff32, 0x19ad9718, 0x04df7692, 0x01ab47c2, 0x6deeea}}, + {{0x0369b9b7, 0x0b6d1c08, 0x0420f6eb, 0x15d83cad, 0x0cc84287, 0x05d7f7b5, 0x19000053, 0x01f8e887, 0xcbb93f}}}, + /* 13*16^56*G: */ + {{{0x0421b54d, 0x1afeaf44, 0x159293fe, 0x1657074a, 0x09dca579, 0x0e95d8fd, 0x036352b2, 0x020c6aaf, 0x28135c}}, + {{0x0ee5d868, 0x0e6784dc, 0x18c4362b, 0x09299923, 0x18c15ef0, 0x0eba083f, 0x18541bea, 0x17c70a37, 0x9ed84a}}}, + /* 15*16^56*G: */ + {{{0x08e4ac10, 0x0bf2ac8f, 0x1892a6a4, 0x0d502559, 0x1b568799, 0x062d04ff, 0x1def5b0f, 0x0ec41620, 0x339a05}}, + {{0x0d1312a0, 0x1b6d4322, 0x07319bbd, 0x13cf11e8, 0x1553e503, 0x06fbc567, 0x11fd0e15, 0x17dbad52, 0xca985f}}} + }, + { + /* 1*16^57*G: */ + {{{0x07ea1f34, 0x1a6d58bb, 0x1ce78374, 0x17a6a808, 0x153d6646, 0x0e8bc9d9, 0x0d9b0ed0, 0x1447a8e9, 0x7f9460}}, + {{0x0d9063ec, 0x05f61656, 0x18d3ff16, 0x1f02a249, 0x16661c6f, 0x195fc783, 0x061da7c7, 0x0ef1f60c, 0xd0d516}}}, + /* 3*16^57*G: */ + {{{0x1dfc0a9d, 0x02dba64d, 0x1ddea837, 0x0846b629, 0x0a2a2de4, 0x11b23570, 0x1808a983, 0x1f098653, 0xdb43e7}}, + {{0x1e9ad713, 0x058849d4, 0x14bc153c, 0x1208e6ad, 0x19fa4883, 0x1640a677, 0x1646e295, 0x1457c6d6, 0xb0168d}}}, + /* 5*16^57*G: */ + {{{0x0283808d, 0x05e58bba, 0x190ea24c, 0x1e0f9c6a, 0x078980f8, 0x0f4890fd, 0x06bae145, 0x103dc1a4, 0xe9af30}}, + {{0x19b39d33, 0x13617c71, 0x04db4665, 0x1724a22a, 0x14971976, 0x132a87f7, 0x098216bc, 0x091388e2, 0xa976c8}}}, + /* 7*16^57*G: */ + {{{0x0d8426df, 0x1497a165, 0x1be28289, 0x0272b49c, 0x083590f4, 0x049c1dfc, 0x0601c0eb, 0x0c65900d, 0x6eb733}}, + {{0x000b4267, 0x1fbacf71, 0x1f86e0cf, 0x05d2f907, 0x08e8d925, 0x05967660, 0x0a680c39, 0x1822e60f, 0x96c56e}}}, + /* 9*16^57*G: */ + {{{0x1a16453d, 0x1e8b6be0, 0x040cf6fd, 0x1d0f7cec, 0x0d0e68b7, 0x0dc8fc5a, 0x1d1785d0, 0x0bc0f3ab, 0xd3979d}}, + {{0x07b6d737, 0x1e5dc18b, 0x1a58693c, 0x1becc514, 0x0456917d, 0x092ba9b9, 0x16e69342, 0x06baf335, 0xc55f93}}}, + /* 11*16^57*G: */ + {{{0x0d326504, 0x150ccd2f, 0x0d480b33, 0x15368c4f, 0x0fc12f65, 0x1dc460d7, 0x08d0b0e0, 0x139cb718, 0x54c392}}, + {{0x1ec9e107, 0x1f808fd3, 0x0299c9a2, 0x0a7b3cc1, 0x0ab4447a, 0x1514248a, 0x1b431226, 0x04ac1d42, 0x469630}}}, + /* 13*16^57*G: */ + {{{0x1b5ecce7, 0x0227abb5, 0x03484d4c, 0x102b1618, 0x059c8732, 0x0741e0cb, 0x1b16e13a, 0x1650ccda, 0x3744e9}}, + {{0x0a247721, 0x00990a0b, 0x09be0e48, 0x116be0a5, 0x1ec3c28e, 0x14ab7594, 0x1ea83839, 0x1f1b3208, 0x9546e0}}}, + /* 15*16^57*G: */ + {{{0x05fef996, 0x064946f2, 0x1094d454, 0x0d1ec728, 0x1f2de140, 0x08520f79, 0x154cc933, 0x02fc6cad, 0x11343e}}, + {{0x1792aded, 0x00a8573a, 0x01bc6390, 0x1c9acb41, 0x13765d0c, 0x0fc98313, 0x1bbac137, 0x101bd751, 0x4a9e84}}} + }, + { + /* 1*16^58*G: */ + {{{0x0e6d8483, 0x164da6bc, 0x059fc373, 0x05af508d, 0x0e94582f, 0x1a4f8db7, 0x18f4a563, 0x0c1467aa, 0x9c39cb}}, + {{0x0b2c50fb, 0x0599ab7b, 0x0f90da25, 0x1bca5e7b, 0x16546bba, 0x0b5bde50, 0x14400f46, 0x03dc927c, 0xf097bf}}}, + /* 3*16^58*G: */ + {{{0x07a14dda, 0x0ae18fc2, 0x12d37cfd, 0x1a0852ce, 0x13083606, 0x147b9200, 0x0b5d10dd, 0x19921d5b, 0x18f37a}}, + {{0x05ac8364, 0x117e6e19, 0x014db2f8, 0x11ef8f15, 0x1cd0b77d, 0x1ff6770d, 0x109b5eef, 0x17554125, 0x2f944b}}}, + /* 5*16^58*G: */ + {{{0x0c7d59be, 0x13a74746, 0x024cbed5, 0x1430b11c, 0x0736b98e, 0x1ff723b9, 0x07693b17, 0x118503cf, 0x541335}}, + {{0x04f80590, 0x0bf50fb7, 0x002cd9cb, 0x04b62b92, 0x0515a53e, 0x07e900b2, 0x00939f12, 0x1bd2d396, 0x680fd4}}}, + /* 7*16^58*G: */ + {{{0x076051a8, 0x15c064fc, 0x115fa963, 0x0ee72c74, 0x05280bed, 0x00d1e0dc, 0x13c1773f, 0x04d23632, 0x4e2fd1}}, + {{0x09cc9005, 0x17f63a7f, 0x113f8b9a, 0x11754b25, 0x031bcebb, 0x1ad0a845, 0x0ec8dc6c, 0x1b7ebe9f, 0x122abb}}}, + /* 9*16^58*G: */ + {{{0x16a80e09, 0x13e547d2, 0x097d7f8d, 0x0be1eecc, 0x08fa0a27, 0x1ab409e0, 0x0d648013, 0x1a97dfe3, 0x2de758}}, + {{0x036a1cd3, 0x0b176faa, 0x16b5b267, 0x15b8cd2b, 0x064a07a1, 0x1958132f, 0x199f5f00, 0x062efb1d, 0xdd9acf}}}, + /* 11*16^58*G: */ + {{{0x1a3628ac, 0x1281ad97, 0x16d593b0, 0x177459be, 0x012a4568, 0x10b9e377, 0x095ca316, 0x159b83a9, 0xf597a0}}, + {{0x0cd3550f, 0x1501886b, 0x04841b9f, 0x1d9f23a8, 0x02cc0772, 0x1db944b1, 0x1155eec6, 0x11c6657a, 0xdb916}}}, + /* 13*16^58*G: */ + {{{0x0b75d6c3, 0x06b41ea5, 0x0e8a159e, 0x14a8afaa, 0x040f3c42, 0x038c10df, 0x1ee42284, 0x10dade89, 0xaa222a}}, + {{0x044cb028, 0x1932d273, 0x0a323d84, 0x03f9296b, 0x0df42607, 0x0512d771, 0x19db3912, 0x12600351, 0x563787}}}, + /* 15*16^58*G: */ + {{{0x035790c9, 0x1d42b9c9, 0x1cf140df, 0x03722ee7, 0x15e2e9e0, 0x0321c979, 0x16dd5bc3, 0x0d79b2e2, 0x8568b2}}, + {{0x06b1f5ac, 0x09faa9c1, 0x0151e7f7, 0x0bcbf1f7, 0x03ce8014, 0x0705721b, 0x0ab7a41e, 0x034b09ba, 0xd3f226}}} + }, + { + /* 1*16^59*G: */ + {{{0x1c38d4e4, 0x0ed4be2a, 0x0c5cdd1f, 0x1acbe363, 0x01e25e2f, 0x173a180c, 0x04e25d59, 0x04c22453, 0x3d9285}}, + {{0x00ccf0d3, 0x1d577806, 0x1eaf1fdb, 0x15627032, 0x069eacc0, 0x0b81ae14, 0x0ad4ffc1, 0x14ba8d1d, 0xd32ca0}}}, + /* 3*16^59*G: */ + {{{0x0dd7718a, 0x039192c0, 0x0f9393b4, 0x1036ca33, 0x0dff891c, 0x0cd12f3b, 0x1f0fdf05, 0x06e57205, 0x3b180e}}, + {{0x1afd82db, 0x1331314c, 0x0b45341c, 0x1222ace0, 0x1211e584, 0x1e4e9482, 0x02abea92, 0x02fe6d6b, 0xf2b6b1}}}, + /* 5*16^59*G: */ + {{{0x13d3653d, 0x140d288c, 0x1919f57e, 0x1d8964e9, 0x03eb2134, 0x07a04c54, 0x127d17bd, 0x00723ad3, 0xf352ff}}, + {{0x0a44f28e, 0x0f333c64, 0x1160b41e, 0x132bf2e5, 0x0d5055c1, 0x0b9efed3, 0x1af4082f, 0x0d996d8b, 0x447262}}}, + /* 7*16^59*G: */ + {{{0x0d4478da, 0x130ef8d7, 0x03805535, 0x19ed8448, 0x13ca1f15, 0x02e6dfbc, 0x108c2bed, 0x0e2069b1, 0x670ee4}}, + {{0x14f563f0, 0x147ff27f, 0x1c91dc18, 0x07702c1f, 0x150e81cc, 0x102d89c3, 0x0b289519, 0x084b7404, 0x5ca0c7}}}, + /* 9*16^59*G: */ + {{{0x03dbd581, 0x02714822, 0x15acd6eb, 0x1d671051, 0x06fa93cf, 0x1d185676, 0x0f6fbeef, 0x0a8693b3, 0x96f2e3}}, + {{0x08f59952, 0x0cd27ff7, 0x13d75153, 0x031e3aa0, 0x1baf435a, 0x0c71cb06, 0x1b3d3f97, 0x0110baa4, 0x9fcd8}}}, + /* 11*16^59*G: */ + {{{0x04e9c2fc, 0x159a1925, 0x0f5c1a87, 0x19e19e5f, 0x09a35e72, 0x01058a30, 0x06b20106, 0x0a2fd073, 0xc4946e}}, + {{0x06398ce8, 0x01a3b1bb, 0x1188c48d, 0x17e71da2, 0x18237e48, 0x07b47a3d, 0x0933a668, 0x041630b1, 0x748901}}}, + /* 13*16^59*G: */ + {{{0x04960b03, 0x05c8b9f4, 0x0023a3d7, 0x18756191, 0x14d8fb6a, 0x0462bc04, 0x0f6fe923, 0x09b537c6, 0x1653b6}}, + {{0x10f73c69, 0x00352a75, 0x0d5939fe, 0x11c1c943, 0x1a8948b3, 0x15ec1f4a, 0x15827d2c, 0x102d3cba, 0xa58370}}}, + /* 15*16^59*G: */ + {{{0x1b63640e, 0x0abbf18d, 0x11cb7cb1, 0x176fe521, 0x1cbf4979, 0x13ce5342, 0x14fd4031, 0x1afda5e2, 0x51076d}}, + {{0x02e4476c, 0x1b4b943c, 0x083bc087, 0x1d49d3ce, 0x0fda6c8b, 0x1280b970, 0x1ddabaf9, 0x0945adbb, 0xb39727}}} + }, + { + /* 1*16^60*G: */ + {{{0x044e8de3, 0x0318258d, 0x130781d8, 0x112cd45d, 0x117915c0, 0x1ee7845e, 0x02dce969, 0x16e8d102, 0xf50b99}}, + {{0x1b7f3588, 0x11f9dd36, 0x1c87a152, 0x0be31a42, 0x1cebbe97, 0x0b9d16f6, 0x1c321e26, 0x03cabe31, 0xe2b506}}}, + /* 3*16^60*G: */ + {{{0x02accf8b, 0x0ee35b5c, 0x005be9f7, 0x05332305, 0x1430481d, 0x1871289c, 0x1dc1917c, 0x0c34aa0a, 0x598d7f}}, + {{0x0f6cb808, 0x1c2339e0, 0x0d502e46, 0x11351e6a, 0x1ebcad22, 0x08b15939, 0x182551b1, 0x1ee9f1e4, 0x9f3121}}}, + /* 5*16^60*G: */ + {{{0x04aa7b3e, 0x1d9cd2a3, 0x1b273aa7, 0x09de360a, 0x0581013c, 0x1048aa0e, 0x113593f4, 0x025e93e9, 0x715a20}}, + {{0x1c7d081d, 0x0d19ca25, 0x02d1f436, 0x178b1151, 0x13b62421, 0x1447c548, 0x10287de4, 0x16354c0d, 0x5922b3}}}, + /* 7*16^60*G: */ + {{{0x020db220, 0x0dedb4bb, 0x01eb3934, 0x1996202d, 0x07876c71, 0x0744bfdc, 0x04971027, 0x0bcd5536, 0x49ec8c}}, + {{0x077338c2, 0x0dc56503, 0x0ee733a6, 0x1860e7ca, 0x15429842, 0x061a432f, 0x0a6cf6a7, 0x09fcbd4c, 0x99a97d}}}, + /* 9*16^60*G: */ + {{{0x1e771268, 0x0f5e518a, 0x02995a14, 0x1e294fb2, 0x07b7a2f4, 0x0c8702f0, 0x1120f9bc, 0x01a90a16, 0x1f8fb7}}, + {{0x0909c8dd, 0x17e98086, 0x04aceac6, 0x1a786239, 0x192a14e1, 0x16ba3930, 0x0afc4b0b, 0x1b68c374, 0xf53e7a}}}, + /* 11*16^60*G: */ + {{{0x08d9819e, 0x0f607959, 0x0b6ac695, 0x0cf25ee8, 0x0732cd60, 0x0a15d33c, 0x187f7574, 0x034c92fe, 0xb8d5c7}}, + {{0x09138ac4, 0x03c5f475, 0x15170f37, 0x093e26c3, 0x1dc79e2f, 0x121acdb1, 0x1e08edbb, 0x1e26426f, 0x1cd14e}}}, + /* 13*16^60*G: */ + {{{0x00bca3f3, 0x149edf33, 0x1801241a, 0x0686a28a, 0x0d8c4ecb, 0x15b0e440, 0x18f7758f, 0x158cb755, 0x2265b5}}, + {{0x117409ff, 0x0c14e362, 0x0e4f5689, 0x014c25a4, 0x164983c8, 0x09d1d884, 0x183d868c, 0x17eb959f, 0x97a198}}}, + /* 15*16^60*G: */ + {{{0x05bac36e, 0x19691ffa, 0x1d77340d, 0x11328b32, 0x1abcb599, 0x054fafe4, 0x049487f6, 0x1a206b09, 0xaf381c}}, + {{0x11b119a9, 0x19ec43d5, 0x1352a43f, 0x1705e3de, 0x02648589, 0x1f914a8d, 0x1ef72515, 0x0ff6fbe1, 0x681a08}}} + }, + { + /* 1*16^61*G: */ + {{{0x0037cfb4, 0x10cca02b, 0x136aa167, 0x01e48d87, 0x0b0e0740, 0x1a4406d0, 0x142c35df, 0x1047febd, 0x42531}}, + {{0x18775d23, 0x05b992f6, 0x1b177500, 0x07c9ea69, 0x01faceea, 0x12686433, 0x1df98a32, 0x199f5c01, 0xc90e8b}}}, + /* 3*16^61*G: */ + {{{0x06c34577, 0x027b39aa, 0x094abdde, 0x013d91cd, 0x0e4bde64, 0x11847460, 0x0922c7d7, 0x141c6179, 0x27557a}}, + {{0x0416193c, 0x0ff5cdc0, 0x110e02eb, 0x0594e6e9, 0x134f318a, 0x0bad24fe, 0x0ceddf23, 0x0b08c20b, 0x8399c7}}}, + /* 5*16^61*G: */ + {{{0x0401f4d3, 0x12c7edb5, 0x056cc07b, 0x185ca1d5, 0x1d7decf6, 0x1c1dfab0, 0x0d923941, 0x02fa4b0e, 0x8e6878}}, + {{0x0294d86b, 0x0140f4a2, 0x08644a24, 0x172de25d, 0x13cae900, 0x04d02836, 0x0fe98be0, 0x110dc593, 0x989cab}}}, + /* 7*16^61*G: */ + {{{0x0d2aa8e6, 0x0cb1ef13, 0x1bff8b71, 0x06b3f881, 0x1dbee205, 0x1401e533, 0x13db440d, 0x08c4a7cb, 0x98a417}}, + {{0x006cf75b, 0x0ba05f6b, 0x1fb4865a, 0x042ff556, 0x1cec9e30, 0x017ad17a, 0x0f5ac455, 0x128fc68c, 0x579ac6}}}, + /* 9*16^61*G: */ + {{{0x152c2d31, 0x0516647b, 0x187bb35f, 0x01576118, 0x1a946180, 0x17221f10, 0x03f64885, 0x084460a8, 0xe16854}}, + {{0x04a50fec, 0x05c41b41, 0x0660e507, 0x0c3257f1, 0x1c9343e7, 0x13216815, 0x0850becb, 0x0b9251ce, 0x70085}}}, + /* 11*16^61*G: */ + {{{0x13a7cdad, 0x119dd71b, 0x02f3ebd2, 0x1b07a5ca, 0x151a53ca, 0x117299c4, 0x0fa8728a, 0x09613aa2, 0xbfa631}}, + {{0x095cb953, 0x0fc44981, 0x1011e871, 0x0321f190, 0x0d1a7261, 0x02ddd8f7, 0x11e0a97e, 0x03299005, 0xb452e8}}}, + /* 13*16^61*G: */ + {{{0x0ade0fb7, 0x0de64280, 0x1946363b, 0x1b8e9bd4, 0x18e200c6, 0x0baf36ec, 0x134fb3c2, 0x05a152c3, 0xe68708}}, + {{0x1d4d6ece, 0x0bcf0798, 0x03089664, 0x03d0bdf1, 0x1a72117c, 0x072990e8, 0x1555797c, 0x090d0992, 0x2135a4}}}, + /* 15*16^61*G: */ + {{{0x0e432daf, 0x01e5af86, 0x009eb272, 0x1db41155, 0x14975f3b, 0x146bca83, 0x07a52ff0, 0x1f0c5535, 0x7ab16e}}, + {{0x071bdc48, 0x08bac455, 0x13f6c04c, 0x139c75ce, 0x1a7c5c7e, 0x0c1f146d, 0x02c68d64, 0x0152f865, 0x584e0e}}} + }, + { + /* 1*16^62*G: */ + {{{0x085fbd44, 0x03a3894c, 0x09899eb0, 0x0595473e, 0x1222c89c, 0x18e70b6a, 0x04178151, 0x1b5b356b, 0x9bbf06}}, + {{0x1a1d3e88, 0x1a23efd3, 0x00dbb4a8, 0x097bb82a, 0x147e8c0d, 0x0d798537, 0x028d9d57, 0x1509bc24, 0x1bcc7f}}}, + /* 3*16^62*G: */ + {{{0x14a7b506, 0x1bd73c95, 0x0182f822, 0x0b1775cf, 0x00ee9227, 0x1d9573a8, 0x0f4d0a73, 0x1ecb676b, 0x646ff7}}, + {{0x0b36f946, 0x0bce0929, 0x039a6572, 0x1bf89f81, 0x08fe8492, 0x1198c025, 0x02956987, 0x13a0c943, 0xbc112a}}}, + /* 5*16^62*G: */ + {{{0x0103b553, 0x0a5b8e4e, 0x0e16a005, 0x01d44f06, 0x0507ebe6, 0x0800593f, 0x0e6a7430, 0x0f54c2fa, 0x7cc054}}, + {{0x0d45a526, 0x1cce5d1e, 0x08a2df55, 0x10b41558, 0x094c4001, 0x14659cfe, 0x116af75c, 0x17a46500, 0x7b329e}}}, + /* 7*16^62*G: */ + {{{0x19e717a4, 0x0177a2a0, 0x1c06e06b, 0x1457b559, 0x0e15468d, 0x16a9b6d7, 0x0d38158f, 0x1321783b, 0x946851}}, + {{0x1526dea3, 0x1af87e8d, 0x02b36729, 0x132b21ee, 0x07dc0579, 0x08735933, 0x06a2cee3, 0x0841b8b2, 0xca5c78}}}, + /* 9*16^62*G: */ + {{{0x08362735, 0x1c161b78, 0x0acc9ef7, 0x1166fde2, 0x0d1f5dff, 0x07c75229, 0x03eac496, 0x037cfc1d, 0xe1b98f}}, + {{0x0ecb11b7, 0x0b09c64e, 0x1d0242be, 0x13dc67be, 0x12ba27e1, 0x1a4d41dd, 0x082df816, 0x15b352ed, 0xca83e9}}}, + /* 11*16^62*G: */ + {{{0x19046346, 0x1a05dec1, 0x0510020e, 0x0b1bce60, 0x1962d56a, 0x1035ecb3, 0x1fbdb422, 0x0b91fac0, 0x3536f2}}, + {{0x0c14fea6, 0x0008315e, 0x166cf8a5, 0x187a3d45, 0x1e1c39e6, 0x0b50d294, 0x0279cee8, 0x1c2fbc6a, 0x1e271f}}}, + /* 13*16^62*G: */ + {{{0x0847f6f8, 0x1ffc1f6b, 0x103bd4c3, 0x136a3f7a, 0x18c3c102, 0x08f9a5bf, 0x1379a405, 0x08d7c47a, 0xdf502}}, + {{0x1b1633a6, 0x06654535, 0x17cd126d, 0x1c2ccd13, 0x174ab4c6, 0x1627de10, 0x0728ac8c, 0x0fe53b5e, 0x210704}}}, + /* 15*16^62*G: */ + {{{0x1d4f1b81, 0x02e7576f, 0x1d2ec546, 0x134f4919, 0x09330ad6, 0x11d99b29, 0x0e63da77, 0x1a899ea6, 0xca9dd5}}, + {{0x02b7c8bd, 0x06316572, 0x0b6843e3, 0x12ec17f5, 0x07b3e66c, 0x18fda223, 0x11590091, 0x10ca37c5, 0x6e4b98}}} + }, + { + /* 1*16^63*G: */ + {{{0x044c0448, 0x1569919a, 0x00c678c7, 0x0aed0d82, 0x093e7963, 0x1e659dbc, 0x1e95250f, 0x1ea5287b, 0xb12fad}}, + {{0x1369de57, 0x12baf3b5, 0x1f137ca5, 0x0bce0665, 0x0a6a71d0, 0x07b33bd9, 0x12b3a5be, 0x12b3361e, 0x2e245}}}, + /* 3*16^63*G: */ + {{{0x04e9151f, 0x09b622a3, 0x0361063d, 0x14673390, 0x0fb3e67e, 0x0eed48ec, 0x15a10c95, 0x069e38a0, 0x48388e}}, + {{0x0d7a9434, 0x1f724f6e, 0x17a0c406, 0x028222a7, 0x02bcc99b, 0x19c98aa4, 0x1a79a894, 0x157e9c89, 0xb0ec63}}}, + /* 5*16^63*G: */ + {{{0x0e5b833d, 0x1efda52c, 0x02c90675, 0x1400e794, 0x109c9593, 0x0fc08280, 0x0f697997, 0x08f5c28a, 0xde8e64}}, + {{0x014c5cc7, 0x07e60cd5, 0x0071b27c, 0x1c062652, 0x1e265987, 0x14465eb6, 0x0f638cc8, 0x0782a122, 0x5e146}}}, + /* 7*16^63*G: */ + {{{0x01f7df48, 0x112c9f56, 0x1c4c5ecf, 0x0e8e7d70, 0x0f385b26, 0x0da7272c, 0x1a7a3955, 0x1a149d8f, 0xb18b12}}, + {{0x0846b546, 0x0d9eac87, 0x14795664, 0x07efc907, 0x0c33b3c8, 0x0135e2d6, 0x0d1173b1, 0x04546b3f, 0xff5319}}}, + /* 9*16^63*G: */ + {{{0x19c9fd24, 0x129d6e34, 0x0847837c, 0x1bb1308a, 0x0694402c, 0x05526394, 0x10cf1f07, 0x0e391b92, 0x5f51ad}}, + {{0x0d2ffd3a, 0x01dd7443, 0x092a65a7, 0x03ec488c, 0x04d03872, 0x02fde617, 0x0528334c, 0x023befce, 0x2bbbef}}}, + /* 11*16^63*G: */ + {{{0x17cec42e, 0x1e2031fe, 0x18ef6df8, 0x1b9267dd, 0x0d8556f3, 0x117721a3, 0x1aefe960, 0x1b26e603, 0xfdf340}}, + {{0x0db6908b, 0x07b1a5be, 0x178c320e, 0x1cd57ea1, 0x0d2c4456, 0x0ff39c65, 0x1b875c30, 0x0c72d198, 0xa3e59a}}}, + /* 13*16^63*G: */ + {{{0x01dc4fe8, 0x00c4a2a7, 0x01c9057f, 0x1142752c, 0x1c983f9e, 0x0948bdbe, 0x15e7b191, 0x19173d4b, 0xe3caeb}}, + {{0x1307b403, 0x15b05b90, 0x13e1a845, 0x0006cb42, 0x1f976513, 0x1ae5c580, 0x04a34880, 0x1a4f7e0b, 0x97f093}}}, + /* 15*16^63*G: */ + {{{0x1c119eff, 0x00a6b2e9, 0x07e6e119, 0x005c1815, 0x1a003399, 0x0d2e72c7, 0x16e26bd0, 0x1728550c, 0x3312d}}, + {{0x18b1b950, 0x14ad1d4f, 0x1455617f, 0x18b3b6be, 0x1e86f0ae, 0x1518bc04, 0x137c413b, 0x0c8d66e4, 0x6e3eda}}} + }, diff --git a/applications/external/flipbip/lib/crypto/options.h b/applications/external/flipbip/lib/crypto/options.h new file mode 100644 index 0000000000..f0edcc60f2 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/options.h @@ -0,0 +1,94 @@ +/** + * Copyright (c) 2013-2014 Pavol Rusnak + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __OPTIONS_H__ +#define __OPTIONS_H__ + +// use precomputed Curve Points (some scalar multiples of curve base point G) +#ifndef USE_PRECOMPUTED_CP +#define USE_PRECOMPUTED_CP 0 +#endif + +// use fast inverse method +#ifndef USE_INVERSE_FAST +#define USE_INVERSE_FAST 1 +#endif + +// support for printing bignum256 structures via printf +#ifndef USE_BN_PRINT +#define USE_BN_PRINT 0 +#endif + +// use deterministic signatures +#ifndef USE_RFC6979 +#define USE_RFC6979 1 +#endif + +// implement BIP32 caching +#ifndef USE_BIP32_CACHE +#define USE_BIP32_CACHE 0 +#define BIP32_CACHE_SIZE 10 +#define BIP32_CACHE_MAXDEPTH 8 +#endif + +// support constructing BIP32 nodes from ed25519 and curve25519 curves. +#ifndef USE_BIP32_25519_CURVES +#define USE_BIP32_25519_CURVES 0 +#endif + +// implement BIP39 caching +#ifndef USE_BIP39_CACHE +#define USE_BIP39_CACHE 0 +#define BIP39_CACHE_SIZE 4 +#endif + +// support Ethereum operations +#ifndef USE_ETHEREUM +#define USE_ETHEREUM 1 +#endif + +// support NEM operations +#ifndef USE_NEM +#define USE_NEM 0 +#endif + +// support MONERO operations +#ifndef USE_MONERO +#define USE_MONERO 0 +#endif + +// support CARDANO operations +#ifndef USE_CARDANO +#define USE_CARDANO 0 +#endif + +// support Keccak hashing +#ifndef USE_KECCAK +#define USE_KECCAK 1 +#endif + +// add way how to mark confidential data +#ifndef CONFIDENTIAL +#define CONFIDENTIAL +#endif + +#endif diff --git a/applications/external/flipbip/lib/crypto/pbkdf2.c b/applications/external/flipbip/lib/crypto/pbkdf2.c new file mode 100644 index 0000000000..e3ebe515e7 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/pbkdf2.c @@ -0,0 +1,195 @@ +/** + * Copyright (c) 2013-2014 Tomas Dzetkulic + * Copyright (c) 2013-2014 Pavol Rusnak + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "pbkdf2.h" +#include +#include "hmac.h" +#include "memzero.h" +#include "sha2.h" + +void pbkdf2_hmac_sha256_Init( + PBKDF2_HMAC_SHA256_CTX* pctx, + const uint8_t* pass, + int passlen, + const uint8_t* salt, + int saltlen, + uint32_t blocknr) { + SHA256_CTX ctx = {0}; +#if BYTE_ORDER == LITTLE_ENDIAN + REVERSE32(blocknr, blocknr); +#endif + + hmac_sha256_prepare(pass, passlen, pctx->odig, pctx->idig); + memzero(pctx->g, sizeof(pctx->g)); + pctx->g[8] = 0x80000000; + pctx->g[15] = (SHA256_BLOCK_LENGTH + SHA256_DIGEST_LENGTH) * 8; + + memcpy(ctx.state, pctx->idig, sizeof(pctx->idig)); + ctx.bitcount = SHA256_BLOCK_LENGTH * 8; + sha256_Update(&ctx, salt, saltlen); + sha256_Update(&ctx, (uint8_t*)&blocknr, sizeof(blocknr)); + sha256_Final(&ctx, (uint8_t*)pctx->g); +#if BYTE_ORDER == LITTLE_ENDIAN + for(uint32_t k = 0; k < SHA256_DIGEST_LENGTH / sizeof(uint32_t); k++) { + REVERSE32(pctx->g[k], pctx->g[k]); + } +#endif + sha256_Transform(pctx->odig, pctx->g, pctx->g); + memcpy(pctx->f, pctx->g, SHA256_DIGEST_LENGTH); + pctx->first = 1; +} + +void pbkdf2_hmac_sha256_Update(PBKDF2_HMAC_SHA256_CTX* pctx, uint32_t iterations) { + for(uint32_t i = pctx->first; i < iterations; i++) { + sha256_Transform(pctx->idig, pctx->g, pctx->g); + sha256_Transform(pctx->odig, pctx->g, pctx->g); + for(uint32_t j = 0; j < SHA256_DIGEST_LENGTH / sizeof(uint32_t); j++) { + pctx->f[j] ^= pctx->g[j]; + } + } + pctx->first = 0; +} + +void pbkdf2_hmac_sha256_Final(PBKDF2_HMAC_SHA256_CTX* pctx, uint8_t* key) { +#if BYTE_ORDER == LITTLE_ENDIAN + for(uint32_t k = 0; k < SHA256_DIGEST_LENGTH / sizeof(uint32_t); k++) { + REVERSE32(pctx->f[k], pctx->f[k]); + } +#endif + memcpy(key, pctx->f, SHA256_DIGEST_LENGTH); + memzero(pctx, sizeof(PBKDF2_HMAC_SHA256_CTX)); +} + +void pbkdf2_hmac_sha256( + const uint8_t* pass, + int passlen, + const uint8_t* salt, + int saltlen, + uint32_t iterations, + uint8_t* key, + int keylen) { + uint32_t last_block_size = keylen % SHA256_DIGEST_LENGTH; + uint32_t blocks_count = keylen / SHA256_DIGEST_LENGTH; + if(last_block_size) { + blocks_count++; + } else { + last_block_size = SHA256_DIGEST_LENGTH; + } + for(uint32_t blocknr = 1; blocknr <= blocks_count; blocknr++) { + PBKDF2_HMAC_SHA256_CTX pctx = {0}; + pbkdf2_hmac_sha256_Init(&pctx, pass, passlen, salt, saltlen, blocknr); + pbkdf2_hmac_sha256_Update(&pctx, iterations); + uint8_t digest[SHA256_DIGEST_LENGTH] = {0}; + pbkdf2_hmac_sha256_Final(&pctx, digest); + uint32_t key_offset = (blocknr - 1) * SHA256_DIGEST_LENGTH; + if(blocknr < blocks_count) { + memcpy(key + key_offset, digest, SHA256_DIGEST_LENGTH); + } else { + memcpy(key + key_offset, digest, last_block_size); + } + } +} + +void pbkdf2_hmac_sha512_Init( + PBKDF2_HMAC_SHA512_CTX* pctx, + const uint8_t* pass, + int passlen, + const uint8_t* salt, + int saltlen, + uint32_t blocknr) { + SHA512_CTX ctx = {0}; +#if BYTE_ORDER == LITTLE_ENDIAN + REVERSE32(blocknr, blocknr); +#endif + + hmac_sha512_prepare(pass, passlen, pctx->odig, pctx->idig); + memzero(pctx->g, sizeof(pctx->g)); + pctx->g[8] = 0x8000000000000000; + pctx->g[15] = (SHA512_BLOCK_LENGTH + SHA512_DIGEST_LENGTH) * 8; + + memcpy(ctx.state, pctx->idig, sizeof(pctx->idig)); + ctx.bitcount[0] = SHA512_BLOCK_LENGTH * 8; + ctx.bitcount[1] = 0; + sha512_Update(&ctx, salt, saltlen); + sha512_Update(&ctx, (uint8_t*)&blocknr, sizeof(blocknr)); + sha512_Final(&ctx, (uint8_t*)pctx->g); +#if BYTE_ORDER == LITTLE_ENDIAN + for(uint32_t k = 0; k < SHA512_DIGEST_LENGTH / sizeof(uint64_t); k++) { + REVERSE64(pctx->g[k], pctx->g[k]); + } +#endif + sha512_Transform(pctx->odig, pctx->g, pctx->g); + memcpy(pctx->f, pctx->g, SHA512_DIGEST_LENGTH); + pctx->first = 1; +} + +void pbkdf2_hmac_sha512_Update(PBKDF2_HMAC_SHA512_CTX* pctx, uint32_t iterations) { + for(uint32_t i = pctx->first; i < iterations; i++) { + sha512_Transform(pctx->idig, pctx->g, pctx->g); + sha512_Transform(pctx->odig, pctx->g, pctx->g); + for(uint32_t j = 0; j < SHA512_DIGEST_LENGTH / sizeof(uint64_t); j++) { + pctx->f[j] ^= pctx->g[j]; + } + } + pctx->first = 0; +} + +void pbkdf2_hmac_sha512_Final(PBKDF2_HMAC_SHA512_CTX* pctx, uint8_t* key) { +#if BYTE_ORDER == LITTLE_ENDIAN + for(uint32_t k = 0; k < SHA512_DIGEST_LENGTH / sizeof(uint64_t); k++) { + REVERSE64(pctx->f[k], pctx->f[k]); + } +#endif + memcpy(key, pctx->f, SHA512_DIGEST_LENGTH); + memzero(pctx, sizeof(PBKDF2_HMAC_SHA512_CTX)); +} + +void pbkdf2_hmac_sha512( + const uint8_t* pass, + int passlen, + const uint8_t* salt, + int saltlen, + uint32_t iterations, + uint8_t* key, + int keylen) { + uint32_t last_block_size = keylen % SHA512_DIGEST_LENGTH; + uint32_t blocks_count = keylen / SHA512_DIGEST_LENGTH; + if(last_block_size) { + blocks_count++; + } else { + last_block_size = SHA512_DIGEST_LENGTH; + } + for(uint32_t blocknr = 1; blocknr <= blocks_count; blocknr++) { + PBKDF2_HMAC_SHA512_CTX pctx = {0}; + pbkdf2_hmac_sha512_Init(&pctx, pass, passlen, salt, saltlen, blocknr); + pbkdf2_hmac_sha512_Update(&pctx, iterations); + uint8_t digest[SHA512_DIGEST_LENGTH] = {0}; + pbkdf2_hmac_sha512_Final(&pctx, digest); + uint32_t key_offset = (blocknr - 1) * SHA512_DIGEST_LENGTH; + if(blocknr < blocks_count) { + memcpy(key + key_offset, digest, SHA512_DIGEST_LENGTH); + } else { + memcpy(key + key_offset, digest, last_block_size); + } + } +} diff --git a/applications/external/flipbip/lib/crypto/pbkdf2.h b/applications/external/flipbip/lib/crypto/pbkdf2.h new file mode 100644 index 0000000000..22b580ff42 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/pbkdf2.h @@ -0,0 +1,82 @@ +/** + * Copyright (c) 2013-2014 Tomas Dzetkulic + * Copyright (c) 2013-2014 Pavol Rusnak + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __PBKDF2_H__ +#define __PBKDF2_H__ + +#include +#include "sha2.h" + +typedef struct _PBKDF2_HMAC_SHA256_CTX { + uint32_t odig[SHA256_DIGEST_LENGTH / sizeof(uint32_t)]; + uint32_t idig[SHA256_DIGEST_LENGTH / sizeof(uint32_t)]; + uint32_t f[SHA256_DIGEST_LENGTH / sizeof(uint32_t)]; + uint32_t g[SHA256_BLOCK_LENGTH / sizeof(uint32_t)]; + char first; +} PBKDF2_HMAC_SHA256_CTX; + +typedef struct _PBKDF2_HMAC_SHA512_CTX { + uint64_t odig[SHA512_DIGEST_LENGTH / sizeof(uint64_t)]; + uint64_t idig[SHA512_DIGEST_LENGTH / sizeof(uint64_t)]; + uint64_t f[SHA512_DIGEST_LENGTH / sizeof(uint64_t)]; + uint64_t g[SHA512_BLOCK_LENGTH / sizeof(uint64_t)]; + char first; +} PBKDF2_HMAC_SHA512_CTX; + +void pbkdf2_hmac_sha256_Init( + PBKDF2_HMAC_SHA256_CTX* pctx, + const uint8_t* pass, + int passlen, + const uint8_t* salt, + int saltlen, + uint32_t blocknr); +void pbkdf2_hmac_sha256_Update(PBKDF2_HMAC_SHA256_CTX* pctx, uint32_t iterations); +void pbkdf2_hmac_sha256_Final(PBKDF2_HMAC_SHA256_CTX* pctx, uint8_t* key); +void pbkdf2_hmac_sha256( + const uint8_t* pass, + int passlen, + const uint8_t* salt, + int saltlen, + uint32_t iterations, + uint8_t* key, + int keylen); + +void pbkdf2_hmac_sha512_Init( + PBKDF2_HMAC_SHA512_CTX* pctx, + const uint8_t* pass, + int passlen, + const uint8_t* salt, + int saltlen, + uint32_t blocknr); +void pbkdf2_hmac_sha512_Update(PBKDF2_HMAC_SHA512_CTX* pctx, uint32_t iterations); +void pbkdf2_hmac_sha512_Final(PBKDF2_HMAC_SHA512_CTX* pctx, uint8_t* key); +void pbkdf2_hmac_sha512( + const uint8_t* pass, + int passlen, + const uint8_t* salt, + int saltlen, + uint32_t iterations, + uint8_t* key, + int keylen); + +#endif diff --git a/applications/external/flipbip/lib/crypto/rand.c b/applications/external/flipbip/lib/crypto/rand.c new file mode 100644 index 0000000000..b35214285e --- /dev/null +++ b/applications/external/flipbip/lib/crypto/rand.c @@ -0,0 +1,102 @@ +/** + * Copyright (c) 2013-2014 Tomas Dzetkulic + * Copyright (c) 2013-2014 Pavol Rusnak + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +// NOTE: +// random32() and random_buffer() have been replaced in this implementation +// with Flipper Zero specific code. The original code is commented out below. + +#include "rand.h" + +// Flipper Zero RNG code: +#include + +#ifndef RAND_PLATFORM_INDEPENDENT + +// Original code: +// #pragma message("NOT SUITABLE FOR PRODUCTION USE! Replace random32() function with your own secure code.") + +// The following code is not supposed to be used in a production environment. +// It's included only to make the library testable. +// The message above tries to prevent any accidental use outside of the test +// environment. +// +// You are supposed to replace the random8() and random32() function with your +// own secure code. There is also a possibility to replace the random_buffer() +// function as it is defined as a weak symbol. + +static uint32_t seed = 0; + +void random_reseed(const uint32_t value) { + seed = value; +} + +// Original code: +// uint32_t random32(void) { +// // Linear congruential generator from Numerical Recipes +// // https://en.wikipedia.org/wiki/Linear_congruential_generator +// seed = 1664525 * seed + 1013904223; +// return seed; +// } + +// Flipper Zero RNG code: +uint32_t random32(void) { + return furi_hal_random_get(); +} + +// Flipper Zero RNG code: +void random_buffer(uint8_t* buf, size_t len) { + furi_hal_random_fill_buf(buf, len); +} + +#endif /* RAND_PLATFORM_INDEPENDENT */ + +// +// The following code is platform independent +// + +// Original code: +// void __attribute__((weak)) random_buffer(uint8_t *buf, size_t len) { +// uint32_t r = 0; +// for (size_t i = 0; i < len; i++) { +// if (i % 4 == 0) { +// r = random32(); +// } +// buf[i] = (r >> ((i % 4) * 8)) & 0xFF; +// } +// } + +uint32_t random_uniform(uint32_t n) { + uint32_t x = 0, max = 0xFFFFFFFF - (0xFFFFFFFF % n); + while((x = random32()) >= max) + ; + return x / (max / n); +} + +void random_permute(char* str, size_t len) { + for(int i = len - 1; i >= 1; i--) { + int j = random_uniform(i + 1); + char t = str[j]; + str[j] = str[i]; + str[i] = t; + } +} diff --git a/applications/external/flipbip/lib/crypto/rand.h b/applications/external/flipbip/lib/crypto/rand.h new file mode 100644 index 0000000000..fa854890f7 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/rand.h @@ -0,0 +1,37 @@ +/** + * Copyright (c) 2013-2014 Tomas Dzetkulic + * Copyright (c) 2013-2014 Pavol Rusnak + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __RAND_H__ +#define __RAND_H__ + +#include +#include + +void random_reseed(const uint32_t value); +uint32_t random32(void); +void random_buffer(uint8_t* buf, size_t len); + +uint32_t random_uniform(uint32_t n); +void random_permute(char* buf, size_t len); + +#endif diff --git a/applications/external/flipbip/lib/crypto/rc4.c b/applications/external/flipbip/lib/crypto/rc4.c new file mode 100644 index 0000000000..b42ad24ede --- /dev/null +++ b/applications/external/flipbip/lib/crypto/rc4.c @@ -0,0 +1,56 @@ +/** + * Copyright (c) 2017 Saleem Rashid + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, E1PRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "rc4.h" + +static inline void rc4_swap(RC4_CTX* ctx, uint8_t i, uint8_t j) { + uint8_t temp = ctx->S[i]; + ctx->S[i] = ctx->S[j]; + ctx->S[j] = temp; +} + +void rc4_init(RC4_CTX* ctx, const uint8_t* key, size_t length) { + ctx->i = 0; + ctx->j = 0; + + for(size_t i = 0; i < 256; i++) { + ctx->S[i] = i; + } + + uint8_t j = 0; + for(size_t i = 0; i < 256; i++) { + j += ctx->S[i] + key[i % length]; + rc4_swap(ctx, i, j); + } +} + +void rc4_encrypt(RC4_CTX* ctx, uint8_t* buffer, size_t length) { + for(size_t idx = 0; idx < length; idx++) { + ctx->i++; + ctx->j += ctx->S[ctx->i]; + + rc4_swap(ctx, ctx->i, ctx->j); + + uint8_t K = ctx->S[(ctx->S[ctx->i] + ctx->S[ctx->j]) % 256]; + buffer[idx] ^= K; + } +} diff --git a/applications/external/flipbip/lib/crypto/rc4.h b/applications/external/flipbip/lib/crypto/rc4.h new file mode 100644 index 0000000000..860d331307 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/rc4.h @@ -0,0 +1,37 @@ +/** + * Copyright (c) 2017 Saleem Rashid + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __RC4_H__ +#define __RC4_H__ + +#include +#include + +typedef struct { + uint8_t S[256]; + uint8_t i, j; +} RC4_CTX; + +void rc4_init(RC4_CTX* ctx, const uint8_t* key, size_t length); +void rc4_encrypt(RC4_CTX* ctx, uint8_t* buffer, size_t length); + +#endif diff --git a/applications/external/flipbip/lib/crypto/rfc6979.c b/applications/external/flipbip/lib/crypto/rfc6979.c new file mode 100644 index 0000000000..6bede6f5cf --- /dev/null +++ b/applications/external/flipbip/lib/crypto/rfc6979.c @@ -0,0 +1,65 @@ +/** + * Copyright (c) 2013-2014 Tomas Dzetkulic + * Copyright (c) 2013-2014 Pavol Rusnak + * Copyright (c) 2015 Jochen Hoenicke + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include + +#include "hmac_drbg.h" +#include "memzero.h" +#include "rfc6979.h" + +void init_rfc6979( + const uint8_t* priv_key, + const uint8_t* hash, + const ecdsa_curve* curve, + rfc6979_state* state) { + if(curve) { + bignum256 hash_bn = {0}; + bn_read_be(hash, &hash_bn); + + // Make sure hash is partly reduced modulo order + assert(bn_bitcount(&curve->order) >= 256); + bn_mod(&hash_bn, &curve->order); + + uint8_t hash_reduced[32] = {0}; + bn_write_be(&hash_bn, hash_reduced); + memzero(&hash_bn, sizeof(hash_bn)); + hmac_drbg_init(state, priv_key, 32, hash_reduced, 32); + memzero(hash_reduced, sizeof(hash_reduced)); + } else { + hmac_drbg_init(state, priv_key, 32, hash, 32); + } +} + +// generate next number from deterministic random number generator +void generate_rfc6979(uint8_t rnd[32], rfc6979_state* state) { + hmac_drbg_generate(state, rnd, 32); +} + +// generate K in a deterministic way, according to RFC6979 +// http://tools.ietf.org/html/rfc6979 +void generate_k_rfc6979(bignum256* k, rfc6979_state* state) { + uint8_t buf[32] = {0}; + generate_rfc6979(buf, state); + bn_read_be(buf, k); + memzero(buf, sizeof(buf)); +} diff --git a/applications/external/flipbip/lib/crypto/rfc6979.h b/applications/external/flipbip/lib/crypto/rfc6979.h new file mode 100644 index 0000000000..02367c38e5 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/rfc6979.h @@ -0,0 +1,44 @@ +/** + * Copyright (c) 2013-2014 Tomas Dzetkulic + * Copyright (c) 2013-2014 Pavol Rusnak + * Copyright (c) 2015-2017 Jochen Hoenicke + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __RFC6979_H__ +#define __RFC6979_H__ + +#include +#include "bignum.h" +#include "ecdsa.h" +#include "hmac_drbg.h" + +// rfc6979 pseudo random number generator state +typedef HMAC_DRBG_CTX rfc6979_state; + +void init_rfc6979( + const uint8_t* priv_key, + const uint8_t* hash, + const ecdsa_curve* curve, + rfc6979_state* rng); +void generate_rfc6979(uint8_t rnd[32], rfc6979_state* rng); +void generate_k_rfc6979(bignum256* k, rfc6979_state* rng); + +#endif diff --git a/applications/external/flipbip/lib/crypto/ripemd160.c b/applications/external/flipbip/lib/crypto/ripemd160.c new file mode 100644 index 0000000000..7d7f3898b2 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/ripemd160.c @@ -0,0 +1,330 @@ +/* + * RIPE MD-160 implementation + * + * Copyright (C) 2006-2015, ARM Limited, All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This file is part of mbed TLS (https://tls.mbed.org) + */ + +/* + * The RIPEMD-160 algorithm was designed by RIPE in 1996 + * http://homes.esat.kuleuven.be/~bosselae/ripemd160.html + * http://ehash.iaik.tugraz.at/wiki/RIPEMD-160 + */ + +#include + +#include "ripemd160.h" +#include "memzero.h" + +/* + * 32-bit integer manipulation macros (little endian) + */ +#ifndef GET_UINT32_LE +#define GET_UINT32_LE(n, b, i) \ + { \ + (n) = ((uint32_t)(b)[(i)]) | ((uint32_t)(b)[(i) + 1] << 8) | \ + ((uint32_t)(b)[(i) + 2] << 16) | ((uint32_t)(b)[(i) + 3] << 24); \ + } +#endif + +#ifndef PUT_UINT32_LE +#define PUT_UINT32_LE(n, b, i) \ + { \ + (b)[(i)] = (uint8_t)(((n)) & 0xFF); \ + (b)[(i) + 1] = (uint8_t)(((n) >> 8) & 0xFF); \ + (b)[(i) + 2] = (uint8_t)(((n) >> 16) & 0xFF); \ + (b)[(i) + 3] = (uint8_t)(((n) >> 24) & 0xFF); \ + } +#endif + +/* + * RIPEMD-160 context setup + */ +void ripemd160_Init(RIPEMD160_CTX* ctx) { + memzero(ctx, sizeof(RIPEMD160_CTX)); + ctx->total[0] = 0; + ctx->total[1] = 0; + ctx->state[0] = 0x67452301; + ctx->state[1] = 0xEFCDAB89; + ctx->state[2] = 0x98BADCFE; + ctx->state[3] = 0x10325476; + ctx->state[4] = 0xC3D2E1F0; +} + +#if !defined(MBEDTLS_RIPEMD160_PROCESS_ALT) +/* + * Process one block + */ +void ripemd160_process(RIPEMD160_CTX* ctx, const uint8_t data[RIPEMD160_BLOCK_LENGTH]) { + uint32_t A = 0, B = 0, C = 0, D = 0, E = 0, Ap = 0, Bp = 0, Cp = 0, Dp = 0, Ep = 0, + X[16] = {0}; + + GET_UINT32_LE(X[0], data, 0); + GET_UINT32_LE(X[1], data, 4); + GET_UINT32_LE(X[2], data, 8); + GET_UINT32_LE(X[3], data, 12); + GET_UINT32_LE(X[4], data, 16); + GET_UINT32_LE(X[5], data, 20); + GET_UINT32_LE(X[6], data, 24); + GET_UINT32_LE(X[7], data, 28); + GET_UINT32_LE(X[8], data, 32); + GET_UINT32_LE(X[9], data, 36); + GET_UINT32_LE(X[10], data, 40); + GET_UINT32_LE(X[11], data, 44); + GET_UINT32_LE(X[12], data, 48); + GET_UINT32_LE(X[13], data, 52); + GET_UINT32_LE(X[14], data, 56); + GET_UINT32_LE(X[15], data, 60); + + A = Ap = ctx->state[0]; + B = Bp = ctx->state[1]; + C = Cp = ctx->state[2]; + D = Dp = ctx->state[3]; + E = Ep = ctx->state[4]; + +#define F1(x, y, z) (x ^ y ^ z) +#define F2(x, y, z) ((x & y) | (~x & z)) +#define F3(x, y, z) ((x | ~y) ^ z) +#define F4(x, y, z) ((x & z) | (y & ~z)) +#define F5(x, y, z) (x ^ (y | ~z)) + +#define S(x, n) ((x << n) | (x >> (32 - n))) + +#define P(a, b, c, d, e, r, s, f, k) \ + a += f(b, c, d) + X[r] + k; \ + a = S(a, s) + e; \ + c = S(c, 10); + +#define P2(a, b, c, d, e, r, s, rp, sp) \ + P(a, b, c, d, e, r, s, F, K); \ + P(a##p, b##p, c##p, d##p, e##p, rp, sp, Fp, Kp); + +#define F F1 +#define K 0x00000000 +#define Fp F5 +#define Kp 0x50A28BE6 + P2(A, B, C, D, E, 0, 11, 5, 8); + P2(E, A, B, C, D, 1, 14, 14, 9); + P2(D, E, A, B, C, 2, 15, 7, 9); + P2(C, D, E, A, B, 3, 12, 0, 11); + P2(B, C, D, E, A, 4, 5, 9, 13); + P2(A, B, C, D, E, 5, 8, 2, 15); + P2(E, A, B, C, D, 6, 7, 11, 15); + P2(D, E, A, B, C, 7, 9, 4, 5); + P2(C, D, E, A, B, 8, 11, 13, 7); + P2(B, C, D, E, A, 9, 13, 6, 7); + P2(A, B, C, D, E, 10, 14, 15, 8); + P2(E, A, B, C, D, 11, 15, 8, 11); + P2(D, E, A, B, C, 12, 6, 1, 14); + P2(C, D, E, A, B, 13, 7, 10, 14); + P2(B, C, D, E, A, 14, 9, 3, 12); + P2(A, B, C, D, E, 15, 8, 12, 6); +#undef F +#undef K +#undef Fp +#undef Kp + +#define F F2 +#define K 0x5A827999 +#define Fp F4 +#define Kp 0x5C4DD124 + P2(E, A, B, C, D, 7, 7, 6, 9); + P2(D, E, A, B, C, 4, 6, 11, 13); + P2(C, D, E, A, B, 13, 8, 3, 15); + P2(B, C, D, E, A, 1, 13, 7, 7); + P2(A, B, C, D, E, 10, 11, 0, 12); + P2(E, A, B, C, D, 6, 9, 13, 8); + P2(D, E, A, B, C, 15, 7, 5, 9); + P2(C, D, E, A, B, 3, 15, 10, 11); + P2(B, C, D, E, A, 12, 7, 14, 7); + P2(A, B, C, D, E, 0, 12, 15, 7); + P2(E, A, B, C, D, 9, 15, 8, 12); + P2(D, E, A, B, C, 5, 9, 12, 7); + P2(C, D, E, A, B, 2, 11, 4, 6); + P2(B, C, D, E, A, 14, 7, 9, 15); + P2(A, B, C, D, E, 11, 13, 1, 13); + P2(E, A, B, C, D, 8, 12, 2, 11); +#undef F +#undef K +#undef Fp +#undef Kp + +#define F F3 +#define K 0x6ED9EBA1 +#define Fp F3 +#define Kp 0x6D703EF3 + P2(D, E, A, B, C, 3, 11, 15, 9); + P2(C, D, E, A, B, 10, 13, 5, 7); + P2(B, C, D, E, A, 14, 6, 1, 15); + P2(A, B, C, D, E, 4, 7, 3, 11); + P2(E, A, B, C, D, 9, 14, 7, 8); + P2(D, E, A, B, C, 15, 9, 14, 6); + P2(C, D, E, A, B, 8, 13, 6, 6); + P2(B, C, D, E, A, 1, 15, 9, 14); + P2(A, B, C, D, E, 2, 14, 11, 12); + P2(E, A, B, C, D, 7, 8, 8, 13); + P2(D, E, A, B, C, 0, 13, 12, 5); + P2(C, D, E, A, B, 6, 6, 2, 14); + P2(B, C, D, E, A, 13, 5, 10, 13); + P2(A, B, C, D, E, 11, 12, 0, 13); + P2(E, A, B, C, D, 5, 7, 4, 7); + P2(D, E, A, B, C, 12, 5, 13, 5); +#undef F +#undef K +#undef Fp +#undef Kp + +#define F F4 +#define K 0x8F1BBCDC +#define Fp F2 +#define Kp 0x7A6D76E9 + P2(C, D, E, A, B, 1, 11, 8, 15); + P2(B, C, D, E, A, 9, 12, 6, 5); + P2(A, B, C, D, E, 11, 14, 4, 8); + P2(E, A, B, C, D, 10, 15, 1, 11); + P2(D, E, A, B, C, 0, 14, 3, 14); + P2(C, D, E, A, B, 8, 15, 11, 14); + P2(B, C, D, E, A, 12, 9, 15, 6); + P2(A, B, C, D, E, 4, 8, 0, 14); + P2(E, A, B, C, D, 13, 9, 5, 6); + P2(D, E, A, B, C, 3, 14, 12, 9); + P2(C, D, E, A, B, 7, 5, 2, 12); + P2(B, C, D, E, A, 15, 6, 13, 9); + P2(A, B, C, D, E, 14, 8, 9, 12); + P2(E, A, B, C, D, 5, 6, 7, 5); + P2(D, E, A, B, C, 6, 5, 10, 15); + P2(C, D, E, A, B, 2, 12, 14, 8); +#undef F +#undef K +#undef Fp +#undef Kp + +#define F F5 +#define K 0xA953FD4E +#define Fp F1 +#define Kp 0x00000000 + P2(B, C, D, E, A, 4, 9, 12, 8); + P2(A, B, C, D, E, 0, 15, 15, 5); + P2(E, A, B, C, D, 5, 5, 10, 12); + P2(D, E, A, B, C, 9, 11, 4, 9); + P2(C, D, E, A, B, 7, 6, 1, 12); + P2(B, C, D, E, A, 12, 8, 5, 5); + P2(A, B, C, D, E, 2, 13, 8, 14); + P2(E, A, B, C, D, 10, 12, 7, 6); + P2(D, E, A, B, C, 14, 5, 6, 8); + P2(C, D, E, A, B, 1, 12, 2, 13); + P2(B, C, D, E, A, 3, 13, 13, 6); + P2(A, B, C, D, E, 8, 14, 14, 5); + P2(E, A, B, C, D, 11, 11, 0, 15); + P2(D, E, A, B, C, 6, 8, 3, 13); + P2(C, D, E, A, B, 15, 5, 9, 11); + P2(B, C, D, E, A, 13, 6, 11, 11); +#undef F +#undef K +#undef Fp +#undef Kp + + C = ctx->state[1] + C + Dp; + ctx->state[1] = ctx->state[2] + D + Ep; + ctx->state[2] = ctx->state[3] + E + Ap; + ctx->state[3] = ctx->state[4] + A + Bp; + ctx->state[4] = ctx->state[0] + B + Cp; + ctx->state[0] = C; +} +#endif /* !MBEDTLS_RIPEMD160_PROCESS_ALT */ + +/* + * RIPEMD-160 process buffer + */ +void ripemd160_Update(RIPEMD160_CTX* ctx, const uint8_t* input, uint32_t ilen) { + uint32_t fill = 0; + uint32_t left = 0; + + if(ilen == 0) return; + + left = ctx->total[0] & 0x3F; + fill = RIPEMD160_BLOCK_LENGTH - left; + + ctx->total[0] += (uint32_t)ilen; + ctx->total[0] &= 0xFFFFFFFF; + + if(ctx->total[0] < (uint32_t)ilen) ctx->total[1]++; + + if(left && ilen >= fill) { + memcpy((void*)(ctx->buffer + left), input, fill); + ripemd160_process(ctx, ctx->buffer); + input += fill; + ilen -= fill; + left = 0; + } + + while(ilen >= RIPEMD160_BLOCK_LENGTH) { + ripemd160_process(ctx, input); + input += RIPEMD160_BLOCK_LENGTH; + ilen -= RIPEMD160_BLOCK_LENGTH; + } + + if(ilen > 0) { + memcpy((void*)(ctx->buffer + left), input, ilen); + } +} + +static const uint8_t ripemd160_padding[RIPEMD160_BLOCK_LENGTH] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +/* + * RIPEMD-160 final digest + */ +void ripemd160_Final(RIPEMD160_CTX* ctx, uint8_t output[RIPEMD160_DIGEST_LENGTH]) { + uint32_t last = 0; + uint32_t padn = 0; + uint32_t high = 0; + uint32_t low = 0; + uint8_t msglen[8] = {0}; + + high = (ctx->total[0] >> 29) | (ctx->total[1] << 3); + low = (ctx->total[0] << 3); + + PUT_UINT32_LE(low, msglen, 0); + PUT_UINT32_LE(high, msglen, 4); + + last = ctx->total[0] & 0x3F; + padn = (last < 56) ? (56 - last) : (120 - last); + + ripemd160_Update(ctx, ripemd160_padding, padn); + ripemd160_Update(ctx, msglen, 8); + + PUT_UINT32_LE(ctx->state[0], output, 0); + PUT_UINT32_LE(ctx->state[1], output, 4); + PUT_UINT32_LE(ctx->state[2], output, 8); + PUT_UINT32_LE(ctx->state[3], output, 12); + PUT_UINT32_LE(ctx->state[4], output, 16); + + memzero(ctx, sizeof(RIPEMD160_CTX)); +} + +/* + * output = RIPEMD-160( input buffer ) + */ +void ripemd160(const uint8_t* msg, uint32_t msg_len, uint8_t hash[RIPEMD160_DIGEST_LENGTH]) { + RIPEMD160_CTX ctx = {0}; + ripemd160_Init(&ctx); + ripemd160_Update(&ctx, msg, msg_len); + ripemd160_Final(&ctx, hash); +} diff --git a/applications/external/flipbip/lib/crypto/ripemd160.h b/applications/external/flipbip/lib/crypto/ripemd160.h new file mode 100644 index 0000000000..271e9ec5b8 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/ripemd160.h @@ -0,0 +1,20 @@ +#ifndef __RIPEMD160_H__ +#define __RIPEMD160_H__ + +#include + +#define RIPEMD160_BLOCK_LENGTH 64 +#define RIPEMD160_DIGEST_LENGTH 20 + +typedef struct _RIPEMD160_CTX { + uint32_t total[2]; /*!< number of bytes processed */ + uint32_t state[5]; /*!< intermediate digest state */ + uint8_t buffer[RIPEMD160_BLOCK_LENGTH]; /*!< data block being processed */ +} RIPEMD160_CTX; + +void ripemd160_Init(RIPEMD160_CTX* ctx); +void ripemd160_Update(RIPEMD160_CTX* ctx, const uint8_t* input, uint32_t ilen); +void ripemd160_Final(RIPEMD160_CTX* ctx, uint8_t output[RIPEMD160_DIGEST_LENGTH]); +void ripemd160(const uint8_t* msg, uint32_t msg_len, uint8_t hash[RIPEMD160_DIGEST_LENGTH]); + +#endif diff --git a/applications/external/flipbip/lib/crypto/script.c b/applications/external/flipbip/lib/crypto/script.c new file mode 100644 index 0000000000..11173e973b --- /dev/null +++ b/applications/external/flipbip/lib/crypto/script.c @@ -0,0 +1,64 @@ +/** + * Copyright (c) 2016 Pavol Rusnak + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "script.h" +#include +#include "base58.h" + +int script_output_to_address(const uint8_t* script, int scriptlen, char* addr, int addrsize) { + uint8_t raw[35] = {0}; + + // P2PKH + if(scriptlen == 25 && script[0] == 0x76 && script[1] == 0xA9 && script[2] == 0x14 && + script[23] == 0x88 && script[24] == 0xAC) { + raw[0] = 0x00; + memcpy(raw + 1, script + 3, 20); + return base58_encode_check(raw, 1 + 20, HASHER_SHA2D, addr, addrsize); + } + + // P2SH + if(scriptlen == 23 && script[0] == 0xA9 && script[1] == 0x14 && script[22] == 0x87) { + raw[0] = 0x05; + memcpy(raw + 1, script + 2, 20); + return base58_encode_check(raw, 1 + 20, HASHER_SHA2D, addr, addrsize); + } + + // P2WPKH + if(scriptlen == 22 && script[0] == 0x00 && script[1] == 0x14) { + raw[0] = 0x06; + raw[1] = 0x00; + raw[2] = 0x00; + memcpy(raw + 3, script + 2, 20); + return base58_encode_check(raw, 3 + 20, HASHER_SHA2D, addr, addrsize); + } + + // P2WSH + if(scriptlen == 34 && script[0] == 0x00 && script[1] == 0x20) { + raw[0] = 0x0A; + raw[1] = 0x00; + raw[2] = 0x00; + memcpy(raw + 3, script + 2, 32); + return base58_encode_check(raw, 3 + 32, HASHER_SHA2D, addr, addrsize); + } + + return 0; +} diff --git a/applications/external/flipbip/lib/crypto/script.h b/applications/external/flipbip/lib/crypto/script.h new file mode 100644 index 0000000000..585b059fa2 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/script.h @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2016 Pavol Rusnak + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __SCRIPT_H__ +#define __SCRIPT_H__ + +#include + +int script_output_to_address(const uint8_t* script, int scriptlen, char* addr, int addrsize); + +#endif diff --git a/applications/external/flipbip/lib/crypto/secp256k1.c b/applications/external/flipbip/lib/crypto/secp256k1.c new file mode 100644 index 0000000000..d954dd733e --- /dev/null +++ b/applications/external/flipbip/lib/crypto/secp256k1.c @@ -0,0 +1,135 @@ +/** + * Copyright (c) 2013-2014 Tomas Dzetkulic + * Copyright (c) 2013-2014 Pavol Rusnak + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "secp256k1.h" + +const ecdsa_curve secp256k1 = { + /* .prime */ {/*.val =*/{ + 0x1ffffc2f, + 0x1ffffff7, + 0x1fffffff, + 0x1fffffff, + 0x1fffffff, + 0x1fffffff, + 0x1fffffff, + 0x1fffffff, + 0xffffff}}, + + /* G */ + {/*.x =*/{/*.val =*/{ + 0x16f81798, + 0x0f940ad8, + 0x138a3656, + 0x17f9b65b, + 0x10b07029, + 0x114ae743, + 0x0eb15681, + 0x0fdf3b97, + 0x79be66}}, + /*.y =*/{/*.val =*/{ + 0x1b10d4b8, + 0x023e847f, + 0x01550667, + 0x0f68914d, + 0x108a8fd1, + 0x1dfe0708, + 0x11957693, + 0x0ee4d478, + 0x483ada}}}, + + /* order */ + {/*.val =*/{ + 0x10364141, + 0x1e92f466, + 0x12280eef, + 0x1db9cd5e, + 0x1fffebaa, + 0x1fffffff, + 0x1fffffff, + 0x1fffffff, + 0xffffff}}, + + /* order_half */ + {/*.val =*/{ + 0x081b20a0, + 0x1f497a33, + 0x09140777, + 0x0edce6af, + 0x1ffff5d5, + 0x1fffffff, + 0x1fffffff, + 0x1fffffff, + 0x7fffff}}, + + /* a */ 0, + + /* b */ {/*.val =*/{7}} + +#if USE_PRECOMPUTED_CP + , + /* cp */ + { +#include "secp256k1.table" + } +#endif +}; + +const curve_info secp256k1_info = { + .bip32_name = "Bitcoin seed", + .params = &secp256k1, + .hasher_base58 = HASHER_SHA2D, + .hasher_sign = HASHER_SHA2D, + .hasher_pubkey = HASHER_SHA2_RIPEMD, + .hasher_script = HASHER_SHA2, +}; + +const curve_info secp256k1_decred_info = { + .bip32_name = "Bitcoin seed", + .params = &secp256k1, + .hasher_base58 = HASHER_BLAKED, + .hasher_sign = HASHER_BLAKE, + .hasher_pubkey = HASHER_BLAKE_RIPEMD, + .hasher_script = HASHER_BLAKE, +}; + +const curve_info secp256k1_groestl_info = { + .bip32_name = "Bitcoin seed", + .params = &secp256k1, + .hasher_base58 = HASHER_GROESTLD_TRUNC, + .hasher_sign = HASHER_SHA2, + .hasher_pubkey = HASHER_SHA2_RIPEMD, + .hasher_script = HASHER_SHA2, +}; + +const curve_info secp256k1_smart_info = { + .bip32_name = "Bitcoin seed", + .params = &secp256k1, +#if USE_KECCAK + .hasher_base58 = HASHER_SHA3K, +#else + .hasher_base58 = HASHER_SHA3, +#endif + .hasher_sign = HASHER_SHA2, + .hasher_pubkey = HASHER_SHA2_RIPEMD, + .hasher_script = HASHER_SHA2, +}; diff --git a/applications/external/flipbip/lib/crypto/secp256k1.h b/applications/external/flipbip/lib/crypto/secp256k1.h new file mode 100644 index 0000000000..3b45e48fea --- /dev/null +++ b/applications/external/flipbip/lib/crypto/secp256k1.h @@ -0,0 +1,38 @@ +/** + * Copyright (c) 2013-2014 Tomas Dzetkulic + * Copyright (c) 2013-2014 Pavol Rusnak + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __SECP256K1_H__ +#define __SECP256K1_H__ + +#include + +#include "bip32.h" +#include "ecdsa.h" + +extern const ecdsa_curve secp256k1; +extern const curve_info secp256k1_info; +extern const curve_info secp256k1_decred_info; +extern const curve_info secp256k1_groestl_info; +extern const curve_info secp256k1_smart_info; + +#endif diff --git a/applications/external/flipbip/lib/crypto/secp256k1.table b/applications/external/flipbip/lib/crypto/secp256k1.table new file mode 100644 index 0000000000..0fa87a8b16 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/secp256k1.table @@ -0,0 +1,1664 @@ + { + /* 1*16^0*G: */ + {{{0x16f81798, 0x0f940ad8, 0x138a3656, 0x17f9b65b, 0x10b07029, 0x114ae743, 0x0eb15681, 0x0fdf3b97, 0x79be66}}, + {{0x1b10d4b8, 0x023e847f, 0x01550667, 0x0f68914d, 0x108a8fd1, 0x1dfe0708, 0x11957693, 0x0ee4d478, 0x483ada}}}, + /* 3*16^0*G: */ + {{{0x1ce036f9, 0x100f889d, 0x1be66c21, 0x03908b06, 0x15229b53, 0x07c2fc4e, 0x0c4124d1, 0x00324b18, 0xf9308a}}, + {{0x04b8e672, 0x05cfebac, 0x1088c6db, 0x01533269, 0x1f356650, 0x1bf3151b, 0x00503f8c, 0x01ec65bd, 0x388f7b}}}, + /* 5*16^0*G: */ + {{{0x1240efe4, 0x1d46ab4d, 0x1866adf2, 0x17097bb8, 0x05128e88, 0x1392852e, 0x024d56d2, 0x09a340e4, 0x2f8bde}}, + {{0x06ac62d6, 0x0543e9d5, 0x035a1037, 0x104e3756, 0x1c426f78, 0x14eed364, 0x0f5b536e, 0x04c6dcbc, 0xd8ac22}}}, + /* 7*16^0*G: */ + {{{0x0ac4f9bc, 0x095eef6e, 0x0c38e73a, 0x0336fc06, 0x07a0e3d4, 0x19b2f975, 0x13aa8e63, 0x0c8dcbb6, 0x5cbdf0}}, + {{0x087264da, 0x08413140, 0x1f79ed69, 0x07a17027, 0x054dba81, 0x06b6c30d, 0x05828c5e, 0x081744ab, 0x6aebca}}}, + /* 9*16^0*G: */ + {{{0x1c27ccbe, 0x1af8886f, 0x15f9c530, 0x0f2d2e98, 0x19abde09, 0x0bc54faa, 0x194c26b4, 0x1c5e18fe, 0xacd484}}, + {{0x064f9c37, 0x0e613156, 0x17e383c1, 0x1111486e, 0x161e9add, 0x04b8bb1d, 0x07f590e0, 0x043614fb, 0xcc3389}}}, + /* 11*16^0*G: */ + {{{0x1da008cb, 0x1f60bc4a, 0x105e246e, 0x133017cb, 0x05aac564, 0x1235b863, 0x04797bd0, 0x1f0b1528, 0x774ae7}}, + {{0x0953c61b, 0x00eba64e, 0x1e75aa0c, 0x1b63c5bf, 0x1b365372, 0x0eab6bdb, 0x1864090f, 0x065d6d6b, 0xd984a0}}}, + /* 13*16^0*G: */ + {{{0x19405aa8, 0x176efc78, 0x03963377, 0x0bf78cc2, 0x08651b07, 0x0902e1ba, 0x022f1f47, 0x185b2ea5, 0xf28773}}, + {{0x1b03ed81, 0x0dae5a96, 0x07ea47ca, 0x140db4a4, 0x1af473a1, 0x0975b2e6, 0x0a25d608, 0x05d1b101, 0xab090}}}, + /* 15*16^0*G: */ + {{{0x027e080e, 0x056de7c7, 0x017de791, 0x0b28de79, 0x1f41131e, 0x0d7184af, 0x0a596919, 0x09efa87d, 0xd7924d}}, + {{0x16a26b58, 0x0826e4ff, 0x05b4e971, 0x015e57b1, 0x06defea4, 0x17611466, 0x0a9a0e10, 0x0e550d8e, 0x581e28}}} + }, + { + /* 1*16^1*G: */ + {{{0x0a6dec0a, 0x027744f1, 0x1e96ba71, 0x0626d370, 0x03e97b2a, 0x155e10e1, 0x1b14c046, 0x1276b3d3, 0xe60fce}}, + {{0x09616821, 0x0f996673, 0x148fc2f8, 0x0d123c89, 0x13710129, 0x0f9a7abc, 0x164a76e6, 0x0e733cb2, 0xf7e350}}}, + /* 3*16^1*G: */ + {{{0x1118e5c3, 0x1ec38550, 0x0afaf066, 0x0f364e8a, 0x05b4bfc5, 0x12b77a73, 0x01f6d105, 0x0bb2c8a6, 0x6eca33}}, + {{0x05a08668, 0x0c517bc0, 0x1e3b0d12, 0x12d47477, 0x075a03a4, 0x0bc83a5c, 0x1c4164bd, 0x16af4f40, 0xd50123}}}, + /* 5*16^1*G: */ + {{{0x0f87f62e, 0x16698f0a, 0x1c5849c3, 0x0dcc70c6, 0x059f010e, 0x1a2769a3, 0x03b035f1, 0x17de37f2, 0xe9623b}}, + {{0x044ee737, 0x1809f57d, 0x1a211394, 0x008793ba, 0x0a929fe6, 0x0a9d476d, 0x07a783fa, 0x07697853, 0x38a974}}}, + /* 7*16^1*G: */ + {{{0x0a8d733c, 0x18556fc1, 0x1f2a3e7a, 0x04e97ec5, 0x0d682ffc, 0x11b79040, 0x16e82212, 0x0e7ca2c3, 0xbc82dd}}, + {{0x147797f0, 0x13c30827, 0x0e25cc07, 0x074175ce, 0x102dfae9, 0x1a5fb8cf, 0x12b152a6, 0x07408963, 0xe5f28c}}}, + /* 9*16^1*G: */ + {{{0x1fbc7671, 0x1f7f118a, 0x01638cb5, 0x1e3790a5, 0x0f490743, 0x08e70bcc, 0x0847480a, 0x0918ecae, 0x8e3d12}}, + {{0x18717dec, 0x178ee320, 0x0f85129f, 0x0a57554c, 0x1e90eb93, 0x0070c9c9, 0x0e07d912, 0x1c21d9f9, 0x99a48}}}, + /* 11*16^1*G: */ + {{{0x1eb31db2, 0x1943a195, 0x1d41a83c, 0x15d04f11, 0x0a2b68fc, 0x0c9f6844, 0x126225a8, 0x15444694, 0x78a891}}, + {{0x19fa4343, 0x034eb11d, 0x002e0b4c, 0x0f379bb0, 0x02df6543, 0x192a9398, 0x172ff3d7, 0x0b7d6a06, 0x6912a3}}}, + /* 13*16^1*G: */ + {{{0x0db0e595, 0x09a47bbc, 0x0820aed9, 0x0c7973f7, 0x076eba71, 0x1bb2c0b0, 0x0c5f5f38, 0x030abb63, 0x7d8678}}, + {{0x1c733de8, 0x0ca8f1d5, 0x09754ca6, 0x0f089c1c, 0x18838293, 0x1715f6a7, 0x01dcb958, 0x1bfd90df, 0xe2b99a}}}, + /* 15*16^1*G: */ + {{{0x16060dfc, 0x047f7c28, 0x179a8a80, 0x08bf0840, 0x0b086765, 0x05cee20d, 0x0b212125, 0x01e00b05, 0xddc531}}, + {{0x07820ca8, 0x1afc55b7, 0x1411cc3e, 0x175f8d57, 0x10e9041d, 0x15b6e647, 0x1a480646, 0x075e41b2, 0xba0d2f}}} + }, + { + /* 1*16^2*G: */ + {{{0x15f51508, 0x123711fe, 0x0b072841, 0x073957ab, 0x1e238d8c, 0x171f0b96, 0x0767a8a9, 0x064258c1, 0x828226}}, + {{0x16e26caf, 0x18db757f, 0x1ec5efb4, 0x0c27585e, 0x00ace62d, 0x0b74185b, 0x1f917a09, 0x0130aafb, 0x11f8a8}}}, + /* 3*16^2*G: */ + {{{0x057e8dfa, 0x07e065cf, 0x11f8613f, 0x01232347, 0x18ca0098, 0x187c5654, 0x11303668, 0x05fe0f33, 0x8262cf}}, + {{0x1bac376a, 0x0e7fc6c7, 0x05311e0d, 0x0dda6656, 0x14f3457b, 0x111762d9, 0x19399bfb, 0x1c412213, 0x83fd95}}}, + /* 5*16^2*G: */ + {{{0x026bdb6f, 0x02972458, 0x1cd2e524, 0x0837a8f6, 0x19c877ca, 0x02d92674, 0x17545a04, 0x1163b41b, 0x19825c}}, + {{0x049cfc9b, 0x0efb8426, 0x1db4e9ad, 0x13dd9919, 0x19f6cebe, 0x10e64a7a, 0x1e3cc809, 0x01e1a990, 0x629431}}}, + /* 7*16^2*G: */ + {{{0x1d82824c, 0x07684a91, 0x054d3994, 0x0b1c68bc, 0x0999edfa, 0x1ab76361, 0x04510f17, 0x0d822c03, 0x6f12d8}}, + {{0x06eb34d0, 0x0bce1a40, 0x152f16e1, 0x19248210, 0x03769391, 0x0a79feb1, 0x1e821d66, 0x1e895677, 0x5c4ff7}}}, + /* 9*16^2*G: */ + {{{0x1b453629, 0x1b6ee016, 0x167980c1, 0x1fb9b81e, 0x0bef645c, 0x138b511d, 0x09745098, 0x0df34155, 0x203a8c}}, + {{0x1ff89f84, 0x0b8e3c29, 0x0a17b516, 0x1bd64b8a, 0x10612686, 0x1b68afa0, 0x06e4db31, 0x0a7bcbbb, 0x3b0f0b}}}, + /* 11*16^2*G: */ + {{{0x046c7ecb, 0x018986ef, 0x0ed33a5e, 0x15da7fcb, 0x0e1ec9d3, 0x1b99a433, 0x0607207b, 0x1d67a068, 0x6e2aca}}, + {{0x0ebc8720, 0x024900f7, 0x19d44b21, 0x0e0d7236, 0x1643afac, 0x026d787d, 0x18527603, 0x0cf2fdfd, 0x9e61a4}}}, + /* 13*16^2*G: */ + {{{0x10a4147e, 0x1b80c79f, 0x1d7c807a, 0x0fbb17ee, 0x10a58274, 0x18bf4524, 0x15aebd85, 0x125d3d22, 0xd5a704}}, + {{0x13fb65ff, 0x1259e5c1, 0x19fd5fe3, 0x09b308f2, 0x00532f4c, 0x04c83b2f, 0x071bf124, 0x1ebb7571, 0x9db526}}}, + /* 15*16^2*G: */ + {{{0x18edcec6, 0x16eda35e, 0x18d3d153, 0x0c3985a6, 0x0dac6a10, 0x17e31816, 0x0ea0148f, 0x13557c31, 0x38c511}}, + {{0x1933db08, 0x0b705fd8, 0x154c2991, 0x02d90456, 0x1282f28a, 0x196d13af, 0x0ca99a32, 0x0450bb2e, 0xe649dd}}} + }, + { + /* 1*16^3*G: */ + {{{0x11e5b739, 0x1fe72daa, 0x0888bb5c, 0x127067fa, 0x0846de0b, 0x0e63637e, 0x1969cbe6, 0x13ee5170, 0x175e15}}, + {{0x09fed695, 0x17d37ff7, 0x090d171b, 0x0b2ab5ba, 0x11f5eacb, 0x0bd28ffb, 0x07ae93be, 0x01b3c78f, 0xd3506e}}}, + /* 3*16^3*G: */ + {{{0x05041216, 0x0dbfc78e, 0x0ae0da99, 0x066bed08, 0x1ed523f7, 0x0cf7ee17, 0x13d04a2d, 0x0f643ef5, 0xda7531}}, + {{0x0e708572, 0x176994c3, 0x1eb3b6b6, 0x1580f5ce, 0x17fc6e9a, 0x110d9a16, 0x17c37c67, 0x08d7ee5a, 0x73f8a0}}}, + /* 5*16^3*G: */ + {{{0x0465a930, 0x00a1f38f, 0x04d4bf6c, 0x0fe382d6, 0x0eb1e258, 0x02c62541, 0x075c15cf, 0x1691d2e9, 0x1c71c5}}, + {{0x034638b5, 0x0c39fb66, 0x05d351c7, 0x08bc7f6e, 0x1b68c793, 0x18f94125, 0x08309c4f, 0x069d1ebf, 0x4a91c3}}}, + /* 7*16^3*G: */ + {{{0x1adb6ee7, 0x18c6d72d, 0x11281df8, 0x01ba864e, 0x14c9c785, 0x01bd484d, 0x159a4dba, 0x1f83e634, 0xd84e4a}}, + {{0x142ebed2, 0x16aab736, 0x08f99260, 0x11592e95, 0x1de4dfdd, 0x06ac7ab2, 0x07384a8e, 0x134f8f6f, 0xe52580}}}, + /* 9*16^3*G: */ + {{{0x049e6d10, 0x0a74f67d, 0x0b26748e, 0x15bfe75d, 0x16ed3f60, 0x15c942ff, 0x053506c8, 0x097bccd0, 0xf3d444}}, + {{0x1347da3f, 0x101c6602, 0x075b18c2, 0x15b4a19d, 0x04b5bfc1, 0x0ad60cc5, 0x18f52eae, 0x1bf4de02, 0xa4324}}}, + /* 11*16^3*G: */ + {{{0x09d33a07, 0x0659a037, 0x0e6f2ad2, 0x05dc1154, 0x1f4044e7, 0x0a9066de, 0x1627e421, 0x0593b383, 0xae3065}}, + {{0x00a0b2a6, 0x0438607b, 0x15fa571d, 0x186febbf, 0x0d5cf1c9, 0x13e99edc, 0x195fbf33, 0x1871ac7f, 0x6cb9d9}}}, + /* 13*16^3*G: */ + {{{0x0c28caca, 0x0cb2a5c8, 0x00a0769d, 0x138f7799, 0x08c9a186, 0x1f3ac19c, 0x07205785, 0x054b7abc, 0xd8dc1b}}, + {{0x03b3ec7a, 0x0db3b751, 0x004a3db3, 0x02ba59d9, 0x07d947d3, 0x06d21012, 0x1f5631b6, 0x1b24f9d8, 0x8cec0a}}}, + /* 15*16^3*G: */ + {{{0x1bc4416f, 0x090fdb31, 0x02c100a2, 0x1dfa47e6, 0x0f31da7a, 0x12d46819, 0x1b335650, 0x1258bf09, 0x2749e2}}, + {{0x1c6bbd8e, 0x14c5ef17, 0x1a58415f, 0x1d3f6cbd, 0x0db3ef59, 0x0a4c87e5, 0x0f1100f6, 0x09c6ece5, 0x50cc2d}}} + }, + { + /* 1*16^4*G: */ + {{{0x03ff4640, 0x135d6c7c, 0x154bff94, 0x0838fcaa, 0x02ee0534, 0x1602db13, 0x1272673a, 0x1a88f601, 0x363d90}}, + {{0x1bee9de9, 0x1001e3f9, 0x0667b2d8, 0x13512010, 0x1363145b, 0x0229cbf9, 0x088654ed, 0x15bf8e64, 0x4e273}}}, + /* 3*16^4*G: */ + {{{0x16e55dc8, 0x1c4890b7, 0x12810e52, 0x12b56dd5, 0x094426ff, 0x12206028, 0x1ecaea12, 0x08f218bf, 0x443140}}, + {{0x1be323b3, 0x0eca2576, 0x0a8b940c, 0x14536f3d, 0x0fed7a66, 0x01bfab21, 0x1be3fa66, 0x085cca6c, 0x96b0c1}}}, + /* 5*16^4*G: */ + {{{0x101b23a8, 0x1f4a42eb, 0x01fb82b7, 0x16fa8e15, 0x1089dab7, 0x01eadc90, 0x01f04989, 0x11b0cd95, 0x9e22fe}}, + {{0x0884edae, 0x1d209e28, 0x14473b3d, 0x0f9293f6, 0x01533c0f, 0x1f8104ce, 0x14405dfc, 0x1d394245, 0xfd2ff0}}}, + /* 7*16^4*G: */ + {{{0x071a70e4, 0x0ba045f8, 0x173d1d77, 0x1dca3ebe, 0x1306dcd5, 0x14f32382, 0x0a34bb75, 0x1aa079c5, 0x508df6}}, + {{0x09950984, 0x1972dfb9, 0x02ab7fb7, 0x006451dd, 0x049c54ec, 0x0255399f, 0x10b5ddcc, 0x13726778, 0x154c43}}}, + /* 9*16^4*G: */ + {{{0x0e1abe11, 0x157ed3b6, 0x12c883db, 0x124384b3, 0x125b2dab, 0x1ac0c980, 0x1d8cce37, 0x108aa212, 0xe3dbff}}, + {{0x1fa8de63, 0x1a4d6aa4, 0x1ad71052, 0x12fb2078, 0x08ef3d3c, 0x1d3aedc5, 0x108590e3, 0x01334682, 0x6f2f9}}}, + /* 11*16^4*G: */ + {{{0x03593449, 0x078d91b0, 0x0a91bff7, 0x16f825c8, 0x1014af61, 0x0e09d03e, 0x10361e36, 0x0c98fbd2, 0x19ace0}}, + {{0x0df83631, 0x120a5c9d, 0x0101a28e, 0x02173e81, 0x02c46ac7, 0x0b9ceca0, 0x0cceffaf, 0x006a4d14, 0xe37992}}}, + /* 13*16^4*G: */ + {{{0x0cba6b63, 0x11f4c496, 0x02ceed7b, 0x1fcce9fa, 0x08e310a0, 0x19914754, 0x16a84230, 0x1d841f0f, 0xd8740c}}, + {{0x0934c5f3, 0x1751b603, 0x005a52af, 0x009eb987, 0x0c37d401, 0x1774c81d, 0x0afcde29, 0x0678d726, 0x6472c1}}}, + /* 15*16^4*G: */ + {{{0x1b3ec038, 0x0ca7f960, 0x031ac9d8, 0x1aa2d5cc, 0x10a50d9f, 0x1f3b1794, 0x020d9220, 0x07236a0c, 0x58ac33}}, + {{0x10246279, 0x17551f88, 0x13eef285, 0x12087816, 0x1fe97021, 0x0924f4c8, 0x0b65e1de, 0x00daab92, 0x9163d7}}} + }, + { + /* 1*16^5*G: */ + {{{0x1ffdf80c, 0x0fbcd2ae, 0x16f346da, 0x094f0342, 0x1638843e, 0x025adba2, 0x0afa3189, 0x02cbbe78, 0x8b4b5f}}, + {{0x1fd4fd36, 0x1f7f8632, 0x18bb95ac, 0x066ca8c2, 0x0da04f9e, 0x0bc09d58, 0x02d2cfef, 0x0ded1a61, 0x4aad0a}}}, + /* 3*16^5*G: */ + {{{0x155812dd, 0x05152c17, 0x0b4c38a8, 0x08ce46aa, 0x0f78e3d4, 0x1f6b602c, 0x14bc2daa, 0x0f525fe6, 0x7029bd}}, + {{0x1a2d2927, 0x10e63358, 0x0cb1cf1c, 0x15d08487, 0x083ac47d, 0x0a257183, 0x0f49f759, 0x1b5fbd16, 0xb0eefa}}}, + /* 5*16^5*G: */ + {{{0x1d486ed1, 0x0e72b41d, 0x1596da92, 0x0b7d7492, 0x17560574, 0x0084ec67, 0x12640275, 0x195d5ccb, 0x9ccfed}}, + {{0x15e95d8d, 0x1b6acf6b, 0x164aa893, 0x02ceb2d2, 0x13411242, 0x12409005, 0x0b3ed848, 0x0e27ad46, 0x7c2f4d}}}, + /* 7*16^5*G: */ + {{{0x1bd0eaca, 0x10358d3a, 0x0b52ade8, 0x0e8aed74, 0x0df19d0c, 0x1ef19e52, 0x050cd6a3, 0x10ec6828, 0xcd9a4b}}, + {{0x0bff4acc, 0x137d7dad, 0x1bd8d3db, 0x0f671dda, 0x0a08b012, 0x0457499f, 0x08fa0552, 0x0f343d1e, 0xf04558}}}, + /* 9*16^5*G: */ + {{{0x07bc57c6, 0x104a9d1e, 0x0c9db2fc, 0x07af447d, 0x03094490, 0x169749eb, 0x0faa213c, 0x05f11db3, 0xad0988}}, + {{0x0e4a0ab8, 0x1196076d, 0x0438b4f2, 0x023a6e6b, 0x0be5f7b3, 0x036394ed, 0x14ae8a06, 0x11885f74, 0x7243c0}}}, + /* 11*16^5*G: */ + {{{0x0ba56302, 0x14186395, 0x03c618ba, 0x0d526f5e, 0x0d2e0f50, 0x116954fd, 0x107c7ab6, 0x015d6794, 0xd9d129}}, + {{0x08291c29, 0x156ed7d4, 0x09d3caba, 0x135d9b3f, 0x186e4173, 0x0ae33931, 0x1bb40a5c, 0x027d85a7, 0x7eb531}}}, + /* 13*16^5*G: */ + {{{0x14d1243a, 0x1669b827, 0x15297f6e, 0x024c047e, 0x1558402b, 0x10d6031f, 0x13be4734, 0x1bca73ad, 0xbc5079}}, + {{0x055db68a, 0x0e4a8b44, 0x1d1c5a7d, 0x1dc9eb63, 0x1c8d75f7, 0x195ca6ff, 0x12ee3bb1, 0x07674e0b, 0x65062a}}}, + /* 15*16^5*G: */ + {{{0x174a3f9f, 0x06922735, 0x02605a42, 0x0f4ccbb8, 0x0625ac28, 0x15573ed9, 0x1fa244f7, 0x0fca0bf8, 0x4d31a7}}, + {{0x101e0ba7, 0x1e581209, 0x18029d53, 0x0df6a36d, 0x02753cbf, 0x079c63c0, 0x1d6c1ac6, 0x192c130a, 0x22241e}}} + }, + { + /* 1*16^6*G: */ + {{{0x1232fcda, 0x1b08ac92, 0x1039def2, 0x01b7ff4d, 0x148c7b70, 0x18e005ea, 0x05b5afdd, 0x14dcbb73, 0x723cba}}, + {{0x1eb39f5f, 0x0ee034ec, 0x1e525200, 0x0140ca6e, 0x04d6e266, 0x09ba4441, 0x1262a484, 0x16ab2b98, 0x96e867}}}, + /* 3*16^6*G: */ + {{{0x00633cb1, 0x0b3f04f4, 0x140844c9, 0x144496d3, 0x01fcb575, 0x1399090c, 0x0b500318, 0x1e62f559, 0x6dde9c}}, + {{0x07ce6b34, 0x1eea4d53, 0x0167bcd5, 0x04ffb59f, 0x066a880b, 0x17c350dd, 0x10757267, 0x1cf4e0fc, 0x9188fb}}}, + /* 5*16^6*G: */ + {{{0x0933f3c5, 0x0cd28c69, 0x1c494890, 0x141ee22b, 0x1b850085, 0x1dbfc723, 0x17a04f12, 0x059ab6b9, 0x486fa7}}, + {{0x0afb0f53, 0x16a538d6, 0x03c8ede6, 0x136f079e, 0x0f19f62d, 0x045d7664, 0x150f9231, 0x033ead7b, 0x62e123}}}, + /* 7*16^6*G: */ + {{{0x1e99f728, 0x1eaca112, 0x1c48813a, 0x06ebf7cd, 0x05303677, 0x1f93dbb5, 0x1d3ed993, 0x0e951295, 0x247969}}, + {{0x0baaebff, 0x1d0028b7, 0x1d68b60d, 0x17e7812a, 0x1664a5ad, 0x143f3ec6, 0x0007b14b, 0x088d11e6, 0xe3d78d}}}, + /* 9*16^6*G: */ + {{{0x0fb0079a, 0x0f0636a1, 0x04981272, 0x1f3dee47, 0x18324916, 0x0cf73b59, 0x1fc18c69, 0x1b547aab, 0x2f39cb}}, + {{0x0c5690ba, 0x1114b981, 0x0a808c3f, 0x167f7910, 0x1a58b9bf, 0x1b6813c6, 0x060d36a4, 0x1bc270c7, 0xabeadb}}}, + /* 11*16^6*G: */ + {{{0x04f7ab73, 0x09980597, 0x1110e9de, 0x07a9d53a, 0x0aed262e, 0x0f43629a, 0x06e95a8e, 0x0d864fac, 0xe5a31d}}, + {{0x10561f42, 0x089d1fe3, 0x032e884e, 0x18889350, 0x0ce5dbf8, 0x054bbd27, 0x15e83046, 0x07b1a3d3, 0x37788c}}}, + /* 13*16^6*G: */ + {{{0x014dcd86, 0x07c94d1e, 0x0fdc6d62, 0x0ba8412d, 0x1dcf11fc, 0x0ecc1028, 0x111f7d43, 0x0941a2a6, 0xcc389d}}, + {{0x08f0a873, 0x075b6ece, 0x1f9e1d1a, 0x0afc31fc, 0x110ca05c, 0x05eddf54, 0x0fb66d5a, 0x1acc1ed7, 0x93ae4f}}}, + /* 15*16^6*G: */ + {{{0x18819311, 0x07ce375b, 0x01dc51c9, 0x1c3dc421, 0x1ed1f0b3, 0x10bf067a, 0x0408dd42, 0x1913ae3d, 0x7f9291}}, + {{0x0c2eb125, 0x14fcdabd, 0x01a85d2a, 0x15548139, 0x015b6120, 0x0f462292, 0x1bc3d743, 0x020c7d87, 0x9da00d}}} + }, + { + /* 1*16^7*G: */ + {{{0x0e7dd7fa, 0x1299f650, 0x0a4660e6, 0x0f2c246f, 0x0d3b5094, 0x17640961, 0x1e62e97f, 0x1a9277d7, 0xeebfa4}}, + {{0x01de8999, 0x0fea7ed7, 0x047dc4b7, 0x099b874e, 0x0089d9ae, 0x1f6d78bc, 0x03c9a7b9, 0x1472e1de, 0x5d9a8c}}}, + /* 3*16^7*G: */ + {{{0x1b7ceceb, 0x1fb3c7fd, 0x05febc3c, 0x0b3f2711, 0x0681473a, 0x1c0937b7, 0x1140dbfe, 0x04084eda, 0x437a86}}, + {{0x16c181e1, 0x1b1de61a, 0x03e5e09c, 0x041f9fb9, 0x097ff872, 0x1f5b4ce9, 0x0cbda6e3, 0x1427dd58, 0xb916b}}}, + /* 5*16^7*G: */ + {{{0x097f96f2, 0x0c6b94f0, 0x121cd735, 0x046a53a5, 0x0c273358, 0x1f1dcd1e, 0x06f20f2d, 0x027c5491, 0xa9ef9f}}, + {{0x16c04be4, 0x01eaad82, 0x06d1c0b0, 0x1f135eb5, 0x1613db74, 0x170b075d, 0x15f3655b, 0x1cb28ab3, 0xe814cc}}}, + /* 7*16^7*G: */ + {{{0x150cf77e, 0x0055ad09, 0x0a2ac36f, 0x17eae8a9, 0x0064207d, 0x13eb4fd5, 0x16f954e0, 0x083dc3a6, 0x66d805}}, + {{0x00eaa3a6, 0x12fcbd7d, 0x0c6ddb4a, 0x0968756f, 0x013f6944, 0x0a1029ab, 0x0d0b0fc7, 0x1ce65fff, 0x51cfdf}}}, + /* 9*16^7*G: */ + {{{0x07213a5a, 0x1aa43144, 0x17e98ae4, 0x064094f0, 0x0c3c80b7, 0x0450e326, 0x1e8afcd4, 0x1c26ca07, 0x62ac05}}, + {{0x146a9e45, 0x08690e07, 0x1e653bf0, 0x12120302, 0x12579e55, 0x13cf03d4, 0x1b934e57, 0x1e741a34, 0x236fbd}}}, + /* 11*16^7*G: */ + {{{0x07bad12b, 0x187ea4c3, 0x1d9b87b0, 0x0bd401d5, 0x12db385d, 0x09d7ae37, 0x1d6d6fd8, 0x092e4891, 0xca13c4}}, + {{0x1723b0f2, 0x0b4cfa34, 0x095d59a2, 0x0156e223, 0x1a3d6637, 0x1d327556, 0x1f22b057, 0x106c3850, 0x83aa09}}}, + /* 13*16^7*G: */ + {{{0x1c80414e, 0x0d772fca, 0x19b3dff7, 0x1f150c5a, 0x09e4914e, 0x0e27a230, 0x0ba16b04, 0x0034e0a5, 0x1cecb1}}, + {{0x12169a3b, 0x0574a496, 0x1c8c7437, 0x0aafca88, 0x1ad16907, 0x11941af6, 0x13ed396a, 0x0cd2c12f, 0xf34360}}}, + /* 15*16^7*G: */ + {{{0x0ed810a9, 0x07ade462, 0x1c005571, 0x113a7e78, 0x123924f8, 0x0d7af8ef, 0x1e732543, 0x0ea09af1, 0x2a6990}}, + {{0x1a9b4728, 0x1c359c47, 0x105171f3, 0x1a8fd2fd, 0x104fc667, 0x0c34aa7d, 0x1f6b0fb1, 0x136d87bf, 0x54f903}}} + }, + { + /* 1*16^8*G: */ + {{{0x19a48db0, 0x1ebc1ad9, 0x0f00effb, 0x042b4536, 0x1de459f1, 0x08504dbd, 0x059c9e47, 0x1b4d2dce, 0x100f44}}, + {{0x0bc65a09, 0x1deae6b1, 0x14656b03, 0x1e9431fe, 0x10666b7f, 0x19980604, 0x0ddcbb23, 0x06325401, 0xcdd9e1}}}, + /* 3*16^8*G: */ + {{{0x15bc15b4, 0x05cd09a4, 0x168bb9a7, 0x0a051c8c, 0x1ca8d927, 0x0774e76b, 0x1727b616, 0x05ca3dd5, 0x10e90e}}, + {{0x18aa258d, 0x075f304a, 0x0edaa20d, 0x0b12c605, 0x11f754ca, 0x14630b56, 0x0109355e, 0x00701abc, 0xc68a37}}}, + /* 5*16^8*G: */ + {{{0x1fe75269, 0x0e9fe181, 0x0f4cc60b, 0x0f47980a, 0x17dcda37, 0x1c85b8a5, 0x18e115d6, 0x085b4a82, 0xf7422f}}, + {{0x17e49bd5, 0x04c07438, 0x08e63806, 0x07446fe9, 0x035977fb, 0x13ee5cfb, 0x04ff4633, 0x03466261, 0x406c2f}}}, + /* 7*16^8*G: */ + {{{0x15a7175f, 0x09db34b7, 0x073d0a99, 0x11cee3a6, 0x1debbedb, 0x0d2ac16a, 0x13fdca1e, 0x0082fa87, 0x2d8cad}}, + {{0x1b9d592a, 0x19bddc8d, 0x0d797833, 0x08d7fb39, 0x09d377a8, 0x197d3096, 0x0529eec8, 0x10663195, 0xc73f3b}}}, + /* 9*16^8*G: */ + {{{0x14b51045, 0x1a64de1c, 0x07096cf8, 0x0d912de6, 0x0cf73bbc, 0x1ea758f4, 0x1a962b9c, 0x03b7314d, 0x1ecbfd}}, + {{0x02c70026, 0x1d338808, 0x0d908b54, 0x000c8d68, 0x09b38b19, 0x05d8c24d, 0x0e9911f4, 0x06117338, 0x1cf6e2}}}, + /* 11*16^8*G: */ + {{{0x09358533, 0x1d66bb37, 0x1ed2e77d, 0x1267f3a9, 0x12a8c10a, 0x0ad148e9, 0x14a20fa5, 0x18bfcaee, 0x9a0894}}, + {{0x0360ba08, 0x19f0e2ee, 0x00376b7e, 0x0dcb7b77, 0x1c32165a, 0x1eafcaa7, 0x1f0c7e45, 0x18840371, 0xa79883}}}, + /* 13*16^8*G: */ + {{{0x198ef7f6, 0x0a202eb0, 0x01e3e7da, 0x07e7eef4, 0x0aea6592, 0x042939dc, 0x0b8d6f67, 0x093b69fa, 0x664dd8}}, + {{0x1d1eac94, 0x1a4b7f9a, 0x039bb3b9, 0x1a756637, 0x058cffc3, 0x0672ee82, 0x04ca8512, 0x02e2fe4f, 0xad5120}}}, + /* 15*16^8*G: */ + {{{0x03c934b3, 0x060535be, 0x02b8b138, 0x03eb00b6, 0x1a7022b3, 0x0cb34c08, 0x018e08c7, 0x126efa17, 0x82113a}}, + {{0x042c6a0f, 0x1f2f3156, 0x111a00dd, 0x1ef84d34, 0x0c628aa1, 0x16795ad0, 0x19982119, 0x1b5935c6, 0x8da1b8}}} + }, + { + /* 1*16^9*G: */ + {{{0x0534fd2d, 0x04566f37, 0x1cece14b, 0x1f1a88c9, 0x0c017a77, 0x113d2502, 0x146c7724, 0x1c4c58fd, 0xe1031b}}, + {{0x1456a00d, 0x0278c794, 0x073b5e69, 0x05ba833c, 0x1535af29, 0x120bb2cb, 0x0179aeda, 0x12512808, 0x9d7061}}}, + /* 3*16^9*G: */ + {{{0x0f028d83, 0x1cb11d77, 0x1d0e5855, 0x0b24db74, 0x069db619, 0x1f2d0aef, 0x17b1a96a, 0x189c78f0, 0xa7ebf7}}, + {{0x19d0bed1, 0x1201c95c, 0x014e4665, 0x07124e96, 0x0804b47a, 0x039bb822, 0x0b573f67, 0x05f7fc6c, 0x620515}}}, + /* 5*16^9*G: */ + {{{0x07dd5cfa, 0x17072011, 0x02752d6e, 0x138a26fe, 0x146336a8, 0x1529a131, 0x1d307371, 0x11b96049, 0x5b5ca0}}, + {{0x1e48e98c, 0x132537cc, 0x0c9a74f9, 0x00cf995e, 0x09094bfd, 0x18675443, 0x0097a647, 0x1ee1542b, 0x3eccb6}}}, + /* 7*16^9*G: */ + {{{0x03531f82, 0x1ca94719, 0x030b27ca, 0x0264d762, 0x02c29ff5, 0x1f3a44e1, 0x0ed733bc, 0x15982229, 0x46f26}}, + {{0x0bceda07, 0x082fe458, 0x0d571fe9, 0x0b28b9b5, 0x12579d02, 0x185617e1, 0x0bab388d, 0x062c6b70, 0x6b804b}}}, + /* 9*16^9*G: */ + {{{0x10432711, 0x058aa1b8, 0x045ae418, 0x08165fbb, 0x1645acf1, 0x1787844c, 0x1ed4d7e5, 0x1b263c1d, 0xc11926}}, + {{0x0fe2610c, 0x04930f17, 0x1cd01579, 0x17245941, 0x0c6cf83a, 0x05f8c366, 0x1a246125, 0x198fa4b6, 0x8be1f8}}}, + /* 11*16^9*G: */ + {{{0x01257963, 0x0bcc3a7d, 0x12aa1870, 0x031aa586, 0x179c45b0, 0x1aa5a9c4, 0x1cb9b36e, 0x1d3d6d11, 0x690846}}, + {{0x066f9835, 0x12bb2cca, 0x124ac94e, 0x18795043, 0x1bb7f6cb, 0x18127c97, 0x0f16d005, 0x196fe7fd, 0xe2485f}}}, + /* 13*16^9*G: */ + {{{0x1ee23ace, 0x0889ca5e, 0x1374bf4b, 0x10f8cb6a, 0x12915dec, 0x0a96fa51, 0x02eaee9d, 0x1f719244, 0xfb3df7}}, + {{0x1722e8de, 0x04ef7a64, 0x149fd7b8, 0x04e285fd, 0x0b58a9e7, 0x08aa06dd, 0x11594a89, 0x178238e7, 0x510e29}}}, + /* 15*16^9*G: */ + {{{0x10272351, 0x103e7f7a, 0x07fc4261, 0x06967bf3, 0x1cd41707, 0x11f59d58, 0x062cfae2, 0x1849eb8a, 0x6dd85e}}, + {{0x1fc7664b, 0x183fd15b, 0x18205f26, 0x18dfef87, 0x041b7877, 0x03fcd7ae, 0x09f82750, 0x1e8243e8, 0x16ea67}}} + }, + { + /* 1*16^10*G: */ + {{{0x1094696d, 0x0af3446c, 0x075abd4b, 0x1164cd48, 0x1d7ec5cf, 0x01cf8a1d, 0x0d4c2b0a, 0x15c8daab, 0xfeea6c}}, + {{0x18090088, 0x0aaef5f8, 0x10510b4c, 0x1912af98, 0x0cd5c981, 0x07095f9f, 0x06eac1b9, 0x0d92fb9c, 0xe57c6b}}}, + /* 3*16^10*G: */ + {{{0x08dfd587, 0x1c9b0dda, 0x0c099581, 0x09747193, 0x1a12d5ec, 0x1d55167a, 0x022cd219, 0x03759e8a, 0x5084b4}}, + {{0x11470e89, 0x13cf4bfc, 0x047d581b, 0x0deac0d1, 0x127475db, 0x13642a94, 0x14c5866a, 0x0343b301, 0x34a963}}}, + /* 5*16^10*G: */ + {{{0x1ab34cc6, 0x0411930b, 0x1cc284b4, 0x1852ecf9, 0x17128c80, 0x1f8fe8c6, 0x17a94fec, 0x07c0c85a, 0x4f14c0}}, + {{0x187e681f, 0x0f61297c, 0x00774089, 0x1c799d1d, 0x02540b9d, 0x1387a1d3, 0x0253194e, 0x1519549d, 0x7b53d0}}}, + /* 7*16^10*G: */ + {{{0x1241d90d, 0x013b8808, 0x09113e0d, 0x19e283b6, 0x1d363e81, 0x1b04af6e, 0x1b475050, 0x0fc938f3, 0xa74db8}}, + {{0x1f7adad4, 0x1928c5c1, 0x0828c4fc, 0x1ca12689, 0x171c8a9e, 0x08452c40, 0x1bcc9ff7, 0x19b5e47d, 0xf78691}}}, + /* 9*16^10*G: */ + {{{0x11c1ae1f, 0x0f665111, 0x13502ca9, 0x1cb18d15, 0x15658456, 0x06a275d1, 0x0b3e6b37, 0x0ae89754, 0x6901fa}}, + {{0x122838b0, 0x1c19832e, 0x11dea4c3, 0x1e774bcb, 0x0900dd79, 0x09c0614e, 0x0849186d, 0x11044e78, 0x35de5c}}}, + /* 11*16^10*G: */ + {{{0x127a4bdb, 0x1818840d, 0x12532b12, 0x14da086a, 0x1a35d046, 0x1b21f8dd, 0x049e8912, 0x05a3a870, 0x8d3cd8}}, + {{0x069a8a2c, 0x1e9a63e7, 0x02b4a5b0, 0x0700fa6e, 0x0236ed4e, 0x1fce803b, 0x07d1c33e, 0x0c301dc8, 0x9bd425}}}, + /* 13*16^10*G: */ + {{{0x14ec1d2d, 0x00669e35, 0x1f63d151, 0x133eb8dc, 0x1691fd90, 0x0b394b3d, 0x1ce6bfa7, 0x0c7ac0c8, 0xeaf983}}, + {{0x0c838452, 0x07f3ab02, 0x1a12d4ff, 0x0a5aa8af, 0x199aaa9f, 0x14507358, 0x1489dd48, 0x074ffcf1, 0xe51818}}}, + /* 15*16^10*G: */ + {{{0x045ae767, 0x059adfb8, 0x025dc72f, 0x0753973d, 0x0e5d8c27, 0x07029603, 0x18d19bd0, 0x02c75db6, 0xfb95bd}}, + {{0x1bbf0e11, 0x029f5057, 0x167c4d06, 0x0de8e314, 0x0275bb81, 0x06ce9b41, 0x16f14801, 0x1b02351b, 0x664c14}}} + }, + { + /* 1*16^11*G: */ + {{{0x01ec6cb1, 0x1fd4bc5e, 0x0160f78c, 0x1acafb01, 0x1ca3cfee, 0x1f25f37f, 0x1372cd9e, 0x03b22093, 0xda67a9}}, + {{0x1a68be1d, 0x14f54713, 0x1dd0285f, 0x0f5b8a11, 0x180e5dec, 0x11fbf64b, 0x0af107d1, 0x06a902c8, 0x9bacaa}}}, + /* 3*16^11*G: */ + {{{0x15bc8a44, 0x17ee8328, 0x18546867, 0x0202ef97, 0x05fc7684, 0x12d25d2d, 0x168f4e15, 0x0b079f9d, 0x4d0180}}, + {{0x1adbc09e, 0x18fca648, 0x00b68d8b, 0x08408d0b, 0x03813969, 0x1d4003eb, 0x174d9fa6, 0x1831969e, 0x3a33c6}}}, + /* 5*16^11*G: */ + {{{0x05daeb00, 0x00d5a943, 0x1917ddaf, 0x07d6499c, 0x0e9d1592, 0x1b5139db, 0x055c20b2, 0x00fbeb9f, 0x2f6615}}, + {{0x033992c0, 0x113b3c4c, 0x174c2304, 0x1bdc4e32, 0x0add06ec, 0x09bd4100, 0x0cfdbbe5, 0x026dea56, 0xfd5c12}}}, + /* 7*16^11*G: */ + {{{0x116aa6d9, 0x01548504, 0x1c0b73c6, 0x05916c8e, 0x15a38366, 0x0ba6d0c1, 0x1f48953c, 0x0fa0bfc8, 0xf59411}}, + {{0x1f2e50cf, 0x1e834b75, 0x1ad6e1b1, 0x04ef107c, 0x0bceb7a9, 0x0ded584a, 0x0c4b3d97, 0x14add2e3, 0xcaa761}}}, + /* 9*16^11*G: */ + {{{0x1ac6f4c0, 0x15701cab, 0x12f71332, 0x1d1bd198, 0x0711dda4, 0x01c5f34b, 0x137b1387, 0x0fe3e9b3, 0xf2d4d}}, + {{0x12339b58, 0x1f3ddd6b, 0x090995b9, 0x14214f20, 0x1e71e8f6, 0x13fef9c0, 0x19345fec, 0x10afd27b, 0x3ec89f}}}, + /* 11*16^11*G: */ + {{{0x1f428cb2, 0x02a829fe, 0x088ad576, 0x1045facd, 0x0d0f1233, 0x10c0acac, 0x0ef47ade, 0x185c5429, 0x1d5dce}}, + {{0x095b189b, 0x12f68804, 0x18112947, 0x1d225f0e, 0x10b88bd3, 0x03c12437, 0x0314341e, 0x10762859, 0x6e5c40}}}, + /* 13*16^11*G: */ + {{{0x08381273, 0x1875447d, 0x04f6b97e, 0x06ed2c0d, 0x126ced84, 0x14d96620, 0x1d0e7f7a, 0x1fa6012c, 0x89d9a2}}, + {{0x1309773d, 0x01f59f76, 0x049cc928, 0x03ad015a, 0x162f58f6, 0x17903b67, 0x1d40ddea, 0x168a3acd, 0xdfca25}}}, + /* 15*16^11*G: */ + {{{0x15c71d91, 0x06195965, 0x0253f487, 0x08bc5c55, 0x0fece239, 0x0e0988ad, 0x0b8714a8, 0x10f074a1, 0x83191b}}, + {{0x02148a61, 0x05cf6047, 0x01117b9c, 0x023ea2ea, 0x0ae8a17a, 0x0f09e8be, 0x0dc2781b, 0x02ae7003, 0xe0ac7f}}} + }, + { + /* 1*16^12*G: */ + {{{0x1a37b7c0, 0x1aa2e660, 0x0441a7d5, 0x11a1ef76, 0x02151ec0, 0x0049af79, 0x13769b80, 0x15416669, 0x53904f}}, + {{0x022771c8, 0x0e584b58, 0x110d1a67, 0x133303c2, 0x13c1c139, 0x16656106, 0x01b62327, 0x1a179002, 0x5bc087}}}, + /* 3*16^12*G: */ + {{{0x08a2050e, 0x0d6217f2, 0x17e299dc, 0x1deaaec2, 0x19b89742, 0x14e63723, 0x0c625add, 0x1fa4978e, 0x673724}}, + {{0x061d3d70, 0x0864d248, 0x0d2730ae, 0x1759fa86, 0x06b6dbe6, 0x01604d44, 0x088080d2, 0x0af12d49, 0xe4cf82}}}, + /* 5*16^12*G: */ + {{{0x02de63bf, 0x1fb7241c, 0x098719b2, 0x15ea650e, 0x166a8e03, 0x05318fb0, 0x10c27966, 0x148e5be9, 0x4366ef}}, + {{0x017924cd, 0x16352047, 0x0a9b5ac0, 0x1618a4b5, 0x068eaf33, 0x09bf0981, 0x1f38bb89, 0x0137dc5a, 0x2e7dd9}}}, + /* 7*16^12*G: */ + {{{0x06f96190, 0x08293ff8, 0x125497bc, 0x005bd20f, 0x0a75fd1f, 0x0ab4b33d, 0x0c7e5ef9, 0x0c4f3235, 0x7bd753}}, + {{0x0bda00f6, 0x1e8b9025, 0x03a9a568, 0x1f98b83c, 0x027d6ce0, 0x123a4a1c, 0x0c2757b5, 0x167b774c, 0x8336f2}}}, + /* 9*16^12*G: */ + {{{0x16ad41ed, 0x1d8a98aa, 0x1c0d490c, 0x11586be9, 0x0dc92030, 0x00cf448c, 0x1706be8d, 0x0f7bb55a, 0x4f7e92}}, + {{0x1e642d57, 0x1a1abc33, 0x0bddd6f7, 0x0628e29d, 0x0f6a62e7, 0x006fa9d4, 0x0c4154a6, 0x0a2ad511, 0xdfe774}}}, + /* 11*16^12*G: */ + {{{0x07748690, 0x1d302893, 0x18c2c073, 0x0f209bb0, 0x13d007d5, 0x0958f6e9, 0x133252d1, 0x10cfa523, 0x2355cb}}, + {{0x0c89582b, 0x1e23a98a, 0x0724e451, 0x10d0b19a, 0x07c58582, 0x02f60ad5, 0x07e3d56e, 0x114b4e3c, 0x21c2f1}}}, + /* 13*16^12*G: */ + {{{0x0ade7f16, 0x0d7510b0, 0x0f80a31b, 0x1975d279, 0x15d24ae9, 0x0955b613, 0x15b004d6, 0x0b7367b4, 0xb6682}}, + {{0x08c56217, 0x0a221342, 0x19f34af6, 0x08781be7, 0x1a97fb72, 0x1b5d45ab, 0x0cffbce9, 0x17031c13, 0xa1fba0}}}, + /* 15*16^12*G: */ + {{{0x19060d5b, 0x08358114, 0x0e8e9f0a, 0x0c276238, 0x151cb904, 0x0d97ecfc, 0x08b2d842, 0x079e17a0, 0xf60204}}, + {{0x1af88f13, 0x17b7a858, 0x17468fdd, 0x14ada56f, 0x17bea37a, 0x07b25627, 0x1c66206a, 0x00d83e19, 0xf036b7}}} + }, + { + /* 1*16^13*G: */ + {{{0x1ad86047, 0x1fcacfa1, 0x06e2f2bb, 0x0a740875, 0x0906779b, 0x053bb265, 0x0e9dc673, 0x017a6b30, 0x8e7bcd}}, + {{0x0460372a, 0x108023f4, 0x1f5a2cfa, 0x111c5c8f, 0x1514579e, 0x08210654, 0x12ce500c, 0x016547b4, 0x10b777}}}, + /* 3*16^13*G: */ + {{{0x041ead4b, 0x1f443cd0, 0x06c0f07f, 0x0bdbf6d2, 0x076be3a7, 0x19a77d7f, 0x0dfb1c51, 0x019191e6, 0xbfc90c}}, + {{0x06fedaed, 0x02963784, 0x182b8f9d, 0x0d1dfe65, 0x02d36fb4, 0x18c6ea82, 0x1b4936e9, 0x163c139b, 0x7a9481}}}, + /* 5*16^13*G: */ + {{{0x15bb3b3e, 0x173de83b, 0x07dcf2c9, 0x0a6c2fdf, 0x13f5b507, 0x0c9f4616, 0x0a937296, 0x0397c7f5, 0x732df1}}, + {{0x17366693, 0x02bbf0f6, 0x11610db3, 0x1b5adac9, 0x13916e69, 0x12ac2012, 0x05df2df8, 0x07dbd1f3, 0x7f4190}}}, + /* 7*16^13*G: */ + {{{0x088dc3b9, 0x0fad9e0c, 0x1dd32671, 0x0cd249d2, 0x1ef6019a, 0x0420664b, 0x1ed0a36a, 0x172c0728, 0x4ce094}}, + {{0x05c0de52, 0x00a7bdb7, 0x1a81940b, 0x00b64193, 0x0aca2162, 0x06c0653b, 0x0cfb55ed, 0x1757e353, 0x5390f}}}, + /* 9*16^13*G: */ + {{{0x0aaafe5a, 0x01baf058, 0x026bb753, 0x08e1674a, 0x03fd0e25, 0x1354ad81, 0x1f7a5b62, 0x16edf8cc, 0x9a968e}}, + {{0x0ed975c0, 0x135fea12, 0x19c33033, 0x0b1014cb, 0x01d0ee7b, 0x13681ec3, 0x08a553f1, 0x00f4da77, 0xabf6fb}}}, + /* 11*16^13*G: */ + {{{0x134c6397, 0x1a8e5fcf, 0x1d33424d, 0x1a7b6a00, 0x0b22107d, 0x17c0129b, 0x06553c2b, 0x1da02e2c, 0xd3c6fb}}, + {{0x08cb3f9c, 0x0fe4682b, 0x1783802c, 0x18ee1120, 0x0b6468a0, 0x1ca6c975, 0x0bc65160, 0x18abcbc5, 0x4a0dd2}}}, + /* 13*16^13*G: */ + {{{0x0ac3137e, 0x0dc12c98, 0x0bd91186, 0x04b6c54b, 0x13953fe9, 0x0353b555, 0x0ee380bc, 0x13025248, 0x4cbde3}}, + {{0x0ec02fe6, 0x00478ae7, 0x03ab5830, 0x074f5cb1, 0x0c370d19, 0x1509a5ca, 0x05654024, 0x0c11e26c, 0x6ce554}}}, + /* 15*16^13*G: */ + {{{0x00fbf84b, 0x1d118367, 0x0fc2e291, 0x091f9778, 0x0cebb03c, 0x0e39860a, 0x091ee2f6, 0x0790562c, 0x4b9d33}}, + {{0x036c3c48, 0x1cf725a5, 0x1d6d7988, 0x0daa87c5, 0x1b7eb676, 0x1ecc133c, 0x054954c4, 0x1f7c4998, 0xfd7fc7}}} + }, + { + /* 1*16^14*G: */ + {{{0x19c43862, 0x1420f0ac, 0x05f99a42, 0x0fe9e307, 0x01bde71a, 0x00c344dc, 0x1c879b42, 0x069839bf, 0x385eed}}, + {{0x142e5453, 0x022c7f2a, 0x01b72330, 0x009dd841, 0x1f4576b3, 0x0f0cf4f5, 0x0fd59c07, 0x187d1d44, 0x283beb}}}, + /* 3*16^14*G: */ + {{{0x16e2d9b3, 0x05e98355, 0x0e358d45, 0x17250636, 0x1845641d, 0x15309ce7, 0x179d784b, 0x1e72f8e0, 0x19a314}}, + {{0x0baaaf33, 0x0a97712e, 0x012f95b5, 0x043a3a48, 0x128b3a50, 0x12fc43fa, 0x03748d25, 0x1ebb58e5, 0x6cacd8}}}, + /* 5*16^14*G: */ + {{{0x12f00480, 0x0c2c3f58, 0x00373b9f, 0x0b100942, 0x07219203, 0x0c31b27b, 0x0a8d5772, 0x0972b51b, 0x5840ed}}, + {{0x1e22cf9e, 0x0c96af15, 0x0b8e1c85, 0x0a44a8a5, 0x1d8daba7, 0x06f550ae, 0x05041e5a, 0x0d64417e, 0x670cda}}}, + /* 7*16^14*G: */ + {{{0x03f54c42, 0x0426f741, 0x068f7239, 0x1834784d, 0x0532545d, 0x060fa767, 0x03ec7716, 0x14a68d23, 0x9f5701}}, + {{0x0feb6a21, 0x1070e249, 0x067949e1, 0x1cf09564, 0x1bfdd89e, 0x0db5ab94, 0x195efee5, 0x171003b3, 0xce7b8f}}}, + /* 9*16^14*G: */ + {{{0x1522461a, 0x14a09994, 0x11c60c9f, 0x06144cb7, 0x0f9a0bcb, 0x0380833f, 0x13f006ee, 0x0d246b51, 0x27f611}}, + {{0x07301a2d, 0x152657ce, 0x018e511a, 0x103641b5, 0x08ee8e49, 0x02b093a1, 0x0b4ba923, 0x1532014d, 0xe512f1}}}, + /* 11*16^14*G: */ + {{{0x114f36b9, 0x07164d96, 0x0e7ce433, 0x119576c7, 0x0a981259, 0x197a1afa, 0x0504986f, 0x10ad9ddf, 0x640779}}, + {{0x04b4b50a, 0x0a58f74f, 0x073baca2, 0x1410e093, 0x07b6c8cd, 0x0bafec2d, 0x1bebbe43, 0x11e89cad, 0xda6192}}}, + /* 13*16^14*G: */ + {{{0x14d6b76f, 0x17b1efcc, 0x120576f3, 0x0b53f769, 0x1feae7fe, 0x1845e04e, 0x1a7d14bd, 0x1c6390ac, 0xa23750}}, + {{0x0dcca8da, 0x0ec96664, 0x0e123450, 0x00f823fb, 0x11113e77, 0x1d1f26a0, 0x19b6b0cb, 0x0294dcfb, 0xf7339b}}}, + /* 15*16^14*G: */ + {{{0x1ceee475, 0x1774e327, 0x1b5e0ac4, 0x15905081, 0x0ff41ab6, 0x0010f9e5, 0x1e357b4f, 0x00f47e46, 0xbbf1ac}}, + {{0x07fe5067, 0x03115004, 0x02d075a1, 0x173b1287, 0x15fe324e, 0x1e641b58, 0x03984220, 0x1bdd5a8c, 0xb4bfb8}}} + }, + { + /* 1*16^15*G: */ + {{{0x03fac3a7, 0x10376c36, 0x11fef271, 0x1bf094b2, 0x1fa180fd, 0x19d2209e, 0x06458df1, 0x17007d9e, 0x6f9d9}}, + {{0x1a842160, 0x03448301, 0x0a0400b6, 0x09ba5eb8, 0x1c4d47ea, 0x11518722, 0x06e9a6e3, 0x11cc060b, 0x7c80c6}}}, + /* 3*16^15*G: */ + {{{0x121ce204, 0x076baf46, 0x19d8f549, 0x0e4b1c84, 0x0f72fb2a, 0x06c2ce53, 0x193ee0dd, 0x1a2c5678, 0x43ca41}}, + {{0x134a8f6b, 0x09282274, 0x09291a39, 0x0d8f667d, 0x1a31f9ab, 0x07c90c6d, 0x0fe87194, 0x105c6e04, 0xdcea5a}}}, + /* 5*16^15*G: */ + {{{0x0be6efda, 0x07e74967, 0x1ca01659, 0x1a9fe7f0, 0x0506d922, 0x1b91bc2d, 0x0fd6d99b, 0x1de45125, 0x9c3e06}}, + {{0x07aefc7d, 0x18a07995, 0x0dbf7df7, 0x1790d0f6, 0x06fd5d43, 0x196a2671, 0x08f62bc2, 0x1cbcec52, 0xa7b709}}}, + /* 7*16^15*G: */ + {{{0x06c88be2, 0x0893d5ae, 0x1bb9789e, 0x18f041a0, 0x0775bea2, 0x13acec18, 0x1c0ceedc, 0x14627c41, 0x5d6f8a}}, + {{0x0d7ad75d, 0x0972a9f8, 0x0fe4b0a2, 0x16df1d4d, 0x0bc20eda, 0x1799d584, 0x13a31c6a, 0x11b1aada, 0xadc4b1}}}, + /* 9*16^15*G: */ + {{{0x12d1b844, 0x1449a8dc, 0x1cf9213c, 0x18070582, 0x08bc5c69, 0x0ae1e09c, 0x157f21ac, 0x186094c1, 0xf57d35}}, + {{0x01266837, 0x125d5deb, 0x04571a91, 0x0d2e4061, 0x0634c700, 0x09fad4f2, 0x1365e413, 0x13d531de, 0x707f3d}}}, + /* 11*16^15*G: */ + {{{0x1cc7cb09, 0x1a6803f9, 0x146d0d48, 0x0fd6d143, 0x071463bc, 0x10ff71ec, 0x1297d65b, 0x0f474cb2, 0x13e760}}, + {{0x08079160, 0x1f3ad450, 0x0d5d9046, 0x15c576cd, 0x0299d65e, 0x1eec2d9a, 0x02c78c97, 0x11bd1f77, 0x284dc8}}}, + /* 13*16^15*G: */ + {{{0x18cdee05, 0x03067092, 0x0bb0ee40, 0x0c3f642e, 0x0901da87, 0x12858d83, 0x0b989000, 0x044ad030, 0x29bea3}}, + {{0x062651c8, 0x12501acd, 0x11a638e7, 0x18636d91, 0x05ec7f9f, 0x0d9fdc38, 0x083aa402, 0x144d21d3, 0x7c40d9}}}, + /* 15*16^15*G: */ + {{{0x12f18ada, 0x0db63ab0, 0x16f6f304, 0x017b2777, 0x14a59d46, 0x17d7f99e, 0x039f670d, 0x0da47051, 0xf52178}}, + {{0x03953516, 0x0eb457e9, 0x16fc2607, 0x1de946d8, 0x1d1d6aa5, 0x10815e68, 0x0d5fb309, 0x17ec071b, 0xe0686f}}} + }, + { + /* 1*16^16*G: */ + {{{0x02d0e6bd, 0x1dbf073a, 0x03d794c4, 0x09a2c7b6, 0x16ecbf77, 0x0a3e0826, 0x18960a88, 0x00248789, 0x3322d4}}, + {{0x0c28b2a0, 0x079d174b, 0x01cebd89, 0x0bec7d45, 0x0f9b7280, 0x0cde26ed, 0x1bd6fec0, 0x12fd2cc9, 0x56e707}}}, + /* 3*16^16*G: */ + {{{0x059ab499, 0x1ece9f90, 0x1cf0cc2a, 0x065338dc, 0x101bc0b1, 0x0b59e33f, 0x16e97486, 0x1e602b80, 0x78baaf}}, + {{0x1ee097fd, 0x00e918c7, 0x0494665a, 0x065ddd1a, 0x0082e916, 0x027076c1, 0x02bebf2a, 0x1b7b60d8, 0xad4bdc}}}, + /* 5*16^16*G: */ + {{{0x1d06ace6, 0x049f0b67, 0x0e883291, 0x01366df0, 0x1ab1a237, 0x024c2494, 0x0f53082e, 0x0234295c, 0x6f70f2}}, + {{0x1602d5de, 0x045f69a5, 0x16b17b81, 0x052acd7c, 0x19f50753, 0x0c79a3dc, 0x0dcdbe57, 0x0612804f, 0x791e8a}}}, + /* 7*16^16*G: */ + {{{0x00ee1b40, 0x04771f73, 0x1a5891f7, 0x1a90b6e3, 0x1ccd48ce, 0x04f8c881, 0x1057e025, 0x1653ad54, 0xe1599d}}, + {{0x178f93a6, 0x0eb132f6, 0x0ca66778, 0x0c74e978, 0x1c7cfa63, 0x04a55517, 0x1283bebe, 0x0465503a, 0x793362}}}, + /* 9*16^16*G: */ + {{{0x09c00c3e, 0x00efd142, 0x048238be, 0x1d1e0792, 0x11859f00, 0x11699ea2, 0x05590d95, 0x12e0880d, 0xbb0b04}}, + {{0x11955a35, 0x0cd4c168, 0x1772429e, 0x0e089d20, 0x1b052fe6, 0x17d0bd58, 0x1d8d9574, 0x0b0a75f3, 0x4067e4}}}, + /* 11*16^16*G: */ + {{{0x05dd32e6, 0x073adce0, 0x0f97b9f8, 0x076aa36a, 0x05fbfc66, 0x0edf03ad, 0x027a6d92, 0x0aa832af, 0xdc5a41}}, + {{0x154a99b9, 0x073edbd3, 0x1926f3e0, 0x05dd20ed, 0x159442ff, 0x12f100df, 0x1e9db73a, 0x14c7f3ec, 0x4af3a8}}}, + /* 13*16^16*G: */ + {{{0x0544e7cb, 0x16a2dd62, 0x0a580d67, 0x0c844b6a, 0x14e99a10, 0x0a52b880, 0x0e76f8cd, 0x0e0730e7, 0x156e19}}, + {{0x0d250a37, 0x0a9c9605, 0x0d0e735b, 0x0dab1abd, 0x14be8949, 0x0b9531c1, 0x01f6a4e5, 0x13f1e632, 0x6bc08d}}}, + /* 15*16^16*G: */ + {{{0x059853ca, 0x1fde14e6, 0x066fd536, 0x12c4d73e, 0x1161348e, 0x1b511473, 0x0e09af29, 0x19d96d08, 0x4269bc}}, + {{0x15b8d367, 0x14ac77a9, 0x196a28f6, 0x12814bf3, 0x1a409fd3, 0x0654e218, 0x1adc8f21, 0x03505802, 0xed2b1c}}} + }, + { + /* 1*16^17*G: */ + {{{0x0134ab83, 0x10eba694, 0x190ce5dc, 0x167f35ee, 0x05868741, 0x1b86c4b3, 0x1f68af45, 0x0fa5bc16, 0x85672c}}, + {{0x190313a6, 0x07184a7b, 0x0a63d132, 0x1e2ff98a, 0x0c2e5e77, 0x024dfd31, 0x0bad8dd0, 0x136b6876, 0x7c481b}}}, + /* 3*16^17*G: */ + {{{0x03ba9000, 0x0f8391d4, 0x097a2dbf, 0x0e5f38d0, 0x0143dc48, 0x1b03ac20, 0x0305a121, 0x1f3ffe3b, 0xac3874}}, + {{0x0f10cf0a, 0x0d1e3ecb, 0x19ba7e93, 0x1c6a1afe, 0x17f93085, 0x0ef44a08, 0x01a6e18b, 0x04611438, 0xaa65e9}}}, + /* 5*16^17*G: */ + {{{0x0d06dbd4, 0x13037b37, 0x08834206, 0x1c7f0af1, 0x1e729ec0, 0x003af4d1, 0x004e6b45, 0x1cf54d0e, 0x570d5c}}, + {{0x1d1ed495, 0x132df675, 0x1182fb56, 0x0746db8c, 0x01bbbb68, 0x173388e8, 0x0bd816d9, 0x092841c0, 0xa6ae53}}}, + /* 7*16^17*G: */ + {{{0x092d230e, 0x1a60fc90, 0x04ce4a10, 0x1c6541a5, 0x06ef5dae, 0x114f701b, 0x0edbe1f0, 0x0e0504d1, 0x75b5f8}}, + {{0x151570b8, 0x1be5efde, 0x047e3ec0, 0x0f49600a, 0x1fa8e026, 0x03a2aa6e, 0x148d8f5e, 0x043c74f0, 0x527cce}}}, + /* 9*16^17*G: */ + {{{0x034fcc0e, 0x1db5fb56, 0x18e21311, 0x1e214857, 0x059c8927, 0x1c0713e9, 0x1b0ac296, 0x1f5c3dbb, 0x44fc8e}}, + {{0x119c420a, 0x07f818f3, 0x14122767, 0x1d294979, 0x1d3d7720, 0x13f3c419, 0x0d9f9249, 0x12975362, 0xd2c7de}}}, + /* 11*16^17*G: */ + {{{0x0bb69991, 0x0d240fe4, 0x0c1c5d75, 0x167f4586, 0x0f535fff, 0x0b310701, 0x0f1a19a8, 0x08f759cf, 0xdea2ba}}, + {{0x116fe2df, 0x033a154f, 0x07b66f1c, 0x1b9b66c4, 0x1b9e7229, 0x1ff6427b, 0x0392b172, 0x1adb2185, 0xae28bf}}}, + /* 13*16^17*G: */ + {{{0x114fc9cc, 0x01206812, 0x09185963, 0x00157853, 0x0a83626d, 0x07b6625c, 0x035bbf07, 0x1314dc2d, 0x3968fc}}, + {{0x1fad37dd, 0x0c9ca44a, 0x023ccd00, 0x08caedff, 0x132c91a1, 0x0c168d56, 0x04a048de, 0x1a1b696b, 0x789cbb}}}, + /* 15*16^17*G: */ + {{{0x0e0c85f1, 0x17610c8d, 0x0e8e8942, 0x0a5ca6c0, 0x145d5a6a, 0x0ef84abc, 0x08a49ac9, 0x00d31c0d, 0x689691}}, + {{0x0dc1de21, 0x03a41199, 0x1540e48b, 0x01833b41, 0x1d72d1f4, 0x02e0ec22, 0x1e495bcc, 0x16967192, 0xaefd3f}}} + }, + { + /* 1*16^18*G: */ + {{{0x00c82a0a, 0x1ecacd7b, 0x19a20cbf, 0x044d8c1e, 0x013b10f9, 0x04f8c8ca, 0x0291ac1b, 0x10136331, 0x948bf}}, + {{0x18c8e589, 0x065bfc46, 0x1f34afb5, 0x1bfe1192, 0x1418c6d4, 0x1a62e8e1, 0x191b71ad, 0x10adb96c, 0x53a562}}}, + /* 3*16^18*G: */ + {{{0x18c8ac7f, 0x1417f2fd, 0x18aa949c, 0x0485dccb, 0x0f849641, 0x1cb6902b, 0x0ef2d70c, 0x1f7c7045, 0x9945b2}}, + {{0x09aea3b0, 0x16ca1d0b, 0x16b37ea5, 0x1ef447dd, 0x0eff5282, 0x1a27fd94, 0x00b581f6, 0x104961e5, 0x3eefed}}}, + /* 5*16^18*G: */ + {{{0x169e353a, 0x08ebcf1c, 0x0ef87dbb, 0x008810a5, 0x1d5fe10a, 0x01113883, 0x03988d7e, 0x0d640b0e, 0x2a314c}}, + {{0x05746067, 0x12c9370f, 0x09962ff0, 0x14a955b6, 0x01ba0138, 0x1f23b5d5, 0x1eb06918, 0x017e6b44, 0x15a4ac}}}, + /* 7*16^18*G: */ + {{{0x09b84966, 0x0f17a4f7, 0x1fcffe62, 0x1e820dba, 0x16c911b4, 0x17d79d35, 0x10b5262d, 0x0016e07f, 0x5959a5}}, + {{0x07473a6a, 0x0533190c, 0x1f890990, 0x01b98119, 0x02a70910, 0x094106e4, 0x025fe50c, 0x0e83eb95, 0x370e6}}}, + /* 9*16^18*G: */ + {{{0x0bc6b173, 0x1864dc91, 0x1b4fface, 0x10c478fd, 0x15f9d7dd, 0x0623182d, 0x1fa38458, 0x0726e445, 0x9eeb31}}, + {{0x1723a71d, 0x09cb106c, 0x19c312e5, 0x0e357da1, 0x01ae30ea, 0x15fedddd, 0x163654aa, 0x1c0221da, 0xe121f1}}}, + /* 11*16^18*G: */ + {{{0x1ec805f3, 0x1b9221fb, 0x16656455, 0x081eea13, 0x097887de, 0x191c4037, 0x03d45bae, 0x1c98fee3, 0x39cc4f}}, + {{0x1a3c48d3, 0x0c8d0609, 0x1244b934, 0x12ae6e7f, 0x0de7dbbb, 0x1349e7a1, 0x03cc5479, 0x058b48df, 0xecb147}}}, + /* 13*16^18*G: */ + {{{0x0e22580e, 0x1eb17dae, 0x15be21e4, 0x06512da5, 0x1d4d2c71, 0x1fef326d, 0x11a5a24b, 0x0e8cdd9b, 0xf94c80}}, + {{0x1127db82, 0x0f291fb3, 0x0dc753a0, 0x0fd88a64, 0x081f6988, 0x1ffb55ad, 0x096a4652, 0x1b8cf0a4, 0x5e9c7f}}}, + /* 15*16^18*G: */ + {{{0x10c4f21f, 0x1f205d68, 0x00f69e9a, 0x0952b29a, 0x199d2502, 0x10346d8c, 0x058bf300, 0x0d8d5aba, 0x8cccb8}}, + {{0x129dfea0, 0x1f893753, 0x192f8a3d, 0x05b95904, 0x158a54b9, 0x17aa4e43, 0x110da66f, 0x0b0c4ea3, 0x57f896}}} + }, + { + /* 1*16^19*G: */ + {{{0x138fd8e8, 0x0766c0cf, 0x1a5d4ab3, 0x01c89bf8, 0x073a8f1b, 0x1e707814, 0x070d3c19, 0x0fe8c300, 0x6260ce}}, + {{0x12b4ae17, 0x0d4274ad, 0x14706630, 0x05244700, 0x01ef7ecd, 0x0824bbb5, 0x15c69fc2, 0x056df4b6, 0xbc2da8}}}, + /* 3*16^19*G: */ + {{{0x01136602, 0x185d232a, 0x078e96ee, 0x08de901b, 0x13b3d38c, 0x049bf919, 0x1f0cf416, 0x0500905b, 0x87d127}}, + {{0x18af6aac, 0x1b41e20e, 0x14efdf13, 0x1108e8df, 0x1745387a, 0x13b67fb3, 0x0f7a49a8, 0x10e14b40, 0x71ce24}}}, + /* 5*16^19*G: */ + {{{0x08c5a916, 0x1902eb9a, 0x15843c96, 0x18881aa6, 0x14aa13f5, 0x0631ed5a, 0x05d1ac2b, 0x07fc4c3d, 0xfd5d7d}}, + {{0x1adb8bda, 0x0a59bd83, 0x13dbeaac, 0x0e70297b, 0x1b52fe5d, 0x1e533ce3, 0x0c1f4ad0, 0x1a1dd6ab, 0xdd83e}}}, + /* 7*16^19*G: */ + {{{0x15f7529c, 0x0ecc84b1, 0x0f547751, 0x0cb7316b, 0x04381387, 0x09e1969a, 0x1848ae91, 0x02130384, 0xde0dd4}}, + {{0x04cd88fe, 0x1e106017, 0x06ddd018, 0x12498d11, 0x03570178, 0x04c116bd, 0x117e6c84, 0x13a21442, 0xd70a6e}}}, + /* 9*16^19*G: */ + {{{0x18f76d11, 0x0417a469, 0x014555cf, 0x02001f7f, 0x092a2151, 0x1f7e1ce1, 0x1139a716, 0x115b499e, 0xb26c20}}, + {{0x03b7356b, 0x00e0058f, 0x009892f7, 0x0060cb3b, 0x16433050, 0x143ec866, 0x0cf3c313, 0x1041293a, 0x1f1cf8}}}, + /* 11*16^19*G: */ + {{{0x069e22db, 0x0a71d833, 0x07224cfe, 0x127feb3f, 0x0294eac9, 0x1e9480c6, 0x148e9f8e, 0x171f8ffe, 0xfceb14}}, + {{0x1c2260a1, 0x1ab96d92, 0x13af932f, 0x0a1cf274, 0x10924ad5, 0x12d3a92f, 0x0347f362, 0x07481ad7, 0x64aa6b}}}, + /* 13*16^19*G: */ + {{{0x038a2755, 0x18420bcd, 0x1271541b, 0x0434e2ca, 0x07faa537, 0x1abba5a8, 0x12b15705, 0x0c4799ab, 0x4e909a}}, + {{0x03cca3de, 0x1bd24fb1, 0x0a60032b, 0x1aa729a4, 0x159dbb6c, 0x036ea83a, 0x16bb3bc9, 0x10f199c6, 0xae56da}}}, + /* 15*16^19*G: */ + {{{0x183ba64d, 0x012c4acc, 0x116568ea, 0x0c1eef11, 0x040e2bde, 0x10a2f5c7, 0x100efec2, 0x0b845c09, 0xc36745}}, + {{0x0a2181fd, 0x0a6647cb, 0x1de3c518, 0x04d55942, 0x164c7426, 0x0737426c, 0x00cc2038, 0x1a0d396c, 0x3a520a}}} + }, + { + /* 1*16^20*G: */ + {{{0x0037fa2d, 0x0a9e6469, 0x0ff710ca, 0x1d91eaeb, 0x14103043, 0x0420a5df, 0x0350f60d, 0x1c15f83b, 0xe5037d}}, + {{0x1d755bda, 0x072ee420, 0x1207c438, 0x1eb607d8, 0x10bddbd5, 0x0684fdcc, 0x0ed7e7e6, 0x0975529a, 0x457153}}}, + /* 3*16^20*G: */ + {{{0x177e7775, 0x04545370, 0x1b657d8e, 0x02ab2711, 0x091aeb5e, 0x01dd67a9, 0x0f3b9615, 0x075ff2c6, 0x9d896a}}, + {{0x1a056691, 0x1e7b69d5, 0x06494efb, 0x139afdc5, 0x0927de89, 0x1276b928, 0x1c2e53a5, 0x1c87e937, 0xdd91a9}}}, + /* 5*16^20*G: */ + {{{0x1c2a3293, 0x1ef026f1, 0x00d1db17, 0x1170ddd2, 0x0f4cd568, 0x052b9941, 0x1e4b43ac, 0x1dce22c6, 0x8327b8}}, + {{0x0e0df9bd, 0x1e42a70c, 0x0c9a905a, 0x1fb569dc, 0x1708496a, 0x1f53313c, 0x063862ec, 0x04cddc15, 0x4997e}}}, + /* 7*16^20*G: */ + {{{0x0562c042, 0x010d9362, 0x037ec689, 0x1a464697, 0x08ed6092, 0x130ec7cd, 0x05a25f59, 0x15454db6, 0x5ae42a}}, + {{0x0f79269c, 0x082e66fc, 0x1f3636fe, 0x01b72a20, 0x09d4a94e, 0x0eee301c, 0x147aad70, 0x0f80bfe0, 0x99d93a}}}, + /* 9*16^20*G: */ + {{{0x1e85af61, 0x1a440942, 0x12b9d9ac, 0x1dae45ba, 0x01b0f4e8, 0x1b47fb61, 0x03ad66ba, 0x1c84d439, 0x92c23a}}, + {{0x036a2b09, 0x1391b34e, 0x0a1bfb53, 0x075b056c, 0x0d5792d2, 0x0beae39c, 0x0ed027c8, 0x11e02aa3, 0x414cf8}}}, + /* 11*16^20*G: */ + {{{0x07b5eba8, 0x11578d96, 0x063a8db3, 0x17db8ff2, 0x0df422da, 0x1a0bb57c, 0x1c422343, 0x118ed5fb, 0xfee560}}, + {{0x0d0b9b5c, 0x1a8ae9b4, 0x04151e4f, 0x01fe857f, 0x1c14ee38, 0x017cc943, 0x02bec450, 0x12269fcb, 0x380759}}}, + /* 13*16^20*G: */ + {{{0x1c63caf4, 0x0f1dd259, 0x1d4f54a0, 0x1fe75651, 0x06afca28, 0x09da6315, 0x1f988284, 0x1d725ccc, 0x42e544}}, + {{0x169c29c8, 0x03d7604c, 0x1bf17c46, 0x0a1cf6d7, 0x15e7873a, 0x11060ba0, 0x19c7dc7c, 0x1c1f2398, 0x9ff854}}}, + /* 15*16^20*G: */ + {{{0x1e0f09a1, 0x0515ecc2, 0x100ca0e0, 0x0213e372, 0x00efef0a, 0x17695238, 0x138e0e65, 0x16ccaa65, 0x7aed83}}, + {{0x05857d73, 0x02ec66f4, 0x0fd29501, 0x165e601e, 0x12d8ed88, 0x1e855881, 0x1df1f76b, 0x0bf3463d, 0xf5b854}}} + }, + { + /* 1*16^21*G: */ + {{{0x04fce725, 0x0c335057, 0x09b16dc9, 0x11b7a38d, 0x171b4e7e, 0x082f478b, 0x1eb7d7aa, 0x161e9440, 0xe06372}}, + {{0x0eee31dd, 0x1381a7ca, 0x04121c2c, 0x1094ef0e, 0x0488cd74, 0x1dd956ad, 0x13f84a89, 0x0e979c31, 0x7a9089}}}, + /* 3*16^21*G: */ + {{{0x1a328d6a, 0x1d540c46, 0x0b7062f6, 0x1aee752b, 0x08fa7b24, 0x04c8c2d8, 0x18028d1a, 0x0b74c469, 0xc663c0}}, + {{0x1ec9b8c0, 0x1d85db55, 0x0afe7308, 0x0aa3d4a2, 0x11317dd8, 0x1ee793ab, 0x10e34e6b, 0x11abee43, 0x3331e9}}}, + /* 5*16^21*G: */ + {{{0x1996de2f, 0x048e4241, 0x0c94452f, 0x1dbe5dc1, 0x1e4e977c, 0x186f7507, 0x091673ac, 0x105bf70d, 0xd3fc26}}, + {{0x14526f8c, 0x0249120e, 0x1eafc5a3, 0x136931be, 0x181da4e5, 0x03aa6a7b, 0x0863da2d, 0x13348be1, 0xc4f0df}}}, + /* 7*16^21*G: */ + {{{0x03e697ea, 0x03624688, 0x17e0fa17, 0x0cf1f730, 0x1abd19ce, 0x0ff64d1f, 0x008df728, 0x087fd658, 0xc17a4b}}, + {{0x1edc6c87, 0x171dc3ee, 0x07c0aac3, 0x03436aff, 0x01fae96e, 0x1c7b8cb0, 0x05532b85, 0x05aab56b, 0x39355c}}}, + /* 9*16^21*G: */ + {{{0x1163da1b, 0x16961811, 0x04e8c460, 0x1dbdcc1f, 0x11fde9a0, 0x1a4ebfe0, 0x02d1a324, 0x0f944cf2, 0x8f618b}}, + {{0x03bdd76e, 0x1f989088, 0x126db9f1, 0x018cd464, 0x05a42645, 0x0d3a6bd6, 0x0dbad7ef, 0x04be117d, 0x78233f}}}, + /* 11*16^21*G: */ + {{{0x0ec8ae90, 0x142c87f0, 0x0ef177bb, 0x04d725d1, 0x1f1b8cfb, 0x0dc6d641, 0x19bae1b5, 0x1e2b6f43, 0x9798c0}}, + {{0x052844d3, 0x14d61757, 0x0c62389e, 0x092cb7a0, 0x073bee2e, 0x04a4a7ce, 0x1b4f74bb, 0x154eb485, 0xba40e2}}}, + /* 13*16^21*G: */ + {{{0x11d66b7f, 0x0d0cbc78, 0x01f72041, 0x0d24a0a3, 0x084757aa, 0x0dc85c49, 0x159d1f3c, 0x1c7f6b45, 0xfdfa6e}}, + {{0x18e5178b, 0x1547c033, 0x15e37a76, 0x0df3ba27, 0x018c4d84, 0x00e4d1ed, 0x036e4f03, 0x03c44933, 0x4d9cf3}}}, + /* 15*16^21*G: */ + {{{0x1265bcf0, 0x003abc24, 0x071f4c2e, 0x1c56f082, 0x1220e69c, 0x14d230e7, 0x190eb77a, 0x071bc453, 0xfd58ce}}, + {{0x0b996292, 0x19f3d4e7, 0x1c73477c, 0x0c37fc51, 0x1e4fb872, 0x155cd242, 0x056f54e0, 0x1ca6ec64, 0xffe0a5}}} + }, + { + /* 1*16^22*G: */ + {{{0x10559754, 0x056b4846, 0x08fd6150, 0x0217bbc5, 0x0e02204b, 0x1dfcee06, 0x114d6342, 0x0e2b9aba, 0x213c7a}}, + {{0x14b458f2, 0x1f9613a9, 0x1a9fbb77, 0x10a1ebe6, 0x1a190bb4, 0x1683122d, 0x0941c04e, 0x016b5c8c, 0x4b6dad}}}, + /* 3*16^22*G: */ + {{{0x1e05dccc, 0x196c008c, 0x066a4f94, 0x1f47da98, 0x1d172ae3, 0x104b5ca9, 0x00c2551b, 0x1c2ea7b4, 0xb8cef6}}, + {{0x0c6d5750, 0x00a5067e, 0x1ada04cc, 0x0aff86d4, 0x0bd99df7, 0x053a7269, 0x0efda935, 0x0c14d993, 0x302b8a}}}, + /* 5*16^22*G: */ + {{{0x173bb31a, 0x0deff709, 0x16e5ed21, 0x1ef6d6bf, 0x15f49701, 0x05ef175d, 0x0e1780a8, 0x1cef368e, 0x3fb33}}, + {{0x1d215c9e, 0x1a65f34b, 0x1d903538, 0x14f8ed88, 0x1572bc65, 0x0b0d55dd, 0x18a07830, 0x0a4a91df, 0xf36ad9}}}, + /* 7*16^22*G: */ + {{{0x1d9a4ab4, 0x0da8af5e, 0x16edf029, 0x186d830a, 0x17a36717, 0x17bda687, 0x184587c5, 0x1a213d87, 0x4b177c}}, + {{0x035ab6f7, 0x156eff23, 0x1d07d562, 0x0fc4abe2, 0x06b486e3, 0x1b3949b1, 0x0997f6a3, 0x1d34bc5f, 0x3ec966}}}, + /* 9*16^22*G: */ + {{{0x101c23a4, 0x1a37d94d, 0x09273d5b, 0x1482b08f, 0x1cd75c22, 0x1c14dcdc, 0x081b0a80, 0x0a40d44f, 0x5e8703}}, + {{0x10d986f9, 0x10bccb58, 0x1333f684, 0x0e3b0e94, 0x06e2da21, 0x0ae8d716, 0x08879c5d, 0x09df7392, 0x5b9664}}}, + /* 11*16^22*G: */ + {{{0x007b0c66, 0x19f9ae90, 0x1b21bec7, 0x0ffdc8ca, 0x0eb7434a, 0x1227b056, 0x002911c8, 0x003261ad, 0xe545c3}}, + {{0x11f2d470, 0x03fabe93, 0x1688e776, 0x0b051c36, 0x1bcbf97e, 0x17ed1cac, 0x1579f971, 0x01d18c52, 0xe06a34}}}, + /* 13*16^22*G: */ + {{{0x03648bba, 0x12c0c85c, 0x10f1d112, 0x01e160d3, 0x1b39882a, 0x17112f80, 0x160284cf, 0x02af4a9e, 0xb2a442}}, + {{0x00fb6452, 0x037d5a50, 0x0705eec9, 0x10aa2a39, 0x17e31c0d, 0x1e4bc7de, 0x19867a7e, 0x1856d26c, 0xfe4f5f}}}, + /* 15*16^22*G: */ + {{{0x0bab27d0, 0x1b6e9177, 0x11440ff3, 0x1683f458, 0x17007f70, 0x180c8c6c, 0x01946ea5, 0x01e7a8a7, 0x1b908e}}, + {{0x00d3d110, 0x0f433af2, 0x1f946d5c, 0x179526b0, 0x04b9ab5b, 0x1e48c0be, 0x0d79bcd5, 0x0bdd88cd, 0x9b6d62}}} + }, + { + /* 1*16^23*G: */ + {{{0x08fbd53c, 0x0661d1d8, 0x118b377c, 0x0718e15b, 0x19a87e28, 0x09a952a0, 0x0d3a36ee, 0x054f5e96, 0x4e7c27}}, + {{0x17dcaae6, 0x059ca0c0, 0x1df74cf8, 0x172c297f, 0x1681b530, 0x084fb6f7, 0x0c6385bf, 0x0ecd93a1, 0x17749c}}}, + /* 3*16^23*G: */ + {{{0x0521b3ff, 0x114c2327, 0x0a9d433b, 0x180e2fd3, 0x024a5233, 0x0695f4d7, 0x1571d791, 0x06021928, 0x2484e}}, + {{0x0269da7e, 0x00d725c8, 0x0eb22ef3, 0x000dbd24, 0x10eebad7, 0x159e1596, 0x0c341fb0, 0x1415447c, 0x9619d0}}}, + /* 5*16^23*G: */ + {{{0x004ba7b9, 0x0b5bcfd4, 0x1d05d47e, 0x0d48e060, 0x0954e20d, 0x1b86b396, 0x195fbde6, 0x04d9f6d9, 0x16c1c5}}, + {{0x1c5bd741, 0x1adf1d28, 0x126a7311, 0x0ab8037b, 0x094deec7, 0x13a2ce45, 0x10e41898, 0x0f868062, 0xdb157f}}}, + /* 7*16^23*G: */ + {{{0x036683fa, 0x01b243d5, 0x1b02097a, 0x0436a701, 0x07b66958, 0x0e73ba29, 0x06be1ea2, 0x1aea7f26, 0x3973c}}, + {{0x1f4a577f, 0x1afd95bb, 0x1a6077b7, 0x1109c31f, 0x1a26cd77, 0x095b195a, 0x0e8d90f8, 0x05986194, 0x38cf5a}}}, + /* 9*16^23*G: */ + {{{0x00bf6f06, 0x0059ccce, 0x010ed5c6, 0x1826644a, 0x05765713, 0x027a5810, 0x054470b0, 0x174e5d9e, 0xba6a9b}}, + {{0x181db551, 0x195f7b83, 0x1a5eabf8, 0x1a29ef58, 0x0bd8e9e5, 0x05f972ac, 0x06c0c808, 0x07166942, 0x13771e}}}, + /* 11*16^23*G: */ + {{{0x11231a43, 0x08f7cf83, 0x0b50ee7f, 0x0a29accb, 0x0442f44d, 0x0ca8326f, 0x174b62bb, 0x1984f989, 0x35c5bc}}, + {{0x1620c8f6, 0x0db97228, 0x0f3c2b9f, 0x0f49980c, 0x0589d4cf, 0x0c105b7d, 0x1b39cd39, 0x0e4772e8, 0xe3675}}}, + /* 13*16^23*G: */ + {{{0x1dd40609, 0x1a05e2b7, 0x00735daf, 0x0321301b, 0x0356ac74, 0x1897e2c4, 0x1af2848b, 0x048b8ab0, 0xfd479c}}, + {{0x0a64ca53, 0x0f1789b3, 0x07291ce7, 0x075dae4c, 0x041fd911, 0x0bd21e4c, 0x1fbfcb2b, 0x16a4d295, 0x3069cf}}}, + /* 15*16^23*G: */ + {{{0x0b799a7f, 0x0db817a9, 0x0e3a1093, 0x116d9aa7, 0x07d544f1, 0x075cd796, 0x0192f7b6, 0x0547599b, 0x6c4000}}, + {{0x13c81e32, 0x1ae64166, 0x0120fda2, 0x157a9904, 0x1dcbdc07, 0x01b5070e, 0x16f9a42e, 0x02a616c6, 0x95c2dc}}} + }, + { + /* 1*16^24*G: */ + {{{0x00fb27b6, 0x1213f142, 0x10c15d8c, 0x1c7b657c, 0x06aa5c76, 0x1c56b0b4, 0x0c6c43c8, 0x07b7cef1, 0xfea74e}}, + {{0x123cb96f, 0x00e9edbf, 0x0fdedddc, 0x16b2d72e, 0x0af93126, 0x1a6f665b, 0x0ca5f3d9, 0x1b736162, 0x6e0568}}}, + /* 3*16^24*G: */ + {{{0x1e889756, 0x0ec0d74d, 0x0012ec97, 0x16c932f6, 0x099f3f27, 0x0cbd938c, 0x1aa089b3, 0x1866423f, 0x762e8b}}, + {{0x1ca6b774, 0x0f12cf03, 0x013e9789, 0x05b66291, 0x0e347197, 0x0278a4c1, 0x05f0f1f3, 0x04c15e7d, 0xc02894}}}, + /* 5*16^24*G: */ + {{{0x0975d2ea, 0x17baf4b8, 0x053a3a89, 0x0559f420, 0x0f4a91e5, 0x1edd9184, 0x14d23866, 0x08fbec12, 0xdf077d}}, + {{0x11936f95, 0x11e16cf1, 0x0f749dea, 0x1d8b709f, 0x0527c8a1, 0x012e4c51, 0x1d109321, 0x11001def, 0xf8617a}}}, + /* 7*16^24*G: */ + {{{0x1c4c92d7, 0x1c248fdd, 0x10e46d16, 0x169addca, 0x1142935d, 0x0f5419a5, 0x080cb85f, 0x0eb17a7b, 0x9f3e7d}}, + {{0x114906dd, 0x05ddfe7d, 0x0538461b, 0x144607ad, 0x11502452, 0x1590e5d5, 0x19ad6218, 0x03d4efa8, 0xecd284}}}, + /* 9*16^24*G: */ + {{{0x12a8c483, 0x1706b995, 0x0102b0d6, 0x1619118a, 0x15281174, 0x01e9177c, 0x1e7b70e3, 0x0baf6b99, 0xa0cc79}}, + {{0x12cc6ba9, 0x04b3a2ac, 0x1a4d8154, 0x091e37be, 0x1df786b3, 0x07e4b918, 0x1cfb88dd, 0x045f1670, 0xabc301}}}, + /* 11*16^24*G: */ + {{{0x05dd3aee, 0x1878db5e, 0x05b4bc85, 0x0a75151f, 0x176ca131, 0x154d6354, 0x1f338388, 0x14a2aa78, 0x6d1c50}}, + {{0x1df597f7, 0x171aa727, 0x1b54eb7f, 0x1c621551, 0x1d474851, 0x19001143, 0x1f725dc9, 0x11c0d57b, 0xafff14}}}, + /* 13*16^24*G: */ + {{{0x04a6d0bb, 0x1c654dbf, 0x086bf719, 0x1a6245eb, 0x0418f659, 0x136c5453, 0x07cfcc46, 0x0c3172ff, 0x5e5f1d}}, + {{0x1033eaf9, 0x141c23c8, 0x1bd94e85, 0x0abe5ca0, 0x121da725, 0x15e68273, 0x1bdcd63d, 0x0560d4fc, 0xd7b150}}}, + /* 15*16^24*G: */ + {{{0x0f005e3f, 0x0d4daf22, 0x10e6f4b7, 0x0d1c637d, 0x1a1495af, 0x05cd6700, 0x09ffff4f, 0x0d6782c8, 0xf8138a}}, + {{0x0f357eb7, 0x16bf0101, 0x12f884d0, 0x18837aaa, 0x1cb51f4e, 0x0af2bd52, 0x0f67e740, 0x077df69d, 0xca758f}}} + }, + { + /* 1*16^25*G: */ + {{{0x17bdde39, 0x16015220, 0x1810ca54, 0x09c2f36e, 0x168d3154, 0x0b86accc, 0x1c384289, 0x027ecef9, 0x76e641}}, + {{0x1901ac01, 0x058ba968, 0x1b480cad, 0x1467a56a, 0x1f0d35e2, 0x136b8340, 0x173d5dc1, 0x11bdc9d2, 0xc90ddf}}}, + /* 3*16^25*G: */ + {{{0x0078ee8d, 0x182848e6, 0x1a46510b, 0x1e419ca0, 0x14ff64eb, 0x1931d54d, 0x06f897fd, 0x15b0b3b5, 0xd08e57}}, + {{0x0da63e86, 0x0cbfa6e1, 0x08bb677a, 0x1def9f28, 0x06df4123, 0x19773abf, 0x035cb585, 0x13095691, 0x852e97}}}, + /* 5*16^25*G: */ + {{{0x029129ec, 0x0c8a3382, 0x12095205, 0x1c759e3c, 0x11d080ca, 0x1f407669, 0x149d7d62, 0x10bc9a89, 0x7da6c0}}, + {{0x0cd9ff0e, 0x1a857715, 0x12961aba, 0x11810ca9, 0x027bf044, 0x0103a48b, 0x015d4474, 0x0d773e83, 0xf49814}}}, + /* 7*16^25*G: */ + {{{0x11654f22, 0x1c1ea4aa, 0x06abba53, 0x0fe72846, 0x1d94fb2f, 0x0800df34, 0x19b886fa, 0x19feb837, 0x90d090}}, + {{0x001a43e1, 0x1aef02bb, 0x08fe1d03, 0x0c6aca7b, 0x170336dd, 0x010f035f, 0x186a54fc, 0x03a5759e, 0xcd569a}}}, + /* 9*16^25*G: */ + {{{0x076b19fa, 0x1b77b28e, 0x020675c6, 0x0dc0da0d, 0x1292ed9d, 0x16188410, 0x07b31cc8, 0x0b0f9e3a, 0xda4798}}, + {{0x126f5af7, 0x15137759, 0x14ff081a, 0x17a27d2a, 0x0569ea67, 0x1483bf0b, 0x1c0745cd, 0x0f137995, 0xebb1d7}}}, + /* 11*16^25*G: */ + {{{0x19135dbd, 0x0c97db2d, 0x1618c7b3, 0x010f5e73, 0x1897cf0c, 0x157ac174, 0x19ab605e, 0x00951bbd, 0xe3e475}}, + {{0x0748045d, 0x083579f2, 0x12576a5a, 0x0405febd, 0x03ffea5a, 0x040ca95c, 0x1b102e63, 0x1f013978, 0x930a5b}}}, + /* 13*16^25*G: */ + {{{0x0dee455f, 0x1f85cf2e, 0x13901d72, 0x0fffcdd1, 0x1db4aff6, 0x099c7c05, 0x06c291d1, 0x0dfd0e15, 0x7e8c65}}, + {{0x171b9cba, 0x19ef4cc0, 0x1d1989c5, 0x05a2ce8d, 0x1a53b4aa, 0x1b07a401, 0x103ca8fd, 0x0659460e, 0xbdddc6}}}, + /* 15*16^25*G: */ + {{{0x0698b59e, 0x1bcb5cdb, 0x0d11e90d, 0x06b24b12, 0x1c7260a3, 0x01ad59f1, 0x1ac56fac, 0x1f12352b, 0x3df841}}, + {{0x0b92baf5, 0x07c733cb, 0x12527e2f, 0x190cf642, 0x0f3867bf, 0x1d74788e, 0x0307680a, 0x1bf31612, 0xb38fe6}}} + }, + { + /* 1*16^26*G: */ + {{{0x0bcbb891, 0x158a8121, 0x09b2fb8e, 0x198c87be, 0x18f9a8f7, 0x0dd53a1f, 0x0f87a0a0, 0x0d607655, 0xc738c5}}, + {{0x099a84c3, 0x1f39aecb, 0x0033fa45, 0x029ddef1, 0x1bbbb823, 0x0797565f, 0x094dfdc6, 0x0f12a35a, 0x893fb5}}}, + /* 3*16^26*G: */ + {{{0x0761d58d, 0x05d5799c, 0x15838bcd, 0x1937c811, 0x0df7aca4, 0x0051ab90, 0x05184289, 0x04f047ec, 0xb8c461}}, + {{0x1d1051a4, 0x1c7505d4, 0x041f16d8, 0x0a08f0bc, 0x1c5053f7, 0x0c7b4bf9, 0x0df45291, 0x0d8a2e1c, 0x8f9ed9}}}, + /* 5*16^26*G: */ + {{{0x050b0040, 0x0d859820, 0x04d2b70b, 0x08fb73e0, 0x03671f3f, 0x04c9ff4d, 0x07de8d43, 0x13ee204e, 0x8d56e}}, + {{0x1b3fd0a1, 0x0c7133d2, 0x05e0afad, 0x0f88e41c, 0x1b285f6d, 0x0a8546bc, 0x0a887ff4, 0x15d7a153, 0xa12185}}}, + /* 7*16^26*G: */ + {{{0x13573b7f, 0x0b2b1ef8, 0x02d89c3d, 0x1438bda6, 0x05a37889, 0x07cbbdf3, 0x198d0788, 0x065a85f9, 0xdc13f2}}, + {{0x0c1f2ba6, 0x15669142, 0x1012c710, 0x0aa8e02e, 0x10a76704, 0x086d4254, 0x1030f1d0, 0x100853c6, 0xc909ba}}}, + /* 9*16^26*G: */ + {{{0x07ae9ceb, 0x017ac85c, 0x1bcb452c, 0x1843d4e1, 0x119a8226, 0x0ab2ed90, 0x1c1cebc6, 0x1cc03bef, 0x25c02d}}, + {{0x0bc6e275, 0x1848689a, 0x1961c991, 0x0c83be14, 0x111d537c, 0x0706e7d6, 0x00f03221, 0x1a590247, 0x8a9fea}}}, + /* 11*16^26*G: */ + {{{0x0c5a3c34, 0x1c04c7b7, 0x16527f87, 0x1d058052, 0x03e58aec, 0x0fa653d7, 0x1273a2ae, 0x03659f1c, 0xfedd9d}}, + {{0x013d7714, 0x15aa5fa7, 0x0fe0a5d8, 0x1d6a8d33, 0x14b00ada, 0x02989647, 0x0382c2fa, 0x18630a77, 0xa52e24}}}, + /* 13*16^26*G: */ + {{{0x0ab6a396, 0x1a72a68d, 0x04d81da6, 0x04372342, 0x088b3730, 0x16bfaf42, 0x1230b7b8, 0x10d78dd4, 0x3e0e32}}, + {{0x1980e27e, 0x1598f246, 0x11a3da8b, 0x002083e6, 0x13fc66ab, 0x1f0ef5a2, 0x1e593cc7, 0x0e5f4766, 0xca4481}}}, + /* 15*16^26*G: */ + {{{0x141023ec, 0x179ac311, 0x1b3d5c2a, 0x0bb1eedf, 0x0b9af564, 0x101004c1, 0x14a1260b, 0x06101865, 0x344ab9}}, + {{0x1e1eeb87, 0x07c4c148, 0x0e6575c1, 0x1d2ed5f8, 0x14c5ffc4, 0x1968f528, 0x18a9cfe3, 0x00856488, 0x6e1c2b}}} + }, + { + /* 1*16^27*G: */ + {{{0x08f6c14b, 0x1974fb2c, 0x0494050d, 0x0e5cbe75, 0x12877d1d, 0x03b1be4b, 0x0e078993, 0x0ca916cb, 0xd89562}}, + {{0x1d7d991f, 0x09b1f6ba, 0x0c19f85e, 0x051ac657, 0x140eb034, 0x03040c61, 0x1ab9ca3b, 0x071e578f, 0xfebfaa}}}, + /* 3*16^27*G: */ + {{{0x0127b756, 0x05d43ffb, 0x0825c120, 0x0517c957, 0x0b416034, 0x116d2830, 0x0499cb4d, 0x05ee2dbe, 0x6d8c78}}, + {{0x1f172571, 0x0a8fba55, 0x1f373299, 0x154db45a, 0x14daf4e3, 0x14169b69, 0x04445166, 0x0112dfb7, 0x99aedf}}}, + /* 5*16^27*G: */ + {{{0x158cf17a, 0x0f70d39b, 0x0208d493, 0x10bb974b, 0x097f8f1f, 0x0d778da0, 0x0b2a3416, 0x1bb2b7ef, 0xebcabe}}, + {{0x1caa0ccd, 0x0366e2fa, 0x0b3a5711, 0x15a425a1, 0x12e6b10f, 0x050db3e1, 0x072c0b00, 0x01f1e457, 0x47d3ce}}}, + /* 7*16^27*G: */ + {{{0x0c855c5b, 0x077728ad, 0x1f22beef, 0x0ac43402, 0x1fc28118, 0x0d1b4f0b, 0x189114cc, 0x05c97a99, 0xe8df4d}}, + {{0x0e465650, 0x0eaf3961, 0x07935f56, 0x076abe3c, 0x132c5966, 0x0da7acf7, 0x0c991113, 0x0e188ff3, 0x6c57fd}}}, + /* 9*16^27*G: */ + {{{0x12e7e454, 0x047aded2, 0x03985434, 0x05dfde1e, 0x01662fe3, 0x03011d4c, 0x00ca4492, 0x1ae31d95, 0x4068d3}}, + {{0x18ef191e, 0x1cd66f2e, 0x10dccc9d, 0x1a43da27, 0x138d1988, 0x0a2cbece, 0x1eaae7b0, 0x16e4a948, 0x8cd853}}}, + /* 11*16^27*G: */ + {{{0x06c5d939, 0x02bd6fc2, 0x0a4cf782, 0x0b450ef7, 0x0027ea47, 0x19973065, 0x1782d56f, 0x19b63b04, 0x12550e}}, + {{0x19e757c9, 0x153a7e2a, 0x16350c64, 0x16e83fd9, 0x04a72838, 0x121e0bb9, 0x1e9d5123, 0x069f0e5a, 0x7b8f83}}}, + /* 13*16^27*G: */ + {{{0x16c8a56f, 0x06855632, 0x1cdd084e, 0x1278a869, 0x0d08f850, 0x1bda9d7d, 0x17531a6e, 0x035876b0, 0x944d67}}, + {{0x1a7be289, 0x0fa6e32e, 0x01945fae, 0x0982e9ba, 0x0c61967d, 0x1c9b099d, 0x1ffd3050, 0x12ef6a03, 0xa71065}}}, + /* 15*16^27*G: */ + {{{0x0e08f15a, 0x175c50c5, 0x04a402eb, 0x13cadb90, 0x1c305fd6, 0x01b2ad69, 0x0833f9ac, 0x1239a133, 0x86a54e}}, + {{0x01388308, 0x09268f3e, 0x0d49534d, 0x053a24b6, 0x16867771, 0x146836ba, 0x1180e9ca, 0x0906f4f0, 0xcfee61}}} + }, + { + /* 1*16^28*G: */ + {{{0x0f676e03, 0x08a852b2, 0x1a13b752, 0x1f8e6d27, 0x08761cef, 0x1219ab8f, 0x1463ac3d, 0x006552ae, 0xb8da94}}, + {{0x0efdf6e7, 0x0447273a, 0x1fced445, 0x18b09b2b, 0x008b092c, 0x0e64bb14, 0x07935f26, 0x148900b4, 0x2804df}}}, + /* 3*16^28*G: */ + {{{0x06e1346b, 0x10cc24ee, 0x16bc717a, 0x1cf62070, 0x152c05ab, 0x1b0f8a68, 0x042f9531, 0x1fe1305a, 0x69068}}, + {{0x17226c13, 0x1dac52a6, 0x11809b9e, 0x0d127329, 0x0442aa4f, 0x0d95e843, 0x189b6a17, 0x1c1217fb, 0xb863e3}}}, + /* 5*16^28*G: */ + {{{0x1ca1f6a1, 0x07348fe6, 0x033fc68c, 0x197a2869, 0x06ce1068, 0x0e2e58f4, 0x1d854a1b, 0x127964b2, 0x898c34}}, + {{0x164f647c, 0x056e1078, 0x0af5e729, 0x0f9f2f3e, 0x06e93b2a, 0x0a122956, 0x15527611, 0x10d56ad4, 0x75f759}}}, + /* 7*16^28*G: */ + {{{0x1d1e3998, 0x134f01e1, 0x0810ca2a, 0x0d91722f, 0x0274e5e5, 0x0ceb8115, 0x0fc0694a, 0x1fda5231, 0xb213e2}}, + {{0x125fb81e, 0x0165ee31, 0x17b45d7b, 0x082cb7d7, 0x03bc3d53, 0x0f5fc1d2, 0x104b0f58, 0x1841e5a7, 0x229f8e}}}, + /* 9*16^28*G: */ + {{{0x025be234, 0x05f0ff65, 0x17cd41c0, 0x07c7ce1b, 0x06e060e8, 0x05d348b0, 0x04f97474, 0x1b02d8ff, 0x4b3b3a}}, + {{0x120e8362, 0x11dbf01c, 0x0846e101, 0x0ffb259e, 0x0c04c41e, 0x14b6fec6, 0x1271e1d7, 0x0770bb57, 0x5eec02}}}, + /* 11*16^28*G: */ + {{{0x0289f55e, 0x0b31a53f, 0x11d9c1ee, 0x0d61e1c5, 0x165be297, 0x08d813c4, 0x1e580809, 0x16dbb609, 0x9f7b88}}, + {{0x1e1e4bde, 0x1e69db2f, 0x0428ac6c, 0x0a6dc1d6, 0x1c71fc0e, 0x035a14c7, 0x03def8c5, 0x1098e082, 0x32f9f7}}}, + /* 13*16^28*G: */ + {{{0x1dfe1d2b, 0x12a4e460, 0x1a1e3945, 0x07ea13ca, 0x173a4c49, 0x056fe9fd, 0x038f9db3, 0x1d396e89, 0xd58a43}}, + {{0x0cd50922, 0x0793cadc, 0x0f5befff, 0x137442b9, 0x0276b54d, 0x14899414, 0x0f3c429c, 0x0d740b10, 0xfc1786}}}, + /* 15*16^28*G: */ + {{{0x049c795e, 0x120a8df1, 0x13a01784, 0x080d5533, 0x1ea4eed4, 0x0b4e1e13, 0x0c4335b6, 0x072e2230, 0x21d271}}, + {{0x19208ece, 0x0009761c, 0x17edd86c, 0x03289495, 0x1b4c3d67, 0x0dc2a915, 0x13a85dcd, 0x16960eb5, 0x94c5f9}}} + }, + { + /* 1*16^29*G: */ + {{{0x03c0df5d, 0x0d08bbc7, 0x15a9e4bc, 0x13dff6a2, 0x17fab201, 0x0d5ca3ae, 0x0ce9f62b, 0x028883f6, 0xe80fea}}, + {{0x0ac9ec78, 0x05a148db, 0x0c8baa7f, 0x0abd015e, 0x094472d1, 0x1b4651e5, 0x01dc7a25, 0x0fec71c0, 0xeed1de}}}, + /* 3*16^29*G: */ + {{{0x17592d55, 0x001acf66, 0x18d4064b, 0x0b728e81, 0x0e3b106d, 0x17b4b19e, 0x149822bf, 0x1b789420, 0x5d2ec6}}, + {{0x0f5183a7, 0x1372e875, 0x04545d09, 0x14f79b5a, 0x0d6950e2, 0x087d1346, 0x17ad63dc, 0x1f138dc8, 0xa92cd}}}, + /* 5*16^29*G: */ + {{{0x1e8f9f5c, 0x08fa5a4f, 0x02029466, 0x03e3c2b3, 0x1404d736, 0x171a10af, 0x1d0bf8b2, 0x1876237e, 0xac371d}}, + {{0x125a503c, 0x1d41ff99, 0x1d478745, 0x0a68b1dc, 0x0e735229, 0x00f3992a, 0x11dffc84, 0x1830e134, 0xc51616}}}, + /* 7*16^29*G: */ + {{{0x19e33446, 0x050e46a8, 0x0bce177d, 0x127788a5, 0x0a17a408, 0x005e8111, 0x10324d23, 0x07429e30, 0x894200}}, + {{0x06387689, 0x069c5007, 0x19d3e610, 0x1aee6cf3, 0x1e4e06bf, 0x16b6877e, 0x1de9362c, 0x12b2b4a0, 0xa9fd03}}}, + /* 9*16^29*G: */ + {{{0x1913cb26, 0x0b9464ad, 0x0ef5b40f, 0x16833802, 0x05c9899e, 0x1227faa8, 0x0aa28b36, 0x0d661468, 0x277026}}, + {{0x1348a7a2, 0x1f38b99f, 0x0056faef, 0x01923799, 0x0b324e94, 0x092683f9, 0x0c69554b, 0x0bcf361b, 0xf649bc}}}, + /* 11*16^29*G: */ + {{{0x195e8247, 0x0555010a, 0x01b346bc, 0x1fb88aad, 0x0ba9097b, 0x13700e7c, 0x1485e397, 0x1a70797d, 0x75e4d0}}, + {{0x19982d22, 0x111fecea, 0x06b624f2, 0x156b6dd5, 0x126d47dd, 0x0b8763db, 0x0641d07e, 0x142ea821, 0x1fce42}}}, + /* 13*16^29*G: */ + {{{0x06333323, 0x03cfa26d, 0x1d2afd1d, 0x177838d1, 0x0da849cb, 0x06b02cc2, 0x0fc0fc08, 0x07066c37, 0x3ed1b6}}, + {{0x15d61ba3, 0x189fe245, 0x1e3dca52, 0x0e514216, 0x1929ea9b, 0x04c4b447, 0x1b9d765f, 0x14916b69, 0xd84f2d}}}, + /* 15*16^29*G: */ + {{{0x133980bf, 0x1282bea5, 0x17402ebc, 0x06e05ca1, 0x0dd4368a, 0x1ebb91a4, 0x0606e11b, 0x1e0d4eb0, 0x70fdd2}}, + {{0x00b75785, 0x17754675, 0x15d29584, 0x006b070b, 0x0596b0a1, 0x008688f7, 0x1a5a55e9, 0x181a1ab0, 0x5edfca}}} + }, + { + /* 1*16^30*G: */ + {{{0x04e16070, 0x0e03dde6, 0x1f5a4577, 0x0304063d, 0x07543f2a, 0x04728eab, 0x010c4ee9, 0x0f7bf9ae, 0xa30169}}, + {{0x1e177ea1, 0x0068d020, 0x084684c3, 0x0bb7ef81, 0x00f9b173, 0x04fd12ea, 0x13d42060, 0x039f6cfc, 0x7370f9}}}, + /* 3*16^30*G: */ + {{{0x138011fc, 0x18093800, 0x1ca15899, 0x12d4cf5a, 0x00a4d835, 0x09984110, 0x0c4455ac, 0x146102bd, 0x6e8313}}, + {{0x1f15ab7d, 0x165b4fd1, 0x1147e69a, 0x1f22b5d3, 0x0c30426a, 0x16d900ed, 0x08130684, 0x117b849e, 0xc14781}}}, + /* 5*16^30*G: */ + {{{0x100e6ba7, 0x1d3a4dc6, 0x045bdfd4, 0x0dd8b689, 0x1e1b43d3, 0x101c526c, 0x147caf47, 0x0132f090, 0xf952a9}}, + {{0x0175e4c1, 0x0dd77728, 0x18a8ae63, 0x0e2cf698, 0x1a0f6555, 0x1b51713f, 0x1afe184d, 0x0b611579, 0xd8a93a}}}, + /* 7*16^30*G: */ + {{{0x03aa0e93, 0x08032d14, 0x1ec7d89a, 0x1c72875d, 0x0893a8f2, 0x18d0cecf, 0x1b9d4100, 0x0bc63a7f, 0x94016d}}, + {{0x07addac2, 0x07769344, 0x15ec1e8e, 0x086e7754, 0x06fd7f48, 0x0e9aa777, 0x165900d5, 0x1dcb88a9, 0x675032}}}, + /* 9*16^30*G: */ + {{{0x0266b17b, 0x07a43170, 0x18aeccac, 0x0ad14404, 0x109c2023, 0x1c42354f, 0x0a246ee5, 0x0e9ab3f6, 0xef22d1}}, + {{0x19dac83e, 0x1537021b, 0x10d06dcc, 0x0e4edee3, 0x0a1073ee, 0x0661d71a, 0x11d5a3e7, 0x192f5649, 0xbc5784}}}, + /* 11*16^30*G: */ + {{{0x12d382a0, 0x18980ad4, 0x1b366b88, 0x1b9779c5, 0x1f927f28, 0x063c0596, 0x04b4e72b, 0x19c99d71, 0xb5f7ef}}, + {{0x05b4b532, 0x117855dd, 0x0b3e316e, 0x1612da53, 0x1ddd371f, 0x0be37065, 0x08d4f025, 0x0b6a387e, 0x684354}}}, + /* 13*16^30*G: */ + {{{0x012cffa5, 0x13492322, 0x0331711f, 0x1a8410cd, 0x0624389e, 0x0a6c7dea, 0x01d9021d, 0x1a565ce2, 0x1cddc3}}, + {{0x1521954e, 0x0f36c4e6, 0x0dad4a2b, 0x193084d6, 0x0b08ac41, 0x0935fca1, 0x0298ff6c, 0x01965e3f, 0x1e476a}}}, + /* 15*16^30*G: */ + {{{0x14a9f22f, 0x1aff21c9, 0x1ea38ab4, 0x10338a42, 0x035b0cc0, 0x05c5ca44, 0x04e7c87e, 0x0b3e4b9d, 0x2accb3}}, + {{0x175c4927, 0x1baee59d, 0x0e9542de, 0x17af7d8b, 0x0edf1154, 0x1d1bf6f8, 0x0b946484, 0x1d2b115a, 0xd518a4}}} + }, + { + /* 1*16^31*G: */ + {{{0x1fb04ed4, 0x1bd631f1, 0x0c1fffea, 0x18661622, 0x18de208c, 0x0e828933, 0x04d918fe, 0x16713ad7, 0x90ad85}}, + {{0x0b6ef150, 0x08ea6a46, 0x00a25366, 0x1df57c2b, 0x022b839a, 0x05eca139, 0x0986bff7, 0x06c41470, 0xe507a}}}, + /* 3*16^31*G: */ + {{{0x10b7b678, 0x13aed99d, 0x1d8e0598, 0x18862379, 0x16c76f13, 0x15e52135, 0x0c6e8661, 0x0e669c84, 0x186e49}}, + {{0x18d91fc1, 0x1d03b797, 0x054d7729, 0x0ee44a89, 0x1e67e110, 0x0412e05b, 0x1612a9ff, 0x1c9300f7, 0xc0d460}}}, + /* 5*16^31*G: */ + {{{0x13421fb8, 0x18372e5d, 0x16957433, 0x12e3e5de, 0x12412984, 0x159a61db, 0x1d8b9f81, 0x1069edb7, 0x61c8d}}, + {{0x0e3ccd80, 0x0af7b342, 0x1bf374a6, 0x0e269674, 0x1eb5c806, 0x092c8702, 0x12deea4e, 0x1b320076, 0x6dfc6a}}}, + /* 7*16^31*G: */ + {{{0x0f6b35a4, 0x0925f0c5, 0x09fed21c, 0x1e6f4d56, 0x068ad889, 0x1920399d, 0x144edcd8, 0x074411dc, 0xf6a6b6}}, + {{0x01f422a6, 0x175b7f64, 0x1b8618b2, 0x0aeadceb, 0x0186f19d, 0x1b827ab0, 0x0e2c72b4, 0x150005a2, 0x3df7c8}}}, + /* 9*16^31*G: */ + {{{0x06954c11, 0x0b411f45, 0x1834062e, 0x1148782a, 0x178ff7fa, 0x0d878a83, 0x0dd88834, 0x051850d8, 0x87a2fc}}, + {{0x072a8b45, 0x0b719971, 0x1e0492dd, 0x11267e54, 0x07532cc4, 0x0a46d069, 0x13be5ec6, 0x1168b55d, 0x33ad51}}}, + /* 11*16^31*G: */ + {{{0x02706ab6, 0x123a3957, 0x194f036b, 0x16683ba5, 0x04cfe3c0, 0x177e5e1c, 0x069a1155, 0x008dcf10, 0xe1472e}}, + {{0x1d58de05, 0x174350b4, 0x0f349d4d, 0x113aaa8a, 0x021f8aa5, 0x08cbc643, 0x1f1a0fda, 0x1548f8b1, 0x82cd92}}}, + /* 13*16^31*G: */ + {{{0x07a84fb6, 0x1fd72a10, 0x0854087a, 0x06d0ea1f, 0x0b9ebc42, 0x06b00f24, 0x1bd77a2d, 0x19009f15, 0x1caf92}}, + {{0x07149109, 0x158c0c81, 0x0b399d85, 0x1982d2d4, 0x01622ec1, 0x127c7f88, 0x14e92069, 0x0b592edc, 0xbc24b8}}}, + /* 15*16^31*G: */ + {{{0x0a955911, 0x1b467f0a, 0x17b54b6d, 0x1c2d44c1, 0x18397107, 0x17f4d9eb, 0x14349627, 0x0d35e12c, 0x705bfd}}, + {{0x1fd200e4, 0x1dbe349f, 0x10b9cb62, 0x1e76a454, 0x051fa297, 0x1ec0faa0, 0x06429f98, 0x02616e7f, 0xe14aa4}}} + }, + { + /* 1*16^32*G: */ + {{{0x1ec4c0da, 0x1bda2264, 0x0fa8cd46, 0x18acf0e4, 0x1162ee88, 0x00d6cc0f, 0x1cce48e7, 0x1a5ec76b, 0x8f68b9}}, + {{0x101fff82, 0x11e5fbca, 0x1442ff7c, 0x1459fd2a, 0x0215dbbe, 0x08615b5f, 0x061b7876, 0x05b740c7, 0x662a9f}}}, + /* 3*16^32*G: */ + {{{0x123809fa, 0x0715c76e, 0x16552f86, 0x08b966a3, 0x11f08fd8, 0x19b1f922, 0x1c8a2ea4, 0x17c5ca13, 0x38381d}}, + {{0x131fed52, 0x0b83a8c1, 0x163c936f, 0x03f99665, 0x0b1cc368, 0x02d2a907, 0x1f72c250, 0x0141f722, 0xe4a32d}}}, + /* 5*16^32*G: */ + {{{0x17c2a310, 0x15213244, 0x04898c0f, 0x0d5d4a80, 0x099a1f18, 0x0dc15523, 0x0b9bda48, 0x049c86e5, 0x492627}}, + {{0x1e27ded0, 0x020db40a, 0x17fe3383, 0x0c6c254e, 0x0303b6d1, 0x1d2b4b8a, 0x0fe568b3, 0x0e7794f5, 0x1337e7}}}, + /* 7*16^32*G: */ + {{{0x0ebd2d31, 0x1c2583ce, 0x01b6e344, 0x1834adfe, 0x1e2f84dc, 0x09d9f23b, 0x12435789, 0x11834481, 0xe30656}}, + {{0x12546e44, 0x095a041c, 0x0dce099a, 0x1900857c, 0x10db6ffb, 0x15883fbe, 0x0982223c, 0x1c6f1268, 0xeac6f}}}, + /* 9*16^32*G: */ + {{{0x163136b0, 0x09861cf1, 0x0d077671, 0x17f1b355, 0x1d63374e, 0x073b11fd, 0x1bf09c6c, 0x01c48519, 0x3b9e10}}, + {{0x0cdbbc8a, 0x09f60b7b, 0x14c7e065, 0x1c514675, 0x15b26a2a, 0x19f5c7a3, 0x0dc77c54, 0x02a5a2d7, 0xfafb98}}}, + /* 11*16^32*G: */ + {{{0x0f485d3f, 0x10478239, 0x01efbba5, 0x140ed102, 0x0def717c, 0x05407aef, 0x06a4addb, 0x092e2559, 0xbb0aad}}, + {{0x1ca2f975, 0x1c9c9281, 0x19c2fff9, 0x14b5f462, 0x1da34895, 0x100fb94b, 0x11e63b34, 0x0a78b06a, 0xea699c}}}, + /* 13*16^32*G: */ + {{{0x16718dc9, 0x177699d1, 0x0448f792, 0x0b169b60, 0x00113e1e, 0x158cbd7f, 0x130353a3, 0x191c9ddf, 0x79090a}}, + {{0x1cfae7c5, 0x11991588, 0x0a4022e5, 0x0d5f6e17, 0x0aa56dd3, 0x0b65e6cd, 0x0e3c4f60, 0x0572320b, 0xeaab72}}}, + /* 15*16^32*G: */ + {{{0x1f60c7d1, 0x134b4a63, 0x1dd6b4a8, 0x0e3bcf9a, 0x1ba668dd, 0x0dde72a4, 0x0d54700f, 0x15bd3f2f, 0xe77c81}}, + {{0x02d72449, 0x162c0f94, 0x0a61b4d3, 0x08e1ee38, 0x01543631, 0x1d991f54, 0x0c8717f0, 0x0f1ddf02, 0x3acf14}}} + }, + { + /* 1*16^33*G: */ + {{{0x13231e11, 0x1437ea82, 0x1a078f99, 0x11d0ca06, 0x036091f4, 0x0ffc8cc6, 0x17597fe6, 0x002ed5f0, 0xe4f3fb}}, + {{0x0feb73bc, 0x1161c2bb, 0x14747260, 0x0fce9d92, 0x0b7286cc, 0x13687501, 0x1c705986, 0x075a1de9, 0x1e6363}}}, + /* 3*16^33*G: */ + {{{0x0bf05bd6, 0x1ccf8273, 0x0aa65194, 0x0adc0642, 0x10deca2f, 0x1a8ff5a3, 0x1fa420cb, 0x0837dc89, 0x900c32}}, + {{0x100d358b, 0x129569df, 0x13bec577, 0x10a6b078, 0x12439d69, 0x19022b85, 0x03d7e571, 0x1d1d163e, 0x6c31f9}}}, + /* 5*16^33*G: */ + {{{0x1f105c50, 0x13e14664, 0x04e1495e, 0x01bddef6, 0x033cd82e, 0x061a01e1, 0x02ab58a3, 0x0c5560b2, 0x5a8d03}}, + {{0x18a4cde9, 0x04d900c1, 0x1404f0d7, 0x0bed14cd, 0x1ff74a60, 0x15b920a1, 0x14da4da9, 0x16227a9c, 0xc059ea}}}, + /* 7*16^33*G: */ + {{{0x12d64feb, 0x03f0c5cd, 0x048a4b19, 0x05f14b25, 0x1a4e8377, 0x1d2bbb65, 0x182923bc, 0x0062465e, 0xd93f4d}}, + {{0x14698359, 0x187deac9, 0x124368de, 0x008617dd, 0x08c4d93e, 0x188e2a6e, 0x1cce88dc, 0x0ba8b964, 0x792555}}}, + /* 9*16^33*G: */ + {{{0x039fbc84, 0x1110266a, 0x15e8059c, 0x00c522c0, 0x0c65b7e7, 0x115e3315, 0x01106c53, 0x18dc6de5, 0x2f0769}}, + {{0x1c201bec, 0x1dc816f0, 0x137575cf, 0x0f36d498, 0x02149cca, 0x1803cc87, 0x1777e977, 0x0e49ae77, 0xb434f3}}}, + /* 11*16^33*G: */ + {{{0x06a758ea, 0x09bf5664, 0x1fc67135, 0x11063124, 0x16e39911, 0x04ad0aa0, 0x0c26561a, 0x100ab3c0, 0xfe7e67}}, + {{0x1b7ab649, 0x07916cae, 0x0c483479, 0x002e0e88, 0x1251f3b8, 0x070b4c24, 0x12e62302, 0x0cf4503b, 0x38aa69}}}, + /* 13*16^33*G: */ + {{{0x0cfffefa, 0x138ab134, 0x1946beb7, 0x10089ee0, 0x1af85101, 0x17c8a861, 0x049f5b7d, 0x194ea706, 0x91baf5}}, + {{0x1f7f6faf, 0x1e9b79a6, 0x1f4c0f71, 0x1de621cd, 0x13d92f4b, 0x14f893ee, 0x13af9765, 0x023268f7, 0x4e5cf}}}, + /* 15*16^33*G: */ + {{{0x0d33c546, 0x1e048bf4, 0x17b6bccb, 0x0ebf6650, 0x1ddd7825, 0x09b9f07a, 0x029f2cb1, 0x043967ef, 0x445841}}, + {{0x000b4fd4, 0x05368c38, 0x0b29cd98, 0x1e1479f6, 0x0da20852, 0x0f571c8d, 0x14e9dc89, 0x0efe7f0e, 0x308d93}}} + }, + { + /* 1*16^34*G: */ + {{{0x00eae29e, 0x17db7571, 0x138741c5, 0x069e5e1a, 0x04266c70, 0x0a9bd22d, 0x0cc7ae58, 0x13631d7e, 0x8c00fa}}, + {{0x0702414b, 0x1e952633, 0x18db1539, 0x15b5f503, 0x0c974c2f, 0x1a1d1b9b, 0x0686a770, 0x0cffd4a4, 0xefa472}}}, + /* 3*16^34*G: */ + {{{0x0bfd913d, 0x1fcab01f, 0x15327ab0, 0x0d01cddc, 0x08d2050a, 0x1d042615, 0x17e1d341, 0x14fd20fb, 0x36362a}}, + {{0x052e243d, 0x027cd756, 0x0cbeabf1, 0x017621ad, 0x02a82d83, 0x1221b86d, 0x1f54d058, 0x0ced9715, 0x48f278}}}, + /* 5*16^34*G: */ + {{{0x0d132896, 0x055d5fcd, 0x0f1b259f, 0x03c7756f, 0x1200dfcb, 0x16cb16ec, 0x180bca56, 0x0dbe6543, 0x448797}}, + {{0x0f685248, 0x0600d895, 0x1daa9e92, 0x081ab4c4, 0x01a3306b, 0x1483ba2b, 0x1f87bf26, 0x0c1a22b5, 0x27bd58}}}, + /* 7*16^34*G: */ + {{{0x1fa2670c, 0x040ab7b5, 0x189cf9b8, 0x01f05740, 0x10324740, 0x0e55419a, 0x0de22daa, 0x18517970, 0x4a4d3a}}, + {{0x16c1764d, 0x045cffee, 0x0a6fbb60, 0x00b2997e, 0x02b9d493, 0x188eef78, 0x093c5f9d, 0x0380308b, 0x70abb9}}}, + /* 9*16^34*G: */ + {{{0x0cb24aa7, 0x0cb57f12, 0x0d1714c3, 0x02118c72, 0x0fe18903, 0x17ec4ffc, 0x000b0eb9, 0x03215d23, 0x5f7b2d}}, + {{0x0a693d7d, 0x0bc03a1a, 0x1207431b, 0x1bd6e127, 0x07c47ce8, 0x1c051e73, 0x0accc28f, 0x00189fbe, 0x77036}}}, + /* 11*16^34*G: */ + {{{0x0f7fb8bd, 0x0c8de2f9, 0x19024290, 0x08c942bf, 0x086d87bc, 0x12736ca4, 0x0340abb0, 0x0a2673b2, 0x513974}}, + {{0x03908c0f, 0x04a5616c, 0x1e6356a0, 0x1865a7c7, 0x15c47faf, 0x1f7ed740, 0x0fcd2e23, 0x07c8ec87, 0xfcd714}}}, + /* 13*16^34*G: */ + {{{0x0e36ca73, 0x15ba48af, 0x0efcfb78, 0x10c9a6d7, 0x14887eac, 0x08895f80, 0x1ee2a90a, 0x1ac57f7b, 0xcf8316}}, + {{0x0ec25534, 0x1a490ca1, 0x035c43d6, 0x0c2b31b1, 0x0ca681c9, 0x0284486e, 0x15cf8e11, 0x11bd6bb3, 0x9feb5}}}, + /* 15*16^34*G: */ + {{{0x1752f97d, 0x0449beb5, 0x0d1d984f, 0x1df78ebe, 0x1a6165a2, 0x09433467, 0x127794e7, 0x13498976, 0x8610de}}, + {{0x1f1b1af2, 0x02bee6a0, 0x1550f820, 0x169b3ea8, 0x1f99e57a, 0x0ccd9299, 0x0ef24df3, 0x14056c61, 0xd31997}}} + }, + { + /* 1*16^35*G: */ + {{{0x00cb3e41, 0x0bfeefe3, 0x02e4b026, 0x1a109e61, 0x18ed3143, 0x076054f4, 0x0a7cf843, 0x1cd3ba90, 0xe7a26c}}, + {{0x0f2cfd51, 0x1454a10e, 0x03a0f883, 0x0d658084, 0x1bb18d0a, 0x00350d57, 0x012d1c6c, 0x0601f4f3, 0x2a758e}}}, + /* 3*16^35*G: */ + {{{0x1aee42db, 0x07cfe95f, 0x0c1c529c, 0x1778b68e, 0x0bfc1d9b, 0x176dc8f6, 0x0543f1ed, 0x1cfb36b2, 0xcc3427}}, + {{0x15d87bdb, 0x1114e008, 0x1c908b71, 0x0b975b1c, 0x1520010e, 0x1fe9fd90, 0x1a862178, 0x0834a438, 0xea2498}}}, + /* 5*16^35*G: */ + {{{0x1ed4a086, 0x1a1b3633, 0x071043bf, 0x0eb82b1d, 0x15cf4b1d, 0x02c2fde5, 0x1177b20f, 0x1759b308, 0x948f05}}, + {{0x1e2bca4b, 0x150c007c, 0x0fe8b468, 0x06514e38, 0x139411c2, 0x08533008, 0x08ce0bd1, 0x13ff6b45, 0x864ca8}}}, + /* 7*16^35*G: */ + {{{0x17542c21, 0x065f5365, 0x09930570, 0x13e9a51d, 0x16d43ae1, 0x1e22a28e, 0x0b24195b, 0x0c525233, 0x258419}}, + {{0x072bfabf, 0x1f5f18cb, 0x10ab5ece, 0x07430dc9, 0x113d5f3e, 0x0d52663a, 0x11200797, 0x03e39b64, 0xfcb35b}}}, + /* 9*16^35*G: */ + {{{0x0c1ecbf8, 0x1e230fa0, 0x1bb5f290, 0x13e1bd35, 0x0421f648, 0x1aa660f4, 0x14948aa5, 0x18826e78, 0x7e12cd}}, + {{0x10bed615, 0x0a2dc66d, 0x18767d67, 0x13ec3b1f, 0x11259c96, 0x0a6d5f26, 0x00dc50fe, 0x111111b9, 0x71284f}}}, + /* 11*16^35*G: */ + {{{0x14557d86, 0x1f3328e0, 0x199ffd05, 0x1dd88f1c, 0x1a6cf1cf, 0x08e53d02, 0x0a99dcae, 0x1fe546e8, 0x4b8ec2}}, + {{0x15167eb9, 0x0ecd8c8d, 0x10fda4af, 0x0be5de1f, 0x1ac5f28d, 0x0396f293, 0x1eac5290, 0x1fe0982a, 0xfde6c3}}}, + /* 13*16^35*G: */ + {{{0x0780763c, 0x15c169da, 0x195a4754, 0x14dabd24, 0x0c07e5f8, 0x1b6e34bd, 0x09094c90, 0x00e672c7, 0xfcd5c1}}, + {{0x18e851cb, 0x0a73a101, 0x1918e92d, 0x13645ce2, 0x0e38cb11, 0x06d9afb9, 0x1118edc8, 0x1c5caa45, 0x18ddab}}}, + /* 15*16^35*G: */ + {{{0x1d8ef686, 0x071df182, 0x09cb99af, 0x1c91e804, 0x06e53f68, 0x12ed7c13, 0x0f9488e2, 0x1dcb0879, 0x900f2c}}, + {{0x0121a8cf, 0x19d24b3f, 0x0455b541, 0x19bfe879, 0x1d110596, 0x0a8d89a4, 0x096b5871, 0x0abd8c08, 0x732ac1}}} + }, + { + /* 1*16^36*G: */ + {{{0x1e6b80ef, 0x0794f59e, 0x1e5093cf, 0x17972cfa, 0x0bdc571c, 0x006111de, 0x1b2348d5, 0x01dc6cc5, 0xb6459e}}, + {{0x1a71ba45, 0x185f85b0, 0x18d6cbfc, 0x075cda91, 0x01db3c4b, 0x0f8b72b3, 0x01b7876b, 0x0da0de7c, 0x67c87}}}, + /* 3*16^36*G: */ + {{{0x119888e9, 0x1ce793c9, 0x1122a2d0, 0x0574d7e4, 0x081673d1, 0x069814b3, 0x1b8b7798, 0x0ee75874, 0x1f90ea}}, + {{0x0f113b79, 0x17efe4bf, 0x0548b995, 0x0ea3fdcb, 0x196a8213, 0x09e938f5, 0x043a5605, 0x0f82bb54, 0x89be36}}}, + /* 5*16^36*G: */ + {{{0x1562222c, 0x02f7db79, 0x19bcb182, 0x0688f323, 0x152bade0, 0x15d699a5, 0x02b5b9c0, 0x09bdbffc, 0x13a4e5}}, + {{0x08200145, 0x058b3465, 0x12413023, 0x138aef5b, 0x09a52d4f, 0x017c0eb0, 0x004ecb2b, 0x09cb02dd, 0xc9d67d}}}, + /* 7*16^36*G: */ + {{{0x143b46bb, 0x1bf26e07, 0x12494950, 0x1a74c7f5, 0x15dbd12e, 0x1e02ec22, 0x0b747501, 0x17e46795, 0x61991e}}, + {{0x0c20a848, 0x047ac80e, 0x0bb363bd, 0x10e5394a, 0x1adf11ca, 0x1c38b37d, 0x124a54bc, 0x011e7fbc, 0x1c5e3}}}, + /* 9*16^36*G: */ + {{{0x121add3b, 0x12df1eee, 0x1c9f63df, 0x15289e8a, 0x026118b9, 0x0e6d868b, 0x0e1d240e, 0x1496f0fa, 0xea27ae}}, + {{0x168ce7dd, 0x1af148ed, 0x1386f9c6, 0x0425ad1c, 0x02f6278b, 0x0759192e, 0x1f795c8f, 0x1cdc8542, 0xc70ff1}}}, + /* 11*16^36*G: */ + {{{0x011ff757, 0x1457c5db, 0x13089b9b, 0x19d2e838, 0x0b6da9b4, 0x087c5b71, 0x1552ea40, 0x06ad6fff, 0x594651}}, + {{0x094d031a, 0x05337654, 0x0fff8eca, 0x1778f6ff, 0x006f9961, 0x1c680ae2, 0x1d401080, 0x019cbbe4, 0x361136}}}, + /* 13*16^36*G: */ + {{{0x036160b5, 0x1b12c51b, 0x19faf019, 0x0a7e48e1, 0x11ec0ccc, 0x17bcb804, 0x0a43a10e, 0x0722bee6, 0x16b26e}}, + {{0x18a1dc0e, 0x0312fdfa, 0x0e8fbe05, 0x0c7558e1, 0x054d4e13, 0x01a9231b, 0x1e2ed8d9, 0x0b4c605d, 0x60f56}}}, + /* 15*16^36*G: */ + {{{0x17b32db8, 0x0b269449, 0x11de47ce, 0x1dae93ec, 0x0ea85c91, 0x0a1e4216, 0x1e4c6fa8, 0x12b88ab3, 0x24b52}}, + {{0x0a64f760, 0x0a2a7d55, 0x16e06a56, 0x16d02240, 0x05be862a, 0x1410e62f, 0x0271edb8, 0x11eb7fe6, 0x609fef}}} + }, + { + /* 1*16^37*G: */ + {{{0x096943e8, 0x16d07ada, 0x1cf16977, 0x1e3f8cfc, 0x106231d6, 0x1a5508c7, 0x0101e4c8, 0x19050177, 0xd68a80}}, + {{0x0b133120, 0x0a642133, 0x114a568a, 0x1cf71ef0, 0x0e28b5b0, 0x0fc8bbd8, 0x1b40312c, 0x1ffe96b0, 0xdb8ba9}}}, + /* 3*16^37*G: */ + {{{0x0ca1c4f9, 0x0da1550c, 0x0df8a08d, 0x1dfc6995, 0x116f44f4, 0x1c1ed30f, 0x0a313102, 0x11e457ae, 0x7815f7}}, + {{0x1778bc15, 0x158f51b5, 0x1df47866, 0x085bcc2a, 0x0c35d5cb, 0x1e798a2c, 0x1da9f764, 0x1d19a735, 0xc1c601}}}, + /* 5*16^37*G: */ + {{{0x0e8c8530, 0x09370e1f, 0x03d3639b, 0x1bed03df, 0x06c6d512, 0x1e3200b5, 0x005db8dd, 0x19b41d88, 0xc39273}}, + {{0x198446c7, 0x0018787b, 0x1bb5c571, 0x1bf97b45, 0x1199850e, 0x09ca20e1, 0x123a7407, 0x084ae867, 0x8c41be}}}, + /* 7*16^37*G: */ + {{{0x037a26c1, 0x0f9205d9, 0x16fda94c, 0x18dcb181, 0x03b25166, 0x1218e0e8, 0x06c09d48, 0x08feb082, 0xda3174}}, + {{0x0cf74d6f, 0x08c1b767, 0x0a05497d, 0x106d8baf, 0x1d8b7d36, 0x00b3e12c, 0x11a748e1, 0x170febb1, 0x753b97}}}, + /* 9*16^37*G: */ + {{{0x1739dc49, 0x15b14549, 0x1d5580f4, 0x0725b4cd, 0x1231a239, 0x162845ff, 0x19b04192, 0x196055e1, 0x6a4be6}}, + {{0x12edd5cf, 0x13515cef, 0x1292934f, 0x1c569962, 0x08058ab7, 0x1371b07d, 0x1d65f705, 0x15455120, 0xf15d8f}}}, + /* 11*16^37*G: */ + {{{0x06987fac, 0x05f45062, 0x0a1bd9e6, 0x121f0c81, 0x18a3c8bc, 0x01301f64, 0x1c4d13a6, 0x13e275cf, 0x1f7c6}}, + {{0x19174c68, 0x1c8f39c0, 0x1daf098f, 0x1ebbc433, 0x0b6f9cb7, 0x01b7194d, 0x08b796c4, 0x07e6dfb4, 0x9d4ecc}}}, + /* 13*16^37*G: */ + {{{0x16daba4d, 0x099d0deb, 0x0c658987, 0x0bf61c40, 0x02bcb9c6, 0x1176ec7d, 0x0e072dc1, 0x002ec3fa, 0x557e94}}, + {{0x17a52316, 0x09ba50dd, 0x10d64294, 0x1371ebf4, 0x0bc86c5e, 0x0e7d0ae6, 0x1f811b4d, 0x074c03f4, 0x7a7e8f}}}, + /* 15*16^37*G: */ + {{{0x09f5341a, 0x1c7a97f9, 0x058dae00, 0x0f4658cd, 0x101419cc, 0x0a0753d8, 0x16a4eca7, 0x10433310, 0x3adada}}, + {{0x0586c6cc, 0x08ac049b, 0x17a3ef2d, 0x0da5cb68, 0x11017e9d, 0x1adf9b55, 0x1a7d54b8, 0x04513326, 0xbfea1e}}} + }, + { + /* 1*16^38*G: */ + {{{0x028d3d5d, 0x04acc07e, 0x11273a90, 0x055d72e6, 0x030b0961, 0x0138483d, 0x01094b70, 0x0fbecb90, 0x324aed}}, + {{0x16ab7c84, 0x1391257c, 0x0cca10e5, 0x027618fc, 0x01f4f192, 0x0061ad76, 0x1cbfc4c3, 0x0aee96c3, 0x648a36}}}, + /* 3*16^38*G: */ + {{{0x0fd53ed3, 0x0e48bac1, 0x095b33bd, 0x1ee9f73b, 0x17c49163, 0x105c98ef, 0x0ab56e3d, 0x1ab32cee, 0x20840b}}, + {{0x1a7a7132, 0x18a1ff28, 0x19c22661, 0x0f88e729, 0x0fac2548, 0x0a3b535d, 0x090d21ef, 0x12f9d830, 0xf29934}}}, + /* 5*16^38*G: */ + {{{0x08a35b35, 0x1e965dac, 0x028487b6, 0x0bb114b8, 0x0ebfd1ab, 0x0814f2c4, 0x06eef44f, 0x1ec1d667, 0xe6b6bf}}, + {{0x1c1007bd, 0x0b949edc, 0x1a6671f1, 0x16d93a77, 0x161ddfe3, 0x01f1c1ac, 0x0bcc99bd, 0x17a6601a, 0x1a5ff2}}}, + /* 7*16^38*G: */ + {{{0x00360dd3, 0x0a77c696, 0x14388243, 0x11506db0, 0x0e3bb47a, 0x1c043706, 0x06ca22c2, 0x0e8b7c93, 0xe05317}}, + {{0x1c24f87b, 0x15766c89, 0x040f70ac, 0x130fbd30, 0x0a01461b, 0x0ac15adb, 0x1ce73602, 0x0e34bb25, 0xdc1c3b}}}, + /* 9*16^38*G: */ + {{{0x1098dfea, 0x00a33316, 0x099e1e5f, 0x1967925d, 0x05e57fad, 0x12e9541d, 0x11678063, 0x074ef10d, 0xa8153b}}, + {{0x0e6d892f, 0x124d4efb, 0x16bd0562, 0x0bc1ee85, 0x13e03b1b, 0x0dce2bc2, 0x03f14f63, 0x0c8c3a0c, 0x2a4739}}}, + /* 11*16^38*G: */ + {{{0x0bcecfa5, 0x13f3bd24, 0x05aec082, 0x1ac5b436, 0x0da3b2a0, 0x14ae31c7, 0x176b7ad1, 0x1661fd95, 0x4f05c3}}, + {{0x15d37b53, 0x16681254, 0x06d2334b, 0x1dc863e0, 0x134b9447, 0x191b0aca, 0x09beb758, 0x1d4c07a8, 0x53a499}}}, + /* 13*16^38*G: */ + {{{0x084b96aa, 0x10f3b2c8, 0x0aaf3391, 0x130c6aa4, 0x1980ed02, 0x02f0d51d, 0x046f8990, 0x1733ecf5, 0xd9309a}}, + {{0x02b28a86, 0x136279be, 0x13fa5e3c, 0x0d93f75f, 0x0cb5fd3b, 0x0783313a, 0x155f5f84, 0x055369d8, 0x6ef99b}}}, + /* 15*16^38*G: */ + {{{0x1f8fcf0e, 0x1bdc682c, 0x1129beb3, 0x16dffbcf, 0x03411d65, 0x1a236f55, 0x14d6ea70, 0x14270ac5, 0x7d587c}}, + {{0x18bc9459, 0x00e0d04e, 0x08de0294, 0x072015a6, 0x16ad0c46, 0x0005b67e, 0x11849c8d, 0x00710609, 0xa7295c}}} + }, + { + /* 1*16^39*G: */ + {{{0x1d054c96, 0x145e9b9f, 0x1472a223, 0x08287751, 0x0e5dceec, 0x0fedf2ff, 0x187db547, 0x092339bc, 0x4df9c1}}, + {{0x0ad10d5d, 0x175d6036, 0x02124064, 0x0a0d9b85, 0x185d4b5d, 0x1a611d0e, 0x1ca01425, 0x0a2125b0, 0x35ec}}}, + /* 3*16^39*G: */ + {{{0x1def001d, 0x07912ed2, 0x06e89fbd, 0x1377ad31, 0x1b64b21f, 0x1e8e04f1, 0x12bc8382, 0x05b64fc5, 0xa549a3}}, + {{0x10624783, 0x0aed8d3f, 0x125c16b7, 0x083c54c5, 0x19456eb1, 0x01876c52, 0x1b2f7d33, 0x0f20db2c, 0x799b7a}}}, + /* 5*16^39*G: */ + {{{0x052ed4cb, 0x0d778f2e, 0x027b1681, 0x09bdf678, 0x12e6ec95, 0x05ecc1a9, 0x13448fc2, 0x061b40fd, 0x7e798f}}, + {{0x14bb9462, 0x0b8b303c, 0x07cde849, 0x06ae37ff, 0x0f057b17, 0x0aa4ef79, 0x120c106a, 0x189449b5, 0xd23dcc}}}, + /* 7*16^39*G: */ + {{{0x13630834, 0x0f9d07de, 0x16b019ff, 0x07e250c7, 0x08108846, 0x0f4b5d46, 0x1e0eb56e, 0x00062a28, 0x224fa2}}, + {{0x047a2272, 0x09e239be, 0x0943dd73, 0x05249d81, 0x109f53d6, 0x187d1c8e, 0x0970f12d, 0x065767db, 0xbbe54e}}}, + /* 9*16^39*G: */ + {{{0x183c19d7, 0x13b24e8a, 0x0b3d5eed, 0x16bc8b58, 0x08ef2bbb, 0x12e67211, 0x07904a68, 0x198c0147, 0xc2d4a0}}, + {{0x0507928d, 0x17945c16, 0x0d1725dc, 0x0095062e, 0x1260d268, 0x1dafbfa0, 0x0a535060, 0x1f38100c, 0x65ada0}}}, + /* 11*16^39*G: */ + {{{0x1c940c9a, 0x064056a5, 0x1d08cc21, 0x1e79c275, 0x1dc2113c, 0x02f13a26, 0x0c643956, 0x0fd860be, 0x2ec22a}}, + {{0x051e7a4d, 0x08ca7ecc, 0x08d6f6b3, 0x00a307b3, 0x07feb124, 0x127a814e, 0x05a130b8, 0x0d1bc66f, 0x8b1da4}}}, + /* 13*16^39*G: */ + {{{0x1d683eeb, 0x138772fe, 0x034c4cea, 0x0dd67141, 0x0b8f33e3, 0x1b292842, 0x13b2ac6b, 0x0e71f351, 0xafc669}}, + {{0x10cd4509, 0x0f14e559, 0x1b77f724, 0x1756aa4b, 0x19c16570, 0x0e3fe511, 0x1d4af0d6, 0x12edba44, 0x2c21}}}, + /* 15*16^39*G: */ + {{{0x176f4293, 0x1100fc3d, 0x0f144e7a, 0x12f16aca, 0x1282e10e, 0x04679b85, 0x0c24486f, 0x0b53e686, 0xd2557b}}, + {{0x1282740a, 0x0d8c3d12, 0x101697c7, 0x16d071bc, 0x0d21fe34, 0x178375a5, 0x1fc049a0, 0x086abc84, 0xa787b3}}} + }, + { + /* 1*16^40*G: */ + {{{0x0c1f98cd, 0x1fe4ce45, 0x1fc0c232, 0x09120a9a, 0x06021523, 0x054e0e63, 0x01c3ebb6, 0x150948e9, 0x9c3919}}, + {{0x14fc599d, 0x13f2f01e, 0x193239af, 0x064deed8, 0x0e641905, 0x0225f930, 0x155d613c, 0x01e949bb, 0xddb84f}}}, + /* 3*16^40*G: */ + {{{0x0fb64db3, 0x1dcc6a9c, 0x1754e105, 0x1bc99473, 0x1b8d6a7e, 0x1c1fdf29, 0x12dd02ee, 0x124537b9, 0xc11423}}, + {{0x1c0259be, 0x118674ff, 0x1159f478, 0x0b01209a, 0x18bd1a87, 0x06f27f4b, 0x1f0a973b, 0x1b8b690d, 0x1237f6}}}, + /* 5*16^40*G: */ + {{{0x03081e46, 0x16f6c1a0, 0x11567a87, 0x044318aa, 0x034713a5, 0x0e160c93, 0x089020b6, 0x1f0634ee, 0x6c5b4b}}, + {{0x0bfbcd70, 0x08fce5c0, 0x108a98bb, 0x019f04d5, 0x1e47841d, 0x1c31e715, 0x10bec8d1, 0x0e2924da, 0xcb0513}}}, + /* 7*16^40*G: */ + {{{0x064dcd4b, 0x0572d762, 0x04704937, 0x018fab32, 0x10a450c3, 0x0332e558, 0x1792d59c, 0x0acce195, 0xe1e9a8}}, + {{0x1b041f2c, 0x085b12f5, 0x085aca4b, 0x09a33559, 0x177927f4, 0x01accd92, 0x14c6deb1, 0x12a88ab8, 0x562b0a}}}, + /* 9*16^40*G: */ + {{{0x0badd73c, 0x02c3b7f1, 0x0992df40, 0x139bb205, 0x014208fd, 0x1a72176e, 0x0265de29, 0x0af5a236, 0x51b21a}}, + {{0x0b36d8d1, 0x1bea570f, 0x11cd2e9b, 0x00261e51, 0x01cfa6c2, 0x03f80e96, 0x0f975528, 0x020003fa, 0x7930}}}, + /* 11*16^40*G: */ + {{{0x1b09f34b, 0x0bae85b9, 0x1319b39b, 0x10e7cc11, 0x19d61e58, 0x114b79f9, 0x1e6186ad, 0x14c76396, 0x9701f3}}, + {{0x00df5793, 0x06e42866, 0x1731e52b, 0x097872ff, 0x08337710, 0x18da98ab, 0x1b4575c0, 0x177195e1, 0x3dd44b}}}, + /* 13*16^40*G: */ + {{{0x1f1e2f46, 0x0e73111d, 0x09de0c05, 0x01ee3d0e, 0x03c57527, 0x0970206b, 0x1b311156, 0x03a593cc, 0xa036b4}}, + {{0x1effb349, 0x198f134a, 0x1c2c7d3d, 0x01c5059f, 0x0b08d068, 0x1b5523cf, 0x0cf5f7c7, 0x14007d2d, 0xc3bf91}}}, + /* 15*16^40*G: */ + {{{0x0c4cea08, 0x06c5c81c, 0x03a8876f, 0x16b1741c, 0x04652654, 0x108a9a00, 0x1141bd29, 0x1b7549d1, 0x6a85fa}}, + {{0x1862f4f3, 0x0cef672c, 0x15c86da8, 0x0e349687, 0x06230b42, 0x19e0a47f, 0x16754c64, 0x00975c8c, 0xb646}}} + }, + { + /* 1*16^41*G: */ + {{{0x00a959e5, 0x1109c109, 0x04753316, 0x02927517, 0x006bb91e, 0x0f940ec7, 0x1f7e3781, 0x0163ba25, 0x605717}}, + {{0x0385a2a8, 0x04cdf499, 0x1893197a, 0x02a5787d, 0x1f262465, 0x116d7b8e, 0x001eb766, 0x164d4d49, 0x9a1af0}}}, + /* 3*16^41*G: */ + {{{0x171c032b, 0x0a6d0b14, 0x0bf72603, 0x16cd142f, 0x166c5ff6, 0x0dafefe3, 0x0980f744, 0x1f9adc00, 0x71eba8}}, + {{0x1668359f, 0x1d5ad470, 0x12d1d579, 0x0635a2ee, 0x0bb7f719, 0x028b7aa6, 0x0e77bd98, 0x0c496c3a, 0xd2ff12}}}, + /* 5*16^41*G: */ + {{{0x0a03a61c, 0x03723a29, 0x01c15d34, 0x10d1e8d2, 0x09dd0507, 0x1a215d55, 0x148cb285, 0x00b66493, 0x855ec3}}, + {{0x065dfc07, 0x0fe37556, 0x1f912597, 0x05ee9d42, 0x0fb4ed33, 0x05ffcda1, 0x105fd50f, 0x05d8be03, 0xdd85d}}}, + /* 7*16^41*G: */ + {{{0x1f32d706, 0x00605240, 0x1a819e2c, 0x119948e8, 0x1bfa2061, 0x094d184a, 0x0fc7c543, 0x0d57567f, 0x3ce448}}, + {{0x1c7fd9e4, 0x05b9b1bf, 0x187a27d0, 0x02ac879a, 0x14906edd, 0x08235884, 0x014a23bf, 0x11b55c6f, 0xe77540}}}, + /* 9*16^41*G: */ + {{{0x191cd3fb, 0x0da065db, 0x0a6f9a1b, 0x1467fb2e, 0x044eb4a2, 0x0190c7c4, 0x1febc0b8, 0x0287e9c6, 0x11ccc5}}, + {{0x15160d86, 0x09b8b5d2, 0x174d1caa, 0x163dfa59, 0x0c239fa0, 0x112249c6, 0x077ad4a3, 0x05520562, 0x4aa56b}}}, + /* 11*16^41*G: */ + {{{0x018f7552, 0x03dc88cb, 0x0153eb0e, 0x02271730, 0x182ddbd4, 0x1bba7c11, 0x11bd0ee5, 0x02fca293, 0x250bb}}, + {{0x1510b14d, 0x18424b11, 0x0f5bc78f, 0x00de7866, 0x1d817da0, 0x1efbaff4, 0x0208d0b5, 0x1f9377d0, 0x731930}}}, + /* 13*16^41*G: */ + {{{0x1f725d12, 0x0e89f49c, 0x0d7d1d41, 0x0c8577b9, 0x02fbfd94, 0x1ce70501, 0x1f4ead28, 0x111668cf, 0x1a749c}}, + {{0x03ac56e4, 0x09b28a69, 0x0436a9c0, 0x0410d313, 0x13d8f607, 0x1f3ae157, 0x18b3d162, 0x12ae7d81, 0x7e91d1}}}, + /* 15*16^41*G: */ + {{{0x09fae458, 0x10824729, 0x1bb25ff5, 0x14b884ec, 0x17b328b0, 0x0ab52efd, 0x06304274, 0x0b7c1f04, 0xc75068}}, + {{0x1757b598, 0x00b420ca, 0x165468ac, 0x1b94a066, 0x0c7b40a5, 0x1a0a6339, 0x1817ed4b, 0x1f19f243, 0xead795}}} + }, + { + /* 1*16^42*G: */ + {{{0x0cb94266, 0x0d34b9f7, 0x1537c4ac, 0x1de1f74f, 0x1a31880c, 0x1cd228c6, 0x10450850, 0x11c47410, 0xa576df}}, + {{0x01b28ec8, 0x145f08d7, 0x05367cfb, 0x1c214fea, 0x0d82c432, 0x0bd7f2c6, 0x02cb24ae, 0x041cecc8, 0x40a6bf}}}, + /* 3*16^42*G: */ + {{{0x0d9ed6c1, 0x14575ac6, 0x1564f5ad, 0x1ce8b787, 0x0dd0ec24, 0x00c3b82f, 0x14fa02ff, 0x0db96e9e, 0x32833}}, + {{0x18fafeee, 0x16375f37, 0x12d252b7, 0x17e9be4b, 0x17c8c265, 0x0ca1d106, 0x1ca311b5, 0x07025fb3, 0x71a898}}}, + /* 5*16^42*G: */ + {{{0x1235983a, 0x0cd4d469, 0x0ef3aca4, 0x14206e02, 0x01531e38, 0x0936b87f, 0x1153718e, 0x15d17223, 0xce4f4e}}, + {{0x0d3cdecf, 0x07eb58c8, 0x0fdd02bb, 0x18ca451d, 0x07543526, 0x10124f38, 0x0eecfab7, 0x0e78721f, 0xf3c9f9}}}, + /* 7*16^42*G: */ + {{{0x15b0e6c9, 0x16d55b32, 0x1b932269, 0x1ff39ef0, 0x0bcbddb5, 0x07d9b6fc, 0x0889e38a, 0x14a9730c, 0x4dbebf}}, + {{0x1eb2cc25, 0x0a53c2aa, 0x1413beba, 0x06236578, 0x029f3589, 0x11373711, 0x0bb7d169, 0x16079227, 0x10fee7}}}, + /* 9*16^42*G: */ + {{{0x05857295, 0x0700d08d, 0x10cfc059, 0x11c8fe06, 0x0a12069c, 0x08c7e50e, 0x10862cb8, 0x017fde8b, 0xa42a24}}, + {{0x0a7eb9c1, 0x159bbff6, 0x1464e555, 0x038459a2, 0x1a4c427a, 0x1915926e, 0x15159e9a, 0x1e4c200b, 0x3aa0b3}}}, + /* 11*16^42*G: */ + {{{0x0fcdc098, 0x1107faab, 0x191a00c8, 0x15c01ed5, 0x099c1550, 0x0fc36062, 0x0899e9fc, 0x05f2df64, 0x34e12b}}, + {{0x0a7474e2, 0x0658d6f3, 0x0620fd99, 0x1ea261e3, 0x172db04d, 0x05e420bc, 0x0c8b65d3, 0x1bbaf6ba, 0xa64ac2}}}, + /* 13*16^42*G: */ + {{{0x0f173b92, 0x06b75af4, 0x07edd847, 0x1ce5e82d, 0x165683b7, 0x0d10c7a6, 0x07ca6f8c, 0x081b3772, 0x10f4d2}}, + {{0x033146c2, 0x0810036b, 0x01ab6df2, 0x16ed3a29, 0x108ba90b, 0x12d2d19c, 0x0eb4846c, 0x12a122ea, 0x850e2d}}}, + /* 15*16^42*G: */ + {{{0x08d84958, 0x137e8ecd, 0x0b3172bb, 0x03bd62d9, 0x0cc866a1, 0x0dcbb6a0, 0x1f9d27c6, 0x016d36ce, 0xe846e8}}, + {{0x1882cf9f, 0x062323db, 0x18306990, 0x03466ce3, 0x0b76fad5, 0x0c8823cc, 0x1895076f, 0x1f91298f, 0xa29cb8}}} + }, + { + /* 1*16^43*G: */ + {{{0x1e58ad71, 0x1bb1c44d, 0x068e8823, 0x01a3eb9f, 0x08c38bb3, 0x1f4b14ef, 0x0f8c2817, 0x11851bd8, 0x7778a7}}, + {{0x1d9f43ac, 0x1a89fe0f, 0x092b158e, 0x070823fe, 0x1580087b, 0x0709797f, 0x08bfdc26, 0x1356b4b6, 0x34626d}}}, + /* 3*16^43*G: */ + {{{0x1319c869, 0x1be37571, 0x07ac9c0b, 0x13f2baec, 0x0acdc18a, 0x1c8117e6, 0x1f234060, 0x0bb302e7, 0x301804}}, + {{0x12b856f0, 0x0063b64e, 0x0f669eff, 0x15099494, 0x02e3bca2, 0x121906b1, 0x1edbe198, 0x0f04a076, 0xac5fc5}}}, + /* 5*16^43*G: */ + {{{0x05ed29b5, 0x0334c37d, 0x0ab746d3, 0x0616c0e2, 0x1c885f58, 0x13edcd31, 0x1bcd0ead, 0x16c3dcaf, 0x322881}}, + {{0x1cd15ad2, 0x1a789373, 0x1b9d813b, 0x075d5729, 0x131e1ca8, 0x18cefa0a, 0x113ac442, 0x1082f406, 0x167702}}}, + /* 7*16^43*G: */ + {{{0x06c96100, 0x1fd4203c, 0x1a72398e, 0x0602354d, 0x1dbcca16, 0x0ecd96f9, 0x0e7fed0f, 0x07581f63, 0x3f3847}}, + {{0x12624707, 0x0f1560df, 0x1d7c6f3c, 0x0e38f816, 0x19ce5665, 0x02231783, 0x0e5494d3, 0x0abeba80, 0x70c69c}}}, + /* 9*16^43*G: */ + {{{0x1d22d2ac, 0x1a637637, 0x12ab3808, 0x1bfc24db, 0x1df2f10d, 0x00704bc2, 0x1db72d0f, 0x18bfa4f4, 0x288113}}, + {{0x0f42a268, 0x00f5aafc, 0x12323f42, 0x07a8942a, 0x16137ddc, 0x1b93064b, 0x1723c81d, 0x002b1f78, 0xa1a7eb}}}, + /* 11*16^43*G: */ + {{{0x045f8ea8, 0x05d406e9, 0x134a4035, 0x0491c72c, 0x19fe5d17, 0x06caeb88, 0x08a954fd, 0x001908c7, 0xf963a2}}, + {{0x19bc99eb, 0x1e2afd82, 0x02d82092, 0x11e46b3b, 0x027208bb, 0x11180ffa, 0x0f028edc, 0x04d18ff0, 0x9c8594}}}, + /* 13*16^43*G: */ + {{{0x0606a315, 0x10d44189, 0x1a58eb67, 0x04c0e5e4, 0x0097e407, 0x05952c87, 0x069fe636, 0x099fee1b, 0xa5d922}}, + {{0x1e3b68d1, 0x1ab099ac, 0x0469f274, 0x1a1a68fa, 0x00de9ed4, 0x0355ebcc, 0x096cd0cc, 0x0007641b, 0x87328b}}}, + /* 15*16^43*G: */ + {{{0x06231493, 0x06dbdaa0, 0x131351a7, 0x02350619, 0x1e6a4964, 0x120e8072, 0x0d813ad3, 0x05c36e78, 0xf1fe98}}, + {{0x158848c1, 0x0b54cd33, 0x17fc3406, 0x07f668dc, 0x199d3f17, 0x1e102fbe, 0x177085b4, 0x1d5db349, 0x2e2019}}} + }, + { + /* 1*16^44*G: */ + {{{0x06d903ac, 0x04f6d4e0, 0x0b5f972c, 0x12c4e9cb, 0x0fd2ed5f, 0x0fe9873d, 0x01118dca, 0x0bdcc6f5, 0x92895}}, + {{0x1bcd091f, 0x08c0749a, 0x0a360ff1, 0x1a4ddf51, 0x095eeac3, 0x0509849d, 0x0aa09ede, 0x0007a7e8, 0xc25621}}}, + /* 3*16^44*G: */ + {{{0x1874b839, 0x088943ab, 0x0f4ad060, 0x022b672a, 0x0b6aebe4, 0x186fd918, 0x16a014f7, 0x03f81c3c, 0x3e03b8}}, + {{0x1c0594ba, 0x060e72b3, 0x0ad6e368, 0x0b8be1fb, 0x18f667de, 0x1303ab8c, 0x1d0b113d, 0x0c7bfe0f, 0xd13ae1}}}, + /* 5*16^44*G: */ + {{{0x0357a513, 0x11fbc734, 0x0cc08fce, 0x037a268b, 0x122c5f15, 0x1141d514, 0x04b358be, 0x16f45e89, 0xe662c0}}, + {{0x0017d07e, 0x095100e5, 0x14e36246, 0x06b9ac4a, 0x1a419d80, 0x11045090, 0x148c176b, 0x079cc248, 0xab0b19}}}, + /* 7*16^44*G: */ + {{{0x1f37d242, 0x0cafbf7e, 0x07052c12, 0x1fd94c0f, 0x1587dc29, 0x1163e5f1, 0x1b2e10e1, 0x1639299e, 0x40bf80}}, + {{0x06405088, 0x08ec13cd, 0x0f4d560f, 0x043d7485, 0x0fe12743, 0x1f4d8d93, 0x0bc13d4f, 0x06bb0ad5, 0xb579dd}}}, + /* 9*16^44*G: */ + {{{0x195a3558, 0x17959b22, 0x0d29fcae, 0x0e3f0bc4, 0x159f6ac0, 0x0bc09c6d, 0x09c201be, 0x12ec03b9, 0x3d14fe}}, + {{0x0443df4c, 0x156d9d63, 0x1075c9f1, 0x0145c28f, 0x16e1482e, 0x1498edfa, 0x07be3ca6, 0x1add08d0, 0x16c6bd}}}, + /* 11*16^44*G: */ + {{{0x02b1fc24, 0x0f4fad6c, 0x0fdd5c3b, 0x11b038fc, 0x04865252, 0x16269649, 0x14947306, 0x081d05cc, 0xdd6fa5}}, + {{0x0e9b74ca, 0x1218e230, 0x1cc88c12, 0x01bcd7da, 0x17e77ec1, 0x18f5f8b2, 0x01bf8d9b, 0x0fd63a63, 0x67e62b}}}, + /* 13*16^44*G: */ + {{{0x1dd08e02, 0x0f548ac9, 0x0b7c0a20, 0x0a8f6ffb, 0x11e80108, 0x0a4cd51e, 0x15e03e1a, 0x1505bcab, 0x13fa2d}}, + {{0x1cb03410, 0x12aa0ee1, 0x090ae5f6, 0x095f7633, 0x032c7e64, 0x0b1035da, 0x09c8c4cd, 0x1608aabb, 0x136338}}}, + /* 15*16^44*G: */ + {{{0x144ee41a, 0x0119d5cc, 0x1f5a69ab, 0x16adba76, 0x08282879, 0x085b3963, 0x0910fdf0, 0x0a3a78e1, 0xd06c48}}, + {{0x0b295e6f, 0x18fc274c, 0x18bb894b, 0x170868c2, 0x030919b7, 0x166a7a7b, 0x02b6eec2, 0x0980b09a, 0x5815fd}}} + }, + { + /* 1*16^45*G: */ + {{{0x03d82751, 0x1d573a8b, 0x0b4d5149, 0x0b69520f, 0x1b285564, 0x1279d071, 0x0424e641, 0x1e7d8db6, 0x85d0fe}}, + {{0x0eb1f962, 0x1611bd12, 0x1dccc560, 0x0ea3d2d0, 0x0f5663e8, 0x04b72c16, 0x102f8a75, 0x10827471, 0x1f0364}}}, + /* 3*16^45*G: */ + {{{0x0cde4cf3, 0x0caa830f, 0x02819aae, 0x01ca6a8f, 0x19ae7934, 0x169368ae, 0x0b0ef9f0, 0x09582284, 0x384dab}}, + {{0x052d0566, 0x1e3cb591, 0x146e9ced, 0x0614672e, 0x0c6f01f4, 0x16b6d15a, 0x090efed3, 0x179a3739, 0xd6e3c5}}}, + /* 5*16^45*G: */ + {{{0x1e5238c2, 0x0579f490, 0x03b2e2e6, 0x0abeb870, 0x0ed48403, 0x1085a741, 0x16a906c5, 0x01d6fa82, 0x14f0ec}}, + {{0x12f07922, 0x14351a3c, 0x0124e75b, 0x1801b006, 0x0747fd25, 0x039f1c21, 0x1602487f, 0x07ba906b, 0xab12d5}}}, + /* 7*16^45*G: */ + {{{0x1543e94d, 0x1b1a977e, 0x1e638623, 0x06054ead, 0x00ddadd1, 0x1a33c52d, 0x01fb1070, 0x176f0585, 0xeb42f3}}, + {{0x05924d89, 0x02acef22, 0x035b5090, 0x108d1bcc, 0x1fb774cd, 0x0eab97e6, 0x04b72683, 0x00e9e4bb, 0x234a6d}}}, + /* 9*16^45*G: */ + {{{0x1e19aaed, 0x19272dab, 0x199cc9c0, 0x1759bd18, 0x0a920459, 0x0017b703, 0x0366a7bb, 0x194a2d04, 0x1cf138}}, + {{0x092f400e, 0x09b752eb, 0x11dffef0, 0x1ddf1fdf, 0x1de17479, 0x195335b6, 0x0e197d0d, 0x1e62e38c, 0xd6ffda}}}, + /* 11*16^45*G: */ + {{{0x16a8aa39, 0x1b6074fd, 0x1e3eb157, 0x0cc6f694, 0x190d937a, 0x104b424c, 0x104b21d6, 0x17cbe81a, 0xb58686}}, + {{0x0b493c1f, 0x1e3c9ae9, 0x16cd1ee3, 0x1b5f31cd, 0x0a91dabb, 0x1c6a2a60, 0x10b05251, 0x086498f1, 0x5632d5}}}, + /* 13*16^45*G: */ + {{{0x103b4cc5, 0x148f5f1d, 0x071df0bb, 0x106374b4, 0x1a802572, 0x1e27f3f9, 0x10ad9ed6, 0x160d7179, 0x5fc19d}}, + {{0x05b57c28, 0x1d9cfdc3, 0x021fb128, 0x0dea0798, 0x05ef4927, 0x09c7cd1d, 0x1f19bb88, 0x181d9318, 0xec8e84}}}, + /* 15*16^45*G: */ + {{{0x0cb38cb5, 0x1a5c2bea, 0x0e22522e, 0x16ffbe9a, 0x0ea1be10, 0x05207e9f, 0x0a277aea, 0x01a85dbc, 0xb88fb7}}, + {{0x1965f3d7, 0x1dfd3ab2, 0x0be31c65, 0x1e7c244f, 0x1a8e24d4, 0x1dcca59a, 0x0a0180d2, 0x15a8dd46, 0xd6c736}}} + }, + { + /* 1*16^46*G: */ + {{{0x0526087e, 0x1aa02412, 0x16880c23, 0x16db1105, 0x0b85dfdf, 0x1b020bcc, 0x1a5f0726, 0x19d2fdd9, 0xff2b0d}}, + {{0x10c29907, 0x04a8f00f, 0x038b3acb, 0x0fdadf72, 0x07936c7b, 0x026e2a68, 0x08622bd3, 0x1fdea497, 0x493d13}}}, + /* 3*16^46*G: */ + {{{0x19d681f9, 0x0c82a7f3, 0x03fae7f1, 0x1c1ddf59, 0x094b066c, 0x1f92f016, 0x0c2222df, 0x1e4eebe4, 0xc745fd}}, + {{0x1bbb1247, 0x018b9a1b, 0x1f5171d8, 0x17a66b8c, 0x018678cd, 0x1ca63874, 0x179e29c4, 0x1e5ed73c, 0x590222}}}, + /* 5*16^46*G: */ + {{{0x15cd0ea3, 0x10267769, 0x12b12057, 0x08f1d041, 0x0e7f2b34, 0x0f2f5b39, 0x142c9e96, 0x1e752ea0, 0xabb279}}, + {{0x1c307bce, 0x1849899b, 0x00bced91, 0x0ed20b3c, 0x18ed47c9, 0x1f060183, 0x1c367ed2, 0x0777e2f2, 0x5dee10}}}, + /* 7*16^46*G: */ + {{{0x1bc9ee3e, 0x017179f8, 0x19ce0b17, 0x1f4352c7, 0x1ed11ea9, 0x1553a133, 0x00a09feb, 0x016b3f8d, 0x3f8115}}, + {{0x199aae06, 0x0756d862, 0x16a0580f, 0x0765b9f9, 0x15662762, 0x1f59e23c, 0x00b519c6, 0x0d1fb7f5, 0x19c88a}}}, + /* 9*16^46*G: */ + {{{0x18e4a007, 0x0b8df7d7, 0x0ecd62d8, 0x19dd9e11, 0x0c7ec15e, 0x1d19d52b, 0x179a5652, 0x05ba0105, 0x5cf813}}, + {{0x1068b883, 0x131f8484, 0x071ffa33, 0x08df2d8f, 0x03df6c89, 0x00ac4246, 0x0837d2b5, 0x0b81ac3f, 0xb45aee}}}, + /* 11*16^46*G: */ + {{{0x06c2d4a7, 0x0ab7f3d9, 0x13ffec42, 0x06df2677, 0x04ed21bc, 0x19cb9e20, 0x125194f8, 0x09a1a974, 0xb6d5fe}}, + {{0x1ae86371, 0x0c6e73f1, 0x178f3204, 0x16fc9cde, 0x1fd8e745, 0x1c904eff, 0x1b0537f3, 0x1427577a, 0x47f373}}}, + /* 13*16^46*G: */ + {{{0x1c66dd33, 0x0499b117, 0x171db714, 0x1f791fe3, 0x1b022ea8, 0x0d8a8014, 0x021c1aec, 0x180cd9eb, 0x61c8bb}}, + {{0x16f10bfa, 0x1ddd4f9d, 0x00832328, 0x020dd585, 0x1d3fb6a5, 0x0cca5cc2, 0x1c0d119a, 0x0473ca9e, 0x93599e}}}, + /* 15*16^46*G: */ + {{{0x1d6b2ff8, 0x002dbe66, 0x01b23ea6, 0x066d82e5, 0x1bdf1876, 0x1a9b9f61, 0x01461f27, 0x14ae84cf, 0x94e32b}}, + {{0x0ce1af3e, 0x0ea42aa9, 0x1aff84eb, 0x15e084a4, 0x19e8cb33, 0x12443316, 0x13864bc7, 0x11687b40, 0xd1b44}}} + }, + { + /* 1*16^47*G: */ + {{{0x1856e241, 0x0072f167, 0x15b74a1e, 0x03dc2919, 0x1212b57f, 0x1973180d, 0x03aa7b4a, 0x1c963d10, 0x827fbb}}, + {{0x0ec293ec, 0x102db45d, 0x040c59b5, 0x0f4c630d, 0x112687ff, 0x19633e8e, 0x0c2dc6fb, 0x12478e4f, 0xc60f9c}}}, + /* 3*16^47*G: */ + {{{0x1bb80fa7, 0x1a242e59, 0x0104e218, 0x0fb4d76e, 0x0819f3aa, 0x1035e990, 0x0bef0346, 0x03ec6118, 0x857e3}}, + {{0x09366b2d, 0x0cc108f8, 0x15c05aaf, 0x1c6e0879, 0x17147172, 0x064e8ee5, 0x1c824b5f, 0x08475c02, 0xf64393}}}, + /* 5*16^47*G: */ + {{{0x09c70e63, 0x0e8161d0, 0x14f525bd, 0x1716f1ce, 0x0672e9cb, 0x032abb25, 0x0010d517, 0x1d4ad7ac, 0x28aacc}}, + {{0x1057da4e, 0x0f81c417, 0x13687a2e, 0x18b39c88, 0x0ebb7f5f, 0x0b33e3b4, 0x18559ea2, 0x05df0341, 0x2b6932}}}, + /* 7*16^47*G: */ + {{{0x13e674b5, 0x00ee297b, 0x0182ab18, 0x11ed39ce, 0x18a4f92d, 0x1964de75, 0x19851776, 0x04b40ab4, 0xa2f3b6}}, + {{0x0e937941, 0x049c6470, 0x0cfe94ec, 0x05f462f8, 0x07c4b922, 0x05487995, 0x02ba0011, 0x0b2c298d, 0x620ea1}}}, + /* 9*16^47*G: */ + {{{0x191eb056, 0x1b00b18e, 0x13f4e1b1, 0x05dfb71d, 0x115f5a00, 0x1ae351fa, 0x048c7662, 0x193d55cb, 0x3c4f83}}, + {{0x005cecab, 0x012c49ed, 0x13dae1dd, 0x056a8903, 0x07880198, 0x12b9e1d9, 0x0da8ceb5, 0x00ea2951, 0x944790}}}, + /* 11*16^47*G: */ + {{{0x1d86dfa9, 0x0830fedd, 0x0e64e9c6, 0x11694813, 0x03baadc3, 0x0f01f408, 0x1f538a70, 0x0511532c, 0xaff8e1}}, + {{0x01d12681, 0x1881e1b6, 0x067e71c1, 0x02db5288, 0x153f4f91, 0x15d50fe7, 0x10ff4f4f, 0x166426ef, 0x8d8b4b}}}, + /* 13*16^47*G: */ + {{{0x189ba9c1, 0x07939b5c, 0x074ce38a, 0x1ef94b41, 0x0e579e40, 0x01315767, 0x02cfa116, 0x08a51b80, 0xd3fb78}}, + {{0x0b51b267, 0x10dc46ff, 0x046b7801, 0x19dbab80, 0x10fe6341, 0x102bac5b, 0x139f29f2, 0x069df4d6, 0xf894d4}}}, + /* 15*16^47*G: */ + {{{0x0f2bb909, 0x0d4b60e8, 0x16636667, 0x0204f8a6, 0x07d7f639, 0x14c41c8c, 0x0a23fd1c, 0x01c15935, 0x4ec930}}, + {{0x04cf4071, 0x0451c1fd, 0x0b0e09ee, 0x1c2d041b, 0x049bad52, 0x0e228c26, 0x13717203, 0x00d7c360, 0x782ba1}}} + }, + { + /* 1*16^48*G: */ + {{{0x0120e2b3, 0x19dac7d1, 0x11fe6a9f, 0x11fb9cfe, 0x0e5217a5, 0x0571a673, 0x16eb9ef9, 0x1e43ea37, 0xeaa649}}, + {{0x1a5ad93d, 0x03d2982d, 0x0fdf9675, 0x0d72cbe2, 0x1aa5a01a, 0x007c4c3c, 0x00eb1a6a, 0x1dab7776, 0xbe3279}}}, + /* 3*16^48*G: */ + {{{0x1f2e070d, 0x0c1fe9d1, 0x0a9aa63d, 0x156e398a, 0x047e229a, 0x18e1dc28, 0x0affd21c, 0x1d2085e9, 0x4b72a5}}, + {{0x096dd780, 0x025d4177, 0x05230f79, 0x08cbbba5, 0x13c10b0b, 0x1dd9b687, 0x073d809d, 0x09c3ad5c, 0x599e1d}}}, + /* 5*16^48*G: */ + {{{0x0a02591c, 0x0e73fec2, 0x1449687a, 0x0a932cb0, 0x1fd613ef, 0x1fdf5af0, 0x038a169a, 0x1f8ca739, 0xa9fc93}}, + {{0x09bec2dc, 0x0856ef7b, 0x13dc94de, 0x111882bf, 0x165e5ca8, 0x00bd0d48, 0x1c5cfa13, 0x073b8a70, 0x9c2ce7}}}, + /* 7*16^48*G: */ + {{{0x0d968b59, 0x08037071, 0x12ef0b84, 0x05175c27, 0x1027709a, 0x1d60904d, 0x1c29a9f5, 0x0f834df3, 0xc94001}}, + {{0x0de572fb, 0x17ebb204, 0x0432723f, 0x08596c87, 0x1742ce28, 0x10dfd2da, 0x18804ee2, 0x0a019370, 0x39d922}}}, + /* 9*16^48*G: */ + {{{0x126b3332, 0x143999ab, 0x1b9779a8, 0x0711a0e7, 0x1f8e0310, 0x09d2fb85, 0x0093b19e, 0x13afdda0, 0x1f84bb}}, + {{0x14e8d52e, 0x0a214518, 0x1b70e895, 0x199c5a86, 0x1edf0c2b, 0x013fbadc, 0x1b30951f, 0x00e57953, 0xee726d}}}, + /* 11*16^48*G: */ + {{{0x0defa98e, 0x06d52a56, 0x0b09e657, 0x1088d023, 0x1e9c7724, 0x0abd9cc8, 0x1341b2a0, 0x112128bf, 0xf13e0}}, + {{0x1e286767, 0x0453bb4d, 0x13ab3370, 0x1ce0bc2d, 0x162db287, 0x1c5853d9, 0x1140d78f, 0x1e2ec9cf, 0xadd521}}}, + /* 13*16^48*G: */ + {{{0x09f59b6b, 0x0f0e01df, 0x02238be9, 0x0718c783, 0x026d3e9b, 0x050e96ac, 0x11f6cdca, 0x14aa3bbd, 0xdde191}}, + {{0x06cb1410, 0x156cb149, 0x0553fb3d, 0x0e7177ce, 0x0e14e8b5, 0x0beb0e29, 0x172f829d, 0x0f00504e, 0x5b2bfb}}}, + /* 15*16^48*G: */ + {{{0x09c6b699, 0x1462afee, 0x191a1c6d, 0x1eae6ad7, 0x01682a86, 0x0bdfcbda, 0x1de9685b, 0x05ddb06d, 0x5fab01}}, + {{0x01c6c3aa, 0x0b990a96, 0x020d466c, 0x1622ffd5, 0x02f7b90a, 0x1a08986b, 0x0513a7ae, 0x0e14787a, 0x2d9bfa}}} + }, + { + /* 1*16^49*G: */ + {{{0x1a34d24f, 0x111b196e, 0x084dd007, 0x0db1e193, 0x02ee541b, 0x0fb6f67a, 0x1a764e47, 0x0878b9e2, 0xe4a42d}}, + {{0x1eba9414, 0x13fb898e, 0x16393c4e, 0x0dddbf51, 0x0d34ce88, 0x0ce67dc5, 0x1cd49bf2, 0x1ce2da38, 0x4d9f92}}}, + /* 3*16^49*G: */ + {{{0x1bea0c68, 0x04208579, 0x1ece4ad7, 0x060246ce, 0x16faf094, 0x1e47469c, 0x0e892526, 0x069c2ad4, 0x3e4196}}, + {{0x1a45edb6, 0x05db7fb8, 0x0f3686af, 0x02328c60, 0x093062fa, 0x05ff1b83, 0x07dfcdcf, 0x13b24964, 0x123c5}}}, + /* 5*16^49*G: */ + {{{0x139824d7, 0x1bae91e4, 0x072625eb, 0x0f6c986a, 0x10b576eb, 0x11f317bf, 0x1423bb52, 0x1ea8abae, 0x8d9438}}, + {{0x1366489f, 0x10027a44, 0x1ac18f62, 0x13c57064, 0x0ef6f8fb, 0x05e98d5b, 0x10a8b298, 0x0e69fdcd, 0x3261e0}}}, + /* 7*16^49*G: */ + {{{0x18d713de, 0x124038d4, 0x0a398823, 0x0185f6e8, 0x14543936, 0x089517f2, 0x1108352a, 0x18ab1dca, 0xb72524}}, + {{0x1b8350e9, 0x17ff292c, 0x1297f2dd, 0x05a4dfc8, 0x09415048, 0x08c174eb, 0x1914410b, 0x13514507, 0x4c51b3}}}, + /* 9*16^49*G: */ + {{{0x1e3b2cb4, 0x0636149f, 0x1c84100b, 0x13e6b7e6, 0x1149e304, 0x1b71c090, 0x09466b71, 0x0b442da2, 0x3de45f}}, + {{0x107eb02f, 0x10f19d61, 0x01c1133d, 0x1c51ccb5, 0x09106823, 0x055254be, 0x17714382, 0x13080bd5, 0xba2a85}}}, + /* 11*16^49*G: */ + {{{0x0ce4e5bf, 0x11a3b37b, 0x04016c5c, 0x0f950d41, 0x106ae9b6, 0x1e71ba44, 0x1a1f078f, 0x18d12b37, 0x8511f1}}, + {{0x01789c08, 0x1e494a26, 0x14d9498b, 0x10f11378, 0x000232da, 0x0fbf6355, 0x121d3077, 0x19f2379a, 0xecdff5}}}, + /* 13*16^49*G: */ + {{{0x1d3258ab, 0x0dab3451, 0x0f05370c, 0x04850315, 0x0ab5957d, 0x0e39770a, 0x0088b3e8, 0x05d039ec, 0x8c5a05}}, + {{0x022d0f8f, 0x0bf04298, 0x16512b79, 0x15d1f381, 0x008c246d, 0x0063c826, 0x16841e6a, 0x09768877, 0x6811db}}}, + /* 15*16^49*G: */ + {{{0x1f91bcee, 0x0c615055, 0x03036105, 0x1e3b1e3c, 0x1f137f5c, 0x1e762ab5, 0x1582f718, 0x02dbd7a6, 0xcef7f8}}, + {{0x01966b33, 0x1da6d4fc, 0x1cbdab1a, 0x1c960542, 0x1245fa63, 0x199ce00e, 0x1c04918e, 0x106c6e90, 0x67e74c}}} + }, + { + /* 1*16^50*G: */ + {{{0x0300bf19, 0x18b9dcea, 0x03fa9251, 0x0a6aed51, 0x12b6b92b, 0x07d6d59a, 0x17655058, 0x1de6c197, 0x1ec80f}}, + {{0x0107cefd, 0x18e6e0e6, 0x05681ed9, 0x0dcefec5, 0x1bf5e014, 0x04ac53d5, 0x1034bce9, 0x06ead6a6, 0xaeefe9}}}, + /* 3*16^50*G: */ + {{{0x12fea1f9, 0x18be5de2, 0x0114ae52, 0x1e16a118, 0x06531c4f, 0x0ed5b388, 0x0ba0ef3f, 0x014aba3e, 0xa6dc88}}, + {{0x1bc345e9, 0x18e0a723, 0x1a3df98e, 0x1713b6fc, 0x0bc50057, 0x01d08b56, 0x1f0c0e1a, 0x0a8fb86c, 0x7ef1a8}}}, + /* 5*16^50*G: */ + {{{0x06d6c9b3, 0x06a061f8, 0x01958df2, 0x1899d0e9, 0x081ba8c6, 0x114e3c52, 0x1664af70, 0x07fd4848, 0xfe6ba9}}, + {{0x0948bdfb, 0x0163c47d, 0x10ba6c03, 0x01e37e0b, 0x13b56d98, 0x00d9a2a0, 0x01cadaed, 0x1ae80a73, 0x7ee918}}}, + /* 7*16^50*G: */ + {{{0x0cf95151, 0x11788398, 0x0b12d910, 0x0900dc88, 0x0ded1b96, 0x04616e04, 0x02fec083, 0x1e28df93, 0x15d5e2}}, + {{0x0ff8ecf2, 0x01503e61, 0x16303e52, 0x009f72fb, 0x023f9bb2, 0x1084bc48, 0x13b1fe43, 0x06322bfa, 0xa5b72e}}}, + /* 9*16^50*G: */ + {{{0x096a5658, 0x0bc085c9, 0x1bd9590f, 0x0964a483, 0x029be381, 0x100493d7, 0x11eb631f, 0x0e4ad108, 0x84c0e8}}, + {{0x181b80d1, 0x0cb394de, 0x13c7f48b, 0x0c35303e, 0x1725ed3a, 0x118c8329, 0x0b12821f, 0x10182c04, 0x265983}}}, + /* 11*16^50*G: */ + {{{0x14dc6a0f, 0x1addae44, 0x1f855d4d, 0x06832285, 0x077c2744, 0x1273d160, 0x0c755949, 0x18e3526e, 0xfed6b1}}, + {{0x176fc7e0, 0x05c6b96c, 0x0ff10273, 0x09ab2614, 0x1ae23137, 0x0c0d7269, 0x1c2a11e4, 0x1cd61fff, 0x8de2ab}}}, + /* 13*16^50*G: */ + {{{0x18e29355, 0x19c42f88, 0x1c8361b6, 0x191d2672, 0x1b9d82f1, 0x1c302011, 0x0f1c3f3b, 0x1b325a79, 0x2a6a4d}}, + {{0x15cc2872, 0x029f007d, 0x1131db00, 0x00b474c9, 0x0f90dfe9, 0x0e40f134, 0x1831d83f, 0x174f894f, 0x8677df}}}, + /* 15*16^50*G: */ + {{{0x0148dabf, 0x1cfd447f, 0x075e3ac7, 0x1ba57269, 0x0e735c1a, 0x07611afd, 0x151b65d9, 0x004d924e, 0xe42d93}}, + {{0x011e1361, 0x1b963ab4, 0x19dfae75, 0x04eae033, 0x18530327, 0x0675fef9, 0x12c362ce, 0x058dc5b0, 0x641386}}} + }, + { + /* 1*16^51*G: */ + {{{0x166642be, 0x0edac941, 0x162ea227, 0x0920e2fa, 0x1fa8bce3, 0x057a3406, 0x10be46c0, 0x11808ce1, 0x146a77}}, + {{0x1d83efd0, 0x0594ba41, 0x1f97b474, 0x152e3a5e, 0x0b2870aa, 0x0c13fcea, 0x0a2b759a, 0x1d866a80, 0xb318e0}}}, + /* 3*16^51*G: */ + {{{0x07315443, 0x0c9c39c1, 0x1a19ca67, 0x1377aa95, 0x142a13d7, 0x04cc1050, 0x0d7fd0b2, 0x0080cc12, 0xfc696c}}, + {{0x17d28960, 0x0486b05a, 0x06f46c5d, 0x1fe90c21, 0x077b5487, 0x10e6eb4b, 0x024aefc3, 0x1d7f076b, 0xe0ce27}}}, + /* 5*16^51*G: */ + {{{0x16fdb4eb, 0x0dd97ae0, 0x0b9a9e74, 0x07baf38c, 0x0b0928fa, 0x151ab15f, 0x0ab46b95, 0x043fe9fe, 0x974af2}}, + {{0x09f6f484, 0x004e1efd, 0x1ff08d21, 0x18ae5477, 0x090ed111, 0x121e8160, 0x0f299347, 0x0faa6a00, 0x555238}}}, + /* 7*16^51*G: */ + {{{0x1d5aeee3, 0x1c8256da, 0x163204dc, 0x109786cc, 0x070c5e82, 0x0d9d3349, 0x062c2448, 0x13693bc7, 0x5baab5}}, + {{0x10f69717, 0x1694d7db, 0x14c7bb60, 0x1bb93b57, 0x1daf7215, 0x004330ff, 0x1a15b968, 0x0c2f81ef, 0x8a577f}}}, + /* 9*16^51*G: */ + {{{0x15726890, 0x08d8f227, 0x00df4561, 0x004bfd59, 0x10a0ee59, 0x17e75fc8, 0x10f4040c, 0x14fd6938, 0xfb685f}}, + {{0x1835783a, 0x0375479d, 0x039e6c98, 0x0d9625d2, 0x0ba094fa, 0x10b6cc32, 0x18ba1a72, 0x045931cb, 0xd750df}}}, + /* 11*16^51*G: */ + {{{0x08bca48a, 0x0325f89d, 0x0f7d8bd5, 0x09abe9d0, 0x1f71d78b, 0x1dc8e143, 0x05682ac9, 0x1ecb3c53, 0x5de58f}}, + {{0x12fd41cd, 0x03ca7406, 0x03e2aa56, 0x0b7b389c, 0x13c843fe, 0x111e1296, 0x0d54c269, 0x07b006b3, 0x685a3b}}}, + /* 13*16^51*G: */ + {{{0x05ef63b6, 0x0f1c1a27, 0x06c60baf, 0x09a02ce6, 0x1c1c85ab, 0x1fed1da7, 0x02febc6d, 0x19bd5ac3, 0x6f1825}}, + {{0x05c655f3, 0x1642367a, 0x1fe51504, 0x12b4c804, 0x134553c8, 0x1026bc19, 0x046e63d0, 0x0fbab232, 0xff097e}}}, + /* 15*16^51*G: */ + {{{0x14a63f3b, 0x1a823f6f, 0x1e3e5c9e, 0x00760332, 0x0c765832, 0x08afaf3b, 0x0ddad61c, 0x12beec54, 0xc5ecb8}}, + {{0x05005024, 0x09f9ba34, 0x0c2fb96d, 0x16cbdcbc, 0x033ec8be, 0x002c7fc9, 0x07cdd3a9, 0x03032f10, 0x222525}}} + }, + { + /* 1*16^52*G: */ + {{{0x1180eef9, 0x0bb543c9, 0x0a2e5ddb, 0x00244134, 0x07b128d0, 0x075d8d50, 0x17c1f8eb, 0x1ec3a45c, 0xfa50c0}}, + {{0x1f4f2811, 0x066c6be9, 0x1e884ece, 0x1065274a, 0x1a68a5e6, 0x09439140, 0x0ea6dcb3, 0x124472fd, 0x6b84c6}}}, + /* 3*16^52*G: */ + {{{0x11da5e12, 0x0f70719c, 0x12b2ca5c, 0x0c14802b, 0x0a2b6a9c, 0x14fc9d0e, 0x0c6f368c, 0x07886f3c, 0xf7502e}}, + {{0x0385f4eb, 0x125ce2f4, 0x0973af1e, 0x0da65dee, 0x072047b8, 0x04a2e1eb, 0x0bf5665c, 0x1dbacf9f, 0x3c57f5}}}, + /* 5*16^52*G: */ + {{{0x10b7d105, 0x03a49988, 0x195f27c8, 0x14b89729, 0x055b3f4c, 0x1b31271a, 0x018a8e93, 0x1f3075cb, 0x12fe78}}, + {{0x1f794a60, 0x0c5637dc, 0x1ba42e11, 0x19c4cbad, 0x1cb771de, 0x0d50ccd3, 0x13dde1ad, 0x14671ad7, 0x2062f1}}}, + /* 7*16^52*G: */ + {{{0x1e0c5d05, 0x110ec19c, 0x15cb6bfd, 0x1cd8a154, 0x04b1a480, 0x1d881404, 0x1cf56312, 0x0268fbe8, 0x76aac3}}, + {{0x11ece63e, 0x0b30cdba, 0x179bb8d5, 0x044b9e02, 0x0625f4b1, 0x19641901, 0x03beafbc, 0x1de1ab8e, 0xef5576}}}, + /* 9*16^52*G: */ + {{{0x1c53c086, 0x1137a42f, 0x0686bb27, 0x0ab86939, 0x08104c6b, 0x0618a2f4, 0x13321f98, 0x0b77cb8b, 0xa663fe}}, + {{0x05016201, 0x14e28195, 0x0653f039, 0x1d994ae7, 0x03dc3991, 0x081644c1, 0x1efd746c, 0x0fed6423, 0xb54199}}}, + /* 11*16^52*G: */ + {{{0x0b758574, 0x16c00f6a, 0x13e71ba6, 0x04cd1286, 0x0bdf3d83, 0x10813d71, 0x16096df2, 0x0f4040d9, 0xde9552}}, + {{0x1b67232a, 0x1bef8fe7, 0x168b7ad2, 0x0d420a2a, 0x09ed7bae, 0x0e423f8b, 0x05393887, 0x0ad5927a, 0x4cd3e0}}}, + /* 13*16^52*G: */ + {{{0x1d85474f, 0x1bcdc55f, 0x18d19a35, 0x18945712, 0x05aea894, 0x065f223c, 0x0b76ffb7, 0x1c0cda65, 0x8da6bc}}, + {{0x1d7b4ef7, 0x1efce3e9, 0x16f75e97, 0x198b260b, 0x1fff10b1, 0x0fae838e, 0x13fe13f7, 0x0d5e63da, 0x13fc6c}}}, + /* 15*16^52*G: */ + {{{0x1dd042ea, 0x1aea12b3, 0x03abf41a, 0x144d4c8b, 0x093beebf, 0x1324f19e, 0x08e6c6f5, 0x18f9f677, 0x7329ac}}, + {{0x0f5c94a1, 0x0d467f61, 0x1ead3c2b, 0x112ee63b, 0x168ee184, 0x073ca7d5, 0x1c5224a1, 0x06a836ed, 0x927249}}} + }, + { + /* 1*16^53*G: */ + {{{0x1f067ec2, 0x129e995a, 0x0ee94883, 0x11156bab, 0x0e8421a2, 0x1fb5bec4, 0x0846c696, 0x1a194e43, 0xda1d61}}, + {{0x1ad836f1, 0x0afdd078, 0x1e6d2299, 0x0e7133a4, 0x11e2966a, 0x1b30b0e4, 0x01b1e701, 0x0b4f9326, 0x8157f5}}}, + /* 3*16^53*G: */ + {{{0x1a95a8db, 0x0ec3b997, 0x074dbd85, 0x1d81888f, 0x11723b83, 0x13234c8d, 0x141067a5, 0x148c607b, 0xe3e90d}}, + {{0x1b0d1cf9, 0x00b67bf8, 0x06134f44, 0x03df2f99, 0x0e76afbb, 0x15486381, 0x1e2ec03e, 0x1800ad82, 0xfbe53b}}}, + /* 5*16^53*G: */ + {{{0x112ee214, 0x1d57eb20, 0x04c55005, 0x149d2f2b, 0x0c01c782, 0x086feae0, 0x1dd6a2d9, 0x18e65c7a, 0x9f4ffe}}, + {{0x1085f37a, 0x17a21112, 0x12200a0f, 0x136d2617, 0x1c69d971, 0x13417eba, 0x1cb983a5, 0x1c2631c5, 0x639ce2}}}, + /* 7*16^53*G: */ + {{{0x1f61a0a5, 0x05b40a28, 0x10538fbe, 0x04a7c367, 0x00b2c4b1, 0x10520fa2, 0x0b06c5c6, 0x05a82269, 0x431f62}}, + {{0x18cef899, 0x15bdbff3, 0x1595dc91, 0x1554086a, 0x1aa7241b, 0x1328cb91, 0x0e3db5b7, 0x0ffcf548, 0xa29832}}}, + /* 9*16^53*G: */ + {{{0x07748503, 0x16133eab, 0x04ca39f2, 0x1c1c8ee6, 0x012ac0e6, 0x1779cfc5, 0x1ee1b2d8, 0x1bbd9cf1, 0x993dba}}, + {{0x1eb0cee2, 0x1d39b09b, 0x02b6a926, 0x10f7ed37, 0x1f51a17f, 0x1c23c3d0, 0x1bade17d, 0x1dd0ad3d, 0xa521a9}}}, + /* 11*16^53*G: */ + {{{0x06d23d80, 0x0f5113ad, 0x11d351e3, 0x17a23e4c, 0x162c0e10, 0x018e5c84, 0x1a968ce5, 0x1740d5ab, 0x75f17a}}, + {{0x080dd57e, 0x03542d81, 0x13a96426, 0x1ff7db76, 0x16292372, 0x1e85f8cd, 0x0a031ff1, 0x1fc2ac73, 0xa07a62}}}, + /* 13*16^53*G: */ + {{{0x01ad3413, 0x115cdb6b, 0x09f5a12b, 0x13800806, 0x07b7a8db, 0x0fa42e5c, 0x12829ba5, 0x0bc23b3e, 0x667855}}, + {{0x1ebca672, 0x12408103, 0x17199804, 0x1c5a2a75, 0x1df9ea6c, 0x136c93e5, 0x191a4949, 0x07bc4f1e, 0x510dda}}}, + /* 15*16^53*G: */ + {{{0x06cc8563, 0x00057ad8, 0x18407aab, 0x09beb7ff, 0x03688922, 0x015ec0cb, 0x1d22b6b2, 0x06c59b4e, 0xebdc4a}}, + {{0x0394ccfa, 0x0d8bde75, 0x0813e492, 0x080f2492, 0x14f07bec, 0x11af366e, 0x0d7e6c7b, 0x089d0ada, 0x659a31}}} + }, + { + /* 1*16^54*G: */ + {{{0x0d064e13, 0x139d8308, 0x1bc7818a, 0x023bc088, 0x14166153, 0x1fcc747e, 0x1a41c857, 0x1fe192e0, 0xa8e282}}, + {{0x11f4cc0c, 0x17be3988, 0x175af5b3, 0x0f347ca1, 0x115888b6, 0x1f9e2d92, 0x1026afed, 0x0b71b703, 0x7f9735}}}, + /* 3*16^54*G: */ + {{{0x1a3979b5, 0x150ccd85, 0x1fa0a788, 0x111f1bcc, 0x00e50ba2, 0x1f858f72, 0x098c9fcd, 0x18b9b5bc, 0xae2207}}, + {{0x0450fa6f, 0x079e6b34, 0x153e225a, 0x10f6fa6f, 0x17060fca, 0x092291d6, 0x1dc6b532, 0x0a2180f3, 0xea91fe}}}, + /* 5*16^54*G: */ + {{{0x0efca824, 0x0080c880, 0x08593eb9, 0x1c189fd4, 0x05461e0b, 0x0a08032c, 0x139673b1, 0x0195ae55, 0xcb8ded}}, + {{0x0f227361, 0x0a05e82c, 0x04c5d0bc, 0x1a3fbf8f, 0x0cbc496a, 0x16243d16, 0x03216cc5, 0x11ee81b1, 0x33a500}}}, + /* 7*16^54*G: */ + {{{0x1bcbd327, 0x008da6d1, 0x192abba5, 0x1c10a443, 0x16ed6b04, 0x04806bf3, 0x0d9c23a5, 0x05315e30, 0xb0c53b}}, + {{0x0d7be436, 0x10b5e251, 0x18da83c5, 0x144418e8, 0x0b2afd82, 0x15300db3, 0x1a858e3d, 0x0803f7af, 0xee2a97}}}, + /* 9*16^54*G: */ + {{{0x17b836a1, 0x1377db1c, 0x19e7bdf7, 0x0c80bfee, 0x1f526c80, 0x0317673b, 0x04835362, 0x07e653b7, 0x6f6ba7}}, + {{0x06832b84, 0x0def97d8, 0x187fe4d3, 0x1218a511, 0x0e9c0d89, 0x1c5202df, 0x0638f6c2, 0x02ffebf8, 0xdc778a}}}, + /* 11*16^54*G: */ + {{{0x1eb39ede, 0x1771a104, 0x0184b50a, 0x110065ae, 0x04360310, 0x1a33f081, 0x0bd2ef3f, 0x0fb8e845, 0x7d471a}}, + {{0x0bf6607e, 0x04de6ca5, 0x08aa3f43, 0x0b9efa38, 0x086fa779, 0x1118f7fe, 0x15941ee0, 0x033e74d0, 0x4a7b}}}, + /* 13*16^54*G: */ + {{{0x165eb1c1, 0x0b6a4f12, 0x0b9716a9, 0x1aeacf9b, 0x0f0967fc, 0x1e618cc1, 0x1eb1c1c3, 0x0c7f36e7, 0xf00251}}, + {{0x0dde2ae0, 0x0d3eb823, 0x1344795f, 0x0dfeb9ad, 0x0afed857, 0x1a52ed13, 0x037d319a, 0x1d1107a4, 0x54ea9}}}, + /* 15*16^54*G: */ + {{{0x1ac32c64, 0x079a849e, 0x0b92fe13, 0x0dfc07d9, 0x1715e0e3, 0x074a87a3, 0x0ea83dc1, 0x00003da4, 0xac1214}}, + {{0x1eb1a867, 0x00956dd6, 0x14fc2262, 0x14ba74b4, 0x099a3ed5, 0x1b4ab982, 0x0ebc61c2, 0x19758671, 0xce8ebc}}} + }, + { + /* 1*16^55*G: */ + {{{0x0319497c, 0x179c16f4, 0x09423008, 0x1363f4a2, 0x1cab15d5, 0x12b73489, 0x161cb4e7, 0x17393450, 0x174a53}}, + {{0x079afa73, 0x1ed09d60, 0x0e6150e0, 0x16743b19, 0x1f9e6646, 0x0aaf9623, 0x10595ed0, 0x06f57f93, 0xccc9dc}}}, + /* 3*16^55*G: */ + {{{0x154b8367, 0x0a4039eb, 0x1541affa, 0x0b6efacf, 0x16a5db77, 0x12ea2c21, 0x09b9032a, 0x095c88ca, 0x5e5a09}}, + {{0x11ce85ca, 0x0994d4ec, 0x197fe911, 0x1553de7b, 0x04b7a796, 0x00f8ab95, 0x18170b24, 0x19348f2b, 0xae8af8}}}, + /* 5*16^55*G: */ + {{{0x17b10d9d, 0x0cc2bc9c, 0x07e3f2bc, 0x0a5e313a, 0x0b82de6a, 0x05c19886, 0x1a1677b3, 0x15b72e05, 0xd4e0}}, + {{0x1140dced, 0x01080243, 0x18b648fa, 0x113192f1, 0x087f70b5, 0x1c232191, 0x10251f4b, 0x130306ec, 0x87b801}}}, + /* 7*16^55*G: */ + {{{0x1c9caee8, 0x0408d304, 0x089c2ec8, 0x1c408b63, 0x0a667632, 0x10cd7762, 0x1303dbde, 0x026d1dee, 0x36652}}, + {{0x1772b711, 0x14f6351d, 0x056e9fc2, 0x17531265, 0x0944501c, 0x1e340dd3, 0x1b666527, 0x0565527b, 0x1f18c3}}}, + /* 9*16^55*G: */ + {{{0x1446c85c, 0x1ffcba8c, 0x007018d4, 0x0fbc11cc, 0x0c6eade3, 0x1b6229fb, 0x1c7ea819, 0x00adfb71, 0xe5891}}, + {{0x0148972e, 0x1b63bf39, 0x1376b757, 0x00469d01, 0x01898f49, 0x00c5d7a4, 0x0f683b1d, 0x0be23f4f, 0xe39a48}}}, + /* 11*16^55*G: */ + {{{0x022e1259, 0x18c56f9e, 0x004d8abf, 0x0e73480d, 0x17771931, 0x1afd5003, 0x18fcadb3, 0x0da4de28, 0xa74012}}, + {{0x134a5f43, 0x1bb8b921, 0x075b6b57, 0x0cbcea76, 0x07ee5178, 0x18ba533e, 0x07fc6c17, 0x175e329e, 0x5a9ff}}}, + /* 13*16^55*G: */ + {{{0x0b08f1fe, 0x0450bd1e, 0x0821eff6, 0x1cfdce17, 0x0d177d7c, 0x02bb2ec1, 0x13929950, 0x042db28e, 0x87e4b8}}, + {{0x0ed5e2ec, 0x0fba564d, 0x1e1b675d, 0x1e47b7ac, 0x0c2cc6e2, 0x1fc7517c, 0x033b35f5, 0x180ecc69, 0xf74e3a}}}, + /* 15*16^55*G: */ + {{{0x01c4d15c, 0x14380735, 0x1039d2e6, 0x1d4f7e0f, 0x14a44105, 0x11606092, 0x0696a4b0, 0x08c7fd4f, 0x35ea1b}}, + {{0x1f3fe1ea, 0x049cdd7b, 0x194257b7, 0x06754060, 0x13b185d6, 0x1d2feba6, 0x0b35e223, 0x0ca373da, 0xad2191}}} + }, + { + /* 1*16^56*G: */ + {{{0x1475b7ba, 0x027eff84, 0x0462cf62, 0x13ce61c9, 0x18cdbe03, 0x0bf6fa80, 0x0170f4f9, 0x1303286f, 0x959396}}, + {{0x1524f2fd, 0x0dc55fc3, 0x1c24e17a, 0x0a7ec990, 0x0d6849c6, 0x1c3525ce, 0x07762e80, 0x05111866, 0x2e7e55}}}, + /* 3*16^56*G: */ + {{{0x0fd69985, 0x04e2eec8, 0x17dcaba8, 0x013996db, 0x0cf149f3, 0x1486fde6, 0x1df9e23d, 0x0eb9d6e5, 0xae976}}, + {{0x1409a003, 0x0e475a08, 0x1b86bfe2, 0x133a82f5, 0x054c5d0b, 0x0ff7028d, 0x1453a6e3, 0x0e7edc91, 0x912199}}}, + /* 5*16^56*G: */ + {{{0x19262b90, 0x0e0c9efe, 0x0f30a6a7, 0x078983fc, 0x05d1fb72, 0x0f8bbc01, 0x04bb26d9, 0x054b582c, 0x2b1586}}, + {{0x083d7557, 0x08ccb732, 0x05226926, 0x0692e1f3, 0x149066f5, 0x06a96d43, 0x0fea9e8c, 0x07b54146, 0x2eb005}}}, + /* 7*16^56*G: */ + {{{0x08e7be40, 0x1fcb8a65, 0x0103b964, 0x05912922, 0x0769af2d, 0x0e0b0b72, 0x199dfba5, 0x1da352dd, 0x6af9ea}}, + {{0x0e387e1c, 0x120b7013, 0x1d655a7e, 0x07eccd41, 0x1dc8145e, 0x152141a3, 0x19259c27, 0x022d200c, 0xb3812a}}}, + /* 9*16^56*G: */ + {{{0x1482801e, 0x135b7d07, 0x0f505574, 0x129f178b, 0x0d6f9407, 0x15a1265c, 0x113bacea, 0x1dc08882, 0x596668}}, + {{0x04870c37, 0x03b8a478, 0x14d4d6b5, 0x0d8396c7, 0x1304e8db, 0x1cb043b8, 0x1d7c7b23, 0x150b775d, 0x949aa0}}}, + /* 11*16^56*G: */ + {{{0x032c19fd, 0x064d973e, 0x000a30f9, 0x1571d20b, 0x10b5b4ac, 0x068bd5ab, 0x01d8bf7d, 0x11036a0a, 0xbe84d1}}, + {{0x12f1281f, 0x1b4a529b, 0x14370dd9, 0x0b4feabc, 0x03994795, 0x12fa4184, 0x02513479, 0x19665b8a, 0xeff960}}}, + /* 13*16^56*G: */ + {{{0x16c69482, 0x08dbafea, 0x0be859ef, 0x156a8026, 0x0bc88cbe, 0x193a6579, 0x1b9507d5, 0x062981af, 0x9867a0}}, + {{0x0f792cd7, 0x178308a3, 0x158a2a45, 0x048b9ea2, 0x099639e6, 0x16aad8dd, 0x0d3e71e4, 0x0b476210, 0xd02e61}}}, + /* 15*16^56*G: */ + {{{0x1d557aa1, 0x0511cec8, 0x007f0a5e, 0x1b25fd9a, 0x1d6abdf1, 0x1975004c, 0x0569649f, 0x08a81b10, 0xa866f2}}, + {{0x01430634, 0x0c0ddda6, 0x184692de, 0x16d38cf8, 0x0e13961e, 0x0c7d2ed8, 0x0d135e4f, 0x1ed50045, 0xb58739}}} + }, + { + /* 1*16^57*G: */ + {{{0x1d82b151, 0x1a89a064, 0x07ee8b6e, 0x01487aac, 0x09a8fcca, 0x108a9d88, 0x195b5916, 0x0a15c803, 0xd2a63a}}, + {{0x1cf89405, 0x00a10ba6, 0x013294b5, 0x1eea15e9, 0x08220a70, 0x172c594a, 0x12dd596b, 0x1f6c887f, 0xe82d86}}}, + /* 3*16^57*G: */ + {{{0x0e4b3ba0, 0x02cfb1af, 0x02fc7c5e, 0x157debe3, 0x1245f5c2, 0x0b8798df, 0x0dcefbf8, 0x00a443ff, 0x410811}}, + {{0x17525595, 0x034b0ee0, 0x08191552, 0x0c930acb, 0x18498133, 0x12d70eb5, 0x19a3cb29, 0x0d2edfea, 0xdc37f3}}}, + /* 5*16^57*G: */ + {{{0x13d98ded, 0x114e4dc4, 0x02808611, 0x1e450677, 0x1d65edbd, 0x114c8298, 0x0323233c, 0x02142d98, 0x63a2a2}}, + {{0x00d1cfc2, 0x0c8cbea7, 0x11e1ce94, 0x17ed4013, 0x194461fa, 0x01992a76, 0x1dbf4194, 0x1c5cffd8, 0x882b42}}}, + /* 7*16^57*G: */ + {{{0x18045445, 0x1430d285, 0x07a35fba, 0x1ee6e320, 0x1bef080f, 0x17172eab, 0x1f28f7c4, 0x0ba893ac, 0xc1581}}, + {{0x0054a206, 0x0a7c3eaa, 0x1633a8c8, 0x00a9c86c, 0x1cd28ba3, 0x1e1db331, 0x045742a4, 0x01475d28, 0x2f30d6}}}, + /* 9*16^57*G: */ + {{{0x03857faf, 0x14891ce6, 0x05865c07, 0x1f95bc3d, 0x0ef7f882, 0x1d47a414, 0x0a70355e, 0x0d7135d1, 0xc757eb}}, + {{0x00584ca4, 0x0eced865, 0x06253040, 0x1a621a20, 0x0c61b627, 0x14f9045f, 0x1cd895cd, 0x19f9a47f, 0xf03a59}}}, + /* 11*16^57*G: */ + {{{0x1459225d, 0x1268c27a, 0x02163443, 0x15eefc5b, 0x002a244f, 0x0a04c8ce, 0x0343e057, 0x15d5b5f4, 0xfa8063}}, + {{0x1ece1507, 0x115bf1db, 0x01f1670e, 0x16d86da0, 0x0425c0a2, 0x11104126, 0x01a45837, 0x120af818, 0xba71f}}}, + /* 13*16^57*G: */ + {{{0x16f0d044, 0x13cff4a0, 0x079869e5, 0x153fa921, 0x0e0a5aab, 0x14e7c317, 0x1ea278bd, 0x18b3a04a, 0x658ca3}}, + {{0x17cb872d, 0x0e8f9977, 0x0633a26b, 0x02cec788, 0x1d37655a, 0x0eb1389d, 0x15183c59, 0x06ef5d44, 0xae5cc1}}}, + /* 15*16^57*G: */ + {{{0x1696756d, 0x1ad64ab9, 0x158505ea, 0x15e2c0ac, 0x1305c676, 0x0cc1ae9f, 0x0aeb3930, 0x0955f23e, 0x31c94b}}, + {{0x1e08ae78, 0x1e22f46e, 0x0b441c2a, 0x1dbf95cd, 0x0160683a, 0x05acd57a, 0x0ea6fd2e, 0x096aadd0, 0xf80f88}}} + }, + { + /* 1*16^58*G: */ + {{{0x1617e073, 0x01b7cda2, 0x0e4c5ecd, 0x197b7a70, 0x1dc866ba, 0x1c4b6be7, 0x1ae243b9, 0x0466a8e3, 0x64587e}}, + {{0x1faf6589, 0x014cf2f4, 0x0ebaacd6, 0x12147226, 0x099a185b, 0x0eb223e1, 0x0b8aba5b, 0x1ab7ed20, 0xd99fcd}}}, + /* 3*16^58*G: */ + {{{0x0e103dd6, 0x0fb8a390, 0x0012166b, 0x0c0980f8, 0x0a17ac34, 0x09e7e2c9, 0x0fe0da88, 0x1aab4840, 0xbc477b}}, + {{0x16f7c343, 0x1c8416c6, 0x0ed12b18, 0x126ae58c, 0x0a6395d2, 0x02a9636f, 0x1549b2eb, 0x0485351b, 0xe31e1e}}}, + /* 5*16^58*G: */ + {{{0x144eab31, 0x086e1d86, 0x198d241f, 0x0b5e2809, 0x1f8e80ac, 0x1a11933d, 0x00e00c0e, 0x1fcb4d77, 0x589db4}}, + {{0x11361f6a, 0x00d75f3a, 0x123e36e5, 0x021caa42, 0x190f31f6, 0x0310125e, 0x0a93d81c, 0x0b821154, 0x625544}}}, + /* 7*16^58*G: */ + {{{0x1a0c2c41, 0x1fcb0b94, 0x00cdb19e, 0x063838ac, 0x0db7c428, 0x0c3056b7, 0x1e8baa28, 0x06fa2dc5, 0x1339b3}}, + {{0x09f1bc2b, 0x02f82a5d, 0x1a48e906, 0x044ff0fb, 0x1a3406b1, 0x1207e889, 0x196e9e8c, 0x0c6c58f5, 0x9f9b29}}}, + /* 9*16^58*G: */ + {{{0x18fc47af, 0x18258fc2, 0x10337b0d, 0x1bfd9065, 0x1b568a8d, 0x194da800, 0x1c5f3140, 0x14226c79, 0x7ff3bb}}, + {{0x19ba43a7, 0x1c30b267, 0x02eb0a5f, 0x0a7635a3, 0x1446b17a, 0x048dba39, 0x18a682f2, 0x15d00314, 0x1f6ba7}}}, + /* 11*16^58*G: */ + {{{0x15213775, 0x0c26b004, 0x150aa640, 0x08527647, 0x07f6100b, 0x149caff6, 0x02ed8507, 0x08c79d6c, 0x8ec670}}, + {{0x1841ffff, 0x100879f3, 0x13d47a43, 0x15314179, 0x1ee0e71d, 0x01a0ae76, 0x0f8c1b99, 0x0df41b4b, 0x8f58a6}}}, + /* 13*16^58*G: */ + {{{0x1452abbb, 0x059ffc69, 0x0e570b8f, 0x154e5fc5, 0x1d495ae6, 0x161ff6ca, 0x06ee276e, 0x16883ce0, 0x83de61}}, + {{0x054eb66e, 0x1028bb58, 0x1390a462, 0x18be6d77, 0x01563d79, 0x1b57c627, 0x027e9afe, 0x0694698c, 0x32f0e3}}}, + /* 15*16^58*G: */ + {{{0x159276f1, 0x0b446137, 0x085ec57a, 0x1c4b6525, 0x0d86833a, 0x1ae007be, 0x076c6a2e, 0x1131ea18, 0x3d7663}}, + {{0x059cbcb3, 0x0f2532dc, 0x0c65e180, 0x03304033, 0x0333ef32, 0x171a6d6c, 0x176d825d, 0x0e6f430f, 0xd37669}}} + }, + { + /* 1*16^59*G: */ + {{{0x1d45e458, 0x0c6b6436, 0x1439ff4d, 0x154d9d44, 0x1de042f0, 0x0369f2a4, 0x0216ce95, 0x1c1c9c9b, 0x8481bd}}, + {{0x1779057e, 0x0b258dac, 0x098b955b, 0x14f38856, 0x0b2ca900, 0x0df9ce76, 0x13761289, 0x11974a80, 0x38ee7b}}}, + /* 3*16^59*G: */ + {{{0x152da17d, 0x10507d20, 0x14191ac5, 0x1827b611, 0x00bc811d, 0x13582ff0, 0x117c2253, 0x03c1ea31, 0x3beaed}}, + {{0x0cc768d2, 0x13824c2f, 0x1fb105b3, 0x1bcef71b, 0x0e1b554c, 0x145f5f40, 0x0b37fbd2, 0x1eab5fef, 0xc3b0d7}}}, + /* 5*16^59*G: */ + {{{0x1a4edcc5, 0x1a7d0bed, 0x00c46dc8, 0x1c644284, 0x15997e74, 0x0fe01c93, 0x15861d4b, 0x14197b93, 0x6e73db}}, + {{0x159da0e4, 0x198fbe6e, 0x019e40de, 0x14efb4e0, 0x08693278, 0x0a844441, 0x1122fa91, 0x1f893dd9, 0xee0ac1}}}, + /* 7*16^59*G: */ + {{{0x0a80b979, 0x04e26212, 0x06aecc50, 0x01ebf465, 0x1c310049, 0x0cbf5523, 0x1649d89f, 0x1126fcb6, 0x7706dd}}, + {{0x0126cfde, 0x067a4082, 0x1fbf8c85, 0x141469fa, 0x09c7117f, 0x0ebcc8f5, 0x1c51dde3, 0x104fab76, 0x8a02a9}}}, + /* 9*16^59*G: */ + {{{0x126fe285, 0x06dce25b, 0x0fbc49f7, 0x17585282, 0x1e06aa45, 0x1e3d20fc, 0x03e034bc, 0x18b25378, 0x16d422}}, + {{0x0155b441, 0x1f07ff36, 0x0d93508c, 0x1e18226e, 0x131b1e93, 0x1f34d31a, 0x1906a2ad, 0x1f4a3c44, 0xdf888}}}, + /* 11*16^59*G: */ + {{{0x1acfa513, 0x11885e55, 0x1838ebab, 0x080f3f34, 0x16a9c4e2, 0x1a23a87a, 0x158ea968, 0x08fdd8ed, 0x1fcc0e}}, + {{0x107afc9c, 0x083add20, 0x060e461d, 0x1bdba2c9, 0x1f9b44be, 0x1c3aa19c, 0x11e2d238, 0x14083c6a, 0x165dc1}}}, + /* 13*16^59*G: */ + {{{0x1edc69b2, 0x0d1383e7, 0x1addc5c8, 0x14c11364, 0x0a386a50, 0x01821ae5, 0x0285cc19, 0x0e75ad97, 0xc12b90}}, + {{0x00dd41dd, 0x19574d81, 0x09eca800, 0x1c3b7c7a, 0x14976b8e, 0x16e44fc4, 0x17c765dc, 0x07fca699, 0x3173c4}}}, + /* 15*16^59*G: */ + {{{0x1961fe4d, 0x1b7ac2ef, 0x041c65ea, 0x0910df16, 0x0a73e8a1, 0x14989896, 0x0a3e8fcf, 0x10bb864f, 0xd059bf}}, + {{0x0ae823c2, 0x0c9ad833, 0x16ad932e, 0x111743e9, 0x155b4fd3, 0x1b2ee424, 0x0978ff81, 0x0c18116a, 0x45107a}}} + }, + { + /* 1*16^60*G: */ + {{{0x0caf666b, 0x06b181fb, 0x0c738c2f, 0x19fda789, 0x1f4637ff, 0x0bcd740b, 0x0aa98ada, 0x0af4f020, 0x13464a}}, + {{0x1f6ecc27, 0x1a4ad483, 0x0250b84f, 0x0601503a, 0x0b0ca48f, 0x019a29e6, 0x1603bdf9, 0x12008c28, 0x69be15}}}, + /* 3*16^60*G: */ + {{{0x0eca5f51, 0x10b5904c, 0x1f26bafc, 0x142e3729, 0x1b5cfdde, 0x0b595f82, 0x1a58b1bb, 0x029bb3dc, 0xdde9d5}}, + {{0x10c638f7, 0x16b4d39e, 0x0255c7e6, 0x1dd7d1bd, 0x18f0950f, 0x19a5853f, 0x04476247, 0x02679c50, 0xb84e69}}}, + /* 5*16^60*G: */ + {{{0x199c88e4, 0x1c83582c, 0x0b51bb0b, 0x1aa27c41, 0x04b179ae, 0x00375890, 0x0dcdba7d, 0x02046d32, 0xfd1a62}}, + {{0x195bc8df, 0x0e4648b2, 0x13003ca6, 0x16e3a92b, 0x17782dc6, 0x0034aa4b, 0x082fec4f, 0x0a973918, 0x1ac97b}}}, + /* 7*16^60*G: */ + {{{0x1f8018ce, 0x0a8adada, 0x024b5a2f, 0x1b4ae71b, 0x06dc7cf2, 0x1e727964, 0x1ae7c4ff, 0x063b1852, 0x4ee485}}, + {{0x1e48381f, 0x01ad30d8, 0x1a01804f, 0x0e92e369, 0x1c5e6710, 0x02046863, 0x02d7f1ed, 0x1a90217f, 0xb68f9e}}}, + /* 9*16^60*G: */ + {{{0x11473678, 0x1429748f, 0x10e4bdc0, 0x00af2a12, 0x0c070cba, 0x18a62adc, 0x036ffd78, 0x13869880, 0xfd76cc}}, + {{0x0d144f4f, 0x10b27754, 0x007210df, 0x051ddc28, 0x12cd6606, 0x10539e81, 0x0f6b83fb, 0x086f0e28, 0xf20465}}}, + /* 11*16^60*G: */ + {{{0x0d7a2193, 0x1805b8a4, 0x05d51bb7, 0x123c89e3, 0x0ea212c6, 0x07413a5a, 0x1008679b, 0x14662476, 0x85a2ab}}, + {{0x10cdcf3a, 0x18641ea5, 0x0ec4909f, 0x0db5bf38, 0x0029fe1c, 0x104168e6, 0x145b60b1, 0x0afd6560, 0x9c1298}}}, + /* 13*16^60*G: */ + {{{0x177568b0, 0x0d8182d9, 0x180ec14d, 0x1d1ba033, 0x1650f35a, 0x16b62bc1, 0x19d1102d, 0x1f8e79ff, 0xd25ddb}}, + {{0x1f39929c, 0x0509c936, 0x0f0fc018, 0x04e7103c, 0x1d92b832, 0x17ba66f3, 0x024e2fab, 0x0eb27f09, 0x7a3aff}}}, + /* 15*16^60*G: */ + {{{0x071d7c13, 0x0ff288c5, 0x03fe8e15, 0x156beff4, 0x0c805641, 0x1f4d4bd5, 0x09c957c1, 0x06287d29, 0x458135}}, + {{0x1aff63cf, 0x11c5e2a9, 0x0f65ae65, 0x000caa85, 0x169c9702, 0x0878bbee, 0x0b62d5fd, 0x1d8292f3, 0x9f5858}}} + }, + { + /* 1*16^61*G: */ + {{{0x0d83f366, 0x16d1d069, 0x1ca16232, 0x1399dbc5, 0x1c97a0cd, 0x0185e60e, 0x18ba6bbd, 0x1eb6e27f, 0xbc4a9d}}, + {{0x181f33c1, 0x1ac6b332, 0x151ec5b5, 0x1153f7f4, 0x198caa6e, 0x1bd6fa5b, 0x1018e0e4, 0x194dcf0b, 0xd3a81}}}, + /* 3*16^61*G: */ + {{{0x1712be3c, 0x03517197, 0x051a99ac, 0x0be31db4, 0x01534729, 0x12edd580, 0x0de34f1c, 0x13b26636, 0x39d734}}, + {{0x1c6ff65c, 0x0180cfa0, 0x09059133, 0x1def4bd9, 0x012e8ef5, 0x1aaa6334, 0x0fdfec49, 0x09eadde7, 0x8f929b}}}, + /* 5*16^61*G: */ + {{{0x1f77f22b, 0x15727de6, 0x1e0b80d6, 0x12e47329, 0x1af26c69, 0x06b614ca, 0x17427947, 0x02fefb83, 0xf0cba6}}, + {{0x1909a03c, 0x1e47163a, 0x08255987, 0x0318fd21, 0x0d04004f, 0x1361b28b, 0x1e627bcc, 0x08627f3b, 0x1a25ab}}}, + /* 7*16^61*G: */ + {{{0x06509c12, 0x0566e6a6, 0x147d1d96, 0x17fc46b7, 0x068ed8d6, 0x18746c39, 0x134c8745, 0x173b642a, 0x381d7a}}, + {{0x0eb46102, 0x03215471, 0x19babdd5, 0x1050b0d9, 0x181e7205, 0x122b9d32, 0x16a7ad74, 0x0b6ffb47, 0xa47aab}}}, + /* 9*16^61*G: */ + {{{0x184fe955, 0x0d9da6b1, 0x18ef3923, 0x12ad4b40, 0x08a53f69, 0x1b9f34a6, 0x1f991e4e, 0x0a8cf570, 0xa703f0}}, + {{0x161344bd, 0x135977de, 0x05c9dfe8, 0x1c2c538b, 0x1199b539, 0x1c96b58c, 0x1eb703a9, 0x06b45608, 0xd500f9}}}, + /* 11*16^61*G: */ + {{{0x06eace58, 0x04cb2b60, 0x1cc8474d, 0x1c8ac745, 0x1a03f1b7, 0x1686b829, 0x035d1b1a, 0x18b55aaa, 0x73c6b3}}, + {{0x0af8654e, 0x0b8548b9, 0x0e34a45c, 0x150f1b77, 0x191c6aa6, 0x04c89b36, 0x1dd06ea4, 0x148e6749, 0x3a2fb4}}}, + /* 13*16^61*G: */ + {{{0x00181d5e, 0x059c45f8, 0x16abc815, 0x03675372, 0x0ddb8de7, 0x069d0e07, 0x1ff68740, 0x1cea0da8, 0xc627f3}}, + {{0x169f886d, 0x13c80531, 0x12f8b0e4, 0x0d600a93, 0x1f7d68ef, 0x0b009c50, 0x098ecb5a, 0x1ae3c885, 0xd78f9d}}}, + /* 15*16^61*G: */ + {{{0x17828b16, 0x07146cfd, 0x09211fcd, 0x0f2b1e51, 0x0de53a04, 0x1a053783, 0x08cfe897, 0x17c1dbfa, 0xbb88fa}}, + {{0x0aea5df7, 0x08a39d10, 0x087a5a71, 0x1502e9c7, 0x19163ec4, 0x02cb008a, 0x0374d177, 0x16609ebd, 0xb73676}}} + }, + { + /* 1*16^62*G: */ + {{{0x05324caa, 0x0a55987f, 0x051ca8e5, 0x16cbc615, 0x0a32e694, 0x063a4a29, 0x0f0348f6, 0x0f7f0531, 0x8c28a9}}, + {{0x0bef9482, 0x138ee39e, 0x072e5167, 0x0f09e08a, 0x0c0eb7ae, 0x16f98fbe, 0x064cde3f, 0x0c74660a, 0x40a304}}}, + /* 3*16^62*G: */ + {{{0x0754dd40, 0x11f438aa, 0x0d19b3e1, 0x044c63f8, 0x0f6e9a24, 0x020fe6b9, 0x1f3d16d2, 0x0e06581b, 0x972924}}, + {{0x0aa36143, 0x025a4979, 0x0b2bd24e, 0x15d0a4ab, 0x0df3690d, 0x03aee5ea, 0x08773457, 0x0884cbfd, 0x91d1a2}}}, + /* 5*16^62*G: */ + {{{0x0c2ca7ff, 0x016c175c, 0x17c0868f, 0x06c8bb2b, 0x127af180, 0x08d6ad17, 0x05b8141e, 0x12eb014f, 0x89637f}}, + {{0x10493e68, 0x16a0af0b, 0x10baadef, 0x178d471c, 0x17489f87, 0x0e78aa1a, 0x109355ee, 0x04919110, 0x2d1fe1}}}, + /* 7*16^62*G: */ + {{{0x0ca8dd7f, 0x0c36b1d0, 0x084e0698, 0x0e5006ad, 0x157421bc, 0x0ed01ea9, 0x1824bf72, 0x1ce37c4b, 0x308138}}, + {{0x0a92c7f2, 0x00af923c, 0x12b64579, 0x0cac8c86, 0x08e18c81, 0x1622e8a0, 0x124978e7, 0x1a51051f, 0x28d1e2}}}, + /* 9*16^62*G: */ + {{{0x066a3fb1, 0x06e62b44, 0x04b881b0, 0x00125033, 0x0862f6ec, 0x1a8642db, 0x0d974795, 0x1d054dbd, 0x575fc4}}, + {{0x102655ad, 0x0dc74854, 0x08ebcbc2, 0x076ae78d, 0x087daed3, 0x0de14bc7, 0x128b59c7, 0x120854df, 0x6f6edb}}}, + /* 11*16^62*G: */ + {{{0x190117df, 0x0db54523, 0x08040a48, 0x0a77779c, 0x02a8fda2, 0x137c0f75, 0x0de889fc, 0x06d6c9d5, 0xa5ec90}}, + {{0x186462fe, 0x0094099f, 0x0528d8f6, 0x08c3e0ac, 0x1a0f71c8, 0x1cc1d68f, 0x01003165, 0x0c4bd828, 0xb79dc6}}}, + /* 13*16^62*G: */ + {{{0x172ad712, 0x187cbae0, 0x0411ca42, 0x131961bc, 0x149b95a3, 0x1cbb0a31, 0x0c252779, 0x1d226621, 0xa153df}}, + {{0x0d48fdd2, 0x052fb29c, 0x1c2cca72, 0x0a7e50b5, 0x1b89abd0, 0x0c6af8f8, 0x0cbf120c, 0x0827f60b, 0xfd94d8}}}, + /* 15*16^62*G: */ + {{{0x11cf5b3a, 0x1861b808, 0x0d69e040, 0x0675077b, 0x1c824c44, 0x1a684476, 0x18564d70, 0x18d5ef28, 0x9a541a}}, + {{0x16a44ae4, 0x1faabaf9, 0x07a94b58, 0x11fdc4a4, 0x1475f554, 0x0d79b447, 0x0adf2bf8, 0x1839620d, 0xb66148}}} + }, + { + /* 1*16^63*G: */ + {{{0x1faccae0, 0x0625d088, 0x12eccdd2, 0x166a18b4, 0x11fd23c8, 0x0a672783, 0x1ea30776, 0x0cc272a4, 0x8ea96}}, + {{0x0e62b945, 0x0d79a518, 0x1c3e3a55, 0x0f077d39, 0x1c5d735b, 0x1e067dca, 0x1e0b8939, 0x17791dc4, 0x620efa}}}, + /* 3*16^63*G: */ + {{{0x06a06f5e, 0x0d205acb, 0x0820dc08, 0x0324a2dd, 0x1b716a34, 0x06a10931, 0x14eb0dec, 0x1f7d4284, 0x383b24}}, + {{0x13c6e772, 0x04fa3c36, 0x030ac102, 0x0d5ce977, 0x05a19e8f, 0x0b36aa75, 0x0881133d, 0x0d589db7, 0x54cf70}}}, + /* 5*16^63*G: */ + {{{0x0638a136, 0x1cbae0ea, 0x00e06571, 0x1a39c66d, 0x1790c2b0, 0x0ce35b06, 0x15b5e279, 0x1a07c05d, 0xe68432}}, + {{0x0c6c2584, 0x17e8c084, 0x0d5ccdaa, 0x13c7d7b6, 0x1e6472e0, 0x13981d00, 0x0998934a, 0x02731c6b, 0xca5be4}}}, + /* 7*16^63*G: */ + {{{0x16e8c10c, 0x0743e220, 0x06f5a01f, 0x0530be72, 0x06e7fb47, 0x1ef67398, 0x10a83bbe, 0x0b3c5fcb, 0x395dd5}}, + {{0x05fe638e, 0x01eb6c98, 0x19a48b2f, 0x013809b8, 0x04e274c9, 0x1f43d7fd, 0x0b174104, 0x1a968b25, 0xfd62dc}}}, + /* 9*16^63*G: */ + {{{0x0d6c14ef, 0x0b326d90, 0x1e4f23d4, 0x11f8ea0d, 0x06480085, 0x16adf771, 0x172e7dbb, 0x1b86aa4b, 0x7a514a}}, + {{0x0b3fbd13, 0x169f0c38, 0x09b893de, 0x1d5dee55, 0x15c3729b, 0x185ca647, 0x1363a25f, 0x1fda2a5c, 0x56edd1}}}, + /* 11*16^63*G: */ + {{{0x0f6d65eb, 0x14a31e82, 0x10085492, 0x1220eea8, 0x08f235a9, 0x179dfa48, 0x04363aa3, 0x0b0864bb, 0x1ee1fd}}, + {{0x1d22941c, 0x101b5c92, 0x0d60ac47, 0x05e9d30c, 0x0fcfaca3, 0x1cfe27a1, 0x1cf073ea, 0x1237bad8, 0xbb6928}}}, + /* 13*16^63*G: */ + {{{0x0e36cb44, 0x18ca8778, 0x12880647, 0x1d16d91b, 0x1357d617, 0x07e86c41, 0x0aa2f016, 0x069a71f5, 0x155156}}, + {{0x1f495a68, 0x14d7a328, 0x0830794c, 0x06e2ebe0, 0x1205da8c, 0x11f85a31, 0x08578dc3, 0x18eaaaea, 0xab4fff}}}, + /* 15*16^63*G: */ + {{{0x1427bacc, 0x0fca3083, 0x0b58b854, 0x0662c9ba, 0x1c4aa9e7, 0x07584ac6, 0x0804d8d6, 0x0c88d7ea, 0x3bc6bc}}, + {{0x0ad6fda6, 0x0619feaf, 0x02fad1c5, 0x16eb4a45, 0x0c02bd71, 0x1771136b, 0x0c1736d8, 0x180e2ed8, 0x8e305c}}} + }, diff --git a/applications/external/flipbip/lib/crypto/segwit_addr.c b/applications/external/flipbip/lib/crypto/segwit_addr.c new file mode 100644 index 0000000000..1dbc151188 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/segwit_addr.c @@ -0,0 +1,225 @@ +/* Copyright (c) 2017, 2021 Pieter Wuille + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include +#include +#include + +#include "segwit_addr.h" + +static uint32_t bech32_polymod_step(uint32_t pre) { + uint8_t b = pre >> 25; + return ((pre & 0x1FFFFFF) << 5) ^ (-((b >> 0) & 1) & 0x3b6a57b2UL) ^ + (-((b >> 1) & 1) & 0x26508e6dUL) ^ (-((b >> 2) & 1) & 0x1ea119faUL) ^ + (-((b >> 3) & 1) & 0x3d4233ddUL) ^ (-((b >> 4) & 1) & 0x2a1462b3UL); +} + +static uint32_t bech32_final_constant(bech32_encoding enc) { + if(enc == BECH32_ENCODING_BECH32) return 1; + if(enc == BECH32_ENCODING_BECH32M) return 0x2bc830a3; + return 0; +} + +static const char* charset = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"; + +static const int8_t charset_rev[128] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 15, -1, 10, 17, 21, 20, 26, 30, 7, 5, -1, -1, -1, -1, -1, -1, -1, 29, + -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1, 1, 0, 3, 16, 11, 28, 12, 14, + 6, 4, 2, -1, -1, -1, -1, -1, -1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, + 19, -1, 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1}; + +int bech32_encode( + char* output, + const char* hrp, + const uint8_t* data, + size_t data_len, + bech32_encoding enc) { + uint32_t chk = 1; + size_t i = 0; + while(hrp[i] != 0) { + int ch = hrp[i]; + if(ch < 33 || ch > 126) { + return 0; + } + + if(ch >= 'A' && ch <= 'Z') return 0; + chk = bech32_polymod_step(chk) ^ (ch >> 5); + ++i; + } + if(i + 7 + data_len > 90) return 0; + chk = bech32_polymod_step(chk); + while(*hrp != 0) { + chk = bech32_polymod_step(chk) ^ (*hrp & 0x1f); + *(output++) = *(hrp++); + } + *(output++) = '1'; + for(i = 0; i < data_len; ++i) { + if(*data >> 5) return 0; + chk = bech32_polymod_step(chk) ^ (*data); + *(output++) = charset[*(data++)]; + } + for(i = 0; i < 6; ++i) { + chk = bech32_polymod_step(chk); + } + chk ^= bech32_final_constant(enc); + for(i = 0; i < 6; ++i) { + *(output++) = charset[(chk >> ((5 - i) * 5)) & 0x1f]; + } + *output = 0; + return 1; +} + +bech32_encoding bech32_decode(char* hrp, uint8_t* data, size_t* data_len, const char* input) { + uint32_t chk = 1; + size_t i = 0; + size_t input_len = strlen(input); + size_t hrp_len = 0; + int have_lower = 0, have_upper = 0; + if(input_len < 8) { + return BECH32_ENCODING_NONE; + } + *data_len = 0; + while(*data_len < input_len && input[(input_len - 1) - *data_len] != '1') { + ++(*data_len); + } + hrp_len = input_len - (1 + *data_len); + if(1 + *data_len >= input_len || *data_len < 6 || hrp_len > BECH32_MAX_HRP_LEN) { + return BECH32_ENCODING_NONE; + } + *(data_len) -= 6; + for(i = 0; i < hrp_len; ++i) { + int ch = input[i]; + if(ch < 33 || ch > 126) { + return BECH32_ENCODING_NONE; + } + if(ch >= 'a' && ch <= 'z') { + have_lower = 1; + } else if(ch >= 'A' && ch <= 'Z') { + have_upper = 1; + ch = (ch - 'A') + 'a'; + } + hrp[i] = ch; + chk = bech32_polymod_step(chk) ^ (ch >> 5); + } + hrp[i] = 0; + chk = bech32_polymod_step(chk); + for(i = 0; i < hrp_len; ++i) { + chk = bech32_polymod_step(chk) ^ (input[i] & 0x1f); + } + ++i; + while(i < input_len) { + int v = (input[i] & 0x80) ? -1 : charset_rev[(int)input[i]]; + if(input[i] >= 'a' && input[i] <= 'z') have_lower = 1; + if(input[i] >= 'A' && input[i] <= 'Z') have_upper = 1; + if(v == -1) { + return BECH32_ENCODING_NONE; + } + chk = bech32_polymod_step(chk) ^ v; + if(i + 6 < input_len) { + data[i - (1 + hrp_len)] = v; + } + ++i; + } + if(have_lower && have_upper) { + return BECH32_ENCODING_NONE; + } + if(chk == bech32_final_constant(BECH32_ENCODING_BECH32)) { + return BECH32_ENCODING_BECH32; + } else if(chk == bech32_final_constant(BECH32_ENCODING_BECH32M)) { + return BECH32_ENCODING_BECH32M; + } else { + return BECH32_ENCODING_NONE; + } +} + +static int convert_bits( + uint8_t* out, + size_t* outlen, + int outbits, + const uint8_t* in, + size_t inlen, + int inbits, + int pad) { + uint32_t val = 0; + int bits = 0; + uint32_t maxv = (((uint32_t)1) << outbits) - 1; + while(inlen--) { + val = (val << inbits) | *(in++); + bits += inbits; + while(bits >= outbits) { + bits -= outbits; + out[(*outlen)++] = (val >> bits) & maxv; + } + } + if(pad) { + if(bits) { + out[(*outlen)++] = (val << (outbits - bits)) & maxv; + } + } else if(((val << (outbits - bits)) & maxv) || bits >= inbits) { + return 0; + } + return 1; +} + +int segwit_addr_encode( + char* output, + const char* hrp, + int witver, + const uint8_t* witprog, + size_t witprog_len) { + uint8_t data[65] = {0}; + size_t datalen = 0; + bech32_encoding enc = BECH32_ENCODING_BECH32; + if(witver > 16) return 0; + if(witver == 0 && witprog_len != 20 && witprog_len != 32) return 0; + if(witprog_len < 2 || witprog_len > 40) return 0; + if(witver > 0) enc = BECH32_ENCODING_BECH32M; + data[0] = witver; + convert_bits(data + 1, &datalen, 5, witprog, witprog_len, 8, 1); + ++datalen; + return bech32_encode(output, hrp, data, datalen, enc); +} + +int segwit_addr_decode( + int* witver, + uint8_t* witdata, + size_t* witdata_len, + const char* hrp, + const char* addr) { + uint8_t data[84] = {0}; + char hrp_actual[84] = {0}; + size_t data_len = 0; + if(strlen(addr) > 90) return 0; + bech32_encoding enc = bech32_decode(hrp_actual, data, &data_len, addr); + if(enc == BECH32_ENCODING_NONE) return 0; + if(data_len == 0 || data_len > 65) return 0; + if(strncmp(hrp, hrp_actual, 84) != 0) return 0; + if(data[0] > 16) return 0; + if(data[0] == 0 && enc != BECH32_ENCODING_BECH32) return 0; + if(data[0] > 0 && enc != BECH32_ENCODING_BECH32M) return 0; + *witdata_len = 0; + if(!convert_bits(witdata, witdata_len, 8, data + 1, data_len - 1, 5, 0)) return 0; + if(*witdata_len < 2 || *witdata_len > 40) return 0; + if(data[0] == 0 && *witdata_len != 20 && *witdata_len != 32) return 0; + *witver = data[0]; + return 1; +} diff --git a/applications/external/flipbip/lib/crypto/segwit_addr.h b/applications/external/flipbip/lib/crypto/segwit_addr.h new file mode 100644 index 0000000000..2251e5c473 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/segwit_addr.h @@ -0,0 +1,97 @@ +/* Copyright (c) 2017, 2021 Pieter Wuille + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef _SEGWIT_ADDR_H_ +#define _SEGWIT_ADDR_H_ 1 + +#include + +// The maximum length of the Bech32 human-readable part according to BIP-173. +#define BECH32_MAX_HRP_LEN 83 + +/** Encode a SegWit address + * + * Out: output: Pointer to a buffer of size 73 + strlen(hrp) that will be + * updated to contain the null-terminated address. + * In: hrp: Pointer to the null-terminated human readable part to use + * (chain/network specific). + * ver: Version of the witness program (between 0 and 16 inclusive). + * prog: Data bytes for the witness program (between 2 and 40 bytes). + * prog_len: Number of data bytes in prog. + * Returns 1 if successful. + */ +int segwit_addr_encode(char* output, const char* hrp, int ver, const uint8_t* prog, size_t prog_len); + +/** Decode a SegWit address + * + * Out: ver: Pointer to an int that will be updated to contain the witness + * program version (between 0 and 16 inclusive). + * prog: Pointer to a buffer of size 40 that will be updated to + * contain the witness program bytes. + * prog_len: Pointer to a size_t that will be updated to contain the length + * of bytes in prog. + * hrp: Pointer to the null-terminated human readable part that is + * expected (chain/network specific). + * addr: Pointer to the null-terminated address. + * Returns 1 if successful. + */ +int segwit_addr_decode(int* ver, uint8_t* prog, size_t* prog_len, const char* hrp, const char* addr); + +/** Supported encodings. */ +typedef enum { + BECH32_ENCODING_NONE, + BECH32_ENCODING_BECH32, + BECH32_ENCODING_BECH32M +} bech32_encoding; + +/** Encode a Bech32 or Bech32m string + * + * Out: output: Pointer to a buffer of size strlen(hrp) + data_len + 8 that + * will be updated to contain the null-terminated Bech32 string. + * In: hrp : Pointer to the null-terminated human readable part. + * data : Pointer to an array of 5-bit values. + * data_len: Length of the data array. + * enc: Which encoding to use (BECH32_ENCODING_BECH32{,M}). + * Returns 1 if successful. + */ +int bech32_encode( + char* output, + const char* hrp, + const uint8_t* data, + size_t data_len, + bech32_encoding enc); + +/** Decode a Bech32 or Bech32m string + * + * Out: hrp: Pointer to a buffer of size BECH32_MAX_HRP_LEN + 1. Will be + * updated to contain the null-terminated human readable part. + * data: Pointer to a buffer of size strlen(input) - 8 that will + * hold the encoded 5-bit data values. + * data_len: Pointer to a size_t that will be updated to be the number + * of entries in data. + * In: input: Pointer to a null-terminated Bech32 string. + * Returns BECH32_ENCODING_BECH32{,M} to indicate decoding was successful + * with the specified encoding standard. BECH32_ENCODING_NONE is returned if + * decoding failed. + */ +bech32_encoding bech32_decode(char* hrp, uint8_t* data, size_t* data_len, const char* input); + +#endif diff --git a/applications/external/flipbip/lib/crypto/setup.py b/applications/external/flipbip/lib/crypto/setup.py new file mode 100644 index 0000000000..aee3dd4a92 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/setup.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python +from distutils.core import setup +from distutils.extension import Extension + +from Cython.Build import cythonize +from Cython.Distutils import build_ext + +srcs = [ + "nist256p1", + "base58", + "bignum", + "bip32", + "ecdsa", + "curve25519", + "hmac", + "rand", + "ripemd160", + "secp256k1", + "sha2", +] + +extensions = [ + Extension( + "TrezorCrypto", + sources=["TrezorCrypto.pyx", "c.pxd"] + [x + ".c" for x in srcs], + extra_compile_args=[], + ) +] + +setup( + name="TrezorCrypto", + version="0.0.0", + description="Cython wrapper around trezor-crypto library", + author="Pavol Rusnak", + author_email="stick@satoshilabs.com", + url="https://github.com/trezor/trezor-crypto", + cmdclass={"build_ext": build_ext}, + ext_modules=cythonize(extensions), +) diff --git a/applications/external/flipbip/lib/crypto/sha2.c b/applications/external/flipbip/lib/crypto/sha2.c new file mode 100644 index 0000000000..85e589c4d6 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/sha2.c @@ -0,0 +1,1252 @@ +/** + * Copyright (c) 2000-2001 Aaron D. Gifford + * Copyright (c) 2013-2014 Pavol Rusnak + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTOR(S) ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTOR(S) BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include "sha2.h" +#include "memzero.h" +#include "byte_order.h" + +/* + * ASSERT NOTE: + * Some sanity checking code is included using assert(). On my FreeBSD + * system, this additional code can be removed by compiling with NDEBUG + * defined. Check your own systems manpage on assert() to see how to + * compile WITHOUT the sanity checking code on your system. + * + * UNROLLED TRANSFORM LOOP NOTE: + * You can define SHA2_UNROLL_TRANSFORM to use the unrolled transform + * loop version for the hash transform rounds (defined using macros + * later in this file). Either define on the command line, for example: + * + * cc -DSHA2_UNROLL_TRANSFORM -o sha2 sha2.c sha2prog.c + * + * or define below: + * + * #define SHA2_UNROLL_TRANSFORM + * + */ + +/*** SHA-256/384/512 Machine Architecture Definitions *****************/ +/* + * BYTE_ORDER NOTE: + * + * Please make sure that your system defines BYTE_ORDER. If your + * architecture is little-endian, make sure it also defines + * LITTLE_ENDIAN and that the two (BYTE_ORDER and LITTLE_ENDIAN) are + * equivilent. + * + * If your system does not define the above, then you can do so by + * hand like this: + * + * #define LITTLE_ENDIAN 1234 + * #define BIG_ENDIAN 4321 + * + * And for little-endian machines, add: + * + * #define BYTE_ORDER LITTLE_ENDIAN + * + * Or for big-endian machines: + * + * #define BYTE_ORDER BIG_ENDIAN + * + * The FreeBSD machine this was written on defines BYTE_ORDER + * appropriately by including (which in turn includes + * where the appropriate definitions are actually + * made). + */ + +#if !defined(BYTE_ORDER) || (BYTE_ORDER != LITTLE_ENDIAN && BYTE_ORDER != BIG_ENDIAN) +#error Define BYTE_ORDER to be equal to either LITTLE_ENDIAN or BIG_ENDIAN +#endif + +typedef uint8_t sha2_byte; /* Exactly 1 byte */ +typedef uint32_t sha2_word32; /* Exactly 4 bytes */ +typedef uint64_t sha2_word64; /* Exactly 8 bytes */ + +/*** SHA-256/384/512 Various Length Definitions ***********************/ +/* NOTE: Most of these are in sha2.h */ +#define SHA1_SHORT_BLOCK_LENGTH (SHA1_BLOCK_LENGTH - 8) +#define SHA256_SHORT_BLOCK_LENGTH (SHA256_BLOCK_LENGTH - 8) +#define SHA512_SHORT_BLOCK_LENGTH (SHA512_BLOCK_LENGTH - 16) + +/* + * Macro for incrementally adding the unsigned 64-bit integer n to the + * unsigned 128-bit integer (represented using a two-element array of + * 64-bit words): + */ +#define ADDINC128(w, n) \ + { \ + (w)[0] += (sha2_word64)(n); \ + if((w)[0] < (n)) { \ + (w)[1]++; \ + } \ + } + +#define MEMCPY_BCOPY(d, s, l) memcpy((d), (s), (l)) + +/*** THE SIX LOGICAL FUNCTIONS ****************************************/ +/* + * Bit shifting and rotation (used by the six SHA-XYZ logical functions: + * + * NOTE: In the original SHA-256/384/512 document, the shift-right + * function was named R and the rotate-right function was called S. + * (See: http://csrc.nist.gov/cryptval/shs/sha256-384-512.pdf on the + * web.) + * + * The newer NIST FIPS 180-2 document uses a much clearer naming + * scheme, SHR for shift-right, ROTR for rotate-right, and ROTL for + * rotate-left. (See: + * http://csrc.nist.gov/publications/fips/fips180-2/fips180-2.pdf + * on the web.) + * + * WARNING: These macros must be used cautiously, since they reference + * supplied parameters sometimes more than once, and thus could have + * unexpected side-effects if used without taking this into account. + */ + +/* Shift-right (used in SHA-256, SHA-384, and SHA-512): */ +#define SHR(b, x) ((x) >> (b)) +/* 32-bit Rotate-right (used in SHA-256): */ +#define ROTR32(b, x) (((x) >> (b)) | ((x) << (32 - (b)))) +/* 64-bit Rotate-right (used in SHA-384 and SHA-512): */ +#define ROTR64(b, x) (((x) >> (b)) | ((x) << (64 - (b)))) +/* 32-bit Rotate-left (used in SHA-1): */ +#define ROTL32(b, x) (((x) << (b)) | ((x) >> (32 - (b)))) + +/* Two of six logical functions used in SHA-1, SHA-256, SHA-384, and SHA-512: */ +#define Ch(x, y, z) (((x) & (y)) ^ ((~(x)) & (z))) +#define Maj(x, y, z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) + +/* Function used in SHA-1: */ +#define Parity(x, y, z) ((x) ^ (y) ^ (z)) + +/* Four of six logical functions used in SHA-256: */ +#define Sigma0_256(x) (ROTR32(2, (x)) ^ ROTR32(13, (x)) ^ ROTR32(22, (x))) +#define Sigma1_256(x) (ROTR32(6, (x)) ^ ROTR32(11, (x)) ^ ROTR32(25, (x))) +#define sigma0_256(x) (ROTR32(7, (x)) ^ ROTR32(18, (x)) ^ SHR(3, (x))) +#define sigma1_256(x) (ROTR32(17, (x)) ^ ROTR32(19, (x)) ^ SHR(10, (x))) + +/* Four of six logical functions used in SHA-384 and SHA-512: */ +#define Sigma0_512(x) (ROTR64(28, (x)) ^ ROTR64(34, (x)) ^ ROTR64(39, (x))) +#define Sigma1_512(x) (ROTR64(14, (x)) ^ ROTR64(18, (x)) ^ ROTR64(41, (x))) +#define sigma0_512(x) (ROTR64(1, (x)) ^ ROTR64(8, (x)) ^ SHR(7, (x))) +#define sigma1_512(x) (ROTR64(19, (x)) ^ ROTR64(61, (x)) ^ SHR(6, (x))) + +/*** INTERNAL FUNCTION PROTOTYPES *************************************/ +/* NOTE: These should not be accessed directly from outside this + * library -- they are intended for private internal visibility/use + * only. + */ +static void sha512_Last(SHA512_CTX*); + +/*** SHA-XYZ INITIAL HASH VALUES AND CONSTANTS ************************/ + +/* Hash constant words K for SHA-1: */ +#define K1_0_TO_19 0x5a827999UL +#define K1_20_TO_39 0x6ed9eba1UL +#define K1_40_TO_59 0x8f1bbcdcUL +#define K1_60_TO_79 0xca62c1d6UL + +/* Initial hash value H for SHA-1: */ +const sha2_word32 sha1_initial_hash_value[SHA1_DIGEST_LENGTH / sizeof(sha2_word32)] = + {0x67452301UL, 0xefcdab89UL, 0x98badcfeUL, 0x10325476UL, 0xc3d2e1f0UL}; + +/* Hash constant words K for SHA-256: */ +static const sha2_word32 K256[64] = { + 0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL, 0x3956c25bUL, 0x59f111f1UL, + 0x923f82a4UL, 0xab1c5ed5UL, 0xd807aa98UL, 0x12835b01UL, 0x243185beUL, 0x550c7dc3UL, + 0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL, 0xc19bf174UL, 0xe49b69c1UL, 0xefbe4786UL, + 0x0fc19dc6UL, 0x240ca1ccUL, 0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL, + 0x983e5152UL, 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL, 0xc6e00bf3UL, 0xd5a79147UL, + 0x06ca6351UL, 0x14292967UL, 0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL, 0x53380d13UL, + 0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL, 0xa2bfe8a1UL, 0xa81a664bUL, + 0xc24b8b70UL, 0xc76c51a3UL, 0xd192e819UL, 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL, + 0x19a4c116UL, 0x1e376c08UL, 0x2748774cUL, 0x34b0bcb5UL, 0x391c0cb3UL, 0x4ed8aa4aUL, + 0x5b9cca4fUL, 0x682e6ff3UL, 0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL, + 0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL}; + +/* Initial hash value H for SHA-256: */ +const sha2_word32 sha256_initial_hash_value[8] = { + 0x6a09e667UL, + 0xbb67ae85UL, + 0x3c6ef372UL, + 0xa54ff53aUL, + 0x510e527fUL, + 0x9b05688cUL, + 0x1f83d9abUL, + 0x5be0cd19UL}; + +/* Hash constant words K for SHA-384 and SHA-512: */ +static const sha2_word64 K512[80] = { + 0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL, 0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL, + 0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL, 0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL, + 0xd807aa98a3030242ULL, 0x12835b0145706fbeULL, 0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL, + 0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL, 0x9bdc06a725c71235ULL, 0xc19bf174cf692694ULL, + 0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL, 0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL, + 0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL, 0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL, + 0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL, 0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL, + 0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL, 0x06ca6351e003826fULL, 0x142929670a0e6e70ULL, + 0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL, 0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL, + 0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL, 0x81c2c92e47edaee6ULL, 0x92722c851482353bULL, + 0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL, 0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL, + 0xd192e819d6ef5218ULL, 0xd69906245565a910ULL, 0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL, + 0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL, 0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL, + 0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL, 0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL, + 0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL, 0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL, + 0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL, 0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL, + 0xca273eceea26619cULL, 0xd186b8c721c0c207ULL, 0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL, + 0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL, 0x113f9804bef90daeULL, 0x1b710b35131c471bULL, + 0x28db77f523047d84ULL, 0x32caab7b40c72493ULL, 0x3c9ebe0a15c9bebcULL, 0x431d67c49c100d4cULL, + 0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL, 0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL}; + +/* Initial hash value H for SHA-512 */ +const sha2_word64 sha512_initial_hash_value[8] = { + 0x6a09e667f3bcc908ULL, + 0xbb67ae8584caa73bULL, + 0x3c6ef372fe94f82bULL, + 0xa54ff53a5f1d36f1ULL, + 0x510e527fade682d1ULL, + 0x9b05688c2b3e6c1fULL, + 0x1f83d9abfb41bd6bULL, + 0x5be0cd19137e2179ULL}; + +/* + * Constant used by SHA256/384/512_End() functions for converting the + * digest to a readable hexadecimal character string: + */ +static const char* sha2_hex_digits = "0123456789abcdef"; + +/*** SHA-1: ***********************************************************/ +void sha1_Init(SHA1_CTX* context) { + MEMCPY_BCOPY(context->state, sha1_initial_hash_value, SHA1_DIGEST_LENGTH); + memzero(context->buffer, SHA1_BLOCK_LENGTH); + context->bitcount = 0; +} + +#ifdef SHA2_UNROLL_TRANSFORM + +/* Unrolled SHA-1 round macros: */ + +#define ROUND1_0_TO_15(a, b, c, d, e) \ + (e) = ROTL32(5, (a)) + Ch((b), (c), (d)) + (e) + K1_0_TO_19 + (W1[j] = *data++); \ + (b) = ROTL32(30, (b)); \ + j++; + +#define ROUND1_16_TO_19(a, b, c, d, e) \ + T1 = W1[(j + 13) & 0x0f] ^ W1[(j + 8) & 0x0f] ^ W1[(j + 2) & 0x0f] ^ W1[j & 0x0f]; \ + (e) = ROTL32(5, a) + Ch(b, c, d) + e + K1_0_TO_19 + (W1[j & 0x0f] = ROTL32(1, T1)); \ + (b) = ROTL32(30, b); \ + j++; + +#define ROUND1_20_TO_39(a, b, c, d, e) \ + T1 = W1[(j + 13) & 0x0f] ^ W1[(j + 8) & 0x0f] ^ W1[(j + 2) & 0x0f] ^ W1[j & 0x0f]; \ + (e) = ROTL32(5, a) + Parity(b, c, d) + e + K1_20_TO_39 + (W1[j & 0x0f] = ROTL32(1, T1)); \ + (b) = ROTL32(30, b); \ + j++; + +#define ROUND1_40_TO_59(a, b, c, d, e) \ + T1 = W1[(j + 13) & 0x0f] ^ W1[(j + 8) & 0x0f] ^ W1[(j + 2) & 0x0f] ^ W1[j & 0x0f]; \ + (e) = ROTL32(5, a) + Maj(b, c, d) + e + K1_40_TO_59 + (W1[j & 0x0f] = ROTL32(1, T1)); \ + (b) = ROTL32(30, b); \ + j++; + +#define ROUND1_60_TO_79(a, b, c, d, e) \ + T1 = W1[(j + 13) & 0x0f] ^ W1[(j + 8) & 0x0f] ^ W1[(j + 2) & 0x0f] ^ W1[j & 0x0f]; \ + (e) = ROTL32(5, a) + Parity(b, c, d) + e + K1_60_TO_79 + (W1[j & 0x0f] = ROTL32(1, T1)); \ + (b) = ROTL32(30, b); \ + j++; + +void sha1_Transform(const sha2_word32* state_in, const sha2_word32* data, sha2_word32* state_out) { + sha2_word32 a = 0, b = 0, c = 0, d = 0, e = 0; + sha2_word32 T1 = 0; + sha2_word32 W1[16] = {0}; + int j = 0; + + /* Initialize registers with the prev. intermediate value */ + a = state_in[0]; + b = state_in[1]; + c = state_in[2]; + d = state_in[3]; + e = state_in[4]; + + j = 0; + + /* Rounds 0 to 15 unrolled: */ + ROUND1_0_TO_15(a, b, c, d, e); + ROUND1_0_TO_15(e, a, b, c, d); + ROUND1_0_TO_15(d, e, a, b, c); + ROUND1_0_TO_15(c, d, e, a, b); + ROUND1_0_TO_15(b, c, d, e, a); + ROUND1_0_TO_15(a, b, c, d, e); + ROUND1_0_TO_15(e, a, b, c, d); + ROUND1_0_TO_15(d, e, a, b, c); + ROUND1_0_TO_15(c, d, e, a, b); + ROUND1_0_TO_15(b, c, d, e, a); + ROUND1_0_TO_15(a, b, c, d, e); + ROUND1_0_TO_15(e, a, b, c, d); + ROUND1_0_TO_15(d, e, a, b, c); + ROUND1_0_TO_15(c, d, e, a, b); + ROUND1_0_TO_15(b, c, d, e, a); + ROUND1_0_TO_15(a, b, c, d, e); + + /* Rounds 16 to 19 unrolled: */ + ROUND1_16_TO_19(e, a, b, c, d); + ROUND1_16_TO_19(d, e, a, b, c); + ROUND1_16_TO_19(c, d, e, a, b); + ROUND1_16_TO_19(b, c, d, e, a); + + /* Rounds 20 to 39 unrolled: */ + ROUND1_20_TO_39(a, b, c, d, e); + ROUND1_20_TO_39(e, a, b, c, d); + ROUND1_20_TO_39(d, e, a, b, c); + ROUND1_20_TO_39(c, d, e, a, b); + ROUND1_20_TO_39(b, c, d, e, a); + ROUND1_20_TO_39(a, b, c, d, e); + ROUND1_20_TO_39(e, a, b, c, d); + ROUND1_20_TO_39(d, e, a, b, c); + ROUND1_20_TO_39(c, d, e, a, b); + ROUND1_20_TO_39(b, c, d, e, a); + ROUND1_20_TO_39(a, b, c, d, e); + ROUND1_20_TO_39(e, a, b, c, d); + ROUND1_20_TO_39(d, e, a, b, c); + ROUND1_20_TO_39(c, d, e, a, b); + ROUND1_20_TO_39(b, c, d, e, a); + ROUND1_20_TO_39(a, b, c, d, e); + ROUND1_20_TO_39(e, a, b, c, d); + ROUND1_20_TO_39(d, e, a, b, c); + ROUND1_20_TO_39(c, d, e, a, b); + ROUND1_20_TO_39(b, c, d, e, a); + + /* Rounds 40 to 59 unrolled: */ + ROUND1_40_TO_59(a, b, c, d, e); + ROUND1_40_TO_59(e, a, b, c, d); + ROUND1_40_TO_59(d, e, a, b, c); + ROUND1_40_TO_59(c, d, e, a, b); + ROUND1_40_TO_59(b, c, d, e, a); + ROUND1_40_TO_59(a, b, c, d, e); + ROUND1_40_TO_59(e, a, b, c, d); + ROUND1_40_TO_59(d, e, a, b, c); + ROUND1_40_TO_59(c, d, e, a, b); + ROUND1_40_TO_59(b, c, d, e, a); + ROUND1_40_TO_59(a, b, c, d, e); + ROUND1_40_TO_59(e, a, b, c, d); + ROUND1_40_TO_59(d, e, a, b, c); + ROUND1_40_TO_59(c, d, e, a, b); + ROUND1_40_TO_59(b, c, d, e, a); + ROUND1_40_TO_59(a, b, c, d, e); + ROUND1_40_TO_59(e, a, b, c, d); + ROUND1_40_TO_59(d, e, a, b, c); + ROUND1_40_TO_59(c, d, e, a, b); + ROUND1_40_TO_59(b, c, d, e, a); + + /* Rounds 60 to 79 unrolled: */ + ROUND1_60_TO_79(a, b, c, d, e); + ROUND1_60_TO_79(e, a, b, c, d); + ROUND1_60_TO_79(d, e, a, b, c); + ROUND1_60_TO_79(c, d, e, a, b); + ROUND1_60_TO_79(b, c, d, e, a); + ROUND1_60_TO_79(a, b, c, d, e); + ROUND1_60_TO_79(e, a, b, c, d); + ROUND1_60_TO_79(d, e, a, b, c); + ROUND1_60_TO_79(c, d, e, a, b); + ROUND1_60_TO_79(b, c, d, e, a); + ROUND1_60_TO_79(a, b, c, d, e); + ROUND1_60_TO_79(e, a, b, c, d); + ROUND1_60_TO_79(d, e, a, b, c); + ROUND1_60_TO_79(c, d, e, a, b); + ROUND1_60_TO_79(b, c, d, e, a); + ROUND1_60_TO_79(a, b, c, d, e); + ROUND1_60_TO_79(e, a, b, c, d); + ROUND1_60_TO_79(d, e, a, b, c); + ROUND1_60_TO_79(c, d, e, a, b); + ROUND1_60_TO_79(b, c, d, e, a); + + /* Compute the current intermediate hash value */ + state_out[0] = state_in[0] + a; + state_out[1] = state_in[1] + b; + state_out[2] = state_in[2] + c; + state_out[3] = state_in[3] + d; + state_out[4] = state_in[4] + e; + + /* Clean up */ + a = b = c = d = e = T1 = 0; +} + +#else /* SHA2_UNROLL_TRANSFORM */ + +void sha1_Transform(const sha2_word32* state_in, const sha2_word32* data, sha2_word32* state_out) { + sha2_word32 a = 0, b = 0, c = 0, d = 0, e = 0; + sha2_word32 T1 = 0; + sha2_word32 W1[16] = {0}; + int j = 0; + + /* Initialize registers with the prev. intermediate value */ + a = state_in[0]; + b = state_in[1]; + c = state_in[2]; + d = state_in[3]; + e = state_in[4]; + j = 0; + do { + T1 = ROTL32(5, a) + Ch(b, c, d) + e + K1_0_TO_19 + (W1[j] = *data++); + e = d; + d = c; + c = ROTL32(30, b); + b = a; + a = T1; + j++; + } while(j < 16); + + do { + T1 = W1[(j + 13) & 0x0f] ^ W1[(j + 8) & 0x0f] ^ W1[(j + 2) & 0x0f] ^ W1[j & 0x0f]; + T1 = ROTL32(5, a) + Ch(b, c, d) + e + K1_0_TO_19 + (W1[j & 0x0f] = ROTL32(1, T1)); + e = d; + d = c; + c = ROTL32(30, b); + b = a; + a = T1; + j++; + } while(j < 20); + + do { + T1 = W1[(j + 13) & 0x0f] ^ W1[(j + 8) & 0x0f] ^ W1[(j + 2) & 0x0f] ^ W1[j & 0x0f]; + T1 = ROTL32(5, a) + Parity(b, c, d) + e + K1_20_TO_39 + (W1[j & 0x0f] = ROTL32(1, T1)); + e = d; + d = c; + c = ROTL32(30, b); + b = a; + a = T1; + j++; + } while(j < 40); + + do { + T1 = W1[(j + 13) & 0x0f] ^ W1[(j + 8) & 0x0f] ^ W1[(j + 2) & 0x0f] ^ W1[j & 0x0f]; + T1 = ROTL32(5, a) + Maj(b, c, d) + e + K1_40_TO_59 + (W1[j & 0x0f] = ROTL32(1, T1)); + e = d; + d = c; + c = ROTL32(30, b); + b = a; + a = T1; + j++; + } while(j < 60); + + do { + T1 = W1[(j + 13) & 0x0f] ^ W1[(j + 8) & 0x0f] ^ W1[(j + 2) & 0x0f] ^ W1[j & 0x0f]; + T1 = ROTL32(5, a) + Parity(b, c, d) + e + K1_60_TO_79 + (W1[j & 0x0f] = ROTL32(1, T1)); + e = d; + d = c; + c = ROTL32(30, b); + b = a; + a = T1; + j++; + } while(j < 80); + + /* Compute the current intermediate hash value */ + state_out[0] = state_in[0] + a; + state_out[1] = state_in[1] + b; + state_out[2] = state_in[2] + c; + state_out[3] = state_in[3] + d; + state_out[4] = state_in[4] + e; + + /* Clean up */ + a = b = c = d = e = T1 = 0; +} + +#endif /* SHA2_UNROLL_TRANSFORM */ + +void sha1_Update(SHA1_CTX* context, const sha2_byte* data, size_t len) { + unsigned int freespace = 0, usedspace = 0; + + if(len == 0) { + /* Calling with no data is valid - we do nothing */ + return; + } + + usedspace = (context->bitcount >> 3) % SHA1_BLOCK_LENGTH; + if(usedspace > 0) { + /* Calculate how much free space is available in the buffer */ + freespace = SHA1_BLOCK_LENGTH - usedspace; + + if(len >= freespace) { + /* Fill the buffer completely and process it */ + MEMCPY_BCOPY(((uint8_t*)context->buffer) + usedspace, data, freespace); + context->bitcount += freespace << 3; + len -= freespace; + data += freespace; +#if BYTE_ORDER == LITTLE_ENDIAN + /* Convert TO host byte order */ + for(int j = 0; j < 16; j++) { + REVERSE32(context->buffer[j], context->buffer[j]); + } +#endif + sha1_Transform(context->state, context->buffer, context->state); + } else { + /* The buffer is not yet full */ + MEMCPY_BCOPY(((uint8_t*)context->buffer) + usedspace, data, len); + context->bitcount += len << 3; + /* Clean up: */ + usedspace = freespace = 0; + return; + } + } + while(len >= SHA1_BLOCK_LENGTH) { + /* Process as many complete blocks as we can */ + MEMCPY_BCOPY(context->buffer, data, SHA1_BLOCK_LENGTH); +#if BYTE_ORDER == LITTLE_ENDIAN + /* Convert TO host byte order */ + for(int j = 0; j < 16; j++) { + REVERSE32(context->buffer[j], context->buffer[j]); + } +#endif + sha1_Transform(context->state, context->buffer, context->state); + context->bitcount += SHA1_BLOCK_LENGTH << 3; + len -= SHA1_BLOCK_LENGTH; + data += SHA1_BLOCK_LENGTH; + } + if(len > 0) { + /* There's left-overs, so save 'em */ + MEMCPY_BCOPY(context->buffer, data, len); + context->bitcount += len << 3; + } + /* Clean up: */ + usedspace = freespace = 0; +} + +void sha1_Final(SHA1_CTX* context, sha2_byte digest[SHA1_DIGEST_LENGTH]) { + unsigned int usedspace = 0; + + /* If no digest buffer is passed, we don't bother doing this: */ + if(digest != (sha2_byte*)0) { + usedspace = (context->bitcount >> 3) % SHA1_BLOCK_LENGTH; + /* Begin padding with a 1 bit: */ + ((uint8_t*)context->buffer)[usedspace++] = 0x80; + + if(usedspace > SHA1_SHORT_BLOCK_LENGTH) { + memzero(((uint8_t*)context->buffer) + usedspace, SHA1_BLOCK_LENGTH - usedspace); + +#if BYTE_ORDER == LITTLE_ENDIAN + /* Convert TO host byte order */ + for(int j = 0; j < 16; j++) { + REVERSE32(context->buffer[j], context->buffer[j]); + } +#endif + /* Do second-to-last transform: */ + sha1_Transform(context->state, context->buffer, context->state); + + /* And prepare the last transform: */ + usedspace = 0; + } + /* Set-up for the last transform: */ + memzero(((uint8_t*)context->buffer) + usedspace, SHA1_SHORT_BLOCK_LENGTH - usedspace); + +#if BYTE_ORDER == LITTLE_ENDIAN + /* Convert TO host byte order */ + for(int j = 0; j < 14; j++) { + REVERSE32(context->buffer[j], context->buffer[j]); + } +#endif + /* Set the bit count: */ + context->buffer[14] = context->bitcount >> 32; + context->buffer[15] = context->bitcount & 0xffffffff; + + /* Final transform: */ + sha1_Transform(context->state, context->buffer, context->state); + +#if BYTE_ORDER == LITTLE_ENDIAN + /* Convert FROM host byte order */ + for(int j = 0; j < 5; j++) { + REVERSE32(context->state[j], context->state[j]); + } +#endif + MEMCPY_BCOPY(digest, context->state, SHA1_DIGEST_LENGTH); + } + + /* Clean up state data: */ + memzero(context, sizeof(SHA1_CTX)); + usedspace = 0; +} + +char* sha1_End(SHA1_CTX* context, char buffer[SHA1_DIGEST_STRING_LENGTH]) { + sha2_byte digest[SHA1_DIGEST_LENGTH] = {0}, *d = digest; + int i = 0; + + if(buffer != (char*)0) { + sha1_Final(context, digest); + + for(i = 0; i < SHA1_DIGEST_LENGTH; i++) { + *buffer++ = sha2_hex_digits[(*d & 0xf0) >> 4]; + *buffer++ = sha2_hex_digits[*d & 0x0f]; + d++; + } + *buffer = (char)0; + } else { + memzero(context, sizeof(SHA1_CTX)); + } + memzero(digest, SHA1_DIGEST_LENGTH); + return buffer; +} + +void sha1_Raw(const sha2_byte* data, size_t len, uint8_t digest[SHA1_DIGEST_LENGTH]) { + SHA1_CTX context = {0}; + sha1_Init(&context); + sha1_Update(&context, data, len); + sha1_Final(&context, digest); +} + +char* sha1_Data(const sha2_byte* data, size_t len, char digest[SHA1_DIGEST_STRING_LENGTH]) { + SHA1_CTX context = {0}; + + sha1_Init(&context); + sha1_Update(&context, data, len); + return sha1_End(&context, digest); +} + +/*** SHA-256: *********************************************************/ +void sha256_Init(SHA256_CTX* context) { + if(context == (SHA256_CTX*)0) { + return; + } + MEMCPY_BCOPY(context->state, sha256_initial_hash_value, SHA256_DIGEST_LENGTH); + memzero(context->buffer, SHA256_BLOCK_LENGTH); + context->bitcount = 0; +} + +void sha256_Init_ex(SHA256_CTX* context, const uint32_t state[8], uint64_t bitcount) { + if(context == (SHA256_CTX*)0) { + return; + } + MEMCPY_BCOPY(context->state, state, SHA256_DIGEST_LENGTH); + memzero(context->buffer, SHA256_BLOCK_LENGTH); + context->bitcount = bitcount; +} + +#ifdef SHA2_UNROLL_TRANSFORM + +/* Unrolled SHA-256 round macros: */ + +#define ROUND256_0_TO_15(a, b, c, d, e, f, g, h) \ + T1 = (h) + Sigma1_256(e) + Ch((e), (f), (g)) + K256[j] + (W256[j] = *data++); \ + (d) += T1; \ + (h) = T1 + Sigma0_256(a) + Maj((a), (b), (c)); \ + j++ + +#define ROUND256(a, b, c, d, e, f, g, h) \ + s0 = W256[(j + 1) & 0x0f]; \ + s0 = sigma0_256(s0); \ + s1 = W256[(j + 14) & 0x0f]; \ + s1 = sigma1_256(s1); \ + T1 = (h) + Sigma1_256(e) + Ch((e), (f), (g)) + K256[j] + \ + (W256[j & 0x0f] += s1 + W256[(j + 9) & 0x0f] + s0); \ + (d) += T1; \ + (h) = T1 + Sigma0_256(a) + Maj((a), (b), (c)); \ + j++ + +void sha256_Transform(const sha2_word32* state_in, const sha2_word32* data, sha2_word32* state_out) { + sha2_word32 a = 0, b = 0, c = 0, d = 0, e = 0, f = 0, g = 0, h = 0, s0 = 0, s1 = 0; + sha2_word32 T1 = 0; + sha2_word32 W256[16] = {0}; + int j = 0; + + /* Initialize registers with the prev. intermediate value */ + a = state_in[0]; + b = state_in[1]; + c = state_in[2]; + d = state_in[3]; + e = state_in[4]; + f = state_in[5]; + g = state_in[6]; + h = state_in[7]; + + j = 0; + do { + /* Rounds 0 to 15 (unrolled): */ + ROUND256_0_TO_15(a, b, c, d, e, f, g, h); + ROUND256_0_TO_15(h, a, b, c, d, e, f, g); + ROUND256_0_TO_15(g, h, a, b, c, d, e, f); + ROUND256_0_TO_15(f, g, h, a, b, c, d, e); + ROUND256_0_TO_15(e, f, g, h, a, b, c, d); + ROUND256_0_TO_15(d, e, f, g, h, a, b, c); + ROUND256_0_TO_15(c, d, e, f, g, h, a, b); + ROUND256_0_TO_15(b, c, d, e, f, g, h, a); + } while(j < 16); + + /* Now for the remaining rounds to 64: */ + do { + ROUND256(a, b, c, d, e, f, g, h); + ROUND256(h, a, b, c, d, e, f, g); + ROUND256(g, h, a, b, c, d, e, f); + ROUND256(f, g, h, a, b, c, d, e); + ROUND256(e, f, g, h, a, b, c, d); + ROUND256(d, e, f, g, h, a, b, c); + ROUND256(c, d, e, f, g, h, a, b); + ROUND256(b, c, d, e, f, g, h, a); + } while(j < 64); + + /* Compute the current intermediate hash value */ + state_out[0] = state_in[0] + a; + state_out[1] = state_in[1] + b; + state_out[2] = state_in[2] + c; + state_out[3] = state_in[3] + d; + state_out[4] = state_in[4] + e; + state_out[5] = state_in[5] + f; + state_out[6] = state_in[6] + g; + state_out[7] = state_in[7] + h; + + /* Clean up */ + a = b = c = d = e = f = g = h = T1 = 0; +} + +#else /* SHA2_UNROLL_TRANSFORM */ + +void sha256_Transform(const sha2_word32* state_in, const sha2_word32* data, sha2_word32* state_out) { + sha2_word32 a = 0, b = 0, c = 0, d = 0, e = 0, f = 0, g = 0, h = 0, s0 = 0, s1 = 0; + sha2_word32 T1 = 0, T2 = 0, W256[16] = {0}; + int j = 0; + + /* Initialize registers with the prev. intermediate value */ + a = state_in[0]; + b = state_in[1]; + c = state_in[2]; + d = state_in[3]; + e = state_in[4]; + f = state_in[5]; + g = state_in[6]; + h = state_in[7]; + + j = 0; + do { + /* Apply the SHA-256 compression function to update a..h with copy */ + T1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[j] + (W256[j] = *data++); + T2 = Sigma0_256(a) + Maj(a, b, c); + h = g; + g = f; + f = e; + e = d + T1; + d = c; + c = b; + b = a; + a = T1 + T2; + + j++; + } while(j < 16); + + do { + /* Part of the message block expansion: */ + s0 = W256[(j + 1) & 0x0f]; + s0 = sigma0_256(s0); + s1 = W256[(j + 14) & 0x0f]; + s1 = sigma1_256(s1); + + /* Apply the SHA-256 compression function to update a..h */ + T1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[j] + + (W256[j & 0x0f] += s1 + W256[(j + 9) & 0x0f] + s0); + T2 = Sigma0_256(a) + Maj(a, b, c); + h = g; + g = f; + f = e; + e = d + T1; + d = c; + c = b; + b = a; + a = T1 + T2; + + j++; + } while(j < 64); + + /* Compute the current intermediate hash value */ + state_out[0] = state_in[0] + a; + state_out[1] = state_in[1] + b; + state_out[2] = state_in[2] + c; + state_out[3] = state_in[3] + d; + state_out[4] = state_in[4] + e; + state_out[5] = state_in[5] + f; + state_out[6] = state_in[6] + g; + state_out[7] = state_in[7] + h; + + /* Clean up */ + a = b = c = d = e = f = g = h = T1 = T2 = 0; +} + +#endif /* SHA2_UNROLL_TRANSFORM */ + +void sha256_Update(SHA256_CTX* context, const sha2_byte* data, size_t len) { + unsigned int freespace = 0, usedspace = 0; + + if(len == 0) { + /* Calling with no data is valid - we do nothing */ + return; + } + + usedspace = (context->bitcount >> 3) % SHA256_BLOCK_LENGTH; + if(usedspace > 0) { + /* Calculate how much free space is available in the buffer */ + freespace = SHA256_BLOCK_LENGTH - usedspace; + + if(len >= freespace) { + /* Fill the buffer completely and process it */ + MEMCPY_BCOPY(((uint8_t*)context->buffer) + usedspace, data, freespace); + context->bitcount += freespace << 3; + len -= freespace; + data += freespace; +#if BYTE_ORDER == LITTLE_ENDIAN + /* Convert TO host byte order */ + for(int j = 0; j < 16; j++) { + REVERSE32(context->buffer[j], context->buffer[j]); + } +#endif + sha256_Transform(context->state, context->buffer, context->state); + } else { + /* The buffer is not yet full */ + MEMCPY_BCOPY(((uint8_t*)context->buffer) + usedspace, data, len); + context->bitcount += len << 3; + /* Clean up: */ + usedspace = freespace = 0; + return; + } + } + while(len >= SHA256_BLOCK_LENGTH) { + /* Process as many complete blocks as we can */ + MEMCPY_BCOPY(context->buffer, data, SHA256_BLOCK_LENGTH); +#if BYTE_ORDER == LITTLE_ENDIAN + /* Convert TO host byte order */ + for(int j = 0; j < 16; j++) { + REVERSE32(context->buffer[j], context->buffer[j]); + } +#endif + sha256_Transform(context->state, context->buffer, context->state); + context->bitcount += SHA256_BLOCK_LENGTH << 3; + len -= SHA256_BLOCK_LENGTH; + data += SHA256_BLOCK_LENGTH; + } + if(len > 0) { + /* There's left-overs, so save 'em */ + MEMCPY_BCOPY(context->buffer, data, len); + context->bitcount += len << 3; + } + /* Clean up: */ + usedspace = freespace = 0; +} + +void sha256_Final(SHA256_CTX* context, sha2_byte digest[SHA256_DIGEST_LENGTH]) { + unsigned int usedspace = 0; + + /* If no digest buffer is passed, we don't bother doing this: */ + if(digest != (sha2_byte*)0) { + usedspace = (context->bitcount >> 3) % SHA256_BLOCK_LENGTH; + /* Begin padding with a 1 bit: */ + ((uint8_t*)context->buffer)[usedspace++] = 0x80; + + if(usedspace > SHA256_SHORT_BLOCK_LENGTH) { + memzero(((uint8_t*)context->buffer) + usedspace, SHA256_BLOCK_LENGTH - usedspace); + +#if BYTE_ORDER == LITTLE_ENDIAN + /* Convert TO host byte order */ + for(int j = 0; j < 16; j++) { + REVERSE32(context->buffer[j], context->buffer[j]); + } +#endif + /* Do second-to-last transform: */ + sha256_Transform(context->state, context->buffer, context->state); + + /* And prepare the last transform: */ + usedspace = 0; + } + /* Set-up for the last transform: */ + memzero(((uint8_t*)context->buffer) + usedspace, SHA256_SHORT_BLOCK_LENGTH - usedspace); + +#if BYTE_ORDER == LITTLE_ENDIAN + /* Convert TO host byte order */ + for(int j = 0; j < 14; j++) { + REVERSE32(context->buffer[j], context->buffer[j]); + } +#endif + /* Set the bit count: */ + context->buffer[14] = context->bitcount >> 32; + context->buffer[15] = context->bitcount & 0xffffffff; + + /* Final transform: */ + sha256_Transform(context->state, context->buffer, context->state); + +#if BYTE_ORDER == LITTLE_ENDIAN + /* Convert FROM host byte order */ + for(int j = 0; j < 8; j++) { + REVERSE32(context->state[j], context->state[j]); + } +#endif + MEMCPY_BCOPY(digest, context->state, SHA256_DIGEST_LENGTH); + } + + /* Clean up state data: */ + memzero(context, sizeof(SHA256_CTX)); + usedspace = 0; +} + +char* sha256_End(SHA256_CTX* context, char buffer[SHA256_DIGEST_STRING_LENGTH]) { + sha2_byte digest[SHA256_DIGEST_LENGTH] = {0}, *d = digest; + int i = 0; + + if(buffer != (char*)0) { + sha256_Final(context, digest); + + for(i = 0; i < SHA256_DIGEST_LENGTH; i++) { + *buffer++ = sha2_hex_digits[(*d & 0xf0) >> 4]; + *buffer++ = sha2_hex_digits[*d & 0x0f]; + d++; + } + *buffer = (char)0; + } else { + memzero(context, sizeof(SHA256_CTX)); + } + memzero(digest, SHA256_DIGEST_LENGTH); + return buffer; +} + +void sha256_Raw(const sha2_byte* data, size_t len, uint8_t digest[SHA256_DIGEST_LENGTH]) { + SHA256_CTX context = {0}; + sha256_Init(&context); + sha256_Update(&context, data, len); + sha256_Final(&context, digest); +} + +char* sha256_Data(const sha2_byte* data, size_t len, char digest[SHA256_DIGEST_STRING_LENGTH]) { + SHA256_CTX context = {0}; + + sha256_Init(&context); + sha256_Update(&context, data, len); + return sha256_End(&context, digest); +} + +/*** SHA-512: *********************************************************/ +void sha512_Init(SHA512_CTX* context) { + if(context == (SHA512_CTX*)0) { + return; + } + MEMCPY_BCOPY(context->state, sha512_initial_hash_value, SHA512_DIGEST_LENGTH); + memzero(context->buffer, SHA512_BLOCK_LENGTH); + context->bitcount[0] = context->bitcount[1] = 0; +} + +#ifdef SHA2_UNROLL_TRANSFORM + +/* Unrolled SHA-512 round macros: */ +#define ROUND512_0_TO_15(a, b, c, d, e, f, g, h) \ + T1 = (h) + Sigma1_512(e) + Ch((e), (f), (g)) + K512[j] + (W512[j] = *data++); \ + (d) += T1; \ + (h) = T1 + Sigma0_512(a) + Maj((a), (b), (c)); \ + j++ + +#define ROUND512(a, b, c, d, e, f, g, h) \ + s0 = W512[(j + 1) & 0x0f]; \ + s0 = sigma0_512(s0); \ + s1 = W512[(j + 14) & 0x0f]; \ + s1 = sigma1_512(s1); \ + T1 = (h) + Sigma1_512(e) + Ch((e), (f), (g)) + K512[j] + \ + (W512[j & 0x0f] += s1 + W512[(j + 9) & 0x0f] + s0); \ + (d) += T1; \ + (h) = T1 + Sigma0_512(a) + Maj((a), (b), (c)); \ + j++ + +void sha512_Transform(const sha2_word64* state_in, const sha2_word64* data, sha2_word64* state_out) { + sha2_word64 a = 0, b = 0, c = 0, d = 0, e = 0, f = 0, g = 0, h = 0, s0 = 0, s1 = 0; + sha2_word64 T1 = 0, W512[16] = {0}; + int j = 0; + + /* Initialize registers with the prev. intermediate value */ + a = state_in[0]; + b = state_in[1]; + c = state_in[2]; + d = state_in[3]; + e = state_in[4]; + f = state_in[5]; + g = state_in[6]; + h = state_in[7]; + + j = 0; + do { + ROUND512_0_TO_15(a, b, c, d, e, f, g, h); + ROUND512_0_TO_15(h, a, b, c, d, e, f, g); + ROUND512_0_TO_15(g, h, a, b, c, d, e, f); + ROUND512_0_TO_15(f, g, h, a, b, c, d, e); + ROUND512_0_TO_15(e, f, g, h, a, b, c, d); + ROUND512_0_TO_15(d, e, f, g, h, a, b, c); + ROUND512_0_TO_15(c, d, e, f, g, h, a, b); + ROUND512_0_TO_15(b, c, d, e, f, g, h, a); + } while(j < 16); + + /* Now for the remaining rounds up to 79: */ + do { + ROUND512(a, b, c, d, e, f, g, h); + ROUND512(h, a, b, c, d, e, f, g); + ROUND512(g, h, a, b, c, d, e, f); + ROUND512(f, g, h, a, b, c, d, e); + ROUND512(e, f, g, h, a, b, c, d); + ROUND512(d, e, f, g, h, a, b, c); + ROUND512(c, d, e, f, g, h, a, b); + ROUND512(b, c, d, e, f, g, h, a); + } while(j < 80); + + /* Compute the current intermediate hash value */ + state_out[0] = state_in[0] + a; + state_out[1] = state_in[1] + b; + state_out[2] = state_in[2] + c; + state_out[3] = state_in[3] + d; + state_out[4] = state_in[4] + e; + state_out[5] = state_in[5] + f; + state_out[6] = state_in[6] + g; + state_out[7] = state_in[7] + h; + + /* Clean up */ + a = b = c = d = e = f = g = h = T1 = 0; +} + +#else /* SHA2_UNROLL_TRANSFORM */ + +void sha512_Transform(const sha2_word64* state_in, const sha2_word64* data, sha2_word64* state_out) { + sha2_word64 a = 0, b = 0, c = 0, d = 0, e = 0, f = 0, g = 0, h = 0, s0 = 0, s1 = 0; + sha2_word64 T1 = 0, T2 = 0, W512[16] = {0}; + int j = 0; + + /* Initialize registers with the prev. intermediate value */ + a = state_in[0]; + b = state_in[1]; + c = state_in[2]; + d = state_in[3]; + e = state_in[4]; + f = state_in[5]; + g = state_in[6]; + h = state_in[7]; + + j = 0; + do { + /* Apply the SHA-512 compression function to update a..h with copy */ + T1 = h + Sigma1_512(e) + Ch(e, f, g) + K512[j] + (W512[j] = *data++); + T2 = Sigma0_512(a) + Maj(a, b, c); + h = g; + g = f; + f = e; + e = d + T1; + d = c; + c = b; + b = a; + a = T1 + T2; + + j++; + } while(j < 16); + + do { + /* Part of the message block expansion: */ + s0 = W512[(j + 1) & 0x0f]; + s0 = sigma0_512(s0); + s1 = W512[(j + 14) & 0x0f]; + s1 = sigma1_512(s1); + + /* Apply the SHA-512 compression function to update a..h */ + T1 = h + Sigma1_512(e) + Ch(e, f, g) + K512[j] + + (W512[j & 0x0f] += s1 + W512[(j + 9) & 0x0f] + s0); + T2 = Sigma0_512(a) + Maj(a, b, c); + h = g; + g = f; + f = e; + e = d + T1; + d = c; + c = b; + b = a; + a = T1 + T2; + + j++; + } while(j < 80); + + /* Compute the current intermediate hash value */ + state_out[0] = state_in[0] + a; + state_out[1] = state_in[1] + b; + state_out[2] = state_in[2] + c; + state_out[3] = state_in[3] + d; + state_out[4] = state_in[4] + e; + state_out[5] = state_in[5] + f; + state_out[6] = state_in[6] + g; + state_out[7] = state_in[7] + h; + + /* Clean up */ + a = b = c = d = e = f = g = h = T1 = T2 = 0; +} + +#endif /* SHA2_UNROLL_TRANSFORM */ + +void sha512_Update(SHA512_CTX* context, const sha2_byte* data, size_t len) { + unsigned int freespace = 0, usedspace = 0; + + if(len == 0) { + /* Calling with no data is valid - we do nothing */ + return; + } + + usedspace = (context->bitcount[0] >> 3) % SHA512_BLOCK_LENGTH; + if(usedspace > 0) { + /* Calculate how much free space is available in the buffer */ + freespace = SHA512_BLOCK_LENGTH - usedspace; + + if(len >= freespace) { + /* Fill the buffer completely and process it */ + MEMCPY_BCOPY(((uint8_t*)context->buffer) + usedspace, data, freespace); + ADDINC128(context->bitcount, freespace << 3); + len -= freespace; + data += freespace; +#if BYTE_ORDER == LITTLE_ENDIAN + /* Convert TO host byte order */ + for(int j = 0; j < 16; j++) { + REVERSE64(context->buffer[j], context->buffer[j]); + } +#endif + sha512_Transform(context->state, context->buffer, context->state); + } else { + /* The buffer is not yet full */ + MEMCPY_BCOPY(((uint8_t*)context->buffer) + usedspace, data, len); + ADDINC128(context->bitcount, len << 3); + /* Clean up: */ + usedspace = freespace = 0; + return; + } + } + while(len >= SHA512_BLOCK_LENGTH) { + /* Process as many complete blocks as we can */ + MEMCPY_BCOPY(context->buffer, data, SHA512_BLOCK_LENGTH); +#if BYTE_ORDER == LITTLE_ENDIAN + /* Convert TO host byte order */ + for(int j = 0; j < 16; j++) { + REVERSE64(context->buffer[j], context->buffer[j]); + } +#endif + sha512_Transform(context->state, context->buffer, context->state); + ADDINC128(context->bitcount, SHA512_BLOCK_LENGTH << 3); + len -= SHA512_BLOCK_LENGTH; + data += SHA512_BLOCK_LENGTH; + } + if(len > 0) { + /* There's left-overs, so save 'em */ + MEMCPY_BCOPY(context->buffer, data, len); + ADDINC128(context->bitcount, len << 3); + } + /* Clean up: */ + usedspace = freespace = 0; +} + +static void sha512_Last(SHA512_CTX* context) { + unsigned int usedspace = 0; + + usedspace = (context->bitcount[0] >> 3) % SHA512_BLOCK_LENGTH; + /* Begin padding with a 1 bit: */ + ((uint8_t*)context->buffer)[usedspace++] = 0x80; + + if(usedspace > SHA512_SHORT_BLOCK_LENGTH) { + memzero(((uint8_t*)context->buffer) + usedspace, SHA512_BLOCK_LENGTH - usedspace); + +#if BYTE_ORDER == LITTLE_ENDIAN + /* Convert TO host byte order */ + for(int j = 0; j < 16; j++) { + REVERSE64(context->buffer[j], context->buffer[j]); + } +#endif + /* Do second-to-last transform: */ + sha512_Transform(context->state, context->buffer, context->state); + + /* And prepare the last transform: */ + usedspace = 0; + } + /* Set-up for the last transform: */ + memzero(((uint8_t*)context->buffer) + usedspace, SHA512_SHORT_BLOCK_LENGTH - usedspace); + +#if BYTE_ORDER == LITTLE_ENDIAN + /* Convert TO host byte order */ + for(int j = 0; j < 14; j++) { + REVERSE64(context->buffer[j], context->buffer[j]); + } +#endif + /* Store the length of input data (in bits): */ + context->buffer[14] = context->bitcount[1]; + context->buffer[15] = context->bitcount[0]; + + /* Final transform: */ + sha512_Transform(context->state, context->buffer, context->state); +} + +void sha512_Final(SHA512_CTX* context, sha2_byte digest[SHA512_DIGEST_LENGTH]) { + /* If no digest buffer is passed, we don't bother doing this: */ + if(digest != (sha2_byte*)0) { + sha512_Last(context); + + /* Save the hash data for output: */ +#if BYTE_ORDER == LITTLE_ENDIAN + /* Convert FROM host byte order */ + for(int j = 0; j < 8; j++) { + REVERSE64(context->state[j], context->state[j]); + } +#endif + MEMCPY_BCOPY(digest, context->state, SHA512_DIGEST_LENGTH); + } + + /* Zero out state data */ + memzero(context, sizeof(SHA512_CTX)); +} + +char* sha512_End(SHA512_CTX* context, char buffer[SHA512_DIGEST_STRING_LENGTH]) { + sha2_byte digest[SHA512_DIGEST_LENGTH] = {0}, *d = digest; + int i = 0; + + if(buffer != (char*)0) { + sha512_Final(context, digest); + + for(i = 0; i < SHA512_DIGEST_LENGTH; i++) { + *buffer++ = sha2_hex_digits[(*d & 0xf0) >> 4]; + *buffer++ = sha2_hex_digits[*d & 0x0f]; + d++; + } + *buffer = (char)0; + } else { + memzero(context, sizeof(SHA512_CTX)); + } + memzero(digest, SHA512_DIGEST_LENGTH); + return buffer; +} + +void sha512_Raw(const sha2_byte* data, size_t len, uint8_t digest[SHA512_DIGEST_LENGTH]) { + SHA512_CTX context = {0}; + sha512_Init(&context); + sha512_Update(&context, data, len); + sha512_Final(&context, digest); +} + +char* sha512_Data(const sha2_byte* data, size_t len, char digest[SHA512_DIGEST_STRING_LENGTH]) { + SHA512_CTX context = {0}; + + sha512_Init(&context); + sha512_Update(&context, data, len); + return sha512_End(&context, digest); +} diff --git a/applications/external/flipbip/lib/crypto/sha2.h b/applications/external/flipbip/lib/crypto/sha2.h new file mode 100644 index 0000000000..5f7c1f0717 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/sha2.h @@ -0,0 +1,92 @@ +/** + * Copyright (c) 2000-2001 Aaron D. Gifford + * Copyright (c) 2013-2014 Pavol Rusnak + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTOR(S) ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTOR(S) BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef __SHA2_H__ +#define __SHA2_H__ + +#include +#include +#include "byte_order.h" + +#define SHA1_BLOCK_LENGTH 64 +#define SHA1_DIGEST_LENGTH 20 +#define SHA1_DIGEST_STRING_LENGTH (SHA1_DIGEST_LENGTH * 2 + 1) +#define SHA256_BLOCK_LENGTH 64 +#define SHA256_DIGEST_LENGTH 32 +#define SHA256_DIGEST_STRING_LENGTH (SHA256_DIGEST_LENGTH * 2 + 1) +#define SHA512_BLOCK_LENGTH 128 +#define SHA512_DIGEST_LENGTH 64 +#define SHA512_DIGEST_STRING_LENGTH (SHA512_DIGEST_LENGTH * 2 + 1) + +typedef struct _SHA1_CTX { + uint32_t state[5]; + uint64_t bitcount; + uint32_t buffer[SHA1_BLOCK_LENGTH / sizeof(uint32_t)]; +} SHA1_CTX; +typedef struct _SHA256_CTX { + uint32_t state[8]; + uint64_t bitcount; + uint32_t buffer[SHA256_BLOCK_LENGTH / sizeof(uint32_t)]; +} SHA256_CTX; +typedef struct _SHA512_CTX { + uint64_t state[8]; + uint64_t bitcount[2]; + uint64_t buffer[SHA512_BLOCK_LENGTH / sizeof(uint64_t)]; +} SHA512_CTX; + +extern const uint32_t sha256_initial_hash_value[8]; +extern const uint64_t sha512_initial_hash_value[8]; + +void sha1_Transform(const uint32_t* state_in, const uint32_t* data, uint32_t* state_out); +void sha1_Init(SHA1_CTX*); +void sha1_Update(SHA1_CTX*, const uint8_t*, size_t); +void sha1_Final(SHA1_CTX*, uint8_t[SHA1_DIGEST_LENGTH]); +char* sha1_End(SHA1_CTX*, char[SHA1_DIGEST_STRING_LENGTH]); +void sha1_Raw(const uint8_t*, size_t, uint8_t[SHA1_DIGEST_LENGTH]); +char* sha1_Data(const uint8_t*, size_t, char[SHA1_DIGEST_STRING_LENGTH]); + +void sha256_Transform(const uint32_t* state_in, const uint32_t* data, uint32_t* state_out); +void sha256_Init(SHA256_CTX*); +void sha256_Init_ex(SHA256_CTX*, const uint32_t state[8], uint64_t bitcount); +void sha256_Update(SHA256_CTX*, const uint8_t*, size_t); +void sha256_Final(SHA256_CTX*, uint8_t[SHA256_DIGEST_LENGTH]); +char* sha256_End(SHA256_CTX*, char[SHA256_DIGEST_STRING_LENGTH]); +void sha256_Raw(const uint8_t*, size_t, uint8_t[SHA256_DIGEST_LENGTH]); +char* sha256_Data(const uint8_t*, size_t, char[SHA256_DIGEST_STRING_LENGTH]); + +void sha512_Transform(const uint64_t* state_in, const uint64_t* data, uint64_t* state_out); +void sha512_Init(SHA512_CTX*); +void sha512_Update(SHA512_CTX*, const uint8_t*, size_t); +void sha512_Final(SHA512_CTX*, uint8_t[SHA512_DIGEST_LENGTH]); +char* sha512_End(SHA512_CTX*, char[SHA512_DIGEST_STRING_LENGTH]); +void sha512_Raw(const uint8_t*, size_t, uint8_t[SHA512_DIGEST_LENGTH]); +char* sha512_Data(const uint8_t*, size_t, char[SHA512_DIGEST_STRING_LENGTH]); + +#endif diff --git a/applications/external/flipbip/lib/crypto/sha3.c b/applications/external/flipbip/lib/crypto/sha3.c new file mode 100644 index 0000000000..d3a43b6a3f --- /dev/null +++ b/applications/external/flipbip/lib/crypto/sha3.c @@ -0,0 +1,392 @@ +/* sha3.c - an implementation of Secure Hash Algorithm 3 (Keccak). + * based on the + * The Keccak SHA-3 submission. Submission to NIST (Round 3), 2011 + * by Guido Bertoni, Joan Daemen, Michaël Peeters and Gilles Van Assche + * + * Copyright: 2013 Aleksey Kravchenko + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. Use this program at your own risk! + */ + +#include +#include + +#include "sha3.h" +#include "memzero.h" +#include "byte_order.h" + +#define I64(x) x##LL +#define ROTL64(qword, n) ((qword) << (n) ^ ((qword) >> (64 - (n)))) +#define le2me_64(x) (x) +#define IS_ALIGNED_64(p) (0 == (((uintptr_t)(const void*)(p)&0x7))) +#define me64_to_le_str(to, from, length) memcpy((to), (from), (length)) + +/* constants */ +#define NumberOfRounds 24 + +/* SHA3 (Keccak) constants for 24 rounds */ +static uint64_t keccak_round_constants[NumberOfRounds] = { + I64(0x0000000000000001), I64(0x0000000000008082), I64(0x800000000000808A), + I64(0x8000000080008000), I64(0x000000000000808B), I64(0x0000000080000001), + I64(0x8000000080008081), I64(0x8000000000008009), I64(0x000000000000008A), + I64(0x0000000000000088), I64(0x0000000080008009), I64(0x000000008000000A), + I64(0x000000008000808B), I64(0x800000000000008B), I64(0x8000000000008089), + I64(0x8000000000008003), I64(0x8000000000008002), I64(0x8000000000000080), + I64(0x000000000000800A), I64(0x800000008000000A), I64(0x8000000080008081), + I64(0x8000000000008080), I64(0x0000000080000001), I64(0x8000000080008008)}; + +/* Initializing a sha3 context for given number of output bits */ +static void keccak_Init(SHA3_CTX* ctx, unsigned bits) { + /* NB: The Keccak capacity parameter = bits * 2 */ + unsigned rate = 1600 - bits * 2; + + memzero(ctx, sizeof(SHA3_CTX)); + ctx->block_size = rate / 8; + assert(rate <= 1600 && (rate % 64) == 0); +} + +/** + * Initialize context before calculating hash. + * + * @param ctx context to initialize + */ +void sha3_224_Init(SHA3_CTX* ctx) { + keccak_Init(ctx, 224); +} + +/** + * Initialize context before calculating hash. + * + * @param ctx context to initialize + */ +void sha3_256_Init(SHA3_CTX* ctx) { + keccak_Init(ctx, 256); +} + +/** + * Initialize context before calculating hash. + * + * @param ctx context to initialize + */ +void sha3_384_Init(SHA3_CTX* ctx) { + keccak_Init(ctx, 384); +} + +/** + * Initialize context before calculating hash. + * + * @param ctx context to initialize + */ +void sha3_512_Init(SHA3_CTX* ctx) { + keccak_Init(ctx, 512); +} + +/* Keccak theta() transformation */ +static void keccak_theta(uint64_t* A) { + unsigned int x = 0; + uint64_t C[5] = {0}, D[5] = {0}; + + for(x = 0; x < 5; x++) { + C[x] = A[x] ^ A[x + 5] ^ A[x + 10] ^ A[x + 15] ^ A[x + 20]; + } + D[0] = ROTL64(C[1], 1) ^ C[4]; + D[1] = ROTL64(C[2], 1) ^ C[0]; + D[2] = ROTL64(C[3], 1) ^ C[1]; + D[3] = ROTL64(C[4], 1) ^ C[2]; + D[4] = ROTL64(C[0], 1) ^ C[3]; + + for(x = 0; x < 5; x++) { + A[x] ^= D[x]; + A[x + 5] ^= D[x]; + A[x + 10] ^= D[x]; + A[x + 15] ^= D[x]; + A[x + 20] ^= D[x]; + } +} + +/* Keccak pi() transformation */ +static void keccak_pi(uint64_t* A) { + uint64_t A1 = 0; + A1 = A[1]; + A[1] = A[6]; + A[6] = A[9]; + A[9] = A[22]; + A[22] = A[14]; + A[14] = A[20]; + A[20] = A[2]; + A[2] = A[12]; + A[12] = A[13]; + A[13] = A[19]; + A[19] = A[23]; + A[23] = A[15]; + A[15] = A[4]; + A[4] = A[24]; + A[24] = A[21]; + A[21] = A[8]; + A[8] = A[16]; + A[16] = A[5]; + A[5] = A[3]; + A[3] = A[18]; + A[18] = A[17]; + A[17] = A[11]; + A[11] = A[7]; + A[7] = A[10]; + A[10] = A1; + /* note: A[ 0] is left as is */ +} + +/* Keccak chi() transformation */ +static void keccak_chi(uint64_t* A) { + int i = 0; + for(i = 0; i < 25; i += 5) { + uint64_t A0 = A[0 + i], A1 = A[1 + i]; + A[0 + i] ^= ~A1 & A[2 + i]; + A[1 + i] ^= ~A[2 + i] & A[3 + i]; + A[2 + i] ^= ~A[3 + i] & A[4 + i]; + A[3 + i] ^= ~A[4 + i] & A0; + A[4 + i] ^= ~A0 & A1; + } +} + +static void sha3_permutation(uint64_t* state) { +#if BYTE_ORDER == BIG_ENDIAN + int i; + for(i = 0; i < 25; i++) { + REVERSE64(state[i], state[i]); + } +#endif + int round = 0; + for(round = 0; round < NumberOfRounds; round++) { + keccak_theta(state); + + /* apply Keccak rho() transformation */ + state[1] = ROTL64(state[1], 1); + state[2] = ROTL64(state[2], 62); + state[3] = ROTL64(state[3], 28); + state[4] = ROTL64(state[4], 27); + state[5] = ROTL64(state[5], 36); + state[6] = ROTL64(state[6], 44); + state[7] = ROTL64(state[7], 6); + state[8] = ROTL64(state[8], 55); + state[9] = ROTL64(state[9], 20); + state[10] = ROTL64(state[10], 3); + state[11] = ROTL64(state[11], 10); + state[12] = ROTL64(state[12], 43); + state[13] = ROTL64(state[13], 25); + state[14] = ROTL64(state[14], 39); + state[15] = ROTL64(state[15], 41); + state[16] = ROTL64(state[16], 45); + state[17] = ROTL64(state[17], 15); + state[18] = ROTL64(state[18], 21); + state[19] = ROTL64(state[19], 8); + state[20] = ROTL64(state[20], 18); + state[21] = ROTL64(state[21], 2); + state[22] = ROTL64(state[22], 61); + state[23] = ROTL64(state[23], 56); + state[24] = ROTL64(state[24], 14); + + keccak_pi(state); + keccak_chi(state); + + /* apply iota(state, round) */ + *state ^= keccak_round_constants[round]; + } +#if BYTE_ORDER == BIG_ENDIAN + for(i = 0; i < 25; i++) { + REVERSE64(state[i], state[i]); + } +#endif +} + +/** + * The core transformation. Process the specified block of data. + * + * @param hash the algorithm state + * @param block the message block to process + * @param block_size the size of the processed block in bytes + */ +static void sha3_process_block(uint64_t hash[25], const uint64_t* block, size_t block_size) { + /* expanded loop */ + hash[0] ^= le2me_64(block[0]); + hash[1] ^= le2me_64(block[1]); + hash[2] ^= le2me_64(block[2]); + hash[3] ^= le2me_64(block[3]); + hash[4] ^= le2me_64(block[4]); + hash[5] ^= le2me_64(block[5]); + hash[6] ^= le2me_64(block[6]); + hash[7] ^= le2me_64(block[7]); + hash[8] ^= le2me_64(block[8]); + /* if not sha3-512 */ + if(block_size > 72) { + hash[9] ^= le2me_64(block[9]); + hash[10] ^= le2me_64(block[10]); + hash[11] ^= le2me_64(block[11]); + hash[12] ^= le2me_64(block[12]); + /* if not sha3-384 */ + if(block_size > 104) { + hash[13] ^= le2me_64(block[13]); + hash[14] ^= le2me_64(block[14]); + hash[15] ^= le2me_64(block[15]); + hash[16] ^= le2me_64(block[16]); + /* if not sha3-256 */ + if(block_size > 136) { + hash[17] ^= le2me_64(block[17]); +#ifdef FULL_SHA3_FAMILY_SUPPORT + /* if not sha3-224 */ + if(block_size > 144) { + hash[18] ^= le2me_64(block[18]); + hash[19] ^= le2me_64(block[19]); + hash[20] ^= le2me_64(block[20]); + hash[21] ^= le2me_64(block[21]); + hash[22] ^= le2me_64(block[22]); + hash[23] ^= le2me_64(block[23]); + hash[24] ^= le2me_64(block[24]); + } +#endif + } + } + } + /* make a permutation of the hash */ + sha3_permutation(hash); +} + +#define SHA3_FINALIZED 0x80000000 + +/** + * Calculate message hash. + * Can be called repeatedly with chunks of the message to be hashed. + * + * @param ctx the algorithm context containing current hashing state + * @param msg message chunk + * @param size length of the message chunk + */ +void sha3_Update(SHA3_CTX* ctx, const unsigned char* msg, size_t size) { + if(size == 0) return; + + size_t idx = (size_t)ctx->rest; + size_t block_size = (size_t)ctx->block_size; + + if(ctx->rest & SHA3_FINALIZED) return; /* too late for additional input */ + ctx->rest = (unsigned)((ctx->rest + size) % block_size); + + /* fill partial block */ + if(idx) { + size_t left = block_size - idx; + memcpy((char*)ctx->message + idx, msg, (size < left ? size : left)); + if(size < left) return; + + /* process partial block */ + sha3_process_block(ctx->hash, ctx->message, block_size); + msg += left; + size -= left; + } + while(size >= block_size) { + uint64_t* aligned_message_block = NULL; + if(IS_ALIGNED_64(msg)) { + /* the most common case is processing of an already aligned message + without copying it */ + aligned_message_block = (uint64_t*)(void*)msg; + } else { + memcpy(ctx->message, msg, block_size); + aligned_message_block = ctx->message; + } + + sha3_process_block(ctx->hash, aligned_message_block, block_size); + msg += block_size; + size -= block_size; + } + if(size) { + memcpy(ctx->message, msg, size); /* save leftovers */ + } +} + +/** + * Store calculated hash into the given array. + * + * @param ctx the algorithm context containing current hashing state + * @param result calculated hash in binary form + */ +void sha3_Final(SHA3_CTX* ctx, unsigned char* result) { + size_t digest_length = 100 - ctx->block_size / 2; + const size_t block_size = ctx->block_size; + + if(!(ctx->rest & SHA3_FINALIZED)) { + /* clear the rest of the data queue */ + memzero((char*)ctx->message + ctx->rest, block_size - ctx->rest); + ((char*)ctx->message)[ctx->rest] |= 0x06; + ((char*)ctx->message)[block_size - 1] |= 0x80; + + /* process final block */ + sha3_process_block(ctx->hash, ctx->message, block_size); + ctx->rest = SHA3_FINALIZED; /* mark context as finalized */ + } + + assert(block_size > digest_length); + if(result) me64_to_le_str(result, ctx->hash, digest_length); + memzero(ctx, sizeof(SHA3_CTX)); +} + +#if USE_KECCAK +/** +* Store calculated hash into the given array. +* +* @param ctx the algorithm context containing current hashing state +* @param result calculated hash in binary form +*/ +void keccak_Final(SHA3_CTX* ctx, unsigned char* result) { + size_t digest_length = 100 - ctx->block_size / 2; + const size_t block_size = ctx->block_size; + + if(!(ctx->rest & SHA3_FINALIZED)) { + /* clear the rest of the data queue */ + memzero((char*)ctx->message + ctx->rest, block_size - ctx->rest); + ((char*)ctx->message)[ctx->rest] |= 0x01; + ((char*)ctx->message)[block_size - 1] |= 0x80; + + /* process final block */ + sha3_process_block(ctx->hash, ctx->message, block_size); + ctx->rest = SHA3_FINALIZED; /* mark context as finalized */ + } + + assert(block_size > digest_length); + if(result) me64_to_le_str(result, ctx->hash, digest_length); + memzero(ctx, sizeof(SHA3_CTX)); +} + +void keccak_256(const unsigned char* data, size_t len, unsigned char* digest) { + SHA3_CTX ctx = {0}; + keccak_256_Init(&ctx); + keccak_Update(&ctx, data, len); + keccak_Final(&ctx, digest); +} + +void keccak_512(const unsigned char* data, size_t len, unsigned char* digest) { + SHA3_CTX ctx = {0}; + keccak_512_Init(&ctx); + keccak_Update(&ctx, data, len); + keccak_Final(&ctx, digest); +} +#endif /* USE_KECCAK */ + +void sha3_256(const unsigned char* data, size_t len, unsigned char* digest) { + SHA3_CTX ctx = {0}; + sha3_256_Init(&ctx); + sha3_Update(&ctx, data, len); + sha3_Final(&ctx, digest); +} + +void sha3_512(const unsigned char* data, size_t len, unsigned char* digest) { + SHA3_CTX ctx = {0}; + sha3_512_Init(&ctx); + sha3_Update(&ctx, data, len); + sha3_Final(&ctx, digest); +} diff --git a/applications/external/flipbip/lib/crypto/sha3.h b/applications/external/flipbip/lib/crypto/sha3.h new file mode 100644 index 0000000000..01818a4fed --- /dev/null +++ b/applications/external/flipbip/lib/crypto/sha3.h @@ -0,0 +1,88 @@ +/* sha3.h - an implementation of Secure Hash Algorithm 3 (Keccak). + * based on the + * The Keccak SHA-3 submission. Submission to NIST (Round 3), 2011 + * by Guido Bertoni, Joan Daemen, Michaël Peeters and Gilles Van Assche + * + * Copyright: 2013 Aleksey Kravchenko + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. Use this program at your own risk! + */ + +#ifndef __SHA3_H__ +#define __SHA3_H__ + +#include +#include "options.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define sha3_224_hash_size 28 +#define sha3_256_hash_size 32 +#define sha3_384_hash_size 48 +#define sha3_512_hash_size 64 +#define sha3_max_permutation_size 25 +#define sha3_max_rate_in_qwords 24 + +#define SHA3_224_BLOCK_LENGTH 144 +#define SHA3_256_BLOCK_LENGTH 136 +#define SHA3_384_BLOCK_LENGTH 104 +#define SHA3_512_BLOCK_LENGTH 72 + +#define SHA3_224_DIGEST_LENGTH sha3_224_hash_size +#define SHA3_256_DIGEST_LENGTH sha3_256_hash_size +#define SHA3_384_DIGEST_LENGTH sha3_384_hash_size +#define SHA3_512_DIGEST_LENGTH sha3_512_hash_size + +/** + * SHA3 Algorithm context. + */ +typedef struct SHA3_CTX { + /* 1600 bits algorithm hashing state */ + uint64_t hash[sha3_max_permutation_size]; + /* 1536-bit buffer for leftovers */ + uint64_t message[sha3_max_rate_in_qwords]; + /* count of bytes in the message[] buffer */ + unsigned rest; + /* size of a message block processed at once */ + unsigned block_size; +} SHA3_CTX; + +/* methods for calculating the hash function */ + +void sha3_224_Init(SHA3_CTX* ctx); +void sha3_256_Init(SHA3_CTX* ctx); +void sha3_384_Init(SHA3_CTX* ctx); +void sha3_512_Init(SHA3_CTX* ctx); +void sha3_Update(SHA3_CTX* ctx, const unsigned char* msg, size_t size); +void sha3_Final(SHA3_CTX* ctx, unsigned char* result); + +#if USE_KECCAK +#define keccak_224_Init sha3_224_Init +#define keccak_256_Init sha3_256_Init +#define keccak_384_Init sha3_384_Init +#define keccak_512_Init sha3_512_Init +#define keccak_Update sha3_Update +void keccak_Final(SHA3_CTX* ctx, unsigned char* result); +void keccak_256(const unsigned char* data, size_t len, unsigned char* digest); +void keccak_512(const unsigned char* data, size_t len, unsigned char* digest); +#endif + +void sha3_256(const unsigned char* data, size_t len, unsigned char* digest); +void sha3_512(const unsigned char* data, size_t len, unsigned char* digest); + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* __SHA3_H__ */ diff --git a/applications/external/flipbip/lib/crypto/shamir.c b/applications/external/flipbip/lib/crypto/shamir.c new file mode 100644 index 0000000000..037af99cc4 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/shamir.c @@ -0,0 +1,338 @@ +/* + * Implementation of the hazardous parts of the SSS library + * + * Copyright (c) 2017 Daan Sprenkels + * Copyright (c) 2019 SatoshiLabs + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * This code contains the actual Shamir secret sharing functionality. The + * implementation of this code is based on the idea that the user likes to + * generate/combine 32 shares (in GF(2^8)) at the same time, because a 256 bit + * key will be exactly 32 bytes. Therefore we bitslice all the input and + * unbitslice the output right before returning. + * + * This bitslice approach optimizes natively on all architectures that are 32 + * bit or more. Care is taken to use not too many registers, to ensure that no + * values have to be leaked to the stack. + * + * All functions in this module are implemented constant time and constant + * lookup operations, as all proper crypto code must be. + */ + +#include "shamir.h" +#include +#include "memzero.h" + +static void bitslice(uint32_t r[8], const uint8_t* x, size_t len) { + size_t bit_idx = 0, arr_idx = 0; + uint32_t cur = 0; + + memset(r, 0, sizeof(uint32_t[8])); + for(arr_idx = 0; arr_idx < len; arr_idx++) { + cur = (uint32_t)x[arr_idx]; + for(bit_idx = 0; bit_idx < 8; bit_idx++) { + r[bit_idx] |= ((cur >> bit_idx) & 1) << arr_idx; + } + } +} + +static void unbitslice(uint8_t* r, const uint32_t x[8], size_t len) { + size_t bit_idx = 0, arr_idx = 0; + uint32_t cur = 0; + + memset(r, 0, sizeof(uint8_t) * len); + for(bit_idx = 0; bit_idx < 8; bit_idx++) { + cur = (uint32_t)x[bit_idx]; + for(arr_idx = 0; arr_idx < len; arr_idx++) { + r[arr_idx] |= ((cur >> arr_idx) & 1) << bit_idx; + } + } +} + +static void bitslice_setall(uint32_t r[8], const uint8_t x) { + size_t idx = 0; + for(idx = 0; idx < 8; idx++) { + r[idx] = -((x >> idx) & 1); + } +} + +/* + * Add (XOR) `r` with `x` and store the result in `r`. + */ +static void gf256_add(uint32_t r[8], const uint32_t x[8]) { + size_t idx = 0; + for(idx = 0; idx < 8; idx++) r[idx] ^= x[idx]; +} + +/* + * Safely multiply two bitsliced polynomials in GF(2^8) reduced by + * x^8 + x^4 + x^3 + x + 1. `r` and `a` may overlap, but overlapping of `r` + * and `b` will produce an incorrect result! If you need to square a polynomial + * use `gf256_square` instead. + */ +static void gf256_mul(uint32_t r[8], const uint32_t a[8], const uint32_t b[8]) { + /* This function implements Russian Peasant multiplication on two + * bitsliced polynomials. + * + * I personally think that these kinds of long lists of operations + * are often a bit ugly. A double for loop would be nicer and would + * take up a lot less lines of code. + * However, some compilers seem to fail in optimizing these kinds of + * loops. So we will just have to do this by hand. + */ + uint32_t a2[8] = {0}; + memcpy(a2, a, sizeof(uint32_t[8])); + + r[0] = a2[0] & b[0]; /* add (assignment, because r is 0) */ + r[1] = a2[1] & b[0]; + r[2] = a2[2] & b[0]; + r[3] = a2[3] & b[0]; + r[4] = a2[4] & b[0]; + r[5] = a2[5] & b[0]; + r[6] = a2[6] & b[0]; + r[7] = a2[7] & b[0]; + a2[0] ^= a2[7]; /* reduce */ + a2[2] ^= a2[7]; + a2[3] ^= a2[7]; + + r[0] ^= a2[7] & b[1]; /* add */ + r[1] ^= a2[0] & b[1]; + r[2] ^= a2[1] & b[1]; + r[3] ^= a2[2] & b[1]; + r[4] ^= a2[3] & b[1]; + r[5] ^= a2[4] & b[1]; + r[6] ^= a2[5] & b[1]; + r[7] ^= a2[6] & b[1]; + a2[7] ^= a2[6]; /* reduce */ + a2[1] ^= a2[6]; + a2[2] ^= a2[6]; + + r[0] ^= a2[6] & b[2]; /* add */ + r[1] ^= a2[7] & b[2]; + r[2] ^= a2[0] & b[2]; + r[3] ^= a2[1] & b[2]; + r[4] ^= a2[2] & b[2]; + r[5] ^= a2[3] & b[2]; + r[6] ^= a2[4] & b[2]; + r[7] ^= a2[5] & b[2]; + a2[6] ^= a2[5]; /* reduce */ + a2[0] ^= a2[5]; + a2[1] ^= a2[5]; + + r[0] ^= a2[5] & b[3]; /* add */ + r[1] ^= a2[6] & b[3]; + r[2] ^= a2[7] & b[3]; + r[3] ^= a2[0] & b[3]; + r[4] ^= a2[1] & b[3]; + r[5] ^= a2[2] & b[3]; + r[6] ^= a2[3] & b[3]; + r[7] ^= a2[4] & b[3]; + a2[5] ^= a2[4]; /* reduce */ + a2[7] ^= a2[4]; + a2[0] ^= a2[4]; + + r[0] ^= a2[4] & b[4]; /* add */ + r[1] ^= a2[5] & b[4]; + r[2] ^= a2[6] & b[4]; + r[3] ^= a2[7] & b[4]; + r[4] ^= a2[0] & b[4]; + r[5] ^= a2[1] & b[4]; + r[6] ^= a2[2] & b[4]; + r[7] ^= a2[3] & b[4]; + a2[4] ^= a2[3]; /* reduce */ + a2[6] ^= a2[3]; + a2[7] ^= a2[3]; + + r[0] ^= a2[3] & b[5]; /* add */ + r[1] ^= a2[4] & b[5]; + r[2] ^= a2[5] & b[5]; + r[3] ^= a2[6] & b[5]; + r[4] ^= a2[7] & b[5]; + r[5] ^= a2[0] & b[5]; + r[6] ^= a2[1] & b[5]; + r[7] ^= a2[2] & b[5]; + a2[3] ^= a2[2]; /* reduce */ + a2[5] ^= a2[2]; + a2[6] ^= a2[2]; + + r[0] ^= a2[2] & b[6]; /* add */ + r[1] ^= a2[3] & b[6]; + r[2] ^= a2[4] & b[6]; + r[3] ^= a2[5] & b[6]; + r[4] ^= a2[6] & b[6]; + r[5] ^= a2[7] & b[6]; + r[6] ^= a2[0] & b[6]; + r[7] ^= a2[1] & b[6]; + a2[2] ^= a2[1]; /* reduce */ + a2[4] ^= a2[1]; + a2[5] ^= a2[1]; + + r[0] ^= a2[1] & b[7]; /* add */ + r[1] ^= a2[2] & b[7]; + r[2] ^= a2[3] & b[7]; + r[3] ^= a2[4] & b[7]; + r[4] ^= a2[5] & b[7]; + r[5] ^= a2[6] & b[7]; + r[6] ^= a2[7] & b[7]; + r[7] ^= a2[0] & b[7]; + + memzero(a2, sizeof(a2)); +} + +/* + * Square `x` in GF(2^8) and write the result to `r`. `r` and `x` may overlap. + */ +static void gf256_square(uint32_t r[8], const uint32_t x[8]) { + uint32_t r8 = 0, r10 = 0, r12 = 0, r14 = 0; + /* Use the Freshman's Dream rule to square the polynomial + * Assignments are done from 7 downto 0, because this allows the user + * to execute this function in-place (e.g. `gf256_square(r, r);`). + */ + r14 = x[7]; + r12 = x[6]; + r10 = x[5]; + r8 = x[4]; + r[6] = x[3]; + r[4] = x[2]; + r[2] = x[1]; + r[0] = x[0]; + + /* Reduce with x^8 + x^4 + x^3 + x + 1 until order is less than 8 */ + r[7] = r14; /* r[7] was 0 */ + r[6] ^= r14; + r10 ^= r14; + /* Skip, because r13 is always 0 */ + r[4] ^= r12; + r[5] = r12; /* r[5] was 0 */ + r[7] ^= r12; + r8 ^= r12; + /* Skip, because r11 is always 0 */ + r[2] ^= r10; + r[3] = r10; /* r[3] was 0 */ + r[5] ^= r10; + r[6] ^= r10; + r[1] = r14; /* r[1] was 0 */ + r[2] ^= r14; /* Substitute r9 by r14 because they will always be equal*/ + r[4] ^= r14; + r[5] ^= r14; + r[0] ^= r8; + r[1] ^= r8; + r[3] ^= r8; + r[4] ^= r8; +} + +/* + * Invert `x` in GF(2^8) and write the result to `r` + */ +static void gf256_inv(uint32_t r[8], uint32_t x[8]) { + uint32_t y[8] = {0}, z[8] = {0}; + + gf256_square(y, x); // y = x^2 + gf256_square(y, y); // y = x^4 + gf256_square(r, y); // r = x^8 + gf256_mul(z, r, x); // z = x^9 + gf256_square(r, r); // r = x^16 + gf256_mul(r, r, z); // r = x^25 + gf256_square(r, r); // r = x^50 + gf256_square(z, r); // z = x^100 + gf256_square(z, z); // z = x^200 + gf256_mul(r, r, z); // r = x^250 + gf256_mul(r, r, y); // r = x^254 + + memzero(y, sizeof(y)); + memzero(z, sizeof(z)); +} + +bool shamir_interpolate( + uint8_t* result, + uint8_t result_index, + const uint8_t* share_indices, + const uint8_t** share_values, + uint8_t share_count, + size_t len) { + size_t i = 0, j = 0; + uint32_t x[8] = {0}; + uint32_t xs[share_count][8]; + memset(xs, 0, sizeof(xs)); + uint32_t ys[share_count][8]; + memset(ys, 0, sizeof(ys)); + uint32_t num[8] = {~0}; /* num is the numerator (=1) */ + uint32_t denom[8] = {0}; + uint32_t tmp[8] = {0}; + uint32_t secret[8] = {0}; + bool ret = true; + + if(len > SHAMIR_MAX_LEN) return false; + + /* Collect the x and y values */ + for(i = 0; i < share_count; i++) { + bitslice_setall(xs[i], share_indices[i]); + bitslice(ys[i], share_values[i], len); + } + bitslice_setall(x, result_index); + + for(i = 0; i < share_count; i++) { + memcpy(tmp, x, sizeof(uint32_t[8])); + gf256_add(tmp, xs[i]); + gf256_mul(num, num, tmp); + } + + /* Use Lagrange basis polynomials to calculate the secret coefficient */ + for(i = 0; i < share_count; i++) { + /* The code below assumes that none of the share_indices are equal to + * result_index. We need to treat that as a special case. */ + if(share_indices[i] != result_index) { + memcpy(denom, x, sizeof(denom)); + gf256_add(denom, xs[i]); + } else { + bitslice_setall(denom, 1); + gf256_add(secret, ys[i]); + } + for(j = 0; j < share_count; j++) { + if(i == j) continue; + memcpy(tmp, xs[i], sizeof(uint32_t[8])); + gf256_add(tmp, xs[j]); + gf256_mul(denom, denom, tmp); + } + if((denom[0] | denom[1] | denom[2] | denom[3] | denom[4] | denom[5] | denom[6] | + denom[7]) == 0) { + /* The share_indices are not unique. */ + ret = false; + break; + } + gf256_inv(tmp, denom); /* inverted denominator */ + gf256_mul(tmp, tmp, num); /* basis polynomial */ + gf256_mul(tmp, tmp, ys[i]); /* scaled coefficient */ + gf256_add(secret, tmp); + } + + if(ret == true) { + unbitslice(result, secret, len); + } + + memzero(x, sizeof(x)); + memzero(xs, sizeof(xs)); + memzero(ys, sizeof(ys)); + memzero(num, sizeof(num)); + memzero(denom, sizeof(denom)); + memzero(tmp, sizeof(tmp)); + memzero(secret, sizeof(secret)); + return ret; +} diff --git a/applications/external/flipbip/lib/crypto/shamir.h b/applications/external/flipbip/lib/crypto/shamir.h new file mode 100644 index 0000000000..2250fc6720 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/shamir.h @@ -0,0 +1,73 @@ +/* + * Low level API for Daan Sprenkels' Shamir secret sharing library + * Copyright (c) 2017 Daan Sprenkels + * Copyright (c) 2019 SatoshiLabs + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Usage of this API is hazardous and is only reserved for beings with a + * good understanding of the Shamir secret sharing scheme and who know how + * crypto code is implemented. If you are unsure about this, use the + * intermediate level API. You have been warned! + */ + +#ifndef __SHAMIR_H__ +#define __SHAMIR_H__ + +#include +#include +#include + +#define SHAMIR_MAX_LEN 32 + +/* + * Computes f(x) given the Shamir shares (x_1, f(x_1)), ... , (x_m, f(x_m)). + * The x coordinates of the shares must be pairwise distinct. Returns true on + * success, otherwise false. + * result: Array of length len where the evaluations of the polynomials in x + * will be written. + * result_index: The x coordinate of the result. + * share_indices: Points to the array of integers x_1, ... , x_m. + * share_values: Points to the array of y_1, ... , y_m, where each y_i is an + * array of bytes of length len representing the evaluations of the + * polynomials in x_i. + * share_count: The number of shares m. + * len: The length of the result array and of each of the y_1, ... , y_m arrays. + * + * The number of shares used to compute the result may be larger than the + * required threshold. + * + * This function does *not* do *any* checking for integrity. If any of the + * shares are not original, this will result in an invalid restored value. + * All values written to `result` should be treated as secret. Even if some of + * the shares that were provided as input were incorrect, the result *still* + * allows an attacker to gain information about the correct result. + * + * This function treats `shares_values`, `share_indices` and `result` as secret + * values. `share_count` is treated as a public value (for performance reasons). + */ +bool shamir_interpolate( + uint8_t* result, + uint8_t result_index, + const uint8_t* share_indices, + const uint8_t** share_values, + uint8_t share_count, + size_t len); + +#endif /* __SHAMIR_H__ */ diff --git a/applications/external/flipbip/lib/crypto/slip39.c b/applications/external/flipbip/lib/crypto/slip39.c new file mode 100644 index 0000000000..268d0f37ee --- /dev/null +++ b/applications/external/flipbip/lib/crypto/slip39.c @@ -0,0 +1,150 @@ +/** + * This file is part of the TREZOR project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "slip39.h" +#include +#include +#include "slip39_wordlist.h" + +/** + * Returns word at position `index`. + */ +const char* get_word(uint16_t index) { + if(index >= WORDS_COUNT) { + return NULL; + } + + return slip39_wordlist[index]; +} + +/** + * Finds the index of a given word. + * Returns true on success and stores result in `index`. + */ +bool word_index(uint16_t* index, const char* word, uint8_t word_length) { + uint16_t lo = 0; + uint16_t hi = WORDS_COUNT; + uint16_t mid = 0; + + while((hi - lo) > 1) { + mid = (hi + lo) / 2; + if(strncmp(slip39_wordlist[mid], word, word_length) > 0) { + hi = mid; + } else { + lo = mid; + } + } + if(strncmp(slip39_wordlist[lo], word, word_length) != 0) { + return false; + } + *index = lo; + return true; +} + +/** + * Returns the index of the first sequence in words_button_seq[] which is not + * less than the given sequence. Returns WORDS_COUNT if there is no such + * sequence. + */ +static uint16_t find_sequence(uint16_t sequence) { + if(sequence <= words_button_seq[0].sequence) { + return 0; + } + + uint16_t lo = 0; + uint16_t hi = WORDS_COUNT; + + while(hi - lo > 1) { + uint16_t mid = (hi + lo) / 2; + if(words_button_seq[mid].sequence >= sequence) { + hi = mid; + } else { + lo = mid; + } + } + + return hi; +} + +/** + * Returns a word matching the button sequence prefix or NULL if no match is + * found. + */ +const char* button_sequence_to_word(uint16_t sequence) { + if(sequence == 0) { + return slip39_wordlist[words_button_seq[0].index]; + } + + uint16_t multiplier = 1; + while(sequence < 1000) { + sequence *= 10; + multiplier *= 10; + } + + uint16_t i = find_sequence(sequence); + if(i >= WORDS_COUNT || words_button_seq[i].sequence - sequence >= multiplier) { + return NULL; + } + + return slip39_wordlist[words_button_seq[i].index]; +} + +/** + * Calculates which buttons on the T9 keyboard can still be pressed after the + * prefix was entered. Returns a 9-bit bitmask, where each bit specifies which + * buttons can be pressed (there are still words in this combination). The least + * significant bit corresponds to the first button. + * + * Example: 110000110 - second, third, eighth and ninth button still can be + * pressed. + */ +uint16_t slip39_word_completion_mask(uint16_t prefix) { + if(prefix >= 1000) { + // Four char prefix -> the mask is zero. + return 0; + } + + // Determine the range of sequences [min, max), which have the given prefix. + uint16_t min = prefix; + uint16_t max = prefix + 1; + uint16_t divider = 1; + while(max <= 1000) { + min *= 10; + max *= 10; + divider *= 10; + } + divider /= 10; + + // Determine the range we will be searching in words_button_seq[]. + min = find_sequence(min); + max = find_sequence(max); + + uint16_t bitmap = 0; + for(uint16_t i = min; i < max; ++i) { + uint8_t digit = (words_button_seq[i].sequence / divider) % 10; + bitmap |= 1 << (digit - 1); + } + + return bitmap; +} diff --git a/applications/external/flipbip/lib/crypto/slip39.h b/applications/external/flipbip/lib/crypto/slip39.h new file mode 100644 index 0000000000..0bc24fa742 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/slip39.h @@ -0,0 +1,39 @@ +/** + * This file is part of the TREZOR project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __SLIP39_H__ +#define __SLIP39_H__ + +#include +#include + +const char* get_word(uint16_t index); + +bool word_index(uint16_t* index, const char* word, uint8_t word_length); + +uint16_t slip39_word_completion_mask(uint16_t prefix); + +const char* button_sequence_to_word(uint16_t prefix); + +#endif diff --git a/applications/external/flipbip/lib/crypto/slip39_wordlist.h b/applications/external/flipbip/lib/crypto/slip39_wordlist.h new file mode 100644 index 0000000000..17d4edeedb --- /dev/null +++ b/applications/external/flipbip/lib/crypto/slip39_wordlist.h @@ -0,0 +1,1203 @@ +/** + * This file is part of the TREZOR project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __SLIP39_WORDLIST_H__ +#define __SLIP39_WORDLIST_H__ + +#include + +#define WORDS_COUNT 1024 + +static const char* const slip39_wordlist[WORDS_COUNT] = { + "academic", "acid", "acne", "acquire", "acrobat", "activity", "actress", "adapt", + "adequate", "adjust", "admit", "adorn", "adult", "advance", "advocate", "afraid", + "again", "agency", "agree", "aide", "aircraft", "airline", "airport", "ajar", + "alarm", "album", "alcohol", "alien", "alive", "alpha", "already", "alto", + "aluminum", "always", "amazing", "ambition", "amount", "amuse", "analysis", "anatomy", + "ancestor", "ancient", "angel", "angry", "animal", "answer", "antenna", "anxiety", + "apart", "aquatic", "arcade", "arena", "argue", "armed", "artist", "artwork", + "aspect", "auction", "august", "aunt", "average", "aviation", "avoid", "award", + "away", "axis", "axle", "beam", "beard", "beaver", "become", "bedroom", + "behavior", "being", "believe", "belong", "benefit", "best", "beyond", "bike", + "biology", "birthday", "bishop", "black", "blanket", "blessing", "blimp", "blind", + "blue", "body", "bolt", "boring", "born", "both", "boundary", "bracelet", + "branch", "brave", "breathe", "briefing", "broken", "brother", "browser", "bucket", + "budget", "building", "bulb", "bulge", "bumpy", "bundle", "burden", "burning", + "busy", "buyer", "cage", "calcium", "camera", "campus", "canyon", "capacity", + "capital", "capture", "carbon", "cards", "careful", "cargo", "carpet", "carve", + "category", "cause", "ceiling", "center", "ceramic", "champion", "change", "charity", + "check", "chemical", "chest", "chew", "chubby", "cinema", "civil", "class", + "clay", "cleanup", "client", "climate", "clinic", "clock", "clogs", "closet", + "clothes", "club", "cluster", "coal", "coastal", "coding", "column", "company", + "corner", "costume", "counter", "course", "cover", "cowboy", "cradle", "craft", + "crazy", "credit", "cricket", "criminal", "crisis", "critical", "crowd", "crucial", + "crunch", "crush", "crystal", "cubic", "cultural", "curious", "curly", "custody", + "cylinder", "daisy", "damage", "dance", "darkness", "database", "daughter", "deadline", + "deal", "debris", "debut", "decent", "decision", "declare", "decorate", "decrease", + "deliver", "demand", "density", "deny", "depart", "depend", "depict", "deploy", + "describe", "desert", "desire", "desktop", "destroy", "detailed", "detect", "device", + "devote", "diagnose", "dictate", "diet", "dilemma", "diminish", "dining", "diploma", + "disaster", "discuss", "disease", "dish", "dismiss", "display", "distance", "dive", + "divorce", "document", "domain", "domestic", "dominant", "dough", "downtown", "dragon", + "dramatic", "dream", "dress", "drift", "drink", "drove", "drug", "dryer", + "duckling", "duke", "duration", "dwarf", "dynamic", "early", "earth", "easel", + "easy", "echo", "eclipse", "ecology", "edge", "editor", "educate", "either", + "elbow", "elder", "election", "elegant", "element", "elephant", "elevator", "elite", + "else", "email", "emerald", "emission", "emperor", "emphasis", "employer", "empty", + "ending", "endless", "endorse", "enemy", "energy", "enforce", "engage", "enjoy", + "enlarge", "entrance", "envelope", "envy", "epidemic", "episode", "equation", "equip", + "eraser", "erode", "escape", "estate", "estimate", "evaluate", "evening", "evidence", + "evil", "evoke", "exact", "example", "exceed", "exchange", "exclude", "excuse", + "execute", "exercise", "exhaust", "exotic", "expand", "expect", "explain", "express", + "extend", "extra", "eyebrow", "facility", "fact", "failure", "faint", "fake", + "false", "family", "famous", "fancy", "fangs", "fantasy", "fatal", "fatigue", + "favorite", "fawn", "fiber", "fiction", "filter", "finance", "findings", "finger", + "firefly", "firm", "fiscal", "fishing", "fitness", "flame", "flash", "flavor", + "flea", "flexible", "flip", "float", "floral", "fluff", "focus", "forbid", + "force", "forecast", "forget", "formal", "fortune", "forward", "founder", "fraction", + "fragment", "frequent", "freshman", "friar", "fridge", "friendly", "frost", "froth", + "frozen", "fumes", "funding", "furl", "fused", "galaxy", "game", "garbage", + "garden", "garlic", "gasoline", "gather", "general", "genius", "genre", "genuine", + "geology", "gesture", "glad", "glance", "glasses", "glen", "glimpse", "goat", + "golden", "graduate", "grant", "grasp", "gravity", "gray", "greatest", "grief", + "grill", "grin", "grocery", "gross", "group", "grownup", "grumpy", "guard", + "guest", "guilt", "guitar", "gums", "hairy", "hamster", "hand", "hanger", + "harvest", "have", "havoc", "hawk", "hazard", "headset", "health", "hearing", + "heat", "helpful", "herald", "herd", "hesitate", "hobo", "holiday", "holy", + "home", "hormone", "hospital", "hour", "huge", "human", "humidity", "hunting", + "husband", "hush", "husky", "hybrid", "idea", "identify", "idle", "image", + "impact", "imply", "improve", "impulse", "include", "income", "increase", "index", + "indicate", "industry", "infant", "inform", "inherit", "injury", "inmate", "insect", + "inside", "install", "intend", "intimate", "invasion", "involve", "iris", "island", + "isolate", "item", "ivory", "jacket", "jerky", "jewelry", "join", "judicial", + "juice", "jump", "junction", "junior", "junk", "jury", "justice", "kernel", + "keyboard", "kidney", "kind", "kitchen", "knife", "knit", "laden", "ladle", + "ladybug", "lair", "lamp", "language", "large", "laser", "laundry", "lawsuit", + "leader", "leaf", "learn", "leaves", "lecture", "legal", "legend", "legs", + "lend", "length", "level", "liberty", "library", "license", "lift", "likely", + "lilac", "lily", "lips", "liquid", "listen", "literary", "living", "lizard", + "loan", "lobe", "location", "losing", "loud", "loyalty", "luck", "lunar", + "lunch", "lungs", "luxury", "lying", "lyrics", "machine", "magazine", "maiden", + "mailman", "main", "makeup", "making", "mama", "manager", "mandate", "mansion", + "manual", "marathon", "march", "market", "marvel", "mason", "material", "math", + "maximum", "mayor", "meaning", "medal", "medical", "member", "memory", "mental", + "merchant", "merit", "method", "metric", "midst", "mild", "military", "mineral", + "minister", "miracle", "mixed", "mixture", "mobile", "modern", "modify", "moisture", + "moment", "morning", "mortgage", "mother", "mountain", "mouse", "move", "much", + "mule", "multiple", "muscle", "museum", "music", "mustang", "nail", "national", + "necklace", "negative", "nervous", "network", "news", "nuclear", "numb", "numerous", + "nylon", "oasis", "obesity", "object", "observe", "obtain", "ocean", "often", + "olympic", "omit", "oral", "orange", "orbit", "order", "ordinary", "organize", + "ounce", "oven", "overall", "owner", "paces", "pacific", "package", "paid", + "painting", "pajamas", "pancake", "pants", "papa", "paper", "parcel", "parking", + "party", "patent", "patrol", "payment", "payroll", "peaceful", "peanut", "peasant", + "pecan", "penalty", "pencil", "percent", "perfect", "permit", "petition", "phantom", + "pharmacy", "photo", "phrase", "physics", "pickup", "picture", "piece", "pile", + "pink", "pipeline", "pistol", "pitch", "plains", "plan", "plastic", "platform", + "playoff", "pleasure", "plot", "plunge", "practice", "prayer", "preach", "predator", + "pregnant", "premium", "prepare", "presence", "prevent", "priest", "primary", "priority", + "prisoner", "privacy", "prize", "problem", "process", "profile", "program", "promise", + "prospect", "provide", "prune", "public", "pulse", "pumps", "punish", "puny", + "pupal", "purchase", "purple", "python", "quantity", "quarter", "quick", "quiet", + "race", "racism", "radar", "railroad", "rainbow", "raisin", "random", "ranked", + "rapids", "raspy", "reaction", "realize", "rebound", "rebuild", "recall", "receiver", + "recover", "regret", "regular", "reject", "relate", "remember", "remind", "remove", + "render", "repair", "repeat", "replace", "require", "rescue", "research", "resident", + "response", "result", "retailer", "retreat", "reunion", "revenue", "review", "reward", + "rhyme", "rhythm", "rich", "rival", "river", "robin", "rocky", "romantic", + "romp", "roster", "round", "royal", "ruin", "ruler", "rumor", "sack", + "safari", "salary", "salon", "salt", "satisfy", "satoshi", "saver", "says", + "scandal", "scared", "scatter", "scene", "scholar", "science", "scout", "scramble", + "screw", "script", "scroll", "seafood", "season", "secret", "security", "segment", + "senior", "shadow", "shaft", "shame", "shaped", "sharp", "shelter", "sheriff", + "short", "should", "shrimp", "sidewalk", "silent", "silver", "similar", "simple", + "single", "sister", "skin", "skunk", "slap", "slavery", "sled", "slice", + "slim", "slow", "slush", "smart", "smear", "smell", "smirk", "smith", + "smoking", "smug", "snake", "snapshot", "sniff", "society", "software", "soldier", + "solution", "soul", "source", "space", "spark", "speak", "species", "spelling", + "spend", "spew", "spider", "spill", "spine", "spirit", "spit", "spray", + "sprinkle", "square", "squeeze", "stadium", "staff", "standard", "starting", "station", + "stay", "steady", "step", "stick", "stilt", "story", "strategy", "strike", + "style", "subject", "submit", "sugar", "suitable", "sunlight", "superior", "surface", + "surprise", "survive", "sweater", "swimming", "swing", "switch", "symbolic", "sympathy", + "syndrome", "system", "tackle", "tactics", "tadpole", "talent", "task", "taste", + "taught", "taxi", "teacher", "teammate", "teaspoon", "temple", "tenant", "tendency", + "tension", "terminal", "testify", "texture", "thank", "that", "theater", "theory", + "therapy", "thorn", "threaten", "thumb", "thunder", "ticket", "tidy", "timber", + "timely", "ting", "tofu", "together", "tolerate", "total", "toxic", "tracks", + "traffic", "training", "transfer", "trash", "traveler", "treat", "trend", "trial", + "tricycle", "trip", "triumph", "trouble", "true", "trust", "twice", "twin", + "type", "typical", "ugly", "ultimate", "umbrella", "uncover", "undergo", "unfair", + "unfold", "unhappy", "union", "universe", "unkind", "unknown", "unusual", "unwrap", + "upgrade", "upstairs", "username", "usher", "usual", "valid", "valuable", "vampire", + "vanish", "various", "vegan", "velvet", "venture", "verdict", "verify", "very", + "veteran", "vexed", "victim", "video", "view", "vintage", "violence", "viral", + "visitor", "visual", "vitamins", "vocal", "voice", "volume", "voter", "voting", + "walnut", "warmth", "warn", "watch", "wavy", "wealthy", "weapon", "webcam", + "welcome", "welfare", "western", "width", "wildlife", "window", "wine", "wireless", + "wisdom", "withdraw", "wits", "wolf", "woman", "work", "worthy", "wrap", + "wrist", "writing", "wrote", "year", "yelp", "yield", "yoga", "zero", +}; + +/** + * This array contains number representations of SLIP-39 words. + * These numbers are determined how the words were entered on a + * T9 keyboard with the following layout: + * ab (1) cd (2) ef (3) + * ghij (4) klm (5) nopq (6) + * rs (7) tuv (8) wxyz (9) + * + * Each word is uniquely defined by four buttons. + */ +static const struct { + uint16_t sequence; + uint16_t index; +} words_button_seq[WORDS_COUNT] = { + {1212, 0}, // academic + {1216, 7}, // adapt + {1236, 8}, // adequate + {1242, 1}, // acid + {1248, 9}, // adjust + {1254, 10}, // admit + {1263, 2}, // acne + {1267, 11}, // adorn + {1268, 3}, // acquire + {1276, 4}, // acrobat + {1281, 13}, // advance + {1284, 5}, // activity + {1285, 12}, // adult + {1286, 14}, // advocate + {1287, 6}, // actress + {1315, 67}, // beam + {1317, 68}, // beard + {1318, 69}, // beaver + {1326, 70}, // become + {1327, 71}, // bedroom + {1341, 72}, // behavior + {1346, 73}, // being + {1354, 74}, // believe + {1356, 75}, // belong + {1363, 76}, // benefit + {1371, 15}, // afraid + {1378, 77}, // best + {1396, 78}, // beyond + {1414, 16}, // again + {1417, 23}, // ajar + {1423, 19}, // aide + {1436, 17}, // agency + {1453, 79}, // bike + {1465, 80}, // biology + {1472, 20}, // aircraft + {1473, 18}, // agree + {1474, 82}, // bishop + {1475, 21}, // airline + {1476, 22}, // airport + {1478, 81}, // birthday + {1512, 83}, // black + {1514, 35}, // ambition + {1516, 84}, // blanket + {1517, 24}, // alarm + {1518, 25}, // album + {1519, 34}, // amazing + {1526, 26}, // alcohol + {1537, 85}, // blessing + {1543, 27}, // alien + {1545, 86}, // blimp + {1546, 87}, // blind + {1548, 28}, // alive + {1564, 29}, // alpha + {1568, 36}, // amount + {1573, 30}, // already + {1583, 88}, // blue + {1585, 32}, // aluminum + {1586, 31}, // alto + {1587, 37}, // amuse + {1591, 33}, // always + {1615, 38}, // analysis + {1617, 48}, // apart + {1618, 39}, // anatomy + {1623, 40}, // ancestor + {1624, 41}, // ancient + {1629, 89}, // body + {1643, 42}, // angel + {1645, 44}, // animal + {1647, 43}, // angry + {1658, 90}, // bolt + {1674, 91}, // boring + {1676, 92}, // born + {1679, 45}, // answer + {1681, 49}, // aquatic + {1683, 46}, // antenna + {1684, 93}, // both + {1686, 94}, // boundary + {1694, 47}, // anxiety + {1712, 95}, // bracelet + {1716, 96}, // branch + {1718, 97}, // brave + {1721, 50}, // arcade + {1731, 98}, // breathe + {1736, 51}, // arena + {1743, 99}, // briefing + {1748, 52}, // argue + {1753, 53}, // armed + {1763, 56}, // aspect + {1765, 100}, // broken + {1768, 101}, // brother + {1769, 102}, // browser + {1784, 54}, // artist + {1789, 55}, // artwork + {1824, 104}, // budget + {1825, 103}, // bucket + {1828, 57}, // auction + {1837, 60}, // average + {1841, 61}, // aviation + {1845, 105}, // building + {1848, 58}, // august + {1851, 106}, // bulb + {1854, 107}, // bulge + {1856, 108}, // bumpy + {1862, 109}, // bundle + {1864, 62}, // avoid + {1868, 59}, // aunt + {1872, 110}, // burden + {1876, 111}, // burning + {1879, 112}, // busy + {1893, 113}, // buyer + {1917, 63}, // award + {1919, 64}, // away + {1947, 65}, // axis + {1953, 66}, // axle + {2143, 114}, // cage + {2147, 185}, // daisy + {2151, 186}, // damage + {2152, 115}, // calcium + {2153, 116}, // camera + {2156, 117}, // campus + {2161, 119}, // capacity + {2162, 187}, // dance + {2164, 120}, // capital + {2168, 121}, // capture + {2169, 118}, // canyon + {2171, 122}, // carbon + {2172, 123}, // cards + {2173, 124}, // careful + {2174, 125}, // cargo + {2175, 188}, // darkness + {2176, 126}, // carpet + {2178, 127}, // carve + {2181, 189}, // database + {2183, 128}, // category + {2184, 190}, // daughter + {2187, 129}, // cause + {2312, 191}, // deadline + {2315, 192}, // deal + {2317, 193}, // debris + {2318, 194}, // debut + {2323, 195}, // decent + {2324, 196}, // decision + {2325, 197}, // declare + {2326, 198}, // decorate + {2327, 199}, // decrease + {2345, 130}, // ceiling + {2351, 201}, // demand + {2354, 200}, // deliver + {2361, 204}, // depart + {2363, 205}, // depend + {2364, 206}, // depict + {2365, 207}, // deploy + {2367, 202}, // density + {2368, 131}, // center + {2369, 203}, // deny + {2371, 132}, // ceramic + {2372, 208}, // describe + {2373, 209}, // desert + {2374, 210}, // desire + {2375, 211}, // desktop + {2378, 212}, // destroy + {2381, 213}, // detailed + {2383, 214}, // detect + {2384, 215}, // device + {2386, 216}, // devote + {2414, 217}, // diagnose + {2415, 133}, // champion + {2416, 134}, // change + {2417, 135}, // charity + {2428, 218}, // dictate + {2432, 136}, // check + {2435, 137}, // chemical + {2437, 138}, // chest + {2438, 219}, // diet + {2439, 139}, // chew + {2453, 220}, // dilemma + {2454, 221}, // diminish + {2463, 141}, // cinema + {2464, 222}, // dining + {2465, 223}, // diploma + {2471, 224}, // disaster + {2472, 225}, // discuss + {2473, 226}, // disease + {2474, 227}, // dish + {2475, 228}, // dismiss + {2476, 229}, // display + {2478, 230}, // distance + {2481, 140}, // chubby + {2483, 231}, // dive + {2484, 142}, // civil + {2486, 232}, // divorce + {2517, 143}, // class + {2519, 144}, // clay + {2531, 145}, // cleanup + {2543, 146}, // client + {2545, 147}, // climate + {2546, 148}, // clinic + {2562, 149}, // clock + {2564, 150}, // clogs + {2567, 151}, // closet + {2568, 152}, // clothes + {2581, 153}, // club + {2587, 154}, // cluster + {2615, 155}, // coal + {2617, 156}, // coastal + {2624, 157}, // coding + {2628, 233}, // document + {2651, 234}, // domain + {2653, 235}, // domestic + {2654, 236}, // dominant + {2656, 159}, // company + {2658, 158}, // column + {2676, 160}, // corner + {2678, 161}, // costume + {2683, 164}, // cover + {2684, 237}, // dough + {2686, 162}, // counter + {2687, 163}, // course + {2691, 165}, // cowboy + {2696, 238}, // downtown + {2712, 166}, // cradle + {2713, 167}, // craft + {2714, 239}, // dragon + {2715, 240}, // dramatic + {2719, 168}, // crazy + {2731, 241}, // dream + {2732, 169}, // credit + {2737, 242}, // dress + {2742, 170}, // cricket + {2743, 243}, // drift + {2745, 171}, // criminal + {2746, 244}, // drink + {2747, 172}, // crisis + {2748, 173}, // critical + {2768, 245}, // drove + {2769, 174}, // crowd + {2782, 175}, // crucial + {2784, 246}, // drug + {2786, 176}, // crunch + {2787, 177}, // crush + {2793, 247}, // dryer + {2797, 178}, // crystal + {2814, 179}, // cubic + {2825, 248}, // duckling + {2853, 249}, // duke + {2858, 180}, // cultural + {2871, 250}, // duration + {2874, 181}, // curious + {2875, 182}, // curly + {2878, 183}, // custody + {2917, 251}, // dwarf + {2954, 184}, // cylinder + {2961, 252}, // dynamic + {3124, 323}, // facility + {3128, 324}, // fact + {3145, 325}, // failure + {3146, 326}, // faint + {3153, 327}, // fake + {3154, 329}, // family + {3156, 330}, // famous + {3157, 328}, // false + {3162, 331}, // fancy + {3164, 332}, // fangs + {3168, 333}, // fantasy + {3173, 255}, // easel + {3175, 253}, // early + {3178, 254}, // earth + {3179, 256}, // easy + {3181, 334}, // fatal + {3184, 335}, // fatigue + {3186, 336}, // favorite + {3196, 337}, // fawn + {3243, 260}, // edge + {3246, 257}, // echo + {3248, 261}, // editor + {3254, 258}, // eclipse + {3265, 259}, // ecology + {3282, 262}, // educate + {3413, 338}, // fiber + {3428, 339}, // fiction + {3458, 340}, // filter + {3461, 341}, // finance + {3462, 342}, // findings + {3464, 343}, // finger + {3472, 346}, // fiscal + {3473, 344}, // firefly + {3474, 347}, // fishing + {3475, 345}, // firm + {3484, 263}, // either + {3486, 348}, // fitness + {3514, 273}, // email + {3515, 349}, // flame + {3516, 264}, // elbow + {3517, 350}, // flash + {3518, 351}, // flavor + {3523, 265}, // elder + {3531, 352}, // flea + {3532, 266}, // election + {3534, 267}, // elegant + {3535, 268}, // element + {3536, 269}, // elephant + {3537, 274}, // emerald + {3538, 270}, // elevator + {3539, 353}, // flexible + {3546, 354}, // flip + {3547, 275}, // emission + {3548, 271}, // elite + {3561, 355}, // float + {3563, 276}, // emperor + {3564, 277}, // emphasis + {3565, 278}, // employer + {3567, 356}, // floral + {3568, 279}, // empty + {3573, 272}, // else + {3583, 357}, // fluff + {3624, 280}, // ending + {3625, 281}, // endless + {3626, 282}, // endorse + {3628, 358}, // focus + {3635, 283}, // enemy + {3636, 285}, // enforce + {3637, 284}, // energy + {3641, 286}, // engage + {3642, 292}, // epidemic + {3646, 287}, // enjoy + {3647, 293}, // episode + {3651, 288}, // enlarge + {3671, 359}, // forbid + {3672, 360}, // force + {3673, 361}, // forecast + {3674, 362}, // forget + {3675, 363}, // formal + {3678, 364}, // fortune + {3679, 365}, // forward + {3681, 294}, // equation + {3683, 290}, // envelope + {3684, 295}, // equip + {3686, 366}, // founder + {3687, 289}, // entrance + {3689, 291}, // envy + {3712, 367}, // fraction + {3714, 368}, // fragment + {3717, 296}, // eraser + {3721, 298}, // escape + {3736, 369}, // frequent + {3737, 370}, // freshman + {3741, 371}, // friar + {3742, 372}, // fridge + {3743, 373}, // friendly + {3762, 297}, // erode + {3767, 374}, // frost + {3768, 375}, // froth + {3769, 376}, // frozen + {3781, 299}, // estate + {3784, 300}, // estimate + {3815, 301}, // evaluate + {3836, 302}, // evening + {3842, 303}, // evidence + {3845, 304}, // evil + {3853, 377}, // fumes + {3862, 378}, // funding + {3865, 305}, // evoke + {3873, 380}, // fused + {3875, 379}, // furl + {3912, 306}, // exact + {3915, 307}, // example + {3923, 308}, // exceed + {3924, 309}, // exchange + {3925, 310}, // exclude + {3928, 311}, // excuse + {3931, 322}, // eyebrow + {3932, 312}, // execute + {3937, 313}, // exercise + {3941, 314}, // exhaust + {3961, 316}, // expand + {3963, 317}, // expect + {3965, 318}, // explain + {3967, 319}, // express + {3968, 315}, // exotic + {3983, 320}, // extend + {3987, 321}, // extra + {4125, 483}, // jacket + {4147, 420}, // hairy + {4151, 381}, // galaxy + {4153, 382}, // game + {4157, 421}, // hamster + {4162, 422}, // hand + {4164, 423}, // hanger + {4171, 383}, // garbage + {4172, 384}, // garden + {4175, 385}, // garlic + {4176, 386}, // gasoline + {4178, 424}, // harvest + {4183, 425}, // have + {4184, 387}, // gather + {4186, 426}, // havoc + {4191, 428}, // hazard + {4195, 427}, // hawk + {4231, 452}, // idea + {4236, 453}, // identify + {4253, 454}, // idle + {4312, 429}, // headset + {4315, 430}, // health + {4317, 431}, // hearing + {4318, 432}, // heat + {4356, 433}, // helpful + {4363, 388}, // general + {4364, 389}, // genius + {4365, 392}, // geology + {4367, 390}, // genre + {4368, 391}, // genuine + {4371, 434}, // herald + {4372, 435}, // herd + {4374, 436}, // hesitate + {4375, 484}, // jerky + {4378, 393}, // gesture + {4393, 485}, // jewelry + {4512, 394}, // glad + {4514, 455}, // image + {4516, 395}, // glance + {4517, 396}, // glasses + {4536, 397}, // glen + {4545, 398}, // glimpse + {4561, 456}, // impact + {4565, 457}, // imply + {4567, 458}, // improve + {4568, 459}, // impulse + {4616, 437}, // hobo + {4618, 399}, // goat + {4623, 463}, // index + {4624, 464}, // indicate + {4625, 460}, // include + {4626, 461}, // income + {4627, 462}, // increase + {4628, 465}, // industry + {4631, 466}, // infant + {4636, 467}, // inform + {4643, 468}, // inherit + {4646, 486}, // join + {4648, 469}, // injury + {4651, 470}, // inmate + {4652, 400}, // golden + {4653, 440}, // home + {4654, 438}, // holiday + {4659, 439}, // holy + {4673, 471}, // insect + {4674, 472}, // inside + {4675, 441}, // hormone + {4676, 442}, // hospital + {4678, 473}, // install + {4681, 476}, // invasion + {4683, 474}, // intend + {4684, 475}, // intimate + {4686, 477}, // involve + {4687, 443}, // hour + {4712, 401}, // graduate + {4716, 402}, // grant + {4717, 403}, // grasp + {4718, 404}, // gravity + {4719, 405}, // gray + {4731, 406}, // greatest + {4743, 407}, // grief + {4745, 408}, // grill + {4746, 409}, // grin + {4747, 478}, // iris + {4751, 479}, // island + {4762, 410}, // grocery + {4765, 480}, // isolate + {4767, 411}, // gross + {4768, 412}, // group + {4769, 413}, // grownup + {4785, 414}, // grumpy + {4817, 415}, // guard + {4824, 487}, // judicial + {4835, 481}, // item + {4837, 416}, // guest + {4842, 488}, // juice + {4843, 444}, // huge + {4845, 417}, // guilt + {4848, 418}, // guitar + {4851, 445}, // human + {4854, 446}, // humidity + {4856, 489}, // jump + {4857, 419}, // gums + {4862, 490}, // junction + {4864, 491}, // junior + {4865, 492}, // junk + {4867, 482}, // ivory + {4868, 447}, // hunting + {4871, 448}, // husband + {4874, 449}, // hush + {4875, 450}, // husky + {4878, 494}, // justice + {4879, 493}, // jury + {4917, 451}, // hybrid + {5123, 502}, // laden + {5124, 549}, // machine + {5125, 503}, // ladle + {5129, 504}, // ladybug + {5141, 550}, // magazine + {5142, 551}, // maiden + {5145, 552}, // mailman + {5146, 553}, // main + {5147, 505}, // lair + {5151, 556}, // mama + {5153, 554}, // makeup + {5154, 555}, // making + {5156, 506}, // lamp + {5161, 557}, // manager + {5162, 558}, // mandate + {5164, 507}, // language + {5167, 559}, // mansion + {5168, 560}, // manual + {5171, 561}, // marathon + {5172, 562}, // march + {5173, 509}, // laser + {5174, 508}, // large + {5175, 563}, // market + {5176, 565}, // mason + {5178, 564}, // marvel + {5183, 566}, // material + {5184, 567}, // math + {5186, 510}, // laundry + {5194, 568}, // maximum + {5196, 569}, // mayor + {5197, 511}, // lawsuit + {5312, 512}, // leader + {5313, 513}, // leaf + {5316, 570}, // meaning + {5317, 514}, // learn + {5318, 515}, // leaves + {5321, 571}, // medal + {5324, 572}, // medical + {5328, 516}, // lecture + {5341, 517}, // legal + {5343, 518}, // legend + {5347, 519}, // legs + {5351, 573}, // member + {5356, 574}, // memory + {5362, 520}, // lend + {5364, 521}, // length + {5368, 575}, // mental + {5372, 576}, // merchant + {5374, 577}, // merit + {5376, 495}, // kernel + {5383, 522}, // level + {5384, 578}, // method + {5387, 579}, // metric + {5391, 496}, // keyboard + {5413, 523}, // liberty + {5417, 524}, // library + {5423, 525}, // license + {5426, 497}, // kidney + {5427, 580}, // midst + {5438, 526}, // lift + {5451, 528}, // lilac + {5452, 581}, // mild + {5453, 527}, // likely + {5454, 582}, // military + {5459, 529}, // lily + {5462, 498}, // kind + {5463, 583}, // mineral + {5464, 584}, // minister + {5467, 530}, // lips + {5468, 531}, // liquid + {5471, 585}, // miracle + {5478, 532}, // listen + {5482, 499}, // kitchen + {5483, 533}, // literary + {5484, 534}, // living + {5491, 535}, // lizard + {5493, 586}, // mixed + {5498, 587}, // mixture + {5613, 537}, // lobe + {5614, 588}, // mobile + {5616, 536}, // loan + {5621, 538}, // location + {5623, 589}, // modern + {5624, 590}, // modify + {5643, 500}, // knife + {5647, 591}, // moisture + {5648, 501}, // knit + {5653, 592}, // moment + {5674, 539}, // losing + {5676, 593}, // morning + {5678, 594}, // mortgage + {5682, 540}, // loud + {5683, 598}, // move + {5684, 595}, // mother + {5686, 596}, // mountain + {5687, 597}, // mouse + {5691, 541}, // loyalty + {5824, 599}, // much + {5825, 542}, // luck + {5853, 600}, // mule + {5858, 601}, // multiple + {5861, 543}, // lunar + {5862, 544}, // lunch + {5864, 545}, // lungs + {5872, 602}, // muscle + {5873, 603}, // museum + {5874, 604}, // music + {5878, 605}, // mustang + {5898, 546}, // luxury + {5946, 547}, // lying + {5974, 548}, // lyrics + {6123, 636}, // paces + {6124, 637}, // pacific + {6125, 638}, // package + {6137, 618}, // obesity + {6141, 641}, // pajamas + {6142, 639}, // paid + {6143, 619}, // object + {6145, 606}, // nail + {6146, 640}, // painting + {6161, 644}, // papa + {6162, 642}, // pancake + {6163, 645}, // paper + {6168, 643}, // pants + {6172, 646}, // parcel + {6173, 620}, // observe + {6174, 617}, // oasis + {6175, 647}, // parking + {6178, 648}, // party + {6181, 621}, // obtain + {6183, 649}, // patent + {6184, 607}, // national + {6187, 650}, // patrol + {6195, 651}, // payment + {6197, 652}, // payroll + {6231, 622}, // ocean + {6312, 653}, // peaceful + {6316, 654}, // peanut + {6317, 655}, // peasant + {6321, 656}, // pecan + {6325, 608}, // necklace + {6341, 609}, // negative + {6361, 657}, // penalty + {6362, 658}, // pencil + {6372, 659}, // percent + {6373, 660}, // perfect + {6375, 661}, // permit + {6378, 610}, // nervous + {6383, 623}, // often + {6384, 662}, // petition + {6389, 611}, // network + {6397, 612}, // news + {6416, 663}, // phantom + {6417, 664}, // pharmacy + {6425, 668}, // pickup + {6428, 669}, // picture + {6432, 670}, // piece + {6453, 671}, // pile + {6463, 673}, // pipeline + {6465, 672}, // pink + {6468, 665}, // photo + {6471, 666}, // phrase + {6478, 674}, // pistol + {6482, 675}, // pitch + {6497, 667}, // physics + {6514, 676}, // plains + {6516, 677}, // plan + {6517, 678}, // plastic + {6518, 679}, // platform + {6519, 680}, // playoff + {6531, 681}, // pleasure + {6548, 625}, // omit + {6568, 682}, // plot + {6586, 683}, // plunge + {6595, 624}, // olympic + {6712, 684}, // practice + {6714, 628}, // orbit + {6715, 626}, // oral + {6716, 627}, // orange + {6719, 685}, // prayer + {6723, 629}, // order + {6724, 630}, // ordinary + {6731, 686}, // preach + {6732, 687}, // predator + {6734, 688}, // pregnant + {6735, 689}, // premium + {6736, 690}, // prepare + {6737, 691}, // presence + {6738, 692}, // prevent + {6741, 631}, // organize + {6743, 693}, // priest + {6745, 694}, // primary + {6746, 695}, // priority + {6747, 696}, // prisoner + {6748, 697}, // privacy + {6749, 698}, // prize + {6761, 699}, // problem + {6762, 700}, // process + {6763, 701}, // profile + {6764, 702}, // program + {6765, 703}, // promise + {6767, 704}, // prospect + {6768, 705}, // provide + {6786, 706}, // prune + {6815, 707}, // public + {6816, 716}, // quantity + {6817, 717}, // quarter + {6825, 613}, // nuclear + {6836, 633}, // oven + {6837, 634}, // overall + {6842, 718}, // quick + {6843, 719}, // quiet + {6851, 614}, // numb + {6853, 615}, // numerous + {6856, 709}, // pumps + {6857, 708}, // pulse + {6861, 712}, // pupal + {6862, 632}, // ounce + {6864, 710}, // punish + {6869, 711}, // puny + {6872, 713}, // purchase + {6876, 714}, // purple + {6956, 616}, // nylon + {6963, 635}, // owner + {6984, 715}, // python + {7121, 722}, // radar + {7123, 720}, // race + {7124, 721}, // racism + {7125, 775}, // sack + {7131, 776}, // safari + {7145, 723}, // railroad + {7146, 724}, // rainbow + {7147, 725}, // raisin + {7151, 777}, // salary + {7156, 778}, // salon + {7158, 779}, // salt + {7162, 726}, // random + {7164, 728}, // rapids + {7165, 727}, // ranked + {7176, 729}, // raspy + {7183, 782}, // saver + {7184, 780}, // satisfy + {7186, 781}, // satoshi + {7197, 783}, // says + {7216, 784}, // scandal + {7217, 785}, // scared + {7218, 786}, // scatter + {7236, 787}, // scene + {7243, 789}, // science + {7246, 788}, // scholar + {7268, 790}, // scout + {7271, 791}, // scramble + {7273, 792}, // screw + {7274, 793}, // script + {7276, 794}, // scroll + {7312, 730}, // reaction + {7313, 795}, // seafood + {7315, 731}, // realize + {7316, 732}, // rebound + {7317, 796}, // season + {7318, 733}, // rebuild + {7321, 734}, // recall + {7323, 735}, // receiver + {7326, 736}, // recover + {7327, 797}, // secret + {7328, 798}, // security + {7343, 739}, // reject + {7345, 799}, // segment + {7347, 737}, // regret + {7348, 738}, // regular + {7351, 740}, // relate + {7353, 741}, // remember + {7354, 742}, // remind + {7356, 743}, // remove + {7361, 745}, // repair + {7362, 744}, // render + {7363, 746}, // repeat + {7364, 800}, // senior + {7365, 747}, // replace + {7368, 748}, // require + {7372, 749}, // rescue + {7373, 750}, // research + {7374, 751}, // resident + {7376, 752}, // response + {7378, 753}, // result + {7381, 754}, // retailer + {7383, 757}, // revenue + {7384, 758}, // review + {7386, 756}, // reunion + {7387, 755}, // retreat + {7391, 759}, // reward + {7412, 801}, // shadow + {7413, 802}, // shaft + {7415, 803}, // shame + {7416, 804}, // shaped + {7417, 805}, // sharp + {7423, 811}, // sidewalk + {7424, 762}, // rich + {7435, 806}, // shelter + {7437, 807}, // sheriff + {7453, 812}, // silent + {7454, 814}, // similar + {7456, 815}, // simple + {7458, 813}, // silver + {7464, 816}, // single + {7467, 808}, // short + {7468, 809}, // should + {7474, 810}, // shrimp + {7478, 817}, // sister + {7481, 763}, // rival + {7483, 764}, // river + {7495, 760}, // rhyme + {7498, 761}, // rhythm + {7516, 820}, // slap + {7517, 827}, // smart + {7518, 821}, // slavery + {7531, 828}, // smear + {7532, 822}, // sled + {7535, 829}, // smell + {7542, 823}, // slice + {7545, 824}, // slim + {7546, 818}, // skin + {7547, 830}, // smirk + {7548, 831}, // smith + {7565, 832}, // smoking + {7569, 825}, // slow + {7584, 833}, // smug + {7586, 819}, // skunk + {7587, 826}, // slush + {7612, 843}, // space + {7614, 765}, // robin + {7615, 834}, // snake + {7616, 835}, // snapshot + {7617, 844}, // spark + {7624, 837}, // society + {7625, 766}, // rocky + {7631, 845}, // speak + {7632, 846}, // species + {7635, 847}, // spelling + {7636, 848}, // spend + {7638, 838}, // software + {7639, 849}, // spew + {7642, 850}, // spider + {7643, 836}, // sniff + {7645, 851}, // spill + {7646, 852}, // spine + {7647, 853}, // spirit + {7648, 854}, // spit + {7651, 767}, // romantic + {7652, 839}, // soldier + {7656, 768}, // romp + {7658, 840}, // solution + {7671, 855}, // spray + {7674, 856}, // sprinkle + {7678, 769}, // roster + {7681, 857}, // square + {7683, 858}, // squeeze + {7685, 841}, // soul + {7686, 770}, // round + {7687, 842}, // source + {7691, 771}, // royal + {7812, 859}, // stadium + {7813, 860}, // staff + {7814, 873}, // subject + {7815, 874}, // submit + {7816, 861}, // standard + {7817, 862}, // starting + {7818, 863}, // station + {7819, 864}, // stay + {7831, 865}, // steady + {7836, 866}, // step + {7841, 875}, // sugar + {7842, 867}, // stick + {7845, 868}, // stilt + {7846, 772}, // ruin + {7848, 876}, // suitable + {7853, 773}, // ruler + {7856, 774}, // rumor + {7863, 878}, // superior + {7865, 877}, // sunlight + {7867, 869}, // story + {7871, 870}, // strategy + {7873, 879}, // surface + {7874, 871}, // strike + {7876, 880}, // surprise + {7878, 881}, // survive + {7895, 872}, // style + {7931, 882}, // sweater + {7945, 883}, // swimming + {7946, 884}, // swing + {7948, 885}, // switch + {7951, 886}, // symbolic + {7956, 887}, // sympathy + {7962, 888}, // syndrome + {7978, 889}, // system + {8125, 890}, // tackle + {8126, 892}, // tadpole + {8128, 891}, // tactics + {8153, 893}, // talent + {8154, 965}, // valid + {8156, 967}, // vampire + {8158, 966}, // valuable + {8164, 968}, // vanish + {8174, 969}, // various + {8175, 894}, // task + {8178, 895}, // taste + {8184, 896}, // taught + {8194, 897}, // taxi + {8312, 898}, // teacher + {8315, 899}, // teammate + {8317, 900}, // teaspoon + {8341, 970}, // vegan + {8356, 901}, // temple + {8358, 971}, // velvet + {8361, 902}, // tenant + {8362, 903}, // tendency + {8367, 904}, // tension + {8368, 972}, // venture + {8372, 973}, // verdict + {8374, 974}, // verify + {8375, 905}, // terminal + {8378, 906}, // testify + {8379, 975}, // very + {8383, 976}, // veteran + {8393, 977}, // vexed + {8398, 907}, // texture + {8416, 908}, // thank + {8418, 909}, // that + {8423, 979}, // video + {8425, 917}, // ticket + {8428, 978}, // victim + {8429, 918}, // tidy + {8431, 910}, // theater + {8436, 911}, // theory + {8437, 912}, // therapy + {8439, 980}, // view + {8451, 919}, // timber + {8453, 920}, // timely + {8459, 946}, // ugly + {8464, 921}, // ting + {8465, 982}, // violence + {8467, 913}, // thorn + {8468, 981}, // vintage + {8471, 983}, // viral + {8473, 914}, // threaten + {8474, 984}, // visitor + {8478, 985}, // visual + {8481, 986}, // vitamins + {8485, 915}, // thumb + {8486, 916}, // thunder + {8517, 948}, // umbrella + {8584, 947}, // ultimate + {8621, 987}, // vocal + {8623, 950}, // undergo + {8626, 949}, // uncover + {8631, 951}, // unfair + {8636, 952}, // unfold + {8638, 922}, // tofu + {8641, 953}, // unhappy + {8642, 988}, // voice + {8643, 923}, // together + {8646, 954}, // union + {8647, 960}, // upgrade + {8648, 955}, // universe + {8653, 924}, // tolerate + {8654, 956}, // unkind + {8656, 957}, // unknown + {8658, 989}, // volume + {8678, 961}, // upstairs + {8681, 925}, // total + {8683, 990}, // voter + {8684, 991}, // voting + {8687, 958}, // unusual + {8694, 926}, // toxic + {8697, 959}, // unwrap + {8712, 927}, // tracks + {8713, 928}, // traffic + {8714, 929}, // training + {8716, 930}, // transfer + {8717, 931}, // trash + {8718, 932}, // traveler + {8731, 933}, // treat + {8736, 934}, // trend + {8737, 962}, // username + {8741, 935}, // trial + {8742, 936}, // tricycle + {8743, 963}, // usher + {8746, 937}, // trip + {8748, 938}, // triumph + {8768, 939}, // trouble + {8781, 964}, // usual + {8783, 940}, // true + {8787, 941}, // trust + {8942, 942}, // twice + {8946, 943}, // twin + {8963, 944}, // type + {8964, 945}, // typical + {9156, 992}, // walnut + {9175, 993}, // warmth + {9176, 994}, // warn + {9182, 995}, // watch + {9189, 996}, // wavy + {9312, 999}, // webcam + {9315, 997}, // wealthy + {9316, 998}, // weapon + {9317, 1019}, // year + {9352, 1000}, // welcome + {9353, 1001}, // welfare + {9356, 1020}, // yelp + {9376, 1023}, // zero + {9378, 1002}, // western + {9428, 1003}, // width + {9435, 1021}, // yield + {9452, 1004}, // wildlife + {9462, 1005}, // window + {9463, 1006}, // wine + {9472, 1008}, // wisdom + {9473, 1007}, // wireless + {9484, 1009}, // withdraw + {9487, 1010}, // wits + {9641, 1022}, // yoga + {9651, 1012}, // woman + {9653, 1011}, // wolf + {9675, 1013}, // work + {9678, 1014}, // worthy + {9716, 1015}, // wrap + {9747, 1016}, // wrist + {9748, 1017}, // writing + {9768, 1018}, // wrote +}; + +#endif diff --git a/applications/external/flipbip/scenes/flipbip_scene.c b/applications/external/flipbip/scenes/flipbip_scene.c new file mode 100644 index 0000000000..d3656a868c --- /dev/null +++ b/applications/external/flipbip/scenes/flipbip_scene.c @@ -0,0 +1,30 @@ +#include "flipbip_scene.h" + +// Generate scene on_enter handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, +void (*const flipbip_on_enter_handlers[])(void*) = { +#include "flipbip_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_event handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event, +bool (*const flipbip_on_event_handlers[])(void* context, SceneManagerEvent event) = { +#include "flipbip_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_exit handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit, +void (*const flipbip_on_exit_handlers[])(void* context) = { +#include "flipbip_scene_config.h" +}; +#undef ADD_SCENE + +// Initialize scene handlers configuration structure +const SceneManagerHandlers flipbip_scene_handlers = { + .on_enter_handlers = flipbip_on_enter_handlers, + .on_event_handlers = flipbip_on_event_handlers, + .on_exit_handlers = flipbip_on_exit_handlers, + .scene_num = FlipBipSceneNum, +}; diff --git a/applications/external/flipbip/scenes/flipbip_scene.h b/applications/external/flipbip/scenes/flipbip_scene.h new file mode 100644 index 0000000000..fd49b44c0c --- /dev/null +++ b/applications/external/flipbip/scenes/flipbip_scene.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +// Generate scene id and total number +#define ADD_SCENE(prefix, name, id) FlipBipScene##id, +typedef enum { +#include "flipbip_scene_config.h" + FlipBipSceneNum, +} FlipBipScene; +#undef ADD_SCENE + +extern const SceneManagerHandlers flipbip_scene_handlers; + +// Generate scene on_enter handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); +#include "flipbip_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_event handlers declaration +#define ADD_SCENE(prefix, name, id) \ + bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event); +#include "flipbip_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_exit handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context); +#include "flipbip_scene_config.h" +#undef ADD_SCENE diff --git a/applications/external/flipbip/scenes/flipbip_scene_config.h b/applications/external/flipbip/scenes/flipbip_scene_config.h new file mode 100644 index 0000000000..a62832162e --- /dev/null +++ b/applications/external/flipbip/scenes/flipbip_scene_config.h @@ -0,0 +1,4 @@ +ADD_SCENE(flipbip, startscreen, Startscreen) +ADD_SCENE(flipbip, menu, Menu) +ADD_SCENE(flipbip, scene_1, Scene_1) +ADD_SCENE(flipbip, settings, Settings) \ No newline at end of file diff --git a/applications/external/flipbip/scenes/flipbip_scene_menu.c b/applications/external/flipbip/scenes/flipbip_scene_menu.c new file mode 100644 index 0000000000..04525909d2 --- /dev/null +++ b/applications/external/flipbip/scenes/flipbip_scene_menu.c @@ -0,0 +1,130 @@ +#include "../flipbip.h" +#include "../helpers/flipbip_file.h" + +enum SubmenuIndex { + SubmenuIndexScene1BTC = 10, + SubmenuIndexScene1ETH, + SubmenuIndexScene1DOGE, + SubmenuIndexScene1New, + SubmenuIndexScene1Import, + SubmenuIndexSettings, +}; + +void flipbip_scene_menu_submenu_callback(void* context, uint32_t index) { + FlipBip* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +void flipbip_scene_menu_on_enter(void* context) { + FlipBip* app = context; + + if(flipbip_has_file(FlipBipFileKey, NULL, false) && + flipbip_has_file(FlipBipFileDat, NULL, false)) { + submenu_add_item( + app->submenu, + "View BTC wallet", + SubmenuIndexScene1BTC, + flipbip_scene_menu_submenu_callback, + app); + submenu_add_item( + app->submenu, + "View ETH wallet", + SubmenuIndexScene1ETH, + flipbip_scene_menu_submenu_callback, + app); + submenu_add_item( + app->submenu, + "View DOGE wallet", + SubmenuIndexScene1DOGE, + flipbip_scene_menu_submenu_callback, + app); + submenu_add_item( + app->submenu, + "Regenerate wallet", + SubmenuIndexScene1New, + flipbip_scene_menu_submenu_callback, + app); + } else { + submenu_add_item( + app->submenu, + "Generate new wallet", + SubmenuIndexScene1New, + flipbip_scene_menu_submenu_callback, + app); + } + submenu_add_item( + app->submenu, + "Import from mnemonic", + SubmenuIndexScene1Import, + flipbip_scene_menu_submenu_callback, + app); + + submenu_add_item( + app->submenu, "Settings", SubmenuIndexSettings, flipbip_scene_menu_submenu_callback, app); + + submenu_set_selected_item( + app->submenu, scene_manager_get_scene_state(app->scene_manager, FlipBipSceneMenu)); + + view_dispatcher_switch_to_view(app->view_dispatcher, FlipBipViewIdMenu); +} + +bool flipbip_scene_menu_on_event(void* context, SceneManagerEvent event) { + FlipBip* app = context; + //UNUSED(app); + if(event.type == SceneManagerEventTypeBack) { + //exit app + scene_manager_stop(app->scene_manager); + view_dispatcher_stop(app->view_dispatcher); + return true; + } else if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexScene1BTC) { + app->overwrite_saved_seed = 0; + app->import_from_mnemonic = 0; + app->bip44_coin = FlipBipCoinBTC0; + scene_manager_set_scene_state( + app->scene_manager, FlipBipSceneMenu, SubmenuIndexScene1BTC); + scene_manager_next_scene(app->scene_manager, FlipBipSceneScene_1); + return true; + } else if(event.event == SubmenuIndexScene1ETH) { + app->overwrite_saved_seed = 0; + app->import_from_mnemonic = 0; + app->bip44_coin = FlipBipCoinETH60; + scene_manager_set_scene_state( + app->scene_manager, FlipBipSceneMenu, SubmenuIndexScene1ETH); + scene_manager_next_scene(app->scene_manager, FlipBipSceneScene_1); + return true; + } else if(event.event == SubmenuIndexScene1DOGE) { + app->overwrite_saved_seed = 0; + app->import_from_mnemonic = 0; + app->bip44_coin = FlipBipCoinDOGE3; + scene_manager_set_scene_state( + app->scene_manager, FlipBipSceneMenu, SubmenuIndexScene1DOGE); + scene_manager_next_scene(app->scene_manager, FlipBipSceneScene_1); + return true; + } else if(event.event == SubmenuIndexScene1New) { + app->overwrite_saved_seed = 1; + app->import_from_mnemonic = 0; + scene_manager_set_scene_state( + app->scene_manager, FlipBipSceneMenu, SubmenuIndexScene1New); + scene_manager_next_scene(app->scene_manager, FlipBipSceneScene_1); + return true; + } else if(event.event == SubmenuIndexScene1Import) { + app->import_from_mnemonic = 1; + app->input_state = FlipBipTextInputMnemonic; + text_input_set_header_text(app->text_input, "Enter mnemonic phrase"); + view_dispatcher_switch_to_view(app->view_dispatcher, FlipBipViewIdTextInput); + return true; + } else if(event.event == SubmenuIndexSettings) { + scene_manager_set_scene_state( + app->scene_manager, FlipBipSceneMenu, SubmenuIndexSettings); + scene_manager_next_scene(app->scene_manager, FlipBipSceneSettings); + return true; + } + } + return false; +} + +void flipbip_scene_menu_on_exit(void* context) { + FlipBip* app = context; + submenu_reset(app->submenu); +} \ No newline at end of file diff --git a/applications/external/flipbip/scenes/flipbip_scene_scene_1.c b/applications/external/flipbip/scenes/flipbip_scene_scene_1.c new file mode 100644 index 0000000000..6f4064cd40 --- /dev/null +++ b/applications/external/flipbip/scenes/flipbip_scene_scene_1.c @@ -0,0 +1,50 @@ +#include "../flipbip.h" +#include "../helpers/flipbip_custom_event.h" +#include "../views/flipbip_scene_1.h" + +void flipbip_scene_1_callback(FlipBipCustomEvent event, void* context) { + furi_assert(context); + FlipBip* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, event); +} + +void flipbip_scene_scene_1_on_enter(void* context) { + furi_assert(context); + FlipBip* app = context; + flipbip_scene_1_set_callback(app->flipbip_scene_1, flipbip_scene_1_callback, app); + view_dispatcher_switch_to_view(app->view_dispatcher, FlipBipViewIdScene1); +} + +bool flipbip_scene_scene_1_on_event(void* context, SceneManagerEvent event) { + FlipBip* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + switch(event.event) { + case FlipBipCustomEventScene1Left: + case FlipBipCustomEventScene1Right: + break; + case FlipBipCustomEventScene1Up: + case FlipBipCustomEventScene1Down: + break; + case FlipBipCustomEventScene1Back: + notification_message(app->notification, &sequence_reset_red); + notification_message(app->notification, &sequence_reset_green); + notification_message(app->notification, &sequence_reset_blue); + if(!scene_manager_search_and_switch_to_previous_scene( + app->scene_manager, FlipBipSceneMenu)) { + scene_manager_stop(app->scene_manager); + view_dispatcher_stop(app->view_dispatcher); + } + consumed = true; + break; + } + } + + return consumed; +} + +void flipbip_scene_scene_1_on_exit(void* context) { + FlipBip* app = context; + UNUSED(app); +} \ No newline at end of file diff --git a/applications/external/flipbip/scenes/flipbip_scene_settings.c b/applications/external/flipbip/scenes/flipbip_scene_settings.c new file mode 100644 index 0000000000..c743c97b8b --- /dev/null +++ b/applications/external/flipbip/scenes/flipbip_scene_settings.c @@ -0,0 +1,141 @@ +#include "../flipbip.h" +#include +// From: lib/crypto +#include + +#define TEXT_LABEL_ON "ON" +#define TEXT_LABEL_OFF "OFF" + +const char* const haptic_text[2] = { + TEXT_LABEL_OFF, + TEXT_LABEL_ON, +}; +const uint32_t haptic_value[2] = { + FlipBipHapticOff, + FlipBipHapticOn, +}; + +const char* const led_text[2] = { + TEXT_LABEL_OFF, + TEXT_LABEL_ON, +}; +const uint32_t led_value[2] = { + FlipBipLedOff, + FlipBipLedOn, +}; + +const char* const bip39_strength_text[3] = { + "12", + "18", + "24", +}; +const uint32_t bip39_strength_value[3] = { + FlipBipStrength128, + FlipBipStrength192, + FlipBipStrength256, +}; + +const char* const passphrase_text[2] = { + TEXT_LABEL_OFF, + TEXT_LABEL_ON, +}; +const uint32_t passphrase_value[2] = { + FlipBipPassphraseOff, + FlipBipPassphraseOn, +}; + +static void flipbip_scene_settings_set_haptic(VariableItem* item) { + FlipBip* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, haptic_text[index]); + app->haptic = haptic_value[index]; +} + +static void flipbip_scene_settings_set_led(VariableItem* item) { + FlipBip* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, led_text[index]); + app->led = led_value[index]; +} + +static void flipbip_scene_settings_set_bip39_strength(VariableItem* item) { + FlipBip* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, bip39_strength_text[index]); + app->bip39_strength = bip39_strength_value[index]; +} + +static void flipbip_scene_settings_set_passphrase(VariableItem* item) { + FlipBip* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, passphrase_text[index]); + app->passphrase = passphrase_value[index]; + + if(app->passphrase == FlipBipPassphraseOn) { + app->input_state = FlipBipTextInputPassphrase; + text_input_set_header_text(app->text_input, "Enter BIP39 passphrase"); + view_dispatcher_switch_to_view(app->view_dispatcher, FlipBipViewIdTextInput); + } else { + memzero(app->passphrase_text, TEXT_BUFFER_SIZE); + } +} + +void flipbip_scene_settings_submenu_callback(void* context, uint32_t index) { + FlipBip* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +void flipbip_scene_settings_on_enter(void* context) { + FlipBip* app = context; + VariableItem* item; + uint8_t value_index; + + // BIP39 strength + item = variable_item_list_add( + app->variable_item_list, "BIP39 Words:", 3, flipbip_scene_settings_set_bip39_strength, app); + value_index = value_index_uint32(app->bip39_strength, bip39_strength_value, 3); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, bip39_strength_text[value_index]); + + // Passphrase + item = variable_item_list_add( + app->variable_item_list, + "BIP39 Passphrase:", + 2, + flipbip_scene_settings_set_passphrase, + app); + value_index = value_index_uint32(app->passphrase, passphrase_value, 2); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, passphrase_text[value_index]); + + // Vibro on/off + item = variable_item_list_add( + app->variable_item_list, "Vibro/Haptic:", 2, flipbip_scene_settings_set_haptic, app); + value_index = value_index_uint32(app->haptic, haptic_value, 2); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, haptic_text[value_index]); + + // LED Effects on/off + item = variable_item_list_add( + app->variable_item_list, "LED FX:", 2, flipbip_scene_settings_set_led, app); + value_index = value_index_uint32(app->led, led_value, 2); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, led_text[value_index]); + + view_dispatcher_switch_to_view(app->view_dispatcher, FlipBipViewIdSettings); +} + +bool flipbip_scene_settings_on_event(void* context, SceneManagerEvent event) { + FlipBip* app = context; + UNUSED(app); + bool consumed = false; + if(event.type == SceneManagerEventTypeCustom) { + } + return consumed; +} + +void flipbip_scene_settings_on_exit(void* context) { + FlipBip* app = context; + variable_item_list_set_selected_item(app->variable_item_list, 0); + variable_item_list_reset(app->variable_item_list); +} \ No newline at end of file diff --git a/applications/external/flipbip/scenes/flipbip_scene_startscreen.c b/applications/external/flipbip/scenes/flipbip_scene_startscreen.c new file mode 100644 index 0000000000..a9cb8ba5fc --- /dev/null +++ b/applications/external/flipbip/scenes/flipbip_scene_startscreen.c @@ -0,0 +1,55 @@ +#include "../flipbip.h" +#include "../helpers/flipbip_custom_event.h" +#include "../views/flipbip_startscreen.h" + +void flipbip_scene_startscreen_callback(FlipBipCustomEvent event, void* context) { + furi_assert(context); + FlipBip* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, event); +} + +void flipbip_scene_startscreen_on_enter(void* context) { + furi_assert(context); + FlipBip* app = context; + flipbip_startscreen_set_callback( + app->flipbip_startscreen, flipbip_scene_startscreen_callback, app); + view_dispatcher_switch_to_view(app->view_dispatcher, FlipBipViewIdStartscreen); +} + +bool flipbip_scene_startscreen_on_event(void* context, SceneManagerEvent event) { + FlipBip* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + switch(event.event) { + case FlipBipCustomEventStartscreenLeft: + case FlipBipCustomEventStartscreenRight: + break; + case FlipBipCustomEventStartscreenUp: + case FlipBipCustomEventStartscreenDown: + break; + case FlipBipCustomEventStartscreenOk: + scene_manager_next_scene(app->scene_manager, FlipBipSceneMenu); + consumed = true; + break; + case FlipBipCustomEventStartscreenBack: + notification_message(app->notification, &sequence_reset_red); + notification_message(app->notification, &sequence_reset_green); + notification_message(app->notification, &sequence_reset_blue); + if(!scene_manager_search_and_switch_to_previous_scene( + app->scene_manager, FlipBipSceneStartscreen)) { + scene_manager_stop(app->scene_manager); + view_dispatcher_stop(app->view_dispatcher); + } + consumed = true; + break; + } + } + + return consumed; +} + +void flipbip_scene_startscreen_on_exit(void* context) { + FlipBip* app = context; + UNUSED(app); +} \ No newline at end of file diff --git a/applications/external/flipbip/views/flipbip_scene_1.c b/applications/external/flipbip/views/flipbip_scene_1.c new file mode 100644 index 0000000000..3437a11497 --- /dev/null +++ b/applications/external/flipbip/views/flipbip_scene_1.c @@ -0,0 +1,724 @@ +#include "../flipbip.h" +#include +#include +#include +#include +//#include +#include +#include +#include "flipbip_icons.h" +#include "assets_icons.h" +#include "../helpers/flipbip_haptic.h" +#include "../helpers/flipbip_led.h" +#include "../helpers/flipbip_string.h" +#include "../helpers/flipbip_file.h" +// From: /lib/crypto +#include +#include +#include +#include +#include + +#define DERIV_PURPOSE 44 +#define DERIV_ACCOUNT 0 +#define DERIV_CHANGE 0 + +#define MAX_ADDR_LEN 42 + 1 // 42 = max length of address + null terminator +#define NUM_ADDRS 6 + +#define PAGE_LOADING 0 +#define PAGE_INFO 1 +#define PAGE_MNEMONIC 2 +#define PAGE_SEED 3 +#define PAGE_XPRV_ROOT 4 +#define PAGE_XPRV_ACCT 5 +#define PAGE_XPUB_ACCT 6 +#define PAGE_XPRV_EXTD 7 +#define PAGE_XPUB_EXTD 8 +#define PAGE_ADDR_BEGIN 9 +#define PAGE_ADDR_END (PAGE_ADDR_BEGIN + NUM_ADDRS - 1) + +#define TEXT_LOADING "Loading..." +#define TEXT_NEW_WALLET "New wallet" +#define TEXT_DEFAULT_COIN "Coin" +#define TEXT_RECEIVE_ADDRESS "receive address:" +#define TEXT_DEFAULT_DERIV "m/44'/X'/0'/0" +const char* TEXT_INFO = "-Scroll pages with up/down-" + "p1,2) Mnemonic/Seed " + "p3) xprv Root Key " + "p4,5) xprv/xpub Accnt Keys" + "p6,7) xprv/xpub Extnd Keys" + "p8+) Receive Addresses "; + +// #define TEXT_SAVE_QR "Save QR" +#define TEXT_QRFILE_EXT ".qrcode" // 7 chars + 1 null + +// bip44_coin, xprv_version, xpub_version, addr_version, wif_version, addr_format +const uint32_t COIN_INFO_ARRAY[3][6] = { + {COIN_BTC, 0x0488ade4, 0x0488b21e, 0x00, 0x80, FlipBipCoinBTC0}, + {COIN_ETH, 0x0488ade4, 0x0488b21e, 0x00, 0x80, FlipBipCoinETH60}, + {COIN_DOGE, 0x02fac398, 0x02facafd, 0x1e, 0x9e, FlipBipCoinBTC0}}; + +// coin_name, derivation_path +const char* COIN_TEXT_ARRAY[3][3] = { + {"BTC", "m/44'/0'/0'/0", "bitcoin:"}, + {"ETH", "m/44'/60'/0'/0", "ethereum:"}, + {"DOGE", "m/44'/3'/0'/0", "dogecoin:"}}; + +struct FlipBipScene1 { + View* view; + FlipBipScene1Callback callback; + void* context; +}; +typedef struct { + int page; + int strength; + uint32_t coin; + bool overwrite; + bool mnemonic_only; + CONFIDENTIAL const char* mnemonic; + CONFIDENTIAL uint8_t seed[64]; + CONFIDENTIAL const HDNode* node; + CONFIDENTIAL const char* xprv_root; + CONFIDENTIAL const char* xprv_account; + CONFIDENTIAL const char* xpub_account; + CONFIDENTIAL const char* xprv_extended; + CONFIDENTIAL const char* xpub_extended; + char* recv_addresses[NUM_ADDRS]; +} FlipBipScene1Model; + +// Node for the receive address +static CONFIDENTIAL HDNode* s_addr_node = NULL; +// Generic display text +static CONFIDENTIAL char* s_disp_text1 = NULL; +static CONFIDENTIAL char* s_disp_text2 = NULL; +static CONFIDENTIAL char* s_disp_text3 = NULL; +static CONFIDENTIAL char* s_disp_text4 = NULL; +static CONFIDENTIAL char* s_disp_text5 = NULL; +static CONFIDENTIAL char* s_disp_text6 = NULL; +// Derivation path text +static const char* s_derivation_text = TEXT_DEFAULT_DERIV; +//static bool s_busy = false; + +void flipbip_scene_1_set_callback( + FlipBipScene1* instance, + FlipBipScene1Callback callback, + void* context) { + furi_assert(instance); + furi_assert(callback); + instance->callback = callback; + instance->context = context; +} + +static void flipbip_scene_1_init_address( + char* addr_text, + const HDNode* node, + uint32_t coin_type, + uint32_t addr_index) { + //s_busy = true; + + // Buffer for address serialization + const size_t buflen = 40; + char buf[40 + 1] = {0}; + + // Use static node for address generation + memcpy(s_addr_node, node, sizeof(HDNode)); + memzero(addr_text, MAX_ADDR_LEN); + + hdnode_private_ckd(s_addr_node, addr_index); + hdnode_fill_public_key(s_addr_node); + + // coin info + // bip44_coin, xprv_version, xpub_version, addr_version, wif_version, addr_format + uint32_t coin_info[6] = {0}; + for(size_t i = 0; i < 6; i++) { + coin_info[i] = COIN_INFO_ARRAY[coin_type][i]; + } + + if(coin_info[5] == FlipBipCoinBTC0) { // BTC / DOGE style address + // BTC / DOGE style address + ecdsa_get_address( + s_addr_node->public_key, coin_info[3], HASHER_SHA2_RIPEMD, HASHER_SHA2D, buf, buflen); + strcpy(addr_text, buf); + + //ecdsa_get_wif(addr_node->private_key, WIF_VERSION, HASHER_SHA2D, buf, buflen); + + } else if(coin_info[5] == FlipBipCoinETH60) { // ETH + // ETH style address + hdnode_get_ethereum_pubkeyhash(s_addr_node, (uint8_t*)buf); + addr_text[0] = '0'; + addr_text[1] = 'x'; + // Convert the hash to a hex string + flipbip_btox((uint8_t*)buf, 20, addr_text + 2); + } + + // Clear the address node + memzero(s_addr_node, sizeof(HDNode)); + + //s_busy = false; +} + +static void flipbip_scene_1_draw_generic(const char* text, size_t line_len) { + // Split the text into parts + for(size_t si = 1; si <= 6; si++) { + char* ptr = NULL; + + if(si == 1) + ptr = s_disp_text1; + else if(si == 2) + ptr = s_disp_text2; + else if(si == 3) + ptr = s_disp_text3; + else if(si == 4) + ptr = s_disp_text4; + else if(si == 5) + ptr = s_disp_text5; + else if(si == 6) + ptr = s_disp_text6; + + memzero(ptr, 30 + 1); + if(line_len > 30) { + strncpy(ptr, text + ((si - 1) * 30), 30); + } else { + strncpy(ptr, text + ((si - 1) * line_len), line_len); + } + } +} + +static void flipbip_scene_1_draw_mnemonic(const char* mnemonic) { + // Delineate sections of the mnemonic every 4 words + const size_t mnemonic_working_len = strlen(mnemonic) + 1; + char* mnemonic_working = malloc(mnemonic_working_len); + strcpy(mnemonic_working, mnemonic); + int word = 0; + for(size_t i = 0; i < strlen(mnemonic_working); i++) { + if(mnemonic_working[i] == ' ') { + word++; + if(word % 4 == 0) { + mnemonic_working[i] = ','; + } + } + } + + // Split the mnemonic into parts + char* mnemonic_part = flipbip_strtok(mnemonic_working, ","); + int mi = 0; + while(mnemonic_part != NULL) { + char* ptr = NULL; + mi++; + + if(mi == 1) + ptr = s_disp_text1; + else if(mi == 2) + ptr = s_disp_text2; + else if(mi == 3) + ptr = s_disp_text3; + else if(mi == 4) + ptr = s_disp_text4; + else if(mi == 5) + ptr = s_disp_text5; + else if(mi == 6) + ptr = s_disp_text6; + + memzero(ptr, 30 + 1); + if(strlen(mnemonic_part) > 30) { + strncpy(ptr, mnemonic_part, 30); + } else { + strncpy(ptr, mnemonic_part, strlen(mnemonic_part)); + } + + mnemonic_part = flipbip_strtok(NULL, ","); + } + + // Free the working mnemonic memory + memzero(mnemonic_working, mnemonic_working_len); + free(mnemonic_working); +} + +static void flipbip_scene_1_draw_seed(FlipBipScene1Model* const model) { + const size_t seed_working_len = 64 * 2 + 1; + char* seed_working = malloc(seed_working_len); + // Convert the seed to a hex string + flipbip_btox(model->seed, 64, seed_working); + + flipbip_scene_1_draw_generic(seed_working, 22); + + // Free the working seed memory + memzero(seed_working, seed_working_len); + free(seed_working); +} + +static void flipbip_scene_1_clear_text() { + memzero((void*)s_disp_text1, 30 + 1); + memzero((void*)s_disp_text2, 30 + 1); + memzero((void*)s_disp_text3, 30 + 1); + memzero((void*)s_disp_text4, 30 + 1); + memzero((void*)s_disp_text5, 30 + 1); + memzero((void*)s_disp_text6, 30 + 1); +} + +void flipbip_scene_1_draw(Canvas* canvas, FlipBipScene1Model* model) { + //UNUSED(model); + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + + flipbip_scene_1_clear_text(); + if(model->page == PAGE_INFO) { + flipbip_scene_1_draw_generic(TEXT_INFO, 27); + } else if(model->page == PAGE_MNEMONIC) { + flipbip_scene_1_draw_mnemonic(model->mnemonic); + } else if(model->page == PAGE_SEED) { + flipbip_scene_1_draw_seed(model); + } else if(model->page == PAGE_XPRV_ROOT) { + flipbip_scene_1_draw_generic(model->xprv_root, 20); + } else if(model->page == PAGE_XPRV_ACCT) { + flipbip_scene_1_draw_generic(model->xprv_account, 20); + } else if(model->page == PAGE_XPUB_ACCT) { + flipbip_scene_1_draw_generic(model->xpub_account, 20); + } else if(model->page == PAGE_XPRV_EXTD) { + flipbip_scene_1_draw_generic(model->xprv_extended, 20); + } else if(model->page == PAGE_XPUB_EXTD) { + flipbip_scene_1_draw_generic(model->xpub_extended, 20); + } else if(model->page >= PAGE_ADDR_BEGIN && model->page <= PAGE_ADDR_END) { + uint32_t line_len = 12; + if(model->coin == FlipBipCoinETH60) { + line_len = 14; + } + flipbip_scene_1_draw_generic( + model->recv_addresses[model->page - PAGE_ADDR_BEGIN], line_len); + } + + if(model->page == PAGE_LOADING) { + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 2, 10, TEXT_LOADING); + canvas_draw_str(canvas, 7, 30, s_derivation_text); + canvas_draw_icon(canvas, 86, 25, &I_Keychain_39x36); + } else if(model->page >= PAGE_ADDR_BEGIN && model->page <= PAGE_ADDR_END) { + // draw address header + canvas_set_font(canvas, FontSecondary); + // coin_name, derivation_path + const char* receive_text = COIN_TEXT_ARRAY[model->coin][0]; + if(receive_text == NULL) { + receive_text = TEXT_DEFAULT_COIN; + } + const size_t receive_len = strlen(receive_text) * 7; + canvas_draw_str_aligned(canvas, 2, 2, AlignLeft, AlignTop, receive_text); + canvas_draw_str_aligned( + canvas, receive_len + 1, 2, AlignLeft, AlignTop, TEXT_RECEIVE_ADDRESS); + + // draw address number + const unsigned char addr_num[1] = {(unsigned char)(model->page - PAGE_ADDR_BEGIN)}; + char addr_num_text[3] = {0}; + flipbip_btox(addr_num, 1, addr_num_text); + addr_num_text[0] = '/'; + canvas_draw_str_aligned(canvas, 125, 2, AlignRight, AlignTop, addr_num_text); + + // draw QR code file path + char addr_name_text[14] = {0}; + strcpy(addr_name_text, COIN_TEXT_ARRAY[model->coin][0]); + flipbip_btox(addr_num, 1, addr_name_text + strlen(addr_name_text)); + strcpy(addr_name_text + strlen(addr_name_text), TEXT_QRFILE_EXT); + //elements_button_right(canvas, addr_name_text); + canvas_draw_str_aligned(canvas, 125, 53, AlignRight, AlignTop, addr_name_text); + + // draw address + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 7, 22, s_disp_text1); + canvas_draw_str(canvas, 7, 34, s_disp_text2); + canvas_draw_str(canvas, 7, 46, s_disp_text3); + canvas_draw_str(canvas, 7, 58, s_disp_text4); + } else { + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned(canvas, 1, 2, AlignLeft, AlignTop, s_disp_text1); + canvas_draw_str_aligned(canvas, 1, 12, AlignLeft, AlignTop, s_disp_text2); + canvas_draw_str_aligned(canvas, 1, 22, AlignLeft, AlignTop, s_disp_text3); + canvas_draw_str_aligned(canvas, 1, 32, AlignLeft, AlignTop, s_disp_text4); + canvas_draw_str_aligned(canvas, 1, 42, AlignLeft, AlignTop, s_disp_text5); + canvas_draw_str_aligned(canvas, 1, 52, AlignLeft, AlignTop, s_disp_text6); + } +} + +static int flipbip_scene_1_model_init( + FlipBipScene1Model* const model, + const int strength, + const uint32_t coin, + const bool overwrite, + const char* passphrase_text) { + model->page = PAGE_LOADING; + model->mnemonic_only = false; + model->strength = strength; + model->coin = coin; + model->overwrite = overwrite; + + // Allocate memory for mnemonic + char* mnemonic = malloc(TEXT_BUFFER_SIZE); + memzero(mnemonic, TEXT_BUFFER_SIZE); + + // Check if the mnemonic key & data is already saved in persistent storage, or overwrite is true + if(overwrite || (!flipbip_has_file(FlipBipFileKey, NULL, false) && + !flipbip_has_file(FlipBipFileDat, NULL, false))) { + // Set mnemonic only mode + model->mnemonic_only = true; + // Generate a random mnemonic using trezor-crypto + const char* mnemonic_gen = mnemonic_generate(strength); + // Check if the mnemonic is valid + if(mnemonic_check(mnemonic_gen) == 0) + return FlipBipStatusMnemonicCheckError; // 13 = mnemonic check error + // Save the mnemonic to persistent storage + else if(!flipbip_save_file_secure(mnemonic_gen)) + return FlipBipStatusSaveError; // 12 = save error + // Clear the generated mnemonic from memory + mnemonic_clear(); + } + + // Load the mnemonic from persistent storage + if(!flipbip_load_file_secure(mnemonic)) { + // Set mnemonic only mode for this error for memory cleanup purposes + model->mnemonic_only = true; + return FlipBipStatusLoadError; // 11 = load error + } + model->mnemonic = mnemonic; + // Check if the mnemonic is valid + if(mnemonic_check(model->mnemonic) == 0) { + // Set mnemonic only mode for this error for memory cleanup purposes + model->mnemonic_only = true; + return FlipBipStatusMnemonicCheckError; // 13 = mnemonic check error + } + + // test return values + //model->mnemonic_only = true; + //return FlipBipStatusMnemonicCheckError; // 13 = mnemonic check error + + // if we are only generating the mnemonic, return + if(model->mnemonic_only) { + return FlipBipStatusReturn; // 10 = mnemonic only, return from parent + } + + // Generate a BIP39 seed from the mnemonic + mnemonic_to_seed(model->mnemonic, passphrase_text, model->seed, 0); + + // Generate a BIP32 root HD node from the mnemonic + HDNode* root = malloc(sizeof(HDNode)); + hdnode_from_seed(model->seed, 64, SECP256K1_NAME, root); + + // buffer for key serialization + const size_t buflen = 128; + char buf[128 + 1] = {0}; + + // coin info + // bip44_coin, xprv_version, xpub_version, addr_version, wif_version, addr_format + uint32_t coin_info[6] = {0}; + for(size_t i = 0; i < 6; i++) { + coin_info[i] = COIN_INFO_ARRAY[coin][i]; + } + + // root + uint32_t fingerprint = 0; + hdnode_serialize_private(root, fingerprint, coin_info[1], buf, buflen); + char* xprv_root = malloc(buflen + 1); + strncpy(xprv_root, buf, buflen); + model->xprv_root = xprv_root; + + HDNode* node = root; + + // purpose m/44' + fingerprint = hdnode_fingerprint(node); + hdnode_private_ckd_prime(node, DERIV_PURPOSE); // purpose + + // coin m/44'/0' or m/44'/60' + fingerprint = hdnode_fingerprint(node); + hdnode_private_ckd_prime(node, coin_info[0]); // coin + + // account m/44'/0'/0' or m/44'/60'/0' + fingerprint = hdnode_fingerprint(node); + hdnode_private_ckd_prime(node, DERIV_ACCOUNT); // account + + hdnode_serialize_private(node, fingerprint, coin_info[1], buf, buflen); + char* xprv_acc = malloc(buflen + 1); + strncpy(xprv_acc, buf, buflen); + model->xprv_account = xprv_acc; + + hdnode_serialize_public(node, fingerprint, coin_info[2], buf, buflen); + char* xpub_acc = malloc(buflen + 1); + strncpy(xpub_acc, buf, buflen); + model->xpub_account = xpub_acc; + + // external/internal (change) m/44'/0'/0'/0 or m/44'/60'/0'/0 + fingerprint = hdnode_fingerprint(node); + hdnode_private_ckd(node, DERIV_CHANGE); // external/internal (change) + + hdnode_serialize_private(node, fingerprint, coin_info[1], buf, buflen); + char* xprv_ext = malloc(buflen + 1); + strncpy(xprv_ext, buf, buflen); + model->xprv_extended = xprv_ext; + + hdnode_serialize_public(node, fingerprint, coin_info[2], buf, buflen); + char* xpub_ext = malloc(buflen + 1); + strncpy(xpub_ext, buf, buflen); + model->xpub_extended = xpub_ext; + + model->node = node; + + // Initialize addresses + for(uint8_t a = 0; a < NUM_ADDRS; a++) { + model->recv_addresses[a] = malloc(MAX_ADDR_LEN); + memzero(model->recv_addresses[a], MAX_ADDR_LEN); + flipbip_scene_1_init_address(model->recv_addresses[a], node, coin, a); + + // Save QR code file + memzero(buf, buflen); + strcpy(buf, COIN_TEXT_ARRAY[coin][0]); + const unsigned char addr_num[1] = {a}; + flipbip_btox(addr_num, 1, buf + strlen(buf)); + strcpy(buf + strlen(buf), TEXT_QRFILE_EXT); + flipbip_save_qrfile(COIN_TEXT_ARRAY[coin][2], model->recv_addresses[a], buf); + memzero(buf, buflen); + } + + model->page = PAGE_INFO; + +#if USE_BIP39_CACHE + // Clear the BIP39 cache + bip39_cache_clear(); +#endif + + // 0 = success + return FlipBipStatusSuccess; +} + +bool flipbip_scene_1_input(InputEvent* event, void* context) { + furi_assert(context); + FlipBipScene1* instance = context; + + // Ignore input if busy + // if(s_busy) { + // return false; + // } + + if(event->type == InputTypeRelease) { + switch(event->key) { + case InputKeyBack: + with_view_model( + instance->view, + FlipBipScene1Model * model, + { + UNUSED(model); + instance->callback(FlipBipCustomEventScene1Back, instance->context); + }, + true); + break; + case InputKeyRight: + case InputKeyDown: + with_view_model( + instance->view, + FlipBipScene1Model * model, + { + //UNUSED(model); + int page = (model->page + 1) % (PAGE_ADDR_END + 1); + if(page == 0) { + page = PAGE_INFO; + } + model->page = page; + }, + true); + break; + case InputKeyLeft: + case InputKeyUp: + with_view_model( + instance->view, + FlipBipScene1Model * model, + { + //UNUSED(model); + int page = (model->page - 1) % (PAGE_ADDR_END + 1); + if(page == 0) { + page = PAGE_ADDR_END; + } + model->page = page; + }, + true); + break; + // case InputKeyRight: + case InputKeyOk: + // with_view_model( + // instance->view, + // FlipBipScene1Model * model, + // { + // if(model->page >= PAGE_ADDR_BEGIN && model->page <= PAGE_ADDR_END) { + + // } + // }, + // true); + // break; + // case InputKeyLeft: + case InputKeyMAX: + break; + } + } + return true; +} + +void flipbip_scene_1_exit(void* context) { + furi_assert(context); + FlipBipScene1* instance = (FlipBipScene1*)context; + + with_view_model( + instance->view, + FlipBipScene1Model * model, + { + model->page = PAGE_LOADING; + model->strength = FlipBipStrength256; + model->coin = FlipBipCoinBTC0; + memzero(model->seed, 64); + // if mnemonic_only is true, then we don't need to free the data here + if(!model->mnemonic_only) { + memzero((void*)model->mnemonic, strlen(model->mnemonic)); + free((void*)model->mnemonic); + memzero((void*)model->node, sizeof(HDNode)); + free((void*)model->node); + memzero((void*)model->xprv_root, strlen(model->xprv_root)); + memzero((void*)model->xprv_account, strlen(model->xprv_account)); + memzero((void*)model->xpub_account, strlen(model->xpub_account)); + memzero((void*)model->xprv_extended, strlen(model->xprv_extended)); + memzero((void*)model->xpub_extended, strlen(model->xpub_extended)); + free((void*)model->xprv_root); + free((void*)model->xprv_account); + free((void*)model->xpub_account); + free((void*)model->xprv_extended); + free((void*)model->xpub_extended); + for(int a = 0; a < NUM_ADDRS; a++) { + memzero((void*)model->recv_addresses[a], MAX_ADDR_LEN); + free((void*)model->recv_addresses[a]); + } + } + }, + true); + + flipbip_scene_1_clear_text(); +} + +void flipbip_scene_1_enter(void* context) { + furi_assert(context); + FlipBipScene1* instance = (FlipBipScene1*)context; + + FlipBip* app = instance->context; + + // BIP39 Strength setting + int strength = 256; // FlipBipStrength256 // 24 words (256 bit) + if(app->bip39_strength == FlipBipStrength128) { + strength = 128; // 12 words (128 bit) + } else if(app->bip39_strength == FlipBipStrength192) { + strength = 192; // 18 words (192 bit) + } + + // BIP39 Passphrase setting + const char* passphrase_text = ""; + if(app->passphrase == FlipBipPassphraseOn && strlen(app->passphrase_text) > 0) { + passphrase_text = app->passphrase_text; + } + + // BIP44 Coin setting + const uint32_t coin = app->bip44_coin; + // coin_name, derivation_path + s_derivation_text = COIN_TEXT_ARRAY[coin][1]; + + // Overwrite the saved seed with a new one setting + bool overwrite = app->overwrite_saved_seed != 0; + if(overwrite) { + s_derivation_text = TEXT_NEW_WALLET; + } + + flipbip_play_happy_bump(app); + //notification_message(app->notification, &sequence_blink_cyan_100); + flipbip_led_set_rgb(app, 255, 0, 0); + + with_view_model( + instance->view, + FlipBipScene1Model * model, + { + // s_busy = true; + + const int status = + flipbip_scene_1_model_init(model, strength, coin, overwrite, passphrase_text); + + // nonzero status, free the mnemonic + if(status != FlipBipStatusSuccess) { + memzero((void*)model->mnemonic, strlen(model->mnemonic)); + free((void*)model->mnemonic); + } + + // if error, set the error message + if(status == FlipBipStatusSaveError) { + model->mnemonic = "ERROR:,Save error"; + model->page = PAGE_MNEMONIC; + flipbip_play_long_bump(app); + } else if(status == FlipBipStatusLoadError) { + model->mnemonic = "ERROR:,Load error"; + model->page = PAGE_MNEMONIC; + flipbip_play_long_bump(app); + } else if(status == FlipBipStatusMnemonicCheckError) { + model->mnemonic = "ERROR:,Mnemonic check error"; + model->page = PAGE_MNEMONIC; + flipbip_play_long_bump(app); + } + + // s_busy = false; + + // if overwrite is set and mnemonic generated, return from scene immediately + if(status == FlipBipStatusReturn) { + instance->callback(FlipBipCustomEventScene1Back, instance->context); + } + }, + true); +} + +FlipBipScene1* flipbip_scene_1_alloc() { + FlipBipScene1* instance = malloc(sizeof(FlipBipScene1)); + instance->view = view_alloc(); + view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(FlipBipScene1Model)); + view_set_context(instance->view, instance); // furi_assert crashes in events without this + view_set_draw_callback(instance->view, (ViewDrawCallback)flipbip_scene_1_draw); + view_set_input_callback(instance->view, flipbip_scene_1_input); + view_set_enter_callback(instance->view, flipbip_scene_1_enter); + view_set_exit_callback(instance->view, flipbip_scene_1_exit); + + // allocate the address node + s_addr_node = (HDNode*)malloc(sizeof(HDNode)); + + // allocate the display text + s_disp_text1 = (char*)malloc(30 + 1); + s_disp_text2 = (char*)malloc(30 + 1); + s_disp_text3 = (char*)malloc(30 + 1); + s_disp_text4 = (char*)malloc(30 + 1); + s_disp_text5 = (char*)malloc(30 + 1); + s_disp_text6 = (char*)malloc(30 + 1); + + return instance; +} + +void flipbip_scene_1_free(FlipBipScene1* instance) { + furi_assert(instance); + + with_view_model( + instance->view, FlipBipScene1Model * model, { UNUSED(model); }, true); + + // free the address node + memzero(s_addr_node, sizeof(HDNode)); + free(s_addr_node); + + // free the display text + flipbip_scene_1_clear_text(); + free(s_disp_text1); + free(s_disp_text2); + free(s_disp_text3); + free(s_disp_text4); + free(s_disp_text5); + free(s_disp_text6); + + view_free(instance->view); + free(instance); +} + +View* flipbip_scene_1_get_view(FlipBipScene1* instance) { + furi_assert(instance); + return instance->view; +} diff --git a/applications/external/flipbip/views/flipbip_scene_1.h b/applications/external/flipbip/views/flipbip_scene_1.h new file mode 100644 index 0000000000..6e370633ee --- /dev/null +++ b/applications/external/flipbip/views/flipbip_scene_1.h @@ -0,0 +1,19 @@ +#pragma once + +#include +#include "../helpers/flipbip_custom_event.h" + +typedef struct FlipBipScene1 FlipBipScene1; + +typedef void (*FlipBipScene1Callback)(FlipBipCustomEvent event, void* context); + +void flipbip_scene_1_set_callback( + FlipBipScene1* flipbip_scene_1, + FlipBipScene1Callback callback, + void* context); + +View* flipbip_scene_1_get_view(FlipBipScene1* flipbip_static); + +FlipBipScene1* flipbip_scene_1_alloc(); + +void flipbip_scene_1_free(FlipBipScene1* flipbip_static); \ No newline at end of file diff --git a/applications/external/flipbip/views/flipbip_startscreen.c b/applications/external/flipbip/views/flipbip_startscreen.c new file mode 100644 index 0000000000..f2b69c0842 --- /dev/null +++ b/applications/external/flipbip/views/flipbip_startscreen.c @@ -0,0 +1,131 @@ +#include "../flipbip.h" +#include +#include +#include +#include +#include "flipbip_icons.h" +#include "assets_icons.h" + +struct FlipBipStartscreen { + View* view; + FlipBipStartscreenCallback callback; + void* context; +}; + +typedef struct { + int some_value; +} FlipBipStartscreenModel; + +void flipbip_startscreen_set_callback( + FlipBipStartscreen* instance, + FlipBipStartscreenCallback callback, + void* context) { + furi_assert(instance); + furi_assert(callback); + instance->callback = callback; + instance->context = context; +} + +void flipbip_startscreen_draw(Canvas* canvas, FlipBipStartscreenModel* model) { + UNUSED(model); + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + + canvas_draw_icon(canvas, 1, 33, &I_Auth_62x31); + + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 18, 11, "FlipBIP - BIP32/39/44"); + + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 23, 22, "Crypto toolkit for Flipper"); + canvas_draw_str(canvas, 99, 34, FLIPBIP_VERSION); + + elements_button_right(canvas, "Start"); +} + +static void flipbip_startscreen_model_init(FlipBipStartscreenModel* const model) { + model->some_value = 1; +} + +bool flipbip_startscreen_input(InputEvent* event, void* context) { + furi_assert(context); + FlipBipStartscreen* instance = context; + if(event->type == InputTypeRelease) { + switch(event->key) { + case InputKeyBack: + with_view_model( + instance->view, + FlipBipStartscreenModel * model, + { + UNUSED(model); + instance->callback(FlipBipCustomEventStartscreenBack, instance->context); + }, + true); + break; + case InputKeyLeft: + case InputKeyRight: + case InputKeyUp: + case InputKeyDown: + case InputKeyOk: + with_view_model( + instance->view, + FlipBipStartscreenModel * model, + { + UNUSED(model); + instance->callback(FlipBipCustomEventStartscreenOk, instance->context); + }, + true); + break; + case InputKeyMAX: + break; + } + } + return true; +} + +void flipbip_startscreen_exit(void* context) { + furi_assert(context); +} + +void flipbip_startscreen_enter(void* context) { + furi_assert(context); + FlipBipStartscreen* instance = (FlipBipStartscreen*)context; + with_view_model( + instance->view, + FlipBipStartscreenModel * model, + { flipbip_startscreen_model_init(model); }, + true); +} + +FlipBipStartscreen* flipbip_startscreen_alloc() { + FlipBipStartscreen* instance = malloc(sizeof(FlipBipStartscreen)); + instance->view = view_alloc(); + view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(FlipBipStartscreenModel)); + view_set_context(instance->view, instance); // furi_assert crashes in events without this + view_set_draw_callback(instance->view, (ViewDrawCallback)flipbip_startscreen_draw); + view_set_input_callback(instance->view, flipbip_startscreen_input); + //view_set_enter_callback(instance->view, flipbip_startscreen_enter); + //view_set_exit_callback(instance->view, flipbip_startscreen_exit); + + with_view_model( + instance->view, + FlipBipStartscreenModel * model, + { flipbip_startscreen_model_init(model); }, + true); + + return instance; +} + +void flipbip_startscreen_free(FlipBipStartscreen* instance) { + furi_assert(instance); + + with_view_model( + instance->view, FlipBipStartscreenModel * model, { UNUSED(model); }, true); + view_free(instance->view); + free(instance); +} + +View* flipbip_startscreen_get_view(FlipBipStartscreen* instance) { + furi_assert(instance); + return instance->view; +} diff --git a/applications/external/flipbip/views/flipbip_startscreen.h b/applications/external/flipbip/views/flipbip_startscreen.h new file mode 100644 index 0000000000..d6eb1fad8b --- /dev/null +++ b/applications/external/flipbip/views/flipbip_startscreen.h @@ -0,0 +1,19 @@ +#pragma once + +#include +#include "../helpers/flipbip_custom_event.h" + +typedef struct FlipBipStartscreen FlipBipStartscreen; + +typedef void (*FlipBipStartscreenCallback)(FlipBipCustomEvent event, void* context); + +void flipbip_startscreen_set_callback( + FlipBipStartscreen* flipbip_startscreen, + FlipBipStartscreenCallback callback, + void* context); + +View* flipbip_startscreen_get_view(FlipBipStartscreen* flipbip_static); + +FlipBipStartscreen* flipbip_startscreen_alloc(); + +void flipbip_startscreen_free(FlipBipStartscreen* flipbip_static); \ No newline at end of file diff --git a/applications/external/flipfrid/LICENSE.md b/applications/external/flipfrid/LICENSE.md deleted file mode 100644 index a856581c9f..0000000000 --- a/applications/external/flipfrid/LICENSE.md +++ /dev/null @@ -1,8 +0,0 @@ -/* - * ---------------------------------------------------------------------------- - * "THE BEER-WARE LICENSE" (Revision 42): - * @G4N4P4T1 wrote this file. As long as you retain this notice you - * can do whatever you want with this stuff. If we meet some day, and you think - * this stuff is worth it, you can buy me a beer in return. - * ---------------------------------------------------------------------------- - */ \ No newline at end of file diff --git a/applications/external/flipfrid/application.fam b/applications/external/flipfrid/application.fam deleted file mode 100644 index e716017356..0000000000 --- a/applications/external/flipfrid/application.fam +++ /dev/null @@ -1,12 +0,0 @@ -App( - appid="rfid_fuzzer", - name="RFID Fuzzer", - apptype=FlipperAppType.EXTERNAL, - entry_point="flipfrid_start", - requires=["gui", "storage", "dialogs", "input", "notification"], - stack_size=2 * 1024, - order=180, - fap_icon="rfid_10px.png", - fap_category="Tools", - fap_icon_assets="images", -) diff --git a/applications/external/flipfrid/flipfrid.c b/applications/external/flipfrid/flipfrid.c deleted file mode 100644 index 172a98ef37..0000000000 --- a/applications/external/flipfrid/flipfrid.c +++ /dev/null @@ -1,288 +0,0 @@ -#include "flipfrid.h" - -#include "scene/flipfrid_scene_entrypoint.h" -#include "scene/flipfrid_scene_load_file.h" -#include "scene/flipfrid_scene_select_field.h" -#include "scene/flipfrid_scene_run_attack.h" -#include "scene/flipfrid_scene_load_custom_uids.h" -#include - -#define RFIDFUZZER_APP_FOLDER "/ext/lrfid/rfidfuzzer" - -static void flipfrid_draw_callback(Canvas* const canvas, void* ctx) { - furi_assert(ctx); - FlipFridState* flipfrid_state = ctx; - furi_mutex_acquire(flipfrid_state->mutex, FuriWaitForever); - - // Draw correct Canvas - switch(flipfrid_state->current_scene) { - case NoneScene: - case SceneEntryPoint: - flipfrid_scene_entrypoint_on_draw(canvas, flipfrid_state); - break; - case SceneSelectFile: - flipfrid_scene_load_file_on_draw(canvas, flipfrid_state); - break; - case SceneSelectField: - flipfrid_scene_select_field_on_draw(canvas, flipfrid_state); - break; - case SceneAttack: - flipfrid_scene_run_attack_on_draw(canvas, flipfrid_state); - break; - case SceneLoadCustomUids: - flipfrid_scene_load_custom_uids_on_draw(canvas, flipfrid_state); - break; - default: - break; - } - - furi_mutex_release(flipfrid_state->mutex); -} - -void flipfrid_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { - furi_assert(event_queue); - - FlipFridEvent event = { - .evt_type = EventTypeKey, .key = input_event->key, .input_type = input_event->type}; - furi_message_queue_put(event_queue, &event, 25); -} - -static void flipfrid_timer_callback(FuriMessageQueue* event_queue) { - furi_assert(event_queue); - FlipFridEvent event = { - .evt_type = EventTypeTick, .key = InputKeyUp, .input_type = InputTypeRelease}; - furi_message_queue_put(event_queue, &event, 25); -} - -FlipFridState* flipfrid_alloc() { - FlipFridState* flipfrid = malloc(sizeof(FlipFridState)); - flipfrid->notification_msg = furi_string_alloc(); - flipfrid->attack_name = furi_string_alloc(); - flipfrid->proto_name = furi_string_alloc(); - flipfrid->data_str = furi_string_alloc(); - - flipfrid->main_menu_items[0] = furi_string_alloc_set("Default Values"); - flipfrid->main_menu_items[1] = furi_string_alloc_set("BF Customer ID"); - flipfrid->main_menu_items[2] = furi_string_alloc_set("Load File"); - flipfrid->main_menu_items[3] = furi_string_alloc_set("Load UIDs from file"); - - flipfrid->main_menu_proto_items[0] = furi_string_alloc_set("EM4100"); - flipfrid->main_menu_proto_items[1] = furi_string_alloc_set("HIDProx"); - flipfrid->main_menu_proto_items[2] = furi_string_alloc_set("PAC/Stanley"); - flipfrid->main_menu_proto_items[3] = furi_string_alloc_set("H10301"); - - flipfrid->previous_scene = NoneScene; - flipfrid->current_scene = SceneEntryPoint; - flipfrid->is_running = true; - flipfrid->is_attacking = false; - flipfrid->key_index = 0; - flipfrid->menu_index = 0; - flipfrid->menu_proto_index = 0; - - flipfrid->attack = FlipFridAttackDefaultValues; - flipfrid->notify = furi_record_open(RECORD_NOTIFICATION); - - flipfrid->data[0] = 0x00; - flipfrid->data[1] = 0x00; - flipfrid->data[2] = 0x00; - flipfrid->data[3] = 0x00; - flipfrid->data[4] = 0x00; - flipfrid->data[5] = 0x00; - - flipfrid->payload[0] = 0x00; - flipfrid->payload[1] = 0x00; - flipfrid->payload[2] = 0x00; - flipfrid->payload[3] = 0x00; - flipfrid->payload[4] = 0x00; - flipfrid->payload[5] = 0x00; - - //Dialog - flipfrid->dialogs = furi_record_open(RECORD_DIALOGS); - - return flipfrid; -} - -void flipfrid_free(FlipFridState* flipfrid) { - //Dialog - furi_record_close(RECORD_DIALOGS); - notification_message(flipfrid->notify, &sequence_blink_stop); - - // Strings - furi_string_free(flipfrid->notification_msg); - furi_string_free(flipfrid->attack_name); - furi_string_free(flipfrid->proto_name); - furi_string_free(flipfrid->data_str); - - for(uint32_t i = 0; i < 4; i++) { - furi_string_free(flipfrid->main_menu_items[i]); - } - - for(uint32_t i = 0; i < 4; i++) { - furi_string_free(flipfrid->main_menu_proto_items[i]); - } - - // The rest - free(flipfrid); -} - -// ENTRYPOINT -int32_t flipfrid_start(void* p) { - UNUSED(p); - // Input - FURI_LOG_I(TAG, "Initializing input"); - FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(FlipFridEvent)); - FlipFridState* flipfrid_state = flipfrid_alloc(); - - DOLPHIN_DEED(DolphinDeedPluginStart); - flipfrid_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); - if(!flipfrid_state->mutex) { - FURI_LOG_E(TAG, "cannot create mutex\r\n"); - furi_message_queue_free(event_queue); - furi_record_close(RECORD_NOTIFICATION); - flipfrid_free(flipfrid_state); - return 255; - } - - Storage* storage = furi_record_open(RECORD_STORAGE); - if(!storage_simply_mkdir(storage, RFIDFUZZER_APP_FOLDER)) { - FURI_LOG_E(TAG, "Could not create folder %s", RFIDFUZZER_APP_FOLDER); - } - furi_record_close(RECORD_STORAGE); - - // Configure view port - FURI_LOG_I(TAG, "Initializing viewport"); - ViewPort* view_port = view_port_alloc(); - view_port_draw_callback_set(view_port, flipfrid_draw_callback, flipfrid_state); - view_port_input_callback_set(view_port, flipfrid_input_callback, event_queue); - - // Configure timer - FURI_LOG_I(TAG, "Initializing timer"); - FuriTimer* timer = - furi_timer_alloc(flipfrid_timer_callback, FuriTimerTypePeriodic, event_queue); - furi_timer_start(timer, furi_kernel_get_tick_frequency() / 10); // 10 times per second - - // Register view port in GUI - FURI_LOG_I(TAG, "Initializing gui"); - Gui* gui = (Gui*)furi_record_open(RECORD_GUI); - gui_add_view_port(gui, view_port, GuiLayerFullscreen); - - // Init values - FlipFridEvent event; - while(flipfrid_state->is_running) { - // Get next event - FuriStatus event_status = furi_message_queue_get(event_queue, &event, 25); - if(event_status == FuriStatusOk) { - if(event.evt_type == EventTypeKey) { - //Handle event key - switch(flipfrid_state->current_scene) { - case NoneScene: - case SceneEntryPoint: - flipfrid_scene_entrypoint_on_event(event, flipfrid_state); - break; - case SceneSelectFile: - flipfrid_scene_load_file_on_event(event, flipfrid_state); - break; - case SceneSelectField: - flipfrid_scene_select_field_on_event(event, flipfrid_state); - break; - case SceneAttack: - flipfrid_scene_run_attack_on_event(event, flipfrid_state); - break; - case SceneLoadCustomUids: - flipfrid_scene_load_custom_uids_on_event(event, flipfrid_state); - break; - default: - break; - } - - } else if(event.evt_type == EventTypeTick) { - //Handle event tick - if(flipfrid_state->current_scene != flipfrid_state->previous_scene) { - // Trigger Exit Scene - switch(flipfrid_state->previous_scene) { - case SceneEntryPoint: - flipfrid_scene_entrypoint_on_exit(flipfrid_state); - break; - case SceneSelectFile: - flipfrid_scene_load_file_on_exit(flipfrid_state); - break; - case SceneSelectField: - flipfrid_scene_select_field_on_exit(flipfrid_state); - break; - case SceneAttack: - flipfrid_scene_run_attack_on_exit(flipfrid_state); - break; - case SceneLoadCustomUids: - flipfrid_scene_load_custom_uids_on_exit(flipfrid_state); - break; - case NoneScene: - break; - default: - break; - } - - // Trigger Entry Scene - switch(flipfrid_state->current_scene) { - case NoneScene: - case SceneEntryPoint: - flipfrid_scene_entrypoint_on_enter(flipfrid_state); - break; - case SceneSelectFile: - flipfrid_scene_load_file_on_enter(flipfrid_state); - break; - case SceneSelectField: - flipfrid_scene_select_field_on_enter(flipfrid_state); - break; - case SceneAttack: - flipfrid_scene_run_attack_on_enter(flipfrid_state); - break; - case SceneLoadCustomUids: - flipfrid_scene_load_custom_uids_on_enter(flipfrid_state); - break; - default: - break; - } - flipfrid_state->previous_scene = flipfrid_state->current_scene; - } - - // Trigger Tick Scene - switch(flipfrid_state->current_scene) { - case NoneScene: - case SceneEntryPoint: - flipfrid_scene_entrypoint_on_tick(flipfrid_state); - break; - case SceneSelectFile: - flipfrid_scene_load_file_on_tick(flipfrid_state); - break; - case SceneSelectField: - flipfrid_scene_select_field_on_tick(flipfrid_state); - break; - case SceneAttack: - flipfrid_scene_run_attack_on_tick(flipfrid_state); - break; - case SceneLoadCustomUids: - flipfrid_scene_load_custom_uids_on_tick(flipfrid_state); - break; - default: - break; - } - view_port_update(view_port); - } - } - } - - // Cleanup - furi_timer_stop(timer); - furi_timer_free(timer); - - FURI_LOG_I(TAG, "Cleaning up"); - gui_remove_view_port(gui, view_port); - view_port_free(view_port); - furi_message_queue_free(event_queue); - furi_record_close(RECORD_GUI); - furi_record_close(RECORD_NOTIFICATION); - furi_mutex_free(flipfrid_state->mutex); - flipfrid_free(flipfrid_state); - - return 0; -} diff --git a/applications/external/flipfrid/flipfrid.h b/applications/external/flipfrid/flipfrid.h deleted file mode 100644 index b95f9f75f1..0000000000 --- a/applications/external/flipfrid/flipfrid.h +++ /dev/null @@ -1,94 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include - -#include -#include - -#define TAG "FlipFrid" - -typedef enum { - FlipFridAttackDefaultValues, - FlipFridAttackBfCustomerId, - FlipFridAttackLoadFile, - FlipFridAttackLoadFileCustomUids, -} FlipFridAttacks; - -typedef enum { - EM4100, - HIDProx, - PAC, - H10301, -} FlipFridProtos; - -typedef enum { - NoneScene, - SceneEntryPoint, - SceneSelectFile, - SceneSelectField, - SceneAttack, - SceneLoadCustomUids, -} FlipFridScene; - -typedef enum { - EventTypeTick, - EventTypeKey, -} EventType; - -typedef struct { - EventType evt_type; - InputKey key; - InputType input_type; -} FlipFridEvent; - -// STRUCTS -typedef struct { - FuriMutex* mutex; - bool is_running; - bool is_attacking; - FlipFridScene current_scene; - FlipFridScene previous_scene; - NotificationApp* notify; - u_int8_t menu_index; - u_int8_t menu_proto_index; - - FuriString* data_str; - uint8_t data[6]; - uint8_t payload[6]; - uint8_t attack_step; - FlipFridAttacks attack; - FlipFridProtos proto; - FuriString* attack_name; - FuriString* proto_name; - FuriString* main_menu_items[4]; - FuriString* main_menu_proto_items[4]; - - DialogsApp* dialogs; - FuriString* notification_msg; - uint8_t key_index; - LFRFIDWorker* worker; - ProtocolDict* dict; - ProtocolId protocol; - bool workr_rund; - bool attack_stop_called; - - uint8_t time_between_cards; - - // Used for custom dictionnary - Stream* uids_stream; -} FlipFridState; \ No newline at end of file diff --git a/applications/external/flipfrid/images/125_10px.png b/applications/external/flipfrid/images/125_10px.png deleted file mode 100644 index ce01284a2c..0000000000 Binary files a/applications/external/flipfrid/images/125_10px.png and /dev/null differ diff --git a/applications/external/flipfrid/scene/flipfrid_scene_entrypoint.c b/applications/external/flipfrid/scene/flipfrid_scene_entrypoint.c deleted file mode 100644 index f4b39aa66b..0000000000 --- a/applications/external/flipfrid/scene/flipfrid_scene_entrypoint.c +++ /dev/null @@ -1,201 +0,0 @@ -#include "flipfrid_scene_entrypoint.h" - -void flipfrid_scene_entrypoint_menu_callback( - FlipFridState* context, - uint32_t index, - uint32_t proto_index) { - switch(index) { - case FlipFridAttackDefaultValues: - context->attack = FlipFridAttackDefaultValues; - context->current_scene = SceneAttack; - furi_string_set(context->attack_name, "Default Values"); - break; - case FlipFridAttackBfCustomerId: - context->attack = FlipFridAttackBfCustomerId; - context->current_scene = SceneAttack; - furi_string_set(context->attack_name, "Bad Customer ID"); - break; - case FlipFridAttackLoadFile: - context->attack = FlipFridAttackLoadFile; - context->current_scene = SceneSelectFile; - furi_string_set(context->attack_name, "Load File"); - break; - case FlipFridAttackLoadFileCustomUids: - context->attack = FlipFridAttackLoadFileCustomUids; - context->current_scene = SceneLoadCustomUids; - furi_string_set(context->attack_name, "Load Custom UIDs"); - break; - default: - break; - } - - switch(proto_index) { - case EM4100: - context->proto = EM4100; - furi_string_set(context->proto_name, "EM4100"); - break; - case HIDProx: - context->proto = HIDProx; - furi_string_set(context->proto_name, "HIDProx"); - break; - case PAC: - context->proto = PAC; - furi_string_set(context->proto_name, "PAC/Stanley"); - break; - case H10301: - context->proto = H10301; - furi_string_set(context->proto_name, "H10301"); - break; - default: - break; - } -} - -void flipfrid_scene_entrypoint_on_enter(FlipFridState* context) { - // Clear the previous payload - context->payload[0] = 0x00; - context->payload[1] = 0x00; - context->payload[2] = 0x00; - context->payload[3] = 0x00; - context->payload[4] = 0x00; - context->payload[5] = 0x00; - - context->menu_index = 0; - /*for(uint32_t i = 0; i < 4; i++) { - menu_items[i] = furi_string_alloc(); - }*/ - - context->menu_proto_index = 0; - /*for(uint32_t i = 0; i < 4; i++) { - menu_proto_items[i] = furi_string_alloc(); - }*/ -} - -void flipfrid_scene_entrypoint_on_exit(FlipFridState* context) { - UNUSED(context); -} - -void flipfrid_scene_entrypoint_on_tick(FlipFridState* context) { - UNUSED(context); -} - -void flipfrid_scene_entrypoint_on_event(FlipFridEvent event, FlipFridState* context) { - if(event.evt_type == EventTypeKey) { - if(event.input_type == InputTypeShort) { - switch(event.key) { - case InputKeyDown: - if(context->menu_index < FlipFridAttackLoadFileCustomUids) { - context->menu_index++; - } - break; - case InputKeyUp: - if(context->menu_index > FlipFridAttackDefaultValues) { - context->menu_index--; - } - break; - case InputKeyLeft: - if(context->menu_proto_index > EM4100) { - context->menu_proto_index--; - } else if(context->menu_proto_index == EM4100) { - context->menu_proto_index = H10301; - } - break; - case InputKeyRight: - if(context->menu_proto_index < H10301) { - context->menu_proto_index++; - } else if(context->menu_proto_index == H10301) { - context->menu_proto_index = EM4100; - } - break; - case InputKeyOk: - flipfrid_scene_entrypoint_menu_callback( - context, context->menu_index, context->menu_proto_index); - break; - case InputKeyBack: - context->is_running = false; - break; - default: - break; - } - } - } -} - -void flipfrid_scene_entrypoint_on_draw(Canvas* canvas, FlipFridState* context) { - canvas_clear(canvas); - canvas_set_color(canvas, ColorBlack); - - if(context->main_menu_items != NULL) { - if(context->main_menu_items[context->menu_index] != NULL) { - if(context->menu_index > FlipFridAttackDefaultValues) { - canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned( - canvas, - 64, - 24, - AlignCenter, - AlignTop, - furi_string_get_cstr(context->main_menu_items[context->menu_index - 1])); - } - - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned( - canvas, - 64, - 36, - AlignCenter, - AlignTop, - furi_string_get_cstr(context->main_menu_items[context->menu_index])); - - if(context->menu_index < FlipFridAttackLoadFileCustomUids) { - canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned( - canvas, - 64, - 48, - AlignCenter, - AlignTop, - furi_string_get_cstr(context->main_menu_items[context->menu_index + 1])); - } - - if(context->menu_proto_index > EM4100) { - canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned( - canvas, - 64, - -12, - AlignCenter, - AlignTop, - furi_string_get_cstr( - context->main_menu_proto_items[context->menu_proto_index - 1])); - } - - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas, 27, 4, AlignCenter, AlignTop, "<"); - - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned( - canvas, - 64, - 4, - AlignCenter, - AlignTop, - furi_string_get_cstr(context->main_menu_proto_items[context->menu_proto_index])); - - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas, 101, 4, AlignCenter, AlignTop, ">"); - - if(context->menu_proto_index < H10301) { - canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned( - canvas, - 64, - -12, - AlignCenter, - AlignTop, - furi_string_get_cstr( - context->main_menu_proto_items[context->menu_proto_index + 1])); - } - } - } -} \ No newline at end of file diff --git a/applications/external/flipfrid/scene/flipfrid_scene_entrypoint.h b/applications/external/flipfrid/scene/flipfrid_scene_entrypoint.h deleted file mode 100644 index 29ca5bdfa2..0000000000 --- a/applications/external/flipfrid/scene/flipfrid_scene_entrypoint.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once -#include "../flipfrid.h" - -void flipfrid_scene_entrypoint_on_enter(FlipFridState* context); -void flipfrid_scene_entrypoint_on_exit(FlipFridState* context); -void flipfrid_scene_entrypoint_on_tick(FlipFridState* context); -void flipfrid_scene_entrypoint_on_event(FlipFridEvent event, FlipFridState* context); -void flipfrid_scene_entrypoint_on_draw(Canvas* canvas, FlipFridState* context); \ No newline at end of file diff --git a/applications/external/flipfrid/scene/flipfrid_scene_load_custom_uids.c b/applications/external/flipfrid/scene/flipfrid_scene_load_custom_uids.c deleted file mode 100644 index 32157556b8..0000000000 --- a/applications/external/flipfrid/scene/flipfrid_scene_load_custom_uids.c +++ /dev/null @@ -1,85 +0,0 @@ -#include "flipfrid_scene_load_custom_uids.h" -#include "flipfrid_scene_run_attack.h" -#include "flipfrid_scene_entrypoint.h" - -#define LFRFID_UIDS_EXTENSION ".txt" -#define RFIDFUZZER_APP_PATH_FOLDER "/ext/rfidfuzzer" - -bool flipfrid_load_uids(FlipFridState* context, const char* file_path) { - bool result = false; - Storage* storage = furi_record_open(RECORD_STORAGE); - context->uids_stream = buffered_file_stream_alloc(storage); - result = - buffered_file_stream_open(context->uids_stream, file_path, FSAM_READ, FSOM_OPEN_EXISTING); - // Close if loading fails - if(!result) { - buffered_file_stream_close(context->uids_stream); - return false; - } - return result; -} - -bool flipfrid_load_custom_uids_from_file(FlipFridState* context) { - // Input events and views are managed by file_select - FuriString* uid_path; - uid_path = furi_string_alloc(); - furi_string_set(uid_path, RFIDFUZZER_APP_PATH_FOLDER); - - DialogsFileBrowserOptions browser_options; - dialog_file_browser_set_basic_options(&browser_options, LFRFID_UIDS_EXTENSION, &I_125_10px); - browser_options.base_path = RFIDFUZZER_APP_PATH_FOLDER; - browser_options.hide_ext = false; - - bool res = dialog_file_browser_show(context->dialogs, uid_path, uid_path, &browser_options); - - if(res) { - res = flipfrid_load_uids(context, furi_string_get_cstr(uid_path)); - } - - furi_string_free(uid_path); - - return res; -} - -void flipfrid_scene_load_custom_uids_on_enter(FlipFridState* context) { - if(flipfrid_load_custom_uids_from_file(context)) { - // Force context loading - flipfrid_scene_run_attack_on_enter(context); - context->current_scene = SceneAttack; - } else { - flipfrid_scene_entrypoint_on_enter(context); - context->current_scene = SceneEntryPoint; - } -} - -void flipfrid_scene_load_custom_uids_on_exit(FlipFridState* context) { - UNUSED(context); -} - -void flipfrid_scene_load_custom_uids_on_tick(FlipFridState* context) { - UNUSED(context); -} - -void flipfrid_scene_load_custom_uids_on_event(FlipFridEvent event, FlipFridState* context) { - if(event.evt_type == EventTypeKey) { - if(event.input_type == InputTypeShort) { - switch(event.key) { - case InputKeyDown: - case InputKeyUp: - case InputKeyLeft: - case InputKeyRight: - case InputKeyOk: - case InputKeyBack: - context->current_scene = SceneEntryPoint; - break; - default: - break; - } - } - } -} - -void flipfrid_scene_load_custom_uids_on_draw(Canvas* canvas, FlipFridState* context) { - UNUSED(context); - UNUSED(canvas); -} diff --git a/applications/external/flipfrid/scene/flipfrid_scene_load_custom_uids.h b/applications/external/flipfrid/scene/flipfrid_scene_load_custom_uids.h deleted file mode 100644 index a8ed982b68..0000000000 --- a/applications/external/flipfrid/scene/flipfrid_scene_load_custom_uids.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once -#include "../flipfrid.h" - -void flipfrid_scene_load_custom_uids_on_enter(FlipFridState* context); -void flipfrid_scene_load_custom_uids_on_exit(FlipFridState* context); -void flipfrid_scene_load_custom_uids_on_tick(FlipFridState* context); -void flipfrid_scene_load_custom_uids_on_event(FlipFridEvent event, FlipFridState* context); -void flipfrid_scene_load_custom_uids_on_draw(Canvas* canvas, FlipFridState* context); -bool flipfrid_load_custom_uids_from_file(FlipFridState* context); \ No newline at end of file diff --git a/applications/external/flipfrid/scene/flipfrid_scene_load_file.c b/applications/external/flipfrid/scene/flipfrid_scene_load_file.c deleted file mode 100644 index 5f3f1a31b2..0000000000 --- a/applications/external/flipfrid/scene/flipfrid_scene_load_file.c +++ /dev/null @@ -1,193 +0,0 @@ -#include "flipfrid_scene_load_file.h" -#include "flipfrid_scene_entrypoint.h" - -#define LFRFID_APP_EXTENSION ".rfid" -#define LFRFID_APP_PATH_FOLDER "/ext/lfrfid" - -bool flipfrid_load(FlipFridState* context, const char* file_path) { - bool result = false; - Storage* storage = furi_record_open(RECORD_STORAGE); - FlipperFormat* fff_data_file = flipper_format_file_alloc(storage); - FuriString* temp_str; - temp_str = furi_string_alloc(); - do { - if(!flipper_format_file_open_existing(fff_data_file, file_path)) { - FURI_LOG_E(TAG, "Error open file %s", file_path); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Error open file"); - break; - } - - // FileType - if(!flipper_format_read_string(fff_data_file, "Filetype", temp_str)) { - FURI_LOG_E(TAG, "Missing or incorrect Filetype"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Missing or incorrect Filetypes"); - break; - } else { - FURI_LOG_I(TAG, "Filetype: %s", furi_string_get_cstr(temp_str)); - } - - // Key type - if(!flipper_format_read_string(fff_data_file, "Key type", temp_str)) { - FURI_LOG_E(TAG, "Missing or incorrect Key type"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Missing or incorrect Key type"); - break; - } else { - FURI_LOG_I(TAG, "Key type: %s", furi_string_get_cstr(temp_str)); - - if(context->proto == EM4100) { - if(strcmp(furi_string_get_cstr(temp_str), "EM4100") != 0) { - FURI_LOG_E(TAG, "Unsupported Key type"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Unsupported Key type"); - break; - } - } else if(context->proto == PAC) { - if(strcmp(furi_string_get_cstr(temp_str), "PAC/Stanley") != 0) { - FURI_LOG_E(TAG, "Unsupported Key type"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Unsupported Key type"); - break; - } - } else if(context->proto == H10301) { - if(strcmp(furi_string_get_cstr(temp_str), "H10301") != 0) { - FURI_LOG_E(TAG, "Unsupported Key type"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Unsupported Key type"); - break; - } - } else { - if(strcmp(furi_string_get_cstr(temp_str), "HIDProx") != 0) { - FURI_LOG_E(TAG, "Unsupported Key type"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Unsupported Key type"); - break; - } - } - } - - // Data - if(!flipper_format_read_string(fff_data_file, "Data", context->data_str)) { - FURI_LOG_E(TAG, "Missing or incorrect Data"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Missing or incorrect Key"); - break; - } else { - FURI_LOG_I(TAG, "Key: %s", furi_string_get_cstr(context->data_str)); - - if(context->proto == EM4100) { - if(furi_string_size(context->data_str) != 14) { - FURI_LOG_E(TAG, "Incorrect Key length"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Incorrect Key length"); - break; - } - } else if(context->proto == PAC) { - if(furi_string_size(context->data_str) != 11) { - FURI_LOG_E(TAG, "Incorrect Key length"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Incorrect Key length"); - break; - } - } else if(context->proto == H10301) { - if(furi_string_size(context->data_str) != 8) { - FURI_LOG_E(TAG, "Incorrect Key length"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Incorrect Key length"); - break; - } - } else { - if(furi_string_size(context->data_str) != 17) { - FURI_LOG_E(TAG, "Incorrect Key length"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Incorrect Key length"); - break; - } - } - - // String to uint8_t - for(uint8_t i = 0; i < 6; i++) { - char temp_str2[3]; - temp_str2[0] = furi_string_get_cstr(context->data_str)[i * 3]; - temp_str2[1] = furi_string_get_cstr(context->data_str)[i * 3 + 1]; - temp_str2[2] = '\0'; - context->data[i] = (uint8_t)strtol(temp_str2, NULL, 16); - } - } - - result = true; - } while(0); - furi_string_free(temp_str); - flipper_format_free(fff_data_file); - if(result) { - FURI_LOG_I(TAG, "Loaded successfully"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Source loaded."); - } - return result; -} - -void flipfrid_scene_load_file_on_enter(FlipFridState* context) { - if(flipfrid_load_protocol_from_file(context)) { - context->current_scene = SceneSelectField; - } else { - flipfrid_scene_entrypoint_on_enter(context); - context->current_scene = SceneEntryPoint; - } -} - -void flipfrid_scene_load_file_on_exit(FlipFridState* context) { - UNUSED(context); -} - -void flipfrid_scene_load_file_on_tick(FlipFridState* context) { - UNUSED(context); -} - -void flipfrid_scene_load_file_on_event(FlipFridEvent event, FlipFridState* context) { - if(event.evt_type == EventTypeKey) { - if(event.input_type == InputTypeShort) { - switch(event.key) { - case InputKeyDown: - case InputKeyUp: - case InputKeyLeft: - case InputKeyRight: - case InputKeyOk: - case InputKeyBack: - context->current_scene = SceneEntryPoint; - break; - default: - break; - } - } - } -} - -void flipfrid_scene_load_file_on_draw(Canvas* canvas, FlipFridState* context) { - UNUSED(context); - UNUSED(canvas); -} - -bool flipfrid_load_protocol_from_file(FlipFridState* context) { - FuriString* user_file_path; - user_file_path = furi_string_alloc(); - furi_string_set(user_file_path, LFRFID_APP_PATH_FOLDER); - - DialogsFileBrowserOptions browser_options; - dialog_file_browser_set_basic_options(&browser_options, LFRFID_APP_EXTENSION, &I_125_10px); - browser_options.base_path = LFRFID_APP_PATH_FOLDER; - - // Input events and views are managed by file_select - bool res = dialog_file_browser_show( - context->dialogs, user_file_path, user_file_path, &browser_options); - - if(res) { - res = flipfrid_load(context, furi_string_get_cstr(user_file_path)); - } - - furi_string_free(user_file_path); - - return res; -} \ No newline at end of file diff --git a/applications/external/flipfrid/scene/flipfrid_scene_load_file.h b/applications/external/flipfrid/scene/flipfrid_scene_load_file.h deleted file mode 100644 index ca82daab4b..0000000000 --- a/applications/external/flipfrid/scene/flipfrid_scene_load_file.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once -#include "../flipfrid.h" - -void flipfrid_scene_load_file_on_enter(FlipFridState* context); -void flipfrid_scene_load_file_on_exit(FlipFridState* context); -void flipfrid_scene_load_file_on_tick(FlipFridState* context); -void flipfrid_scene_load_file_on_event(FlipFridEvent event, FlipFridState* context); -void flipfrid_scene_load_file_on_draw(Canvas* canvas, FlipFridState* context); -bool flipfrid_load_protocol_from_file(FlipFridState* context); \ No newline at end of file diff --git a/applications/external/flipfrid/scene/flipfrid_scene_run_attack.c b/applications/external/flipfrid/scene/flipfrid_scene_run_attack.c deleted file mode 100644 index 225752559c..0000000000 --- a/applications/external/flipfrid/scene/flipfrid_scene_run_attack.c +++ /dev/null @@ -1,668 +0,0 @@ -#include "flipfrid_scene_run_attack.h" -#include - -uint8_t counter = 0; - -uint8_t id_list[17][5] = { - {0x00, 0x00, 0x00, 0x00, 0x00}, // Null bytes - {0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, // Only FF - {0x11, 0x11, 0x11, 0x11, 0x11}, // Only 11 - {0x22, 0x22, 0x22, 0x22, 0x22}, // Only 22 - {0x33, 0x33, 0x33, 0x33, 0x33}, // Only 33 - {0x44, 0x44, 0x44, 0x44, 0x44}, // Only 44 - {0x55, 0x55, 0x55, 0x55, 0x55}, // Only 55 - {0x66, 0x66, 0x66, 0x66, 0x66}, // Only 66 - {0x77, 0x77, 0x77, 0x77, 0x77}, // Only 77 - {0x88, 0x88, 0x88, 0x88, 0x88}, // Only 88 - {0x99, 0x99, 0x99, 0x99, 0x99}, // Only 99 - {0x12, 0x34, 0x56, 0x78, 0x9A}, // Incremental UID - {0x9A, 0x78, 0x56, 0x34, 0x12}, // Decremental UID - {0x04, 0xd0, 0x9b, 0x0d, 0x6a}, // From arha - {0x34, 0x00, 0x29, 0x3d, 0x9e}, // From arha - {0x04, 0xdf, 0x00, 0x00, 0x01}, // From arha - {0xCA, 0xCA, 0xCA, 0xCA, 0xCA}, // From arha -}; - -uint8_t id_list_hid[14][6] = { - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // Null bytes - {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, // Only FF - {0x11, 0x11, 0x11, 0x11, 0x11, 0x11}, // Only 11 - {0x22, 0x22, 0x22, 0x22, 0x22, 0x22}, // Only 22 - {0x33, 0x33, 0x33, 0x33, 0x33, 0x33}, // Only 33 - {0x44, 0x44, 0x44, 0x44, 0x44, 0x44}, // Only 44 - {0x55, 0x55, 0x55, 0x55, 0x55, 0x55}, // Only 55 - {0x66, 0x66, 0x66, 0x66, 0x66, 0x66}, // Only 66 - {0x77, 0x77, 0x77, 0x77, 0x77, 0x77}, // Only 77 - {0x88, 0x88, 0x88, 0x88, 0x88, 0x88}, // Only 88 - {0x99, 0x99, 0x99, 0x99, 0x99, 0x99}, // Only 99 - {0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC}, // Incremental UID - {0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12}, // Decremental UID - {0xCA, 0xCA, 0xCA, 0xCA, 0xCA, 0xCA}, // From arha -}; - -uint8_t id_list_pac[17][4] = { - {0x00, 0x00, 0x00, 0x00}, // Null bytes - {0xFF, 0xFF, 0xFF, 0xFF}, // Only FF - {0x11, 0x11, 0x11, 0x11}, // Only 11 - {0x22, 0x22, 0x22, 0x22}, // Only 22 - {0x33, 0x33, 0x33, 0x33}, // Only 33 - {0x44, 0x44, 0x44, 0x44}, // Only 44 - {0x55, 0x55, 0x55, 0x55}, // Only 55 - {0x66, 0x66, 0x66, 0x66}, // Only 66 - {0x77, 0x77, 0x77, 0x77}, // Only 77 - {0x88, 0x88, 0x88, 0x88}, // Only 88 - {0x99, 0x99, 0x99, 0x99}, // Only 99 - {0x12, 0x34, 0x56, 0x78}, // Incremental UID - {0x9A, 0x78, 0x56, 0x34}, // Decremental UID - {0x04, 0xd0, 0x9b, 0x0d}, // From arha - {0x34, 0x00, 0x29, 0x3d}, // From arha - {0x04, 0xdf, 0x00, 0x00}, // From arha - {0xCA, 0xCA, 0xCA, 0xCA}, // From arha -}; - -uint8_t id_list_h[14][3] = { - {0x00, 0x00, 0x00}, // Null bytes - {0xFF, 0xFF, 0xFF}, // Only FF - {0x11, 0x11, 0x11}, // Only 11 - {0x22, 0x22, 0x22}, // Only 22 - {0x33, 0x33, 0x33}, // Only 33 - {0x44, 0x44, 0x44}, // Only 44 - {0x55, 0x55, 0x55}, // Only 55 - {0x66, 0x66, 0x66}, // Only 66 - {0x77, 0x77, 0x77}, // Only 77 - {0x88, 0x88, 0x88}, // Only 88 - {0x99, 0x99, 0x99}, // Only 99 - {0x12, 0x34, 0x56}, // Incremental UID - {0x56, 0x34, 0x12}, // Decremental UID - {0xCA, 0xCA, 0xCA}, // From arha -}; - -void flipfrid_scene_run_attack_on_enter(FlipFridState* context) { - context->time_between_cards = 10; - context->attack_step = 0; - context->attack_stop_called = false; - context->dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax); - context->worker = lfrfid_worker_alloc(context->dict); - if(context->proto == HIDProx) { - context->protocol = protocol_dict_get_protocol_by_name(context->dict, "HIDProx"); - } else if(context->proto == PAC) { - context->protocol = protocol_dict_get_protocol_by_name(context->dict, "PAC/Stanley"); - } else if(context->proto == H10301) { - context->protocol = protocol_dict_get_protocol_by_name(context->dict, "H10301"); - } else { - context->protocol = protocol_dict_get_protocol_by_name(context->dict, "EM4100"); - } -} - -void flipfrid_scene_run_attack_on_exit(FlipFridState* context) { - if(context->workr_rund) { - lfrfid_worker_stop(context->worker); - lfrfid_worker_stop_thread(context->worker); - context->workr_rund = false; - } - lfrfid_worker_free(context->worker); - protocol_dict_free(context->dict); - notification_message(context->notify, &sequence_blink_stop); -} - -void flipfrid_scene_run_attack_on_tick(FlipFridState* context) { - if(context->is_attacking) { - if(1 == counter) { - protocol_dict_set_data(context->dict, context->protocol, context->payload, 6); - lfrfid_worker_free(context->worker); - context->worker = lfrfid_worker_alloc(context->dict); - lfrfid_worker_start_thread(context->worker); - lfrfid_worker_emulate_start(context->worker, context->protocol); - context->workr_rund = true; - } else if(0 == counter) { - if(context->workr_rund) { - lfrfid_worker_stop(context->worker); - lfrfid_worker_stop_thread(context->worker); - context->workr_rund = false; - furi_delay_ms(200); - } - switch(context->attack) { - case FlipFridAttackDefaultValues: - if(context->proto == EM4100) { - context->payload[0] = id_list[context->attack_step][0]; - context->payload[1] = id_list[context->attack_step][1]; - context->payload[2] = id_list[context->attack_step][2]; - context->payload[3] = id_list[context->attack_step][3]; - context->payload[4] = id_list[context->attack_step][4]; - - if(context->attack_step == 16) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - } else { - context->attack_step++; - } - break; - } else if(context->proto == PAC) { - context->payload[0] = id_list_pac[context->attack_step][0]; - context->payload[1] = id_list_pac[context->attack_step][1]; - context->payload[2] = id_list_pac[context->attack_step][2]; - context->payload[3] = id_list_pac[context->attack_step][3]; - - if(context->attack_step == 16) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - } else { - context->attack_step++; - } - break; - } else if(context->proto == H10301) { - context->payload[0] = id_list_h[context->attack_step][0]; - context->payload[1] = id_list_h[context->attack_step][1]; - context->payload[2] = id_list_h[context->attack_step][2]; - - if(context->attack_step == 13) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - } else { - context->attack_step++; - } - break; - } else { - context->payload[0] = id_list_hid[context->attack_step][0]; - context->payload[1] = id_list_hid[context->attack_step][1]; - context->payload[2] = id_list_hid[context->attack_step][2]; - context->payload[3] = id_list_hid[context->attack_step][3]; - context->payload[4] = id_list_hid[context->attack_step][4]; - context->payload[5] = id_list_hid[context->attack_step][5]; - - if(context->attack_step == 13) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - - } else { - context->attack_step++; - } - break; - } - - case FlipFridAttackBfCustomerId: - if(context->proto == EM4100) { - context->payload[0] = context->attack_step; - context->payload[1] = 0x00; - context->payload[2] = 0x00; - context->payload[3] = 0x00; - context->payload[4] = 0x00; - - if(context->attack_step == 255) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - } else { - context->attack_step++; - } - break; - } else if(context->proto == PAC) { - context->payload[0] = context->attack_step; - context->payload[1] = 0x00; - context->payload[2] = 0x00; - context->payload[3] = 0x00; - - if(context->attack_step == 255) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - } else { - context->attack_step++; - } - break; - } else if(context->proto == H10301) { - context->payload[0] = context->attack_step; - context->payload[1] = 0x00; - context->payload[2] = 0x00; - - if(context->attack_step == 255) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - } else { - context->attack_step++; - } - break; - } else { - context->payload[0] = context->attack_step; - context->payload[1] = 0x00; - context->payload[2] = 0x00; - context->payload[3] = 0x00; - context->payload[4] = 0x00; - context->payload[5] = 0x00; - - if(context->attack_step == 255) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - } else { - context->attack_step++; - } - break; - } - - case FlipFridAttackLoadFile: - if(context->proto == EM4100) { - context->payload[0] = context->data[0]; - context->payload[1] = context->data[1]; - context->payload[2] = context->data[2]; - context->payload[3] = context->data[3]; - context->payload[4] = context->data[4]; - - context->payload[context->key_index] = context->attack_step; - - if(context->attack_step == 255) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - break; - } else { - context->attack_step++; - } - break; - } else if(context->proto == PAC) { - context->payload[0] = context->data[0]; - context->payload[1] = context->data[1]; - context->payload[2] = context->data[2]; - context->payload[3] = context->data[3]; - - context->payload[context->key_index] = context->attack_step; - - if(context->attack_step == 255) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - break; - } else { - context->attack_step++; - } - break; - } else if(context->proto == H10301) { - context->payload[0] = context->data[0]; - context->payload[1] = context->data[1]; - context->payload[2] = context->data[2]; - - context->payload[context->key_index] = context->attack_step; - - if(context->attack_step == 255) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - break; - } else { - context->attack_step++; - } - break; - } else { - context->payload[0] = context->data[0]; - context->payload[1] = context->data[1]; - context->payload[2] = context->data[2]; - context->payload[3] = context->data[3]; - context->payload[4] = context->data[4]; - context->payload[5] = context->data[5]; - - context->payload[context->key_index] = context->attack_step; - - if(context->attack_step == 255) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - break; - } else { - context->attack_step++; - } - break; - } - - case FlipFridAttackLoadFileCustomUids: - if(context->proto == EM4100) { - bool end_of_list = false; - while(true) { - furi_string_reset(context->data_str); - if(!stream_read_line(context->uids_stream, context->data_str)) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - stream_rewind(context->uids_stream); - end_of_list = true; - break; - } - if(furi_string_get_char(context->data_str, 0) == '#') continue; - if(furi_string_size(context->data_str) != 11) break; - break; - } - if(end_of_list) break; - FURI_LOG_D(TAG, furi_string_get_cstr(context->data_str)); - if(furi_string_size(context->data_str) != 11) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_error); - break; - } - - // string is valid, parse it in context->payload - for(uint8_t i = 0; i < 5; i++) { - char temp_str[3]; - temp_str[0] = furi_string_get_cstr(context->data_str)[i * 2]; - temp_str[1] = furi_string_get_cstr(context->data_str)[i * 2 + 1]; - temp_str[2] = '\0'; - context->payload[i] = (uint8_t)strtol(temp_str, NULL, 16); - } - break; - } else if(context->proto == PAC) { - bool end_of_list = false; - while(true) { - furi_string_reset(context->data_str); - if(!stream_read_line(context->uids_stream, context->data_str)) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - stream_rewind(context->uids_stream); - end_of_list = true; - break; - } - if(furi_string_get_char(context->data_str, 0) == '#') continue; - if(furi_string_size(context->data_str) != 9) break; - break; - } - if(end_of_list) break; - FURI_LOG_D(TAG, furi_string_get_cstr(context->data_str)); - if(furi_string_size(context->data_str) != 9) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_error); - break; - } - - // string is valid, parse it in context->payload - for(uint8_t i = 0; i < 4; i++) { - char temp_str[3]; - temp_str[0] = furi_string_get_cstr(context->data_str)[i * 2]; - temp_str[1] = furi_string_get_cstr(context->data_str)[i * 2 + 1]; - temp_str[2] = '\0'; - context->payload[i] = (uint8_t)strtol(temp_str, NULL, 16); - } - break; - } else if(context->proto == H10301) { - bool end_of_list = false; - while(true) { - furi_string_reset(context->data_str); - if(!stream_read_line(context->uids_stream, context->data_str)) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - stream_rewind(context->uids_stream); - end_of_list = true; - break; - }; - if(furi_string_get_char(context->data_str, 0) == '#') continue; - if(furi_string_size(context->data_str) != 7) break; - break; - } - if(end_of_list) break; - FURI_LOG_D(TAG, furi_string_get_cstr(context->data_str)); - if(furi_string_size(context->data_str) != 7) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_error); - break; - }; - - // string is valid, parse it in context->payload - for(uint8_t i = 0; i < 3; i++) { - char temp_str[3]; - temp_str[0] = furi_string_get_cstr(context->data_str)[i * 2]; - temp_str[1] = furi_string_get_cstr(context->data_str)[i * 2 + 1]; - temp_str[2] = '\0'; - context->payload[i] = (uint8_t)strtol(temp_str, NULL, 16); - } - break; - } else { - bool end_of_list = false; - while(true) { - furi_string_reset(context->data_str); - if(!stream_read_line(context->uids_stream, context->data_str)) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - stream_rewind(context->uids_stream); - end_of_list = true; - break; - }; - if(furi_string_get_char(context->data_str, 0) == '#') continue; - if(furi_string_size(context->data_str) != 13) break; - break; - } - FURI_LOG_D(TAG, furi_string_get_cstr(context->data_str)); - if(end_of_list) break; - if(furi_string_size(context->data_str) != 13) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_error); - break; - }; - - // string is valid, parse it in context->payload - for(uint8_t i = 0; i < 6; i++) { - char temp_str[3]; - temp_str[0] = furi_string_get_cstr(context->data_str)[i * 2]; - temp_str[1] = furi_string_get_cstr(context->data_str)[i * 2 + 1]; - temp_str[2] = '\0'; - context->payload[i] = (uint8_t)strtol(temp_str, NULL, 16); - } - break; - default: - break; - } - } - } - if(counter > context->time_between_cards) { - counter = 0; - } else { - counter++; - } - } -} - -void flipfrid_scene_run_attack_on_event(FlipFridEvent event, FlipFridState* context) { - if(event.evt_type == EventTypeKey) { - if(event.input_type == InputTypeShort) { - switch(event.key) { - case InputKeyDown: - break; - case InputKeyUp: - break; - case InputKeyLeft: - if(!context->is_attacking) { - if(context->time_between_cards > 5) { - context->time_between_cards--; - } - } - break; - case InputKeyRight: - if(!context->is_attacking) { - if(context->time_between_cards < 70) { - context->time_between_cards++; - } - } - break; - case InputKeyOk: - counter = 0; - if(!context->is_attacking) { - notification_message(context->notify, &sequence_blink_start_blue); - context->is_attacking = true; - } else { - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - } - break; - case InputKeyBack: - context->is_attacking = false; - counter = 0; - - notification_message(context->notify, &sequence_blink_stop); - if(context->attack_stop_called) { - context->attack_stop_called = false; - context->attack_step = 0; - if(context->attack == FlipFridAttackLoadFileCustomUids) { - furi_string_reset(context->data_str); - stream_rewind(context->uids_stream); - buffered_file_stream_close(context->uids_stream); - } - - furi_string_reset(context->notification_msg); - context->current_scene = SceneEntryPoint; - } - - context->attack_stop_called = true; - break; - default: - break; - } - } - if(event.input_type == InputTypeLong) { - switch(event.key) { - case InputKeyLeft: - if(!context->is_attacking) { - if(context->time_between_cards > 0) { - if((context->time_between_cards - 10) > 5) { - context->time_between_cards -= 10; - } - } - } - break; - case InputKeyRight: - if(!context->is_attacking) { - if(context->time_between_cards < 70) { - context->time_between_cards += 10; - } - } - break; - default: - break; - } - } - } -} - -void flipfrid_scene_run_attack_on_draw(Canvas* canvas, FlipFridState* context) { - canvas_clear(canvas); - canvas_set_color(canvas, ColorBlack); - - // Frame - //canvas_draw_frame(canvas, 0, 0, 128, 64); - - // Title - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned( - canvas, 64, 2, AlignCenter, AlignTop, furi_string_get_cstr(context->attack_name)); - - char uid[18]; - char speed[16]; - if(context->proto == HIDProx) { - snprintf( - uid, - sizeof(uid), - "%02X:%02X:%02X:%02X:%02X:%02X", - context->payload[0], - context->payload[1], - context->payload[2], - context->payload[3], - context->payload[4], - context->payload[5]); - } else if(context->proto == PAC) { - snprintf( - uid, - sizeof(uid), - "%02X:%02X:%02X:%02X", - context->payload[0], - context->payload[1], - context->payload[2], - context->payload[3]); - } else if(context->proto == H10301) { - snprintf( - uid, - sizeof(uid), - "%02X:%02X:%02X", - context->payload[0], - context->payload[1], - context->payload[2]); - } else { - snprintf( - uid, - sizeof(uid), - "%02X:%02X:%02X:%02X:%02X", - context->payload[0], - context->payload[1], - context->payload[2], - context->payload[3], - context->payload[4]); - } - - canvas_draw_str_aligned(canvas, 64, 38, AlignCenter, AlignTop, uid); - - canvas_set_font(canvas, FontSecondary); - - canvas_draw_str_aligned( - canvas, 64, 26, AlignCenter, AlignTop, furi_string_get_cstr(context->proto_name)); - - snprintf(speed, sizeof(speed), "Time delay: %d", context->time_between_cards); - - //canvas_draw_str_aligned(canvas, 0, 22, AlignLeft, AlignTop, "Speed:"); - canvas_draw_str_aligned(canvas, 64, 14, AlignCenter, AlignTop, speed); - //char start_stop_msg[20]; - if(context->is_attacking) { - elements_button_center(canvas, "Stop"); - //snprintf(start_stop_msg, sizeof(start_stop_msg), " Press OK to stop "); - } else { - elements_button_center(canvas, "Start"); - elements_button_left(canvas, "TD -"); - elements_button_right(canvas, "+ TD"); - } - //canvas_draw_str_aligned(canvas, 64, 44, AlignCenter, AlignTop, start_stop_msg); -} diff --git a/applications/external/flipfrid/scene/flipfrid_scene_run_attack.h b/applications/external/flipfrid/scene/flipfrid_scene_run_attack.h deleted file mode 100644 index ae56d35e7e..0000000000 --- a/applications/external/flipfrid/scene/flipfrid_scene_run_attack.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once -#include "../flipfrid.h" - -void flipfrid_scene_run_attack_on_enter(FlipFridState* context); -void flipfrid_scene_run_attack_on_exit(FlipFridState* context); -void flipfrid_scene_run_attack_on_tick(FlipFridState* context); -void flipfrid_scene_run_attack_on_event(FlipFridEvent event, FlipFridState* context); -void flipfrid_scene_run_attack_on_draw(Canvas* canvas, FlipFridState* context); diff --git a/applications/external/flipfrid/scene/flipfrid_scene_select_field.c b/applications/external/flipfrid/scene/flipfrid_scene_select_field.c deleted file mode 100644 index ccb49e910a..0000000000 --- a/applications/external/flipfrid/scene/flipfrid_scene_select_field.c +++ /dev/null @@ -1,160 +0,0 @@ -#include "flipfrid_scene_select_field.h" - -void flipfrid_center_displayed_key(FlipFridState* context, uint8_t index) { - char key_cstr[18]; - uint8_t key_len = 18; - uint8_t str_index = (index * 3); - int data_len = sizeof(context->data) / sizeof(context->data[0]); - int key_index = 0; - - if(context->proto == EM4100) { - key_len = 16; - } - if(context->proto == PAC) { - key_len = 13; - } - if(context->proto == H10301) { - key_len = 10; - } - - for(uint8_t i = 0; i < data_len; i++) { - if(context->data[i] < 9) { - key_index += - snprintf(&key_cstr[key_index], key_len - key_index, "0%X ", context->data[i]); - } else { - key_index += - snprintf(&key_cstr[key_index], key_len - key_index, "%X ", context->data[i]); - } - } - - char display_menu[17] = { - 'X', 'X', ' ', 'X', 'X', ' ', '<', 'X', 'X', '>', ' ', 'X', 'X', ' ', 'X', 'X', '\0'}; - - if(index > 1) { - display_menu[0] = key_cstr[str_index - 6]; - display_menu[1] = key_cstr[str_index - 5]; - } else { - display_menu[0] = ' '; - display_menu[1] = ' '; - } - - if(index > 0) { - display_menu[3] = key_cstr[str_index - 3]; - display_menu[4] = key_cstr[str_index - 2]; - } else { - display_menu[3] = ' '; - display_menu[4] = ' '; - } - - display_menu[7] = key_cstr[str_index]; - display_menu[8] = key_cstr[str_index + 1]; - - if((str_index + 4) <= (uint8_t)strlen(key_cstr)) { - display_menu[11] = key_cstr[str_index + 3]; - display_menu[12] = key_cstr[str_index + 4]; - } else { - display_menu[11] = ' '; - display_menu[12] = ' '; - } - - if((str_index + 8) <= (uint8_t)strlen(key_cstr)) { - display_menu[14] = key_cstr[str_index + 6]; - display_menu[15] = key_cstr[str_index + 7]; - } else { - display_menu[14] = ' '; - display_menu[15] = ' '; - } - - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, display_menu); -} - -void flipfrid_scene_select_field_on_enter(FlipFridState* context) { - furi_string_reset(context->notification_msg); -} - -void flipfrid_scene_select_field_on_exit(FlipFridState* context) { - UNUSED(context); -} - -void flipfrid_scene_select_field_on_tick(FlipFridState* context) { - UNUSED(context); -} - -void flipfrid_scene_select_field_on_event(FlipFridEvent event, FlipFridState* context) { - if(event.evt_type == EventTypeKey) { - if(event.input_type == InputTypeShort) { - const char* key_cstr = furi_string_get_cstr(context->data_str); - int data_len = sizeof(context->data) / sizeof(context->data[0]); - - // don't look, it's ugly but I'm a python dev so... - uint8_t nb_bytes = 0; - for(uint8_t i = 0; i < strlen(key_cstr); i++) { - if(' ' == key_cstr[i]) { - nb_bytes++; - } - } - - switch(event.key) { - case InputKeyDown: - for(uint8_t i = 0; i < data_len; i++) { - if(context->key_index == i) { - context->data[i] = (context->data[i] - 1); - } - } - break; - case InputKeyUp: - for(uint8_t i = 0; i < data_len; i++) { - if(context->key_index == i) { - context->data[i] = (context->data[i] + 1); - } - } - break; - case InputKeyLeft: - if(context->key_index > 0) { - context->key_index = context->key_index - 1; - } - break; - case InputKeyRight: - if(context->key_index < nb_bytes) { - context->key_index = context->key_index + 1; - } - break; - case InputKeyOk: - furi_string_reset(context->notification_msg); - context->current_scene = SceneAttack; - break; - case InputKeyBack: - context->key_index = 0; - furi_string_reset(context->notification_msg); - context->current_scene = SceneSelectFile; - break; - default: - break; - } - FURI_LOG_D(TAG, "Position: %d/%d", context->key_index, nb_bytes); - } - } -} - -void flipfrid_scene_select_field_on_draw(Canvas* canvas, FlipFridState* context) { - canvas_clear(canvas); - canvas_set_color(canvas, ColorBlack); - - // Frame - //canvas_draw_frame(canvas, 0, 0, 128, 64); - - canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned(canvas, 12, 5, AlignLeft, AlignTop, "Left and right: select byte"); - canvas_draw_str_aligned(canvas, 12, 15, AlignLeft, AlignTop, "Up and down: adjust byte"); - - char msg_index[18]; - canvas_set_font(canvas, FontPrimary); - snprintf(msg_index, sizeof(msg_index), "Field index : %d", context->key_index); - canvas_draw_str_aligned(canvas, 64, 30, AlignCenter, AlignTop, msg_index); - - flipfrid_center_displayed_key(context, context->key_index); - canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned( - canvas, 64, 45, AlignCenter, AlignTop, furi_string_get_cstr(context->notification_msg)); -} diff --git a/applications/external/flipfrid/scene/flipfrid_scene_select_field.h b/applications/external/flipfrid/scene/flipfrid_scene_select_field.h deleted file mode 100644 index 5533e321cd..0000000000 --- a/applications/external/flipfrid/scene/flipfrid_scene_select_field.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once -#include "../flipfrid.h" - -void flipfrid_scene_select_field_on_enter(FlipFridState* context); -void flipfrid_scene_select_field_on_exit(FlipFridState* context); -void flipfrid_scene_select_field_on_tick(FlipFridState* context); -void flipfrid_scene_select_field_on_event(FlipFridEvent event, FlipFridState* context); -void flipfrid_scene_select_field_on_draw(Canvas* canvas, FlipFridState* context); -void center_displayed_key(FlipFridState* context, uint8_t index); \ No newline at end of file diff --git a/applications/external/flipper_i2ctools/application.fam b/applications/external/flipper_i2ctools/application.fam index 581de63bce..b810b23775 100644 --- a/applications/external/flipper_i2ctools/application.fam +++ b/applications/external/flipper_i2ctools/application.fam @@ -1,6 +1,6 @@ App( appid="i2c_tools", - name="[GPIO] i2c Tools", + name="[I2C] Tools", apptype=FlipperAppType.EXTERNAL, entry_point="i2ctools_app", requires=["gui"], @@ -9,4 +9,7 @@ App( fap_icon="i2ctools.png", fap_category="GPIO", fap_icon_assets="images", + fap_author="@NaejEL", + fap_version="1.0", + fap_description="Set of i2c tools", ) diff --git a/applications/external/flipper_i2ctools/images/ButtonDown_7x4.png b/applications/external/flipper_i2ctools/images/ButtonDown_7x4.png deleted file mode 100644 index 2954bb6a67..0000000000 Binary files a/applications/external/flipper_i2ctools/images/ButtonDown_7x4.png and /dev/null differ diff --git a/applications/external/flipper_i2ctools/images/ButtonLeft_4x7.png b/applications/external/flipper_i2ctools/images/ButtonLeft_4x7.png deleted file mode 100644 index 0b4655d432..0000000000 Binary files a/applications/external/flipper_i2ctools/images/ButtonLeft_4x7.png and /dev/null differ diff --git a/applications/external/flipper_i2ctools/images/ButtonRight_4x7.png b/applications/external/flipper_i2ctools/images/ButtonRight_4x7.png deleted file mode 100644 index 8e1c74c1c0..0000000000 Binary files a/applications/external/flipper_i2ctools/images/ButtonRight_4x7.png and /dev/null differ diff --git a/applications/external/flipper_i2ctools/images/ButtonUp_7x4.png b/applications/external/flipper_i2ctools/images/ButtonUp_7x4.png deleted file mode 100644 index 1be79328b4..0000000000 Binary files a/applications/external/flipper_i2ctools/images/ButtonUp_7x4.png and /dev/null differ diff --git a/applications/external/flipper_i2ctools/images/Ok_btn_9x9.png b/applications/external/flipper_i2ctools/images/Ok_btn_9x9.png deleted file mode 100644 index 9a1539da20..0000000000 Binary files a/applications/external/flipper_i2ctools/images/Ok_btn_9x9.png and /dev/null differ diff --git a/applications/external/flipper_i2ctools/views/main_view.h b/applications/external/flipper_i2ctools/views/main_view.h index f0ce5e8adf..34fad74fa7 100644 --- a/applications/external/flipper_i2ctools/views/main_view.h +++ b/applications/external/flipper_i2ctools/views/main_view.h @@ -2,6 +2,7 @@ #include #include #include +#include #define APP_NAME "I2C Tools" #define SCAN_MENU_TEXT "Scan" @@ -35,4 +36,4 @@ typedef struct { void draw_main_view(Canvas* canvas, i2cMainView* main_view); i2cMainView* i2c_main_view_alloc(); -void i2c_main_view_free(i2cMainView* main_view); \ No newline at end of file +void i2c_main_view_free(i2cMainView* main_view); diff --git a/applications/external/flipper_i2ctools/views/scanner_view.h b/applications/external/flipper_i2ctools/views/scanner_view.h index 52f30a7bf3..a38d9a9178 100644 --- a/applications/external/flipper_i2ctools/views/scanner_view.h +++ b/applications/external/flipper_i2ctools/views/scanner_view.h @@ -2,8 +2,9 @@ #include #include #include +#include #include "../i2cscanner.h" #define SCAN_TEXT "SCAN" -void draw_scanner_view(Canvas* canvas, i2cScanner* i2c_scanner); \ No newline at end of file +void draw_scanner_view(Canvas* canvas, i2cScanner* i2c_scanner); diff --git a/applications/external/flipper_i2ctools/views/sender_view.h b/applications/external/flipper_i2ctools/views/sender_view.h index c4fdd98a28..9b2e75a5c9 100644 --- a/applications/external/flipper_i2ctools/views/sender_view.h +++ b/applications/external/flipper_i2ctools/views/sender_view.h @@ -2,8 +2,9 @@ #include #include #include +#include #include "../i2csender.h" #define SEND_TEXT "SEND" -void draw_sender_view(Canvas* canvas, i2cSender* i2c_sender); \ No newline at end of file +void draw_sender_view(Canvas* canvas, i2cSender* i2c_sender); diff --git a/applications/external/flipper_i2ctools/views/sniffer_view.h b/applications/external/flipper_i2ctools/views/sniffer_view.h index 8f2140bbac..b827294f89 100644 --- a/applications/external/flipper_i2ctools/views/sniffer_view.h +++ b/applications/external/flipper_i2ctools/views/sniffer_view.h @@ -2,8 +2,9 @@ #include #include #include +#include #include "../i2csniffer.h" #define SNIFF_TEXT "SNIFF" -void draw_sniffer_view(Canvas* canvas, i2cSniffer* i2c_sniffer); \ No newline at end of file +void draw_sniffer_view(Canvas* canvas, i2cSniffer* i2c_sniffer); diff --git a/applications/external/flizzer_tracker/LICENSE b/applications/external/flizzer_tracker/LICENSE new file mode 100644 index 0000000000..cd5059b4c2 --- /dev/null +++ b/applications/external/flizzer_tracker/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 LTVA1 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/applications/external/flizzer_tracker/application.fam b/applications/external/flizzer_tracker/application.fam new file mode 100644 index 0000000000..c5b2d2cee8 --- /dev/null +++ b/applications/external/flizzer_tracker/application.fam @@ -0,0 +1,16 @@ +App( + appid="flizzer_tracker", + name="Flizzer Tracker", + apptype=FlipperAppType.EXTERNAL, + entry_point="flizzer_tracker_app", + cdefines=["APP_FLIZZER_TRACKER"], + stack_size=2 * 1024, + order=90, + fap_version=(0, 2), + fap_description="An advanced Flipper Zero chiptune tracker with 4 channels", + fap_author="LTVA", + fap_weburl="https://github.com/LTVA1/flizzer_tracker", + fap_icon="flizzer_tracker.png", + fap_icon_assets="images", + fap_category="Media", +) diff --git a/applications/external/flizzer_tracker/audio_modes.c b/applications/external/flizzer_tracker/audio_modes.c new file mode 100644 index 0000000000..07bb1df53d --- /dev/null +++ b/applications/external/flizzer_tracker/audio_modes.c @@ -0,0 +1,8 @@ +#include +#include + +char* audio_modes_text[2] = { + "Internal", + "External", +}; +bool audio_modes_values[2] = {false, true}; \ No newline at end of file diff --git a/applications/external/flizzer_tracker/diskop.c b/applications/external/flizzer_tracker/diskop.c new file mode 100644 index 0000000000..146282f82b --- /dev/null +++ b/applications/external/flizzer_tracker/diskop.c @@ -0,0 +1,259 @@ +#include "diskop.h" + +#define CFG_FILENAME "settings.cfg" + +void save_instrument_inner(Stream* stream, Instrument* inst) { + size_t rwops = stream_write(stream, (uint8_t*)inst->name, sizeof(inst->name)); + rwops = stream_write(stream, (uint8_t*)&inst->waveform, sizeof(inst->waveform)); + rwops = stream_write(stream, (uint8_t*)&inst->flags, sizeof(inst->flags)); + rwops = stream_write( + stream, (uint8_t*)&inst->sound_engine_flags, sizeof(inst->sound_engine_flags)); + + rwops = stream_write(stream, (uint8_t*)&inst->base_note, sizeof(inst->base_note)); + rwops = stream_write(stream, (uint8_t*)&inst->finetune, sizeof(inst->finetune)); + + rwops = stream_write(stream, (uint8_t*)&inst->slide_speed, sizeof(inst->slide_speed)); + + rwops = stream_write(stream, (uint8_t*)&inst->adsr, sizeof(inst->adsr)); + rwops = stream_write(stream, (uint8_t*)&inst->pw, sizeof(inst->pw)); + + if(inst->sound_engine_flags & SE_ENABLE_RING_MOD) { + rwops = stream_write(stream, (uint8_t*)&inst->ring_mod, sizeof(inst->ring_mod)); + } + + if(inst->sound_engine_flags & SE_ENABLE_HARD_SYNC) { + rwops = stream_write(stream, (uint8_t*)&inst->hard_sync, sizeof(inst->hard_sync)); + } + + uint8_t progsteps = 0; + + for(uint8_t i = 0; i < INST_PROG_LEN; i++) { + if((inst->program[i] & 0x7fff) != TE_PROGRAM_NOP) { + progsteps = i + 1; + } + } + + rwops = stream_write(stream, (uint8_t*)&progsteps, sizeof(progsteps)); + + if(progsteps > 0) { + rwops = + stream_write(stream, (uint8_t*)inst->program, progsteps * sizeof(inst->program[0])); + } + + rwops = stream_write(stream, (uint8_t*)&inst->program_period, sizeof(inst->program_period)); + + if(inst->flags & TE_ENABLE_VIBRATO) { + rwops = stream_write(stream, (uint8_t*)&inst->vibrato_speed, sizeof(inst->vibrato_speed)); + rwops = stream_write(stream, (uint8_t*)&inst->vibrato_depth, sizeof(inst->vibrato_depth)); + rwops = stream_write(stream, (uint8_t*)&inst->vibrato_delay, sizeof(inst->vibrato_delay)); + } + + if(inst->flags & TE_ENABLE_PWM) { + rwops = stream_write(stream, (uint8_t*)&inst->pwm_speed, sizeof(inst->pwm_speed)); + rwops = stream_write(stream, (uint8_t*)&inst->pwm_depth, sizeof(inst->pwm_depth)); + rwops = stream_write(stream, (uint8_t*)&inst->pwm_delay, sizeof(inst->pwm_delay)); + } + + if(inst->sound_engine_flags & SE_ENABLE_FILTER) { + rwops = stream_write(stream, (uint8_t*)&inst->filter_cutoff, sizeof(inst->filter_cutoff)); + rwops = stream_write( + stream, (uint8_t*)&inst->filter_resonance, sizeof(inst->filter_resonance)); + rwops = stream_write(stream, (uint8_t*)&inst->filter_type, sizeof(inst->filter_type)); + } + + UNUSED(rwops); +} + +bool save_instrument(FlizzerTrackerApp* tracker, FuriString* filepath) { + bool file_removed = + storage_simply_remove(tracker->storage, furi_string_get_cstr(filepath)); // just in case + bool open_file = file_stream_open( + tracker->stream, furi_string_get_cstr(filepath), FSAM_WRITE, FSOM_OPEN_ALWAYS); + + uint8_t version = TRACKER_ENGINE_VERSION; + size_t rwops = + stream_write(tracker->stream, (uint8_t*)INST_FILE_SIG, sizeof(INST_FILE_SIG) - 1); + rwops = stream_write(tracker->stream, (uint8_t*)&version, sizeof(uint8_t)); + + Instrument* inst = tracker->song.instrument[tracker->current_instrument]; + + save_instrument_inner(tracker->stream, inst); + + file_stream_close(tracker->stream); + tracker->is_saving_instrument = false; + furi_string_free(filepath); + + UNUSED(file_removed); + UNUSED(open_file); + UNUSED(rwops); + return false; +} + +bool save_song(FlizzerTrackerApp* tracker, FuriString* filepath) { + bool file_removed = + storage_simply_remove(tracker->storage, furi_string_get_cstr(filepath)); // just in case + bool open_file = file_stream_open( + tracker->stream, furi_string_get_cstr(filepath), FSAM_WRITE, FSOM_OPEN_ALWAYS); + + uint8_t version = TRACKER_ENGINE_VERSION; + size_t rwops = + stream_write(tracker->stream, (uint8_t*)SONG_FILE_SIG, sizeof(SONG_FILE_SIG) - 1); + rwops = stream_write(tracker->stream, (uint8_t*)&version, sizeof(uint8_t)); + + TrackerSong* song = &tracker->song; + + /*for(uint32_t i = 0; i < 23444; i++) + { + rwops = stream_write(tracker->stream, (uint8_t*)&song->loop_end, sizeof(uint8_t)); + }*/ + + rwops = stream_write(tracker->stream, (uint8_t*)song->song_name, sizeof(song->song_name)); + rwops = stream_write(tracker->stream, (uint8_t*)&song->loop_start, sizeof(song->loop_start)); + rwops = stream_write(tracker->stream, (uint8_t*)&song->loop_end, sizeof(song->loop_end)); + rwops = stream_write( + tracker->stream, (uint8_t*)&song->pattern_length, sizeof(song->pattern_length)); + + rwops = stream_write(tracker->stream, (uint8_t*)&song->speed, sizeof(song->speed)); + rwops = stream_write(tracker->stream, (uint8_t*)&song->rate, sizeof(song->rate)); + + rwops = stream_write( + tracker->stream, (uint8_t*)&song->num_sequence_steps, sizeof(song->num_sequence_steps)); + + for(uint16_t i = 0; i < song->num_sequence_steps; i++) { + rwops = stream_write( + tracker->stream, + (uint8_t*)&song->sequence.sequence_step[i], + sizeof(song->sequence.sequence_step[0])); + } + + rwops = + stream_write(tracker->stream, (uint8_t*)&song->num_patterns, sizeof(song->num_patterns)); + + for(uint16_t i = 0; i < song->num_patterns; i++) { + rwops = stream_write( + tracker->stream, + (uint8_t*)song->pattern[i].step, + sizeof(TrackerSongPatternStep) * (song->pattern_length)); + } + + rwops = stream_write( + tracker->stream, (uint8_t*)&song->num_instruments, sizeof(song->num_instruments)); + + for(uint16_t i = 0; i < song->num_instruments; i++) { + save_instrument_inner(tracker->stream, song->instrument[i]); + } + + file_stream_close(tracker->stream); + tracker->is_saving = false; + furi_string_free(filepath); + + UNUSED(file_removed); + UNUSED(open_file); + UNUSED(rwops); + return false; +} + +bool load_song_util(FlizzerTrackerApp* tracker, FuriString* filepath) { + bool open_file = file_stream_open( + tracker->stream, furi_string_get_cstr(filepath), FSAM_READ, FSOM_OPEN_ALWAYS); + + bool result = load_song(&tracker->song, tracker->stream); + + tracker->is_loading = false; + file_stream_close(tracker->stream); + furi_string_free(filepath); + UNUSED(open_file); + return result; +} + +bool load_instrument_disk(TrackerSong* song, uint8_t inst, Stream* stream) { + set_default_instrument(song->instrument[inst]); + + char header[sizeof(INST_FILE_SIG) + 2] = {0}; + size_t rwops = stream_read(stream, (uint8_t*)&header, sizeof(INST_FILE_SIG) - 1); + header[sizeof(INST_FILE_SIG)] = '\0'; + + uint8_t version = 0; + + if(strcmp(header, INST_FILE_SIG) == 0) { + rwops = stream_read(stream, (uint8_t*)&version, sizeof(version)); + + if(version <= TRACKER_ENGINE_VERSION) { + load_instrument_inner(stream, song->instrument[inst], version); + } + } + + UNUSED(rwops); + return false; +} + +bool load_instrument_util(FlizzerTrackerApp* tracker, FuriString* filepath) { + bool open_file = file_stream_open( + tracker->stream, furi_string_get_cstr(filepath), FSAM_READ, FSOM_OPEN_ALWAYS); + + bool result = + load_instrument_disk(&tracker->song, tracker->current_instrument, tracker->stream); + + tracker->is_loading_instrument = false; + file_stream_close(tracker->stream); + furi_string_free(filepath); + UNUSED(open_file); + return result; +} + +void save_config(FlizzerTrackerApp* tracker) { + // stream_read_line + FuriString* filepath = furi_string_alloc(); + FuriString* config_line = furi_string_alloc(); + furi_string_cat_printf(filepath, "%s/%s", FLIZZER_TRACKER_FOLDER, CFG_FILENAME); + + bool open_file = file_stream_open( + tracker->stream, furi_string_get_cstr(filepath), FSAM_WRITE, FSOM_OPEN_ALWAYS); + UNUSED(open_file); + + furi_string_cat_printf( + config_line, "%s = %s\n", "external_audio", tracker->external_audio ? "true" : "false"); + stream_write_string(tracker->stream, config_line); + + file_stream_close(tracker->stream); + furi_string_free(filepath); + furi_string_free(config_line); +} + +void load_config(FlizzerTrackerApp* tracker) { + FuriString* filepath = furi_string_alloc(); + FuriString* config_line = furi_string_alloc(); + furi_string_cat_printf(filepath, "%s/%s", FLIZZER_TRACKER_FOLDER, CFG_FILENAME); + + bool open_file = file_stream_open( + tracker->stream, furi_string_get_cstr(filepath), FSAM_READ, FSOM_OPEN_ALWAYS); + UNUSED(open_file); + + stream_read_line(tracker->stream, config_line); + + sscanf( + furi_string_get_cstr(config_line), "%s%s%s", tracker->param, tracker->eq, tracker->value); + + if(strcmp(tracker->param, "external_audio") == 0) { + if(strcmp(tracker->value, "false") == 0) { + tracker->external_audio = false; + // strcpy(tracker->value, "false_"); + } + + if(strcmp(tracker->value, "true") == 0) { + tracker->external_audio = true; + // strcpy(tracker->value, "true_"); + } + + sound_engine_init( + &tracker->sound_engine, + tracker->sound_engine.sample_rate, + tracker->external_audio, + tracker->sound_engine.audio_buffer_size); + // sound_engine_set_audio_output(tracker->external_audio); + } + + file_stream_close(tracker->stream); + furi_string_free(filepath); + furi_string_free(config_line); +} \ No newline at end of file diff --git a/applications/external/flizzer_tracker/diskop.h b/applications/external/flizzer_tracker/diskop.h new file mode 100644 index 0000000000..ed8b52a11a --- /dev/null +++ b/applications/external/flizzer_tracker/diskop.h @@ -0,0 +1,13 @@ +#pragma once + +#include "flizzer_tracker.h" +#include "tracker_engine/diskop.h" + +bool save_song(FlizzerTrackerApp* tracker, FuriString* filepath); +bool save_instrument(FlizzerTrackerApp* tracker, FuriString* filepath); + +bool load_song_util(FlizzerTrackerApp* tracker, FuriString* filepath); +bool load_instrument_util(FlizzerTrackerApp* tracker, FuriString* filepath); + +void save_config(FlizzerTrackerApp* tracker); +void load_config(FlizzerTrackerApp* tracker); \ No newline at end of file diff --git a/applications/external/flizzer_tracker/flizzer_tracker.c b/applications/external/flizzer_tracker/flizzer_tracker.c new file mode 100644 index 0000000000..ed696cdc6d --- /dev/null +++ b/applications/external/flizzer_tracker/flizzer_tracker.c @@ -0,0 +1,215 @@ +#include "flizzer_tracker.h" +#include "diskop.h" +#include "init_deinit.h" +#include "input_event.h" +#include "util.h" +#include "view/instrument_editor.h" +#include "view/pattern_editor.h" + +#include "font.h" +#include + +void draw_callback(Canvas* canvas, void* ctx) { + TrackerViewModel* model = (TrackerViewModel*)ctx; + FlizzerTrackerApp* tracker = (FlizzerTrackerApp*)(model->tracker); + + canvas_set_color(canvas, ColorXOR); + + if(tracker->is_loading || tracker->is_loading_instrument) { + canvas_draw_str(canvas, 10, 10, "Loading..."); + return; + } + + if(tracker->is_saving || tracker->is_saving_instrument) { + canvas_draw_str(canvas, 10, 10, "Saving..."); + return; + } + + if(tracker->showing_help) { + canvas_draw_icon(canvas, 0, 0, &I_help); + return; + } + + canvas_set_custom_u8g2_font(canvas, u8g2_font_tom_thumb_4x6_tr); + + switch(tracker->mode) { + case PATTERN_VIEW: { + if(tracker->tracker_engine.song == NULL) { + stop(); + tracker_engine_set_song(&tracker->tracker_engine, &tracker->song); + } + + if(tracker->focus != EDIT_PATTERN) { + draw_songinfo_view(canvas, tracker); + } + + if(tracker->focus != EDIT_PATTERN) { + draw_sequence_view(canvas, tracker); + } + + draw_pattern_view(canvas, tracker); + break; + } + + case INST_EDITOR_VIEW: { + draw_instrument_view(canvas, tracker); + draw_instrument_program_view(canvas, tracker); + break; + } + + default: + break; + } +} + +bool input_callback(InputEvent* input_event, void* ctx) { + // Проверяем, что контекст не нулевой + furi_assert(ctx); + TrackerView* tracker_view = (TrackerView*)ctx; + FlizzerTrackerApp* tracker = (FlizzerTrackerApp*)(tracker_view->context); + + bool consumed = false; + + if(input_event->key == InputKeyBack && input_event->type == InputTypeShort) { + tracker->period = furi_get_tick() - tracker->current_time; + tracker->current_time = furi_get_tick(); + + tracker->was_it_back_keypress = true; + } + + else if(input_event->type == InputTypeShort || input_event->type == InputTypeLong) { + tracker->was_it_back_keypress = false; + tracker->period = 0; + } + + uint32_t final_period = (tracker->was_it_back_keypress ? tracker->period : 0); + + FlizzerTrackerEvent event = { + .type = EventTypeInput, .input = *input_event, .period = final_period}; + + if(!(tracker->is_loading) && !(tracker->is_saving)) { + furi_message_queue_put(tracker->event_queue, &event, FuriWaitForever); + } + + consumed = true; + return consumed; +} + +int32_t flizzer_tracker_app(void* p) { + UNUSED(p); + + Storage* storage = furi_record_open(RECORD_STORAGE); + storage_simply_mkdir(storage, FLIZZER_TRACKER_INSTRUMENTS_FOLDER); + furi_record_close(RECORD_STORAGE); + + FlizzerTrackerApp* tracker = init_tracker(44100, 50, true, 1024); + + // Текущее событие типа кастомного типа FlizzerTrackerEvent + FlizzerTrackerEvent event; + + view_dispatcher_switch_to_view(tracker->view_dispatcher, VIEW_TRACKER); + + // Бесконечный цикл обработки очереди событий + while(!(tracker->quit)) { + // Выбираем событие из очереди в переменную event (ждём бесконечно долго, если очередь пуста) + // и проверяем, что у нас получилось это сделать + furi_check( + furi_message_queue_get(tracker->event_queue, &event, FuriWaitForever) == FuriStatusOk); + + // Наше событие — это нажатие кнопки + if(event.type == EventTypeInput) { + process_input_event(tracker, &event); + } + + if(event.type == EventTypeSaveSong) { + save_song(tracker, tracker->filepath); + } + + if(event.type == EventTypeSaveInstrument) { + save_instrument(tracker, tracker->filepath); + } + + if(event.type == EventTypeLoadSong) { + stop_song(tracker); + + tracker->tracker_engine.sequence_position = tracker->tracker_engine.pattern_position = + tracker->current_instrument = 0; + + tracker->dialogs = furi_record_open(RECORD_DIALOGS); + tracker->is_loading = true; + + FuriString* path; + path = furi_string_alloc(); + furi_string_set(path, FLIZZER_TRACKER_FOLDER); + + DialogsFileBrowserOptions browser_options; + dialog_file_browser_set_basic_options( + &browser_options, SONG_FILE_EXT, &I_flizzer_tracker_module); + browser_options.base_path = FLIZZER_TRACKER_FOLDER; + browser_options.hide_ext = false; + + bool ret = dialog_file_browser_show(tracker->dialogs, path, path, &browser_options); + + furi_record_close(RECORD_DIALOGS); + + const char* cpath = furi_string_get_cstr(path); + + if(ret && strcmp(&cpath[strlen(cpath) - 4], SONG_FILE_EXT) == 0) { + bool result = load_song_util(tracker, path); + UNUSED(result); + } + + else { + furi_string_free(path); + tracker->is_loading = false; + } + } + + if(event.type == EventTypeLoadInstrument) { + stop_song(tracker); + + tracker->dialogs = furi_record_open(RECORD_DIALOGS); + tracker->is_loading_instrument = true; + + FuriString* path; + path = furi_string_alloc(); + furi_string_set(path, FLIZZER_TRACKER_INSTRUMENTS_FOLDER); + + DialogsFileBrowserOptions browser_options; + dialog_file_browser_set_basic_options( + &browser_options, INST_FILE_EXT, &I_flizzer_tracker_instrument); + browser_options.base_path = FLIZZER_TRACKER_FOLDER; + browser_options.hide_ext = false; + + bool ret = dialog_file_browser_show(tracker->dialogs, path, path, &browser_options); + + furi_record_close(RECORD_DIALOGS); + + const char* cpath = furi_string_get_cstr(path); + + if(ret && strcmp(&cpath[strlen(cpath) - 4], INST_FILE_EXT) == 0) { + bool result = load_instrument_util(tracker, path); + UNUSED(result); + } + + else { + furi_string_free(path); + tracker->is_loading = false; + } + } + + if(event.type == EventTypeSetAudioMode) { + sound_engine_PWM_timer_init(tracker->external_audio); + + tracker->sound_engine.external_audio_output = tracker->external_audio; + } + } + + stop(); + + save_config(tracker); + + deinit_tracker(tracker); + + return 0; +} diff --git a/applications/external/flizzer_tracker/flizzer_tracker.h b/applications/external/flizzer_tracker/flizzer_tracker.h new file mode 100644 index 0000000000..6ece14e20d --- /dev/null +++ b/applications/external/flizzer_tracker/flizzer_tracker.h @@ -0,0 +1,225 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "flizzer_tracker_hal.h" +#include "sound_engine/freqs.h" +#include "sound_engine/sound_engine_defs.h" +#include "sound_engine/sound_engine_filter.h" +#include "tracker_engine/tracker_engine_defs.h" + +#define FLIZZER_TRACKER_FOLDER STORAGE_APP_DATA_PATH_PREFIX +#define FLIZZER_TRACKER_INSTRUMENTS_FOLDER APP_DATA_PATH("instruments") +#define FILE_NAME_LEN 64 + +typedef enum { + EventTypeInput, + EventTypeSaveSong, + EventTypeLoadSong, + EventTypeLoadInstrument, + EventTypeSaveInstrument, + EventTypeSetAudioMode, +} EventType; + +typedef struct { + EventType type; + InputEvent input; + uint32_t period; +} FlizzerTrackerEvent; + +typedef enum { + PATTERN_VIEW, + INST_EDITOR_VIEW, + EXPORT_WAV_VIEW, +} TrackerMode; + +typedef enum { + EDIT_PATTERN, + EDIT_SEQUENCE, + EDIT_SONGINFO, + EDIT_INSTRUMENT, + EDIT_PROGRAM, +} TrackerFocus; + +typedef enum { + SI_PATTERNPOS, + SI_SEQUENCEPOS, + SI_SONGSPEED, + SI_SONGRATE, + SI_MASTERVOL, + + SI_SONGNAME, + SI_CURRENTINSTRUMENT, + SI_INSTRUMENTNAME, + /* ======== */ + SI_PARAMS, +} SongInfoParam; + +typedef enum { + INST_CURRENTINSTRUMENT, + INST_INSTRUMENTNAME, + + INST_CURRENT_NOTE, + INST_FINETUNE, + + INST_SLIDESPEED, + INST_SETPW, + INST_PW, + INST_SETCUTOFF, + + INST_WAVE_NOISE, + INST_WAVE_PULSE, + INST_WAVE_TRIANGLE, + INST_WAVE_SAWTOOTH, + INST_WAVE_NOISE_METAL, + INST_WAVE_SINE, + + INST_ATTACK, + INST_DECAY, + INST_SUSTAIN, + INST_RELEASE, + INST_VOLUME, + + INST_ENABLEFILTER, + INST_FILTERCUTOFF, + INST_FILTERRESONANCE, + + INST_FILTERTYPE, + INST_ENABLERINGMOD, + INST_RINGMODSRC, + INST_ENABLEHARDSYNC, + INST_HARDSYNCSRC, + + INST_RETRIGGERONSLIDE, + INST_ENABLEKEYSYNC, + + INST_ENABLEVIBRATO, + INST_VIBRATOSPEED, + INST_VIBRATODEPTH, + INST_VIBRATODELAY, + + INST_ENABLEPWM, + INST_PWMSPEED, + INST_PWMDEPTH, + INST_PWMDELAY, + + INST_PROGRESTART, + INST_PROGRAMEPERIOD, + /* ========= */ + INST_PARAMS, +} InstrumentParam; + +typedef struct { + View* view; + void* context; +} TrackerView; + +typedef enum { + VIEW_TRACKER, + VIEW_KEYBOARD, + VIEW_SUBMENU_PATTERN, + VIEW_SUBMENU_PATTERN_COPYPASTE, + VIEW_SUBMENU_INSTRUMENT, + VIEW_FILE_OVERWRITE, + VIEW_INSTRUMENT_FILE_OVERWRITE, + VIEW_SETTINGS, +} FlizzerTrackerViews; + +typedef enum { + SUBMENU_PATTERN_LOAD_SONG, + SUBMENU_PATTERN_SAVE_SONG, + SUBMENU_PATTERN_SETTINGS, + SUBMENU_PATTERN_HELP, + SUBMENU_PATTERN_EXIT, +} PatternSubmenuParams; + +typedef enum { + SUBMENU_PATTERN_COPYPASTE_COPY, + SUBMENU_PATTERN_COPYPASTE_PASTE, + SUBMENU_PATTERN_COPYPASTE_CUT, + SUBMENU_PATTERN_COPYPASTE_CLEAR, +} PatternCopypasteSubmenuParams; + +typedef enum { + SUBMENU_INSTRUMENT_LOAD, + SUBMENU_INSTRUMENT_SAVE, + SUBMENU_INSTRUMENT_EXIT, +} InstrumentSubmenuParams; + +typedef struct { + NotificationApp* notification; + FuriMessageQueue* event_queue; + Gui* gui; + TrackerView* tracker_view; + ViewDispatcher* view_dispatcher; + TextInput* text_input; + Storage* storage; + Stream* stream; + FuriString* filepath; + DialogsApp* dialogs; + Submenu* pattern_submenu; + Submenu* pattern_copypaste_submenu; + Submenu* instrument_submenu; + VariableItemList* settings_list; + Widget* overwrite_file_widget; + Widget* overwrite_instrument_file_widget; + char filename[FILE_NAME_LEN + 1]; + bool was_it_back_keypress; + uint32_t current_time; + uint32_t period; + + bool external_audio; + + SoundEngine sound_engine; + TrackerEngine tracker_engine; + + TrackerSong song; + + uint8_t selected_param; + + uint8_t mode, focus; + uint8_t patternx, current_channel, current_digit, program_position, current_program_step, + current_instrument, current_note, current_volume; + + uint8_t inst_editor_shift; + + int16_t source_pattern_index; + + bool editing; + bool was_editing; + + bool is_loading; + bool is_saving; + bool is_loading_instrument; + bool is_saving_instrument; + bool showing_help; + + bool cut_pattern; //if we need to clear the pattern we pasted from + + bool quit; + + char eq[2]; + char param[80]; + char value[10]; +} FlizzerTrackerApp; + +typedef struct { + FlizzerTrackerApp* tracker; +} TrackerViewModel; + +void draw_callback(Canvas* canvas, void* ctx); +bool input_callback(InputEvent* input_event, void* ctx); diff --git a/applications/external/flizzer_tracker/flizzer_tracker.png b/applications/external/flizzer_tracker/flizzer_tracker.png new file mode 100644 index 0000000000..f691ab6309 Binary files /dev/null and b/applications/external/flizzer_tracker/flizzer_tracker.png differ diff --git a/applications/external/flizzer_tracker/flizzer_tracker_hal.c b/applications/external/flizzer_tracker/flizzer_tracker_hal.c new file mode 100644 index 0000000000..a2f483f08e --- /dev/null +++ b/applications/external/flizzer_tracker/flizzer_tracker_hal.c @@ -0,0 +1,313 @@ +#include "flizzer_tracker_hal.h" +#include "flizzer_tracker.h" + +void sound_engine_dma_isr(void* ctx) { + SoundEngine* sound_engine = (SoundEngine*)ctx; + + // sound_engine->counter++; + + // half of transfer + if(LL_DMA_IsActiveFlag_HT1(DMA1)) { + LL_DMA_ClearFlag_HT1(DMA1); + // fill first half of buffer + uint16_t* audio_buffer = sound_engine->audio_buffer; + uint32_t audio_buffer_length = sound_engine->audio_buffer_size / 2; + sound_engine_fill_buffer(sound_engine, audio_buffer, audio_buffer_length); + } + + // transfer complete + if(LL_DMA_IsActiveFlag_TC1(DMA1)) { + LL_DMA_ClearFlag_TC1(DMA1); + // fill second half of buffer + uint32_t audio_buffer_length = sound_engine->audio_buffer_size / 2; + uint16_t* audio_buffer = &sound_engine->audio_buffer[audio_buffer_length]; + sound_engine_fill_buffer(sound_engine, audio_buffer, audio_buffer_length); + } +} + +void tracker_engine_timer_isr( + void* ctx) // the tracker engine interrupt is of higher priority than sound engine one so it can run at the middle of filling the buffer, thus allowing sample-accurate tight effect timing +{ + TrackerEngine* tracker_engine = (TrackerEngine*)ctx; + // tracker_engine->counter++; + + if(LL_TIM_IsActiveFlag_UPDATE(TRACKER_ENGINE_TIMER)) { + LL_TIM_ClearFlag_UPDATE(TRACKER_ENGINE_TIMER); + tracker_engine_advance_tick(tracker_engine); + } +} + +void sound_engine_PWM_timer_init(bool external_audio_output) // external audio on pin PA6 +{ + if(external_audio_output) { + /*if(furi_hal_speaker_is_mine()) { + furi_hal_speaker_release(); + }*/ + + //LL_TIM_DisableAllOutputs(SPEAKER_PWM_TIMER); + //LL_TIM_DisableCounter(SPEAKER_PWM_TIMER); + + if(!(furi_hal_speaker_is_mine())) { + if(furi_hal_speaker_acquire(1000)) { + LL_TIM_DisableAllOutputs(SPEAKER_PWM_TIMER); + LL_TIM_DisableCounter(SPEAKER_PWM_TIMER); + + LL_TIM_InitTypeDef TIM_InitStruct = {0}; + LL_TIM_OC_InitTypeDef TIM_OC_InitStruct = {0}; + + TIM_InitStruct.Prescaler = 0; + TIM_InitStruct.Autoreload = + 1023; // 10-bit PWM resolution at around 60 kHz PWM rate + TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP; + LL_TIM_Init(SPEAKER_PWM_TIMER, &TIM_InitStruct); + + TIM_OC_InitStruct.OCMode = LL_TIM_OCMODE_PWM1; + TIM_OC_InitStruct.OCState = LL_TIM_OCSTATE_ENABLE; + TIM_OC_InitStruct.CompareValue = 0; + LL_TIM_OC_Init(SPEAKER_PWM_TIMER, SPEAKER_PWM_TIMER_CHANNEL, &TIM_OC_InitStruct); + + SPEAKER_PWM_TIMER->CNT = 0; + + LL_TIM_EnableAllOutputs(SPEAKER_PWM_TIMER); + LL_TIM_EnableCounter(SPEAKER_PWM_TIMER); + } + } + + furi_hal_gpio_init(&gpio_ext_pa6, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + + LL_TIM_InitTypeDef TIM_InitStruct = {0}; + LL_TIM_OC_InitTypeDef TIM_OC_InitStruct = {0}; + + TIM_InitStruct.Prescaler = 0; + TIM_InitStruct.Autoreload = 1023; // 10-bit PWM resolution at around 60 kHz PWM rate + TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP; + LL_TIM_Init(SPEAKER_PWM_TIMER, &TIM_InitStruct); + + TIM_OC_InitStruct.OCMode = LL_TIM_OCMODE_PWM1; + TIM_OC_InitStruct.OCState = LL_TIM_OCSTATE_ENABLE; + TIM_OC_InitStruct.CompareValue = 0; + LL_TIM_OC_Init(SPEAKER_PWM_TIMER, SPEAKER_PWM_TIMER_CHANNEL, &TIM_OC_InitStruct); + + SPEAKER_PWM_TIMER->CNT = 0; + + LL_TIM_EnableAllOutputs(SPEAKER_PWM_TIMER); + LL_TIM_EnableCounter(SPEAKER_PWM_TIMER); + + furi_hal_gpio_init(&gpio_ext_pa6, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + //furi_hal_gpio_init_ex(&gpio_ext_pa6, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedLow, GpioAltFn14TIM16); + } + + else { + if(!(furi_hal_speaker_is_mine())) { + if(furi_hal_speaker_acquire(1000)) { + LL_TIM_DisableAllOutputs(SPEAKER_PWM_TIMER); + LL_TIM_DisableCounter(SPEAKER_PWM_TIMER); + + LL_TIM_InitTypeDef TIM_InitStruct = {0}; + LL_TIM_OC_InitTypeDef TIM_OC_InitStruct = {0}; + + TIM_InitStruct.Prescaler = 0; + TIM_InitStruct.Autoreload = + 1023; // 10-bit PWM resolution at around 60 kHz PWM rate + TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP; + LL_TIM_Init(SPEAKER_PWM_TIMER, &TIM_InitStruct); + + TIM_OC_InitStruct.OCMode = LL_TIM_OCMODE_PWM1; + TIM_OC_InitStruct.OCState = LL_TIM_OCSTATE_ENABLE; + TIM_OC_InitStruct.CompareValue = 0; + LL_TIM_OC_Init(SPEAKER_PWM_TIMER, SPEAKER_PWM_TIMER_CHANNEL, &TIM_OC_InitStruct); + + SPEAKER_PWM_TIMER->CNT = 0; + + LL_TIM_EnableAllOutputs(SPEAKER_PWM_TIMER); + LL_TIM_EnableCounter(SPEAKER_PWM_TIMER); + } + } + + furi_hal_gpio_init(&gpio_ext_pa6, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + //furi_hal_gpio_init_ex(&gpio_ext_pa6, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedLow, GpioAltFn14TIM16); + } + + furi_hal_gpio_init_ex( + &gpio_ext_pa6, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedLow, GpioAltFn14TIM16); +} + +void sound_engine_set_audio_output(bool external_audio_output) { + if(external_audio_output) { + furi_hal_gpio_init_ex( + &gpio_ext_pa6, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedLow, GpioAltFn14TIM16); + + if(furi_hal_speaker_is_mine()) { + furi_hal_speaker_release(); + } + } + + else { + if(!(furi_hal_speaker_is_mine())) { + bool unu = furi_hal_speaker_acquire(1000); + UNUSED(unu); + } + + furi_hal_gpio_init(&gpio_ext_pa6, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + } +} + +void sound_engine_timer_init(uint32_t sample_rate) // external audio on pin PA6 +{ + if(!furi_hal_bus_is_enabled(FuriHalBusTIM1)) { + furi_hal_bus_enable(FuriHalBusTIM1); + } + + LL_TIM_InitTypeDef TIM_InitStruct = {0}; + LL_TIM_OC_InitTypeDef TIM_OC_InitStruct = {0}; + + TIM_InitStruct.Prescaler = 0; + TIM_InitStruct.Autoreload = + TIMER_BASE_CLOCK / sample_rate - 1; // to support various sample rates + TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP; + LL_TIM_Init(SAMPLE_RATE_TIMER, &TIM_InitStruct); + + TIM_OC_InitStruct.OCMode = LL_TIM_OCMODE_PWM1; + TIM_OC_InitStruct.OCState = LL_TIM_OCSTATE_ENABLE; + LL_TIM_OC_Init(SAMPLE_RATE_TIMER, SPEAKER_PWM_TIMER_CHANNEL, &TIM_OC_InitStruct); + + LL_TIM_EnableAllOutputs(SAMPLE_RATE_TIMER); + + SAMPLE_RATE_TIMER->CNT = 0; +} + +void tracker_engine_timer_init(uint8_t rate) // 0-255 hz +{ + if(!furi_hal_bus_is_enabled(FuriHalBusTIM2)) { + furi_hal_bus_enable(FuriHalBusTIM2); + } + + LL_TIM_InitTypeDef TIM_InitStruct = {0}; + LL_TIM_OC_InitTypeDef TIM_OC_InitStruct = {0}; + + TIM_InitStruct.Prescaler = 0; // using 32-bit timer + TIM_InitStruct.Autoreload = + (uint32_t)TIMER_BASE_CLOCK / (uint32_t)rate - 1; // to support various tracker engine rates + TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP; + LL_TIM_Init(TRACKER_ENGINE_TIMER, &TIM_InitStruct); + + TIM_OC_InitStruct.OCMode = LL_TIM_OCMODE_PWM1; + TIM_OC_InitStruct.OCState = LL_TIM_OCSTATE_ENABLE; + LL_TIM_OC_Init(TRACKER_ENGINE_TIMER, SPEAKER_PWM_TIMER_CHANNEL, &TIM_OC_InitStruct); + + LL_TIM_EnableIT_UPDATE(TRACKER_ENGINE_TIMER); + + TRACKER_ENGINE_TIMER->CNT = 0; +} + +void tracker_engine_set_rate(uint8_t rate) { + if(!furi_hal_bus_is_enabled(FuriHalBusTIM2)) { + furi_hal_bus_enable(FuriHalBusTIM2); + } + + LL_TIM_InitTypeDef TIM_InitStruct = {0}; + + TIM_InitStruct.Prescaler = 0; // using 32-bit timer + TIM_InitStruct.Autoreload = + (uint32_t)TIMER_BASE_CLOCK / (uint32_t)rate - 1; // to support various tracker engine rates + TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP; + LL_TIM_Init(TRACKER_ENGINE_TIMER, &TIM_InitStruct); + + TRACKER_ENGINE_TIMER->CNT = 0; +} + +void tracker_engine_init_hardware(uint8_t rate) { + tracker_engine_timer_init(rate); +} + +void sound_engine_dma_init(uint32_t address, uint32_t size) { + uint32_t dma_dst = (uint32_t) & (SPEAKER_PWM_TIMER->CCR1); + + LL_DMA_ConfigAddresses(DMA_INSTANCE, address, dma_dst, LL_DMA_DIRECTION_MEMORY_TO_PERIPH); + LL_DMA_SetDataLength(DMA_INSTANCE, size); + + LL_DMA_SetPeriphRequest(DMA_INSTANCE, LL_DMAMUX_REQ_TIM1_UP); + LL_DMA_SetDataTransferDirection(DMA_INSTANCE, LL_DMA_DIRECTION_MEMORY_TO_PERIPH); + LL_DMA_SetChannelPriorityLevel(DMA_INSTANCE, LL_DMA_PRIORITY_VERYHIGH); + LL_DMA_SetMode(DMA_INSTANCE, LL_DMA_MODE_CIRCULAR); + LL_DMA_SetPeriphIncMode(DMA_INSTANCE, LL_DMA_PERIPH_NOINCREMENT); + LL_DMA_SetMemoryIncMode(DMA_INSTANCE, LL_DMA_MEMORY_INCREMENT); + LL_DMA_SetPeriphSize(DMA_INSTANCE, LL_DMA_PDATAALIGN_HALFWORD); + LL_DMA_SetMemorySize(DMA_INSTANCE, LL_DMA_MDATAALIGN_HALFWORD); + + LL_DMA_EnableIT_TC(DMA_INSTANCE); + LL_DMA_EnableIT_HT(DMA_INSTANCE); +} + +void sound_engine_init_hardware( + uint32_t sample_rate, + bool external_audio_output, + uint16_t* audio_buffer, + uint32_t audio_buffer_size) { + sound_engine_dma_init((uint32_t)audio_buffer, audio_buffer_size); + sound_engine_timer_init(sample_rate); + sound_engine_PWM_timer_init(external_audio_output); +} + +void sound_engine_dma_start() { + LL_DMA_EnableChannel(DMA_INSTANCE); + LL_TIM_EnableDMAReq_UPDATE(SAMPLE_RATE_TIMER); +} + +void sound_engine_dma_stop() { + LL_DMA_DisableChannel(DMA_INSTANCE); +} + +void sound_engine_start() { + SAMPLE_RATE_TIMER->CNT = 0; + LL_TIM_EnableCounter(SAMPLE_RATE_TIMER); + + sound_engine_dma_start(); +} + +void sound_engine_stop() { + LL_TIM_DisableAllOutputs(SAMPLE_RATE_TIMER); + LL_TIM_DisableCounter(SAMPLE_RATE_TIMER); + + sound_engine_dma_stop(); +} + +void sound_engine_deinit_timer() { + LL_TIM_DisableAllOutputs(SAMPLE_RATE_TIMER); + LL_TIM_DisableAllOutputs(SPEAKER_PWM_TIMER); + + LL_TIM_DisableCounter(SPEAKER_PWM_TIMER); + + if(furi_hal_speaker_is_mine()) { + furi_hal_speaker_release(); + } + + if(furi_hal_bus_is_enabled(FuriHalBusTIM2)) { + furi_hal_bus_disable(FuriHalBusTIM2); + } + if(furi_hal_bus_is_enabled(FuriHalBusTIM1)) { + furi_hal_bus_disable(FuriHalBusTIM1); + } +} + +void tracker_engine_start() { + TRACKER_ENGINE_TIMER->CNT = 0; + + LL_TIM_EnableAllOutputs(TRACKER_ENGINE_TIMER); + LL_TIM_EnableCounter(TRACKER_ENGINE_TIMER); +} + +void tracker_engine_stop() { + LL_TIM_DisableAllOutputs(TRACKER_ENGINE_TIMER); + LL_TIM_DisableCounter(TRACKER_ENGINE_TIMER); +} + +void play() { + tracker_engine_start(); + sound_engine_start(); +} + +void stop() { + sound_engine_stop(); + tracker_engine_stop(); +} diff --git a/applications/external/flizzer_tracker/flizzer_tracker_hal.h b/applications/external/flizzer_tracker/flizzer_tracker_hal.h new file mode 100644 index 0000000000..e99e10c3c7 --- /dev/null +++ b/applications/external/flizzer_tracker/flizzer_tracker_hal.h @@ -0,0 +1,43 @@ +#pragma once + +#include "sound_engine/sound_engine.h" +#include "tracker_engine/tracker_engine.h" + +#include +#include +#include + +#include +#include +#include + +#define SPEAKER_PWM_TIMER TIM16 +#define SAMPLE_RATE_TIMER TIM1 +#define TRACKER_ENGINE_TIMER TIM2 + +#define SPEAKER_PWM_TIMER_CHANNEL LL_TIM_CHANNEL_CH1 + +#define TIMER_BASE_CLOCK 64000000 /* CPU frequency, 64 MHz */ + +#define DMA_INSTANCE DMA1, LL_DMA_CHANNEL_1 + +void sound_engine_dma_isr(void* ctx); +void tracker_engine_timer_isr(void* ctx); +void sound_engine_init_hardware( + uint32_t sample_rate, + bool external_audio_output, + uint16_t* audio_buffer, + uint32_t audio_buffer_size); +void sound_engine_dma_init(uint32_t address, uint32_t size); +void sound_engine_PWM_timer_init(bool external_audio_output); +void sound_engine_set_audio_output(bool external_audio_output); +void tracker_engine_init_hardware(uint8_t rate); +void tracker_engine_timer_init(uint8_t rate); +void tracker_engine_set_rate(uint8_t rate); +void sound_engine_start(); +void sound_engine_stop(); +void stop(); +void play(); +void tracker_engine_stop(); +void sound_engine_deinit_timer(); +void tracker_engine_start(); \ No newline at end of file diff --git a/applications/external/flizzer_tracker/font.h b/applications/external/flizzer_tracker/font.h new file mode 100644 index 0000000000..bf2625d6b3 --- /dev/null +++ b/applications/external/flizzer_tracker/font.h @@ -0,0 +1,31 @@ +#include + +/* +Fontname: -Raccoon-Fixed4x6-Medium-R-Normal--6-60-75-75-P-40-ISO10646-1 +Copyright: +Glyphs: 95/203 +BBX Build Mode: 0 +*/ +// this is a modified version with dot and semicolon moved 1 pixel to the left; lowercase symbols removed to save space +// changed "G", "N" and "V" glyphs +const uint8_t u8g2_font_tom_thumb_4x6_tr[610] = + "a\0\2\2\2\3\2\3\4\3\5\0\0\5\0\5\0\1`\0\0\2E\0\4@\62\1\4@\62\2" + "\4@\62\3\4@\62\4\4@\62\5\4@\62\6\4@\62\7\4@\62\10\4@\62\11\4@\62\12" + "\4@\62\13\4@\62\14\4@\62\15\4@\62\16\4@\62\17\4@\62\20\4@\62\21\4@\62\22" + "\4@\62\23\4@\62\24\4@\62\25\4@\62\26\4@\62\27\4@\62\30\4@\62\31\4@\62\32" + "\4@\62\33\4@\62\34\4@\62\35\4@\62\36\4@\62\37\4@\62 \4@\62!\5u\62+" + "\42\6\313\63I\5#\10W\62i\250\241\2$\10Wr#\216\230\0%\10W\62\31\265Q\0&\10" + "W\62J\215\224\4'\5\351\63\2(\6vr\252\14)\7V\62\61%\5*\6O\63\251\3+\7" + "\317ri%\0,\5Jr\12-\5G\63\3.\5E\62\1/\7W\262U\31\1\60\7Wr\313" + "Z\0\61\6Vr\253\1\62\7W\62\32\244r\63\11W\62\32\244\14\26\0\64\7W\62I\215X\65" + "\10W\62#j\260\0\66\7Wrs\244\21\67\7W\62\63\225\21\70\10W\62#\15\65\2\71\10W" + "\62#\215\270\0:\5\315\62);\7Rr\31(\0<\10W\262\251\6\31\4=\6\317\62\33\14>" + "\11W\62\31d\220J\0\77\10W\62\63e\230\0@\7Wr\325\320@A\7Wr\325P*B\10" + "W\62*\255\264\0C\7Wr\263\6\2D\7W\62*Y\13E\7W\62#\216\70F\10W\62#" + "\216\30\1G\7Wr\63\251$H\10W\62I\15\245\2I\7W\62+V\3J\7W\262\245\252\0" + "K\10W\62I\255\244\2L\6W\62\261\71M\10W\62i\14\245\2N\7W\62*\271\2O\7W" + "r\225U\1P\10W\62*\255\30\1Q\7Wr\225\32IR\7W\62*\215US\10Wr\33d" + "\260\0T\7W\62+\266\0U\7W\62\311\225\4V\7W\62\311U\1W\10W\62I\215\241\2X" + "\10W\62I\265T\0Y\10W\62I\225\25\0Z\7W\62\63\225\3[\7W\62#\226\3\134\7\317" + "\62\31d\20]\7W\62\263\34\1^\5\313s\15_\5G\62\3`\5\312\63\61\0\0\0\4\377\377" + "\0"; \ No newline at end of file diff --git a/applications/external/flizzer_tracker/images/channel_off.png b/applications/external/flizzer_tracker/images/channel_off.png new file mode 100644 index 0000000000..e4b283ae9f Binary files /dev/null and b/applications/external/flizzer_tracker/images/channel_off.png differ diff --git a/applications/external/flizzer_tracker/images/channel_on.png b/applications/external/flizzer_tracker/images/channel_on.png new file mode 100644 index 0000000000..f1473e7972 Binary files /dev/null and b/applications/external/flizzer_tracker/images/channel_on.png differ diff --git a/applications/external/flizzer_tracker/images/checkbox_checked.png b/applications/external/flizzer_tracker/images/checkbox_checked.png new file mode 100644 index 0000000000..24dfc492a4 Binary files /dev/null and b/applications/external/flizzer_tracker/images/checkbox_checked.png differ diff --git a/applications/external/flizzer_tracker/images/checkbox_empty.png b/applications/external/flizzer_tracker/images/checkbox_empty.png new file mode 100644 index 0000000000..36a517a44a Binary files /dev/null and b/applications/external/flizzer_tracker/images/checkbox_empty.png differ diff --git a/applications/external/flizzer_tracker/images/flizzer_tracker_instrument.png b/applications/external/flizzer_tracker/images/flizzer_tracker_instrument.png new file mode 100644 index 0000000000..d2b9db8cad Binary files /dev/null and b/applications/external/flizzer_tracker/images/flizzer_tracker_instrument.png differ diff --git a/applications/external/flizzer_tracker/images/flizzer_tracker_module.png b/applications/external/flizzer_tracker/images/flizzer_tracker_module.png new file mode 100644 index 0000000000..57899a4ec9 Binary files /dev/null and b/applications/external/flizzer_tracker/images/flizzer_tracker_module.png differ diff --git a/applications/external/flizzer_tracker/images/help.png b/applications/external/flizzer_tracker/images/help.png new file mode 100644 index 0000000000..ca90b07134 Binary files /dev/null and b/applications/external/flizzer_tracker/images/help.png differ diff --git a/applications/external/flizzer_tracker/images/note_release.png b/applications/external/flizzer_tracker/images/note_release.png new file mode 100644 index 0000000000..de4fc9f209 Binary files /dev/null and b/applications/external/flizzer_tracker/images/note_release.png differ diff --git a/applications/external/flizzer_tracker/init_deinit.c b/applications/external/flizzer_tracker/init_deinit.c new file mode 100644 index 0000000000..33e42fe367 --- /dev/null +++ b/applications/external/flizzer_tracker/init_deinit.c @@ -0,0 +1,298 @@ +#include "init_deinit.h" +#include "input_event.h" + +#include "diskop.h" + +#define AUDIO_MODES_COUNT 2 + +TrackerView* tracker_view_alloc(FlizzerTrackerApp* tracker) { + TrackerView* tracker_view = malloc(sizeof(TrackerView)); + tracker_view->view = view_alloc(); + tracker_view->context = tracker; + view_set_context(tracker_view->view, tracker_view); + view_allocate_model(tracker_view->view, ViewModelTypeLocking, sizeof(TrackerViewModel)); + view_set_draw_callback(tracker_view->view, draw_callback); + view_set_input_callback(tracker_view->view, input_callback); + + return tracker_view; +} + +void tracker_view_free(TrackerView* tracker_view) { + furi_assert(tracker_view); + view_free(tracker_view->view); + free(tracker_view); +} + +uint8_t my_value_index_bool( + const bool value, + const bool values[], + uint8_t + values_count) // why the fuck it gives unresolved symbol if I include it from toolbox???!!! +{ + uint8_t index = 0; + + for(uint8_t i = 0; i < values_count; i++) { + if(value == values[i]) { + index = i; + break; + } + } + + return index; +} + +FlizzerTrackerApp* init_tracker( + uint32_t sample_rate, + uint8_t rate, + bool external_audio_output, + uint32_t audio_buffer_size) { + FlizzerTrackerApp* tracker = malloc(sizeof(FlizzerTrackerApp)); + memset(tracker, 0, sizeof(FlizzerTrackerApp)); + + tracker->external_audio = external_audio_output; + + sound_engine_init( + &tracker->sound_engine, sample_rate, external_audio_output, audio_buffer_size); + tracker_engine_init(&tracker->tracker_engine, rate, &tracker->sound_engine); + + tracker->tracker_engine.song = &tracker->song; + + tracker->current_note = MIDDLE_C; + + // Очередь событий на 8 элементов размера FlizzerTrackerEvent + tracker->event_queue = furi_message_queue_alloc(8, sizeof(FlizzerTrackerEvent)); + + tracker->gui = furi_record_open(RECORD_GUI); + tracker->view_dispatcher = view_dispatcher_alloc(); + + tracker->tracker_view = tracker_view_alloc(tracker); + + view_dispatcher_add_view(tracker->view_dispatcher, VIEW_TRACKER, tracker->tracker_view->view); + view_dispatcher_attach_to_gui( + tracker->view_dispatcher, tracker->gui, ViewDispatcherTypeFullscreen); + + with_view_model( + tracker->tracker_view->view, TrackerViewModel * model, { model->tracker = tracker; }, true); + + tracker->storage = furi_record_open(RECORD_STORAGE); + tracker->stream = file_stream_alloc(tracker->storage); + + tracker->text_input = text_input_alloc(); + view_dispatcher_add_view( + tracker->view_dispatcher, VIEW_KEYBOARD, text_input_get_view(tracker->text_input)); + + tracker->pattern_submenu = submenu_alloc(); + tracker->pattern_copypaste_submenu = submenu_alloc(); + tracker->instrument_submenu = submenu_alloc(); + + view_set_previous_callback(submenu_get_view(tracker->pattern_submenu), submenu_exit_callback); + view_set_previous_callback( + submenu_get_view(tracker->pattern_copypaste_submenu), submenu_exit_callback); + view_set_previous_callback( + submenu_get_view(tracker->instrument_submenu), submenu_exit_callback); + + submenu_add_item( + tracker->pattern_submenu, + "Load song", + SUBMENU_PATTERN_LOAD_SONG, + submenu_callback, + tracker); + submenu_add_item( + tracker->pattern_submenu, + "Save song", + SUBMENU_PATTERN_SAVE_SONG, + submenu_callback, + tracker); + submenu_add_item( + tracker->pattern_submenu, "Settings", SUBMENU_PATTERN_SETTINGS, submenu_callback, tracker); + submenu_add_item( + tracker->pattern_submenu, "Help", SUBMENU_PATTERN_HELP, submenu_callback, tracker); + submenu_add_item( + tracker->pattern_submenu, "Exit", SUBMENU_PATTERN_EXIT, submenu_callback, tracker); + + submenu_add_item( + tracker->instrument_submenu, + "Load instrument", + SUBMENU_INSTRUMENT_LOAD, + submenu_callback, + tracker); + submenu_add_item( + tracker->instrument_submenu, + "Save instrument", + SUBMENU_INSTRUMENT_SAVE, + submenu_callback, + tracker); + submenu_add_item( + tracker->instrument_submenu, "Exit", SUBMENU_INSTRUMENT_EXIT, submenu_callback, tracker); + + submenu_add_item( + tracker->pattern_copypaste_submenu, + "Copy", + SUBMENU_PATTERN_COPYPASTE_COPY, + submenu_copypaste_callback, + tracker); + submenu_add_item( + tracker->pattern_copypaste_submenu, + "Paste", + SUBMENU_PATTERN_COPYPASTE_PASTE, + submenu_copypaste_callback, + tracker); + submenu_add_item( + tracker->pattern_copypaste_submenu, + "Cut", + SUBMENU_PATTERN_COPYPASTE_CUT, + submenu_copypaste_callback, + tracker); + submenu_add_item( + tracker->pattern_copypaste_submenu, + "Clear", + SUBMENU_PATTERN_COPYPASTE_CLEAR, + submenu_copypaste_callback, + tracker); + + view_dispatcher_add_view( + tracker->view_dispatcher, + VIEW_SUBMENU_PATTERN, + submenu_get_view(tracker->pattern_submenu)); + view_dispatcher_add_view( + tracker->view_dispatcher, + VIEW_SUBMENU_PATTERN_COPYPASTE, + submenu_get_view(tracker->pattern_copypaste_submenu)); + view_dispatcher_add_view( + tracker->view_dispatcher, + VIEW_SUBMENU_INSTRUMENT, + submenu_get_view(tracker->instrument_submenu)); + + load_config(tracker); + + tracker->settings_list = variable_item_list_alloc(); + View* view = variable_item_list_get_view(tracker->settings_list); + view_set_previous_callback(view, submenu_settings_exit_callback); + + VariableItem* item; + uint8_t value_index; + + item = variable_item_list_add( + tracker->settings_list, + "Audio output", + AUDIO_MODES_COUNT, + audio_output_changed_callback, + tracker); + value_index = + my_value_index_bool(tracker->external_audio, audio_modes_values, AUDIO_MODES_COUNT); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, audio_modes_text[value_index]); + + view_dispatcher_add_view(tracker->view_dispatcher, VIEW_SETTINGS, view); + + tracker->overwrite_file_widget = widget_alloc(); + + widget_add_button_element( + tracker->overwrite_file_widget, + GuiButtonTypeLeft, + "No", + (ButtonCallback)overwrite_file_widget_no_input_callback, + tracker); + widget_add_button_element( + tracker->overwrite_file_widget, + GuiButtonTypeRight, + "Yes", + (ButtonCallback)overwrite_file_widget_yes_input_callback, + tracker); + + widget_add_text_scroll_element( + tracker->overwrite_file_widget, + 0, + 0, + 128, + 64, + "This song file already exists,\n do you want to overwrite it?"); + + view_dispatcher_add_view( + tracker->view_dispatcher, + VIEW_FILE_OVERWRITE, + widget_get_view(tracker->overwrite_file_widget)); + + tracker->overwrite_instrument_file_widget = widget_alloc(); + + widget_add_button_element( + tracker->overwrite_instrument_file_widget, + GuiButtonTypeLeft, + "No", + (ButtonCallback)overwrite_instrument_file_widget_no_input_callback, + tracker); + widget_add_button_element( + tracker->overwrite_instrument_file_widget, + GuiButtonTypeRight, + "Yes", + (ButtonCallback)overwrite_instrument_file_widget_yes_input_callback, + tracker); + + widget_add_text_scroll_element( + tracker->overwrite_instrument_file_widget, + 0, + 0, + 128, + 64, + "This instrument file already\nexists, do you want to\noverwrite it?"); + + view_dispatcher_add_view( + tracker->view_dispatcher, + VIEW_INSTRUMENT_FILE_OVERWRITE, + widget_get_view(tracker->overwrite_instrument_file_widget)); + + tracker->notification = furi_record_open(RECORD_NOTIFICATION); + notification_message(tracker->notification, &sequence_display_backlight_enforce_on); + + set_default_song(tracker); + + tracker->focus = EDIT_SONGINFO; + tracker->source_pattern_index = -1; + + return tracker; +} + +void deinit_tracker(FlizzerTrackerApp* tracker) { + notification_message(tracker->notification, &sequence_display_backlight_enforce_auto); + furi_record_close(RECORD_NOTIFICATION); + + // Специальная очистка памяти, занимаемой очередью + furi_message_queue_free(tracker->event_queue); + + view_dispatcher_remove_view(tracker->view_dispatcher, VIEW_SETTINGS); + view_dispatcher_remove_view(tracker->view_dispatcher, VIEW_FILE_OVERWRITE); + view_dispatcher_remove_view(tracker->view_dispatcher, VIEW_SUBMENU_INSTRUMENT); + view_dispatcher_remove_view(tracker->view_dispatcher, VIEW_SUBMENU_PATTERN_COPYPASTE); + view_dispatcher_remove_view(tracker->view_dispatcher, VIEW_SUBMENU_PATTERN); + view_dispatcher_remove_view(tracker->view_dispatcher, VIEW_KEYBOARD); + view_dispatcher_remove_view(tracker->view_dispatcher, VIEW_TRACKER); + + text_input_free(tracker->text_input); + + variable_item_list_free(tracker->settings_list); + + submenu_free(tracker->pattern_submenu); + submenu_free(tracker->pattern_copypaste_submenu); + submenu_free(tracker->instrument_submenu); + + widget_free(tracker->overwrite_file_widget); + widget_free(tracker->overwrite_instrument_file_widget); + + view_dispatcher_free(tracker->view_dispatcher); + + tracker_view_free(tracker->tracker_view); + furi_record_close(RECORD_GUI); + + stream_free(tracker->stream); + furi_record_close(RECORD_STORAGE); + + sound_engine_deinit(&tracker->sound_engine); + + if(tracker->tracker_engine.song == NULL) { + tracker_engine_set_song(&tracker->tracker_engine, &tracker->song); + } + + tracker_engine_deinit(&tracker->tracker_engine, false); + + free(tracker); +} \ No newline at end of file diff --git a/applications/external/flizzer_tracker/init_deinit.h b/applications/external/flizzer_tracker/init_deinit.h new file mode 100644 index 0000000000..ebc80f9be7 --- /dev/null +++ b/applications/external/flizzer_tracker/init_deinit.h @@ -0,0 +1,14 @@ +#pragma once + +#include "flizzer_tracker.h" +#include "flizzer_tracker_hal.h" + +extern bool audio_modes_values[]; +extern char* audio_modes_text[]; + +FlizzerTrackerApp* init_tracker( + uint32_t sample_rate, + uint8_t rate, + bool external_audio_output, + uint32_t audio_buffer_size); +void deinit_tracker(FlizzerTrackerApp* tracker); \ No newline at end of file diff --git a/applications/external/flizzer_tracker/input/instrument.c b/applications/external/flizzer_tracker/input/instrument.c new file mode 100644 index 0000000000..cb55ebbf7a --- /dev/null +++ b/applications/external/flizzer_tracker/input/instrument.c @@ -0,0 +1,536 @@ +#include "instrument.h" +#include "songinfo.h" + +void edit_instrument_param(FlizzerTrackerApp* tracker, uint8_t selected_param, int8_t delta) { + if(!(tracker->current_digit)) { + delta *= 16; + } + + Instrument* inst = tracker->song.instrument[tracker->current_instrument]; + + switch(selected_param) { + case INST_CURRENTINSTRUMENT: { + int16_t inst = tracker->current_instrument; + + int8_t inst_delta = delta > 0 ? 1 : -1; + + inst += inst_delta; + + clamp(inst, 0, 0, tracker->song.num_instruments); + + if(check_and_allocate_instrument(&tracker->song, (uint8_t)inst)) { + tracker->current_instrument = inst; + } + + break; + } + + case INST_INSTRUMENTNAME: { + text_input_set_header_text(tracker->text_input, "Instrument name:"); + text_input_set_result_callback( + tracker->text_input, + return_from_keyboard_callback, + tracker, + (char*)&inst->name, + MUS_INST_NAME_LEN + 1, + false); + + view_dispatcher_switch_to_view(tracker->view_dispatcher, VIEW_KEYBOARD); + break; + } + + case INST_CURRENT_NOTE: { + int8_t note_delta = 0; + + if(delta < 0) { + if(tracker->current_digit) { + note_delta = -12; + } + + else { + note_delta = -1; + } + } + + if(delta > 0) { + if(tracker->current_digit) { + note_delta = 12; + } + + else { + note_delta = 1; + } + } + + clamp(inst->base_note, note_delta, 0, MAX_NOTE); + + break; + } + + case INST_FINETUNE: { + int8_t fine_delta = 0; + + if(delta < 0) { + if(tracker->current_digit) { + fine_delta = -1; + } + + else { + fine_delta = -10; + } + } + + if(delta > 0) { + if(tracker->current_digit) { + fine_delta = 1; + } + + else { + fine_delta = 10; + } + } + + inst->finetune += fine_delta; + + break; + } + + case INST_SLIDESPEED: { + if((int16_t)inst->slide_speed + (int16_t)delta >= 0 && + (int16_t)inst->slide_speed + (int16_t)delta <= 0xff) { + inst->slide_speed += delta; + } + + break; + } + + case INST_SETPW: { + flipbit(inst->flags, TE_SET_PW); + break; + } + + case INST_PW: { + if((int16_t)inst->pw + (int16_t)delta >= 0 && (int16_t)inst->pw + (int16_t)delta <= 0xff) { + inst->pw += delta; + } + + break; + } + + case INST_SETCUTOFF: { + flipbit(inst->flags, TE_SET_CUTOFF); + break; + } + + case INST_WAVE_NOISE: { + flipbit(inst->waveform, SE_WAVEFORM_NOISE); + break; + } + + case INST_WAVE_PULSE: { + flipbit(inst->waveform, SE_WAVEFORM_PULSE); + break; + } + + case INST_WAVE_TRIANGLE: { + flipbit(inst->waveform, SE_WAVEFORM_TRIANGLE); + break; + } + + case INST_WAVE_SAWTOOTH: { + flipbit(inst->waveform, SE_WAVEFORM_SAW); + break; + } + + case INST_WAVE_NOISE_METAL: { + flipbit(inst->waveform, SE_WAVEFORM_NOISE_METAL); + break; + } + + case INST_WAVE_SINE: { + flipbit(inst->waveform, SE_WAVEFORM_SINE); + break; + } + + case INST_ATTACK: { + if((int16_t)inst->adsr.a + (int16_t)delta >= 0 && + (int16_t)inst->adsr.a + (int16_t)delta <= 0xff) { + inst->adsr.a += delta; + } + + break; + } + + case INST_DECAY: { + if((int16_t)inst->adsr.d + (int16_t)delta >= 0 && + (int16_t)inst->adsr.d + (int16_t)delta <= 0xff) { + inst->adsr.d += delta; + } + + break; + } + + case INST_SUSTAIN: { + if((int16_t)inst->adsr.s + (int16_t)delta >= 0 && + (int16_t)inst->adsr.s + (int16_t)delta <= 0xff) { + inst->adsr.s += delta; + } + + break; + } + + case INST_RELEASE: { + if((int16_t)inst->adsr.r + (int16_t)delta >= 0 && + (int16_t)inst->adsr.r + (int16_t)delta <= 0xff) { + inst->adsr.r += delta; + } + + break; + } + + case INST_VOLUME: { + if((int16_t)inst->adsr.volume + (int16_t)delta >= 0 && + (int16_t)inst->adsr.volume + (int16_t)delta <= 0xff) { + inst->adsr.volume += delta; + } + + break; + } + + case INST_ENABLEFILTER: { + flipbit(inst->sound_engine_flags, SE_ENABLE_FILTER); + break; + } + + case INST_FILTERCUTOFF: { + if((int16_t)inst->filter_cutoff + (int16_t)delta >= 0 && + (int16_t)inst->filter_cutoff + (int16_t)delta <= 0xff) { + inst->filter_cutoff += delta; + } + + break; + } + + case INST_FILTERRESONANCE: { + if((int16_t)inst->filter_resonance + (int16_t)delta >= 0 && + (int16_t)inst->filter_resonance + (int16_t)delta <= 0xff) { + inst->filter_resonance += delta; + } + + break; + } + + case INST_FILTERTYPE: { + int8_t flt_delta = (delta > 0 ? 1 : -1); + + if((int16_t)inst->filter_type + (int16_t)flt_delta >= 0 && + (int16_t)inst->filter_type + (int16_t)flt_delta < FIL_MODES) { + inst->filter_type += flt_delta; + } + + break; + } + + case INST_ENABLERINGMOD: { + flipbit(inst->sound_engine_flags, SE_ENABLE_RING_MOD); + break; + } + + case INST_RINGMODSRC: { + if((int16_t)inst->ring_mod + (int16_t)delta >= 0 && + (int16_t)inst->ring_mod + (int16_t)delta < SONG_MAX_CHANNELS) { + inst->ring_mod += delta; + } + + if((int16_t)inst->ring_mod + (int16_t)delta < 0) { + inst->ring_mod = 0xff; // 0xff = self + } + + if((int16_t)inst->ring_mod == 0xff && (int16_t)delta > 0) { + inst->ring_mod = 0; + } + + break; + } + + case INST_ENABLEHARDSYNC: { + flipbit(inst->sound_engine_flags, SE_ENABLE_HARD_SYNC); + break; + } + + case INST_HARDSYNCSRC: { + if((int16_t)inst->hard_sync + (int16_t)delta >= 0 && + (int16_t)inst->hard_sync + (int16_t)delta < SONG_MAX_CHANNELS) { + inst->hard_sync += delta; + } + + if((int16_t)inst->hard_sync + (int16_t)delta < 0) { + inst->hard_sync = 0xff; // 0xff = self + } + + if((int16_t)inst->hard_sync == 0xff && (int16_t)delta > 0) { + inst->hard_sync = 0; + } + + break; + } + + case INST_RETRIGGERONSLIDE: { + flipbit(inst->flags, TE_RETRIGGER_ON_SLIDE); + break; + } + + case INST_ENABLEKEYSYNC: { + flipbit(inst->sound_engine_flags, SE_ENABLE_KEYDOWN_SYNC); + break; + } + + case INST_ENABLEVIBRATO: { + flipbit(inst->flags, TE_ENABLE_VIBRATO); + break; + } + + case INST_VIBRATOSPEED: { + if((int16_t)inst->vibrato_speed + (int16_t)delta >= 0 && + (int16_t)inst->vibrato_speed + (int16_t)delta <= 0xff) { + inst->vibrato_speed += delta; + } + + break; + } + + case INST_VIBRATODEPTH: { + if((int16_t)inst->vibrato_depth + (int16_t)delta >= 0 && + (int16_t)inst->vibrato_depth + (int16_t)delta <= 0xff) { + inst->vibrato_depth += delta; + } + + break; + } + + case INST_VIBRATODELAY: { + if((int16_t)inst->vibrato_delay + (int16_t)delta >= 0 && + (int16_t)inst->vibrato_delay + (int16_t)delta <= 0xff) { + inst->vibrato_delay += delta; + } + + break; + } + + case INST_ENABLEPWM: { + flipbit(inst->flags, TE_ENABLE_PWM); + break; + } + + case INST_PWMSPEED: { + if((int16_t)inst->pwm_speed + (int16_t)delta >= 0 && + (int16_t)inst->pwm_speed + (int16_t)delta <= 0xff) { + inst->pwm_speed += delta; + } + + break; + } + + case INST_PWMDEPTH: { + if((int16_t)inst->pwm_depth + (int16_t)delta >= 0 && + (int16_t)inst->pwm_depth + (int16_t)delta <= 0xff) { + inst->pwm_depth += delta; + } + + break; + } + + case INST_PWMDELAY: { + if((int16_t)inst->pwm_delay + (int16_t)delta >= 0 && + (int16_t)inst->pwm_delay + (int16_t)delta <= 0xff) { + inst->pwm_delay += delta; + } + + break; + } + + case INST_PROGRESTART: { + flipbit(inst->flags, TE_PROG_NO_RESTART); + break; + } + + case INST_PROGRAMEPERIOD: { + if((int16_t)inst->program_period + (int16_t)delta >= 0 && + (int16_t)inst->program_period + (int16_t)delta <= 0xff) { + inst->program_period += delta; + } + + break; + } + } +} + +void instrument_edit_event(FlizzerTrackerApp* tracker, FlizzerTrackerEvent* event) { + if(event->input.key == InputKeyOk && event->input.type == InputTypeShort && + !tracker->tracker_engine.playing) { + tracker->editing = !(tracker->editing); + return; + } + + if(event->input.key == InputKeyOk && event->input.type == InputTypeLong && !tracker->editing) { + reset_buffer(&tracker->sound_engine); + tracker_engine_set_song(&tracker->tracker_engine, NULL); + + for(int i = 1; i < SONG_MAX_CHANNELS; i++) { + tracker->tracker_engine.channel[i].channel_flags &= TEC_PLAYING; + tracker->tracker_engine.sound_engine->channel[i].frequency = 0; + tracker->tracker_engine.sound_engine->channel[i].waveform = 0; + } + + Instrument* inst = tracker->song.instrument[tracker->current_instrument]; + tracker_engine_trigger_instrument_internal( + &tracker->tracker_engine, 0, inst, (MIDDLE_C << 8)); + tracker->tracker_engine.playing = true; + play(); + return; + } + + if(event->input.key == InputKeyOk && event->input.type == InputTypeRelease && + !tracker->editing) { + SoundEngineChannel* se_channel = &tracker->sound_engine.channel[0]; + sound_engine_enable_gate(&tracker->sound_engine, se_channel, false); + return; + } + + if(event->input.key == InputKeyRight && event->input.type == InputTypeShort) { + switch(tracker->selected_param) { + default: { + tracker->current_digit++; + + if(tracker->current_digit > 1) { + tracker->selected_param++; + + tracker->current_digit = 0; + + if(tracker->selected_param > INST_PARAMS - 1) { + tracker->selected_param = 0; + } + } + + break; + } + + case INST_CURRENTINSTRUMENT: + case INST_INSTRUMENTNAME: + case INST_SETPW: + case INST_SETCUTOFF: + case INST_WAVE_NOISE: + case INST_WAVE_PULSE: + case INST_WAVE_TRIANGLE: + case INST_WAVE_SAWTOOTH: + case INST_WAVE_NOISE_METAL: + case INST_WAVE_SINE: + case INST_ENABLEFILTER: + case INST_FILTERTYPE: + case INST_ENABLERINGMOD: + case INST_RINGMODSRC: + case INST_ENABLEHARDSYNC: + case INST_HARDSYNCSRC: + case INST_RETRIGGERONSLIDE: + case INST_ENABLEKEYSYNC: + case INST_ENABLEVIBRATO: + case INST_ENABLEPWM: + case INST_PROGRESTART: { + tracker->selected_param++; + + tracker->current_digit = 1; + + if(tracker->selected_param > INST_PARAMS - 1) { + tracker->selected_param = 0; + } + + break; + } + } + } + + if(event->input.key == InputKeyLeft && event->input.type == InputTypeShort) { + switch(tracker->selected_param) { + default: { + tracker->current_digit--; + + if(tracker->current_digit > 1) // unsigned int overflow + { + tracker->selected_param--; + + tracker->current_digit = 1; + + if(tracker->selected_param > INST_PARAMS - 1) // unsigned int overflow + { + tracker->selected_param = INST_PARAMS - 1; + } + } + + break; + } + + case INST_CURRENTINSTRUMENT: + case INST_INSTRUMENTNAME: + case INST_SETPW: + case INST_SETCUTOFF: + case INST_WAVE_NOISE: + case INST_WAVE_PULSE: + case INST_WAVE_TRIANGLE: + case INST_WAVE_SAWTOOTH: + case INST_WAVE_NOISE_METAL: + case INST_WAVE_SINE: + case INST_ENABLEFILTER: + case INST_FILTERTYPE: + case INST_ENABLERINGMOD: + case INST_RINGMODSRC: + case INST_ENABLEHARDSYNC: + case INST_HARDSYNCSRC: + case INST_RETRIGGERONSLIDE: + case INST_ENABLEKEYSYNC: + case INST_ENABLEVIBRATO: + case INST_ENABLEPWM: + case INST_PROGRESTART: { + tracker->selected_param--; + + tracker->current_digit = 1; + + if(tracker->selected_param > INST_PARAMS - 1) // unsigned int overflow + { + tracker->selected_param = INST_PARAMS - 1; + } + + break; + } + } + + return; + } + + if(event->input.key == InputKeyDown && event->input.type == InputTypeShort) { + if(tracker->editing) { + edit_instrument_param(tracker, tracker->selected_param, -1); + } + + return; + } + + if(event->input.key == InputKeyUp && event->input.type == InputTypeShort) { + if(tracker->editing) { + edit_instrument_param(tracker, tracker->selected_param, 1); + } + + return; + } + + if(tracker->selected_param > INST_VIBRATODELAY) { + tracker->inst_editor_shift = 6; + } + + if(tracker->selected_param > INST_PWMDELAY) { + tracker->inst_editor_shift = 12; + } + + if(tracker->selected_param < INST_CURRENT_NOTE) { + tracker->inst_editor_shift = 0; + } +} \ No newline at end of file diff --git a/applications/external/flizzer_tracker/input/instrument.h b/applications/external/flizzer_tracker/input/instrument.h new file mode 100644 index 0000000000..af5d86fc01 --- /dev/null +++ b/applications/external/flizzer_tracker/input/instrument.h @@ -0,0 +1,12 @@ +#pragma once + +#include +#include +#include + +#include "../flizzer_tracker.h" +#include "../sound_engine/sound_engine_defs.h" +#include "../tracker_engine/tracker_engine_defs.h" +#include "../util.h" + +void instrument_edit_event(FlizzerTrackerApp* tracker, FlizzerTrackerEvent* event); \ No newline at end of file diff --git a/applications/external/flizzer_tracker/input/instrument_program.c b/applications/external/flizzer_tracker/input/instrument_program.c new file mode 100644 index 0000000000..5143ec3eb8 --- /dev/null +++ b/applications/external/flizzer_tracker/input/instrument_program.c @@ -0,0 +1,239 @@ +#include "instrument_program.h" +#include "../macros.h" + +void instrument_program_edit_event(FlizzerTrackerApp* tracker, FlizzerTrackerEvent* event) { + if(event->input.key == InputKeyOk && event->input.type == InputTypeShort) { + tracker->editing = !(tracker->editing); + return; + } + + if(event->input.key == InputKeyRight && event->input.type == InputTypeShort && + tracker->editing) { + tracker->current_digit = my_min(2, tracker->current_digit + 1); + return; + } + + if(event->input.key == InputKeyOk && event->input.type == InputTypeLong && tracker->editing) { + Instrument* inst = tracker->song.instrument[tracker->current_instrument]; + + if(tracker->current_program_step < INST_PROG_LEN - 1) { + if((inst->program[tracker->current_program_step] & 0x7fff) < TE_PROGRAM_LOOP_BEGIN && + ((inst->program[tracker->current_program_step + 1] & 0x7fff) < + TE_PROGRAM_LOOP_BEGIN || + (inst->program[tracker->current_program_step + 1] & 0x7f00) == + TE_PROGRAM_LOOP_END)) // so we can unite with loop end as in klystrack + { + inst->program[tracker->current_program_step] ^= 0x8000; // flipping unite bit + } + } + + return; + } + + if(event->input.key == InputKeyLeft && event->input.type == InputTypeShort && + tracker->editing) { + tracker->current_digit = fmax(0, (int16_t)tracker->current_digit - 1); + return; + } + + if(event->input.key == InputKeyBack && event->input.type == InputTypeShort && + tracker->editing) { + Instrument* inst = tracker->song.instrument[tracker->current_instrument]; + inst->program[tracker->current_program_step] = TE_PROGRAM_NOP; + } + + if(event->input.key == InputKeyUp && event->input.type == InputTypeShort) { + if(!(tracker->editing)) { + if((int16_t)tracker->current_program_step - 1 >= 0) { + tracker->current_program_step--; + + if(tracker->program_position > tracker->current_program_step) { + tracker->program_position = tracker->current_program_step; + } + } + + else { + tracker->current_program_step = INST_PROG_LEN - 1; + + tracker->program_position = INST_PROG_LEN - 1 - 7; + } + } + + if(tracker->editing) { + Instrument* inst = tracker->song.instrument[tracker->current_instrument]; + uint16_t opcode = inst->program[tracker->current_program_step]; + + switch(tracker->current_digit) { + case 0: // MSB + { + uint8_t param = ((opcode & 0x7f00) >> 8); + + if(param < 0xff) { + param++; + } + + if((inst->program[tracker->current_program_step] & 0x7fff) == TE_PROGRAM_NOP) { + param = 0; + inst->program[tracker->current_program_step] = 0; + } + + param &= 0x7f; + + inst->program[tracker->current_program_step] &= 0x80ff; + inst->program[tracker->current_program_step] |= ((uint16_t)param << 8); + + break; + } + + case 1: // upper digit of param, e.g. eXx + { + int8_t nibble = ((opcode & 0x00f0) >> 4); + + if(nibble + 1 <= 0xf) { + nibble++; + } + + else { + nibble = 0; + } + + inst->program[tracker->current_program_step] &= 0xff0f; + inst->program[tracker->current_program_step] |= (nibble << 4); + + break; + } + + case 2: // lower digit of param, e.g. exX + { + int8_t nibble = (opcode & 0x000f); + + if(nibble + 1 <= 0xf) { + nibble++; + } + + else { + nibble = 0; + } + + inst->program[tracker->current_program_step] &= 0xfff0; + inst->program[tracker->current_program_step] |= nibble; + + break; + } + + default: + break; + } + } + + return; + } + + if(event->input.key == InputKeyDown && event->input.type == InputTypeShort) { + if(!(tracker->editing)) { + if(tracker->current_program_step + 1 < INST_PROG_LEN) { + tracker->current_program_step++; + + if(tracker->program_position < tracker->current_program_step - 7) { + tracker->program_position = tracker->current_program_step - 7; + } + } + + else { + tracker->current_program_step = 0; + + tracker->program_position = 0; + } + } + + if(tracker->editing) { + Instrument* inst = tracker->song.instrument[tracker->current_instrument]; + uint16_t opcode = inst->program[tracker->current_program_step]; + + switch(tracker->current_digit) { + case 0: // MSB + { + uint8_t param = ((opcode & 0x7f00) >> 8); + + if(param < (TE_PROGRAM_JUMP >> 8) && param > 0) { + param--; + + inst->program[tracker->current_program_step] &= 0x80ff; + inst->program[tracker->current_program_step] |= ((uint16_t)param << 8); + } + + if((inst->program[tracker->current_program_step] & 0x7f00) == TE_PROGRAM_JUMP && + (inst->program[tracker->current_program_step] & 0x7fff) != TE_PROGRAM_END && + (inst->program[tracker->current_program_step] & 0x7fff) != TE_PROGRAM_NOP) { + inst->program[tracker->current_program_step] = + TE_PROGRAM_LOOP_END | + (inst->program[tracker->current_program_step] & 0x8000); + } + + if((inst->program[tracker->current_program_step] & 0x7fff) == TE_PROGRAM_END) { + // param = (TE_PROGRAM_JUMP >> 8); + inst->program[tracker->current_program_step] = + TE_PROGRAM_JUMP | (inst->program[tracker->current_program_step] & 0x8000); + } + + if((inst->program[tracker->current_program_step] & 0x7fff) == TE_PROGRAM_NOP) { + // param = (TE_PROGRAM_END >> 8); + inst->program[tracker->current_program_step] = + TE_PROGRAM_END | (inst->program[tracker->current_program_step] & 0x8000); + } + + if((inst->program[tracker->current_program_step] & 0x7f00) == + (TE_PROGRAM_LOOP_BEGIN - 0x100)) { + // param = (TE_PROGRAM_END >> 8); + inst->program[tracker->current_program_step] = + TE_EFFECT_TRIGGER_RELEASE | + (inst->program[tracker->current_program_step] & 0x8000); + } + + break; + } + + case 1: // upper digit of param, e.g. eXx + { + int8_t nibble = ((opcode & 0x00f0) >> 4); + + if(nibble - 1 >= 0) { + nibble--; + } + + else { + nibble = 0xf; + } + + inst->program[tracker->current_program_step] &= 0xff0f; + inst->program[tracker->current_program_step] |= (nibble << 4); + + break; + } + + case 2: // lower digit of param, e.g. exX + { + int8_t nibble = (opcode & 0x000f); + + if(nibble - 1 >= 0) { + nibble--; + } + + else { + nibble = 0xf; + } + + inst->program[tracker->current_program_step] &= 0xfff0; + inst->program[tracker->current_program_step] |= nibble; + + break; + } + + default: + break; + } + } + + return; + } +} \ No newline at end of file diff --git a/applications/external/flizzer_tracker/input/instrument_program.h b/applications/external/flizzer_tracker/input/instrument_program.h new file mode 100644 index 0000000000..88009406d5 --- /dev/null +++ b/applications/external/flizzer_tracker/input/instrument_program.h @@ -0,0 +1,12 @@ +#pragma once + +#include +#include +#include + +#include "../flizzer_tracker.h" +#include "../sound_engine/sound_engine_defs.h" +#include "../tracker_engine/tracker_engine_defs.h" +#include "../util.h" + +void instrument_program_edit_event(FlizzerTrackerApp* tracker, FlizzerTrackerEvent* event); \ No newline at end of file diff --git a/applications/external/flizzer_tracker/input/pattern.c b/applications/external/flizzer_tracker/input/pattern.c new file mode 100644 index 0000000000..43bfdca11f --- /dev/null +++ b/applications/external/flizzer_tracker/input/pattern.c @@ -0,0 +1,410 @@ +#include "pattern.h" + +uint8_t get_field(uint8_t patternx) { + uint8_t field = 0; + + if(patternx <= 1) field = 0; + if(patternx == 2) field = 1; + if(patternx == 3) field = 2; + if(patternx > 3) field = 3; + + return field; +} + +void edit_note( + FlizzerTrackerApp* tracker, + TrackerSongPatternStep* step, + int8_t delta) // here we need data about last note if we place a new note +{ + int16_t note = tracker_engine_get_note(step); + + if(note == MUS_NOTE_RELEASE) { + if(delta < 0) { + set_note(step, MUS_NOTE_CUT); + } + + return; + } + + if(note == MUS_NOTE_CUT) { + if(delta > 0) { + set_note(step, MUS_NOTE_RELEASE); + } + + return; + } + + if(note == MUS_NOTE_NONE) { + note = + tracker->current_note; // remember which note we entered earlier and use it as reference + } + + clamp(note, delta, 0, MAX_NOTE); + + set_note(step, (uint8_t)note); + set_instrument(step, tracker->current_instrument); + + tracker->current_note = (uint8_t)note; +} + +void edit_instrument(FlizzerTrackerApp* tracker, TrackerSongPatternStep* step, int8_t delta) { + int16_t inst = tracker_engine_get_instrument(step); + + if(inst == MUS_NOTE_INSTRUMENT_NONE) { + if(delta > 0) { + inst = tracker->current_instrument; + } + + else { + inst = MUS_NOTE_INSTRUMENT_NONE - 1; + } + } + + clamp(inst, delta, 0, tracker->song.num_instruments - 1); + tracker->current_instrument = inst; // remember last instrument + set_instrument(step, (uint8_t)inst); +} + +void edit_volume(FlizzerTrackerApp* tracker, TrackerSongPatternStep* step, int8_t delta) { + int16_t vol = tracker_engine_get_volume(step); + + vol = tracker->current_volume; + + if(vol + delta < 0) { + vol = MUS_NOTE_VOLUME_NONE - 1 - delta; + } + + if(vol + delta >= MUS_NOTE_VOLUME_NONE) { + vol = 0 - delta; + } + + clamp(vol, delta, 0, MUS_NOTE_VOLUME_NONE - 1); + + set_volume(step, (uint8_t)vol); + + tracker->current_volume = vol; +} + +void edit_command(TrackerSongPatternStep* step, uint8_t digit, int8_t delta) { + int32_t command = tracker_engine_get_command(step); + + switch(digit) { + case 0: // upper 7 bits + { + int16_t fx_name = ((command & 0x7f00) >> 8); + + if(fx_name + delta > 35) // loop + { // 0-9 and then A-Z + fx_name = 0; + } + + else if(fx_name + delta < 0) { + fx_name = 35; + } + + else { + fx_name += delta; + } + + command &= 0x00ff; + + command |= (fx_name << 8); + + set_command(step, (uint16_t)command); + + break; + } + + case 1: // upper digit of command param + { + int8_t upper_digit = ((command & 0x00f0) >> 4); + + if(upper_digit + delta > 0xf) // loop + { + upper_digit = 0; + } + + else if(upper_digit + delta < 0) { + upper_digit = 0xf; + } + + else { + upper_digit += delta; + } + + command &= 0xff0f; + + command |= (upper_digit << 4); + + set_command(step, (uint16_t)command); + + break; + } + + case 2: // lower digit of command param + { + int8_t lower_digit = (command & 0x000f); + + if(lower_digit + delta > 0xf) // loop + { + lower_digit = 0; + } + + else if(lower_digit + delta < 0) { + lower_digit = 0xf; + } + + else { + lower_digit += delta; + } + + command &= 0xfff0; + + command |= lower_digit; + + set_command(step, (uint16_t)command); + + break; + } + + default: + break; + } +} + +void delete_field(TrackerSongPatternStep* step, uint8_t field) { + switch(field) { + case 0: // note + { + set_note(step, MUS_NOTE_NONE); + set_instrument(step, MUS_NOTE_INSTRUMENT_NONE); // also delete instrument + break; + } + + case 1: // instrument + { + set_instrument(step, MUS_NOTE_INSTRUMENT_NONE); + break; + } + + case 2: // volume + { + set_volume(step, MUS_NOTE_VOLUME_NONE); + break; + } + + case 3: // command + { + set_command(step, 0); + break; + } + + default: + break; + } +} + +void edit_pattern_step(FlizzerTrackerApp* tracker, TrackerSongPatternStep* step, int8_t delta) { + switch(get_field(tracker->patternx)) { + case 0: // note + { + if(tracker->patternx) // editing octave + { + edit_note(tracker, step, 12 * delta); + } + + else // editing note + { + edit_note(tracker, step, delta); + } + + break; + } + + case 1: // instrument + { + edit_instrument(tracker, step, delta); + break; + } + + case 2: // volume + { + edit_volume(tracker, step, delta); + break; + } + + case 3: // command + { + uint8_t digit = 0; + if(tracker->patternx == 4) digit = 0; + if(tracker->patternx == 5) digit = 1; + if(tracker->patternx == 6) digit = 2; + edit_command(step, digit, delta); + break; + } + + default: + break; + } +} + +void pattern_edit_event(FlizzerTrackerApp* tracker, FlizzerTrackerEvent* event) { + if(event->input.key == InputKeyLeft && event->input.type == InputTypeLong && + !(tracker->editing)) { + flipbit( + tracker->tracker_engine.channel[tracker->current_channel].channel_flags, TEC_DISABLED); + return; + } + + if(event->input.key == InputKeyDown && event->input.type == InputTypeLong && + !(tracker->editing)) { + tracker->tracker_engine.pattern_position = + tracker->tracker_engine.song->pattern_length - 1; // go to pattern last row + return; + } + + if(event->input.key == InputKeyUp && event->input.type == InputTypeLong && + !(tracker->editing)) { + tracker->tracker_engine.pattern_position = 0; // return to pattern 1st row + return; + } + + uint8_t sequence_position = tracker->tracker_engine.sequence_position; + uint8_t current_pattern = + tracker->tracker_engine.song->sequence.sequence_step[sequence_position] + .pattern_indices[tracker->current_channel]; + uint16_t pattern_step = tracker->tracker_engine.pattern_position; + + uint16_t pattern_length = tracker->tracker_engine.song->pattern_length; + + TrackerSongPattern* pattern = &tracker->tracker_engine.song->pattern[current_pattern]; + + TrackerSongPatternStep* step = NULL; + + if(pattern_step < pattern_length) { + step = &pattern->step[pattern_step]; + } + + if(!(step)) return; + + if(event->input.key == InputKeyOk && event->input.type == InputTypeShort && + !tracker->tracker_engine.playing) { + tracker->editing = !tracker->editing; + + if(tracker->editing) { + // stop_song(tracker); + } + } + + if(event->input.key == InputKeyOk && event->input.type == InputTypeLong) { + if(!(tracker->editing)) { + if(tracker->tracker_engine.playing) { + stop_song(tracker); + } + + else { + if(tracker->tracker_engine.pattern_position == tracker->song.pattern_length - 1 && + tracker->tracker_engine.sequence_position == + tracker->song.num_sequence_steps - + 1) // if we are at the very end of the song + { + stop_song(tracker); + } + + else { + play_song(tracker, true); + } + } + } + + else { + if(get_field(tracker->patternx) == 0) { + set_note(step, MUS_NOTE_RELEASE); + } + } + } + + if(event->input.key == InputKeyRight && event->input.type == InputTypeShort) { + tracker->patternx++; + + if(tracker->patternx > MAX_PATTERNX - 1) { + tracker->current_channel++; + + tracker->patternx = 0; + + if(tracker->current_channel > SONG_MAX_CHANNELS - 1) { + tracker->current_channel = 0; + } + } + } + + if(event->input.key == InputKeyLeft && event->input.type == InputTypeShort) { + tracker->patternx--; + + if(tracker->patternx > MAX_PATTERNX - 1) // unsigned int overflow + { + tracker->current_channel--; + + tracker->patternx = MAX_PATTERNX - 1; + + if(tracker->current_channel > SONG_MAX_CHANNELS - 1) // unsigned int overflow + { + tracker->current_channel = SONG_MAX_CHANNELS - 1; + } + } + } + + if(event->input.key == InputKeyDown && event->input.type == InputTypeShort) { + if(!(tracker->editing)) { + tracker->tracker_engine.pattern_position++; + + if(tracker->tracker_engine.pattern_position > + tracker->tracker_engine.song->pattern_length - 1 && + tracker->tracker_engine.sequence_position < + tracker->tracker_engine.song->num_sequence_steps - 1) { + tracker->tracker_engine.pattern_position = 0; + tracker->tracker_engine.sequence_position++; + } + + else if( + tracker->tracker_engine.pattern_position > + tracker->tracker_engine.song->pattern_length - 1) { + tracker->tracker_engine.pattern_position = + tracker->tracker_engine.song->pattern_length - 1; + } + } + + if(tracker->editing) { + edit_pattern_step(tracker, step, -1); + } + } + + if(event->input.key == InputKeyUp && event->input.type == InputTypeShort) { + if(!(tracker->editing)) { + int16_t temp_pattern_position = tracker->tracker_engine.pattern_position - 1; + + if(temp_pattern_position < 0) { + if(tracker->tracker_engine.sequence_position > 0) { + tracker->tracker_engine.sequence_position--; + tracker->tracker_engine.pattern_position = + tracker->tracker_engine.song->pattern_length - 1; + } + } + + else { + tracker->tracker_engine.pattern_position--; + } + } + + if(tracker->editing) { + edit_pattern_step(tracker, step, 1); + } + } + + if(event->input.key == InputKeyBack && event->input.type == InputTypeShort && + tracker->editing) { + uint8_t field = get_field(tracker->patternx); + + delete_field(step, field); + } +} \ No newline at end of file diff --git a/applications/external/flizzer_tracker/input/pattern.h b/applications/external/flizzer_tracker/input/pattern.h new file mode 100644 index 0000000000..5f2e7b70b4 --- /dev/null +++ b/applications/external/flizzer_tracker/input/pattern.h @@ -0,0 +1,14 @@ +#pragma once + +#include +#include +#include + +#include "../flizzer_tracker.h" +#include "../sound_engine/sound_engine_defs.h" +#include "../tracker_engine/tracker_engine_defs.h" +#include "../util.h" + +#define MAX_PATTERNX (2 + 1 + 1 + 3) + +void pattern_edit_event(FlizzerTrackerApp* tracker, FlizzerTrackerEvent* event); \ No newline at end of file diff --git a/applications/external/flizzer_tracker/input/sequence.c b/applications/external/flizzer_tracker/input/sequence.c new file mode 100644 index 0000000000..3db4daa55a --- /dev/null +++ b/applications/external/flizzer_tracker/input/sequence.c @@ -0,0 +1,209 @@ +#include "sequence.h" + +void delete_sequence_step(FlizzerTrackerApp* tracker) { + uint8_t sequence_position = tracker->tracker_engine.sequence_position; + uint8_t* pattern = &tracker->tracker_engine.song->sequence.sequence_step[sequence_position] + .pattern_indices[tracker->current_channel]; + *pattern = 0; +} + +void edit_sequence_step(FlizzerTrackerApp* tracker, int8_t delta) { + uint8_t digit = tracker->current_digit; + + uint8_t sequence_position = tracker->tracker_engine.sequence_position; + uint8_t pattern_index = tracker->tracker_engine.song->sequence.sequence_step[sequence_position] + .pattern_indices[tracker->current_channel]; + + uint8_t* pattern = &tracker->tracker_engine.song->sequence.sequence_step[sequence_position] + .pattern_indices[tracker->current_channel]; + uint8_t temp_pattern = *pattern; + + switch(digit) { + case 0: // upper nibble + { + int8_t nibble = ((pattern_index & 0xf0) >> 4); + + if(nibble + delta < 0) { + nibble = 0xf; + } + + else if(nibble + delta > 0xf) { + nibble = 0; + } + + else { + nibble += delta; + } + + temp_pattern &= 0x0f; + temp_pattern |= (nibble << 4); + + break; + } + + case 1: // lower nibble + { + int8_t nibble = (pattern_index & 0x0f); + + if(nibble + delta < 0) { + nibble = 0xf; + } + + else if(nibble + delta > 0xf) { + nibble = 0; + } + + else { + nibble += delta; + } + + temp_pattern &= 0xf0; + temp_pattern |= nibble; + + break; + } + } + + if(check_and_allocate_pattern(&tracker->song, temp_pattern)) { + *pattern = temp_pattern; + } +} + +void sequence_edit_event(FlizzerTrackerApp* tracker, FlizzerTrackerEvent* event) { + if(event->input.key == InputKeyOk && event->input.type == InputTypeShort && + !tracker->tracker_engine.playing) { + tracker->editing = !tracker->editing; + } + + if(event->input.key == InputKeyOk && event->input.type == InputTypeLong) { + if(!(tracker->editing)) { + if(tracker->tracker_engine.playing) { + stop_song(tracker); + } + + else { + if(tracker->tracker_engine.pattern_position == tracker->song.pattern_length - 1 && + tracker->tracker_engine.sequence_position == + tracker->song.num_sequence_steps - + 1) // if we are at the very end of the song + { + stop_song(tracker); + } + + else { + play_song(tracker, true); + } + } + } + } + + if(event->input.key == InputKeyRight && event->input.type == InputTypeShort) { + tracker->current_digit++; + + if(tracker->current_digit > 1) { + tracker->current_channel++; + + tracker->current_digit = 0; + + if(tracker->current_channel > SONG_MAX_CHANNELS - 1) { + tracker->current_channel = 0; + } + } + } + + if(event->input.key == InputKeyLeft && event->input.type == InputTypeShort) { + tracker->current_digit--; + + if(tracker->current_digit > 1) // unsigned int overflow + { + tracker->current_channel--; + + tracker->current_digit = 1; + + if(tracker->current_channel > SONG_MAX_CHANNELS - 1) // unsigned int overflow + { + tracker->current_channel = SONG_MAX_CHANNELS - 1; + } + } + } + + if(event->input.key == InputKeyDown && event->input.type == InputTypeShort) { + if(!(tracker->editing)) { + tracker->tracker_engine.sequence_position++; + + if(tracker->tracker_engine.sequence_position >= + tracker->tracker_engine.song->num_sequence_steps) { + tracker->tracker_engine.sequence_position = 0; + } + } + + if(tracker->editing) { + edit_sequence_step(tracker, -1); + } + } + + if(event->input.key == InputKeyUp && event->input.type == InputTypeShort) { + if(!(tracker->editing)) { + int16_t temp_sequence_position = tracker->tracker_engine.sequence_position - 1; + + if(temp_sequence_position < 0) { + tracker->tracker_engine.sequence_position = + tracker->tracker_engine.song->num_sequence_steps - 1; + } + + else { + tracker->tracker_engine.sequence_position--; + } + } + + if(tracker->editing) { + edit_sequence_step(tracker, 1); + } + } + + if(event->input.key == InputKeyRight && event->input.type == InputTypeLong && + !(tracker->editing)) // set loop begin or loop end for the song + { + TrackerSong* song = &tracker->song; + + if(song->loop_start == song->loop_end && song->loop_end == 0) // if both are 0 + { + song->loop_end = tracker->tracker_engine.sequence_position; + } + + else { + if(tracker->tracker_engine.sequence_position < song->loop_end) { + song->loop_start = tracker->tracker_engine.sequence_position; + } + + if(tracker->tracker_engine.sequence_position > song->loop_start) { + song->loop_end = tracker->tracker_engine.sequence_position; + } + } + } + + if(event->input.key == InputKeyLeft && event->input.type == InputTypeLong && + !(tracker->editing)) // erase loop begin and loop end points + { + TrackerSong* song = &tracker->song; + + song->loop_start = song->loop_end = 0; + } + + if(event->input.key == InputKeyUp && event->input.type == InputTypeLong && + !(tracker->editing)) // jump to the beginning + { + tracker->tracker_engine.sequence_position = 0; + } + + if(event->input.key == InputKeyDown && event->input.type == InputTypeLong && + !(tracker->editing)) // jump to the end + { + tracker->tracker_engine.sequence_position = tracker->song.num_sequence_steps - 1; + } + + if(event->input.key == InputKeyBack && event->input.type == InputTypeShort && + tracker->editing) { + delete_sequence_step(tracker); + } +} \ No newline at end of file diff --git a/applications/external/flizzer_tracker/input/sequence.h b/applications/external/flizzer_tracker/input/sequence.h new file mode 100644 index 0000000000..057b0de6bc --- /dev/null +++ b/applications/external/flizzer_tracker/input/sequence.h @@ -0,0 +1,12 @@ +#pragma once + +#include +#include +#include + +#include "../flizzer_tracker.h" +#include "../sound_engine/sound_engine_defs.h" +#include "../tracker_engine/tracker_engine_defs.h" +#include "../util.h" + +void sequence_edit_event(FlizzerTrackerApp* tracker, FlizzerTrackerEvent* event); \ No newline at end of file diff --git a/applications/external/flizzer_tracker/input/songinfo.c b/applications/external/flizzer_tracker/input/songinfo.c new file mode 100644 index 0000000000..a10ed7bece --- /dev/null +++ b/applications/external/flizzer_tracker/input/songinfo.c @@ -0,0 +1,224 @@ +#include "songinfo.h" + +#include "../diskop.h" + +void edit_songinfo_param(FlizzerTrackerApp* tracker, uint8_t selected_param, int8_t delta) { + if(!(tracker->current_digit)) { + delta *= 16; + } + + switch(selected_param) { + case SI_PATTERNPOS: { + uint16_t new_length = tracker->song.pattern_length; + + if((int16_t)new_length + (int16_t)delta > 0 && + (int16_t)new_length + (int16_t)delta <= 0x100) { + new_length += delta; + change_pattern_length(&tracker->song, new_length); + + if(tracker->tracker_engine.pattern_position >= new_length) { + tracker->tracker_engine.pattern_position = new_length - 1; + } + } + + break; + } + + case SI_SEQUENCEPOS: { + if((int16_t)tracker->song.num_sequence_steps + (int16_t)delta > 0 && + (int16_t)tracker->song.num_sequence_steps + (int16_t)delta <= 0x100) { + tracker->song.num_sequence_steps += delta; + + if(tracker->tracker_engine.sequence_position >= tracker->song.num_sequence_steps) { + tracker->tracker_engine.sequence_position = tracker->song.num_sequence_steps - 1; + } + } + + break; + } + + case SI_SONGSPEED: { + if((int16_t)tracker->song.speed + (int16_t)delta > 1 && + (int16_t)tracker->song.speed + (int16_t)delta <= 0xff) { + tracker->song.speed += delta; + } + + break; + } + + case SI_SONGRATE: { + if((int16_t)tracker->song.rate + (int16_t)delta > 1 && + (int16_t)tracker->song.rate + (int16_t)delta <= 0xff) { + tracker->song.rate += delta; + } + + break; + } + + case SI_MASTERVOL: { + if((int16_t)tracker->tracker_engine.master_volume + (int16_t)delta > 0 && + (int16_t)tracker->tracker_engine.master_volume + (int16_t)delta <= 0xff) { + tracker->tracker_engine.master_volume += delta; + } + + break; + } + + case SI_SONGNAME: { + text_input_set_header_text(tracker->text_input, "Song name:"); + text_input_set_result_callback( + tracker->text_input, + return_from_keyboard_callback, + tracker, + (char*)&tracker->song.song_name, + MUS_SONG_NAME_LEN + 1, + false); + + view_dispatcher_switch_to_view(tracker->view_dispatcher, VIEW_KEYBOARD); + break; + } + + case SI_CURRENTINSTRUMENT: { + int16_t inst = tracker->current_instrument; + + int8_t inst_delta = delta > 0 ? 1 : -1; + + inst += inst_delta; + + clamp(inst, 0, 0, tracker->song.num_instruments - 1); + + tracker->current_instrument = inst; + + break; + } + + case SI_INSTRUMENTNAME: { + text_input_set_header_text(tracker->text_input, "Instrument name:"); + text_input_set_result_callback( + tracker->text_input, + return_from_keyboard_callback, + tracker, + (char*)&tracker->song.instrument[tracker->current_instrument]->name, + MUS_INST_NAME_LEN + 1, + false); + + view_dispatcher_switch_to_view(tracker->view_dispatcher, VIEW_KEYBOARD); + break; + } + + default: + break; + } +} + +void songinfo_edit_event(FlizzerTrackerApp* tracker, FlizzerTrackerEvent* event) { + if(event->input.key == InputKeyOk && event->input.type == InputTypeShort && + !tracker->tracker_engine.playing) { + tracker->editing = !tracker->editing; + } + + if(event->input.key == InputKeyOk && event->input.type == InputTypeLong) { + if(!(tracker->editing)) { + if(tracker->tracker_engine.playing) { + stop_song(tracker); + } + + else { + if(tracker->tracker_engine.pattern_position == tracker->song.pattern_length - 1 && + tracker->tracker_engine.sequence_position == + tracker->song.num_sequence_steps - + 1) // if we are at the very end of the song + { + stop_song(tracker); + } + + else { + play_song(tracker, true); + } + } + } + } + + if(event->input.key == InputKeyRight && event->input.type == InputTypeShort) { + switch(tracker->selected_param) { + default: { + tracker->current_digit++; + + if(tracker->current_digit > 1) { + tracker->selected_param++; + + tracker->current_digit = 0; + + if(tracker->selected_param > SI_PARAMS - 1) { + tracker->selected_param = 0; + } + } + + break; + } + + case SI_CURRENTINSTRUMENT: + case SI_SONGNAME: + case SI_INSTRUMENTNAME: { + tracker->selected_param++; + + tracker->current_digit = 0; + + if(tracker->selected_param > SI_PARAMS - 1) { + tracker->selected_param = 0; + } + + break; + } + } + } + + if(event->input.key == InputKeyLeft && event->input.type == InputTypeShort) { + switch(tracker->selected_param) { + default: { + tracker->current_digit--; + + if(tracker->current_digit > 1) // unsigned int overflow + { + tracker->selected_param--; + + tracker->current_digit = 1; + + if(tracker->selected_param > SI_PARAMS - 1) // unsigned int overflow + { + tracker->selected_param = SI_PARAMS - 1; + } + } + + break; + } + + case SI_CURRENTINSTRUMENT: + case SI_SONGNAME: + case SI_INSTRUMENTNAME: { + tracker->selected_param--; + + tracker->current_digit = 0; + + if(tracker->selected_param > SI_PARAMS - 1) // unsigned int overflow + { + tracker->selected_param = SI_PARAMS - 1; + } + + break; + } + } + } + + if(event->input.key == InputKeyDown && event->input.type == InputTypeShort) { + if(tracker->editing) { + edit_songinfo_param(tracker, tracker->selected_param, -1); + } + } + + if(event->input.key == InputKeyUp && event->input.type == InputTypeShort) { + if(tracker->editing) { + edit_songinfo_param(tracker, tracker->selected_param, 1); + } + } +} \ No newline at end of file diff --git a/applications/external/flizzer_tracker/input/songinfo.h b/applications/external/flizzer_tracker/input/songinfo.h new file mode 100644 index 0000000000..3add936eb5 --- /dev/null +++ b/applications/external/flizzer_tracker/input/songinfo.h @@ -0,0 +1,14 @@ +#pragma once + +#include +#include +#include +#include + +#include "../flizzer_tracker.h" +#include "../sound_engine/sound_engine_defs.h" +#include "../tracker_engine/tracker_engine_defs.h" +#include "../util.h" + +void songinfo_edit_event(FlizzerTrackerApp* tracker, FlizzerTrackerEvent* event); +void return_from_keyboard_callback(void* ctx); \ No newline at end of file diff --git a/applications/external/flizzer_tracker/input_event.c b/applications/external/flizzer_tracker/input_event.c new file mode 100644 index 0000000000..55b9e7d62a --- /dev/null +++ b/applications/external/flizzer_tracker/input_event.c @@ -0,0 +1,501 @@ +#include "input_event.h" + +#include "diskop.h" + +#define AUDIO_MODES_COUNT 2 + +void return_from_keyboard_callback(void* ctx) { + FlizzerTrackerApp* tracker = (FlizzerTrackerApp*)ctx; + + if(!tracker->is_loading && !tracker->is_saving && !tracker->is_loading_instrument && + !tracker->is_saving_instrument) { + uint8_t string_length = 0; + char* string = NULL; + + if(tracker->focus == EDIT_SONGINFO && tracker->mode == PATTERN_VIEW) { + switch(tracker->selected_param) { + case SI_SONGNAME: { + string_length = MUS_SONG_NAME_LEN; + string = (char*)&tracker->song.song_name; + break; + } + + case SI_INSTRUMENTNAME: { + string_length = MUS_INST_NAME_LEN; + string = (char*)&tracker->song.instrument[tracker->current_instrument]->name; + break; + } + } + } + + if(tracker->focus == EDIT_INSTRUMENT && tracker->mode == INST_EDITOR_VIEW) { + switch(tracker->selected_param) { + case INST_INSTRUMENTNAME: { + string_length = MUS_INST_NAME_LEN; + string = (char*)&tracker->song.instrument[tracker->current_instrument]->name; + break; + } + } + } + + if(string == NULL || string_length == 0) return; + + for(uint8_t i = 0; i < string_length; + i++) // I tinyfied the font by deleting lowercase chars, and I don't like the lowercase chars of any 3x5 pixels font + { + string[i] = toupper(string[i]); + } + } + + view_dispatcher_switch_to_view(tracker->view_dispatcher, VIEW_TRACKER); + + if(tracker->is_saving) { + stop_song(tracker); + + tracker->filepath = furi_string_alloc(); + furi_string_cat_printf( + tracker->filepath, "%s/%s%s", FLIZZER_TRACKER_FOLDER, tracker->filename, SONG_FILE_EXT); + + if(storage_file_exists(tracker->storage, furi_string_get_cstr(tracker->filepath))) { + view_dispatcher_switch_to_view(tracker->view_dispatcher, VIEW_FILE_OVERWRITE); + return; + } + + else { + FlizzerTrackerEvent event = {.type = EventTypeSaveSong, .input = {{0}}, .period = 0}; + furi_message_queue_put(tracker->event_queue, &event, FuriWaitForever); + } + } + + if(tracker->is_saving_instrument) { + stop_song(tracker); + + tracker->filepath = furi_string_alloc(); + furi_string_cat_printf( + tracker->filepath, + "%s/%s%s", + FLIZZER_TRACKER_INSTRUMENTS_FOLDER, + tracker->filename, + INST_FILE_EXT); + + if(storage_file_exists(tracker->storage, furi_string_get_cstr(tracker->filepath))) { + view_dispatcher_switch_to_view( + tracker->view_dispatcher, VIEW_INSTRUMENT_FILE_OVERWRITE); + return; + } + + else { + FlizzerTrackerEvent event = { + .type = EventTypeSaveInstrument, .input = {{0}}, .period = 0}; + furi_message_queue_put(tracker->event_queue, &event, FuriWaitForever); + } + } +} + +void overwrite_file_widget_yes_input_callback(GuiButtonType result, InputType type, void* ctx) { + UNUSED(result); + + FlizzerTrackerApp* tracker = (FlizzerTrackerApp*)ctx; + + if(type == InputTypeShort) { + tracker->is_saving = true; + view_dispatcher_switch_to_view(tracker->view_dispatcher, VIEW_TRACKER); + // save_song(tracker, tracker->filepath); + static FlizzerTrackerEvent event = { + .type = EventTypeSaveSong, .input = {{0}}, .period = 0}; + furi_message_queue_put(tracker->event_queue, &event, FuriWaitForever); + } +} + +void overwrite_file_widget_no_input_callback(GuiButtonType result, InputType type, void* ctx) { + UNUSED(result); + + FlizzerTrackerApp* tracker = (FlizzerTrackerApp*)ctx; + + if(type == InputTypeShort) { + tracker->is_saving = false; + furi_string_free(tracker->filepath); + view_dispatcher_switch_to_view(tracker->view_dispatcher, VIEW_TRACKER); + } +} + +void overwrite_instrument_file_widget_yes_input_callback( + GuiButtonType result, + InputType type, + void* ctx) { + UNUSED(result); + + FlizzerTrackerApp* tracker = (FlizzerTrackerApp*)ctx; + + if(type == InputTypeShort) { + tracker->is_saving_instrument = true; + view_dispatcher_switch_to_view(tracker->view_dispatcher, VIEW_TRACKER); + // save_song(tracker, tracker->filepath); + static FlizzerTrackerEvent event = { + .type = EventTypeSaveInstrument, .input = {{0}}, .period = 0}; + furi_message_queue_put(tracker->event_queue, &event, FuriWaitForever); + } +} + +void overwrite_instrument_file_widget_no_input_callback( + GuiButtonType result, + InputType type, + void* ctx) { + UNUSED(result); + + FlizzerTrackerApp* tracker = (FlizzerTrackerApp*)ctx; + + if(type == InputTypeShort) { + tracker->is_saving_instrument = false; + furi_string_free(tracker->filepath); + view_dispatcher_switch_to_view(tracker->view_dispatcher, VIEW_TRACKER); + } +} + +uint32_t submenu_settings_exit_callback(void* context) { + UNUSED(context); + return VIEW_SUBMENU_PATTERN; +} + +uint32_t submenu_exit_callback(void* context) { + UNUSED(context); + return VIEW_TRACKER; +} + +void submenu_callback(void* context, uint32_t index) { + FlizzerTrackerApp* tracker = (FlizzerTrackerApp*)context; + + switch(tracker->mode) { + case PATTERN_VIEW: { + switch(index) { + case SUBMENU_PATTERN_EXIT: { + tracker->quit = true; + + static InputEvent inevent = {.sequence = 0, .key = InputKeyLeft, .type = InputTypeMAX}; + FlizzerTrackerEvent event = { + .type = EventTypeInput, + .input = inevent, + .period = + 0}; // making an event so tracker does not wait for next keypress and exits immediately + furi_message_queue_put(tracker->event_queue, &event, FuriWaitForever); + view_dispatcher_switch_to_view(tracker->view_dispatcher, VIEW_TRACKER); + break; + } + + case SUBMENU_PATTERN_HELP: { + tracker->showing_help = true; + view_dispatcher_switch_to_view(tracker->view_dispatcher, VIEW_TRACKER); + break; + } + + case SUBMENU_PATTERN_SAVE_SONG: { + text_input_set_header_text(tracker->text_input, "Song filename:"); + memset(&tracker->filename, 0, FILE_NAME_LEN); + text_input_set_result_callback( + tracker->text_input, + return_from_keyboard_callback, + tracker, + (char*)&tracker->filename, + FILE_NAME_LEN, + true); + + tracker->is_saving = true; + + view_dispatcher_switch_to_view(tracker->view_dispatcher, VIEW_KEYBOARD); + break; + } + + case SUBMENU_PATTERN_LOAD_SONG: { + FlizzerTrackerEvent event = {.type = EventTypeLoadSong, .input = {{0}}, .period = 0}; + furi_message_queue_put(tracker->event_queue, &event, FuriWaitForever); + view_dispatcher_switch_to_view(tracker->view_dispatcher, VIEW_TRACKER); + break; + } + + case SUBMENU_PATTERN_SETTINGS: { + view_dispatcher_switch_to_view(tracker->view_dispatcher, VIEW_SETTINGS); + break; + } + + default: + break; + } + + break; + } + + case INST_EDITOR_VIEW: { + switch(index) { + case SUBMENU_INSTRUMENT_EXIT: { + tracker->quit = true; + + static InputEvent inevent = {.sequence = 0, .key = InputKeyLeft, .type = InputTypeMAX}; + FlizzerTrackerEvent event = { + .type = EventTypeInput, + .input = inevent, + .period = + 0}; // making an event so tracker does not wait for next keypress and exits immediately + furi_message_queue_put(tracker->event_queue, &event, FuriWaitForever); + view_dispatcher_switch_to_view(tracker->view_dispatcher, VIEW_TRACKER); + break; + } + + case SUBMENU_INSTRUMENT_SAVE: { + text_input_set_header_text(tracker->text_input, "Instrument filename:"); + memset(&tracker->filename, 0, FILE_NAME_LEN); + text_input_set_result_callback( + tracker->text_input, + return_from_keyboard_callback, + tracker, + (char*)&tracker->filename, + FILE_NAME_LEN, + true); + + tracker->is_saving_instrument = true; + + view_dispatcher_switch_to_view(tracker->view_dispatcher, VIEW_KEYBOARD); + break; + } + + case SUBMENU_INSTRUMENT_LOAD: { + FlizzerTrackerEvent event = { + .type = EventTypeLoadInstrument, .input = {{0}}, .period = 0}; + furi_message_queue_put(tracker->event_queue, &event, FuriWaitForever); + view_dispatcher_switch_to_view(tracker->view_dispatcher, VIEW_TRACKER); + break; + } + + default: + break; + } + + break; + } + + default: + break; + } +} + +void submenu_copypaste_callback(void* context, uint32_t index) { + FlizzerTrackerApp* tracker = (FlizzerTrackerApp*)context; + + uint8_t sequence_position = tracker->tracker_engine.sequence_position; + uint8_t current_pattern_index = + tracker->tracker_engine.song->sequence.sequence_step[sequence_position] + .pattern_indices[tracker->current_channel]; + + TrackerSongPattern* source_pattern; + + if(tracker->source_pattern_index >= 0) { + source_pattern = &tracker->song.pattern[tracker->source_pattern_index]; + } + + TrackerSongPattern* current_pattern = &tracker->song.pattern[current_pattern_index]; + + uint16_t pattern_length = tracker->tracker_engine.song->pattern_length; + + switch(index) { + case SUBMENU_PATTERN_COPYPASTE_COPY: { + tracker->source_pattern_index = current_pattern_index; + tracker->cut_pattern = false; + break; + } + + case SUBMENU_PATTERN_COPYPASTE_PASTE: { + if(tracker->source_pattern_index >= 0) { + memcpy( + current_pattern->step, + source_pattern->step, + sizeof(TrackerSongPatternStep) * pattern_length); + + if(tracker->cut_pattern) { + set_empty_pattern(source_pattern, pattern_length); + tracker->cut_pattern = false; + } + } + break; + } + + case SUBMENU_PATTERN_COPYPASTE_CUT: { + tracker->source_pattern_index = current_pattern_index; + tracker->cut_pattern = true; + break; + } + + case SUBMENU_PATTERN_COPYPASTE_CLEAR: { + set_empty_pattern(current_pattern, pattern_length); + break; + } + + default: + break; + } + + view_dispatcher_switch_to_view(tracker->view_dispatcher, VIEW_TRACKER); +} + +void audio_output_changed_callback(VariableItem* item) { + FlizzerTrackerApp* tracker = (FlizzerTrackerApp*)variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, audio_modes_text[(index > 1 ? 1 : index)]); + + if(tracker) { + tracker->external_audio = (bool)index; + + tracker->external_audio = audio_modes_values[(index > 1 ? 1 : index)]; + + // sound_engine_init(&tracker->sound_engine, tracker->sound_engine.sample_rate, tracker->external_audio, tracker->sound_engine.audio_buffer_size); + // sound_engine_init_hardware(tracker->sound_engine.sample_rate, tracker->external_audio, tracker->sound_engine.audio_buffer, tracker->sound_engine.audio_buffer_size); + + FlizzerTrackerEvent event = {.type = EventTypeSetAudioMode, .input = {{0}}, .period = 0}; + furi_message_queue_put(tracker->event_queue, &event, FuriWaitForever); + + UNUSED(event); + } +} + +void cycle_focus(FlizzerTrackerApp* tracker) { + switch(tracker->mode) { + case PATTERN_VIEW: { + tracker->focus++; + + if(tracker->focus > EDIT_SONGINFO) { + tracker->focus = EDIT_PATTERN; + } + + break; + } + + case INST_EDITOR_VIEW: { + tracker->focus++; + + if(tracker->focus > EDIT_PROGRAM) { + tracker->focus = EDIT_INSTRUMENT; + + if(tracker->current_digit > 1) { + tracker->current_digit = 1; + } + } + + break; + } + + default: + break; + } +} + +void cycle_view(FlizzerTrackerApp* tracker) { + if(tracker->mode == PATTERN_VIEW) { + tracker->mode = INST_EDITOR_VIEW; + tracker->focus = EDIT_INSTRUMENT; + + tracker->selected_param = 0; + tracker->current_digit = 0; + + return; + } + + if(tracker->mode == INST_EDITOR_VIEW) { + tracker->mode = PATTERN_VIEW; + tracker->focus = EDIT_PATTERN; + + if(tracker->tracker_engine.song == NULL) { + stop_song(tracker); + tracker_engine_set_song(&tracker->tracker_engine, &tracker->song); + } + + tracker->selected_param = 0; + tracker->current_digit = 0; + + return; + } +} + +void process_input_event(FlizzerTrackerApp* tracker, FlizzerTrackerEvent* event) { + if(event->input.key == InputKeyBack && event->input.type == InputTypeShort && + tracker->showing_help) { + tracker->showing_help = false; + return; + } + + if(tracker->showing_help || tracker->is_loading || tracker->is_saving || + tracker->is_loading_instrument || tracker->is_saving_instrument) + return; //do not react until these are finished + + if(event->input.key == InputKeyBack && event->input.type == InputTypeShort && + event->period > 0 && event->period < 300 && !(tracker->editing)) { + cycle_view(tracker); + stop_song(tracker); + return; + } + + else if( + event->input.key == InputKeyBack && event->input.type == InputTypeShort && + !(tracker->editing)) { + cycle_focus(tracker); + //stop_song(tracker); + return; + } + + if(event->input.key == InputKeyBack && event->input.type == InputTypeLong) { + switch(tracker->mode) { + case PATTERN_VIEW: { + if(tracker->focus == EDIT_PATTERN) { + submenu_set_selected_item( + tracker->pattern_copypaste_submenu, SUBMENU_PATTERN_COPYPASTE_COPY); + view_dispatcher_switch_to_view( + tracker->view_dispatcher, VIEW_SUBMENU_PATTERN_COPYPASTE); + } + + else { + submenu_set_selected_item(tracker->pattern_submenu, SUBMENU_PATTERN_LOAD_SONG); + view_dispatcher_switch_to_view(tracker->view_dispatcher, VIEW_SUBMENU_PATTERN); + } + break; + } + + case INST_EDITOR_VIEW: { + submenu_set_selected_item(tracker->instrument_submenu, SUBMENU_INSTRUMENT_LOAD); + view_dispatcher_switch_to_view(tracker->view_dispatcher, VIEW_SUBMENU_INSTRUMENT); + break; + } + + default: + break; + } + + return; + } + + switch(tracker->focus) { + case EDIT_PATTERN: { + pattern_edit_event(tracker, event); + break; + } + + case EDIT_SEQUENCE: { + sequence_edit_event(tracker, event); + break; + } + + case EDIT_SONGINFO: { + songinfo_edit_event(tracker, event); + break; + } + + case EDIT_INSTRUMENT: { + instrument_edit_event(tracker, event); + break; + } + + case EDIT_PROGRAM: { + instrument_program_edit_event(tracker, event); + break; + } + + default: + break; + } +} \ No newline at end of file diff --git a/applications/external/flizzer_tracker/input_event.h b/applications/external/flizzer_tracker/input_event.h new file mode 100644 index 0000000000..c2b195054d --- /dev/null +++ b/applications/external/flizzer_tracker/input_event.h @@ -0,0 +1,40 @@ +#pragma once + +#include +#include +#include + +#include "flizzer_tracker.h" +#include "sound_engine/sound_engine_defs.h" +#include "tracker_engine/tracker_engine_defs.h" +#include "util.h" + +#include "input/instrument.h" +#include "input/instrument_program.h" +#include "input/pattern.h" +#include "input/sequence.h" +#include "input/songinfo.h" + +extern bool audio_modes_values[]; +extern char* audio_modes_text[]; + +void return_from_keyboard_callback(void* ctx); + +void overwrite_file_widget_yes_input_callback(GuiButtonType result, InputType type, void* ctx); +void overwrite_file_widget_no_input_callback(GuiButtonType result, InputType type, void* ctx); + +void overwrite_instrument_file_widget_yes_input_callback( + GuiButtonType result, + InputType type, + void* ctx); +void overwrite_instrument_file_widget_no_input_callback( + GuiButtonType result, + InputType type, + void* ctx); + +uint32_t submenu_exit_callback(void* context); +uint32_t submenu_settings_exit_callback(void* context); +void submenu_callback(void* context, uint32_t index); +void submenu_copypaste_callback(void* context, uint32_t index); +void audio_output_changed_callback(VariableItem* item); +void process_input_event(FlizzerTrackerApp* tracker, FlizzerTrackerEvent* event); \ No newline at end of file diff --git a/applications/external/flizzer_tracker/macros.h b/applications/external/flizzer_tracker/macros.h new file mode 100644 index 0000000000..0144705c95 --- /dev/null +++ b/applications/external/flizzer_tracker/macros.h @@ -0,0 +1,2 @@ +#define my_min(a, b) (((a) < (b)) ? (a) : (b)) +#define my_max(a, b) (((a) > (b)) ? (a) : (b)) \ No newline at end of file diff --git a/applications/external/flizzer_tracker/sound_engine/freqs.c b/applications/external/flizzer_tracker/sound_engine/freqs.c new file mode 100644 index 0000000000..371dfc2101 --- /dev/null +++ b/applications/external/flizzer_tracker/sound_engine/freqs.c @@ -0,0 +1,36 @@ +#include "freqs.h" + +const uint32_t frequency_table[FREQ_TAB_SIZE] = { + (uint32_t)(2093.00 * 1024), // 7th octave, the highest in this tracker + (uint32_t)(2217.46 * 1024), // frequency precision is 1 / 1024th of Hz + (uint32_t)(2349.32 * 1024), + (uint32_t)(2489.02 * 1024), + (uint32_t)(2637.02 * 1024), + (uint32_t)(2793.83 * 1024), + (uint32_t)(2959.96 * 1024), + (uint32_t)(3135.96 * 1024), + (uint32_t)(3322.44 * 1024), + (uint32_t)(3520.00 * 1024), + (uint32_t)(3729.31 * 1024), + (uint32_t)(3951.07 * 1024), +}; + +uint32_t get_freq(uint16_t note) { + if(note >= ((FREQ_TAB_SIZE * 8) << 8)) { + return frequency_table[FREQ_TAB_SIZE - 1]; + } + + if((note & 0xff) == 0) { + return frequency_table[((note >> 8) % 12)] / + (2 << (((NUM_OCTAVES) - ((note >> 8) / 12)) - 2)); // wrap to one octave + } + + else { + uint64_t f1 = frequency_table[((note >> 8) % 12)] / + (uint64_t)(2 << (((NUM_OCTAVES) - ((note >> 8) / 12)) - 2)); + uint64_t f2 = frequency_table[(((note >> 8) + 1) % 12)] / + (uint64_t)(2 << (((NUM_OCTAVES) - (((note >> 8) + 1) / 12)) - 2)); + + return f1 + (uint64_t)((f2 - f1) * (uint64_t)(note & 0xff)) / (uint64_t)256; + } +} \ No newline at end of file diff --git a/applications/external/flizzer_tracker/sound_engine/freqs.h b/applications/external/flizzer_tracker/sound_engine/freqs.h new file mode 100644 index 0000000000..2b0706739e --- /dev/null +++ b/applications/external/flizzer_tracker/sound_engine/freqs.h @@ -0,0 +1,11 @@ +#pragma once + +#include +#include + +#define FREQ_TAB_SIZE 12 /* one octave */ +#define NUM_OCTAVES 8 /* 0-7th octaves */ + +extern const uint32_t frequency_table[FREQ_TAB_SIZE]; + +uint32_t get_freq(uint16_t note); \ No newline at end of file diff --git a/applications/external/flizzer_tracker/sound_engine/sound_engine.c b/applications/external/flizzer_tracker/sound_engine/sound_engine.c new file mode 100644 index 0000000000..4908c92267 --- /dev/null +++ b/applications/external/flizzer_tracker/sound_engine/sound_engine.c @@ -0,0 +1,201 @@ +#include "sound_engine.h" +#include "../flizzer_tracker_hal.h" + +#include + +#define PI 3.1415 + +void sound_engine_init( + SoundEngine* sound_engine, + uint32_t sample_rate, + bool external_audio_output, + uint32_t audio_buffer_size) { + if(sound_engine->audio_buffer) { + free(sound_engine->audio_buffer); + } + + if(sound_engine->sine_lut) { + free(sound_engine->sine_lut); + } + + memset(sound_engine, 0, sizeof(SoundEngine)); + + sound_engine->audio_buffer = malloc(audio_buffer_size * sizeof(sound_engine->audio_buffer[0])); + memset(sound_engine->audio_buffer, 0, sizeof(SoundEngine)); + sound_engine->audio_buffer_size = audio_buffer_size; + sound_engine->sample_rate = sample_rate; + sound_engine->external_audio_output = external_audio_output; + + for(int i = 0; i < NUM_CHANNELS; ++i) { + sound_engine->channel[i].lfsr = RANDOM_SEED; + } + + for(int i = 0; i < SINE_LUT_SIZE; ++i) { + sound_engine->sine_lut[i] = (uint8_t)((sinf(i / 64.0 * PI) + 1.0) * 127.0); + } + + furi_hal_interrupt_set_isr_ex( + FuriHalInterruptIdDma1Ch1, 15, sound_engine_dma_isr, sound_engine); + + sound_engine_init_hardware( + sample_rate, external_audio_output, sound_engine->audio_buffer, audio_buffer_size); +} + +void sound_engine_deinit(SoundEngine* sound_engine) { + free(sound_engine->audio_buffer); + + if(!(sound_engine->external_audio_output)) { + if(furi_hal_speaker_is_mine()) { + furi_hal_speaker_release(); + } + } + + else { + furi_hal_gpio_init(&gpio_ext_pa6, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + } + + furi_hal_interrupt_set_isr_ex(FuriHalInterruptIdDma1Ch1, 13, NULL, NULL); + sound_engine_stop(); + sound_engine_deinit_timer(); +} + +void sound_engine_set_channel_frequency( + SoundEngine* sound_engine, + SoundEngineChannel* channel, + uint16_t note) { + uint32_t frequency = get_freq(note); + + if(frequency != 0) { + channel->frequency = (uint64_t)(ACC_LENGTH) / (uint64_t)1024 * (uint64_t)(frequency) / + (uint64_t)sound_engine->sample_rate; + } + + else { + channel->frequency = 0; + } +} + +void sound_engine_enable_gate(SoundEngine* sound_engine, SoundEngineChannel* channel, bool enable) { + if(enable) { + channel->adsr.envelope = 0; + channel->adsr.envelope_speed = envspd(sound_engine, channel->adsr.a); + channel->adsr.envelope_state = ATTACK; + + channel->flags |= SE_ENABLE_GATE; + + if(channel->flags & SE_ENABLE_KEYDOWN_SYNC) { + channel->accumulator = 0; + } + } + + else { + channel->adsr.envelope_state = RELEASE; + channel->adsr.envelope_speed = envspd(sound_engine, channel->adsr.r); + } +} + +void sound_engine_fill_buffer( + SoundEngine* sound_engine, + uint16_t* audio_buffer, + uint32_t audio_buffer_size) { + int32_t channel_output[NUM_CHANNELS]; + int32_t channel_output_final[NUM_CHANNELS]; + + for(uint32_t i = 0; i < audio_buffer_size; ++i) { + int32_t output = WAVE_AMP * 2; + + for(uint32_t chan = 0; chan < NUM_CHANNELS; ++chan) { + SoundEngineChannel* channel = &sound_engine->channel[chan]; + + if(channel->frequency > 0) { + uint32_t prev_acc = channel->accumulator; + + channel->accumulator += channel->frequency; + + channel->sync_bit |= (channel->accumulator & ACC_LENGTH); + + channel->accumulator &= ACC_LENGTH - 1; + + if(channel->flags & SE_ENABLE_HARD_SYNC) { + uint8_t hard_sync_src = channel->hard_sync == 0xff ? i : channel->hard_sync; + + if(sound_engine->channel[hard_sync_src].sync_bit) { + channel->accumulator = 0; + } + } + + channel_output[chan] = + sound_engine_osc(sound_engine, channel, prev_acc) - WAVE_AMP / 2; + + if(channel->flags & SE_ENABLE_RING_MOD) { + uint8_t ring_mod_src = channel->ring_mod == 0xff ? i : channel->ring_mod; + channel_output[chan] = + channel_output[chan] * channel_output[ring_mod_src] / WAVE_AMP; + } + + channel_output_final[chan] = sound_engine_cycle_and_output_adsr( + channel_output[chan], sound_engine, &channel->adsr, &channel->flags); + + if(channel->flags & SE_ENABLE_FILTER) { + if(channel->filter_mode != 0) { + sound_engine_filter_cycle(&channel->filter, channel_output_final[chan]); + + switch(channel->filter_mode) { + case FIL_OUTPUT_LOWPASS: { + channel_output_final[chan] = + sound_engine_output_lowpass(&channel->filter); + break; + } + + case FIL_OUTPUT_HIGHPASS: { + channel_output_final[chan] = + sound_engine_output_highpass(&channel->filter); + break; + } + + case FIL_OUTPUT_BANDPASS: { + channel_output_final[chan] = + sound_engine_output_bandpass(&channel->filter); + break; + } + + case FIL_OUTPUT_LOW_HIGH: { + channel_output_final[chan] = + sound_engine_output_lowpass(&channel->filter) + + sound_engine_output_highpass(&channel->filter); + break; + } + + case FIL_OUTPUT_HIGH_BAND: { + channel_output_final[chan] = + sound_engine_output_highpass(&channel->filter) + + sound_engine_output_bandpass(&channel->filter); + break; + } + + case FIL_OUTPUT_LOW_BAND: { + channel_output_final[chan] = + sound_engine_output_lowpass(&channel->filter) + + sound_engine_output_bandpass(&channel->filter); + break; + } + + case FIL_OUTPUT_LOW_HIGH_BAND: { + channel_output_final[chan] = + sound_engine_output_lowpass(&channel->filter) + + sound_engine_output_highpass(&channel->filter) + + sound_engine_output_bandpass(&channel->filter); + break; + } + } + } + } + + output += channel_output_final[chan]; + } + } + + //audio_buffer[i] = output / (64 * 4); + audio_buffer[i] = output >> 8; + } +} \ No newline at end of file diff --git a/applications/external/flizzer_tracker/sound_engine/sound_engine.h b/applications/external/flizzer_tracker/sound_engine/sound_engine.h new file mode 100644 index 0000000000..a16d02f6d1 --- /dev/null +++ b/applications/external/flizzer_tracker/sound_engine/sound_engine.h @@ -0,0 +1,23 @@ +#pragma once + +#include "freqs.h" +#include "sound_engine_adsr.h" +#include "sound_engine_defs.h" +#include "sound_engine_filter.h" +#include "sound_engine_osc.h" + +void sound_engine_init( + SoundEngine* sound_engine, + uint32_t sample_rate, + bool external_audio_output, + uint32_t audio_buffer_size); +void sound_engine_deinit(SoundEngine* sound_engine); +void sound_engine_set_channel_frequency( + SoundEngine* sound_engine, + SoundEngineChannel* channel, + uint16_t note); +void sound_engine_fill_buffer( + SoundEngine* sound_engine, + uint16_t* audio_buffer, + uint32_t audio_buffer_size); +void sound_engine_enable_gate(SoundEngine* sound_engine, SoundEngineChannel* channel, bool enable); \ No newline at end of file diff --git a/applications/external/flizzer_tracker/sound_engine/sound_engine_adsr.c b/applications/external/flizzer_tracker/sound_engine/sound_engine_adsr.c new file mode 100644 index 0000000000..21eb8f76bb --- /dev/null +++ b/applications/external/flizzer_tracker/sound_engine/sound_engine_adsr.c @@ -0,0 +1,58 @@ +#include "sound_engine_adsr.h" + +int32_t sound_engine_cycle_and_output_adsr( + int32_t input, + SoundEngine* eng, + SoundEngineADSR* adsr, + uint16_t* flags) { + switch(adsr->envelope_state) { + case ATTACK: { + adsr->envelope += adsr->envelope_speed; + + if(adsr->envelope >= MAX_ADSR) { + adsr->envelope_state = DECAY; + adsr->envelope = MAX_ADSR; + + adsr->envelope_speed = envspd(eng, adsr->d); + } + + break; + } + + case DECAY: { + if(adsr->envelope > ((uint32_t)adsr->s << 17) + adsr->envelope_speed) { + adsr->envelope -= adsr->envelope_speed; + } + + else { + adsr->envelope = (uint32_t)adsr->s << 17; + adsr->envelope_state = (adsr->s == 0) ? RELEASE : SUSTAIN; + + adsr->envelope_speed = envspd(eng, adsr->r); + } + + break; + } + + case SUSTAIN: + case DONE: { + break; + } + + case RELEASE: { + if(adsr->envelope > adsr->envelope_speed) { + adsr->envelope -= adsr->envelope_speed; + } + + else { + adsr->envelope_state = DONE; + *flags &= ~SE_ENABLE_GATE; + adsr->envelope = 0; + } + + break; + } + } + + return (int32_t)((int32_t)input * (int32_t)(adsr->envelope >> 10) / (int32_t)(MAX_ADSR >> 10) * (int32_t)adsr->volume / (int32_t)MAX_ADSR_VOLUME); +} \ No newline at end of file diff --git a/applications/external/flizzer_tracker/sound_engine/sound_engine_adsr.h b/applications/external/flizzer_tracker/sound_engine/sound_engine_adsr.h new file mode 100644 index 0000000000..9b915c68d6 --- /dev/null +++ b/applications/external/flizzer_tracker/sound_engine/sound_engine_adsr.h @@ -0,0 +1,9 @@ +#pragma once + +#include "sound_engine_defs.h" + +int32_t sound_engine_cycle_and_output_adsr( + int32_t input, + SoundEngine* eng, + SoundEngineADSR* adsr, + uint16_t* flags); \ No newline at end of file diff --git a/applications/external/flizzer_tracker/sound_engine/sound_engine_defs.h b/applications/external/flizzer_tracker/sound_engine/sound_engine_defs.h new file mode 100644 index 0000000000..bfc9b14a7f --- /dev/null +++ b/applications/external/flizzer_tracker/sound_engine/sound_engine_defs.h @@ -0,0 +1,102 @@ +#pragma once + +#include +#include +#include + +#define NUM_CHANNELS 4 + +#define RANDOM_SEED 0xf31782ce + +#define ACC_BITS 23 +#define ACC_LENGTH (1 << (ACC_BITS - 1)) + +#define OUTPUT_BITS 16 +#define WAVE_AMP (1 << OUTPUT_BITS) + +#define SINE_LUT_SIZE 256 +#define SINE_LUT_BITDEPTH 8 + +#define MAX_ADSR (0xff << 17) +#define MAX_ADSR_VOLUME 0x80 +#define BASE_FREQ 22050 +#define envspd(eng, slope) \ + ((slope) != 0 ? \ + (((uint64_t)MAX_ADSR / ((slope) * (slope)*256 / 8)) * BASE_FREQ / eng->sample_rate) : \ + ((uint64_t)MAX_ADSR * BASE_FREQ / eng->sample_rate)) + +typedef enum { + SE_WAVEFORM_NONE = 0, + SE_WAVEFORM_NOISE = 1, + SE_WAVEFORM_PULSE = 2, + SE_WAVEFORM_TRIANGLE = 4, + SE_WAVEFORM_SAW = 8, + SE_WAVEFORM_NOISE_METAL = 16, + SE_WAVEFORM_SINE = 32, +} SoundEngineWaveformType; + +typedef enum { + SE_ENABLE_FILTER = 1, + SE_ENABLE_GATE = 2, + SE_ENABLE_RING_MOD = 4, + SE_ENABLE_HARD_SYNC = 8, + SE_ENABLE_KEYDOWN_SYNC = 16, // sync oscillators on keydown +} SoundEngineFlags; + +typedef enum { + FIL_OUTPUT_LOWPASS = 1, + FIL_OUTPUT_HIGHPASS = 2, + FIL_OUTPUT_BANDPASS = 3, + FIL_OUTPUT_LOW_HIGH = 4, + FIL_OUTPUT_HIGH_BAND = 5, + FIL_OUTPUT_LOW_BAND = 6, + FIL_OUTPUT_LOW_HIGH_BAND = 7, + /* ============ */ + FIL_MODES = 8, +} SoundEngineFilterModes; + +typedef enum { + ATTACK = 1, + DECAY = 2, + SUSTAIN = 3, + RELEASE = 4, + DONE = 5, +} SoundEngineEnvelopeStates; + +typedef struct { + uint8_t a, d, s, r, volume, envelope_state; + uint32_t envelope, envelope_speed; +} SoundEngineADSR; + +typedef struct { + int32_t cutoff, resonance, low, high, band; +} SoundEngineFilter; + +typedef struct { + uint32_t accumulator; + uint32_t frequency; + uint8_t waveform; + uint16_t pw; + uint32_t lfsr; + SoundEngineADSR adsr; + + uint16_t flags; + + uint8_t ring_mod, hard_sync; // 0xff = self + uint8_t sync_bit; + + uint8_t filter_mode; + + SoundEngineFilter filter; +} SoundEngineChannel; + +typedef struct { + SoundEngineChannel channel[NUM_CHANNELS]; + uint32_t sample_rate; + uint16_t* audio_buffer; + uint32_t audio_buffer_size; + bool external_audio_output; + uint8_t sine_lut[SINE_LUT_SIZE]; + + // uint32_t counter; //for debug +} SoundEngine; \ No newline at end of file diff --git a/applications/external/flizzer_tracker/sound_engine/sound_engine_filter.c b/applications/external/flizzer_tracker/sound_engine/sound_engine_filter.c new file mode 100644 index 0000000000..6a02aa15ac --- /dev/null +++ b/applications/external/flizzer_tracker/sound_engine/sound_engine_filter.c @@ -0,0 +1,28 @@ +#include "sound_engine_filter.h" + +void sound_engine_filter_set_coeff(SoundEngineFilter* flt, uint32_t frequency, uint16_t resonance) { + flt->cutoff = (frequency << 5); + flt->resonance = ((int32_t)resonance * 11 / 6) - 200; +} + +void sound_engine_filter_cycle( + SoundEngineFilter* flt, + int32_t input) // don't ask me how it works, stolen from Furnace tracker TSU synth +{ + input /= 8; + flt->low = flt->low + ((flt->cutoff * flt->band) >> 16); + flt->high = input - flt->low - (((256 - flt->resonance) * flt->band) >> 8); + flt->band = ((flt->cutoff * flt->high) >> 16) + flt->band; +} + +int32_t sound_engine_output_lowpass(SoundEngineFilter* flt) { + return flt->low * 8; +} + +int32_t sound_engine_output_highpass(SoundEngineFilter* flt) { + return flt->high * 8; +} + +int32_t sound_engine_output_bandpass(SoundEngineFilter* flt) { + return flt->band * 8; +} \ No newline at end of file diff --git a/applications/external/flizzer_tracker/sound_engine/sound_engine_filter.h b/applications/external/flizzer_tracker/sound_engine/sound_engine_filter.h new file mode 100644 index 0000000000..43d4e39660 --- /dev/null +++ b/applications/external/flizzer_tracker/sound_engine/sound_engine_filter.h @@ -0,0 +1,9 @@ +#pragma once + +#include "sound_engine_defs.h" + +void sound_engine_filter_set_coeff(SoundEngineFilter* flt, uint32_t frequency, uint16_t resonance); +void sound_engine_filter_cycle(SoundEngineFilter* flt, int32_t input); +int32_t sound_engine_output_lowpass(SoundEngineFilter* flt); +int32_t sound_engine_output_highpass(SoundEngineFilter* flt); +int32_t sound_engine_output_bandpass(SoundEngineFilter* flt); \ No newline at end of file diff --git a/applications/external/flizzer_tracker/sound_engine/sound_engine_osc.c b/applications/external/flizzer_tracker/sound_engine/sound_engine_osc.c new file mode 100644 index 0000000000..b3fd2fe7bf --- /dev/null +++ b/applications/external/flizzer_tracker/sound_engine/sound_engine_osc.c @@ -0,0 +1,278 @@ +#include "sound_engine_osc.h" + +static inline uint16_t sound_engine_pulse(uint32_t acc, uint32_t pw) // 0-FFF pulse width range +{ + return ( + ((acc >> (((uint32_t)ACC_BITS - 17))) >= ((pw == 0xfff ? pw + 1 : pw) << 4) ? + (WAVE_AMP - 1) : + 0)); +} + +static inline uint16_t sound_engine_saw(uint32_t acc) { + return (acc >> (ACC_BITS - OUTPUT_BITS - 1)) & (WAVE_AMP - 1); +} + +uint16_t sound_engine_triangle(uint32_t acc) { + return ( + (((acc & (ACC_LENGTH / 2)) ? ~acc : acc) >> (ACC_BITS - OUTPUT_BITS - 2)) & + (WAVE_AMP * 2 - 1)); +} + +static inline uint16_t sound_engine_sine(uint32_t acc, SoundEngine* sound_engine) { + return ( + (uint16_t)sound_engine->sine_lut[(acc >> (ACC_BITS - SINE_LUT_BITDEPTH))] + << (OUTPUT_BITS - SINE_LUT_BITDEPTH)); +} + +inline static void shift_lfsr(uint32_t* v, uint32_t tap_0, uint32_t tap_1) { + typedef uint32_t T; + const T zero = (T)(0); + const T lsb = zero + (T)(1); + const T feedback = ((lsb << (tap_0)) ^ (lsb << (tap_1))); + + *v = (*v >> 1) ^ ((zero - (*v & lsb)) & feedback); +} + +static inline uint16_t sound_engine_noise(SoundEngineChannel* channel, uint32_t prev_acc) { + if((prev_acc & (ACC_LENGTH / 32)) != (channel->accumulator & (ACC_LENGTH / 32))) { + if(channel->waveform & SE_WAVEFORM_NOISE_METAL) { + shift_lfsr(&channel->lfsr, 14, 8); + channel->lfsr &= (1 << (14 + 1)) - 1; + } + + else { + shift_lfsr(&channel->lfsr, 22, 17); + channel->lfsr &= (1 << (22 + 1)) - 1; + } + } + + return (channel->lfsr) & (WAVE_AMP - 1); +} + +uint16_t + sound_engine_osc(SoundEngine* sound_engine, SoundEngineChannel* channel, uint32_t prev_acc) { + switch(channel->waveform) { + case SE_WAVEFORM_NOISE: + case SE_WAVEFORM_NOISE_METAL: + case(SE_WAVEFORM_NOISE | SE_WAVEFORM_NOISE_METAL): { + return sound_engine_noise(channel, prev_acc); + break; + } + + case SE_WAVEFORM_PULSE: { + return sound_engine_pulse(channel->accumulator, channel->pw); + break; + } + + case SE_WAVEFORM_TRIANGLE: { + return sound_engine_triangle(channel->accumulator); + break; + } + + case SE_WAVEFORM_SAW: { + return sound_engine_saw(channel->accumulator); + break; + } + + case SE_WAVEFORM_SINE: { + return sound_engine_sine(channel->accumulator, sound_engine); + break; + } + + case(SE_WAVEFORM_PULSE | SE_WAVEFORM_NOISE): + case(SE_WAVEFORM_PULSE | SE_WAVEFORM_NOISE_METAL): + case(SE_WAVEFORM_PULSE | SE_WAVEFORM_NOISE | SE_WAVEFORM_NOISE_METAL): { + return sound_engine_pulse(channel->accumulator, channel->pw) & + sound_engine_noise(channel, prev_acc); + } + + case(SE_WAVEFORM_TRIANGLE | SE_WAVEFORM_NOISE): + case(SE_WAVEFORM_TRIANGLE | SE_WAVEFORM_NOISE_METAL): + case(SE_WAVEFORM_TRIANGLE | SE_WAVEFORM_NOISE | SE_WAVEFORM_NOISE_METAL): { + return sound_engine_triangle(channel->accumulator) & sound_engine_noise(channel, prev_acc); + } + + case(SE_WAVEFORM_PULSE | SE_WAVEFORM_TRIANGLE): { + return sound_engine_pulse(channel->accumulator, channel->pw) & + sound_engine_triangle(channel->accumulator); + } + + case(SE_WAVEFORM_TRIANGLE | SE_WAVEFORM_PULSE | SE_WAVEFORM_NOISE): + case(SE_WAVEFORM_TRIANGLE | SE_WAVEFORM_PULSE | SE_WAVEFORM_NOISE_METAL): + case(SE_WAVEFORM_TRIANGLE | SE_WAVEFORM_PULSE | SE_WAVEFORM_NOISE | SE_WAVEFORM_NOISE_METAL): { + return sound_engine_pulse(channel->accumulator, channel->pw) & + sound_engine_noise(channel, prev_acc) & sound_engine_triangle(channel->accumulator); + } + + case(SE_WAVEFORM_SAW | SE_WAVEFORM_NOISE): + case(SE_WAVEFORM_SAW | SE_WAVEFORM_NOISE_METAL): + case(SE_WAVEFORM_SAW | SE_WAVEFORM_NOISE | SE_WAVEFORM_NOISE_METAL): { + return sound_engine_saw(channel->accumulator) & sound_engine_noise(channel, prev_acc); + } + + case(SE_WAVEFORM_PULSE | SE_WAVEFORM_SAW): { + return sound_engine_pulse(channel->accumulator, channel->pw) & + sound_engine_saw(channel->accumulator); + } + + case(SE_WAVEFORM_PULSE | SE_WAVEFORM_SAW | SE_WAVEFORM_NOISE): + case(SE_WAVEFORM_PULSE | SE_WAVEFORM_SAW | SE_WAVEFORM_NOISE_METAL): + case(SE_WAVEFORM_PULSE | SE_WAVEFORM_SAW | SE_WAVEFORM_NOISE | SE_WAVEFORM_NOISE_METAL): { + return sound_engine_pulse(channel->accumulator, channel->pw) & + sound_engine_saw(channel->accumulator) & sound_engine_noise(channel, prev_acc); + } + + case(SE_WAVEFORM_TRIANGLE | SE_WAVEFORM_SAW): { + return sound_engine_triangle(channel->accumulator) & + sound_engine_saw(channel->accumulator); + } + + case(SE_WAVEFORM_TRIANGLE | SE_WAVEFORM_SAW | SE_WAVEFORM_NOISE): + case(SE_WAVEFORM_TRIANGLE | SE_WAVEFORM_SAW | SE_WAVEFORM_NOISE_METAL): + case(SE_WAVEFORM_TRIANGLE | SE_WAVEFORM_SAW | SE_WAVEFORM_NOISE | SE_WAVEFORM_NOISE_METAL): { + return sound_engine_triangle(channel->accumulator) & + sound_engine_saw(channel->accumulator) & sound_engine_noise(channel, prev_acc); + } + + case(SE_WAVEFORM_PULSE | SE_WAVEFORM_TRIANGLE | SE_WAVEFORM_SAW): { + return sound_engine_pulse(channel->accumulator, channel->pw) & + sound_engine_triangle(channel->accumulator) & + sound_engine_saw(channel->accumulator); + } + + case(SE_WAVEFORM_PULSE | SE_WAVEFORM_TRIANGLE | SE_WAVEFORM_SAW | SE_WAVEFORM_NOISE): + case(SE_WAVEFORM_PULSE | SE_WAVEFORM_TRIANGLE | SE_WAVEFORM_SAW | SE_WAVEFORM_NOISE_METAL): + case( + SE_WAVEFORM_PULSE | SE_WAVEFORM_TRIANGLE | SE_WAVEFORM_SAW | SE_WAVEFORM_NOISE | + SE_WAVEFORM_NOISE_METAL): { + return sound_engine_pulse(channel->accumulator, channel->pw) & + sound_engine_triangle(channel->accumulator) & + sound_engine_saw(channel->accumulator) & sound_engine_noise(channel, prev_acc); + } + + case(SE_WAVEFORM_SINE | SE_WAVEFORM_NOISE): + case(SE_WAVEFORM_SINE | SE_WAVEFORM_NOISE_METAL): + case(SE_WAVEFORM_SINE | SE_WAVEFORM_NOISE | SE_WAVEFORM_NOISE_METAL): { + return sound_engine_sine(channel->accumulator, sound_engine) & + sound_engine_noise(channel, prev_acc); + } + + case(SE_WAVEFORM_SINE | SE_WAVEFORM_PULSE): { + return sound_engine_pulse(channel->accumulator, channel->pw) & + sound_engine_sine(channel->accumulator, sound_engine); + } + + case(SE_WAVEFORM_SINE | SE_WAVEFORM_PULSE | SE_WAVEFORM_NOISE): + case(SE_WAVEFORM_SINE | SE_WAVEFORM_PULSE | SE_WAVEFORM_NOISE_METAL): + case(SE_WAVEFORM_SINE | SE_WAVEFORM_PULSE | SE_WAVEFORM_NOISE | SE_WAVEFORM_NOISE_METAL): { + return sound_engine_pulse(channel->accumulator, channel->pw) & + sound_engine_sine(channel->accumulator, sound_engine) & + sound_engine_noise(channel, prev_acc); + } + + case(SE_WAVEFORM_SINE | SE_WAVEFORM_TRIANGLE): { + return sound_engine_triangle(channel->accumulator) & + sound_engine_sine(channel->accumulator, sound_engine); + } + + case(SE_WAVEFORM_SINE | SE_WAVEFORM_TRIANGLE | SE_WAVEFORM_NOISE): + case(SE_WAVEFORM_SINE | SE_WAVEFORM_TRIANGLE | SE_WAVEFORM_NOISE_METAL): + case(SE_WAVEFORM_SINE | SE_WAVEFORM_TRIANGLE | SE_WAVEFORM_NOISE | SE_WAVEFORM_NOISE_METAL): { + return sound_engine_triangle(channel->accumulator) & + sound_engine_sine(channel->accumulator, sound_engine) & + sound_engine_noise(channel, prev_acc); + } + + case(SE_WAVEFORM_SINE | SE_WAVEFORM_TRIANGLE | SE_WAVEFORM_PULSE): { + return sound_engine_pulse(channel->accumulator, channel->pw) & + sound_engine_triangle(channel->accumulator) & + sound_engine_sine(channel->accumulator, sound_engine); + } + + case(SE_WAVEFORM_SINE | SE_WAVEFORM_TRIANGLE | SE_WAVEFORM_PULSE | SE_WAVEFORM_NOISE): + case(SE_WAVEFORM_SINE | SE_WAVEFORM_TRIANGLE | SE_WAVEFORM_PULSE | SE_WAVEFORM_NOISE_METAL): + case( + SE_WAVEFORM_SINE | SE_WAVEFORM_TRIANGLE | SE_WAVEFORM_PULSE | SE_WAVEFORM_NOISE | + SE_WAVEFORM_NOISE_METAL): { + return sound_engine_pulse(channel->accumulator, channel->pw) & + sound_engine_triangle(channel->accumulator) & + sound_engine_sine(channel->accumulator, sound_engine) & + sound_engine_noise(channel, prev_acc); + } + + case(SE_WAVEFORM_SINE | SE_WAVEFORM_SAW): { + return sound_engine_saw(channel->accumulator) & + sound_engine_sine(channel->accumulator, sound_engine); + } + + case(SE_WAVEFORM_SINE | SE_WAVEFORM_SAW | SE_WAVEFORM_NOISE): + case(SE_WAVEFORM_SINE | SE_WAVEFORM_SAW | SE_WAVEFORM_NOISE_METAL): + case(SE_WAVEFORM_SINE | SE_WAVEFORM_SAW | SE_WAVEFORM_NOISE | SE_WAVEFORM_NOISE_METAL): { + return sound_engine_saw(channel->accumulator) & + sound_engine_sine(channel->accumulator, sound_engine) & + sound_engine_noise(channel, prev_acc); + } + + case(SE_WAVEFORM_SINE | SE_WAVEFORM_PULSE | SE_WAVEFORM_SAW): { + return sound_engine_pulse(channel->accumulator, channel->pw) & + sound_engine_saw(channel->accumulator) & + sound_engine_sine(channel->accumulator, sound_engine); + } + + case(SE_WAVEFORM_SINE | SE_WAVEFORM_PULSE | SE_WAVEFORM_SAW | SE_WAVEFORM_NOISE): + case(SE_WAVEFORM_SINE | SE_WAVEFORM_PULSE | SE_WAVEFORM_SAW | SE_WAVEFORM_NOISE_METAL): + case( + SE_WAVEFORM_SINE | SE_WAVEFORM_PULSE | SE_WAVEFORM_SAW | SE_WAVEFORM_NOISE | + SE_WAVEFORM_NOISE_METAL): { + return sound_engine_pulse(channel->accumulator, channel->pw) & + sound_engine_saw(channel->accumulator) & + sound_engine_sine(channel->accumulator, sound_engine) & + sound_engine_noise(channel, prev_acc); + } + + case(SE_WAVEFORM_SINE | SE_WAVEFORM_TRIANGLE | SE_WAVEFORM_SAW): { + return sound_engine_saw(channel->accumulator) & + sound_engine_triangle(channel->accumulator) & + sound_engine_sine(channel->accumulator, sound_engine); + } + + case(SE_WAVEFORM_SINE | SE_WAVEFORM_TRIANGLE | SE_WAVEFORM_SAW | SE_WAVEFORM_NOISE): + case(SE_WAVEFORM_SINE | SE_WAVEFORM_TRIANGLE | SE_WAVEFORM_SAW | SE_WAVEFORM_NOISE_METAL): + case( + SE_WAVEFORM_SINE | SE_WAVEFORM_TRIANGLE | SE_WAVEFORM_SAW | SE_WAVEFORM_NOISE | + SE_WAVEFORM_NOISE_METAL): { + return sound_engine_saw(channel->accumulator) & + sound_engine_triangle(channel->accumulator) & + sound_engine_sine(channel->accumulator, sound_engine) & + sound_engine_noise(channel, prev_acc); + } + + case(SE_WAVEFORM_SINE | SE_WAVEFORM_PULSE | SE_WAVEFORM_TRIANGLE | SE_WAVEFORM_SAW): { + return sound_engine_saw(channel->accumulator) & + sound_engine_pulse(channel->accumulator, channel->pw) & + sound_engine_triangle(channel->accumulator) & + sound_engine_sine(channel->accumulator, sound_engine); + } + + case( + SE_WAVEFORM_SINE | SE_WAVEFORM_PULSE | SE_WAVEFORM_TRIANGLE | SE_WAVEFORM_SAW | + SE_WAVEFORM_NOISE): + case( + SE_WAVEFORM_SINE | SE_WAVEFORM_PULSE | SE_WAVEFORM_TRIANGLE | SE_WAVEFORM_SAW | + SE_WAVEFORM_NOISE_METAL): + case( + SE_WAVEFORM_SINE | SE_WAVEFORM_PULSE | SE_WAVEFORM_TRIANGLE | SE_WAVEFORM_SAW | + SE_WAVEFORM_NOISE | SE_WAVEFORM_NOISE_METAL): { + return sound_engine_saw(channel->accumulator) & + sound_engine_pulse(channel->accumulator, channel->pw) & + sound_engine_triangle(channel->accumulator) & + sound_engine_sine(channel->accumulator, sound_engine) & + sound_engine_noise(channel, prev_acc); + } + + default: + break; + } + + return WAVE_AMP / 2; +} \ No newline at end of file diff --git a/applications/external/flizzer_tracker/sound_engine/sound_engine_osc.h b/applications/external/flizzer_tracker/sound_engine/sound_engine_osc.h new file mode 100644 index 0000000000..97973372ee --- /dev/null +++ b/applications/external/flizzer_tracker/sound_engine/sound_engine_osc.h @@ -0,0 +1,8 @@ +#pragma once + +#include "sound_engine_defs.h" + +uint16_t sound_engine_triangle(uint32_t acc); + +uint16_t + sound_engine_osc(SoundEngine* sound_engine, SoundEngineChannel* channel, uint32_t prev_acc); \ No newline at end of file diff --git a/applications/external/flizzer_tracker/tracker_engine/diskop.c b/applications/external/flizzer_tracker/tracker_engine/diskop.c new file mode 100644 index 0000000000..46de567744 --- /dev/null +++ b/applications/external/flizzer_tracker/tracker_engine/diskop.c @@ -0,0 +1,127 @@ +#include "diskop.h" + +void load_instrument_inner(Stream* stream, Instrument* inst, uint8_t version) { + UNUSED(version); + + size_t rwops = stream_read(stream, (uint8_t*)inst->name, sizeof(inst->name)); + rwops = stream_read(stream, (uint8_t*)&inst->waveform, sizeof(inst->waveform)); + rwops = stream_read(stream, (uint8_t*)&inst->flags, sizeof(inst->flags)); + rwops = + stream_read(stream, (uint8_t*)&inst->sound_engine_flags, sizeof(inst->sound_engine_flags)); + + rwops = stream_read(stream, (uint8_t*)&inst->base_note, sizeof(inst->base_note)); + rwops = stream_read(stream, (uint8_t*)&inst->finetune, sizeof(inst->finetune)); + + rwops = stream_read(stream, (uint8_t*)&inst->slide_speed, sizeof(inst->slide_speed)); + + rwops = stream_read(stream, (uint8_t*)&inst->adsr, sizeof(inst->adsr)); + rwops = stream_read(stream, (uint8_t*)&inst->pw, sizeof(inst->pw)); + + if(inst->sound_engine_flags & SE_ENABLE_RING_MOD) { + rwops = stream_read(stream, (uint8_t*)&inst->ring_mod, sizeof(inst->ring_mod)); + } + + if(inst->sound_engine_flags & SE_ENABLE_HARD_SYNC) { + rwops = stream_read(stream, (uint8_t*)&inst->hard_sync, sizeof(inst->hard_sync)); + } + + uint8_t progsteps = 0; + + rwops = stream_read(stream, (uint8_t*)&progsteps, sizeof(progsteps)); + + if(progsteps > 0) { + rwops = stream_read(stream, (uint8_t*)inst->program, progsteps * sizeof(inst->program[0])); + } + + rwops = stream_read(stream, (uint8_t*)&inst->program_period, sizeof(inst->program_period)); + + if(inst->flags & TE_ENABLE_VIBRATO) { + rwops = stream_read(stream, (uint8_t*)&inst->vibrato_speed, sizeof(inst->vibrato_speed)); + rwops = stream_read(stream, (uint8_t*)&inst->vibrato_depth, sizeof(inst->vibrato_depth)); + rwops = stream_read(stream, (uint8_t*)&inst->vibrato_delay, sizeof(inst->vibrato_delay)); + } + + if(inst->flags & TE_ENABLE_PWM) { + rwops = stream_read(stream, (uint8_t*)&inst->pwm_speed, sizeof(inst->pwm_speed)); + rwops = stream_read(stream, (uint8_t*)&inst->pwm_depth, sizeof(inst->pwm_depth)); + rwops = stream_read(stream, (uint8_t*)&inst->pwm_delay, sizeof(inst->pwm_delay)); + } + + if(inst->sound_engine_flags & SE_ENABLE_FILTER) { + rwops = stream_read(stream, (uint8_t*)&inst->filter_cutoff, sizeof(inst->filter_cutoff)); + rwops = + stream_read(stream, (uint8_t*)&inst->filter_resonance, sizeof(inst->filter_resonance)); + rwops = stream_read(stream, (uint8_t*)&inst->filter_type, sizeof(inst->filter_type)); + } + + UNUSED(rwops); +} + +bool load_song_inner(TrackerSong* song, Stream* stream) { + uint8_t version = 0; + size_t rwops = stream_read(stream, (uint8_t*)&version, sizeof(version)); + + if(version > + TRACKER_ENGINE_VERSION) // if song is of newer version this version of tracker engine can't support + { + return false; + } + + tracker_engine_deinit_song(song, false); + memset(song, 0, sizeof(TrackerSong)); + + rwops = stream_read(stream, (uint8_t*)song->song_name, sizeof(song->song_name)); + rwops = stream_read(stream, (uint8_t*)&song->loop_start, sizeof(song->loop_start)); + rwops = stream_read(stream, (uint8_t*)&song->loop_end, sizeof(song->loop_end)); + rwops = stream_read(stream, (uint8_t*)&song->pattern_length, sizeof(song->pattern_length)); + + rwops = stream_read(stream, (uint8_t*)&song->speed, sizeof(song->speed)); + rwops = stream_read(stream, (uint8_t*)&song->rate, sizeof(song->rate)); + + rwops = + stream_read(stream, (uint8_t*)&song->num_sequence_steps, sizeof(song->num_sequence_steps)); + + for(uint16_t i = 0; i < song->num_sequence_steps; i++) { + rwops = stream_read( + stream, + (uint8_t*)&song->sequence.sequence_step[i], + sizeof(song->sequence.sequence_step[0])); + } + + rwops = stream_read(stream, (uint8_t*)&song->num_patterns, sizeof(song->num_patterns)); + + for(uint16_t i = 0; i < song->num_patterns; i++) { + song->pattern[i].step = (TrackerSongPatternStep*)malloc( + sizeof(TrackerSongPatternStep) * (song->pattern_length)); + set_empty_pattern(&song->pattern[i], song->pattern_length); + rwops = stream_read( + stream, + (uint8_t*)song->pattern[i].step, + sizeof(TrackerSongPatternStep) * (song->pattern_length)); + } + + rwops = stream_read(stream, (uint8_t*)&song->num_instruments, sizeof(song->num_instruments)); + + for(uint16_t i = 0; i < song->num_instruments; i++) { + song->instrument[i] = (Instrument*)malloc(sizeof(Instrument)); + set_default_instrument(song->instrument[i]); + load_instrument_inner(stream, song->instrument[i], version); + } + + UNUSED(rwops); + return false; +} + +bool load_song(TrackerSong* song, Stream* stream) { + char header[sizeof(SONG_FILE_SIG) + 2] = {0}; + size_t rwops = stream_read(stream, (uint8_t*)&header, sizeof(SONG_FILE_SIG) - 1); + header[sizeof(SONG_FILE_SIG)] = '\0'; + + if(strcmp(header, SONG_FILE_SIG) == 0) { + bool result = load_song_inner(song, stream); + UNUSED(result); + } + + UNUSED(rwops); + return false; +} \ No newline at end of file diff --git a/applications/external/flizzer_tracker/tracker_engine/diskop.h b/applications/external/flizzer_tracker/tracker_engine/diskop.h new file mode 100644 index 0000000000..bed7c0190b --- /dev/null +++ b/applications/external/flizzer_tracker/tracker_engine/diskop.h @@ -0,0 +1,12 @@ +#pragma once + +#include "tracker_engine.h" +#include "tracker_engine_defs.h" +#include +#include +#include +#include + +bool load_song(TrackerSong* song, Stream* stream); +bool load_instrument(Instrument* inst, Stream* stream); +void load_instrument_inner(Stream* stream, Instrument* inst, uint8_t version); \ No newline at end of file diff --git a/applications/external/flizzer_tracker/tracker_engine/do_effects.c b/applications/external/flizzer_tracker/tracker_engine/do_effects.c new file mode 100644 index 0000000000..a556840c28 --- /dev/null +++ b/applications/external/flizzer_tracker/tracker_engine/do_effects.c @@ -0,0 +1,466 @@ +#include "do_effects.h" +#include + +#include "../sound_engine/sound_engine.h" +#include "../sound_engine/sound_engine_filter.h" +#include "tracker_engine.h" + +void do_command( + uint16_t opcode, + TrackerEngine* tracker_engine, + uint8_t channel, + uint8_t tick, + bool from_program) { + UNUSED(from_program); + + TrackerEngineChannel* te_channel = &tracker_engine->channel[channel]; + SoundEngineChannel* se_channel = &tracker_engine->sound_engine->channel[channel]; + + switch(opcode & 0x7f00) { + case TE_EFFECT_ARPEGGIO: { + if(tick == 0) { + if(te_channel->fixed_note != 0xffff) { + te_channel->note = te_channel->last_note; + te_channel->fixed_note = 0xffff; + } + + if((opcode & 0xff) == 0xf0) + te_channel->arpeggio_note = te_channel->extarp1; + else if((opcode & 0xff) == 0xf1) + te_channel->arpeggio_note = te_channel->extarp2; + else + te_channel->arpeggio_note = (opcode & 0xff); + } + break; + } + + case TE_EFFECT_PORTAMENTO_UP: { + uint32_t prev = te_channel->note; + + te_channel->note += ((opcode & 0xff) << 2); + if(prev > te_channel->note) te_channel->note = 0xffff; + + te_channel->target_note = te_channel->note; + break; + } + + case TE_EFFECT_PORTAMENTO_DOWN: { + int32_t prev = te_channel->note; + + te_channel->note -= ((opcode & 0xff) << 2); + if(prev < te_channel->note) te_channel->note = 0; + + te_channel->target_note = te_channel->note; + break; + } + + case TE_EFFECT_VIBRATO: { + if(tick == 0) { + if(opcode & 0xff) { + te_channel->flags |= TE_ENABLE_VIBRATO; + + te_channel->vibrato_speed = (opcode & 0xf0); + te_channel->vibrato_depth = ((opcode & 0x0f) << 4); + } + + else { + te_channel->flags &= ~(TE_ENABLE_VIBRATO); + } + } + + break; + } + + case TE_EFFECT_PWM: { + if(tick == 0) { + if(opcode & 0xff) { + te_channel->flags |= TE_ENABLE_PWM; + + te_channel->pwm_speed = (opcode & 0xf0); + te_channel->pwm_depth = ((opcode & 0x0f) << 4); + } + + else { + te_channel->flags &= ~(TE_ENABLE_PWM); + } + } + + break; + } + + case TE_EFFECT_SET_PW: { + if(tick == 0) { + te_channel->pw = ((opcode & 0xff) << 4); + } + + break; + } + + case TE_EFFECT_PW_UP: { + int16_t temp_pw = te_channel->pw + (int16_t)(opcode & 0xff); + + if(temp_pw < 0) temp_pw = 0; + if(temp_pw > 0xfff) temp_pw = 0xfff; + + te_channel->pw = temp_pw; + + break; + } + + case TE_EFFECT_PW_DOWN: { + int16_t temp_pw = te_channel->pw - (int16_t)(opcode & 0xff); + + if(temp_pw < 0) temp_pw = 0; + if(temp_pw > 0xfff) temp_pw = 0xfff; + + te_channel->pw = temp_pw; + + break; + } + + case TE_EFFECT_SET_CUTOFF: { + if(tick == 0) { + te_channel->filter_cutoff = ((opcode & 0xff) << 3); + sound_engine_filter_set_coeff( + &se_channel->filter, te_channel->filter_cutoff, te_channel->filter_resonance); + } + + break; + } + + case TE_EFFECT_VOLUME_FADE: { + if(!(te_channel->channel_flags & TEC_DISABLED)) { + te_channel->volume -= (opcode & 0xf); + if(te_channel->volume > MAX_ADSR_VOLUME) te_channel->volume = 0; + te_channel->volume += ((opcode >> 4) & 0xf); + if(te_channel->volume > MAX_ADSR_VOLUME) te_channel->volume = MAX_ADSR_VOLUME; + + se_channel->adsr.volume = (int32_t)te_channel->volume; + se_channel->adsr.volume = (int32_t)se_channel->adsr.volume * + (int32_t)tracker_engine->master_volume / MAX_ADSR_VOLUME; + } + + break; + } + + case TE_EFFECT_SET_WAVEFORM: { + if(tick == 0) { + se_channel->waveform = (opcode & 0x3f); + } + + break; + } + + case TE_EFFECT_SET_VOLUME: { + if(tick == 0) { + if(!(te_channel->channel_flags & TEC_DISABLED)) { + te_channel->volume = opcode & 0xff; + + se_channel->adsr.volume = (int32_t)te_channel->volume; + se_channel->adsr.volume = (int32_t)se_channel->adsr.volume * + (int32_t)tracker_engine->master_volume / MAX_ADSR_VOLUME; + } + } + + break; + } + + case TE_EFFECT_EXT: { + switch(opcode & 0x7ff0) { + case TE_EFFECT_EXT_TOGGLE_FILTER: { + if(tick == 0) { + if(opcode & 0xf) { + se_channel->flags |= SE_ENABLE_FILTER; + } + + else { + se_channel->flags &= ~SE_ENABLE_FILTER; + } + } + + break; + } + + case TE_EFFECT_EXT_PORTA_DN: { + if(tick == 0) { + int32_t prev = te_channel->note; + + te_channel->note -= (opcode & 0xf); + if(prev < te_channel->note) te_channel->note = 0; + + te_channel->target_note = te_channel->note; + } + + break; + } + + case TE_EFFECT_EXT_PORTA_UP: { + if(tick == 0) { + uint32_t prev = te_channel->note; + + te_channel->note += (opcode & 0xf); + if(prev > te_channel->note) te_channel->note = 0xffff; + + te_channel->target_note = te_channel->note; + } + + break; + } + + case TE_EFFECT_EXT_FILTER_MODE: { + if(tick == 0) { + se_channel->filter_mode = (opcode & 0xf); + } + + break; + } + + case TE_EFFECT_EXT_RETRIGGER: { + if((opcode & 0xf) > 0 && (tick % (opcode & 0xf)) == 0) { + uint8_t prev_vol_tr = te_channel->volume; + uint8_t prev_vol_cyd = se_channel->adsr.volume; + tracker_engine_trigger_instrument_internal( + tracker_engine, channel, te_channel->instrument, te_channel->last_note); + te_channel->volume = prev_vol_tr; + se_channel->adsr.volume = prev_vol_cyd; + } + + break; + } + + case TE_EFFECT_EXT_FINE_VOLUME_DOWN: { + if(tick == 0) { + te_channel->volume -= (opcode & 0xf); + + if(te_channel->volume > MAX_ADSR_VOLUME) te_channel->volume = 0; + + se_channel->adsr.volume = (int32_t)te_channel->volume; + se_channel->adsr.volume = (int32_t)se_channel->adsr.volume * + (int32_t)tracker_engine->master_volume / MAX_ADSR_VOLUME; + } + + break; + } + + case TE_EFFECT_EXT_FINE_VOLUME_UP: { + if(tick == 0) { + te_channel->volume += (opcode & 0xf); + + if(te_channel->volume > MAX_ADSR_VOLUME) te_channel->volume = MAX_ADSR_VOLUME; + + se_channel->adsr.volume = (int32_t)te_channel->volume; + se_channel->adsr.volume = (int32_t)se_channel->adsr.volume * + (int32_t)tracker_engine->master_volume / MAX_ADSR_VOLUME; + } + + break; + } + + case TE_EFFECT_EXT_NOTE_CUT: { + if((opcode & 0xf) <= tick) { + se_channel->adsr.volume = 0; + te_channel->volume = 0; + } + + break; + } + + case TE_EFFECT_EXT_PHASE_RESET: { + if(tick == (opcode & 0xf)) { + se_channel->accumulator = 0; + se_channel->lfsr = RANDOM_SEED; + } + + break; + } + } + + break; + } + + case TE_EFFECT_SET_SPEED_PROG_PERIOD: { + if(tick == 0) { + if(from_program) { + te_channel->program_period = opcode & 0xff; + } + + else { + tracker_engine->song->speed = opcode & 0xff; + } + } + + break; + } + + case TE_EFFECT_CUTOFF_UP: { + te_channel->filter_cutoff += (opcode & 0xff); + + if(te_channel->filter_cutoff > 0x7ff) { + te_channel->filter_cutoff = 0x7ff; + } + + sound_engine_filter_set_coeff( + &se_channel->filter, te_channel->filter_cutoff, te_channel->filter_resonance); + + break; + } + + case TE_EFFECT_CUTOFF_DOWN: { + te_channel->filter_cutoff -= (opcode & 0xff); + + if(te_channel->filter_cutoff > 0x7ff) // unsigned int overflow + { + te_channel->filter_cutoff = 0; + } + + sound_engine_filter_set_coeff( + &se_channel->filter, te_channel->filter_cutoff, te_channel->filter_resonance); + + break; + } + + case TE_EFFECT_SET_RESONANCE: { + if(tick == 0) { + te_channel->filter_resonance = (opcode & 0xff); + sound_engine_filter_set_coeff( + &se_channel->filter, te_channel->filter_cutoff, te_channel->filter_resonance); + } + + break; + } + + case TE_EFFECT_RESONANCE_UP: { + te_channel->filter_resonance += (opcode & 0xff); + + if(te_channel->filter_resonance > 0xff) { + te_channel->filter_resonance = 0xff; + } + + sound_engine_filter_set_coeff( + &se_channel->filter, te_channel->filter_cutoff, te_channel->filter_resonance); + break; + } + + case TE_EFFECT_RESONANCE_DOWN: { + te_channel->filter_resonance -= (opcode & 0xff); + + if(te_channel->filter_resonance > 0xff) { + te_channel->filter_resonance = 0; + } + + sound_engine_filter_set_coeff( + &se_channel->filter, te_channel->filter_cutoff, te_channel->filter_resonance); + break; + } + + case TE_EFFECT_SET_RING_MOD_SRC: { + if(tick == 0) { + se_channel->ring_mod = (opcode & 0xff); + } + + break; + } + + case TE_EFFECT_SET_HARD_SYNC_SRC: { + if(tick == 0) { + se_channel->hard_sync = (opcode & 0xff); + } + + break; + } + + case TE_EFFECT_SET_ATTACK: { + if(tick == 0) { + se_channel->adsr.a = (opcode & 0xff); + + if(se_channel->adsr.envelope_state == ATTACK) { + se_channel->adsr.envelope_speed = + envspd(tracker_engine->sound_engine, se_channel->adsr.a); + } + } + + break; + } + + case TE_EFFECT_SET_DECAY: { + if(tick == 0) { + se_channel->adsr.d = (opcode & 0xff); + + if(se_channel->adsr.envelope_state == DECAY) { + se_channel->adsr.envelope_speed = + envspd(tracker_engine->sound_engine, se_channel->adsr.d); + } + } + + break; + } + + case TE_EFFECT_SET_SUSTAIN: { + if(tick == 0) { + se_channel->adsr.s = (opcode & 0xff); + } + + break; + } + + case TE_EFFECT_SET_RELEASE: { + if(tick == 0) { + se_channel->adsr.r = (opcode & 0xff); + + if(se_channel->adsr.envelope_state == RELEASE) { + se_channel->adsr.envelope_speed = + envspd(tracker_engine->sound_engine, se_channel->adsr.r); + } + } + + break; + } + + case TE_EFFECT_PROGRAM_RESTART: { + if(tick == 0) { + te_channel->program_counter = 0; + te_channel->program_loop = 0; + te_channel->program_period = 0; + te_channel->program_tick = 0; + } + + break; + } + + case TE_EFFECT_PORTA_UP_SEMITONE: { + uint32_t prev = te_channel->note; + + te_channel->note += ((opcode & 0xff) << 8); + if(prev > te_channel->note) te_channel->note = 0xffff; + + te_channel->target_note = te_channel->note; + break; + } + + case TE_EFFECT_PORTA_DOWN_SEMITONE: { + int32_t prev = te_channel->note; + + te_channel->note -= ((opcode & 0xff) << 8); + if(prev < te_channel->note) te_channel->note = 0; + + te_channel->target_note = te_channel->note; + break; + } + + case TE_EFFECT_ARPEGGIO_ABS: { + te_channel->arpeggio_note = 0; + te_channel->fixed_note = ((opcode & 0xff) << 8); + + break; + } + + case TE_EFFECT_TRIGGER_RELEASE: { + sound_engine_enable_gate(tracker_engine->sound_engine, se_channel, 0); + + break; + } + + default: + break; + } +} \ No newline at end of file diff --git a/applications/external/flizzer_tracker/tracker_engine/do_effects.h b/applications/external/flizzer_tracker/tracker_engine/do_effects.h new file mode 100644 index 0000000000..654a61765e --- /dev/null +++ b/applications/external/flizzer_tracker/tracker_engine/do_effects.h @@ -0,0 +1,10 @@ +#include "tracker_engine_defs.h" +#include +#include + +void do_command( + uint16_t opcode, + TrackerEngine* tracker_engine, + uint8_t channel, + uint8_t tick, + bool from_program); \ No newline at end of file diff --git a/applications/external/flizzer_tracker/tracker_engine/tracker_engine.c b/applications/external/flizzer_tracker/tracker_engine/tracker_engine.c new file mode 100644 index 0000000000..5958e3a05e --- /dev/null +++ b/applications/external/flizzer_tracker/tracker_engine/tracker_engine.c @@ -0,0 +1,696 @@ +#include "tracker_engine.h" + +#include "../flizzer_tracker_hal.h" +#include "../macros.h" + +#include "../sound_engine/sound_engine_osc.h" +#include + +void tracker_engine_init(TrackerEngine* tracker_engine, uint8_t rate, SoundEngine* sound_engine) { + memset(tracker_engine, 0, sizeof(TrackerEngine)); + + furi_hal_interrupt_set_isr_ex( + FuriHalInterruptIdTIM2, 14, tracker_engine_timer_isr, (void*)tracker_engine); + tracker_engine_init_hardware(rate); + + tracker_engine->sound_engine = sound_engine; + tracker_engine->rate = rate; +} + +void tracker_engine_deinit_song(TrackerSong* song, bool free_song) { + for(int i = 0; i < MAX_PATTERNS; i++) { + if(song->pattern[i].step != NULL) { + free(song->pattern[i].step); + } + } + + for(int i = 0; i < MAX_INSTRUMENTS; i++) { + if(song->instrument[i] != NULL) { + free(song->instrument[i]); + } + } + + if(free_song) { + free(song); + } +} + +void tracker_engine_deinit(TrackerEngine* tracker_engine, bool free_song) { + tracker_engine_deinit_song(tracker_engine->song, free_song); + + furi_hal_interrupt_set_isr_ex(FuriHalInterruptIdTIM2, 13, NULL, NULL); + tracker_engine_stop(); +} + +void set_note(TrackerSongPatternStep* step, uint8_t note) { + step->note &= 0x80; + step->note |= (note & 0x7f); +} + +void set_instrument(TrackerSongPatternStep* step, uint8_t inst) { + step->note &= 0x7f; + step->inst_vol &= 0x0f; + + step->note |= ((inst & 0x10) << 3); + step->inst_vol |= ((inst & 0xf) << 4); +} + +void set_volume(TrackerSongPatternStep* step, uint8_t vol) { + step->command &= 0x7fff; + step->inst_vol &= 0xf0; + + step->command |= ((vol & 0x10) << 11); + step->inst_vol |= (vol & 0xf); +} + +void set_command(TrackerSongPatternStep* step, uint16_t command) { + step->command &= 0x8000; + step->command |= command & (0x7fff); +} + +void set_default_instrument(Instrument* inst) { + memset(inst, 0, sizeof(Instrument)); + + inst->flags = TE_SET_CUTOFF | TE_SET_PW | TE_ENABLE_VIBRATO; + inst->sound_engine_flags = SE_ENABLE_KEYDOWN_SYNC; + + inst->base_note = MIDDLE_C; + + inst->waveform = SE_WAVEFORM_PULSE; + inst->pw = 0x80; + + inst->adsr.a = 0x4; + inst->adsr.d = 0x28; + inst->adsr.volume = 0x80; + + inst->filter_type = FIL_OUTPUT_LOWPASS; + inst->filter_cutoff = 0xff; + + inst->program_period = 1; + + for(int i = 0; i < INST_PROG_LEN; i++) { + inst->program[i] = TE_PROGRAM_NOP; + } + + inst->vibrato_speed = 0x60; + inst->vibrato_depth = 0x20; + inst->vibrato_delay = 0x20; +} + +void set_empty_pattern(TrackerSongPattern* pattern, uint16_t pattern_length) { + for(uint16_t i = 0; i < pattern_length; i++) { + TrackerSongPatternStep* step = &pattern->step[i]; + + set_note(step, MUS_NOTE_NONE); + set_instrument(step, MUS_NOTE_INSTRUMENT_NONE); + set_volume(step, MUS_NOTE_VOLUME_NONE); + set_command(step, 0); + } +} + +uint8_t tracker_engine_get_note(TrackerSongPatternStep* step) { + return (step->note & 0x7f); +} + +uint8_t tracker_engine_get_instrument(TrackerSongPatternStep* step) { + return ((step->note & 0x80) >> 3) | ((step->inst_vol & 0xf0) >> 4); +} + +uint8_t tracker_engine_get_volume(TrackerSongPatternStep* step) { + return (step->inst_vol & 0xf) | ((step->command & 0x8000) >> 11); +} + +uint16_t tracker_engine_get_command(TrackerSongPatternStep* step) { + return (step->command & 0x7fff); +} + +void tracker_engine_set_note( + TrackerEngine* tracker_engine, + uint8_t chan, + uint16_t note, + bool update_note) { + if(update_note) tracker_engine->channel[chan].note = note; + + sound_engine_set_channel_frequency( + tracker_engine->sound_engine, &tracker_engine->sound_engine->channel[chan], note); +} + +void tracker_engine_set_song(TrackerEngine* tracker_engine, TrackerSong* song) { + tracker_engine->song = song; +} + +void tracker_engine_trigger_instrument_internal( + TrackerEngine* tracker_engine, + uint8_t chan, + Instrument* pinst, + uint16_t note) { + SoundEngineChannel* se_channel = &tracker_engine->sound_engine->channel[chan]; + TrackerEngineChannel* te_channel = &tracker_engine->channel[chan]; + + te_channel->channel_flags = TEC_PLAYING | (te_channel->channel_flags & TEC_DISABLED); + + te_channel->program_period = pinst->program_period; + + if(!(pinst->flags & TE_PROG_NO_RESTART) && pinst->program_period > 0) { + te_channel->channel_flags |= TEC_PROGRAM_RUNNING; + + te_channel->program_counter = 0; + te_channel->program_loop = 1; + te_channel->program_tick = 0; + } + + te_channel->instrument = pinst; + + se_channel->waveform = pinst->waveform; + se_channel->flags = pinst->sound_engine_flags; + + te_channel->flags = pinst->flags; + + te_channel->arpeggio_note = 0; + te_channel->fixed_note = 0xffff; + + note += (uint16_t)(((int16_t)pinst->base_note - MIDDLE_C) << 8); + tracker_engine_set_note(tracker_engine, chan, note + (int16_t)pinst->finetune, true); + + te_channel->last_note = te_channel->target_note = note + (int16_t)pinst->finetune; + + te_channel->extarp1 = te_channel->extarp2 = 0; + + if(pinst->flags & TE_ENABLE_VIBRATO) { + te_channel->vibrato_speed = pinst->vibrato_speed; + te_channel->vibrato_depth = pinst->vibrato_depth; + te_channel->vibrato_delay = pinst->vibrato_delay; + } + + if(pinst->flags & TE_ENABLE_PWM) { + te_channel->pwm_speed = pinst->pwm_speed; + te_channel->pwm_depth = pinst->pwm_depth; + te_channel->pwm_delay = pinst->pwm_delay; + } + + if(pinst->sound_engine_flags & SE_ENABLE_KEYDOWN_SYNC) { + te_channel->vibrato_position = ((ACC_LENGTH / 2 / 2) << 9); + te_channel->pwm_position = ((ACC_LENGTH / 2 / 2) << 9); + + se_channel->accumulator = 0; + se_channel->lfsr = RANDOM_SEED; + } + + if(pinst->flags & TE_SET_CUTOFF) { + te_channel->filter_cutoff = ((uint16_t)pinst->filter_cutoff << 3); + te_channel->filter_resonance = (uint16_t)pinst->filter_resonance; + + se_channel->filter.low = 0; + se_channel->filter.high = 0; + se_channel->filter.band = 0; + + sound_engine_filter_set_coeff( + &se_channel->filter, te_channel->filter_cutoff, te_channel->filter_resonance); + } + + if(pinst->sound_engine_flags & SE_ENABLE_FILTER) { + te_channel->filter_type = pinst->filter_type; + se_channel->filter_mode = te_channel->filter_type; + } + + if(pinst->flags & TE_SET_PW) { + te_channel->pw = (pinst->pw << 4); + se_channel->pw = (pinst->pw << 4); + } + + se_channel->ring_mod = pinst->ring_mod; + se_channel->hard_sync = pinst->hard_sync; + + te_channel->slide_speed = pinst->slide_speed; + + se_channel->adsr.a = pinst->adsr.a; + se_channel->adsr.d = pinst->adsr.d; + se_channel->adsr.s = pinst->adsr.s; + se_channel->adsr.r = pinst->adsr.r; + se_channel->adsr.volume = pinst->adsr.volume; + se_channel->adsr.volume = (int32_t)se_channel->adsr.volume * + (int32_t)tracker_engine->master_volume / MAX_ADSR_VOLUME; + + te_channel->volume = pinst->adsr.volume; + te_channel->volume = + (int32_t)te_channel->volume * (int32_t)tracker_engine->master_volume / MAX_ADSR_VOLUME; + + sound_engine_enable_gate( + tracker_engine->sound_engine, &tracker_engine->sound_engine->channel[chan], true); +} + +void tracker_engine_execute_track_command( + TrackerEngine* tracker_engine, + uint8_t chan, + TrackerSongPatternStep* step, + bool first_tick) { + UNUSED(first_tick); + UNUSED(tracker_engine); + UNUSED(chan); + + uint8_t vol = tracker_engine_get_volume(step); + uint16_t opcode = tracker_engine_get_command(step); + + if(vol != MUS_NOTE_VOLUME_NONE && + !(tracker_engine->channel[chan].channel_flags & TEC_DISABLED)) { + tracker_engine->sound_engine->channel[chan].adsr.volume = + (int32_t)tracker_engine->channel[chan].volume * (int32_t)vol / (MUS_NOTE_VOLUME_NONE); + // tracker_engine->sound_engine->channel[chan].adsr.volume = (int32_t)tracker_engine->sound_engine->channel[chan].adsr.volume * (int32_t)tracker_engine->channel[chan].instrument->adsr.volume / MAX_ADSR_VOLUME * (int32_t)tracker_engine->master_volume / MAX_ADSR_VOLUME; + } + + if(tracker_engine->channel[chan].instrument != NULL && opcode != 0) { + if((opcode & 0x7f00) == TE_EFFECT_ARPEGGIO) { + tracker_engine->channel[chan].extarp1 = ((opcode & 0xf0) >> 4); + tracker_engine->channel[chan].extarp2 = (opcode & 0xf); + } + + else { + do_command(opcode, tracker_engine, chan, tracker_engine->current_tick, false); + } + } + + if(tracker_engine->channel[chan].channel_flags & TEC_DISABLED) { + tracker_engine->sound_engine->channel[chan].adsr.volume = 0; + } +} + +void tracker_engine_execute_program_tick( + TrackerEngine* tracker_engine, + uint8_t chan, + uint8_t advance) { + TrackerEngineChannel* te_channel = &tracker_engine->channel[chan]; + uint8_t tick = te_channel->program_tick; + uint8_t visited[INST_PROG_LEN] = {0}; + +do_it_again:; + + const uint16_t inst = te_channel->instrument->program[tick]; + + if((inst & 0x7fff) == TE_PROGRAM_END) { + te_channel->channel_flags &= ~(TEC_PROGRAM_RUNNING); + return; + } + + uint8_t dont_reloop = 0; + + if((inst & 0x7fff) != TE_PROGRAM_NOP) { + switch(inst & 0x7f00) { + case TE_PROGRAM_JUMP: { + if(!visited[tick]) { + visited[tick] = 1; + tick = inst & (INST_PROG_LEN - 1); + } + + else + return; + + break; + } + + case TE_PROGRAM_LOOP_BEGIN: + break; + + case TE_PROGRAM_LOOP_END: { + if(te_channel->program_loop == (inst & 0xff)) { + if(advance) te_channel->program_loop = 1; + } + + else { + if(advance) ++te_channel->program_loop; + + uint8_t l = 0; + + while((te_channel->instrument->program[tick] & 0x7f00) != TE_PROGRAM_LOOP_BEGIN && + tick > 0) { + --tick; + if(!(te_channel->instrument->program[tick] & 0x8000)) ++l; + } + + --tick; + + dont_reloop = l <= 1; + } + + break; + } + + default: { + do_command(inst, tracker_engine, chan, te_channel->program_counter, true); + break; + } + } + } + + if((inst & 0x7fff) == TE_PROGRAM_NOP || (inst & 0x7f00) != TE_PROGRAM_JUMP) { + ++tick; + if(tick >= INST_PROG_LEN) { + tick = 0; + } + } + + // skip to next on msb + + if(((inst & 0x8000) || ((inst & 0x7f00) == TE_PROGRAM_LOOP_BEGIN) || + ((inst & 0x7f00) == TE_PROGRAM_JUMP)) && + (inst & 0x7fff) != TE_PROGRAM_NOP && !dont_reloop) { + goto do_it_again; + } + + if(advance) { + te_channel->program_tick = tick; + } +} + +void tracker_engine_advance_channel(TrackerEngine* tracker_engine, uint8_t chan) { + SoundEngineChannel* se_channel = &tracker_engine->sound_engine->channel[chan]; + TrackerEngineChannel* te_channel = &tracker_engine->channel[chan]; + + if(te_channel->channel_flags & TEC_PLAYING) { + if(!(se_channel->flags & SE_ENABLE_GATE)) { + te_channel->flags &= ~(TEC_PLAYING); + } + + if(te_channel->slide_speed != 0) { + if(te_channel->target_note > te_channel->note) { + te_channel->note += my_min( + te_channel->slide_speed * 4, te_channel->target_note - te_channel->note); + } + + else if(te_channel->target_note < te_channel->note) { + te_channel->note -= my_min( + te_channel->slide_speed * 4, te_channel->note - te_channel->target_note); + } + } + + if(te_channel->channel_flags & TEC_PROGRAM_RUNNING) { + uint8_t u = (te_channel->program_counter + 1) >= te_channel->program_period; + tracker_engine_execute_program_tick(tracker_engine, chan, u); + ++te_channel->program_counter; + + if(u) te_channel->program_counter = 0; + } + + int16_t vib = 0; + int32_t pwm = 0; + + if(te_channel->flags & TE_ENABLE_VIBRATO) { + if(te_channel->vibrato_delay > 0) { + te_channel->vibrato_delay--; + } + + else { + te_channel->vibrato_position += ((uint32_t)te_channel->vibrato_speed << 21); + vib = + (int32_t)(sound_engine_triangle(te_channel->vibrato_position >> 9) - WAVE_AMP / 2) * + (int32_t)te_channel->vibrato_depth / (256 * 128); + } + } + + if(te_channel->flags & TE_ENABLE_PWM) { + if(te_channel->pwm_delay > 0) { + te_channel->pwm_delay--; + } + + else { + te_channel->pwm_position += + ((uint32_t)te_channel->pwm_speed + << 20); // so minimum PWM speed is even lower than minimum vibrato speed + pwm = ((int32_t)sound_engine_triangle((te_channel->pwm_position) >> 9) - + WAVE_AMP / 2) * + (int32_t)te_channel->pwm_depth / (256 * 16); + } + + int16_t final_pwm = (int16_t)tracker_engine->channel[chan].pw + pwm; + + if(final_pwm < 0) { + final_pwm = 0; + } + + if(final_pwm > 0xfff) { + final_pwm = 0xfff; + } + + tracker_engine->sound_engine->channel[chan].pw = final_pwm; + } + + else { + tracker_engine->sound_engine->channel[chan].pw = tracker_engine->channel[chan].pw; + } + + int32_t chn_note = + (int16_t)(te_channel->fixed_note != 0xffff ? te_channel->fixed_note : te_channel->note) + + vib + ((int16_t)te_channel->arpeggio_note << 8); + + if(chn_note < 0) { + chn_note = 0; + } + + if(chn_note > ((12 * 7 + 11) << 8)) { + chn_note = ((12 * 7 + 11) << 8); // highest note is B-7 + } + + tracker_engine_set_note(tracker_engine, chan, (uint16_t)chn_note, false); + } + + if(tracker_engine->channel[chan].channel_flags & + TEC_DISABLED) // so we can't set some non-zero volme from inst program too + { + tracker_engine->sound_engine->channel[chan].adsr.volume = 0; + } +} + +void tracker_engine_advance_tick(TrackerEngine* tracker_engine) { + if(!(tracker_engine->playing)) return; + + if(!(tracker_engine->sound_engine)) return; + + TrackerSong* song = tracker_engine->song; + + uint16_t opcode = 0; + + for(uint8_t chan = 0; chan < SONG_MAX_CHANNELS; chan++) { + SoundEngineChannel* se_channel = &tracker_engine->sound_engine->channel[chan]; + TrackerEngineChannel* te_channel = &tracker_engine->channel[chan]; + + if(tracker_engine->song) { + uint16_t sequence_position = tracker_engine->sequence_position; + uint8_t current_pattern = + song->sequence.sequence_step[sequence_position].pattern_indices[chan]; + uint8_t pattern_step = tracker_engine->pattern_position; + + TrackerSongPattern* pattern = &song->pattern[current_pattern]; + + uint8_t note_delay = 0; + + opcode = tracker_engine_get_command(&pattern->step[pattern_step]); + + if((opcode & 0x7ff0) == TE_EFFECT_EXT_NOTE_DELAY) { + note_delay = (opcode & 0xf); + } + + if(tracker_engine->current_tick == note_delay) { + uint8_t note = tracker_engine_get_note(&pattern->step[pattern_step]); + uint8_t inst = tracker_engine_get_instrument(&pattern->step[pattern_step]); + + Instrument* pinst = NULL; + + if(inst == MUS_NOTE_INSTRUMENT_NONE) { + pinst = te_channel->instrument; + } + + else { + if(inst < song->num_instruments) { + pinst = song->instrument[inst]; + te_channel->instrument = pinst; + } + } + + if(note == MUS_NOTE_CUT) { + sound_engine_enable_gate(tracker_engine->sound_engine, se_channel, 0); + se_channel->adsr.volume = 0; + te_channel->volume = 0; + } + + if(note == MUS_NOTE_RELEASE) { + sound_engine_enable_gate(tracker_engine->sound_engine, se_channel, 0); + } + + else if( + pinst && note != MUS_NOTE_RELEASE && note != MUS_NOTE_CUT && + note != MUS_NOTE_NONE) { + uint8_t prev_adsr_volume = se_channel->adsr.volume; + + if((opcode & 0x7f00) == TE_EFFECT_SLIDE) { + if(pinst->flags & TE_RETRIGGER_ON_SLIDE) { + uint16_t temp_note = te_channel->note; + tracker_engine_trigger_instrument_internal( + tracker_engine, chan, pinst, note << 8); + te_channel->note = temp_note; + } + + te_channel->target_note = + ((note + pinst->base_note - MIDDLE_C) << 8) + pinst->finetune; + te_channel->slide_speed = (opcode & 0xff); + } + + else if((opcode & 0x7f00) == TE_EFFECT_LEGATO) { + te_channel->note = te_channel->target_note = te_channel->last_note = + ((note + pinst->base_note - MIDDLE_C) << 8) + pinst->finetune; + } + + else { + tracker_engine_trigger_instrument_internal( + tracker_engine, chan, pinst, note << 8); + te_channel->note = + ((note + pinst->base_note - MIDDLE_C) << 8) + pinst->finetune; + + te_channel->target_note = + ((note + pinst->base_note - MIDDLE_C) << 8) + pinst->finetune; + } + + if(inst == MUS_NOTE_INSTRUMENT_NONE) { + se_channel->adsr.volume = prev_adsr_volume; + } + } + } + + tracker_engine_execute_track_command( + tracker_engine, + chan, + &pattern->step[pattern_step], + tracker_engine->current_tick == note_delay); + } + + tracker_engine_advance_channel( + tracker_engine, + chan); // this will be executed even if the song pointer is NULL; handy for live instrument playback from inst editor ("jams") + } + + if(tracker_engine->song) { + tracker_engine->current_tick++; + + if(tracker_engine->current_tick >= song->speed) { + bool flag = true; + + for(int chan = 0; chan < SONG_MAX_CHANNELS; ++chan) { + uint16_t sequence_position = tracker_engine->sequence_position; + uint8_t current_pattern = + song->sequence.sequence_step[sequence_position].pattern_indices[chan]; + uint8_t pattern_step = tracker_engine->pattern_position; + + TrackerSongPattern* pattern = &song->pattern[current_pattern]; + + opcode = tracker_engine_get_command(&pattern->step[pattern_step]); + + if((opcode & 0x7ff0) == TE_EFFECT_EXT_PATTERN_LOOP) { + if(opcode & 0xf) // loop end + { + if(!(tracker_engine->in_loop)) { + tracker_engine->loops_left = (opcode & 0xf); + tracker_engine->in_loop = true; + + for(int j = tracker_engine->pattern_position; j >= 0; j--) { + if(tracker_engine_get_command(&pattern->step[j]) == + TE_EFFECT_EXT_PATTERN_LOOP) // search for loop start + { + tracker_engine->pattern_position = + fmax((int16_t)j - 1, 0); // jump to loop start + + goto out; + } + } + } + + else { + tracker_engine->loops_left--; + + if(tracker_engine->loops_left == 0) { + tracker_engine->in_loop = false; + goto out; + } + + for(int j = tracker_engine->pattern_position; j >= 0; j--) { + if(tracker_engine_get_command(&pattern->step[j]) == + TE_EFFECT_EXT_PATTERN_LOOP) // search for loop start + { + tracker_engine->pattern_position = + fmax((int16_t)j - 1, 0); // jump to loop start + + goto out; + } + } + } + } + + else // loop start + { + } + + out:; + } + + if((opcode & 0x7f00) == TE_EFFECT_SKIP_PATTERN) { + tracker_engine->sequence_position++; + tracker_engine->pattern_position = 0; + + flag = false; + + if(tracker_engine->sequence_position >= song->num_sequence_steps) { + tracker_engine->playing = false; + tracker_engine->sequence_position--; + tracker_engine->pattern_position = song->pattern_length - 1; + + for(int i = 0; i < SONG_MAX_CHANNELS; i++) { + sound_engine_enable_gate( + tracker_engine->sound_engine, + &tracker_engine->sound_engine->channel[i], + false); + } + + goto end_process; + } + } + } + + if(flag) { + tracker_engine->pattern_position++; + } + + tracker_engine->current_tick = 0; + + if(tracker_engine->pattern_position >= song->pattern_length) { + tracker_engine->pattern_position = 0; + + if(song->loop_start != 0 || song->loop_end != 0) { + if(tracker_engine->sequence_position == song->loop_end) { + tracker_engine->sequence_position = + song->loop_start; // infinite loop between loop start and loop end + } + + else { + tracker_engine->sequence_position++; + } + } + + else { + tracker_engine->sequence_position++; + } + + if(tracker_engine->sequence_position >= song->num_sequence_steps) { + tracker_engine->playing = false; + tracker_engine->sequence_position--; + tracker_engine->pattern_position = song->pattern_length - 1; + + for(int i = 0; i < SONG_MAX_CHANNELS; i++) { + sound_engine_enable_gate( + tracker_engine->sound_engine, + &tracker_engine->sound_engine->channel[i], + false); + } + } + } + } + } + +end_process:; +} \ No newline at end of file diff --git a/applications/external/flizzer_tracker/tracker_engine/tracker_engine.h b/applications/external/flizzer_tracker/tracker_engine/tracker_engine.h new file mode 100644 index 0000000000..694fe8f423 --- /dev/null +++ b/applications/external/flizzer_tracker/tracker_engine/tracker_engine.h @@ -0,0 +1,28 @@ +#pragma once + +#include "do_effects.h" +#include "tracker_engine_defs.h" + +void tracker_engine_init(TrackerEngine* tracker_engine, uint8_t rate, SoundEngine* sound_engine); +void tracker_engine_deinit(TrackerEngine* tracker_engine, bool free_song); +void tracker_engine_advance_tick(TrackerEngine* tracker_engine); +void tracker_engine_set_song(TrackerEngine* tracker_engine, TrackerSong* song); +void tracker_engine_deinit_song(TrackerSong* song, bool free_song); +void tracker_engine_trigger_instrument_internal( + TrackerEngine* tracker_engine, + uint8_t chan, + Instrument* pinst, + uint16_t note); + +uint8_t tracker_engine_get_note(TrackerSongPatternStep* step); +uint8_t tracker_engine_get_instrument(TrackerSongPatternStep* step); +uint8_t tracker_engine_get_volume(TrackerSongPatternStep* step); +uint16_t tracker_engine_get_command(TrackerSongPatternStep* step); + +void set_note(TrackerSongPatternStep* step, uint8_t note); +void set_instrument(TrackerSongPatternStep* step, uint8_t inst); +void set_volume(TrackerSongPatternStep* step, uint8_t vol); +void set_command(TrackerSongPatternStep* step, uint16_t command); + +void set_default_instrument(Instrument* inst); +void set_empty_pattern(TrackerSongPattern* pattern, uint16_t pattern_length); \ No newline at end of file diff --git a/applications/external/flizzer_tracker/tracker_engine/tracker_engine_defs.h b/applications/external/flizzer_tracker/tracker_engine/tracker_engine_defs.h new file mode 100644 index 0000000000..ef92b1a44d --- /dev/null +++ b/applications/external/flizzer_tracker/tracker_engine/tracker_engine_defs.h @@ -0,0 +1,232 @@ +#pragma once + +#include +#include +#include + +#include "../sound_engine/sound_engine_defs.h" + +#define INST_PROG_LEN 16 +#define MUS_SONG_NAME_LEN 16 +#define MUS_INST_NAME_LEN (MUS_SONG_NAME_LEN - 3) + +#define SONG_MAX_CHANNELS NUM_CHANNELS +#define MAX_INSTRUMENTS 31 +#define MAX_PATTERN_LENGTH 256 +#define MAX_PATTERNS 256 +#define MAX_SEQUENCE_LENGTH 256 + +#define MUS_NOTE_NONE 127 +#define MUS_NOTE_RELEASE 126 +#define MUS_NOTE_CUT 125 + +#define MUS_NOTE_INSTRUMENT_NONE 31 +#define MUS_NOTE_VOLUME_NONE 31 + +#define SONG_FILE_SIG "FZT!SONG" +#define SONG_FILE_EXT ".fzt" +#define INST_FILE_SIG "FZT!INST" +#define INST_FILE_EXT ".fzi" + +#define TRACKER_ENGINE_VERSION 1 + +#define MIDDLE_C (12 * 4) +#define MAX_NOTE (12 * 7 + 11) + +typedef enum { + TE_ENABLE_VIBRATO = 1, + TE_ENABLE_PWM = 2, + TE_PROG_NO_RESTART = 4, + TE_SET_CUTOFF = 8, + TE_SET_PW = 16, + TE_RETRIGGER_ON_SLIDE = 32, // call trigger instrument function even if slide command is there +} TrackerEngineFlags; + +typedef enum { + TEC_PLAYING = 1, + TEC_PROGRAM_RUNNING = 2, + TEC_DISABLED = 4, +} TrackerEngineChannelFlags; + +typedef enum { + TE_EFFECT_ARPEGGIO = 0x0000, + TE_EFFECT_PORTAMENTO_UP = 0x0100, + TE_EFFECT_PORTAMENTO_DOWN = 0x0200, + TE_EFFECT_SLIDE = 0x0300, + TE_EFFECT_VIBRATO = 0x0400, + TE_EFFECT_PWM = 0x0500, + TE_EFFECT_SET_PW = 0x0600, + TE_EFFECT_PW_DOWN = 0x0700, + TE_EFFECT_PW_UP = 0x0800, + TE_EFFECT_SET_CUTOFF = 0x0900, + TE_EFFECT_VOLUME_FADE = 0x0a00, + TE_EFFECT_SET_WAVEFORM = 0x0b00, + TE_EFFECT_SET_VOLUME = 0x0c00, + TE_EFFECT_SKIP_PATTERN = 0x0d00, + + TE_EFFECT_EXT = 0x0e00, + TE_EFFECT_EXT_TOGGLE_FILTER = 0x0e00, + TE_EFFECT_EXT_PORTA_UP = 0x0e10, + TE_EFFECT_EXT_PORTA_DN = 0x0e20, + TE_EFFECT_EXT_FILTER_MODE = 0x0e30, + TE_EFFECT_EXT_PATTERN_LOOP = + 0x0e60, // e60 = start, e61-e6f = end and indication how many loops you want + TE_EFFECT_EXT_RETRIGGER = 0x0e90, + TE_EFFECT_EXT_FINE_VOLUME_DOWN = 0x0ea0, + TE_EFFECT_EXT_FINE_VOLUME_UP = 0x0eb0, + TE_EFFECT_EXT_NOTE_CUT = 0x0ec0, + TE_EFFECT_EXT_NOTE_DELAY = 0x0ed0, + TE_EFFECT_EXT_PHASE_RESET = 0x0ef0, + + TE_EFFECT_SET_SPEED_PROG_PERIOD = 0x0f00, + TE_EFFECT_CUTOFF_UP = 0x1000, // Gxx + TE_EFFECT_CUTOFF_DOWN = 0x1100, // Hxx + TE_EFFECT_SET_RESONANCE = 0x1200, // Ixx + TE_EFFECT_RESONANCE_UP = 0x1300, // Jxx + TE_EFFECT_RESONANCE_DOWN = 0x1400, // Kxx + + TE_EFFECT_SET_ATTACK = 0x1500, // Lxx + TE_EFFECT_SET_DECAY = 0x1600, // Mxx + TE_EFFECT_SET_SUSTAIN = 0x1700, // Nxx + TE_EFFECT_SET_RELEASE = 0x1800, // Oxx + TE_EFFECT_PROGRAM_RESTART = 0x1900, // Pxx + /* + TE_EFFECT_ = 0x1a00, //Qxx + */ + + TE_EFFECT_SET_RING_MOD_SRC = 0x1b00, // Rxx + TE_EFFECT_SET_HARD_SYNC_SRC = 0x1c00, // Sxx + + TE_EFFECT_PORTA_UP_SEMITONE = 0x1d00, // Txx + TE_EFFECT_PORTA_DOWN_SEMITONE = 0x1e00, // Uxx + /* + TE_EFFECT_ = 0x1f00, //Vxx + TE_EFFECT_ = 0x2000, //Wxx + */ + + TE_EFFECT_LEGATO = 0x2100, // Xxx + TE_EFFECT_ARPEGGIO_ABS = 0x2200, // Yxx + TE_EFFECT_TRIGGER_RELEASE = 0x2300, // Zxx + + /* These effects work only in instrument program */ + TE_PROGRAM_LOOP_BEGIN = 0x7d00, + TE_PROGRAM_LOOP_END = 0x7e00, + TE_PROGRAM_JUMP = 0x7f00, + TE_PROGRAM_NOP = 0x7ffe, + TE_PROGRAM_END = 0x7fff, +} EffectCommandsOpcodes; + +typedef struct { + uint8_t a, d, s, r, volume; +} InstrumentAdsr; + +typedef struct { + char name[MUS_INST_NAME_LEN + 1]; + + uint8_t waveform; + uint16_t flags; + uint16_t sound_engine_flags; + + uint8_t slide_speed; + + InstrumentAdsr adsr; + + uint8_t ring_mod, hard_sync; // 0xff = self + + uint8_t pw; // store only one byte since we don't have the luxury of virtually unlimited memory! + + uint16_t program + [INST_PROG_LEN]; // MSB is unite bit (indicates this and next command must be executed at once) + uint8_t program_period; + + uint8_t vibrato_speed, vibrato_depth, vibrato_delay; + uint8_t pwm_speed, pwm_depth, pwm_delay; + + uint8_t filter_cutoff, filter_resonance, filter_type; + + uint8_t base_note; + int8_t finetune; +} Instrument; + +typedef struct { + Instrument* instrument; + + uint16_t flags; + + uint8_t channel_flags; + + uint16_t note, target_note, last_note, fixed_note; + int16_t arpeggio_note; + + uint8_t volume; + + uint8_t program_counter, program_tick, program_loop, program_period; + + uint16_t filter_cutoff, filter_resonance; + uint8_t filter_type; + + uint8_t vibrato_speed, vibrato_depth, vibrato_delay; + uint8_t pwm_speed, pwm_depth, pwm_delay; + + uint32_t vibrato_position, pwm_position; // basically accumulators + + uint8_t extarp1, extarp2; + + uint16_t pw; + + uint8_t slide_speed; +} TrackerEngineChannel; + +typedef struct { + uint8_t note; // MSB is used for instrument number MSB + uint8_t inst_vol; // high nibble + MSB from note = instrument, low nibble = 4 volume LSBs + uint16_t command; // MSB used as volume MSB +} TrackerSongPatternStep; + +typedef struct { + TrackerSongPatternStep* step; +} TrackerSongPattern; + +typedef struct { + uint8_t pattern_indices[SONG_MAX_CHANNELS]; +} TrackerSongSequenceStep; + +typedef struct { + TrackerSongSequenceStep sequence_step[MAX_SEQUENCE_LENGTH]; +} TrackerSongSequence; + +typedef struct { + Instrument* instrument[MAX_INSTRUMENTS]; + TrackerSongPattern pattern[MAX_PATTERNS]; + TrackerSongSequence sequence; + + uint8_t num_patterns, num_instruments; + uint16_t num_sequence_steps; + uint16_t pattern_length; + + char song_name[MUS_SONG_NAME_LEN + 1]; + uint8_t speed, rate; + + uint8_t loop_start, loop_end; +} TrackerSong; + +typedef struct { + TrackerEngineChannel channel[SONG_MAX_CHANNELS]; + + TrackerSong* song; + SoundEngine* sound_engine; + + uint16_t pattern_position, sequence_position; + int16_t current_tick; + uint16_t absolute_position; // sequence_position * pattern_length + pattern_position + + uint8_t speed, rate; + uint8_t master_volume; + + bool playing; // if we reach the end of the song and song does not loop we just stop there + + bool in_loop; // for E6X (pattern loop) command + uint8_t loops_left; + + // uint32_t counter; //for debug +} TrackerEngine; \ No newline at end of file diff --git a/applications/external/flizzer_tracker/util.c b/applications/external/flizzer_tracker/util.c new file mode 100644 index 0000000000..bbf3119eef --- /dev/null +++ b/applications/external/flizzer_tracker/util.c @@ -0,0 +1,202 @@ +#include "util.h" +#include "macros.h" + +void reset_buffer(SoundEngine* sound_engine) { + for(uint16_t i = 0; i < sound_engine->audio_buffer_size; i++) { + sound_engine->audio_buffer[i] = 512; + } +} + +void stop_song(FlizzerTrackerApp* tracker) { + tracker->tracker_engine.playing = false; + tracker->editing = tracker->was_editing; + + for(int i = 0; i < SONG_MAX_CHANNELS; i++) { + tracker->sound_engine.channel[i].adsr.volume = 0; + tracker->tracker_engine.channel[i].channel_flags &= ~(TEC_PROGRAM_RUNNING); + } + + stop(); + + reset_buffer(&tracker->sound_engine); +} + +void play_song(FlizzerTrackerApp* tracker, bool from_cursor) { + uint16_t temppos = tracker->tracker_engine.pattern_position; + + stop_song(tracker); + + sound_engine_dma_init( + (uint32_t)tracker->sound_engine.audio_buffer, tracker->sound_engine.audio_buffer_size); + + tracker->tracker_engine.playing = true; + + tracker->was_editing = tracker->editing; + tracker->editing = false; + + if(!(from_cursor)) { + tracker->tracker_engine.pattern_position = 0; + temppos = 0; + } + + tracker_engine_timer_init(tracker->song.rate); + + /*sound_engine_init_hardware(tracker->sound_engine.sample_rate, + tracker->sound_engine.external_audio_output, + tracker->sound_engine.audio_buffer, + tracker->sound_engine.audio_buffer_size); + tracker_engine_init_hardware(tracker->song.rate);*/ + + tracker->tracker_engine.current_tick = 0; + tracker_engine_set_song(&tracker->tracker_engine, &tracker->song); + + for(uint8_t i = 0; i < SONG_MAX_CHANNELS; i++) { + bool was_disabled = tracker->tracker_engine.channel[i].channel_flags & TEC_DISABLED; + + memset(&tracker->sound_engine.channel[i], 0, sizeof(SoundEngineChannel)); + memset(&tracker->tracker_engine.channel[i], 0, sizeof(TrackerEngineChannel)); + + if(was_disabled) { + tracker->tracker_engine.channel[i].channel_flags |= TEC_DISABLED; + } + } + + tracker->tracker_engine.pattern_position = temppos; + + play(); +} + +bool is_pattern_empty(TrackerSong* song, uint8_t pattern) { + TrackerSongPattern song_pattern = song->pattern[pattern]; + + for(int i = 0; i < song->pattern_length; i++) { + TrackerSongPatternStep* step = &song_pattern.step[i]; + + if(tracker_engine_get_note(step) != MUS_NOTE_NONE || + tracker_engine_get_instrument(step) != MUS_NOTE_INSTRUMENT_NONE || + tracker_engine_get_volume(step) != MUS_NOTE_VOLUME_NONE || + tracker_engine_get_command(step) != 0) { + return false; + } + } + + return true; +} + +bool check_and_allocate_pattern(TrackerSong* song, uint8_t pattern) { + if(pattern < song->num_patterns) // we can set this pattern since it already exists + { + return true; + } + + else { + if(song->pattern[pattern - 1].step == NULL) + return false; // if we hop through several patterns (e.g. editing upper digit) + + if(!(is_pattern_empty( + song, pattern - 1))) // don't let the user flood the song with empty patterns + { + song->pattern[pattern].step = + malloc(sizeof(TrackerSongPatternStep) * song->pattern_length); + set_empty_pattern(&song->pattern[pattern], song->pattern_length); + song->num_patterns++; + return true; + } + + else { + return false; + } + } +} + +void resize_pattern(TrackerSongPattern* pattern, uint16_t old_length, uint16_t new_length) { + TrackerSongPattern temp; + temp.step = malloc((new_length) * sizeof(TrackerSongPatternStep)); + + set_empty_pattern(&temp, new_length); + memcpy( + temp.step, pattern->step, my_min(old_length, new_length) * sizeof(TrackerSongPatternStep)); + + free(pattern->step); + pattern->step = temp.step; +} + +void change_pattern_length(TrackerSong* song, uint16_t new_length) { + for(int i = 0; i < MAX_PATTERNS; i++) { + if(song->pattern[i].step) { + resize_pattern(&song->pattern[i], song->pattern_length, new_length); + } + } + + song->pattern_length = new_length; +} + +bool is_default_instrument(Instrument* inst) { + Instrument* ref = malloc(sizeof(Instrument)); + set_default_instrument(ref); + bool is_default = memcmp(ref, inst, sizeof(Instrument)) != 0 ? false : true; + free(ref); + return is_default; +} + +bool check_and_allocate_instrument(TrackerSong* song, uint8_t inst) { + if(inst < song->num_instruments) // we can go to this instrument since it already exists + { + return true; + } + + else { + if(inst >= MAX_INSTRUMENTS) return false; + + if(!(is_default_instrument( + song->instrument + [inst - 1]))) // don't let the user flood the song with default instrument + { + song->instrument[inst] = malloc(sizeof(Instrument)); + set_default_instrument(song->instrument[inst]); + song->num_instruments++; + return true; + } + + else { + return false; + } + } +} + +void set_default_song(FlizzerTrackerApp* tracker) { + tracker->tracker_engine.master_volume = 0x80; + + tracker->song.speed = 6; + tracker->song.rate = tracker->tracker_engine.rate; + tracker->song.num_instruments = 1; + tracker->song.num_patterns = 5; + tracker->song.num_sequence_steps = 1; + tracker->song.pattern_length = 64; + + tracker->song.sequence.sequence_step[0].pattern_indices[0] = 1; + tracker->song.sequence.sequence_step[0].pattern_indices[1] = 2; + tracker->song.sequence.sequence_step[0].pattern_indices[2] = 3; + tracker->song.sequence.sequence_step[0].pattern_indices[3] = 4; + + for(int i = 0; i < 5; i++) { + tracker->song.pattern[i].step = malloc(64 * sizeof(TrackerSongPatternStep)); + memset(tracker->song.pattern[i].step, 0, 64 * sizeof(TrackerSongPatternStep)); + } + + for(int i = 0; i < 64; ++i) { + for(int j = 0; j < 5; j++) { + set_note(&tracker->song.pattern[j].step[i], MUS_NOTE_NONE); + + set_instrument(&tracker->song.pattern[j].step[i], MUS_NOTE_INSTRUMENT_NONE); + + set_volume(&tracker->song.pattern[j].step[i], MUS_NOTE_VOLUME_NONE); + } + } + + tracker->song.instrument[0] = malloc(sizeof(Instrument)); + + set_default_instrument(tracker->song.instrument[0]); + + tracker->tracker_engine.playing = false; +} \ No newline at end of file diff --git a/applications/external/flizzer_tracker/util.h b/applications/external/flizzer_tracker/util.h new file mode 100644 index 0000000000..45a2b0f078 --- /dev/null +++ b/applications/external/flizzer_tracker/util.h @@ -0,0 +1,26 @@ +#pragma once + +#include +#include + +#include "flizzer_tracker.h" +#include "sound_engine/sound_engine_defs.h" +#include "tracker_engine/tracker_engine.h" +#include "tracker_engine/tracker_engine_defs.h" + +#include "macros.h" + +#define clamp(val, add, _min, _max) val = my_min(_max, my_max(_min, (int32_t)val + add)) +#define flipbit(val, bit) \ + { val ^= bit; }; + +void reset_buffer(SoundEngine* sound_engine); +void play_song(FlizzerTrackerApp* tracker, bool from_cursor); +void stop_song(FlizzerTrackerApp* tracker); + +bool is_pattern_empty(TrackerSong* song, uint8_t pattern); +bool check_and_allocate_pattern(TrackerSong* song, uint8_t pattern); +void change_pattern_length(TrackerSong* song, uint16_t new_length); + +bool check_and_allocate_instrument(TrackerSong* song, uint8_t inst); +void set_default_song(FlizzerTrackerApp* tracker); \ No newline at end of file diff --git a/applications/external/flizzer_tracker/view/char_array.c b/applications/external/flizzer_tracker/view/char_array.c new file mode 100644 index 0000000000..7c17138ae5 --- /dev/null +++ b/applications/external/flizzer_tracker/view/char_array.c @@ -0,0 +1,4 @@ +const char to_char_array[] = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', +}; \ No newline at end of file diff --git a/applications/external/flizzer_tracker/view/instrument_editor.c b/applications/external/flizzer_tracker/view/instrument_editor.c new file mode 100644 index 0000000000..b931a5de52 --- /dev/null +++ b/applications/external/flizzer_tracker/view/instrument_editor.c @@ -0,0 +1,669 @@ +#include "instrument_editor.h" +#include "pattern_editor.h" + +#include "../macros.h" +#include "opcode_description.h" + +#include + +void draw_inst_flag( + FlizzerTrackerApp* tracker, + Canvas* canvas, + uint8_t focus, + uint8_t param, + const char* text, + uint8_t x, + uint8_t y, + uint16_t flags, + uint16_t mask) { + canvas_draw_icon(canvas, x, y - 5, ((flags & mask) ? &I_checkbox_checked : &I_checkbox_empty)); + canvas_draw_str(canvas, x + 6, y, text); + + if(tracker->focus == focus && tracker->selected_param == param && tracker->editing) { + if(text[strlen(text) - 1] == ':') { + canvas_draw_box(canvas, x + 5, y - 6, strlen(text) * 4 - 1, 7); + } + + else { + canvas_draw_box(canvas, x + 5, y - 6, strlen(text) * 4 + 1, 7); + } + } + + if(tracker->focus == focus && tracker->selected_param == param && !(tracker->editing)) { + if(text[strlen(text) - 1] == ':') { + canvas_draw_frame(canvas, x + 5, y - 6, strlen(text) * 4 - 1, 7); + } + + else { + canvas_draw_frame(canvas, x + 5, y - 6, strlen(text) * 4 + 1, 7); + } + } +} + +void draw_inst_text_one_digit( + FlizzerTrackerApp* tracker, + Canvas* canvas, + uint8_t focus, + uint8_t param, + const char* text, + uint8_t x, + uint8_t y, + uint8_t value) // text MUST end with semicolon +{ + canvas_draw_str(canvas, x, y, text); + char buffer[4]; + snprintf(buffer, sizeof(buffer), "%01X", (value & 0xF)); + canvas_draw_str(canvas, x + strlen(text) * 4 - 2, y, buffer); + + if(tracker->focus == focus && tracker->selected_param == param && tracker->editing) { + canvas_draw_box(canvas, x + strlen(text) * 4 - 3, y - 6, 5, 7); + } + + if(tracker->focus == focus && tracker->selected_param == param && !(tracker->editing)) { + canvas_draw_frame(canvas, x + strlen(text) * 4 - 3, y - 6, 5, 7); + } +} + +void draw_inst_text_two_digits( + FlizzerTrackerApp* tracker, + Canvas* canvas, + uint8_t focus, + uint8_t param, + const char* text, + uint8_t x, + uint8_t y, + uint8_t value) // text MUST end with semicolon +{ + canvas_draw_str(canvas, x, y, text); + char buffer[4]; + snprintf(buffer, sizeof(buffer), "%02X", value); + canvas_draw_str(canvas, x + strlen(text) * 4 - 2, y, buffer); + + if(tracker->focus == focus && tracker->selected_param == param && tracker->editing) { + canvas_draw_box( + canvas, x + strlen(text) * 4 + 4 * tracker->current_digit - 3, y - 6, 5, 7); + } + + if(tracker->focus == focus && tracker->selected_param == param && !(tracker->editing)) { + canvas_draw_frame( + canvas, x + strlen(text) * 4 + 4 * tracker->current_digit - 3, y - 6, 5, 7); + } +} + +static const char* filter_types[] = { + "NONE", + "LOW", + "HIGH", + "BAND", + "LOHI", + "HIBD", + "LOBD", + "ALL", +}; + +static const char* instrument_editor_params_description[] = { + "CURRENT INSTRUMENT", + "CURRENT INSTRUMENT NAME", + "INSTRUMENT BASE NOTE", + "INSTRUMENT FINETUNE", + "SLIDE SPEED", + "SET PULSE WIDTH ON KEYDOWN", + "PULSE WIDTH", + "SET FILTER PARAMETERS ON KEYDOWN", + "NOISE WAVEFORM", + "PULSE WAVEFORM", + "TRIANGLE WAVEFORM", + "SAWTOOTH WAVEFORM", + "METALLIC NOISE WAVEFORM", + "SINE WAVEFORM", + "ENVELOPE ATTACK", + "ENVELOPE DECAY", + "ENVELOPE SUSTAIN", + "ENVELOPE RELEASE", + "ENVELOPE VOLUME", + "ENABLE FILTER", + "FILTER CUTOFF FREQUENCY", + "FILTER RESONANCE", + "FILTER TYPE (NONE=OFF)", + "ENABLE RING MODULATION", + "RINGMOD SOURCE CHANNEL (F=SELF)", + "ENABLE HARD SYNC", + "HARDSYNC SOURCE CHANNEL (F=SELF)", + "RETRIGGER INSTRUMENT ON SLIDE", + "SYNC OSCILLATORS ON KEYDOWN", + "ENABLE VIBRATO", + "VIBRATO SPEED", + "VIBRATO DEPTH", + "VIBRATO DELAY (IN TICKS)", + "ENABLE PWM", + "PWM SPEED", + "PWM DEPTH", + "PWM DELAY (IN TICKS)", + "DON'T RESTART PROGRAM ON KEYDOWN", + "PROG.PERIOD (00 = PROGRAM OFF)", +}; + +void draw_instrument_view(Canvas* canvas, FlizzerTrackerApp* tracker) { + SoundEngineChannel* se_channel = &tracker->sound_engine.channel[0]; + if(!(se_channel->flags & SE_ENABLE_GATE) && tracker->tracker_engine.song == NULL) { + stop(); + tracker->tracker_engine.playing = false; + tracker_engine_set_song(&tracker->tracker_engine, &tracker->song); + } + + char buffer[30]; + Instrument* inst = tracker->song.instrument[tracker->current_instrument]; + uint8_t shift = tracker->inst_editor_shift; + + if(shift < 6) { + snprintf(buffer, sizeof(buffer), "INST:%c", to_char(tracker->current_instrument)); + draw_generic_n_digit_field( + tracker, canvas, EDIT_INSTRUMENT, INST_CURRENTINSTRUMENT, buffer, 0, 5 - shift, 1); + snprintf( + buffer, + sizeof(buffer), + "%s", + tracker->song.instrument[tracker->current_instrument]->name); + draw_generic_n_digit_field( + tracker, canvas, EDIT_INSTRUMENT, INST_INSTRUMENTNAME, buffer, 4 * 7 - 1, 5 - shift, 1); + } + + if(shift < 12) { + snprintf(buffer, sizeof(buffer), "NOTE:%s", notename(inst->base_note)); + canvas_draw_str(canvas, 0, 11 - shift, buffer); + + if(tracker->editing && tracker->focus == EDIT_INSTRUMENT && + tracker->selected_param == INST_CURRENT_NOTE) { + if(tracker->current_digit) { + canvas_draw_box(canvas, 19 + 2 * 4, 5 - shift, 5, 7); + } + + else { + canvas_draw_box(canvas, 19, 5 - shift, 5 + 4, 7); + } + } + + if(!(tracker->editing) && tracker->focus == EDIT_INSTRUMENT && + tracker->selected_param == INST_CURRENT_NOTE) { + if(tracker->current_digit) { + canvas_draw_frame(canvas, 19 + 2 * 4, 5 - shift, 5, 7); + } + + else { + canvas_draw_frame(canvas, 19, 5 - shift, 5 + 4, 7); + } + } + + snprintf(buffer, sizeof(buffer), "FINE:%+02d", inst->finetune); + canvas_draw_str(canvas, 37, 11 - shift, buffer); + + if(tracker->editing && tracker->focus == EDIT_INSTRUMENT && + tracker->selected_param == INST_FINETUNE) { + if(tracker->current_digit) { + canvas_draw_box(canvas, 60 + 4, 5 - shift, 5, 7); + } + + else { + canvas_draw_box(canvas, 60, 5 - shift, 5, 7); + } + } + + if(!(tracker->editing) && tracker->focus == EDIT_INSTRUMENT && + tracker->selected_param == INST_FINETUNE) { + if(tracker->current_digit) { + canvas_draw_frame(canvas, 60 + 4, 5 - shift, 5, 7); + } + + else { + canvas_draw_frame(canvas, 60, 5 - shift, 5, 7); + } + } + } + + draw_inst_text_two_digits( + tracker, + canvas, + EDIT_INSTRUMENT, + INST_SLIDESPEED, + "SL.SPD:", + 0, + 17 - shift, + inst->slide_speed); + + draw_inst_flag( + tracker, canvas, EDIT_INSTRUMENT, INST_SETPW, "PW:", 36, 17 - shift, inst->flags, TE_SET_PW); + draw_inst_text_two_digits( + tracker, canvas, EDIT_INSTRUMENT, INST_PW, "", 54, 17 - shift, inst->pw); + draw_inst_flag( + tracker, + canvas, + EDIT_INSTRUMENT, + INST_SETCUTOFF, + "CUT", + 61, + 17 - shift, + inst->flags, + TE_SET_CUTOFF); + + draw_inst_flag( + tracker, + canvas, + EDIT_INSTRUMENT, + INST_WAVE_NOISE, + "N", + 0, + 23 - shift, + inst->waveform, + SE_WAVEFORM_NOISE); + draw_inst_flag( + tracker, + canvas, + EDIT_INSTRUMENT, + INST_WAVE_PULSE, + "P", + 10, + 23 - shift, + inst->waveform, + SE_WAVEFORM_PULSE); + draw_inst_flag( + tracker, + canvas, + EDIT_INSTRUMENT, + INST_WAVE_TRIANGLE, + "T", + 20, + 23 - shift, + inst->waveform, + SE_WAVEFORM_TRIANGLE); + draw_inst_flag( + tracker, + canvas, + EDIT_INSTRUMENT, + INST_WAVE_SAWTOOTH, + "S", + 30, + 23 - shift, + inst->waveform, + SE_WAVEFORM_SAW); + draw_inst_flag( + tracker, + canvas, + EDIT_INSTRUMENT, + INST_WAVE_NOISE_METAL, + "M", + 40, + 23 - shift, + inst->waveform, + SE_WAVEFORM_NOISE_METAL); + draw_inst_flag( + tracker, + canvas, + EDIT_INSTRUMENT, + INST_WAVE_SINE, + "SINE", + 50, + 23 - shift, + inst->waveform, + SE_WAVEFORM_SINE); + + draw_inst_text_two_digits( + tracker, canvas, EDIT_INSTRUMENT, INST_ATTACK, "A:", 0, 29 - shift, inst->adsr.a); + draw_inst_text_two_digits( + tracker, canvas, EDIT_INSTRUMENT, INST_DECAY, "D:", 16, 29 - shift, inst->adsr.d); + draw_inst_text_two_digits( + tracker, canvas, EDIT_INSTRUMENT, INST_SUSTAIN, "S:", 32, 29 - shift, inst->adsr.s); + draw_inst_text_two_digits( + tracker, canvas, EDIT_INSTRUMENT, INST_RELEASE, "R:", 48, 29 - shift, inst->adsr.r); + draw_inst_text_two_digits( + tracker, canvas, EDIT_INSTRUMENT, INST_VOLUME, "V:", 64, 29 - shift, inst->adsr.volume); + + draw_inst_flag( + tracker, + canvas, + EDIT_INSTRUMENT, + INST_ENABLEFILTER, + "FIL", + 0, + 35 - shift, + inst->sound_engine_flags, + SE_ENABLE_FILTER); + draw_inst_text_two_digits( + tracker, + canvas, + EDIT_INSTRUMENT, + INST_FILTERCUTOFF, + "CUT:", + 20, + 35 - shift, + inst->filter_cutoff); + draw_inst_text_two_digits( + tracker, + canvas, + EDIT_INSTRUMENT, + INST_FILTERRESONANCE, + "RES:", + 44, + 35 - shift, + inst->filter_resonance); + + snprintf(buffer, sizeof(buffer), "TYPE:%s", filter_types[inst->filter_type]); + canvas_draw_str(canvas, 0, 41 - shift, buffer); + + if(tracker->editing && tracker->focus == EDIT_INSTRUMENT && + tracker->selected_param == INST_FILTERTYPE) { + canvas_draw_box( + canvas, 19, 35 - shift, strlen(filter_types[inst->filter_type]) * 4 + 1, 7); + } + + if(!(tracker->editing) && tracker->focus == EDIT_INSTRUMENT && + tracker->selected_param == INST_FILTERTYPE) { + canvas_draw_frame( + canvas, 19, 35 - shift, strlen(filter_types[inst->filter_type]) * 4 + 1, 7); + } + + draw_inst_flag( + tracker, + canvas, + EDIT_INSTRUMENT, + INST_ENABLERINGMOD, + "R:", + 38, + 41 - shift, + inst->sound_engine_flags, + SE_ENABLE_RING_MOD); + draw_inst_text_one_digit( + tracker, canvas, EDIT_INSTRUMENT, INST_RINGMODSRC, "", 52, 41 - shift, inst->ring_mod); + draw_inst_flag( + tracker, + canvas, + EDIT_INSTRUMENT, + INST_ENABLEHARDSYNC, + "H:", + 56, + 41 - shift, + inst->sound_engine_flags, + SE_ENABLE_HARD_SYNC); + draw_inst_text_one_digit( + tracker, canvas, EDIT_INSTRUMENT, INST_HARDSYNCSRC, "", 70, 41 - shift, inst->hard_sync); + + draw_inst_flag( + tracker, + canvas, + EDIT_INSTRUMENT, + INST_RETRIGGERONSLIDE, + "SL.RETRIG", + 0, + 47 - shift, + inst->flags, + TE_RETRIGGER_ON_SLIDE); + draw_inst_flag( + tracker, + canvas, + EDIT_INSTRUMENT, + INST_ENABLEKEYSYNC, + "KSYNC", + 44, + 47 - shift, + inst->sound_engine_flags, + SE_ENABLE_KEYDOWN_SYNC); + + draw_inst_flag( + tracker, + canvas, + EDIT_INSTRUMENT, + INST_ENABLEVIBRATO, + "VIB", + 0, + 53 - shift, + inst->flags, + TE_ENABLE_VIBRATO); + draw_inst_text_two_digits( + tracker, + canvas, + EDIT_INSTRUMENT, + INST_VIBRATOSPEED, + "S:", + 20, + 53 - shift, + inst->vibrato_speed); + draw_inst_text_two_digits( + tracker, + canvas, + EDIT_INSTRUMENT, + INST_VIBRATODEPTH, + "D:", + 36, + 53 - shift, + inst->vibrato_depth); + draw_inst_text_two_digits( + tracker, + canvas, + EDIT_INSTRUMENT, + INST_VIBRATODELAY, + "DEL:", + 52, + 53 - shift, + inst->vibrato_delay); + + if(shift >= 6) { + draw_inst_flag( + tracker, + canvas, + EDIT_INSTRUMENT, + INST_ENABLEPWM, + "PWM", + 0, + 59 - shift, + inst->flags, + TE_ENABLE_PWM); + draw_inst_text_two_digits( + tracker, canvas, EDIT_INSTRUMENT, INST_PWMSPEED, "S:", 20, 59 - shift, inst->pwm_speed); + draw_inst_text_two_digits( + tracker, canvas, EDIT_INSTRUMENT, INST_PWMDEPTH, "D:", 36, 59 - shift, inst->pwm_depth); + draw_inst_text_two_digits( + tracker, + canvas, + EDIT_INSTRUMENT, + INST_PWMDELAY, + "DEL:", + 52, + 59 - shift, + inst->pwm_delay); + } + + if(shift >= 12) { + draw_inst_flag( + tracker, + canvas, + EDIT_INSTRUMENT, + INST_PROGRESTART, + "NO PROG.RESTART", + 0, + 65 - shift, + inst->flags, + TE_PROG_NO_RESTART); + } + + draw_inst_text_two_digits( + tracker, + canvas, + EDIT_INSTRUMENT, + INST_PROGRAMEPERIOD, + "P.PERIOD:", + 81, + 56, + inst->program_period); + + canvas_draw_line(canvas, 0, 57, 127, 57); + canvas_draw_line(canvas, 79, 0, 79, 56); + canvas_draw_line(canvas, 80, 49, 127, 49); + + if(tracker->focus == EDIT_INSTRUMENT) { + canvas_draw_str( + canvas, 0, 64, instrument_editor_params_description[tracker->selected_param]); + } +} + +char command_get_char(uint16_t command) { + if((command >> 8) < 36) { + return to_char_array[(command >> 8)]; + } + + if(command == TE_PROGRAM_END) { + return ':'; + } + + if((command & 0xff00) == TE_PROGRAM_JUMP) { + return '^'; + } + + if((command & 0xff00) == TE_PROGRAM_LOOP_END) { + return '>'; + } + + if((command & 0xff00) == TE_PROGRAM_LOOP_BEGIN) { + return '<'; + } + + return '?'; +} + +void draw_program_step(Canvas* canvas, uint8_t y, FlizzerTrackerApp* tracker, uint8_t index) { + char buffer[15]; + + Instrument* inst = tracker->song.instrument[tracker->current_instrument]; + uint16_t opcode = inst->program[index]; + + if(opcode != TE_PROGRAM_NOP) { + if((opcode & 0x7f00) == TE_EFFECT_ARPEGGIO) { + if((opcode & 0xff) != 0xf0 && (opcode & 0xff) != 0xf1) { + snprintf( + buffer, + sizeof(buffer), + "%01X %c%02X %s", + index, + command_get_char(opcode & 0x7fff), + (opcode & 0xff), + notename(my_min( + 12 * 7 + 11, + (opcode & 0xff) + + tracker->song.instrument[tracker->current_instrument]->base_note))); + } + + else { + snprintf( + buffer, + sizeof(buffer), + "%01X %c%02X %s", + index, + command_get_char(opcode & 0x7fff), + (opcode & 0xff), + notename((opcode & 0xff))); + } + } + + else if((opcode & 0x7f00) == TE_EFFECT_ARPEGGIO_ABS) { + snprintf( + buffer, + sizeof(buffer), + "%01X %c%02X F.%s", + index, + command_get_char(opcode & 0x7fff), + (opcode & 0xff), + notename(opcode & 0xff)); + } + + else { + snprintf( + buffer, + sizeof(buffer), + "%01X %c%02X %s", + index, + command_get_char(opcode & 0x7fff), + (opcode & 0xff), + get_opcode_description(opcode, true) ? get_opcode_description(opcode, true) : ""); + } + + if(opcode & 0x8000) { + if(index == 0) { + canvas_draw_line(canvas, 84 + 4 * 4 + 2, y, 84 + 4 * 4 + 2, y - 3); + canvas_draw_dot(canvas, 84 + 4 * 4 + 1, y - 4); + } + + if(index > 0 && !(inst->program[index - 1] & 0x8000)) { + canvas_draw_line(canvas, 84 + 4 * 4 + 2, y, 84 + 4 * 4 + 2, y - 3); + canvas_draw_dot(canvas, 84 + 4 * 4 + 1, y - 4); + } + + if(index > 0 && (inst->program[index - 1] & 0x8000)) { + canvas_draw_line(canvas, 84 + 4 * 4 + 2, y, 84 + 4 * 4 + 2, y - 5); + } + } + + else { + if(index > 0 && (inst->program[index - 1] & 0x8000)) { + canvas_draw_line(canvas, 84 + 4 * 4 + 2, y - 3, 84 + 4 * 4 + 2, y - 5); + canvas_draw_dot(canvas, 84 + 4 * 4 + 1, y - 2); + } + } + } + + else { + snprintf(buffer, sizeof(buffer), "%01X ---", index); + } + + canvas_draw_str(canvas, 81, y, buffer); +} + +void draw_instrument_program_view(Canvas* canvas, FlizzerTrackerApp* tracker) { + Instrument* inst = tracker->song.instrument[tracker->current_instrument]; + + for(uint8_t i = tracker->program_position; + i < my_min(INST_PROG_LEN, tracker->program_position + 8); + i++) { + draw_program_step(canvas, 6 + 6 * i - tracker->program_position * 6, tracker, i); + + if(i == tracker->current_program_step && tracker->focus == EDIT_PROGRAM) { + if(tracker->editing) { + canvas_draw_box( + canvas, + 80 + 8 + tracker->current_digit * 4, + 6 * i - tracker->program_position * 6, + 5, + 7); + } + + else { + canvas_draw_box(canvas, 80, 6 * i - tracker->program_position * 6, 5, 7); + } + } + } + + // draw arrow pointing at current program step + + for(uint8_t i = 0; i < SONG_MAX_CHANNELS; i++) { + if(tracker->tracker_engine.channel[i].instrument == inst && + (tracker->tracker_engine.channel[i].channel_flags & TEC_PROGRAM_RUNNING) && + (tracker->tracker_engine.sound_engine->channel[i].flags & SE_ENABLE_GATE)) { + if(tracker->tracker_engine.channel[i].program_tick >= tracker->program_position && + tracker->tracker_engine.channel[i].program_tick < tracker->program_position + 8) { + canvas_draw_str( + canvas, + 85, + 6 * tracker->tracker_engine.channel[i].program_tick - + tracker->program_position * 6 + 6, + ">"); + break; + } + } + } + + if(tracker->focus == EDIT_PROGRAM) { + uint16_t opcode = (inst->program[tracker->current_program_step] & 0x7fff); + canvas_draw_str( + canvas, + 0, + 64, + get_opcode_description(opcode, false) ? get_opcode_description(opcode, false) : ""); + } +} \ No newline at end of file diff --git a/applications/external/flizzer_tracker/view/instrument_editor.h b/applications/external/flizzer_tracker/view/instrument_editor.h new file mode 100644 index 0000000000..b8656c0116 --- /dev/null +++ b/applications/external/flizzer_tracker/view/instrument_editor.h @@ -0,0 +1,11 @@ +#pragma once + +#include "../flizzer_tracker.h" +#include "../tracker_engine/tracker_engine_defs.h" +#include "pattern_editor.h" + +#include +#include + +void draw_instrument_view(Canvas* canvas, FlizzerTrackerApp* tracker); +void draw_instrument_program_view(Canvas* canvas, FlizzerTrackerApp* tracker); \ No newline at end of file diff --git a/applications/external/flizzer_tracker/view/opcode_description.c b/applications/external/flizzer_tracker/view/opcode_description.c new file mode 100644 index 0000000000..f42ef207fd --- /dev/null +++ b/applications/external/flizzer_tracker/view/opcode_description.c @@ -0,0 +1,72 @@ +#include "opcode_description.h" +#include + +static const OpcodeDescription opcode_desc[] = { + {TE_PROGRAM_LOOP_BEGIN, 0x7f00, "PROGRAM LOOP BEGIN", "L.BEG."}, + {TE_PROGRAM_LOOP_END, 0x7f00, "PROGRAM LOOP END", "L.END"}, + + {TE_PROGRAM_NOP, 0x7fff, "NO OPERATION", ""}, + {TE_PROGRAM_END, 0x7fff, "PROGRAM END", "PR.END"}, + {TE_PROGRAM_JUMP, 0x7f00, "JUMP TO POSITION", "GOTO"}, + + //==================================================== + + {TE_EFFECT_ARPEGGIO, 0x7f00, "RELATIVE ARPEGGIO NOTE", ""}, + {TE_EFFECT_PORTAMENTO_UP, 0x7f00, "PORTAMENTO UP", "PORTUP"}, + {TE_EFFECT_PORTAMENTO_DOWN, 0x7f00, "PORTAMENTO DOWN", "PORTDN"}, + {TE_EFFECT_SLIDE, 0x7f00, "SLIDE", "SLIDE"}, + {TE_EFFECT_VIBRATO, 0x7f00, "VIBRATO", "VIB"}, + {TE_EFFECT_PWM, 0x7f00, "PULSE WIDTH MODIFICATION", "PWM"}, + + {TE_EFFECT_SET_PW, 0x7f00, "SET PULSE WIDTH", "SET PW"}, + {TE_EFFECT_PW_DOWN, 0x7f00, "PULSE WIDTH DOWN", "PWDOWN"}, + {TE_EFFECT_PW_UP, 0x7f00, "PULSE WIDTH UP", "PW UP"}, + {TE_EFFECT_SET_CUTOFF, 0x7f00, "SET FILTER CUTOFF", "F.CUT"}, + + {TE_EFFECT_VOLUME_FADE, 0x7f00, "VOLUME FADE", "V.FADE"}, + {TE_EFFECT_SET_WAVEFORM, 0x7f00, "SET WAVEFORM", "S.WAVE"}, + {TE_EFFECT_SET_VOLUME, 0x7f00, "SET VOLUME", "VOLUME"}, + {TE_EFFECT_SKIP_PATTERN, 0x7f00, "SKIP PATTERN", "P.SKIP"}, + + {TE_EFFECT_EXT_TOGGLE_FILTER, 0x7ff0, "TOGGLE FILTER (0=OFF,1-F=ON)", "T.FILT"}, + {TE_EFFECT_EXT_PORTA_UP, 0x7ff0, "FINE PORTAMENTO UP", "PUP F."}, + {TE_EFFECT_EXT_PORTA_DN, 0x7ff0, "FINE PORTAMENTO DOWN", "PDN F."}, + {TE_EFFECT_EXT_FILTER_MODE, 0x7ff0, "SET FILTER MODE", "F.MODE"}, + {TE_EFFECT_EXT_PATTERN_LOOP, 0x7ff0, "PATTERN LOOP:E60=BEGIN,E6X=END", "PAT.L."}, + {TE_EFFECT_EXT_RETRIGGER, 0x7ff0, "RETRIGGER AT TICK X (X>0)", "RETRIG"}, + {TE_EFFECT_EXT_FINE_VOLUME_DOWN, 0x7ff0, "FINE VOLUME DOWN", "VDN F."}, + {TE_EFFECT_EXT_FINE_VOLUME_UP, 0x7ff0, "FINE VOLUME UP", "VUP F."}, + {TE_EFFECT_EXT_NOTE_CUT, 0x7ff0, "NOTE CUT", "N.CUT"}, + {TE_EFFECT_EXT_NOTE_DELAY, 0x7ff0, "NOTE DELAY", "N.DEL."}, + {TE_EFFECT_EXT_PHASE_RESET, 0x7ff0, "PHASE RESET ON TICK X", "PH.RES."}, + + {TE_EFFECT_SET_SPEED_PROG_PERIOD, 0x7f00, "SET SPEED (PROG.PER.IN PROGRAM)", "P.PER."}, + {TE_EFFECT_CUTOFF_UP, 0x7f00, "FILTER CUTOFF UP", "CUT.UP"}, + {TE_EFFECT_CUTOFF_DOWN, 0x7f00, "FILTER CUTOFF DOWN", "CUT.DN"}, + {TE_EFFECT_SET_RESONANCE, 0x7f00, "SET FILTER RESONANCE", "F.RES."}, + {TE_EFFECT_RESONANCE_UP, 0x7f00, "FILTER RESONANCE UP", "F.R.UP"}, + {TE_EFFECT_RESONANCE_DOWN, 0x7f00, "FILTER RESONANCE DOWN", "F.R.DN"}, + {TE_EFFECT_SET_ATTACK, 0x7f00, "SET ENVELOPE ATTACK", "ADSR A"}, + {TE_EFFECT_SET_DECAY, 0x7f00, "SET ENVELOPE DECAY", "ADSR D"}, + {TE_EFFECT_SET_SUSTAIN, 0x7f00, "SET ENVELOPE SUSTAIN", "ADSR S"}, + {TE_EFFECT_SET_RELEASE, 0x7f00, "SET ENVELOPE RELEASE", "ADSR R"}, + {TE_EFFECT_PROGRAM_RESTART, 0x7f00, "RESTART INSTRUMENT PROGRAM", "P.RES."}, + {TE_EFFECT_SET_RING_MOD_SRC, 0x7f00, "SET RING MODULATION SOURCE CH.", "R.SRC"}, + {TE_EFFECT_SET_HARD_SYNC_SRC, 0x7f00, "SET HARD SYNC SOURCE CHANNEL", "S.SRC"}, + {TE_EFFECT_PORTA_UP_SEMITONE, 0x7f00, "PORTAMENTO UP (SEMITONES)", "PU.SEM"}, + {TE_EFFECT_PORTA_DOWN_SEMITONE, 0x7f00, "PORTAMENTO DOWN (SEMITONES)", "PD.SEM"}, + {TE_EFFECT_LEGATO, 0x7f00, "LEGATO", "LEGATO"}, + {TE_EFFECT_ARPEGGIO_ABS, 0x7f00, "ABSOLUTE ARPEGGIO NOTE", ""}, + {TE_EFFECT_TRIGGER_RELEASE, 0x7f00, "TRIGGER RELEASE", "TR.REL"}, + {0, 0, NULL, NULL}, +}; + +char* get_opcode_description(uint16_t opcode, bool short_description) { + for(int i = 0; opcode_desc[i].name != NULL; i++) { + if(opcode_desc[i].opcode == (opcode & opcode_desc[i].mask)) { + return short_description ? opcode_desc[i].shortname : opcode_desc[i].name; + } + } + + return NULL; +} \ No newline at end of file diff --git a/applications/external/flizzer_tracker/view/opcode_description.h b/applications/external/flizzer_tracker/view/opcode_description.h new file mode 100644 index 0000000000..2d98673bc1 --- /dev/null +++ b/applications/external/flizzer_tracker/view/opcode_description.h @@ -0,0 +1,12 @@ +#pragma once + +#include "../tracker_engine/tracker_engine_defs.h" +#include + +typedef struct { + uint16_t opcode; + uint16_t mask; + char *name, *shortname; +} OpcodeDescription; + +char* get_opcode_description(uint16_t opcode, bool short_description); \ No newline at end of file diff --git a/applications/external/flizzer_tracker/view/pattern_editor.c b/applications/external/flizzer_tracker/view/pattern_editor.c new file mode 100644 index 0000000000..19c62d9f94 --- /dev/null +++ b/applications/external/flizzer_tracker/view/pattern_editor.c @@ -0,0 +1,442 @@ +#include "pattern_editor.h" +#include "../macros.h" + +#include + +#define PATTERN_EDITOR_Y ((tracker->focus == EDIT_PATTERN) ? 4 : (64 - (6 * 5) - 1)) + +static const char* notenames[] = { + "C-", + "C#", + "D-", + "D#", + "E-", + "F-", + "F#", + "G-", + "G#", + "A-", + "A#", + "B-", +}; + +char* notename(uint8_t note) { + static char buffer[6]; + + if(note == MUS_NOTE_CUT) { + snprintf(buffer, sizeof(buffer), "%s", "OFF"); + return buffer; + } + + if(note == MUS_NOTE_RELEASE) { + snprintf(buffer, sizeof(buffer), "%s", " "); + return buffer; + } + + if(note == 0xf0) // external arpeggio notes + { + snprintf(buffer, sizeof(buffer), "%s", "EXT.0"); + return buffer; + } + + if(note == 0xf1) { + snprintf(buffer, sizeof(buffer), "%s", "EXT.1"); + return buffer; + } + + else { + uint8_t final_note = my_min(12 * 7 + 11, note); + snprintf(buffer, sizeof(buffer), "%s%d", notenames[final_note % 12], final_note / 12); + } + + return buffer; +} + +char to_char(uint8_t number) { + return to_char_array[number]; +} + +void draw_pattern_view(Canvas* canvas, FlizzerTrackerApp* tracker) { + char command_buffer[6] = {0}; + char buffer[11] = {0}; + + canvas_draw_line(canvas, 0, PATTERN_EDITOR_Y, 127, PATTERN_EDITOR_Y); + + for(int i = 0; i < SONG_MAX_CHANNELS; ++i) { + uint8_t sequence_position = tracker->tracker_engine.sequence_position; + uint8_t current_pattern = + tracker->tracker_engine.song->sequence.sequence_step[sequence_position] + .pattern_indices[i]; + uint16_t pattern_step = tracker->tracker_engine.pattern_position; + + uint16_t pattern_length = tracker->tracker_engine.song->pattern_length; + + TrackerSongPattern* pattern = &tracker->tracker_engine.song->pattern[current_pattern]; + + for(uint8_t pos = 0; pos < ((tracker->focus == EDIT_PATTERN) ? 9 : 5); ++pos) { + TrackerSongPatternStep* step = NULL; + + if(pattern_step - ((tracker->focus == EDIT_PATTERN) ? 4 : 2) + pos >= 0 && + pattern_step - ((tracker->focus == EDIT_PATTERN) ? 4 : 2) + pos < pattern_length) { + step = + &pattern->step[pattern_step + pos - ((tracker->focus == EDIT_PATTERN) ? 4 : 2)]; + } + + uint8_t string_x = i * 32; + uint8_t string_y = + PATTERN_EDITOR_Y + 6 * pos + 6 + ((tracker->focus == EDIT_PATTERN) ? 3 : 1); + + if(step) { + uint8_t note = tracker_engine_get_note(step); + uint8_t inst = tracker_engine_get_instrument(step); + uint8_t vol = tracker_engine_get_volume(step); + uint16_t command = tracker_engine_get_command(step); + + char inst_ch = to_char(inst); + char vol_ch = to_char(vol); + char command_ch = to_char(command >> 8); + + if(inst == MUS_NOTE_INSTRUMENT_NONE) { + inst_ch = '-'; + } + + if(vol == MUS_NOTE_VOLUME_NONE) { + vol_ch = '-'; + } + + if(command == 0) { + snprintf(command_buffer, sizeof(command_buffer), "---"); + } + + else { + snprintf( + command_buffer, + sizeof(command_buffer), + "%c%02X", + command_ch, + (command & 0xff)); + } + + snprintf( + buffer, + sizeof(buffer), + "%s%c%c%s", + (note == MUS_NOTE_NONE ? "---" : notename(note)), + inst_ch, + vol_ch, + command_buffer); + + canvas_draw_str(canvas, string_x, string_y, buffer); + + if(note == MUS_NOTE_RELEASE) { + canvas_draw_icon(canvas, string_x, string_y - 5, &I_note_release); + } + } + } + } + + if(tracker->editing && tracker->focus == EDIT_PATTERN) { + uint16_t x = tracker->current_channel * 32 + tracker->patternx * 4 + + (tracker->patternx > 0 ? 4 : 0) - 1; + uint16_t y = PATTERN_EDITOR_Y + 6 * ((tracker->focus == EDIT_PATTERN) ? 4 : 2) + + ((tracker->focus == EDIT_PATTERN) ? 3 : 1); + + canvas_draw_box(canvas, x, y, (tracker->patternx > 0 ? 5 : 9), 7); + } + + if(!(tracker->editing) && tracker->focus == EDIT_PATTERN) { + uint16_t x = tracker->current_channel * 32 + tracker->patternx * 4 + + (tracker->patternx > 0 ? 4 : 0) - 1; + uint16_t y = PATTERN_EDITOR_Y + 6 * ((tracker->focus == EDIT_PATTERN) ? 4 : 2) + + ((tracker->focus == EDIT_PATTERN) ? 3 : 1); + + canvas_draw_frame(canvas, x, y, (tracker->patternx > 0 ? 5 : 9), 7); + } + + canvas_set_color(canvas, ColorBlack); + + for(int i = 1; i < SONG_MAX_CHANNELS; ++i) { + for(int y = PATTERN_EDITOR_Y + 1; y < 64; y += 2) { + canvas_draw_dot(canvas, i * 32 - 1, y); + } + } + + for(int i = 0; i < SONG_MAX_CHANNELS; ++i) { + if(tracker->tracker_engine.channel[i].channel_flags & TEC_DISABLED) { + canvas_draw_icon(canvas, 13 + 32 * i, PATTERN_EDITOR_Y - 3, &I_channel_off); + } + + else { + canvas_draw_icon(canvas, 13 + 32 * i, PATTERN_EDITOR_Y - 3, &I_channel_on); + } + } + + canvas_set_color(canvas, ColorXOR); +} + +#define SEQ_SLIDER_X (4 * (4 * 2 + 1) + 2) +#define SEQ_SLIDER_Y (32) + +void draw_sequence_view(Canvas* canvas, FlizzerTrackerApp* tracker) { + char buffer[4]; + + uint8_t sequence_position = tracker->tracker_engine.sequence_position; + TrackerSong* song = &tracker->song; + + for(int pos = sequence_position - 2; pos < sequence_position + 3; pos++) { + if(pos >= 0 && pos < tracker->song.num_sequence_steps) { + for(int i = 0; i < SONG_MAX_CHANNELS; ++i) { + uint8_t current_pattern = + tracker->tracker_engine.song->sequence.sequence_step[pos].pattern_indices[i]; + + uint8_t x = i * (4 * 2 + 1) + 3; + uint8_t y = (pos - (sequence_position - 2)) * 6 + 5; + + snprintf(buffer, sizeof(buffer), "%02X", current_pattern); + canvas_draw_str(canvas, x, y, buffer); + } + } + } + + if(song->loop_start != 0 || song->loop_end != 0) { + canvas_set_color(canvas, ColorBlack); + + for(int pos = sequence_position - 2; pos < sequence_position + 3; pos++) { + if(pos >= 0 && pos < tracker->song.num_sequence_steps) { + if(pos == song->loop_start) { + int16_t y = (pos - (sequence_position - 2)) * 6; + + canvas_draw_line(canvas, 0, fmax(y, 0), 1, fmax(y, 0)); + canvas_draw_line(canvas, 0, fmax(y, 0), 0, fmax(y + 4, 0)); + } + + if(pos > song->loop_start && pos < song->loop_end) { + int16_t y = (pos - (sequence_position - 2)) * 6; + + canvas_draw_line(canvas, 0, fmax(y - 1, 0), 0, fmax(y + 4, 0)); + } + + if(pos == song->loop_end) { + int16_t y = (pos - (sequence_position - 2)) * 6; + + canvas_draw_line(canvas, 0, fmax(y + 4, 0), 1, fmax(y + 4, 0)); + canvas_draw_line(canvas, 0, fmax(y - 1, 0), 0, fmax(y + 4, 0)); + + break; + } + } + } + + canvas_set_color(canvas, ColorXOR); + } + + canvas_set_color(canvas, ColorBlack); + + canvas_draw_line(canvas, SEQ_SLIDER_X, 0, SEQ_SLIDER_X + 2, 0); + canvas_draw_line(canvas, SEQ_SLIDER_X, SEQ_SLIDER_Y, SEQ_SLIDER_X + 2, SEQ_SLIDER_Y); + + canvas_draw_line(canvas, SEQ_SLIDER_X, 0, SEQ_SLIDER_X, SEQ_SLIDER_Y); + canvas_draw_line(canvas, SEQ_SLIDER_X + 2, 0, SEQ_SLIDER_X + 2, SEQ_SLIDER_Y); + + uint8_t start_pos = + sequence_position * (SEQ_SLIDER_Y - 2) / tracker->song.num_sequence_steps + 1; + uint8_t slider_length = (SEQ_SLIDER_Y - 2) / tracker->song.num_sequence_steps + 1; + + canvas_draw_line( + canvas, SEQ_SLIDER_X + 1, start_pos, SEQ_SLIDER_X + 1, (start_pos + slider_length)); + + canvas_set_color(canvas, ColorXOR); + + if(tracker->editing && tracker->focus == EDIT_SEQUENCE) { + uint8_t x = tracker->current_channel * (4 + 4 + 1) + (tracker->current_digit ? 4 : 0) + 2; + uint8_t y = 11; + + canvas_draw_box(canvas, x, y, 5, 7); + } + + if(!(tracker->editing) && tracker->focus == EDIT_SEQUENCE) { + uint8_t x = tracker->current_channel * (4 + 4 + 1) + (tracker->current_digit ? 4 : 0) + 2; + uint8_t y = 11; + + canvas_draw_frame(canvas, x, y, 5, 7); + } +} + +#define member_size(type, member) sizeof(((type*)0)->member) + +#define SONG_HEADER_SIZE \ + (member_size(TrackerSong, song_name) + member_size(TrackerSong, speed) + \ + member_size(TrackerSong, rate) + member_size(TrackerSong, loop_start) + \ + member_size(TrackerSong, loop_end) + member_size(TrackerSong, num_patterns) + \ + member_size(TrackerSong, num_sequence_steps) + member_size(TrackerSong, num_instruments) + \ + member_size(TrackerSong, pattern_length)) + +uint32_t calculate_song_size(TrackerSong* song) { + uint32_t song_size = + SONG_HEADER_SIZE + sizeof(Instrument) * song->num_instruments + + sizeof(TrackerSongPatternStep) * song->num_patterns * song->pattern_length + + sizeof(TrackerSongSequenceStep) * song->num_sequence_steps; + return song_size; +} + +void draw_generic_n_digit_field( + FlizzerTrackerApp* tracker, + Canvas* canvas, + uint8_t focus, + uint8_t param, + const char* text, + uint8_t x, + uint8_t y, + uint8_t digits) // last 1-2 symbols are digits we are editing +{ + canvas_draw_str(canvas, x, y, text); + + if(tracker->focus == focus && tracker->selected_param == param && tracker->editing) { + bool select_string = true; + + if(tracker->focus == EDIT_SONGINFO) { + if(param != SI_SONGNAME && param != SI_INSTRUMENTNAME) { + select_string = false; + } + } + + if(tracker->focus == EDIT_INSTRUMENT) { + if(param != INST_INSTRUMENTNAME) { + select_string = false; + } + } + + if(!(select_string)) { + if(tracker->focus == EDIT_INSTRUMENT && param == INST_CURRENTINSTRUMENT) { + canvas_draw_box(canvas, x + strlen(text) * 4 - digits * 4 - 1, y - 6, 5, 7); + } + + else { + canvas_draw_box( + canvas, + x + strlen(text) * 4 - digits * 4 + tracker->current_digit * 4 - 1, + y - 6, + 5, + 7); + } + } + + else { + canvas_draw_box(canvas, x - 1, y - 6, fmax(5, strlen(text) * 4 + 1), 7); + } + } + + if(tracker->focus == focus && tracker->selected_param == param && !(tracker->editing)) { + bool select_string = true; + + if(tracker->focus == EDIT_SONGINFO) { + if(param != SI_SONGNAME && param != SI_INSTRUMENTNAME) { + select_string = false; + } + } + + if(tracker->focus == EDIT_INSTRUMENT) { + if(param != INST_INSTRUMENTNAME) { + select_string = false; + } + } + + if(!(select_string)) { + if(tracker->focus == EDIT_INSTRUMENT && param == INST_CURRENTINSTRUMENT) { + canvas_draw_frame(canvas, x + strlen(text) * 4 - digits * 4 - 1, y - 6, 5, 7); + } + + else { + canvas_draw_frame( + canvas, + x + strlen(text) * 4 - digits * 4 + tracker->current_digit * 4 - 1, + y - 6, + 5, + 7); + } + } + + else { + canvas_draw_frame(canvas, x - 1, y - 6, fmax(5, strlen(text) * 4 + 1), 7); + } + } +} + +void draw_songinfo_view(Canvas* canvas, FlizzerTrackerApp* tracker) { + char buffer[30]; + + snprintf( + buffer, + sizeof(buffer), + "PAT.P.%02X/%02X", + tracker->tracker_engine.pattern_position, + tracker->song.pattern_length - 1); + draw_generic_n_digit_field(tracker, canvas, EDIT_SONGINFO, SI_PATTERNPOS, buffer, 42, 5, 2); + snprintf( + buffer, + sizeof(buffer), + "SEQ.P.%02X/%02X", + tracker->tracker_engine.sequence_position, + tracker->song.num_sequence_steps - 1); + draw_generic_n_digit_field(tracker, canvas, EDIT_SONGINFO, SI_SEQUENCEPOS, buffer, 42, 11, 2); + snprintf(buffer, sizeof(buffer), "SPD.%02X", tracker->song.speed); + draw_generic_n_digit_field(tracker, canvas, EDIT_SONGINFO, SI_SONGSPEED, buffer, 42, 17, 2); + snprintf(buffer, sizeof(buffer), "RATE %02X", tracker->song.rate); + draw_generic_n_digit_field( + tracker, canvas, EDIT_SONGINFO, SI_SONGRATE, buffer, 42 + 4 * 7, 17, 2); + snprintf(buffer, sizeof(buffer), "VOL %02X", tracker->tracker_engine.master_volume); + draw_generic_n_digit_field( + tracker, canvas, EDIT_SONGINFO, SI_MASTERVOL, buffer, 42 + 4 * 7 + 4 * 8, 17, 2); + + snprintf(buffer, sizeof(buffer), "SONG:"); + canvas_draw_str(canvas, 42, 23, buffer); + snprintf(buffer, sizeof(buffer), "%s", tracker->song.song_name); + draw_generic_n_digit_field( + tracker, canvas, EDIT_SONGINFO, SI_SONGNAME, buffer, 42 + 4 * 5, 23, 1); + + snprintf(buffer, sizeof(buffer), "INST:%c", to_char(tracker->current_instrument)); + draw_generic_n_digit_field( + tracker, canvas, EDIT_SONGINFO, SI_CURRENTINSTRUMENT, buffer, 42, 29, 1); + snprintf( + buffer, sizeof(buffer), "%s", tracker->song.instrument[tracker->current_instrument]->name); + draw_generic_n_digit_field( + tracker, canvas, EDIT_SONGINFO, SI_INSTRUMENTNAME, buffer, 42 + 4 * 7, 29, 1); + + uint32_t song_size = calculate_song_size(&tracker->song); + uint32_t free_bytes = memmgr_get_free_heap(); + canvas_draw_line(canvas, 128 - 4 * 10 - 2, 0, 128 - 4 * 10 - 2, 10); + + char song_size_buffer[19]; + char free_bytes_buffer[19]; + + if(song_size > 9999) { + snprintf( + song_size_buffer, + sizeof(song_size_buffer), + "TUNE:%ld%c%01ldK", + song_size / 1024, + '.', + (song_size % 1024) / 103); + } + + else { + snprintf(song_size_buffer, sizeof(song_size_buffer), "TUNE:%ld", song_size); + } + + if(free_bytes > 9999) { + snprintf( + free_bytes_buffer, + sizeof(song_size_buffer), + "FREE:%ld%c%01ldK", + free_bytes / 1024, + '.', + (free_bytes % 1024) / 103); + } + + else { + snprintf(free_bytes_buffer, sizeof(song_size_buffer), "FREE:%ld", free_bytes); + } + + canvas_draw_str(canvas, 128 - 4 * 10, 5, song_size_buffer); + canvas_draw_str(canvas, 128 - 4 * 10, 11, free_bytes_buffer); +} \ No newline at end of file diff --git a/applications/external/flizzer_tracker/view/pattern_editor.h b/applications/external/flizzer_tracker/view/pattern_editor.h new file mode 100644 index 0000000000..707b6abb88 --- /dev/null +++ b/applications/external/flizzer_tracker/view/pattern_editor.h @@ -0,0 +1,25 @@ +#pragma once + +#include "../flizzer_tracker.h" +#include "../tracker_engine/tracker_engine_defs.h" + +#include +#include + +extern const char to_char_array[]; + +void draw_pattern_view(Canvas* canvas, FlizzerTrackerApp* tracker); +void draw_sequence_view(Canvas* canvas, FlizzerTrackerApp* tracker); +void draw_songinfo_view(Canvas* canvas, FlizzerTrackerApp* tracker); + +void draw_generic_n_digit_field( + FlizzerTrackerApp* tracker, + Canvas* canvas, + uint8_t focus, + uint8_t param, + const char* text, + uint8_t x, + uint8_t y, + uint8_t digits); +char to_char(uint8_t number); +char* notename(uint8_t note); \ No newline at end of file diff --git a/applications/external/game15/application.fam b/applications/external/game15/application.fam index 969cb536a0..d6b1e10a0e 100644 --- a/applications/external/game15/application.fam +++ b/applications/external/game15/application.fam @@ -8,4 +8,7 @@ App( fap_icon="game15_10px.png", order=30, fap_category="Games", + fap_author="@x27", + fap_version="1.0", + fap_description="Logic Game", ) diff --git a/applications/external/game15/game15.c b/applications/external/game15/game15.c index 8faea4380b..32d47b944c 100644 --- a/applications/external/game15/game15.c +++ b/applications/external/game15/game15.c @@ -11,8 +11,7 @@ #define CELL_HEIGHT 8 #define MOVE_TICKS 5 #define KEY_STACK_SIZE 16 -#define SAVING_DIRECTORY "/ext/apps/Games" -#define SAVING_FILENAME SAVING_DIRECTORY "/game15.save" +#define SAVING_FILENAME APP_DATA_PATH("game15.save") #define POPUP_MENU_ITEMS 2 typedef enum { @@ -116,8 +115,9 @@ static int key_stack_push(uint8_t value) { static bool storage_game_state_load() { Storage* storage = furi_record_open(RECORD_STORAGE); - File* file = storage_file_alloc(storage); + storage_common_migrate(storage, EXT_PATH("apps/Games/game15.save"), SAVING_FILENAME); + File* file = storage_file_alloc(storage); uint16_t bytes_readed = 0; if(storage_file_open(file, SAVING_FILENAME, FSAM_READ, FSOM_OPEN_EXISTING)) bytes_readed = storage_file_read(file, &game_state, sizeof(game_state_t)); @@ -130,12 +130,6 @@ static bool storage_game_state_load() { static void storage_game_state_save() { Storage* storage = furi_record_open(RECORD_STORAGE); - if(storage_common_stat(storage, SAVING_DIRECTORY, NULL) == FSE_NOT_EXIST) { - if(!storage_simply_mkdir(storage, SAVING_DIRECTORY)) { - return; - } - } - File* file = storage_file_alloc(storage); if(storage_file_open(file, SAVING_FILENAME, FSAM_WRITE, FSOM_CREATE_ALWAYS)) { storage_file_write(file, &game_state, sizeof(game_state_t)); @@ -462,6 +456,9 @@ int32_t game15_app() { sandbox_init( FPS, (SandboxRenderCallback)render_callback, (SandboxEventHandler)game_event_handler); + // Call dolphin deed on game start + // dolphin_deed(DolphinDeedPluginGameStart); + sandbox_loop(); sandbox_free(); game_free(); diff --git a/applications/external/game15/images/Game15.png b/applications/external/game15/images/Game15.png deleted file mode 100644 index f13c2907b5..0000000000 Binary files a/applications/external/game15/images/Game15.png and /dev/null differ diff --git a/applications/external/game15/images/Game15Popup.png b/applications/external/game15/images/Game15Popup.png deleted file mode 100644 index 1df14729fc..0000000000 Binary files a/applications/external/game15/images/Game15Popup.png and /dev/null differ diff --git a/applications/external/game15/images/Game15Restore.png b/applications/external/game15/images/Game15Restore.png deleted file mode 100644 index 05aac27f61..0000000000 Binary files a/applications/external/game15/images/Game15Restore.png and /dev/null differ diff --git a/applications/external/game_2048/application.fam b/applications/external/game_2048/application.fam index 89210ca235..6be28fafd7 100644 --- a/applications/external/game_2048/application.fam +++ b/applications/external/game_2048/application.fam @@ -11,4 +11,7 @@ App( order=90, fap_icon="game_2048.png", fap_category="Games", + fap_author="@eugene-kirzhanov", + fap_version="1.0", + fap_description="2048 Game", ) diff --git a/applications/external/game_2048/game_2048.c b/applications/external/game_2048/game_2048.c index cf8bba8f10..430bc5b5d9 100644 --- a/applications/external/game_2048/game_2048.c +++ b/applications/external/game_2048/game_2048.c @@ -23,8 +23,7 @@ #define FRAME_TOP 1 #define FRAME_SIZE 61 -#define SAVING_DIRECTORY "/ext/apps/Games" -#define SAVING_FILENAME SAVING_DIRECTORY "/game_2048.save" +#define SAVING_FILENAME APP_DATA_PATH("game_2048.save") typedef enum { GameStateMenu, @@ -300,6 +299,7 @@ void init_game(GameState* const game_state, bool clear_top_score) { bool load_game(GameState* game_state) { Storage* storage = furi_record_open(RECORD_STORAGE); + storage_common_migrate(storage, EXT_PATH("apps/Games/game_2048.save"), SAVING_FILENAME); File* file = storage_file_alloc(storage); uint16_t bytes_readed = 0; @@ -317,12 +317,6 @@ bool load_game(GameState* game_state) { void save_game(GameState* game_state) { Storage* storage = furi_record_open(RECORD_STORAGE); - if(storage_common_stat(storage, SAVING_DIRECTORY, NULL) == FSE_NOT_EXIST) { - if(!storage_simply_mkdir(storage, SAVING_DIRECTORY)) { - return; - } - } - File* file = storage_file_alloc(storage); if(storage_file_open(file, SAVING_FILENAME, FSAM_WRITE, FSOM_CREATE_ALWAYS)) { storage_file_write(file, game_state, sizeof(GameState)); @@ -399,6 +393,9 @@ int32_t game_2048_app() { Gui* gui = furi_record_open(RECORD_GUI); gui_add_view_port(gui, view_port, GuiLayerFullscreen); + // Call dolphin deed on game start + // dolphin_deed(DolphinDeedPluginGameStart); + bool is_finished = false; while(!is_finished) { FuriStatus event_status = furi_message_queue_get(event_queue, &input, FuriWaitForever); diff --git a/applications/external/game_of_life/application.fam b/applications/external/game_of_life/application.fam index 55e244d228..0037dbe4ea 100644 --- a/applications/external/game_of_life/application.fam +++ b/applications/external/game_of_life/application.fam @@ -1,5 +1,5 @@ App( - appid="GameOfLife", + appid="gameoflife", name="Game of Life", apptype=FlipperAppType.EXTERNAL, entry_point="game_of_life_app", @@ -9,4 +9,8 @@ App( order=110, fap_icon="golIcon.png", fap_category="Games", + fap_author="@tgxn (original by @itsyourbedtime)", + fap_weburl="https://github.com/tgxn/flipperzero-firmware/blob/dev/applications/game_of_life/game_of_life.c", + fap_version="1.0", + fap_description="Life, is a cellular automaton devised by the British mathematician John Horton Conway in 1970.", ) diff --git a/applications/external/geiger/application.fam b/applications/external/geiger/application.fam index ea5e1fa858..eca3167e4c 100644 --- a/applications/external/geiger/application.fam +++ b/applications/external/geiger/application.fam @@ -1,6 +1,6 @@ App( appid="flipper_geiger", - name="[GPIO] Geiger Counter", + name="[J305] Geiger Counter", apptype=FlipperAppType.EXTERNAL, entry_point="flipper_geiger_app", cdefines=["APP_GEIGER"], @@ -10,4 +10,8 @@ App( stack_size=1 * 1024, fap_icon="geiger.png", fap_category="GPIO", + fap_author="@nmrr", + fap_weburl="https://github.com/nmrr/flipperzero-geigercounter", + fap_version="1.0", + fap_description="Works with J305 Geiger tube on external board", ) diff --git a/applications/external/geiger/flipper_geiger.c b/applications/external/geiger/flipper_geiger.c index 9c3d0d3fc4..17f2b70584 100644 --- a/applications/external/geiger/flipper_geiger.c +++ b/applications/external/geiger/flipper_geiger.c @@ -39,53 +39,33 @@ typedef struct { static void draw_callback(Canvas* canvas, void* ctx) { furi_assert(ctx); - mutexStruct* mutexVal = ctx; - mutexStruct mutexDraw; - furi_mutex_acquire(mutexVal->mutex, FuriWaitForever); - memcpy(&mutexDraw, mutexVal, sizeof(mutexStruct)); - furi_mutex_release(mutexVal->mutex); + mutexStruct displayStruct; + mutexStruct* geigerMutex = ctx; + furi_mutex_acquire(geigerMutex->mutex, FuriWaitForever); + memcpy(&displayStruct, geigerMutex, sizeof(mutexStruct)); + furi_mutex_release(geigerMutex->mutex); char buffer[32]; - if(mutexDraw.data == 0) - snprintf(buffer, sizeof(buffer), "%ld cps - %ld cpm", mutexDraw.cps, mutexDraw.cpm); - else if(mutexDraw.data == 1) + if(displayStruct.data == 0) snprintf( - buffer, - sizeof(buffer), - "%ld cps - %.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) + buffer, sizeof(buffer), "%ld cps - %ld cpm", displayStruct.cps, displayStruct.cpm); + else if(displayStruct.data == 1) 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); + "%ld cps - %.2f uSv/h", + displayStruct.cps, + ((double)displayStruct.cpm * (double)CONVERSION_FACTOR)); else snprintf( buffer, sizeof(buffer), - "%ld cps - %.2f uR/h", - mutexDraw.cps, - ((double)mutexDraw.cpm * (double)CONVERSION_FACTOR) * (double)100); + "%ld cps - %.2f mSv/y", + displayStruct.cps, + (((double)displayStruct.cpm * (double)CONVERSION_FACTOR)) * (double)8.76); for(int i = 0; i < SCREEN_SIZE_X; i += 2) { - float Y = SCREEN_SIZE_Y - (mutexDraw.line[i / 2] * mutexDraw.coef); + float Y = SCREEN_SIZE_Y - (displayStruct.line[i / 2] * displayStruct.coef); canvas_draw_line(canvas, i, Y, i, SCREEN_SIZE_Y); canvas_draw_line(canvas, i + 1, Y, i + 1, SCREEN_SIZE_Y); @@ -109,7 +89,7 @@ static void clock_tick(void* ctx) { randomNumber &= 0xFFF; if(randomNumber == 0) randomNumber = 1; - furi_hal_pwm_start(FuriHalPwmOutputIdLptim2PA4, randomNumber, 50); + furi_hal_pwm_set_params(FuriHalPwmOutputIdLptim2PA4, randomNumber, 50); FuriMessageQueue* queue = ctx; EventApp event = {.type = ClockEventTypeTick}; @@ -123,7 +103,8 @@ static void gpiocallback(void* ctx) { furi_message_queue_put(queue, &event, 0); } -int32_t flipper_geiger_app() { +int32_t flipper_geiger_app(void* p) { + UNUSED(p); EventApp event; FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(EventApp)); @@ -146,7 +127,7 @@ int32_t flipper_geiger_app() { } ViewPort* view_port = view_port_alloc(); - view_port_draw_callback_set(view_port, draw_callback, &mutexVal.mutex); + view_port_draw_callback_set(view_port, draw_callback, &mutexVal); view_port_input_callback_set(view_port, input_callback, event_queue); furi_hal_gpio_add_int_callback(&gpio_ext_pa7, gpiocallback, event_queue); @@ -158,7 +139,13 @@ int32_t flipper_geiger_app() { furi_timer_start(timer, 1000); // ENABLE 5V pin - furi_hal_power_enable_otg(); + + // Enable 5v power, multiple attempts to avoid issues with power chip protection false triggering + uint8_t attempts = 0; + while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) { + furi_hal_power_enable_otg(); + furi_delay_ms(10); + } while(1) { FuriStatus event_status = furi_message_queue_get(event_queue, &event, FuriWaitForever); @@ -186,7 +173,7 @@ int32_t flipper_geiger_app() { if(mutexVal.data != 0) mutexVal.data--; else - mutexVal.data = 5; + mutexVal.data = 2; screenRefresh = 1; furi_mutex_release(mutexVal.mutex); @@ -194,7 +181,7 @@ int32_t flipper_geiger_app() { event.input.type == InputTypeShort)) { furi_mutex_acquire(mutexVal.mutex, FuriWaitForever); - if(mutexVal.data != 5) + if(mutexVal.data != 2) mutexVal.data++; else mutexVal.data = 0; @@ -235,7 +222,10 @@ int32_t flipper_geiger_app() { if(screenRefresh == 1) view_port_update(view_port); } - furi_hal_power_disable_otg(); + // Disable 5v power + if(furi_hal_power_is_otg_enabled()) { + furi_hal_power_disable_otg(); + } furi_hal_gpio_disable_int_callback(&gpio_ext_pa7); furi_hal_gpio_remove_int_callback(&gpio_ext_pa7); diff --git a/applications/external/gpioreader_a/application.fam b/applications/external/gpioreader_a/application.fam index 4ec90df6a5..c373460967 100644 --- a/applications/external/gpioreader_a/application.fam +++ b/applications/external/gpioreader_a/application.fam @@ -1,11 +1,14 @@ App( appid="gpio_reader_a", - name="[GPIO] Reader (Aurelilc)", + name="[GPIO] Reader (aureli1c)", apptype=FlipperAppType.EXTERNAL, entry_point="GPIO_reader_app", requires=["gui"], stack_size=1 * 1024, fap_category="GPIO", fap_icon="icon.png", - order=1, + fap_author="@aureli1c", + fap_weburl="https://github.com/aureli1c/flipperzero_GPIO_read", + fap_version="1.0", + fap_description="Read GPIO pins states, and display them on the screen", ) diff --git a/applications/external/gpioreader_b/application.fam b/applications/external/gpioreader_b/application.fam index aa26060aa4..c3a4177744 100644 --- a/applications/external/gpioreader_b/application.fam +++ b/applications/external/gpioreader_b/application.fam @@ -7,7 +7,6 @@ App( requires=["gui"], stack_size=1 * 1024, order=50, - fap_libs=["assets"], fap_category="GPIO", fap_icon="icon.png", ) diff --git a/applications/external/gpioreader_b/scenes/gpio_scene_start.c b/applications/external/gpioreader_b/scenes/gpio_scene_start.c index 57cab91a56..19d0173fa0 100644 --- a/applications/external/gpioreader_b/scenes/gpio_scene_start.c +++ b/applications/external/gpioreader_b/scenes/gpio_scene_start.c @@ -97,7 +97,7 @@ bool gpio_scene_start_on_event(void* context, SceneManagerEvent event) { } else if(event.event == GpioStartEventUsbUart) { scene_manager_set_scene_state(app->scene_manager, GpioSceneStart, GpioItemUsbUart); if(!furi_hal_usb_is_locked()) { - DOLPHIN_DEED(DolphinDeedGpioUartBridge); + dolphin_deed(DolphinDeedGpioUartBridge); scene_manager_next_scene(app->scene_manager, GpioSceneUsbUart); } else { scene_manager_next_scene(app->scene_manager, GpioSceneUsbUartCloseRpc); diff --git a/applications/external/gps_nmea_uart/application.fam b/applications/external/gps_nmea_uart/application.fam index e02785744c..c3897e38ac 100644 --- a/applications/external/gps_nmea_uart/application.fam +++ b/applications/external/gps_nmea_uart/application.fam @@ -8,4 +8,7 @@ App( order=35, fap_icon="gps_10px.png", fap_category="GPIO", + fap_author="@ezod & @xMasterX", + fap_version="1.0", + fap_description="Works with GPS modules via UART, using NMEA protocol.", ) diff --git a/applications/external/hc_sr04/application.fam b/applications/external/hc_sr04/application.fam index 351f4e03db..7bbbaa5be9 100644 --- a/applications/external/hc_sr04/application.fam +++ b/applications/external/hc_sr04/application.fam @@ -10,4 +10,7 @@ App( order=20, fap_icon="dist_sensor10px.png", fap_category="GPIO", + fap_author="@xMasterX (first implementation by @Sanqui)", + fap_version="1.0", + fap_description="HC-SR(04) Distance sensor reader", ) diff --git a/applications/external/heap_defence_game/application.fam b/applications/external/heap_defence_game/application.fam index b132531d1d..d19b48ebbd 100644 --- a/applications/external/heap_defence_game/application.fam +++ b/applications/external/heap_defence_game/application.fam @@ -8,4 +8,7 @@ App( fap_category="Games", fap_icon="box.png", fap_icon_assets="assets_images", + fap_author="@xMasterX (original implementation by @wquinoa & @Vedmein)", + fap_version="1.0", + fap_description="Heap Defence game from hackathon (aka Stack Attack)", ) diff --git a/applications/external/heap_defence_game/heap_defence.c b/applications/external/heap_defence_game/heap_defence.c index ae9ae76cc6..7feba107c4 100644 --- a/applications/external/heap_defence_game/heap_defence.c +++ b/applications/external/heap_defence_game/heap_defence.c @@ -533,6 +533,9 @@ int32_t heap_defence_app(void* p) { game->game_status = 0; game->animation = AnimationPause; + // Call dolphin deed on game start + // dolphin_deed(DolphinDeedPluginGameStart); + GameEvent event = {0}; while(event.input.key != InputKeyBack) { if(furi_message_queue_get(event_queue, &event, 100) != FuriStatusOk) { diff --git a/applications/external/hex_editor/application.fam b/applications/external/hex_editor/application.fam new file mode 100644 index 0000000000..4b187e469b --- /dev/null +++ b/applications/external/hex_editor/application.fam @@ -0,0 +1,20 @@ +App( + appid="hex_editor", + name="HEX Editor", + apptype=FlipperAppType.EXTERNAL, + entry_point="hex_editor_app", + cdefines=["APP_HEX_EDITOR"], + requires=[ + "gui", + "dialogs", + ], + stack_size=2 * 1024, + order=20, + fap_icon="icons/edit_10px.png", + fap_category="Tools", + fap_icon_assets="icons", + fap_author="@dunaevai135", + fap_weburl="https://github.com/dunaevai135/flipper-zero-hex_editor", + fap_version="1.0", + fap_description="Read any file line by line, and use Ok to edit char. Useful for NFC file - Edit Dump feature without PC/Phone.", +) diff --git a/applications/external/hex_editor/hex_editor.c b/applications/external/hex_editor/hex_editor.c new file mode 100644 index 0000000000..fe8eb8b833 --- /dev/null +++ b/applications/external/hex_editor/hex_editor.c @@ -0,0 +1,357 @@ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#define TAG "HexEditor" + +typedef struct { + // uint8_t file_bytes[HEX_editor_LINES_ON_SCREEN][HEX_editor_BYTES_PER_LINE]; + uint32_t file_offset; + uint32_t file_read_bytes; + uint32_t file_size; + uint8_t string_offset; + char editable_char; + Stream* stream; + bool mode; // Print address or content +} HexEditorModel; + +typedef struct { + HexEditorModel* model; + FuriMutex** mutex; + + FuriMessageQueue* input_queue; + + ViewPort* view_port; + Gui* gui; + Storage* storage; + + FuriString* buffer; +} HexEditor; + +static void draw_callback(Canvas* canvas, void* ctx) { + // UNUSED(ctx); + HexEditor* hex_editor = ctx; + + canvas_clear(canvas); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 0, 10, "Line and mode:"); + // elements_button_right(canvas, "Info"); + + // // elements_string_fit_width(canvas, buffer, 100); + canvas_set_font(canvas, FontSecondary); + + canvas_draw_str_aligned( + canvas, + 0, + 20, + AlignLeft, + AlignBottom, + furi_string_get_cstr(hex_editor->buffer) + hex_editor->model->string_offset); + // elements_scrollable_text_line( + // canvas, 0, 20, 128, hex_editor->buffer, hex_editor->model->string_offset, false); + + // canvas_draw_line(canvas, 3, 20, 5, 30); + + canvas_draw_icon(canvas, 0, 20, &I_Pin_arrow_up_7x9); + + if(hex_editor->model->mode) { + elements_button_left(canvas, "ASCII -"); + elements_button_right(canvas, "ASCII +"); + } else { + elements_button_left(canvas, ""); + elements_button_right(canvas, ""); + } + + canvas_set_font(canvas, FontPrimary); + canvas_draw_glyph(canvas, 0, 45, '0' + hex_editor->model->mode); + canvas_draw_glyph(canvas, 30, 45, hex_editor->model->editable_char); +} + +static void input_callback(InputEvent* input_event, void* ctx) { + // Проверяем, что контекст не нулевой + furi_assert(ctx); + HexEditor* hex_editor = ctx; + + furi_message_queue_put(hex_editor->input_queue, input_event, 100); +} + +static HexEditor* hex_editor_alloc() { + HexEditor* instance = malloc(sizeof(HexEditor)); + + instance->model = malloc(sizeof(HexEditorModel)); + memset(instance->model, 0x0, sizeof(HexEditorModel)); + + instance->model->editable_char = ' '; + + instance->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + + instance->input_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); + + instance->view_port = view_port_alloc(); + view_port_draw_callback_set(instance->view_port, draw_callback, instance); + view_port_input_callback_set(instance->view_port, input_callback, instance); + + instance->gui = furi_record_open(RECORD_GUI); + gui_add_view_port(instance->gui, instance->view_port, GuiLayerFullscreen); + + instance->storage = furi_record_open(RECORD_STORAGE); + + instance->buffer = furi_string_alloc(); + + return instance; +} + +static void hex_editor_free(HexEditor* instance) { + furi_record_close(RECORD_STORAGE); + + gui_remove_view_port(instance->gui, instance->view_port); + furi_record_close(RECORD_GUI); + view_port_free(instance->view_port); + + furi_message_queue_free(instance->input_queue); + + furi_mutex_free(instance->mutex); + + if(instance->model->stream) buffered_file_stream_close(instance->model->stream); + + furi_string_free(instance->buffer); + + free(instance->model); + free(instance); +} + +static bool hex_editor_open_file(HexEditor* hex_editor, const char* file_path) { + furi_assert(hex_editor); + furi_assert(file_path); + + hex_editor->model->stream = buffered_file_stream_alloc(hex_editor->storage); + bool isOk = true; + + do { + if(!buffered_file_stream_open( + hex_editor->model->stream, file_path, FSAM_READ_WRITE, FSOM_OPEN_EXISTING)) { + FURI_LOG_E(TAG, "Unable to open stream: %s", file_path); + isOk = false; + break; + }; + + hex_editor->model->file_size = stream_size(hex_editor->model->stream); + } while(false); + + return isOk; +} + +// static bool hex_editor_read_file(HexEditor* hex_editor) { +// furi_assert(hex_editor); +// furi_assert(hex_editor->model->stream); +// // furi_assert(hex_editor->model->file_offset % hex_editor_BYTES_PER_LINE == 0); + +// memset(hex_editor->model->file_bytes, 0x0, hex_editor_BUF_SIZE); +// bool isOk = true; + +// do { +// uint32_t offset = hex_editor->model->file_offset; +// if(!stream_seek(hex_editor->model->stream, offset, true)) { +// FURI_LOG_E(TAG, "Unable to seek stream"); +// isOk = false; +// break; +// } + +// hex_editor->model->file_read_bytes = stream_read( +// hex_editor->model->stream, +// (uint8_t*)hex_editor->model->file_bytes, +// hex_editor_BUF_SIZE); +// } while(false); + +// return isOk; +// } + +int32_t hex_editor_app(void* p) { + UNUSED(p); + + HexEditor* hex_editor = hex_editor_alloc(); + + FuriString* file_path; + file_path = furi_string_alloc(); + + // furi_string_printf( + // hex_editor->buffer, + // "qqqqq1231231232343454565676urtfgsdfascesc\nasdqwe\new ra sssssssssssssssssssssssssqqqqqqqqqqq1231231232343454565676urtfgsdfascesc\nq2e"); + + do { + if(p && strlen(p)) { + furi_string_set(file_path, (const char*)p); + } else { + furi_string_set(file_path, STORAGE_EXT_PATH_PREFIX); + + DialogsFileBrowserOptions browser_options; + dialog_file_browser_set_basic_options(&browser_options, "*", &I_edit_10px); + browser_options.hide_ext = false; + + DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); + bool res = dialog_file_browser_show(dialogs, file_path, file_path, &browser_options); + + furi_record_close(RECORD_DIALOGS); + if(!res) { + FURI_LOG_I(TAG, "No file selected"); + break; + } + } + + FURI_LOG_I(TAG, "File selected: %s", furi_string_get_cstr(file_path)); + + if(!hex_editor_open_file(hex_editor, furi_string_get_cstr(file_path))) break; + + if(!stream_read_line(hex_editor->model->stream, hex_editor->buffer)) { + FURI_LOG_T(TAG, "No keys left in dict"); + break; + } + + InputEvent event; + int8_t off; + while(1) { + // Выбираем событие из очереди в переменную event (ждем бесконечно долго, если очередь пуста) + // и проверяем, что у нас получилось это сделать + furi_check( + furi_message_queue_get(hex_editor->input_queue, &event, FuriWaitForever) == + FuriStatusOk); + + // Если нажата кнопка "назад", то выходим из цикла, а следовательно и из приложения + if(event.type == InputTypeShort || event.type == InputTypeRepeat) { + if(!hex_editor->model->mode) { + off = 1; + if(event.type == InputTypeRepeat) { + off = 2; + } + if(event.key == InputKeyRight) { + hex_editor->model->string_offset += off; + if(hex_editor->model->string_offset >= + furi_string_size(hex_editor->buffer)) { + // dengeros + hex_editor->model->string_offset -= + furi_string_size(hex_editor->buffer); + } + } + if(event.key == InputKeyLeft) { + if(hex_editor->model->string_offset - off < 0) { + // dengeros + hex_editor->model->string_offset += + furi_string_size(hex_editor->buffer); + } + hex_editor->model->string_offset -= off; + } + if(event.key == InputKeyDown) { + hex_editor->model->string_offset = 0; + if(!stream_read_line(hex_editor->model->stream, hex_editor->buffer)) { + FURI_LOG_T(TAG, "No keys left in dict"); + } + } + if(event.key == InputKeyUp) { + hex_editor->model->string_offset = 0; + // TODO asert + if(!stream_seek(hex_editor->model->stream, -1, StreamOffsetFromCurrent)) { + FURI_LOG_E(TAG, "Unable to seek stream"); + break; + } + // NOT work on first line + stream_seek_to_char( + hex_editor->model->stream, '\n', StreamDirectionBackward); + + // if(!stream_seek(hex_editor->model->stream, -1, StreamOffsetFromCurrent)) { + // FURI_LOG_E(TAG, "Unable to seek stream"); + // break; + // } + if(!stream_seek_to_char( + hex_editor->model->stream, '\n', StreamDirectionBackward)) { + stream_rewind(hex_editor->model->stream); + } else { + if(!stream_seek( + hex_editor->model->stream, 1, StreamOffsetFromCurrent)) { + FURI_LOG_E(TAG, "Unable to seek stream"); + break; + } + } + + if(!stream_read_line(hex_editor->model->stream, hex_editor->buffer)) { + FURI_LOG_T(TAG, "No keys left in dict"); + break; + } + } + + if(event.key == InputKeyOk) { + hex_editor->model->editable_char = furi_string_get_char( + hex_editor->buffer, hex_editor->model->string_offset); + + hex_editor->model->mode = 1; + } + } else { + off = 1; + if(event.type == InputTypeRepeat) { + off = 4; + } + if(event.key == InputKeyRight) { + hex_editor->model->editable_char += off; + } + if(event.key == InputKeyLeft) { + hex_editor->model->editable_char -= off; + } + + if(event.key == InputKeyOk) { + if(!stream_seek(hex_editor->model->stream, -1, StreamOffsetFromCurrent)) { + FURI_LOG_E(TAG, "Unable to seek stream"); + break; + } + stream_seek_to_char( + hex_editor->model->stream, '\n', StreamDirectionBackward); + stream_seek( + hex_editor->model->stream, + hex_editor->model->string_offset + 1, + StreamOffsetFromCurrent); + + stream_write_char( + hex_editor->model->stream, hex_editor->model->editable_char); + + hex_editor->model->editable_char = ' '; + + hex_editor->model->mode = 0; + + stream_seek_to_char( + hex_editor->model->stream, '\n', StreamDirectionBackward); + + if(!stream_seek(hex_editor->model->stream, 1, StreamOffsetFromCurrent)) { + FURI_LOG_E(TAG, "Unable to seek stream"); + break; + } + + if(!stream_read_line(hex_editor->model->stream, hex_editor->buffer)) { + FURI_LOG_T(TAG, "No keys left in dict"); + break; + } + } + } + } + if(event.key == InputKeyBack) { + break; + } + // ? + view_port_update(hex_editor->view_port); + } + } while(false); + + furi_string_free(file_path); + hex_editor_free(hex_editor); + + return 0; +} diff --git a/applications/external/hex_editor/icons/edit_10px.png b/applications/external/hex_editor/icons/edit_10px.png new file mode 100644 index 0000000000..eeebcb2b90 Binary files /dev/null and b/applications/external/hex_editor/icons/edit_10px.png differ diff --git a/applications/external/hex_viewer/application.fam b/applications/external/hex_viewer/application.fam index 96bf1a387b..34e52b9c72 100644 --- a/applications/external/hex_viewer/application.fam +++ b/applications/external/hex_viewer/application.fam @@ -12,4 +12,7 @@ App( fap_icon="icons/hex_10px.png", fap_category="Misc", fap_icon_assets="icons", + fap_author="@QtRoS", + fap_version="1.0", + fap_description="App allows to view various files as HEX.", ) diff --git a/applications/external/hex_viewer/hex_viewer.c b/applications/external/hex_viewer/hex_viewer.c index bd4afa1a8b..0546877447 100644 --- a/applications/external/hex_viewer/hex_viewer.c +++ b/applications/external/hex_viewer/hex_viewer.c @@ -13,7 +13,7 @@ #define TAG "HexViewer" -#define HEX_VIEWER_APP_PATH_FOLDER "/any" +#define HEX_VIEWER_APP_PATH_FOLDER STORAGE_EXT_PATH_PREFIX #define HEX_VIEWER_APP_EXTENSION "*" #define HEX_VIEWER_BYTES_PER_LINE 4u diff --git a/applications/external/hid_app/assets/Ble_disconnected_15x15.png b/applications/external/hid_app/assets/Ble_disconnected_15x15.png deleted file mode 100644 index bd54646d89..0000000000 Binary files a/applications/external/hid_app/assets/Ble_disconnected_15x15.png and /dev/null differ diff --git a/applications/external/hid_app/assets/ButtonDown_7x4.png b/applications/external/hid_app/assets/ButtonDown_7x4.png deleted file mode 100644 index 2954bb6a67..0000000000 Binary files a/applications/external/hid_app/assets/ButtonDown_7x4.png and /dev/null differ diff --git a/applications/external/hid_app/assets/ButtonLeft_4x7.png b/applications/external/hid_app/assets/ButtonLeft_4x7.png deleted file mode 100644 index 0b4655d432..0000000000 Binary files a/applications/external/hid_app/assets/ButtonLeft_4x7.png and /dev/null differ diff --git a/applications/external/hid_app/assets/ButtonRight_4x7.png b/applications/external/hid_app/assets/ButtonRight_4x7.png deleted file mode 100644 index 8e1c74c1c0..0000000000 Binary files a/applications/external/hid_app/assets/ButtonRight_4x7.png and /dev/null differ diff --git a/applications/external/hid_app/assets/ButtonUp_7x4.png b/applications/external/hid_app/assets/ButtonUp_7x4.png deleted file mode 100644 index 1be79328b4..0000000000 Binary files a/applications/external/hid_app/assets/ButtonUp_7x4.png and /dev/null differ diff --git a/applications/external/hid_app/assets/Button_18x18.png b/applications/external/hid_app/assets/Button_18x18.png deleted file mode 100644 index 30a5b4fab2..0000000000 Binary files a/applications/external/hid_app/assets/Button_18x18.png and /dev/null differ diff --git a/applications/external/hid_app/assets/Circles_47x47.png b/applications/external/hid_app/assets/Circles_47x47.png deleted file mode 100644 index 6a16ebf7bb..0000000000 Binary files a/applications/external/hid_app/assets/Circles_47x47.png and /dev/null differ diff --git a/applications/external/hid_app/assets/Left_mouse_icon_9x9.png b/applications/external/hid_app/assets/Left_mouse_icon_9x9.png deleted file mode 100644 index c533d85729..0000000000 Binary files a/applications/external/hid_app/assets/Left_mouse_icon_9x9.png and /dev/null differ diff --git a/applications/external/hid_app/assets/Ok_btn_9x9.png b/applications/external/hid_app/assets/Ok_btn_9x9.png deleted file mode 100644 index 9a1539da20..0000000000 Binary files a/applications/external/hid_app/assets/Ok_btn_9x9.png and /dev/null differ diff --git a/applications/external/hid_app/assets/Ok_btn_pressed_13x13.png b/applications/external/hid_app/assets/Ok_btn_pressed_13x13.png deleted file mode 100644 index 6b46ba3a82..0000000000 Binary files a/applications/external/hid_app/assets/Ok_btn_pressed_13x13.png and /dev/null differ diff --git a/applications/external/hid_app/assets/Pin_arrow_up_7x9.png b/applications/external/hid_app/assets/Pin_arrow_up_7x9.png deleted file mode 100644 index a91a6fd5e9..0000000000 Binary files a/applications/external/hid_app/assets/Pin_arrow_up_7x9.png and /dev/null differ diff --git a/applications/external/hid_app/assets/Pin_back_arrow_10x8.png b/applications/external/hid_app/assets/Pin_back_arrow_10x8.png deleted file mode 100644 index 3bafabd144..0000000000 Binary files a/applications/external/hid_app/assets/Pin_back_arrow_10x8.png and /dev/null differ diff --git a/applications/external/hid_app/assets/Pressed_Button_13x13.png b/applications/external/hid_app/assets/Pressed_Button_13x13.png deleted file mode 100644 index 823926b842..0000000000 Binary files a/applications/external/hid_app/assets/Pressed_Button_13x13.png and /dev/null differ diff --git a/applications/external/hid_app/assets/Right_mouse_icon_9x9.png b/applications/external/hid_app/assets/Right_mouse_icon_9x9.png deleted file mode 100644 index 446d7176c8..0000000000 Binary files a/applications/external/hid_app/assets/Right_mouse_icon_9x9.png and /dev/null differ diff --git a/applications/external/pocsag_pager/images/Quest_7x8.png b/applications/external/hid_app/assets/Space_60x18.png similarity index 72% rename from applications/external/pocsag_pager/images/Quest_7x8.png rename to applications/external/hid_app/assets/Space_60x18.png index 6825247fbe..e29f50ae92 100644 Binary files a/applications/external/pocsag_pager/images/Quest_7x8.png and b/applications/external/hid_app/assets/Space_60x18.png differ diff --git a/applications/external/hid_app/assets/Space_65x18.png b/applications/external/hid_app/assets/Space_65x18.png deleted file mode 100644 index b60ae50970..0000000000 Binary files a/applications/external/hid_app/assets/Space_65x18.png and /dev/null differ diff --git a/applications/external/hid_app/assets/Voldwn_6x6.png b/applications/external/hid_app/assets/Voldwn_6x6.png deleted file mode 100644 index ce487b5468..0000000000 Binary files a/applications/external/hid_app/assets/Voldwn_6x6.png and /dev/null differ diff --git a/applications/external/hid_app/assets/Volup_8x6.png b/applications/external/hid_app/assets/Volup_8x6.png deleted file mode 100644 index ee9045f7fd..0000000000 Binary files a/applications/external/hid_app/assets/Volup_8x6.png and /dev/null differ diff --git a/applications/external/hid_app/hid.c b/applications/external/hid_app/hid.c index 7e86882292..7b136e63f2 100644 --- a/applications/external/hid_app/hid.c +++ b/applications/external/hid_app/hid.c @@ -22,10 +22,12 @@ static void hid_submenu_callback(void* context, uint32_t index) { Hid* app = context; if(index == HidSubmenuIndexKeynote) { app->view_id = HidViewKeynote; + hid_keynote_set_orientation(app->hid_keynote, false); view_dispatcher_switch_to_view(app->view_dispatcher, HidViewKeynote); } else if(index == HidSubmenuIndexKeynoteVertical) { - app->view_id = HidViewKeynoteVertical; - view_dispatcher_switch_to_view(app->view_dispatcher, HidViewKeynoteVertical); + app->view_id = HidViewKeynote; + hid_keynote_set_orientation(app->hid_keynote, true); + view_dispatcher_switch_to_view(app->view_dispatcher, HidViewKeynote); } else if(index == HidSubmenuIndexKeyboard) { app->view_id = HidViewKeyboard; view_dispatcher_switch_to_view(app->view_dispatcher, HidViewKeyboard); @@ -62,7 +64,6 @@ static void bt_hid_connection_status_changed_callback(BtStatus status, void* con } } hid_keynote_set_connected_status(hid->hid_keynote, connected); - hid_keynote_vertical_set_connected_status(hid->hid_keynote_vertical, connected); hid_keyboard_set_connected_status(hid->hid_keyboard, connected); hid_numpad_set_connected_status(hid->hid_numpad, connected); hid_media_set_connected_status(hid->hid_media, connected); @@ -177,15 +178,6 @@ Hid* hid_app_alloc_view(void* context) { view_dispatcher_add_view( app->view_dispatcher, HidViewKeynote, hid_keynote_get_view(app->hid_keynote)); - // Keynote Vertical view - app->hid_keynote_vertical = hid_keynote_vertical_alloc(app); - view_set_previous_callback( - hid_keynote_vertical_get_view(app->hid_keynote_vertical), hid_exit_confirm_view); - view_dispatcher_add_view( - app->view_dispatcher, - HidViewKeynoteVertical, - hid_keynote_vertical_get_view(app->hid_keynote_vertical)); - // Keyboard view app->hid_keyboard = hid_keyboard_alloc(app); view_set_previous_callback(hid_keyboard_get_view(app->hid_keyboard), hid_exit_confirm_view); @@ -252,8 +244,6 @@ void hid_free(Hid* app) { dialog_ex_free(app->dialog); view_dispatcher_remove_view(app->view_dispatcher, HidViewKeynote); hid_keynote_free(app->hid_keynote); - view_dispatcher_remove_view(app->view_dispatcher, HidViewKeynoteVertical); - hid_keynote_vertical_free(app->hid_keynote_vertical); view_dispatcher_remove_view(app->view_dispatcher, HidViewKeyboard); hid_keyboard_free(app->hid_keyboard); view_dispatcher_remove_view(app->view_dispatcher, HidViewNumpad); @@ -414,7 +404,7 @@ int32_t hid_usb_app(void* p) { bt_hid_connection_status_changed_callback(BtStatusConnected, app); - DOLPHIN_DEED(DolphinDeedPluginStart); + dolphin_deed(DolphinDeedPluginStart); view_dispatcher_run(app->view_dispatcher); @@ -438,7 +428,7 @@ int32_t hid_ble_app(void* p) { // Migrate data from old sd-card folder Storage* storage = furi_record_open(RECORD_STORAGE); - storage_common_rename( + storage_common_migrate( storage, EXT_PATH("apps/Tools/" HID_BT_KEYS_STORAGE_NAME), APP_DATA_PATH(HID_BT_KEYS_STORAGE_NAME)); @@ -454,7 +444,7 @@ int32_t hid_ble_app(void* p) { furi_hal_bt_start_advertising(); bt_set_status_changed_callback(app->bt, bt_hid_connection_status_changed_callback, app); - DOLPHIN_DEED(DolphinDeedPluginStart); + dolphin_deed(DolphinDeedPluginStart); view_dispatcher_run(app->view_dispatcher); diff --git a/applications/external/hid_app/hid.h b/applications/external/hid_app/hid.h index f7c310e027..2b5a8059b4 100644 --- a/applications/external/hid_app/hid.h +++ b/applications/external/hid_app/hid.h @@ -17,7 +17,6 @@ #include #include #include "views/hid_keynote.h" -#include "views/hid_keynote_vertical.h" #include "views/hid_keyboard.h" #include "views/hid_numpad.h" #include "views/hid_media.h" @@ -26,6 +25,9 @@ #include "views/hid_tikshorts.h" #include "views/hid_mouse_clicker.h" +#include "hid_icons.h" +#include + #include "hid_path.h" typedef enum { @@ -43,7 +45,6 @@ struct Hid { Submenu* device_type_submenu; DialogEx* dialog; HidKeynote* hid_keynote; - HidKeynoteVertical* hid_keynote_vertical; HidKeyboard* hid_keyboard; HidNumpad* hid_numpad; HidMedia* hid_media; diff --git a/applications/external/hid_app/views.h b/applications/external/hid_app/views.h index 5d02220cd6..1f15364863 100644 --- a/applications/external/hid_app/views.h +++ b/applications/external/hid_app/views.h @@ -1,7 +1,6 @@ typedef enum { HidViewSubmenu, HidViewKeynote, - HidViewKeynoteVertical, HidViewKeyboard, HidViewNumpad, HidViewMedia, diff --git a/applications/external/hid_app/views/hid_keyboard.c b/applications/external/hid_app/views/hid_keyboard.c index 9d29ccf5e3..9b8c87b137 100644 --- a/applications/external/hid_app/views/hid_keyboard.c +++ b/applications/external/hid_app/views/hid_keyboard.c @@ -3,7 +3,6 @@ #include #include #include "../hid.h" -#include "hid_icons.h" #define TAG "HidKeyboard" diff --git a/applications/external/hid_app/views/hid_keynote.c b/applications/external/hid_app/views/hid_keynote.c index 5e5eeb7909..e3d5f7a994 100644 --- a/applications/external/hid_app/views/hid_keynote.c +++ b/applications/external/hid_app/views/hid_keynote.c @@ -2,8 +2,6 @@ #include #include "../hid.h" -#include "hid_icons.h" - #define TAG "HidKeynote" struct HidKeynote { @@ -111,6 +109,91 @@ static void hid_keynote_draw_callback(Canvas* canvas, void* context) { elements_multiline_text_aligned(canvas, 91, 57, AlignLeft, AlignBottom, "Back"); } +static void hid_keynote_draw_vertical_callback(Canvas* canvas, void* context) { + furi_assert(context); + HidKeynoteModel* model = context; + + // Header + canvas_set_font(canvas, FontPrimary); + if(model->transport == HidTransportBle) { + if(model->connected) { + canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15); + } else { + canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15); + } + + elements_multiline_text_aligned(canvas, 20, 3, AlignLeft, AlignTop, "Keynote"); + } else { + elements_multiline_text_aligned(canvas, 12, 3, AlignLeft, AlignTop, "Keynote"); + } + + canvas_draw_icon(canvas, 2, 18, &I_Pin_back_arrow_10x8); + canvas_set_font(canvas, FontSecondary); + elements_multiline_text_aligned(canvas, 15, 19, AlignLeft, AlignTop, "Hold to exit"); + + const uint8_t x_2 = 23; + const uint8_t x_1 = 2; + const uint8_t x_3 = 44; + + const uint8_t y_1 = 44; + const uint8_t y_2 = 65; + + // Up + canvas_draw_icon(canvas, x_2, y_1, &I_Button_18x18); + if(model->up_pressed) { + elements_slightly_rounded_box(canvas, x_2 + 3, y_1 + 2, 13, 13); + canvas_set_color(canvas, ColorWhite); + } + hid_keynote_draw_arrow(canvas, x_2 + 9, y_1 + 6, CanvasDirectionBottomToTop); + canvas_set_color(canvas, ColorBlack); + + // Down + canvas_draw_icon(canvas, x_2, y_2, &I_Button_18x18); + if(model->down_pressed) { + elements_slightly_rounded_box(canvas, x_2 + 3, y_2 + 2, 13, 13); + canvas_set_color(canvas, ColorWhite); + } + hid_keynote_draw_arrow(canvas, x_2 + 9, y_2 + 10, CanvasDirectionTopToBottom); + canvas_set_color(canvas, ColorBlack); + + // Left + canvas_draw_icon(canvas, x_1, y_2, &I_Button_18x18); + if(model->left_pressed) { + elements_slightly_rounded_box(canvas, x_1 + 3, y_2 + 2, 13, 13); + canvas_set_color(canvas, ColorWhite); + } + hid_keynote_draw_arrow(canvas, x_1 + 7, y_2 + 8, CanvasDirectionRightToLeft); + canvas_set_color(canvas, ColorBlack); + + // Right + canvas_draw_icon(canvas, x_3, y_2, &I_Button_18x18); + if(model->right_pressed) { + elements_slightly_rounded_box(canvas, x_3 + 3, y_2 + 2, 13, 13); + canvas_set_color(canvas, ColorWhite); + } + hid_keynote_draw_arrow(canvas, x_3 + 11, y_2 + 8, CanvasDirectionLeftToRight); + canvas_set_color(canvas, ColorBlack); + + // Ok + canvas_draw_icon(canvas, 2, 86, &I_Space_60x18); + if(model->ok_pressed) { + elements_slightly_rounded_box(canvas, 5, 88, 55, 13); + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_icon(canvas, 11, 90, &I_Ok_btn_9x9); + elements_multiline_text_aligned(canvas, 26, 98, AlignLeft, AlignBottom, "Space"); + canvas_set_color(canvas, ColorBlack); + + // Back + canvas_draw_icon(canvas, 2, 107, &I_Space_60x18); + if(model->back_pressed) { + elements_slightly_rounded_box(canvas, 5, 109, 55, 13); + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_icon(canvas, 11, 111, &I_Pin_back_arrow_10x8); + elements_multiline_text_aligned(canvas, 26, 119, AlignLeft, AlignBottom, "Back"); +} + static void hid_keynote_process(HidKeynote* hid_keynote, InputEvent* event) { with_view_model( hid_keynote->view, @@ -212,3 +295,16 @@ void hid_keynote_set_connected_status(HidKeynote* hid_keynote, bool connected) { with_view_model( hid_keynote->view, HidKeynoteModel * model, { model->connected = connected; }, true); } + +void hid_keynote_set_orientation(HidKeynote* hid_keynote, bool vertical) { + furi_assert(hid_keynote); + + if(vertical) { + view_set_draw_callback(hid_keynote->view, hid_keynote_draw_vertical_callback); + view_set_orientation(hid_keynote->view, ViewOrientationVerticalFlip); + + } else { + view_set_draw_callback(hid_keynote->view, hid_keynote_draw_callback); + view_set_orientation(hid_keynote->view, ViewOrientationHorizontal); + } +} diff --git a/applications/external/hid_app/views/hid_keynote.h b/applications/external/hid_app/views/hid_keynote.h index 4d4a0a9b1a..84bfed4ce4 100644 --- a/applications/external/hid_app/views/hid_keynote.h +++ b/applications/external/hid_app/views/hid_keynote.h @@ -12,3 +12,5 @@ void hid_keynote_free(HidKeynote* hid_keynote); View* hid_keynote_get_view(HidKeynote* hid_keynote); void hid_keynote_set_connected_status(HidKeynote* hid_keynote, bool connected); + +void hid_keynote_set_orientation(HidKeynote* hid_keynote, bool vertical); diff --git a/applications/external/hid_app/views/hid_keynote_vertical.c b/applications/external/hid_app/views/hid_keynote_vertical.c deleted file mode 100644 index 7d23038133..0000000000 --- a/applications/external/hid_app/views/hid_keynote_vertical.c +++ /dev/null @@ -1,228 +0,0 @@ -#include "hid_keynote_vertical.h" -#include -#include "../hid.h" - -#include "hid_icons.h" - -#define TAG "HidKeynoteVertical" - -struct HidKeynoteVertical { - View* view; - Hid* hid; -}; - -typedef struct { - bool left_pressed; - bool up_pressed; - bool right_pressed; - bool down_pressed; - bool ok_pressed; - bool back_pressed; - bool connected; - HidTransport transport; -} HidKeynoteVerticalModel; - -static void - hid_keynote_vertical_draw_arrow(Canvas* canvas, uint8_t x, uint8_t y, CanvasDirection dir) { - canvas_draw_triangle(canvas, x, y, 5, 3, dir); - if(dir == CanvasDirectionBottomToTop) { - canvas_draw_line(canvas, x, y + 6, x, y - 1); - } else if(dir == CanvasDirectionTopToBottom) { - canvas_draw_line(canvas, x, y - 6, x, y + 1); - } else if(dir == CanvasDirectionRightToLeft) { - canvas_draw_line(canvas, x + 6, y, x - 1, y); - } else if(dir == CanvasDirectionLeftToRight) { - canvas_draw_line(canvas, x - 6, y, x + 1, y); - } -} - -static void hid_keynote_vertical_draw_callback(Canvas* canvas, void* context) { - furi_assert(context); - HidKeynoteVerticalModel* model = context; - - // Header - if(model->transport == HidTransportBle) { - if(model->connected) { - canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15); - } else { - canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15); - } - } - - canvas_set_font(canvas, FontPrimary); - elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Keynote"); - canvas_set_font(canvas, FontSecondary); - elements_multiline_text_aligned( - canvas, 24, 14, AlignLeft, AlignTop, "Vertical Up --->"); - - canvas_draw_icon(canvas, 68, 2, &I_Pin_back_arrow_10x8); - canvas_set_font(canvas, FontSecondary); - elements_multiline_text_aligned(canvas, 127, 3, AlignRight, AlignTop, "Hold to exit"); - - // Up - canvas_draw_icon(canvas, 21, 24, &I_Button_18x18); - if(model->up_pressed) { - elements_slightly_rounded_box(canvas, 24, 26, 13, 13); - canvas_set_color(canvas, ColorWhite); - } - hid_keynote_vertical_draw_arrow(canvas, 30, 30, CanvasDirectionBottomToTop); - canvas_set_color(canvas, ColorBlack); - - // Down - canvas_draw_icon(canvas, 21, 45, &I_Button_18x18); - if(model->down_pressed) { - elements_slightly_rounded_box(canvas, 24, 47, 13, 13); - canvas_set_color(canvas, ColorWhite); - } - hid_keynote_vertical_draw_arrow(canvas, 30, 55, CanvasDirectionTopToBottom); - canvas_set_color(canvas, ColorBlack); - - // Left - canvas_draw_icon(canvas, 0, 35, &I_Button_18x18); - if(model->left_pressed) { - elements_slightly_rounded_box(canvas, 3, 37, 13, 13); - canvas_set_color(canvas, ColorWhite); - } - hid_keynote_vertical_draw_arrow(canvas, 7, 43, CanvasDirectionRightToLeft); - canvas_set_color(canvas, ColorBlack); - - // Right - canvas_draw_icon(canvas, 42, 35, &I_Button_18x18); - if(model->right_pressed) { - elements_slightly_rounded_box(canvas, 45, 37, 13, 13); - canvas_set_color(canvas, ColorWhite); - } - hid_keynote_vertical_draw_arrow(canvas, 53, 43, CanvasDirectionLeftToRight); - canvas_set_color(canvas, ColorBlack); - - // Ok - canvas_draw_icon(canvas, 63, 25, &I_Space_65x18); - if(model->ok_pressed) { - elements_slightly_rounded_box(canvas, 66, 27, 60, 13); - canvas_set_color(canvas, ColorWhite); - } - canvas_draw_icon(canvas, 74, 29, &I_Ok_btn_9x9); - elements_multiline_text_aligned(canvas, 91, 36, AlignLeft, AlignBottom, "Space"); - canvas_set_color(canvas, ColorBlack); - - // Back - canvas_draw_icon(canvas, 63, 45, &I_Space_65x18); - if(model->back_pressed) { - elements_slightly_rounded_box(canvas, 66, 47, 60, 13); - canvas_set_color(canvas, ColorWhite); - } - canvas_draw_icon(canvas, 74, 49, &I_Pin_back_arrow_10x8); - elements_multiline_text_aligned(canvas, 91, 57, AlignLeft, AlignBottom, "Back"); -} - -static void - hid_keynote_vertical_process(HidKeynoteVertical* hid_keynote_vertical, InputEvent* event) { - with_view_model( - hid_keynote_vertical->view, - HidKeynoteVerticalModel * model, - { - if(event->type == InputTypePress) { - if(event->key == InputKeyUp) { - model->up_pressed = true; - hid_hal_keyboard_press(hid_keynote_vertical->hid, HID_KEYBOARD_LEFT_ARROW); - } else if(event->key == InputKeyDown) { - model->down_pressed = true; - hid_hal_keyboard_press(hid_keynote_vertical->hid, HID_KEYBOARD_RIGHT_ARROW); - } else if(event->key == InputKeyLeft) { - model->left_pressed = true; - hid_hal_keyboard_press(hid_keynote_vertical->hid, HID_KEYBOARD_DOWN_ARROW); - } else if(event->key == InputKeyRight) { - model->right_pressed = true; - hid_hal_keyboard_press(hid_keynote_vertical->hid, HID_KEYBOARD_UP_ARROW); - } else if(event->key == InputKeyOk) { - model->ok_pressed = true; - hid_hal_keyboard_press(hid_keynote_vertical->hid, HID_KEYBOARD_SPACEBAR); - } else if(event->key == InputKeyBack) { - model->back_pressed = true; - } - } else if(event->type == InputTypeRelease) { - if(event->key == InputKeyUp) { - model->up_pressed = false; - hid_hal_keyboard_release(hid_keynote_vertical->hid, HID_KEYBOARD_LEFT_ARROW); - } else if(event->key == InputKeyDown) { - model->down_pressed = false; - hid_hal_keyboard_release(hid_keynote_vertical->hid, HID_KEYBOARD_RIGHT_ARROW); - } else if(event->key == InputKeyLeft) { - model->left_pressed = false; - hid_hal_keyboard_release(hid_keynote_vertical->hid, HID_KEYBOARD_DOWN_ARROW); - } else if(event->key == InputKeyRight) { - model->right_pressed = false; - hid_hal_keyboard_release(hid_keynote_vertical->hid, HID_KEYBOARD_UP_ARROW); - } else if(event->key == InputKeyOk) { - model->ok_pressed = false; - hid_hal_keyboard_release(hid_keynote_vertical->hid, HID_KEYBOARD_SPACEBAR); - } else if(event->key == InputKeyBack) { - model->back_pressed = false; - } - } else if(event->type == InputTypeShort) { - if(event->key == InputKeyBack) { - hid_hal_keyboard_press(hid_keynote_vertical->hid, HID_KEYBOARD_DELETE); - hid_hal_keyboard_release(hid_keynote_vertical->hid, HID_KEYBOARD_DELETE); - hid_hal_consumer_key_press(hid_keynote_vertical->hid, HID_CONSUMER_AC_BACK); - hid_hal_consumer_key_release(hid_keynote_vertical->hid, HID_CONSUMER_AC_BACK); - } - } - }, - true); -} - -static bool hid_keynote_vertical_input_callback(InputEvent* event, void* context) { - furi_assert(context); - HidKeynoteVertical* hid_keynote_vertical = context; - bool consumed = false; - - if(event->type == InputTypeLong && event->key == InputKeyBack) { - hid_hal_keyboard_release_all(hid_keynote_vertical->hid); - } else { - hid_keynote_vertical_process(hid_keynote_vertical, event); - consumed = true; - } - - return consumed; -} - -HidKeynoteVertical* hid_keynote_vertical_alloc(Hid* hid) { - HidKeynoteVertical* hid_keynote_vertical = malloc(sizeof(HidKeynoteVertical)); - hid_keynote_vertical->view = view_alloc(); - hid_keynote_vertical->hid = hid; - view_set_context(hid_keynote_vertical->view, hid_keynote_vertical); - view_allocate_model( - hid_keynote_vertical->view, ViewModelTypeLocking, sizeof(HidKeynoteVerticalModel)); - view_set_draw_callback(hid_keynote_vertical->view, hid_keynote_vertical_draw_callback); - view_set_input_callback(hid_keynote_vertical->view, hid_keynote_vertical_input_callback); - - with_view_model( - hid_keynote_vertical->view, - HidKeynoteVerticalModel * model, - { model->transport = hid->transport; }, - true); - - return hid_keynote_vertical; -} - -void hid_keynote_vertical_free(HidKeynoteVertical* hid_keynote_vertical) { - furi_assert(hid_keynote_vertical); - view_free(hid_keynote_vertical->view); - free(hid_keynote_vertical); -} - -View* hid_keynote_vertical_get_view(HidKeynoteVertical* hid_keynote_vertical) { - furi_assert(hid_keynote_vertical); - return hid_keynote_vertical->view; -} - -void hid_keynote_vertical_set_connected_status( - HidKeynoteVertical* hid_keynote_vertical, - bool connected) { - furi_assert(hid_keynote_vertical); - with_view_model( - hid_keynote_vertical->view, - HidKeynoteVerticalModel * model, - { model->connected = connected; }, - true); -} diff --git a/applications/external/hid_app/views/hid_keynote_vertical.h b/applications/external/hid_app/views/hid_keynote_vertical.h deleted file mode 100644 index bb7134adb4..0000000000 --- a/applications/external/hid_app/views/hid_keynote_vertical.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include - -typedef struct Hid Hid; -typedef struct HidKeynoteVertical HidKeynoteVertical; - -HidKeynoteVertical* hid_keynote_vertical_alloc(Hid* bt_hid); - -void hid_keynote_vertical_free(HidKeynoteVertical* hid_keynote_vertical); - -View* hid_keynote_vertical_get_view(HidKeynoteVertical* hid_keynote_vertical); - -void hid_keynote_vertical_set_connected_status( - HidKeynoteVertical* hid_keynote_vertical, - bool connected); diff --git a/applications/external/hid_app/views/hid_media.c b/applications/external/hid_app/views/hid_media.c index 0028ac596d..98f88819fe 100644 --- a/applications/external/hid_app/views/hid_media.c +++ b/applications/external/hid_app/views/hid_media.c @@ -5,8 +5,6 @@ #include #include "../hid.h" -#include "hid_icons.h" - #define TAG "HidMedia" struct HidMedia { diff --git a/applications/external/hid_app/views/hid_mouse.c b/applications/external/hid_app/views/hid_mouse.c index 75df53dd1f..e93ea3fc17 100644 --- a/applications/external/hid_app/views/hid_mouse.c +++ b/applications/external/hid_app/views/hid_mouse.c @@ -2,8 +2,6 @@ #include #include "../hid.h" -#include "hid_icons.h" - #define TAG "HidMouse" struct HidMouse { diff --git a/applications/external/hid_app/views/hid_mouse_clicker.c b/applications/external/hid_app/views/hid_mouse_clicker.c index d85affc433..6777034ffd 100644 --- a/applications/external/hid_app/views/hid_mouse_clicker.c +++ b/applications/external/hid_app/views/hid_mouse_clicker.c @@ -2,8 +2,6 @@ #include #include "../hid.h" -#include "hid_icons.h" - #define TAG "HidMouseClicker" #define DEFAULT_CLICK_RATE 1 #define MAXIMUM_CLICK_RATE 60 diff --git a/applications/external/hid_app/views/hid_mouse_jiggler.c b/applications/external/hid_app/views/hid_mouse_jiggler.c index 09c14c6688..c2ff93f605 100644 --- a/applications/external/hid_app/views/hid_mouse_jiggler.c +++ b/applications/external/hid_app/views/hid_mouse_jiggler.c @@ -2,8 +2,6 @@ #include #include "../hid.h" -#include "hid_icons.h" - #define TAG "HidMouseJiggler" struct HidMouseJiggler { diff --git a/applications/external/hid_app/views/hid_numpad.c b/applications/external/hid_app/views/hid_numpad.c index d3b488801c..b342bb38dd 100644 --- a/applications/external/hid_app/views/hid_numpad.c +++ b/applications/external/hid_app/views/hid_numpad.c @@ -3,7 +3,6 @@ #include #include #include "../hid.h" -#include "hid_icons.h" #define TAG "HidNumpad" @@ -39,26 +38,26 @@ typedef struct { int8_t y; } HidNumpadPoint; -#define MARGIN_TOP 0 -#define MARGIN_LEFT 24 +#define MARGIN_TOP 32 +#define MARGIN_LEFT 1 #define KEY_WIDTH 20 #define KEY_HEIGHT 15 #define KEY_PADDING 1 -#define ROW_COUNT 5 -#define COLUMN_COUNT 4 +#define ROW_COUNT 6 +#define COLUMN_COUNT 3 const HidNumpadKey hid_numpad_keyset[ROW_COUNT][COLUMN_COUNT] = { { {.width = 1, .height = 1, .icon = NULL, .key = "NL", .value = HID_KEYPAD_NUMLOCK}, {.width = 1, .height = 1, .icon = NULL, .key = "/", .value = HID_KEYPAD_SLASH}, {.width = 1, .height = 1, .icon = NULL, .key = "*", .value = HID_KEYPAD_ASTERISK}, - {.width = 1, .height = 1, .icon = NULL, .key = "-", .value = HID_KEYPAD_MINUS}, + // {.width = 1, .height = 1, .icon = NULL, .key = "-", .value = HID_KEYPAD_MINUS}, }, { {.width = 1, .height = 1, .icon = NULL, .key = "7", .value = HID_KEYPAD_7}, {.width = 1, .height = 1, .icon = NULL, .key = "8", .value = HID_KEYBOARD_8}, {.width = 1, .height = 1, .icon = NULL, .key = "9", .value = HID_KEYBOARD_9}, - {.width = 1, .height = 2, .icon = NULL, .key = "+", .value = HID_KEYPAD_PLUS}, + // {.width = 1, .height = 2, .icon = NULL, .key = "+", .value = HID_KEYPAD_PLUS}, }, { {.width = 1, .height = 1, .icon = NULL, .key = "4", .value = HID_KEYPAD_4}, @@ -69,13 +68,18 @@ const HidNumpadKey hid_numpad_keyset[ROW_COUNT][COLUMN_COUNT] = { {.width = 1, .height = 1, .icon = NULL, .key = "1", .value = HID_KEYPAD_1}, {.width = 1, .height = 1, .icon = NULL, .key = "2", .value = HID_KEYPAD_2}, {.width = 1, .height = 1, .icon = NULL, .key = "3", .value = HID_KEYPAD_3}, - {.width = 1, .height = 2, .icon = NULL, .key = "En", .value = HID_KEYPAD_ENTER}, + // {.width = 1, .height = 2, .icon = NULL, .key = "En", .value = HID_KEYPAD_ENTER}, }, { {.width = 2, .height = 1, .icon = NULL, .key = "0", .value = HID_KEYBOARD_0}, {.width = 0, .height = 0, .icon = NULL, .key = "0", .value = HID_KEYBOARD_0}, {.width = 1, .height = 1, .icon = NULL, .key = ".", .value = HID_KEYPAD_DOT}, }, + { + {.width = 1, .height = 1, .icon = NULL, .key = "En", .value = HID_KEYPAD_ENTER}, + {.width = 1, .height = 1, .icon = NULL, .key = "-", .value = HID_KEYPAD_MINUS}, + {.width = 1, .height = 1, .icon = NULL, .key = "+", .value = HID_KEYPAD_PLUS}, + }, }; static void hid_numpad_draw_key( @@ -128,26 +132,36 @@ static void hid_numpad_draw_callback(Canvas* canvas, void* context) { furi_assert(context); HidNumpadModel* model = context; - if((!model->connected) && (model->transport == HidTransportBle)) { - canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15); - canvas_set_font(canvas, FontPrimary); - elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Numpad"); + // Header + canvas_set_font(canvas, FontPrimary); + if(model->transport == HidTransportBle) { + if(model->connected) { + canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15); + } else { + canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15); + elements_multiline_text_aligned( + canvas, 7, 60, AlignLeft, AlignBottom, "Waiting for\nConnection..."); + } + elements_multiline_text_aligned(canvas, 20, 3, AlignLeft, AlignTop, "Numpad"); - canvas_draw_icon(canvas, 68, 3, &I_Pin_back_arrow_10x8); - canvas_set_font(canvas, FontSecondary); - elements_multiline_text_aligned(canvas, 127, 4, AlignRight, AlignTop, "Hold to exit"); + } else { + elements_multiline_text_aligned(canvas, 12, 3, AlignLeft, AlignTop, "Numpad"); + } - elements_multiline_text_aligned( - canvas, 4, 60, AlignLeft, AlignBottom, "Waiting for Connection..."); + canvas_draw_icon(canvas, 3, 18, &I_Pin_back_arrow_10x8); + canvas_set_font(canvas, FontSecondary); + elements_multiline_text_aligned(canvas, 15, 19, AlignLeft, AlignTop, "Hold to exit"); + + if(!model->connected && (model->transport == HidTransportBle)) { return; } canvas_set_font(canvas, FontKeyboard); - uint8_t initY = model->y == 0 ? 0 : 1; + uint8_t initY = 0; // = model->y == 0 ? 0 : 1; - if(model->y > 5) { - initY = model->y - 4; - } + // if(model->y > ROW_COUNT) { + // initY = model->y - (ROW_COUNT - 1); + // } for(uint8_t y = initY; y < ROW_COUNT; y++) { const HidNumpadKey* numpadKeyRow = hid_numpad_keyset[y]; @@ -269,6 +283,7 @@ HidNumpad* hid_numpad_alloc(Hid* bt_hid) { hid_numpad->hid = bt_hid; view_set_context(hid_numpad->view, hid_numpad); view_allocate_model(hid_numpad->view, ViewModelTypeLocking, sizeof(HidNumpadModel)); + view_set_orientation(hid_numpad->view, ViewOrientationVertical); view_set_draw_callback(hid_numpad->view, hid_numpad_draw_callback); view_set_input_callback(hid_numpad->view, hid_numpad_input_callback); diff --git a/applications/external/hid_app/views/hid_tikshorts.c b/applications/external/hid_app/views/hid_tikshorts.c index 0e156fdf05..b494245cf1 100644 --- a/applications/external/hid_app/views/hid_tikshorts.c +++ b/applications/external/hid_app/views/hid_tikshorts.c @@ -2,8 +2,6 @@ #include "../hid.h" #include -#include "hid_icons.h" - #define TAG "HidTikShorts" struct HidTikShorts { diff --git a/applications/external/ibtn_fuzzer/LICENSE.md b/applications/external/ibtn_fuzzer/LICENSE.md deleted file mode 100644 index ba3b844562..0000000000 --- a/applications/external/ibtn_fuzzer/LICENSE.md +++ /dev/null @@ -1,8 +0,0 @@ -/* - * ---------------------------------------------------------------------------- - * "THE BEER-WARE LICENSE" (Revision 42): - * @xMasterX and @G4N4P4T1(made original version) wrote this file. As long as you retain this notice you - * can do whatever you want with this stuff. If we meet some day, and you think - * this stuff is worth it, you can buy me a beer in return. - * ---------------------------------------------------------------------------- - */ \ No newline at end of file diff --git a/applications/external/ibtn_fuzzer/application.fam b/applications/external/ibtn_fuzzer/application.fam deleted file mode 100644 index 87c02a9136..0000000000 --- a/applications/external/ibtn_fuzzer/application.fam +++ /dev/null @@ -1,12 +0,0 @@ -App( - appid="ibtn_fuzzer", - name="iButton Fuzzer", - apptype=FlipperAppType.EXTERNAL, - entry_point="ibtnfuzzer_start", - requires=["gui", "storage", "dialogs", "input", "notification"], - stack_size=1 * 1024, - order=15, - fap_icon="ibutt_10px.png", - fap_category="Tools", - fap_icon_assets="images", -) diff --git a/applications/external/ibtn_fuzzer/ibtnfuzzer.c b/applications/external/ibtn_fuzzer/ibtnfuzzer.c deleted file mode 100644 index 9e6239b695..0000000000 --- a/applications/external/ibtn_fuzzer/ibtnfuzzer.c +++ /dev/null @@ -1,282 +0,0 @@ -#include "ibtnfuzzer.h" - -#include "scene/ibtnfuzzer_scene_entrypoint.h" -#include "scene/ibtnfuzzer_scene_load_file.h" -#include "scene/ibtnfuzzer_scene_select_field.h" -#include "scene/ibtnfuzzer_scene_run_attack.h" -#include "scene/ibtnfuzzer_scene_load_custom_uids.h" -#include - -#define IBTNFUZZER_APP_FOLDER "/ext/ibtnfuzzer" - -static void ibtnfuzzer_draw_callback(Canvas* const canvas, void* ctx) { - furi_assert(ctx); - iBtnFuzzerState* ibtnfuzzer_state = ctx; - furi_mutex_acquire(ibtnfuzzer_state->mutex, FuriWaitForever); - - // Draw correct Canvas - switch(ibtnfuzzer_state->current_scene) { - case NoneScene: - case SceneEntryPoint: - ibtnfuzzer_scene_entrypoint_on_draw(canvas, ibtnfuzzer_state); - break; - case SceneSelectFile: - ibtnfuzzer_scene_load_file_on_draw(canvas, ibtnfuzzer_state); - break; - case SceneSelectField: - ibtnfuzzer_scene_select_field_on_draw(canvas, ibtnfuzzer_state); - break; - case SceneAttack: - ibtnfuzzer_scene_run_attack_on_draw(canvas, ibtnfuzzer_state); - break; - case SceneLoadCustomUids: - ibtnfuzzer_scene_load_custom_uids_on_draw(canvas, ibtnfuzzer_state); - break; - } - - furi_mutex_release(ibtnfuzzer_state->mutex); -} - -void ibtnfuzzer_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { - furi_assert(event_queue); - - iBtnFuzzerEvent event = { - .evt_type = EventTypeKey, .key = input_event->key, .input_type = input_event->type}; - furi_message_queue_put(event_queue, &event, 25); -} - -static void ibtnfuzzer_timer_callback(FuriMessageQueue* event_queue) { - furi_assert(event_queue); - iBtnFuzzerEvent event = { - .evt_type = EventTypeTick, .key = InputKeyUp, .input_type = InputTypeRelease}; - furi_message_queue_put(event_queue, &event, 25); -} - -iBtnFuzzerState* ibtnfuzzer_alloc() { - iBtnFuzzerState* ibtnfuzzer = malloc(sizeof(iBtnFuzzerState)); - ibtnfuzzer->notification_msg = furi_string_alloc(); - ibtnfuzzer->attack_name = furi_string_alloc(); - ibtnfuzzer->proto_name = furi_string_alloc(); - ibtnfuzzer->data_str = furi_string_alloc(); - - ibtnfuzzer->main_menu_items[0] = furi_string_alloc_set("Default Values"); - ibtnfuzzer->main_menu_items[1] = furi_string_alloc_set("Load File"); - ibtnfuzzer->main_menu_items[2] = furi_string_alloc_set("Load UIDs from file"); - - ibtnfuzzer->main_menu_proto_items[0] = furi_string_alloc_set("DS1990"); - ibtnfuzzer->main_menu_proto_items[1] = furi_string_alloc_set("Metakom"); - ibtnfuzzer->main_menu_proto_items[2] = furi_string_alloc_set("Cyfral"); - - ibtnfuzzer->previous_scene = NoneScene; - ibtnfuzzer->current_scene = SceneEntryPoint; - ibtnfuzzer->is_running = true; - ibtnfuzzer->is_attacking = false; - ibtnfuzzer->key_index = 0; - ibtnfuzzer->menu_index = 0; - ibtnfuzzer->menu_proto_index = 0; - - ibtnfuzzer->attack = iBtnFuzzerAttackDefaultValues; - ibtnfuzzer->notify = furi_record_open(RECORD_NOTIFICATION); - - ibtnfuzzer->data[0] = 0x00; - ibtnfuzzer->data[1] = 0x00; - ibtnfuzzer->data[2] = 0x00; - ibtnfuzzer->data[3] = 0x00; - ibtnfuzzer->data[4] = 0x00; - ibtnfuzzer->data[5] = 0x00; - ibtnfuzzer->data[6] = 0x00; - ibtnfuzzer->data[7] = 0x00; - - ibtnfuzzer->payload[0] = 0x00; - ibtnfuzzer->payload[1] = 0x00; - ibtnfuzzer->payload[2] = 0x00; - ibtnfuzzer->payload[3] = 0x00; - ibtnfuzzer->payload[4] = 0x00; - ibtnfuzzer->payload[5] = 0x00; - ibtnfuzzer->payload[6] = 0x00; - ibtnfuzzer->payload[7] = 0x00; - - //Dialog - ibtnfuzzer->dialogs = furi_record_open(RECORD_DIALOGS); - - return ibtnfuzzer; -} - -void ibtnfuzzer_free(iBtnFuzzerState* ibtnfuzzer) { - //Dialog - furi_record_close(RECORD_DIALOGS); - notification_message(ibtnfuzzer->notify, &sequence_blink_stop); - - // Strings - furi_string_free(ibtnfuzzer->notification_msg); - furi_string_free(ibtnfuzzer->attack_name); - furi_string_free(ibtnfuzzer->proto_name); - furi_string_free(ibtnfuzzer->data_str); - - for(uint32_t i = 0; i < 3; i++) { - furi_string_free(ibtnfuzzer->main_menu_items[i]); - } - - for(uint32_t i = 0; i < 3; i++) { - furi_string_free(ibtnfuzzer->main_menu_proto_items[i]); - } - - // The rest - free(ibtnfuzzer); -} - -// ENTRYPOINT -int32_t ibtnfuzzer_start(void* p) { - UNUSED(p); - // Input - FURI_LOG_I(TAG, "Initializing input"); - FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(iBtnFuzzerEvent)); - iBtnFuzzerState* ibtnfuzzer_state = ibtnfuzzer_alloc(); - - DOLPHIN_DEED(DolphinDeedPluginStart); - ibtnfuzzer_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); - if(!ibtnfuzzer_state->mutex) { - FURI_LOG_E(TAG, "cannot create mutex\r\n"); - furi_message_queue_free(event_queue); - furi_record_close(RECORD_NOTIFICATION); - ibtnfuzzer_free(ibtnfuzzer_state); - return 255; - } - - Storage* storage = furi_record_open(RECORD_STORAGE); - if(!storage_simply_mkdir(storage, IBTNFUZZER_APP_FOLDER)) { - FURI_LOG_E(TAG, "Could not create folder %s", IBTNFUZZER_APP_FOLDER); - } - furi_record_close(RECORD_STORAGE); - - // Configure view port - FURI_LOG_I(TAG, "Initializing viewport"); - ViewPort* view_port = view_port_alloc(); - view_port_draw_callback_set(view_port, ibtnfuzzer_draw_callback, ibtnfuzzer_state); - view_port_input_callback_set(view_port, ibtnfuzzer_input_callback, event_queue); - - // Configure timer - FURI_LOG_I(TAG, "Initializing timer"); - FuriTimer* timer = - furi_timer_alloc(ibtnfuzzer_timer_callback, FuriTimerTypePeriodic, event_queue); - furi_timer_start(timer, furi_kernel_get_tick_frequency() / 10); // 10 times per second - - // Register view port in GUI - FURI_LOG_I(TAG, "Initializing gui"); - Gui* gui = (Gui*)furi_record_open(RECORD_GUI); - gui_add_view_port(gui, view_port, GuiLayerFullscreen); - - // Init values - iBtnFuzzerEvent event; - while(ibtnfuzzer_state->is_running) { - // Get next event - FuriStatus event_status = furi_message_queue_get(event_queue, &event, 25); - //furi_mutex_acquire(ibtnfuzzer_state->mutex, FuriWaitForever); - if(event_status == FuriStatusOk) { - if(event.evt_type == EventTypeKey) { - //Handle event key - switch(ibtnfuzzer_state->current_scene) { - case NoneScene: - case SceneEntryPoint: - ibtnfuzzer_scene_entrypoint_on_event(event, ibtnfuzzer_state); - break; - case SceneSelectFile: - ibtnfuzzer_scene_load_file_on_event(event, ibtnfuzzer_state); - break; - case SceneSelectField: - ibtnfuzzer_scene_select_field_on_event(event, ibtnfuzzer_state); - break; - case SceneAttack: - ibtnfuzzer_scene_run_attack_on_event(event, ibtnfuzzer_state); - break; - case SceneLoadCustomUids: - ibtnfuzzer_scene_load_custom_uids_on_event(event, ibtnfuzzer_state); - break; - } - - } else if(event.evt_type == EventTypeTick) { - //Handle event tick - if(ibtnfuzzer_state->current_scene != ibtnfuzzer_state->previous_scene) { - // Trigger Exit Scene - switch(ibtnfuzzer_state->previous_scene) { - case SceneEntryPoint: - ibtnfuzzer_scene_entrypoint_on_exit(ibtnfuzzer_state); - break; - case SceneSelectFile: - ibtnfuzzer_scene_load_file_on_exit(ibtnfuzzer_state); - break; - case SceneSelectField: - ibtnfuzzer_scene_select_field_on_exit(ibtnfuzzer_state); - break; - case SceneAttack: - ibtnfuzzer_scene_run_attack_on_exit(ibtnfuzzer_state); - break; - case SceneLoadCustomUids: - ibtnfuzzer_scene_load_custom_uids_on_exit(ibtnfuzzer_state); - break; - case NoneScene: - break; - } - - // Trigger Entry Scene - switch(ibtnfuzzer_state->current_scene) { - case NoneScene: - case SceneEntryPoint: - ibtnfuzzer_scene_entrypoint_on_enter(ibtnfuzzer_state); - break; - case SceneSelectFile: - ibtnfuzzer_scene_load_file_on_enter(ibtnfuzzer_state); - break; - case SceneSelectField: - ibtnfuzzer_scene_select_field_on_enter(ibtnfuzzer_state); - break; - case SceneAttack: - ibtnfuzzer_scene_run_attack_on_enter(ibtnfuzzer_state); - break; - case SceneLoadCustomUids: - ibtnfuzzer_scene_load_custom_uids_on_enter(ibtnfuzzer_state); - break; - } - ibtnfuzzer_state->previous_scene = ibtnfuzzer_state->current_scene; - } - - // Trigger Tick Scene - switch(ibtnfuzzer_state->current_scene) { - case NoneScene: - case SceneEntryPoint: - ibtnfuzzer_scene_entrypoint_on_tick(ibtnfuzzer_state); - break; - case SceneSelectFile: - ibtnfuzzer_scene_load_file_on_tick(ibtnfuzzer_state); - break; - case SceneSelectField: - ibtnfuzzer_scene_select_field_on_tick(ibtnfuzzer_state); - break; - case SceneAttack: - ibtnfuzzer_scene_run_attack_on_tick(ibtnfuzzer_state); - break; - case SceneLoadCustomUids: - ibtnfuzzer_scene_load_custom_uids_on_tick(ibtnfuzzer_state); - break; - } - view_port_update(view_port); - } - } - //furi_mutex_release(ibtnfuzzer_state->mutex); - } - - // Cleanup - furi_timer_stop(timer); - furi_timer_free(timer); - - FURI_LOG_I(TAG, "Cleaning up"); - gui_remove_view_port(gui, view_port); - view_port_free(view_port); - furi_message_queue_free(event_queue); - furi_record_close(RECORD_GUI); - furi_record_close(RECORD_NOTIFICATION); - furi_mutex_free(ibtnfuzzer_state->mutex); - ibtnfuzzer_free(ibtnfuzzer_state); - - return 0; -} diff --git a/applications/external/ibtn_fuzzer/ibtnfuzzer.h b/applications/external/ibtn_fuzzer/ibtnfuzzer.h deleted file mode 100644 index 7a9e2b537a..0000000000 --- a/applications/external/ibtn_fuzzer/ibtnfuzzer.h +++ /dev/null @@ -1,94 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include - -#include -#include - -#define TAG "iBtnFuzzer" - -typedef enum { - iBtnFuzzerAttackDefaultValues, - iBtnFuzzerAttackLoadFile, - iBtnFuzzerAttackLoadFileCustomUids, -} iBtnFuzzerAttacks; - -typedef enum { - DS1990, - Metakom, - Cyfral, -} iBtnFuzzerProtos; - -typedef enum { - NoneScene, - SceneEntryPoint, - SceneSelectFile, - SceneSelectField, - SceneAttack, - SceneLoadCustomUids, -} iBtnFuzzerScene; - -typedef enum { - EventTypeTick, - EventTypeKey, -} EventType; - -typedef struct { - EventType evt_type; - InputKey key; - InputType input_type; -} iBtnFuzzerEvent; - -// STRUCTS -typedef struct { - FuriMutex* mutex; - bool is_running; - bool is_attacking; - iBtnFuzzerScene current_scene; - iBtnFuzzerScene previous_scene; - NotificationApp* notify; - u_int8_t menu_index; - u_int8_t menu_proto_index; - - FuriString* data_str; - uint8_t data[8]; - uint8_t payload[8]; - uint8_t attack_step; - iBtnFuzzerAttacks attack; - iBtnFuzzerProtos proto; - FuriString* attack_name; - FuriString* proto_name; - FuriString* main_menu_items[3]; - FuriString* main_menu_proto_items[3]; - - DialogsApp* dialogs; - FuriString* notification_msg; - uint8_t key_index; - iButtonWorker* worker; - iButtonKey* key; - iButtonProtocolId keytype; - iButtonProtocols* protocols; - bool workr_rund; - bool enter_rerun; - bool attack_stop_called; - - uint8_t time_between_cards; - - // Used for custom dictionnary - Stream* uids_stream; -} iBtnFuzzerState; \ No newline at end of file diff --git a/applications/external/ibtn_fuzzer/images/ibutt_10px.png b/applications/external/ibtn_fuzzer/images/ibutt_10px.png deleted file mode 100644 index 2fdaf123a6..0000000000 Binary files a/applications/external/ibtn_fuzzer/images/ibutt_10px.png and /dev/null differ diff --git a/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_entrypoint.c b/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_entrypoint.c deleted file mode 100644 index 1dd239c3bb..0000000000 --- a/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_entrypoint.c +++ /dev/null @@ -1,201 +0,0 @@ -#include "ibtnfuzzer_scene_entrypoint.h" - -void ibtnfuzzer_scene_entrypoint_menu_callback( - iBtnFuzzerState* context, - uint32_t index, - uint32_t proto_index) { - switch(index) { - case iBtnFuzzerAttackDefaultValues: - context->attack = iBtnFuzzerAttackDefaultValues; - context->current_scene = SceneAttack; - furi_string_set(context->attack_name, "Default Values"); - break; - case iBtnFuzzerAttackLoadFile: - context->attack = iBtnFuzzerAttackLoadFile; - context->current_scene = SceneSelectFile; - furi_string_set(context->attack_name, "Load File"); - break; - case iBtnFuzzerAttackLoadFileCustomUids: - context->attack = iBtnFuzzerAttackLoadFileCustomUids; - context->current_scene = SceneLoadCustomUids; - furi_string_set(context->attack_name, "Load Custom UIDs"); - break; - default: - break; - } - - switch(proto_index) { - case DS1990: - context->proto = DS1990; - furi_string_set(context->proto_name, "DS1990"); - break; - case Metakom: - context->proto = Metakom; - furi_string_set(context->proto_name, "Metakom"); - break; - case Cyfral: - context->proto = Cyfral; - furi_string_set(context->proto_name, "Cyfral"); - break; - default: - break; - } -} - -void ibtnfuzzer_scene_entrypoint_on_enter(iBtnFuzzerState* context) { - // Clear the previous payload - context->payload[0] = 0x00; - context->payload[1] = 0x00; - context->payload[2] = 0x00; - context->payload[3] = 0x00; - context->payload[4] = 0x00; - context->payload[5] = 0x00; - context->payload[6] = 0x00; - context->payload[7] = 0x00; - - context->menu_index = 0; - /*for(uint32_t i = 0; i < 4; i++) { - menu_items[i] = furi_string_alloc(); - }*/ - - context->menu_proto_index = 0; - /*for(uint32_t i = 0; i < 4; i++) { - menu_proto_items[i] = furi_string_alloc(); - }*/ -} - -void ibtnfuzzer_scene_entrypoint_on_exit(iBtnFuzzerState* context) { - context->enter_rerun = false; -} - -void ibtnfuzzer_scene_entrypoint_on_tick(iBtnFuzzerState* context) { - UNUSED(context); -} - -void ibtnfuzzer_scene_entrypoint_on_event(iBtnFuzzerEvent event, iBtnFuzzerState* context) { - if(event.evt_type == EventTypeKey) { - if(event.input_type == InputTypeShort) { - switch(event.key) { - case InputKeyDown: - if(context->menu_index < iBtnFuzzerAttackLoadFileCustomUids) { - context->menu_index++; - } - break; - case InputKeyUp: - if(context->menu_index > iBtnFuzzerAttackDefaultValues) { - context->menu_index--; - } - break; - case InputKeyLeft: - if(context->menu_proto_index > DS1990) { - context->menu_proto_index--; - } else if(context->menu_proto_index == DS1990) { - context->menu_proto_index = Cyfral; - } - break; - case InputKeyRight: - if(context->menu_proto_index < Cyfral) { - context->menu_proto_index++; - } else if(context->menu_proto_index == Cyfral) { - context->menu_proto_index = DS1990; - } - break; - case InputKeyOk: - ibtnfuzzer_scene_entrypoint_menu_callback( - context, context->menu_index, context->menu_proto_index); - break; - case InputKeyBack: - context->is_running = false; - break; - default: - break; - } - } - } -} - -void ibtnfuzzer_scene_entrypoint_on_draw(Canvas* canvas, iBtnFuzzerState* context) { - if(!context->enter_rerun) { - ibtnfuzzer_scene_entrypoint_on_enter(context); - context->enter_rerun = true; - } - - canvas_clear(canvas); - canvas_set_color(canvas, ColorBlack); - - if(context->main_menu_items != NULL) { - if(context->main_menu_items[context->menu_index] != NULL) { - if(context->menu_index > iBtnFuzzerAttackDefaultValues) { - canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned( - canvas, - 64, - 24, - AlignCenter, - AlignTop, - furi_string_get_cstr(context->main_menu_items[context->menu_index - 1])); - } - - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned( - canvas, - 64, - 36, - AlignCenter, - AlignTop, - furi_string_get_cstr(context->main_menu_items[context->menu_index])); - - if(context->menu_index < iBtnFuzzerAttackLoadFileCustomUids) { - canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned( - canvas, - 64, - 48, - AlignCenter, - AlignTop, - furi_string_get_cstr(context->main_menu_items[context->menu_index + 1])); - } - - if(context->menu_proto_index > DS1990) { - canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned( - canvas, - 64, - -12, - AlignCenter, - AlignTop, - furi_string_get_cstr( - context->main_menu_proto_items[context->menu_proto_index - 1])); - } - - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas, 27, 4, AlignCenter, AlignTop, "<"); - - canvas_set_font(canvas, FontPrimary); - if(context->main_menu_proto_items[context->menu_proto_index] != NULL) { - canvas_draw_str_aligned( - canvas, - 64, - 4, - AlignCenter, - AlignTop, - furi_string_get_cstr( - context->main_menu_proto_items[context->menu_proto_index])); - } - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas, 101, 4, AlignCenter, AlignTop, ">"); - - if(context->menu_proto_index < Cyfral) { - canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned( - canvas, - 64, - -12, - AlignCenter, - AlignTop, - furi_string_get_cstr( - context->main_menu_proto_items[context->menu_proto_index + 1])); - } - } - } -} \ No newline at end of file diff --git a/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_entrypoint.h b/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_entrypoint.h deleted file mode 100644 index b77aec369f..0000000000 --- a/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_entrypoint.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once -#include "../ibtnfuzzer.h" - -void ibtnfuzzer_scene_entrypoint_on_enter(iBtnFuzzerState* context); -void ibtnfuzzer_scene_entrypoint_on_exit(iBtnFuzzerState* context); -void ibtnfuzzer_scene_entrypoint_on_tick(iBtnFuzzerState* context); -void ibtnfuzzer_scene_entrypoint_on_event(iBtnFuzzerEvent event, iBtnFuzzerState* context); -void ibtnfuzzer_scene_entrypoint_on_draw(Canvas* canvas, iBtnFuzzerState* context); \ No newline at end of file diff --git a/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_custom_uids.c b/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_custom_uids.c deleted file mode 100644 index 07199ab463..0000000000 --- a/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_custom_uids.c +++ /dev/null @@ -1,86 +0,0 @@ -#include "ibtnfuzzer_scene_load_custom_uids.h" -#include "ibtnfuzzer_scene_run_attack.h" -#include "ibtnfuzzer_scene_entrypoint.h" - -#define IBTNFUZZER_UIDS_EXTENSION ".txt" -#define IBTNFUZZER_APP_PATH_FOLDER "/ext/ibtnfuzzer" - -bool ibtnfuzzer_load_uids(iBtnFuzzerState* context, const char* file_path) { - bool result = false; - Storage* storage = furi_record_open(RECORD_STORAGE); - context->uids_stream = buffered_file_stream_alloc(storage); - result = - buffered_file_stream_open(context->uids_stream, file_path, FSAM_READ, FSOM_OPEN_EXISTING); - // Close if loading fails - if(!result) { - buffered_file_stream_close(context->uids_stream); - return false; - } - return result; -} - -bool ibtnfuzzer_load_custom_uids_from_file(iBtnFuzzerState* context) { - // Input events and views are managed by file_select - FuriString* uid_path; - uid_path = furi_string_alloc(); - furi_string_set(uid_path, IBTNFUZZER_APP_PATH_FOLDER); - - DialogsFileBrowserOptions browser_options; - dialog_file_browser_set_basic_options( - &browser_options, IBTNFUZZER_UIDS_EXTENSION, &I_ibutt_10px); - browser_options.base_path = IBTNFUZZER_APP_PATH_FOLDER; - browser_options.hide_ext = false; - - bool res = dialog_file_browser_show(context->dialogs, uid_path, uid_path, &browser_options); - - if(res) { - res = ibtnfuzzer_load_uids(context, furi_string_get_cstr(uid_path)); - } - - furi_string_free(uid_path); - - return res; -} - -void ibtnfuzzer_scene_load_custom_uids_on_enter(iBtnFuzzerState* context) { - if(ibtnfuzzer_load_custom_uids_from_file(context)) { - // Force context loading - ibtnfuzzer_scene_run_attack_on_enter(context); - context->current_scene = SceneAttack; - } else { - ibtnfuzzer_scene_entrypoint_on_enter(context); - context->current_scene = SceneEntryPoint; - } -} - -void ibtnfuzzer_scene_load_custom_uids_on_exit(iBtnFuzzerState* context) { - UNUSED(context); -} - -void ibtnfuzzer_scene_load_custom_uids_on_tick(iBtnFuzzerState* context) { - UNUSED(context); -} - -void ibtnfuzzer_scene_load_custom_uids_on_event(iBtnFuzzerEvent event, iBtnFuzzerState* context) { - if(event.evt_type == EventTypeKey) { - if(event.input_type == InputTypeShort) { - switch(event.key) { - case InputKeyDown: - case InputKeyUp: - case InputKeyLeft: - case InputKeyRight: - case InputKeyOk: - case InputKeyBack: - context->current_scene = SceneEntryPoint; - break; - default: - break; - } - } - } -} - -void ibtnfuzzer_scene_load_custom_uids_on_draw(Canvas* canvas, iBtnFuzzerState* context) { - UNUSED(context); - UNUSED(canvas); -} diff --git a/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_custom_uids.h b/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_custom_uids.h deleted file mode 100644 index bb51c70794..0000000000 --- a/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_custom_uids.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once -#include "../ibtnfuzzer.h" - -void ibtnfuzzer_scene_load_custom_uids_on_enter(iBtnFuzzerState* context); -void ibtnfuzzer_scene_load_custom_uids_on_exit(iBtnFuzzerState* context); -void ibtnfuzzer_scene_load_custom_uids_on_tick(iBtnFuzzerState* context); -void ibtnfuzzer_scene_load_custom_uids_on_event(iBtnFuzzerEvent event, iBtnFuzzerState* context); -void ibtnfuzzer_scene_load_custom_uids_on_draw(Canvas* canvas, iBtnFuzzerState* context); -bool ibtnfuzzer_load_custom_uids_from_file(iBtnFuzzerState* context); \ No newline at end of file diff --git a/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_file.c b/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_file.c deleted file mode 100644 index 47d5122abf..0000000000 --- a/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_file.c +++ /dev/null @@ -1,180 +0,0 @@ -#include "ibtnfuzzer_scene_load_file.h" -#include "ibtnfuzzer_scene_entrypoint.h" - -#define IBUTTON_FUZZER_APP_EXTENSION ".ibtn" -#define IBUTTON_FUZZER_APP_PATH_FOLDER "/ext/ibutton" - -bool ibtnfuzzer_load(iBtnFuzzerState* context, const char* file_path) { - bool result = false; - Storage* storage = furi_record_open(RECORD_STORAGE); - FlipperFormat* fff_data_file = flipper_format_file_alloc(storage); - FuriString* temp_str; - temp_str = furi_string_alloc(); - do { - if(!flipper_format_file_open_existing(fff_data_file, file_path)) { - FURI_LOG_E(TAG, "Error open file %s", file_path); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Error open file"); - break; - } - - // FileType - if(!flipper_format_read_string(fff_data_file, "Filetype", temp_str)) { - FURI_LOG_E(TAG, "Missing or incorrect Filetype"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Missing or incorrect Filetypes"); - break; - } else { - FURI_LOG_I(TAG, "Filetype: %s", furi_string_get_cstr(temp_str)); - } - - // Key type - if(!flipper_format_read_string(fff_data_file, "Key type", temp_str)) { - FURI_LOG_E(TAG, "Missing or incorrect Key type"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Missing or incorrect Key type"); - break; - } else { - FURI_LOG_I(TAG, "Key type: %s", furi_string_get_cstr(temp_str)); - - if(context->proto == DS1990) { - if(strcmp(furi_string_get_cstr(temp_str), "Dallas") != 0) { - FURI_LOG_E(TAG, "Unsupported Key type"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Unsupported Key type"); - break; - } - } else if(context->proto == Cyfral) { - if(strcmp(furi_string_get_cstr(temp_str), "Cyfral") != 0) { - FURI_LOG_E(TAG, "Unsupported Key type"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Unsupported Key type"); - break; - } - } else { - if(strcmp(furi_string_get_cstr(temp_str), "Metakom") != 0) { - FURI_LOG_E(TAG, "Unsupported Key type"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Unsupported Key type"); - break; - } - } - } - - // Data - if(!flipper_format_read_string(fff_data_file, "Data", context->data_str)) { - FURI_LOG_E(TAG, "Missing or incorrect Data"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Missing or incorrect Key"); - break; - } else { - FURI_LOG_I(TAG, "Key: %s", furi_string_get_cstr(context->data_str)); - - if(context->proto == DS1990) { - if(furi_string_size(context->data_str) != 23) { - FURI_LOG_E(TAG, "Incorrect Key length"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Incorrect Key length"); - break; - } - } else if(context->proto == Cyfral) { - if(furi_string_size(context->data_str) != 5) { - FURI_LOG_E(TAG, "Incorrect Key length"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Incorrect Key length"); - break; - } - } else { - if(furi_string_size(context->data_str) != 11) { - FURI_LOG_E(TAG, "Incorrect Key length"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Incorrect Key length"); - break; - } - } - - // String to uint8_t - for(uint8_t i = 0; i < 8; i++) { - char temp_str2[3]; - temp_str2[0] = furi_string_get_cstr(context->data_str)[i * 3]; - temp_str2[1] = furi_string_get_cstr(context->data_str)[i * 3 + 1]; - temp_str2[2] = '\0'; - context->data[i] = (uint8_t)strtol(temp_str2, NULL, 16); - } - } - - result = true; - } while(0); - furi_string_free(temp_str); - flipper_format_free(fff_data_file); - if(result) { - FURI_LOG_I(TAG, "Loaded successfully"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Source loaded."); - } - return result; -} - -void ibtnfuzzer_scene_load_file_on_enter(iBtnFuzzerState* context) { - if(ibtnfuzzer_load_protocol_from_file(context)) { - context->current_scene = SceneSelectField; - } else { - ibtnfuzzer_scene_entrypoint_on_enter(context); - context->current_scene = SceneEntryPoint; - } -} - -void ibtnfuzzer_scene_load_file_on_exit(iBtnFuzzerState* context) { - UNUSED(context); -} - -void ibtnfuzzer_scene_load_file_on_tick(iBtnFuzzerState* context) { - UNUSED(context); -} - -void ibtnfuzzer_scene_load_file_on_event(iBtnFuzzerEvent event, iBtnFuzzerState* context) { - if(event.evt_type == EventTypeKey) { - if(event.input_type == InputTypeShort) { - switch(event.key) { - case InputKeyDown: - case InputKeyUp: - case InputKeyLeft: - case InputKeyRight: - case InputKeyOk: - case InputKeyBack: - context->current_scene = SceneEntryPoint; - break; - default: - break; - } - } - } -} - -void ibtnfuzzer_scene_load_file_on_draw(Canvas* canvas, iBtnFuzzerState* context) { - UNUSED(context); - UNUSED(canvas); -} - -bool ibtnfuzzer_load_protocol_from_file(iBtnFuzzerState* context) { - FuriString* user_file_path; - user_file_path = furi_string_alloc(); - furi_string_set(user_file_path, IBUTTON_FUZZER_APP_PATH_FOLDER); - - DialogsFileBrowserOptions browser_options; - dialog_file_browser_set_basic_options( - &browser_options, IBUTTON_FUZZER_APP_EXTENSION, &I_ibutt_10px); - browser_options.base_path = IBUTTON_FUZZER_APP_PATH_FOLDER; - - // Input events and views are managed by file_select - bool res = dialog_file_browser_show( - context->dialogs, user_file_path, user_file_path, &browser_options); - - if(res) { - res = ibtnfuzzer_load(context, furi_string_get_cstr(user_file_path)); - } - - furi_string_free(user_file_path); - - return res; -} \ No newline at end of file diff --git a/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_file.h b/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_file.h deleted file mode 100644 index 34031d08c5..0000000000 --- a/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_file.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once -#include "../ibtnfuzzer.h" - -void ibtnfuzzer_scene_load_file_on_enter(iBtnFuzzerState* context); -void ibtnfuzzer_scene_load_file_on_exit(iBtnFuzzerState* context); -void ibtnfuzzer_scene_load_file_on_tick(iBtnFuzzerState* context); -void ibtnfuzzer_scene_load_file_on_event(iBtnFuzzerEvent event, iBtnFuzzerState* context); -void ibtnfuzzer_scene_load_file_on_draw(Canvas* canvas, iBtnFuzzerState* context); -bool ibtnfuzzer_load_protocol_from_file(iBtnFuzzerState* context); \ No newline at end of file diff --git a/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.c b/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.c deleted file mode 100644 index 53f2f694f5..0000000000 --- a/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.c +++ /dev/null @@ -1,501 +0,0 @@ -#include "ibtnfuzzer_scene_run_attack.h" -#include - -uint8_t counter = 0; - -uint8_t id_list_ds1990[18][8] = { - {0x01, 0xBE, 0x40, 0x11, 0x5A, 0x36, 0x00, 0xE1}, //– код универсального ключа, для Vizit - {0x01, 0xBE, 0x40, 0x11, 0x5A, 0x56, 0x00, 0xBB}, //- проверен работает - {0x01, 0xBE, 0x40, 0x11, 0x00, 0x00, 0x00, 0x77}, //- проверен работает - {0x01, 0xBE, 0x40, 0x11, 0x0A, 0x00, 0x00, 0x1D}, //- проверен работает Визит иногда КЕЙМАНЫ - {0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x2F}, //- проверен(метаком, цифрал, ВИЗИТ). - {0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x9B}, //- проверен Визит, Метакомы, КОНДОР - {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x14}, //???-Открываает 98% Метаком и некоторые Цифрал - {0x01, 0x00, 0x00, 0x00, 0x00, 0x90, 0x19, 0xFF}, //???-Отлично работает на старых домофонах - {0x01, 0x6F, 0x2E, 0x88, 0x8A, 0x00, 0x00, 0x4D}, //???-Открывать что-то должен - {0x01, 0x53, 0xD4, 0xFE, 0x00, 0x00, 0x7E, 0x88}, //???-Cyfral, Metakom - {0x01, 0x53, 0xD4, 0xFE, 0x00, 0x00, 0x00, 0x6F}, //???-домофоны Визит (Vizit) - до 99% - {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3D}, //???-домофоны Cyfral CCD-20 - до 70% - {0x01, 0x00, 0xBE, 0x11, 0xAA, 0x00, 0x00, 0xFB}, //???-домофоны Кейман (KEYMAN) - {0x01, 0x76, 0xB8, 0x2E, 0x0F, 0x00, 0x00, 0x5C}, //???-домофоны Форвард - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // Null bytes - {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x14}, // Only FF - {0x01, 0x78, 0x00, 0x48, 0xFD, 0xFF, 0xFF, 0xD1}, // StarNew Uni5 - {0x01, 0xA9, 0xE4, 0x3C, 0x09, 0x00, 0x00, 0xE6}, // Eltis Uni -}; - -uint8_t id_list_metakom[17][4] = { - {0x00, 0x00, 0x00, 0x00}, // Null bytes - {0xFF, 0xFF, 0xFF, 0xFF}, // Only FF - {0x11, 0x11, 0x11, 0x11}, // Only 11 - {0x22, 0x22, 0x22, 0x22}, // Only 22 - {0x33, 0x33, 0x33, 0x33}, // Only 33 - {0x44, 0x44, 0x44, 0x44}, // Only 44 - {0x55, 0x55, 0x55, 0x55}, // Only 55 - {0x66, 0x66, 0x66, 0x66}, // Only 66 - {0x77, 0x77, 0x77, 0x77}, // Only 77 - {0x88, 0x88, 0x88, 0x88}, // Only 88 - {0x99, 0x99, 0x99, 0x99}, // Only 99 - {0x12, 0x34, 0x56, 0x78}, // Incremental UID - {0x9A, 0x78, 0x56, 0x34}, // Decremental UID - {0x04, 0xd0, 0x9b, 0x0d}, // ?? - {0x34, 0x00, 0x29, 0x3d}, // ?? - {0x04, 0xdf, 0x00, 0x00}, // ?? - {0xCA, 0xCA, 0xCA, 0xCA}, // ?? -}; - -uint8_t id_list_cyfral[16][2] = { - {0x00, 0x00}, // Null bytes - {0xFF, 0xFF}, // Only FF - {0x11, 0x11}, // Only 11 - {0x22, 0x22}, // Only 22 - {0x33, 0x33}, // Only 33 - {0x44, 0x44}, // Only 44 - {0x55, 0x55}, // Only 55 - {0x66, 0x66}, // Only 66 - {0x77, 0x77}, // Only 77 - {0x88, 0x88}, // Only 88 - {0x99, 0x99}, // Only 99 - {0x12, 0x34}, // Incremental UID - {0x56, 0x34}, // Decremental UID - {0xCA, 0xCA}, // ?? - {0x8E, 0xC9}, // Elevator code - {0x6A, 0x50}, // VERY fresh code from smartkey -}; - -void ibtnfuzzer_scene_run_attack_on_enter(iBtnFuzzerState* context) { - context->time_between_cards = 8; - context->attack_step = 0; - context->attack_stop_called = false; - context->protocols = ibutton_protocols_alloc(); - context->key = ibutton_key_alloc(ibutton_protocols_get_max_data_size(context->protocols)); - context->worker = ibutton_worker_alloc(context->protocols); - if(context->proto == Metakom) { - context->keytype = ibutton_protocols_get_id_by_name(context->protocols, "Metakom"); - } else if(context->proto == Cyfral) { - context->keytype = ibutton_protocols_get_id_by_name(context->protocols, "Cyfral"); - } else { - context->keytype = ibutton_protocols_get_id_by_name(context->protocols, "DS1990"); - } - context->workr_rund = false; -} - -void ibtnfuzzer_scene_run_attack_on_exit(iBtnFuzzerState* context) { - if(context->workr_rund) { - ibutton_worker_stop(context->worker); - ibutton_worker_stop_thread(context->worker); - context->workr_rund = false; - } - ibutton_key_free(context->key); - ibutton_worker_free(context->worker); - ibutton_protocols_free(context->protocols); - notification_message(context->notify, &sequence_blink_stop); -} - -void ibtnfuzzer_scene_run_attack_on_tick(iBtnFuzzerState* context) { - if(context->is_attacking) { - if(1 == counter) { - ibutton_worker_start_thread(context->worker); - ibutton_key_set_protocol_id(context->key, context->keytype); - iButtonEditableData data; - ibutton_protocols_get_editable_data(context->protocols, context->key, &data); - data.size = sizeof(context->payload); - for(size_t i = 0; i < data.size; i++) { - data.ptr[i] = context->payload[i]; - } - - ibutton_worker_emulate_start(context->worker, context->key); - context->workr_rund = true; - } else if(0 == counter) { - if(context->workr_rund) { - ibutton_worker_stop(context->worker); - ibutton_worker_stop_thread(context->worker); - context->workr_rund = false; - furi_delay_ms(500); - } - switch(context->attack) { - case iBtnFuzzerAttackDefaultValues: - if(context->proto == DS1990) { - context->payload[0] = id_list_ds1990[context->attack_step][0]; - context->payload[1] = id_list_ds1990[context->attack_step][1]; - context->payload[2] = id_list_ds1990[context->attack_step][2]; - context->payload[3] = id_list_ds1990[context->attack_step][3]; - context->payload[4] = id_list_ds1990[context->attack_step][4]; - context->payload[5] = id_list_ds1990[context->attack_step][5]; - context->payload[6] = id_list_ds1990[context->attack_step][6]; - context->payload[7] = id_list_ds1990[context->attack_step][7]; - - if(context->attack_step == 17) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - } else { - context->attack_step++; - } - break; - } else if(context->proto == Metakom) { - context->payload[0] = id_list_metakom[context->attack_step][0]; - context->payload[1] = id_list_metakom[context->attack_step][1]; - context->payload[2] = id_list_metakom[context->attack_step][2]; - context->payload[3] = id_list_metakom[context->attack_step][3]; - - if(context->attack_step == 16) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - } else { - context->attack_step++; - } - break; - } else { - context->payload[0] = id_list_cyfral[context->attack_step][0]; - context->payload[1] = id_list_cyfral[context->attack_step][1]; - - if(context->attack_step == 15) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - } else { - context->attack_step++; - } - break; - } - case iBtnFuzzerAttackLoadFile: - if(context->proto == DS1990) { - context->payload[0] = context->data[0]; - context->payload[1] = context->data[1]; - context->payload[2] = context->data[2]; - context->payload[3] = context->data[3]; - context->payload[4] = context->data[4]; - context->payload[5] = context->data[5]; - context->payload[6] = context->data[6]; - context->payload[7] = context->data[7]; - - context->payload[context->key_index] = context->attack_step; - - if(context->attack_step == 255) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - break; - } else { - context->attack_step++; - } - break; - } else if(context->proto == Cyfral) { - context->payload[0] = context->data[0]; - context->payload[1] = context->data[1]; - - context->payload[context->key_index] = context->attack_step; - - if(context->attack_step == 255) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - break; - } else { - context->attack_step++; - } - break; - } else { - context->payload[0] = context->data[0]; - context->payload[1] = context->data[1]; - context->payload[2] = context->data[2]; - context->payload[3] = context->data[3]; - - context->payload[context->key_index] = context->attack_step; - - if(context->attack_step == 255) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - break; - } else { - context->attack_step++; - } - break; - } - - case iBtnFuzzerAttackLoadFileCustomUids: - if(context->proto == DS1990) { - bool end_of_list = false; - while(true) { - furi_string_reset(context->data_str); - if(!stream_read_line(context->uids_stream, context->data_str)) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - stream_rewind(context->uids_stream); - end_of_list = true; - break; - } - if(furi_string_get_char(context->data_str, 0) == '#') continue; - if(furi_string_size(context->data_str) != 17) break; - break; - } - if(end_of_list) break; - FURI_LOG_D(TAG, furi_string_get_cstr(context->data_str)); - if(furi_string_size(context->data_str) != 17) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_error); - break; - } - - // string is valid, parse it in context->payload - for(uint8_t i = 0; i < 8; i++) { - char temp_str[3]; - temp_str[0] = furi_string_get_cstr(context->data_str)[i * 2]; - temp_str[1] = furi_string_get_cstr(context->data_str)[i * 2 + 1]; - temp_str[2] = '\0'; - context->payload[i] = (uint8_t)strtol(temp_str, NULL, 16); - } - break; - } else if(context->proto == Cyfral) { - bool end_of_list = false; - while(true) { - furi_string_reset(context->data_str); - if(!stream_read_line(context->uids_stream, context->data_str)) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - stream_rewind(context->uids_stream); - end_of_list = true; - break; - } - if(furi_string_get_char(context->data_str, 0) == '#') continue; - if(furi_string_size(context->data_str) != 5) break; - break; - } - if(end_of_list) break; - FURI_LOG_D(TAG, furi_string_get_cstr(context->data_str)); - if(furi_string_size(context->data_str) != 5) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_error); - break; - } - - // string is valid, parse it in context->payload - for(uint8_t i = 0; i < 2; i++) { - char temp_str[3]; - temp_str[0] = furi_string_get_cstr(context->data_str)[i * 2]; - temp_str[1] = furi_string_get_cstr(context->data_str)[i * 2 + 1]; - temp_str[2] = '\0'; - context->payload[i] = (uint8_t)strtol(temp_str, NULL, 16); - } - break; - } else { - bool end_of_list = false; - while(true) { - furi_string_reset(context->data_str); - if(!stream_read_line(context->uids_stream, context->data_str)) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - stream_rewind(context->uids_stream); - end_of_list = true; - break; - } - if(furi_string_get_char(context->data_str, 0) == '#') continue; - if(furi_string_size(context->data_str) != 9) break; - break; - } - FURI_LOG_D(TAG, furi_string_get_cstr(context->data_str)); - if(end_of_list) break; - if(furi_string_size(context->data_str) != 9) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_error); - break; - } - - // string is valid, parse it in context->payload - for(uint8_t i = 0; i < 4; i++) { - char temp_str[3]; - temp_str[0] = furi_string_get_cstr(context->data_str)[i * 2]; - temp_str[1] = furi_string_get_cstr(context->data_str)[i * 2 + 1]; - temp_str[2] = '\0'; - context->payload[i] = (uint8_t)strtol(temp_str, NULL, 16); - } - break; - } - } - } - - if(counter > context->time_between_cards) { - counter = 0; - } else { - counter++; - } - } -} - -void ibtnfuzzer_scene_run_attack_on_event(iBtnFuzzerEvent event, iBtnFuzzerState* context) { - if(event.evt_type == EventTypeKey) { - if(event.input_type == InputTypeShort) { - switch(event.key) { - case InputKeyDown: - break; - case InputKeyUp: - break; - case InputKeyLeft: - if(!context->is_attacking) { - if(context->time_between_cards > 4) { - context->time_between_cards--; - } - } - break; - case InputKeyRight: - if(!context->is_attacking) { - if(context->time_between_cards < 80) { - context->time_between_cards++; - } - } - break; - case InputKeyOk: - counter = 0; - if(!context->is_attacking) { - notification_message(context->notify, &sequence_blink_start_blue); - context->is_attacking = true; - } else { - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - } - break; - case InputKeyBack: - context->is_attacking = false; - counter = 0; - - notification_message(context->notify, &sequence_blink_stop); - if(context->attack_stop_called) { - context->attack_stop_called = false; - context->attack_step = 0; - if(context->attack == iBtnFuzzerAttackLoadFileCustomUids) { - furi_string_reset(context->data_str); - stream_rewind(context->uids_stream); - buffered_file_stream_close(context->uids_stream); - } - - furi_string_reset(context->notification_msg); - context->current_scene = SceneEntryPoint; - } - - context->attack_stop_called = true; - break; - default: - break; - } - } - if(event.input_type == InputTypeLong) { - switch(event.key) { - case InputKeyLeft: - if(!context->is_attacking) { - if(context->time_between_cards > 4) { - if((context->time_between_cards - 10) > 4) { - context->time_between_cards -= 10; - } - } - } - break; - case InputKeyRight: - if(!context->is_attacking) { - if(context->time_between_cards < 80) { - context->time_between_cards += 10; - } - } - break; - default: - break; - } - } - } -} - -void ibtnfuzzer_scene_run_attack_on_draw(Canvas* canvas, iBtnFuzzerState* context) { - canvas_clear(canvas); - canvas_set_color(canvas, ColorBlack); - - // Frame - //canvas_draw_frame(canvas, 0, 0, 128, 64); - - // Title - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned( - canvas, 64, 2, AlignCenter, AlignTop, furi_string_get_cstr(context->attack_name)); - - char uid[25]; - char speed[16]; - if(context->proto == Metakom) { - snprintf( - uid, - sizeof(uid), - "%02X:%02X:%02X:%02X", - context->payload[0], - context->payload[1], - context->payload[2], - context->payload[3]); - } else if(context->proto == Cyfral) { - snprintf(uid, sizeof(uid), "%02X:%02X", context->payload[0], context->payload[1]); - } else { - snprintf( - uid, - sizeof(uid), - "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X", - context->payload[0], - context->payload[1], - context->payload[2], - context->payload[3], - context->payload[4], - context->payload[5], - context->payload[6], - context->payload[7]); - } - - canvas_draw_str_aligned(canvas, 64, 38, AlignCenter, AlignTop, uid); - - canvas_set_font(canvas, FontSecondary); - - canvas_draw_str_aligned( - canvas, 64, 26, AlignCenter, AlignTop, furi_string_get_cstr(context->proto_name)); - - snprintf(speed, sizeof(speed), "Time delay: %d", context->time_between_cards); - - //canvas_draw_str_aligned(canvas, 0, 22, AlignLeft, AlignTop, "Speed:"); - canvas_draw_str_aligned(canvas, 64, 14, AlignCenter, AlignTop, speed); - //char start_stop_msg[20]; - if(context->is_attacking) { - elements_button_center(canvas, "Stop"); - //snprintf(start_stop_msg, sizeof(start_stop_msg), " Press OK to stop "); - } else { - elements_button_center(canvas, "Start"); - elements_button_left(canvas, "TD -"); - elements_button_right(canvas, "+ TD"); - } - //canvas_draw_str_aligned(canvas, 64, 44, AlignCenter, AlignTop, start_stop_msg); -} diff --git a/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.h b/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.h deleted file mode 100644 index 9e44478f78..0000000000 --- a/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once -#include "../ibtnfuzzer.h" - -void ibtnfuzzer_scene_run_attack_on_enter(iBtnFuzzerState* context); -void ibtnfuzzer_scene_run_attack_on_exit(iBtnFuzzerState* context); -void ibtnfuzzer_scene_run_attack_on_tick(iBtnFuzzerState* context); -void ibtnfuzzer_scene_run_attack_on_event(iBtnFuzzerEvent event, iBtnFuzzerState* context); -void ibtnfuzzer_scene_run_attack_on_draw(Canvas* canvas, iBtnFuzzerState* context); diff --git a/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_select_field.c b/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_select_field.c deleted file mode 100644 index f3217f65ee..0000000000 --- a/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_select_field.c +++ /dev/null @@ -1,160 +0,0 @@ -#include "ibtnfuzzer_scene_select_field.h" - -void ibtnfuzzer_center_displayed_key(iBtnFuzzerState* context, uint8_t index) { - char key_cstr[25]; - uint8_t key_len = 25; - uint8_t str_index = (index * 3); - int data_len = sizeof(context->data) / sizeof(context->data[0]); - int key_index = 0; - - if(context->proto == DS1990) { - key_len = 25; - } - if(context->proto == Metakom) { - key_len = 13; - } - if(context->proto == Cyfral) { - key_len = 7; - } - - for(uint8_t i = 0; i < data_len; i++) { - if(context->data[i] < 9) { - key_index += - snprintf(&key_cstr[key_index], key_len - key_index, "0%X ", context->data[i]); - } else { - key_index += - snprintf(&key_cstr[key_index], key_len - key_index, "%X ", context->data[i]); - } - } - - char display_menu[17] = { - 'X', 'X', ' ', 'X', 'X', ' ', '<', 'X', 'X', '>', ' ', 'X', 'X', ' ', 'X', 'X', '\0'}; - - if(index > 1) { - display_menu[0] = key_cstr[str_index - 6]; - display_menu[1] = key_cstr[str_index - 5]; - } else { - display_menu[0] = ' '; - display_menu[1] = ' '; - } - - if(index > 0) { - display_menu[3] = key_cstr[str_index - 3]; - display_menu[4] = key_cstr[str_index - 2]; - } else { - display_menu[3] = ' '; - display_menu[4] = ' '; - } - - display_menu[7] = key_cstr[str_index]; - display_menu[8] = key_cstr[str_index + 1]; - - if((str_index + 4) <= (uint8_t)strlen(key_cstr)) { - display_menu[11] = key_cstr[str_index + 3]; - display_menu[12] = key_cstr[str_index + 4]; - } else { - display_menu[11] = ' '; - display_menu[12] = ' '; - } - - if((str_index + 8) <= (uint8_t)strlen(key_cstr)) { - display_menu[14] = key_cstr[str_index + 6]; - display_menu[15] = key_cstr[str_index + 7]; - } else { - display_menu[14] = ' '; - display_menu[15] = ' '; - } - - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, display_menu); -} - -void ibtnfuzzer_scene_select_field_on_enter(iBtnFuzzerState* context) { - furi_string_reset(context->notification_msg); -} - -void ibtnfuzzer_scene_select_field_on_exit(iBtnFuzzerState* context) { - UNUSED(context); -} - -void ibtnfuzzer_scene_select_field_on_tick(iBtnFuzzerState* context) { - UNUSED(context); -} - -void ibtnfuzzer_scene_select_field_on_event(iBtnFuzzerEvent event, iBtnFuzzerState* context) { - if(event.evt_type == EventTypeKey) { - if(event.input_type == InputTypeShort) { - const char* key_cstr = furi_string_get_cstr(context->data_str); - int data_len = sizeof(context->data) / sizeof(context->data[0]); - - // don't look, it's ugly but I'm a python dev so... - uint8_t nb_bytes = 0; - for(uint8_t i = 0; i < strlen(key_cstr); i++) { - if(' ' == key_cstr[i]) { - nb_bytes++; - } - } - - switch(event.key) { - case InputKeyDown: - for(uint8_t i = 0; i < data_len; i++) { - if(context->key_index == i) { - context->data[i] = (context->data[i] - 1); - } - } - break; - case InputKeyUp: - for(uint8_t i = 0; i < data_len; i++) { - if(context->key_index == i) { - context->data[i] = (context->data[i] + 1); - } - } - break; - case InputKeyLeft: - if(context->key_index > 0) { - context->key_index = context->key_index - 1; - } - break; - case InputKeyRight: - if(context->key_index < nb_bytes) { - context->key_index = context->key_index + 1; - } - break; - case InputKeyOk: - furi_string_reset(context->notification_msg); - context->current_scene = SceneAttack; - break; - case InputKeyBack: - context->key_index = 0; - furi_string_reset(context->notification_msg); - context->current_scene = SceneSelectFile; - break; - default: - break; - } - FURI_LOG_D(TAG, "Position: %d/%d", context->key_index, nb_bytes); - } - } -} - -void ibtnfuzzer_scene_select_field_on_draw(Canvas* canvas, iBtnFuzzerState* context) { - canvas_clear(canvas); - canvas_set_color(canvas, ColorBlack); - - // Frame - //canvas_draw_frame(canvas, 0, 0, 128, 64); - - canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned(canvas, 12, 5, AlignLeft, AlignTop, "Left and right: select byte"); - canvas_draw_str_aligned(canvas, 12, 15, AlignLeft, AlignTop, "Up and down: adjust byte"); - - char msg_index[18]; - canvas_set_font(canvas, FontPrimary); - snprintf(msg_index, sizeof(msg_index), "Field index : %d", context->key_index); - canvas_draw_str_aligned(canvas, 64, 30, AlignCenter, AlignTop, msg_index); - - ibtnfuzzer_center_displayed_key(context, context->key_index); - canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned( - canvas, 64, 45, AlignCenter, AlignTop, furi_string_get_cstr(context->notification_msg)); -} diff --git a/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_select_field.h b/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_select_field.h deleted file mode 100644 index b6c684c3b0..0000000000 --- a/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_select_field.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once -#include "../ibtnfuzzer.h" - -void ibtnfuzzer_scene_select_field_on_enter(iBtnFuzzerState* context); -void ibtnfuzzer_scene_select_field_on_exit(iBtnFuzzerState* context); -void ibtnfuzzer_scene_select_field_on_tick(iBtnFuzzerState* context); -void ibtnfuzzer_scene_select_field_on_event(iBtnFuzzerEvent event, iBtnFuzzerState* context); -void ibtnfuzzer_scene_select_field_on_draw(Canvas* canvas, iBtnFuzzerState* context); -void center_displayed_key(iBtnFuzzerState* context, uint8_t index); \ No newline at end of file diff --git a/applications/external/ifttt/application.fam b/applications/external/ifttt/application.fam index 6a3c4986e9..195c8a9eb0 100644 --- a/applications/external/ifttt/application.fam +++ b/applications/external/ifttt/application.fam @@ -1,6 +1,6 @@ App( - appid="ESP8266_IFTTT_Virtual_Button", - name="[ESP8266] IFTTT Virtual Button", + appid="ifttt", + name="[ESP8266] IFTTT Button", apptype=FlipperAppType.EXTERNAL, entry_point="ifttt_virtual_button_app", cdefines=["APP_IFTTT_VIRTUAL_BUTTON"], diff --git a/applications/external/ifttt/ifttt_virtual_button.c b/applications/external/ifttt/ifttt_virtual_button.c index ba1684daf1..01a02b0f36 100644 --- a/applications/external/ifttt/ifttt_virtual_button.c +++ b/applications/external/ifttt/ifttt_virtual_button.c @@ -1,8 +1,7 @@ #include "ifttt_virtual_button.h" -#define IFTTT_FOLDER "/ext/apps_data/ifttt" -#define IFTTT_CONFIG_FOLDER "/ext/apps_data/ifttt/config" -const char* CONFIG_FILE_PATH = "/ext/apps_data/ifttt/config/config.settings"; +#define IFTTT_CONFIG_FOLDER APP_DATA_PATH("config") +const char* CONFIG_FILE_PATH = APP_DATA_PATH("config/config.settings"); #define FLIPPERZERO_SERIAL_BAUD 115200 typedef enum ESerialCommand { ESerialCommand_Config } ESerialCommand; @@ -219,10 +218,7 @@ int32_t ifttt_virtual_button_app(void* p) { UNUSED(p); Storage* storage = furi_record_open(RECORD_STORAGE); - if(!storage_simply_mkdir(storage, IFTTT_FOLDER)) { - } - if(!storage_simply_mkdir(storage, IFTTT_CONFIG_FOLDER)) { - } + storage_simply_mkdir(storage, IFTTT_CONFIG_FOLDER); furi_record_close(RECORD_STORAGE); uint32_t first_scene = VirtualButtonAppSceneStart; diff --git a/applications/external/ir_remote/application.fam b/applications/external/ir_remote/application.fam index 1107ae45d6..5a98bd9047 100644 --- a/applications/external/ir_remote/application.fam +++ b/applications/external/ir_remote/application.fam @@ -10,5 +10,8 @@ App( ], fap_category="Tools", fap_icon="ir_10px.png", - fap_icon_assets="images", + fap_author="@Hong5489 & @friebel & @d4ve10", + fap_weburl="https://github.com/Hong5489/ir_remote", + fap_version="1.0", + fap_description="Bind any IR remote button to each button on flipper d-pad, provides another way to use flipper as IR remote.", ) diff --git a/applications/external/ir_remote/images/ButtonDown_7x4.png b/applications/external/ir_remote/images/ButtonDown_7x4.png deleted file mode 100644 index 2954bb6a67..0000000000 Binary files a/applications/external/ir_remote/images/ButtonDown_7x4.png and /dev/null differ diff --git a/applications/external/ir_remote/images/ButtonLeft_4x7.png b/applications/external/ir_remote/images/ButtonLeft_4x7.png deleted file mode 100644 index 0b4655d432..0000000000 Binary files a/applications/external/ir_remote/images/ButtonLeft_4x7.png and /dev/null differ diff --git a/applications/external/ir_remote/images/ButtonRight_4x7.png b/applications/external/ir_remote/images/ButtonRight_4x7.png deleted file mode 100644 index 8e1c74c1c0..0000000000 Binary files a/applications/external/ir_remote/images/ButtonRight_4x7.png and /dev/null differ diff --git a/applications/external/ir_remote/images/ButtonUp_7x4.png b/applications/external/ir_remote/images/ButtonUp_7x4.png deleted file mode 100644 index 1be79328b4..0000000000 Binary files a/applications/external/ir_remote/images/ButtonUp_7x4.png and /dev/null differ diff --git a/applications/external/ir_remote/images/Ok_btn_9x9.png b/applications/external/ir_remote/images/Ok_btn_9x9.png deleted file mode 100644 index 9a1539da20..0000000000 Binary files a/applications/external/ir_remote/images/Ok_btn_9x9.png and /dev/null differ diff --git a/applications/external/ir_remote/images/back_10px.png b/applications/external/ir_remote/images/back_10px.png deleted file mode 100644 index f9c615a99e..0000000000 Binary files a/applications/external/ir_remote/images/back_10px.png and /dev/null differ diff --git a/applications/external/ir_remote/images/sub1_10px.png b/applications/external/ir_remote/images/sub1_10px.png deleted file mode 100644 index 5a25fdf4ef..0000000000 Binary files a/applications/external/ir_remote/images/sub1_10px.png and /dev/null differ diff --git a/applications/external/ir_remote/infrared_remote.h b/applications/external/ir_remote/infrared_remote.h index 6eac193d3a..00ca2e1070 100644 --- a/applications/external/ir_remote/infrared_remote.h +++ b/applications/external/ir_remote/infrared_remote.h @@ -4,6 +4,8 @@ #include "infrared_remote_button.h" +#define IR_REMOTE_PATH EXT_PATH("infrared/remote") + typedef struct InfraredRemote InfraredRemote; InfraredRemote* infrared_remote_alloc(); diff --git a/applications/external/ir_remote/infrared_remote_app.c b/applications/external/ir_remote/infrared_remote_app.c index 7056fae896..5ced9e71f3 100644 --- a/applications/external/ir_remote/infrared_remote_app.c +++ b/applications/external/ir_remote/infrared_remote_app.c @@ -1,10 +1,12 @@ #include #include +#include + #include #include #include -#include +#include #include #include @@ -13,8 +15,7 @@ #include "infrared_signal.h" #include "infrared_remote.h" #include "infrared_remote_button.h" -#define TAG "IR_Remote" -#define MENU_BTN_TXT_X 36 +#define TAG "ir_remote" #include @@ -32,6 +33,7 @@ typedef struct { FuriString* left_hold_button; FuriString* right_hold_button; FuriString* ok_hold_button; + InfraredWorker* infrared_worker; } IRApp; // Screen is 128x64 px @@ -61,47 +63,17 @@ static void app_draw_callback(Canvas* canvas, void* ctx) { canvas_set_font(canvas, FontSecondary); canvas_draw_str_aligned( - canvas, - MENU_BTN_TXT_X, - 8, - AlignCenter, - AlignCenter, - furi_string_get_cstr(app->up_button)); + canvas, 32, 8, AlignCenter, AlignCenter, furi_string_get_cstr(app->up_button)); canvas_draw_str_aligned( - canvas, - MENU_BTN_TXT_X, - 18, - AlignCenter, - AlignCenter, - furi_string_get_cstr(app->down_button)); + canvas, 32, 18, AlignCenter, AlignCenter, furi_string_get_cstr(app->down_button)); canvas_draw_str_aligned( - canvas, - MENU_BTN_TXT_X, - 28, - AlignCenter, - AlignCenter, - furi_string_get_cstr(app->left_button)); + canvas, 32, 28, AlignCenter, AlignCenter, furi_string_get_cstr(app->left_button)); canvas_draw_str_aligned( - canvas, - MENU_BTN_TXT_X, - 38, - AlignCenter, - AlignCenter, - furi_string_get_cstr(app->right_button)); + canvas, 32, 38, AlignCenter, AlignCenter, furi_string_get_cstr(app->right_button)); canvas_draw_str_aligned( - canvas, - MENU_BTN_TXT_X, - 48, - AlignCenter, - AlignCenter, - furi_string_get_cstr(app->ok_button)); + canvas, 32, 48, AlignCenter, AlignCenter, furi_string_get_cstr(app->ok_button)); canvas_draw_str_aligned( - canvas, - MENU_BTN_TXT_X, - 58, - AlignCenter, - AlignCenter, - furi_string_get_cstr(app->back_button)); + canvas, 32, 58, AlignCenter, AlignCenter, furi_string_get_cstr(app->back_button)); canvas_draw_line(canvas, 0, 65, 64, 65); @@ -113,41 +85,21 @@ static void app_draw_callback(Canvas* canvas, void* ctx) { canvas_draw_icon(canvas, 0, 118, &I_back_10px); canvas_draw_str_aligned( - canvas, - MENU_BTN_TXT_X, - 73, - AlignCenter, - AlignCenter, - furi_string_get_cstr(app->up_hold_button)); + canvas, 32, 73, AlignCenter, AlignCenter, furi_string_get_cstr(app->up_hold_button)); canvas_draw_str_aligned( - canvas, - MENU_BTN_TXT_X, - 83, - AlignCenter, - AlignCenter, - furi_string_get_cstr(app->down_hold_button)); + canvas, 32, 83, AlignCenter, AlignCenter, furi_string_get_cstr(app->down_hold_button)); canvas_draw_str_aligned( - canvas, - MENU_BTN_TXT_X, - 93, - AlignCenter, - AlignCenter, - furi_string_get_cstr(app->left_hold_button)); + canvas, 32, 93, AlignCenter, AlignCenter, furi_string_get_cstr(app->left_hold_button)); canvas_draw_str_aligned( canvas, - MENU_BTN_TXT_X, + 32, 103, AlignCenter, AlignCenter, furi_string_get_cstr(app->right_hold_button)); canvas_draw_str_aligned( - canvas, - MENU_BTN_TXT_X, - 113, - AlignCenter, - AlignCenter, - furi_string_get_cstr(app->ok_hold_button)); - canvas_draw_str_aligned(canvas, MENU_BTN_TXT_X, 123, AlignCenter, AlignCenter, "Exit App"); + canvas, 32, 113, AlignCenter, AlignCenter, furi_string_get_cstr(app->ok_hold_button)); + canvas_draw_str_aligned(canvas, 32, 123, AlignCenter, AlignCenter, "Exit App"); } } @@ -158,10 +110,9 @@ static void app_input_callback(InputEvent* input_event, void* ctx) { furi_message_queue_put(event_queue, input_event, FuriWaitForever); } -int32_t infrared_remote_app(void* p) { - UNUSED(p); +int32_t infrared_remote_app(char* p) { FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); - DOLPHIN_DEED(DolphinDeedPluginStart); + dolphin_deed(DolphinDeedPluginStart); // App button string IRApp* app = malloc(sizeof(IRApp)); @@ -177,6 +128,7 @@ int32_t infrared_remote_app(void* p) { app->right_hold_button = furi_string_alloc(); app->ok_hold_button = furi_string_alloc(); app->view_port = view_port_alloc(); + app->infrared_worker = infrared_worker_alloc(); // Configure view port view_port_draw_callback_set(app->view_port, app_draw_callback, app); @@ -188,21 +140,26 @@ int32_t infrared_remote_app(void* p) { InputEvent event; + FuriString* map_file = furi_string_alloc(); Storage* storage = furi_record_open(RECORD_STORAGE); FlipperFormat* ff = flipper_format_file_alloc(storage); - - DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); - DialogsFileBrowserOptions browser_options; - dialog_file_browser_set_basic_options(&browser_options, ".txt", &I_sub1_10px); - FuriString* map_file = furi_string_alloc(); - furi_string_set(map_file, "/ext/infrared/remote"); - if(!storage_file_exists(storage, ANY_PATH("infrared/remote"))) { - storage_common_mkdir(storage, ANY_PATH("infrared/remote")); //Make Folder If dir not exist + if(!storage_file_exists(storage, IR_REMOTE_PATH)) { + storage_common_mkdir(storage, IR_REMOTE_PATH); //Make Folder If dir not exist } - bool res = dialog_file_browser_show(dialogs, map_file, map_file, &browser_options); - - furi_record_close(RECORD_DIALOGS); + bool res; + if(p && strlen(p)) { + furi_string_set(map_file, p); + res = true; + } else { + DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); + DialogsFileBrowserOptions browser_options; + dialog_file_browser_set_basic_options(&browser_options, ".txt", &I_sub1_10px); + browser_options.base_path = IR_REMOTE_PATH; + furi_string_set(map_file, IR_REMOTE_PATH); + res = dialog_file_browser_show(dialogs, map_file, map_file, &browser_options); + furi_record_close(RECORD_DIALOGS); + } // if user didn't choose anything, free everything and exit if(!res) { @@ -247,6 +204,9 @@ int32_t infrared_remote_app(void* p) { InfraredSignal* right_hold_signal = infrared_signal_alloc(); InfraredSignal* ok_hold_signal = infrared_signal_alloc(); + InfraredSignal* active_signal = NULL; + bool is_transmitting = false; + bool up_enabled = false; bool down_enabled = false; bool left_enabled = false; @@ -281,8 +241,6 @@ int32_t infrared_remote_app(void* p) { //set missing filenames to N/A //assign button signals size_t index = 0; - - flipper_format_rewind(ff); if(!flipper_format_read_string(ff, "UP", app->up_button)) { FURI_LOG_W(TAG, "Could not read UP string"); furi_string_set(app->up_button, "N/A"); @@ -297,7 +255,6 @@ int32_t infrared_remote_app(void* p) { } } - flipper_format_rewind(ff); if(!flipper_format_read_string(ff, "DOWN", app->down_button)) { FURI_LOG_W(TAG, "Could not read DOWN string"); furi_string_set(app->down_button, "N/A"); @@ -312,7 +269,6 @@ int32_t infrared_remote_app(void* p) { } } - flipper_format_rewind(ff); if(!flipper_format_read_string(ff, "LEFT", app->left_button)) { FURI_LOG_W(TAG, "Could not read LEFT string"); furi_string_set(app->left_button, "N/A"); @@ -327,7 +283,6 @@ int32_t infrared_remote_app(void* p) { } } - flipper_format_rewind(ff); if(!flipper_format_read_string(ff, "RIGHT", app->right_button)) { FURI_LOG_W(TAG, "Could not read RIGHT string"); furi_string_set(app->right_button, "N/A"); @@ -342,7 +297,6 @@ int32_t infrared_remote_app(void* p) { } } - flipper_format_rewind(ff); if(!flipper_format_read_string(ff, "OK", app->ok_button)) { FURI_LOG_W(TAG, "Could not read OK string"); furi_string_set(app->ok_button, "N/A"); @@ -357,7 +311,6 @@ int32_t infrared_remote_app(void* p) { } } - flipper_format_rewind(ff); if(!flipper_format_read_string(ff, "BACK", app->back_button)) { FURI_LOG_W(TAG, "Could not read BACK string"); furi_string_set(app->back_button, "N/A"); @@ -372,7 +325,6 @@ int32_t infrared_remote_app(void* p) { } } - flipper_format_rewind(ff); if(!flipper_format_read_string(ff, "UPHOLD", app->up_hold_button)) { FURI_LOG_W(TAG, "Could not read UPHOLD string"); furi_string_set(app->up_hold_button, "N/A"); @@ -387,7 +339,6 @@ int32_t infrared_remote_app(void* p) { } } - flipper_format_rewind(ff); if(!flipper_format_read_string(ff, "DOWNHOLD", app->down_hold_button)) { FURI_LOG_W(TAG, "Could not read DOWNHOLD string"); furi_string_set(app->down_hold_button, "N/A"); @@ -402,7 +353,6 @@ int32_t infrared_remote_app(void* p) { } } - flipper_format_rewind(ff); if(!flipper_format_read_string(ff, "LEFTHOLD", app->left_hold_button)) { FURI_LOG_W(TAG, "Could not read LEFTHOLD string"); furi_string_set(app->left_hold_button, "N/A"); @@ -417,7 +367,6 @@ int32_t infrared_remote_app(void* p) { } } - flipper_format_rewind(ff); if(!flipper_format_read_string(ff, "RIGHTHOLD", app->right_hold_button)) { FURI_LOG_W(TAG, "Could not read RIGHTHOLD string"); furi_string_set(app->right_hold_button, "N/A"); @@ -432,7 +381,6 @@ int32_t infrared_remote_app(void* p) { } } - flipper_format_rewind(ff); if(!flipper_format_read_string(ff, "OKHOLD", app->ok_hold_button)) { FURI_LOG_W(TAG, "Could not read OKHOLD string"); furi_string_set(app->ok_hold_button, "N/A"); @@ -480,43 +428,37 @@ int32_t infrared_remote_app(void* p) { switch(event.key) { case InputKeyUp: if(up_enabled) { - infrared_signal_transmit(up_signal); - notification_message(notification, &sequence_blink_start_magenta); + active_signal = up_signal; FURI_LOG_I(TAG, "up"); } break; case InputKeyDown: if(down_enabled) { - infrared_signal_transmit(down_signal); - notification_message(notification, &sequence_blink_start_magenta); + active_signal = down_signal; FURI_LOG_I(TAG, "down"); } break; case InputKeyRight: if(right_enabled) { - infrared_signal_transmit(right_signal); - notification_message(notification, &sequence_blink_start_magenta); + active_signal = right_signal; FURI_LOG_I(TAG, "right"); } break; case InputKeyLeft: if(left_enabled) { - infrared_signal_transmit(left_signal); - notification_message(notification, &sequence_blink_start_magenta); + active_signal = left_signal; FURI_LOG_I(TAG, "left"); } break; case InputKeyOk: if(ok_enabled) { - infrared_signal_transmit(ok_signal); - notification_message(notification, &sequence_blink_start_magenta); + active_signal = ok_signal; FURI_LOG_I(TAG, "ok"); } break; case InputKeyBack: if(back_enabled) { - infrared_signal_transmit(back_signal); - notification_message(notification, &sequence_blink_start_magenta); + active_signal = back_signal; FURI_LOG_I(TAG, "back"); } break; @@ -529,36 +471,31 @@ int32_t infrared_remote_app(void* p) { switch(event.key) { case InputKeyUp: if(up_hold_enabled) { - infrared_signal_transmit(up_hold_signal); - notification_message(notification, &sequence_blink_start_magenta); + active_signal = up_hold_signal; FURI_LOG_I(TAG, "up!"); } break; case InputKeyDown: if(down_hold_enabled) { - infrared_signal_transmit(down_hold_signal); - notification_message(notification, &sequence_blink_start_magenta); + active_signal = down_hold_signal; FURI_LOG_I(TAG, "down!"); } break; case InputKeyRight: if(right_hold_enabled) { - infrared_signal_transmit(right_hold_signal); - notification_message(notification, &sequence_blink_start_magenta); + active_signal = right_hold_signal; FURI_LOG_I(TAG, "right!"); } break; case InputKeyLeft: if(left_hold_enabled) { - infrared_signal_transmit(left_hold_signal); - notification_message(notification, &sequence_blink_start_magenta); + active_signal = left_hold_signal; FURI_LOG_I(TAG, "left!"); } break; case InputKeyOk: if(ok_hold_enabled) { - infrared_signal_transmit(ok_hold_signal); - notification_message(notification, &sequence_blink_start_magenta); + active_signal = ok_hold_signal; FURI_LOG_I(TAG, "ok!"); } break; @@ -566,8 +503,39 @@ int32_t infrared_remote_app(void* p) { running = false; break; } - } else if(event.type == InputTypeRelease) { + } else if(event.type == InputTypeRelease && is_transmitting) { notification_message(notification, &sequence_blink_stop); + infrared_worker_tx_stop(app->infrared_worker); + is_transmitting = false; + active_signal = NULL; + } + + if(active_signal != NULL && + (event.type == InputTypeShort || event.type == InputTypeLong)) { + if(is_transmitting) { + infrared_worker_tx_stop(app->infrared_worker); + } + + if(infrared_signal_is_raw(active_signal)) { + InfraredRawSignal* raw_signal = + infrared_signal_get_raw_signal(active_signal); + infrared_worker_set_raw_signal( + app->infrared_worker, + raw_signal->timings, + raw_signal->timings_size, + raw_signal->frequency, + raw_signal->duty_cycle); + } else { + InfraredMessage* message = infrared_signal_get_message(active_signal); + infrared_worker_set_decoded_signal(app->infrared_worker, message); + } + + infrared_worker_tx_set_get_signal_callback( + app->infrared_worker, infrared_worker_tx_get_signal_steady_callback, app); + + infrared_worker_tx_start(app->infrared_worker); + notification_message(notification, &sequence_blink_start_magenta); + is_transmitting = true; } } } @@ -586,6 +554,12 @@ int32_t infrared_remote_app(void* p) { furi_string_free(app->right_hold_button); furi_string_free(app->ok_hold_button); + if(is_transmitting) { + infrared_worker_tx_stop(app->infrared_worker); + notification_message(notification, &sequence_blink_stop); + } + infrared_worker_free(app->infrared_worker); + infrared_remote_free(remote); view_port_enabled_set(app->view_port, false); gui_remove_view_port(gui, app->view_port); diff --git a/applications/external/ir_scope/application.fam b/applications/external/ir_scope/application.fam index f99e14515d..f6fb6fbdc3 100644 --- a/applications/external/ir_scope/application.fam +++ b/applications/external/ir_scope/application.fam @@ -8,4 +8,7 @@ App( stack_size=2 * 1024, fap_icon="ir_scope.png", fap_category="Tools", + fap_author="@kallanreed", + fap_version="1.0", + fap_description="App allows to see incoming IR signals.", ) diff --git a/applications/external/jetpack_joyride/application.fam b/applications/external/jetpack_joyride/application.fam new file mode 100644 index 0000000000..1b98e11ce8 --- /dev/null +++ b/applications/external/jetpack_joyride/application.fam @@ -0,0 +1,15 @@ +# For details & more options, see documentation/AppManifests.md in firmware repo + +App( + appid="jetpack_joyride", + name="Jetpack Joyride", + apptype=FlipperAppType.EXTERNAL, + entry_point="jetpack_game_app", + cdefines=["APP_JETPACK_GAME"], + requires=["gui"], + stack_size=4 * 1024, + order=100, + fap_icon="icon.png", + fap_category="Games", + fap_icon_assets="assets", +) diff --git a/applications/external/jetpack_joyride/assets/air_vent.png b/applications/external/jetpack_joyride/assets/air_vent.png new file mode 100644 index 0000000000..a7fcf0b203 Binary files /dev/null and b/applications/external/jetpack_joyride/assets/air_vent.png differ diff --git a/applications/external/jetpack_joyride/assets/alert/frame_01.png b/applications/external/jetpack_joyride/assets/alert/frame_01.png new file mode 100644 index 0000000000..ac4cca1b1f Binary files /dev/null and b/applications/external/jetpack_joyride/assets/alert/frame_01.png differ diff --git a/applications/external/jetpack_joyride/assets/alert/frame_02.png b/applications/external/jetpack_joyride/assets/alert/frame_02.png new file mode 100644 index 0000000000..c3955f090f Binary files /dev/null and b/applications/external/jetpack_joyride/assets/alert/frame_02.png differ diff --git a/applications/external/subghz_bruteforcer/images/Sub1ghz_14/frame_rate b/applications/external/jetpack_joyride/assets/alert/frame_rate similarity index 100% rename from applications/external/subghz_bruteforcer/images/Sub1ghz_14/frame_rate rename to applications/external/jetpack_joyride/assets/alert/frame_rate diff --git a/applications/external/jetpack_joyride/assets/barry/frame_01.png b/applications/external/jetpack_joyride/assets/barry/frame_01.png new file mode 100644 index 0000000000..8abdcaf615 Binary files /dev/null and b/applications/external/jetpack_joyride/assets/barry/frame_01.png differ diff --git a/applications/external/jetpack_joyride/assets/barry/frame_02.png b/applications/external/jetpack_joyride/assets/barry/frame_02.png new file mode 100644 index 0000000000..5a4587ac8f Binary files /dev/null and b/applications/external/jetpack_joyride/assets/barry/frame_02.png differ diff --git a/applications/external/jetpack_joyride/assets/barry/frame_03.png b/applications/external/jetpack_joyride/assets/barry/frame_03.png new file mode 100644 index 0000000000..d188aed3c6 Binary files /dev/null and b/applications/external/jetpack_joyride/assets/barry/frame_03.png differ diff --git a/applications/external/jetpack_joyride/assets/barry/frame_rate b/applications/external/jetpack_joyride/assets/barry/frame_rate new file mode 100644 index 0000000000..e440e5c842 --- /dev/null +++ b/applications/external/jetpack_joyride/assets/barry/frame_rate @@ -0,0 +1 @@ +3 \ No newline at end of file diff --git a/applications/external/jetpack_joyride/assets/barry_infill.png b/applications/external/jetpack_joyride/assets/barry_infill.png new file mode 100644 index 0000000000..9462801f02 Binary files /dev/null and b/applications/external/jetpack_joyride/assets/barry_infill.png differ diff --git a/applications/external/jetpack_joyride/assets/bg1.png b/applications/external/jetpack_joyride/assets/bg1.png new file mode 100644 index 0000000000..82d614e1cc Binary files /dev/null and b/applications/external/jetpack_joyride/assets/bg1.png differ diff --git a/applications/external/jetpack_joyride/assets/bg2.png b/applications/external/jetpack_joyride/assets/bg2.png new file mode 100644 index 0000000000..ec8590d3ad Binary files /dev/null and b/applications/external/jetpack_joyride/assets/bg2.png differ diff --git a/applications/external/jetpack_joyride/assets/bg3.png b/applications/external/jetpack_joyride/assets/bg3.png new file mode 100644 index 0000000000..ebb1dd66ba Binary files /dev/null and b/applications/external/jetpack_joyride/assets/bg3.png differ diff --git a/applications/external/jetpack_joyride/assets/coin.png b/applications/external/jetpack_joyride/assets/coin.png new file mode 100644 index 0000000000..a2b5a409e4 Binary files /dev/null and b/applications/external/jetpack_joyride/assets/coin.png differ diff --git a/applications/external/jetpack_joyride/assets/coin_infill.png b/applications/external/jetpack_joyride/assets/coin_infill.png new file mode 100644 index 0000000000..ab37874ff8 Binary files /dev/null and b/applications/external/jetpack_joyride/assets/coin_infill.png differ diff --git a/applications/external/jetpack_joyride/assets/dead_scientist.png b/applications/external/jetpack_joyride/assets/dead_scientist.png new file mode 100644 index 0000000000..cd7a9993a3 Binary files /dev/null and b/applications/external/jetpack_joyride/assets/dead_scientist.png differ diff --git a/applications/external/jetpack_joyride/assets/dead_scientist_infill.png b/applications/external/jetpack_joyride/assets/dead_scientist_infill.png new file mode 100644 index 0000000000..6f036fde2f Binary files /dev/null and b/applications/external/jetpack_joyride/assets/dead_scientist_infill.png differ diff --git a/applications/external/jetpack_joyride/assets/door.png b/applications/external/jetpack_joyride/assets/door.png new file mode 100644 index 0000000000..1ef8610546 Binary files /dev/null and b/applications/external/jetpack_joyride/assets/door.png differ diff --git a/applications/external/jetpack_joyride/assets/missile/frame_01.png b/applications/external/jetpack_joyride/assets/missile/frame_01.png new file mode 100644 index 0000000000..f0b62ed030 Binary files /dev/null and b/applications/external/jetpack_joyride/assets/missile/frame_01.png differ diff --git a/applications/external/jetpack_joyride/assets/missile/frame_rate b/applications/external/jetpack_joyride/assets/missile/frame_rate new file mode 100644 index 0000000000..e440e5c842 --- /dev/null +++ b/applications/external/jetpack_joyride/assets/missile/frame_rate @@ -0,0 +1 @@ +3 \ No newline at end of file diff --git a/applications/external/jetpack_joyride/assets/missile_infill.png b/applications/external/jetpack_joyride/assets/missile_infill.png new file mode 100644 index 0000000000..d15f5a88eb Binary files /dev/null and b/applications/external/jetpack_joyride/assets/missile_infill.png differ diff --git a/applications/external/jetpack_joyride/assets/pillar.png b/applications/external/jetpack_joyride/assets/pillar.png new file mode 100644 index 0000000000..61979b393a Binary files /dev/null and b/applications/external/jetpack_joyride/assets/pillar.png differ diff --git a/applications/external/jetpack_joyride/assets/scientist_left.png b/applications/external/jetpack_joyride/assets/scientist_left.png new file mode 100644 index 0000000000..a9e880b6b2 Binary files /dev/null and b/applications/external/jetpack_joyride/assets/scientist_left.png differ diff --git a/applications/external/jetpack_joyride/assets/scientist_left_infill.png b/applications/external/jetpack_joyride/assets/scientist_left_infill.png new file mode 100644 index 0000000000..4639cb957a Binary files /dev/null and b/applications/external/jetpack_joyride/assets/scientist_left_infill.png differ diff --git a/applications/external/jetpack_joyride/assets/scientist_right.png b/applications/external/jetpack_joyride/assets/scientist_right.png new file mode 100644 index 0000000000..dc40b560de Binary files /dev/null and b/applications/external/jetpack_joyride/assets/scientist_right.png differ diff --git a/applications/external/jetpack_joyride/assets/scientist_right_infill.png b/applications/external/jetpack_joyride/assets/scientist_right_infill.png new file mode 100644 index 0000000000..e4bc7def88 Binary files /dev/null and b/applications/external/jetpack_joyride/assets/scientist_right_infill.png differ diff --git a/applications/external/jetpack_joyride/icon.png b/applications/external/jetpack_joyride/icon.png new file mode 100644 index 0000000000..0d2d6cf42a Binary files /dev/null and b/applications/external/jetpack_joyride/icon.png differ diff --git a/applications/external/jetpack_joyride/includes/background_asset.c b/applications/external/jetpack_joyride/includes/background_asset.c new file mode 100644 index 0000000000..0a5ecdda91 --- /dev/null +++ b/applications/external/jetpack_joyride/includes/background_asset.c @@ -0,0 +1,81 @@ +#include + +#include "background_assets.h" + +static AssetProperties assetProperties[BG_ASSETS_MAX] = { + {.width = 27, .spawn_chance = 1, .x_offset = 24, .y_offset = 36, .sprite = &I_door}, + {.width = 12, .spawn_chance = 6, .x_offset = 33, .y_offset = 14, .sprite = &I_air_vent}}; + +void background_assets_tick(BackgroundAsset* const assets) { + // Move assets towards the player + for(int i = 0; i < BG_ASSETS_MAX; i++) { + if(assets[i].visible) { + assets[i].point.x -= 1; // move left by 2 units + if(assets[i].point.x <= + -assets[i].properties->width) { // if the asset is out of screen + assets[i].visible = false; // set asset x coordinate to 0 to mark it as "inactive" + } + } + } +} + +void spawn_random_background_asset(BackgroundAsset* const assets) { + // Calculate the total spawn chances for all assets + int total_spawn_chance = 0; + for(int i = 0; i < BG_ASSETS_MAX; ++i) { + total_spawn_chance += assetProperties[i].spawn_chance; + } + + // Generate a random number between 0 and total_spawn_chance + int random_number = rand() % total_spawn_chance; + + // Select the asset based on the random number + int chosen_asset = -1; + int accumulated_chance = 0; + for(int i = 0; i < BG_ASSETS_MAX; ++i) { + accumulated_chance += assetProperties[i].spawn_chance; + if(random_number < accumulated_chance) { + chosen_asset = i; + break; + } + } + + // If no asset is chosen, return + if(chosen_asset == -1) { + return; + } + + // Look for an available slot for the chosen asset + for(int i = 0; i < BG_ASSETS_MAX; ++i) { + if(assets[i].visible == false) { + // Spawn the asset + assets[i].point.x = 127 + assetProperties[chosen_asset].x_offset; + assets[i].point.y = assetProperties[chosen_asset].y_offset; + assets[i].properties = &assetProperties[chosen_asset]; + assets[i].visible = true; + break; + } + } +} + +void draw_background_assets(const BackgroundAsset* assets, Canvas* const canvas, int distance) { + canvas_draw_box(canvas, 0, 6, 128, 1); + canvas_draw_box(canvas, 0, 56, 128, 2); + + // Calculate the pillar offset based on the traveled distance + int pillar_offset = distance % 64; + + // Draw pillars + for(int x = -pillar_offset; x < 128; x += 64) { + canvas_draw_icon(canvas, x, 6, &I_pillar); + } + + // Draw assets + for(int i = 0; i < BG_ASSETS_MAX; ++i) { + if(assets[i].visible) { + canvas_set_color(canvas, ColorBlack); + canvas_draw_icon( + canvas, assets[i].point.x, assets[i].point.y, assets[i].properties->sprite); + } + } +} diff --git a/applications/external/jetpack_joyride/includes/background_assets.h b/applications/external/jetpack_joyride/includes/background_assets.h new file mode 100644 index 0000000000..d42fcfd711 --- /dev/null +++ b/applications/external/jetpack_joyride/includes/background_assets.h @@ -0,0 +1,34 @@ +#ifndef BACKGROUND_ASSETS_H +#define BACKGROUND_ASSETS_H + +#include +#include + +#include + +#include "point.h" +#include "states.h" +#include "game_sprites.h" +#include + +#define BG_ASSETS_MAX 3 + +typedef struct { + int width; + int spawn_chance; + int x_offset; + int y_offset; + const Icon* sprite; +} AssetProperties; + +typedef struct { + POINT point; + AssetProperties* properties; + bool visible; +} BackgroundAsset; + +void background_assets_tick(BackgroundAsset* const assets); +void spawn_random_background_asset(BackgroundAsset* const assets); +void draw_background_assets(const BackgroundAsset* assets, Canvas* const canvas, int distance); + +#endif // BACKGROUND_ASSETS_H diff --git a/applications/external/jetpack_joyride/includes/barry.c b/applications/external/jetpack_joyride/includes/barry.c new file mode 100644 index 0000000000..61d3a6fc4d --- /dev/null +++ b/applications/external/jetpack_joyride/includes/barry.c @@ -0,0 +1,33 @@ +#include "barry.h" +#include "game_sprites.h" + +#include +#include + +void barry_tick(BARRY* const barry) { + // Do jetpack things + if(barry->isBoosting) { + barry->gravity += GRAVITY_BOOST; // Increase upward momentum + } else { + barry->gravity += GRAVITY_FALL; // Increase downward momentum faster + } + + barry->point.y += barry->gravity; + + // Constrain barry's height within sprite_height and 64 - sprite_height + if(barry->point.y > (64 - BARRY_HEIGHT)) { + barry->point.y = 64 - BARRY_HEIGHT; + barry->gravity = 0; // stop upward momentum + } else if(barry->point.y < 0) { + barry->point.y = 0; + barry->gravity = 0; // stop downward momentum + } +} + +void draw_barry(const BARRY* barry, Canvas* const canvas, const GameSprites* sprites) { + canvas_set_color(canvas, ColorBlack); + canvas_draw_icon_animation(canvas, barry->point.x, barry->point.y, sprites->barry); + + canvas_set_color(canvas, ColorWhite); + canvas_draw_icon(canvas, barry->point.x, barry->point.y, sprites->barry_infill); +} \ No newline at end of file diff --git a/applications/external/jetpack_joyride/includes/barry.h b/applications/external/jetpack_joyride/includes/barry.h new file mode 100644 index 0000000000..494af434d4 --- /dev/null +++ b/applications/external/jetpack_joyride/includes/barry.h @@ -0,0 +1,23 @@ +#ifndef BARRY_H +#define BARRY_H + +#include + +#include +#include "point.h" +#include "game_sprites.h" + +#define GRAVITY_TICK 0.2 +#define GRAVITY_BOOST -0.4 +#define GRAVITY_FALL 0.3 + +typedef struct { + float gravity; + POINT point; + bool isBoosting; +} BARRY; + +void barry_tick(BARRY* const barry); +void draw_barry(const BARRY* barry, Canvas* const canvas, const GameSprites* sprites); + +#endif // BARRY_H \ No newline at end of file diff --git a/applications/external/jetpack_joyride/includes/coin.c b/applications/external/jetpack_joyride/includes/coin.c new file mode 100644 index 0000000000..7a3811a8c9 --- /dev/null +++ b/applications/external/jetpack_joyride/includes/coin.c @@ -0,0 +1,98 @@ +#include +#include + +#include +#include + +#include "coin.h" +#include "barry.h" + +#define PATTERN_MAX_HEIGHT 40 + +// Patterns +const COIN_PATTERN coin_patterns[] = { + {// Square pattern + .count = 9, + .coins = {{0, 0}, {8, 0}, {16, 0}, {0, 8}, {8, 8}, {16, 8}, {0, 16}, {8, 16}, {16, 16}}}, + {// Wavy pattern (approximate sine wave) + .count = 8, + .coins = {{0, 8}, {8, 16}, {16, 24}, {24, 16}, {32, 8}, {40, 0}, {48, 8}, {56, 16}}}, + {// Diagonal pattern + .count = 5, + .coins = {{0, 0}, {8, 8}, {16, 16}, {24, 24}, {32, 32}}}, + // Add more patterns here +}; + +void coin_tick(COIN* const coins, BARRY* const barry, int* const total_coins) { + // Move coins towards the player + for(int i = 0; i < COINS_MAX; i++) { + if(coin_colides(&coins[i], barry)) { + coins[i].point.x = 0; // Remove the coin + (*total_coins)++; + } + if(coins[i].point.x > 0) { + coins[i].point.x -= 1; // move left by 1 unit + if(coins[i].point.x < -COIN_WIDTH) { // if the coin is out of screen + coins[i].point.x = 0; // set coin x coordinate to 0 to mark it as "inactive" + } + } + } +} + +bool coin_colides(COIN* const coin, BARRY* const barry) { + return !( + barry->point.x > coin->point.x + COIN_WIDTH || // Barry is to the right of the coin + barry->point.x + BARRY_WIDTH < coin->point.x || // Barry is to the left of the coin + barry->point.y > coin->point.y + COIN_WIDTH || // Barry is below the coin + barry->point.y + BARRY_HEIGHT < coin->point.y); // Barry is above the coin +} + +void spawn_random_coin(COIN* const coins) { + // Select a random pattern + int pattern_index = rand() % (sizeof(coin_patterns) / sizeof(coin_patterns[0])); + const COIN_PATTERN* pattern = &coin_patterns[pattern_index]; + + // Count available slots for new coins + int available_slots = 0; + for(int i = 0; i < COINS_MAX; ++i) { + if(coins[i].point.x <= 0) { + ++available_slots; + } + } + + // If there aren't enough slots, return without spawning coins + if(available_slots < pattern->count) return; + + // Spawn coins according to the selected pattern + int coin_index = 0; + int random_offset = rand() % (SCREEN_HEIGHT - PATTERN_MAX_HEIGHT); + int random_offset_x = rand() % 16; + for(int i = 0; i < pattern->count; ++i) { + // Find an available slot for a new coin + while(coins[coin_index].point.x > 0 && coin_index < COINS_MAX) { + ++coin_index; + } + // If no slot is available, stop spawning coins + if(coin_index == COINS_MAX) break; + + // Spawn the coin + coins[coin_index].point.x = SCREEN_WIDTH - 1 + pattern->coins[i].x + random_offset_x; + coins[coin_index].point.y = + random_offset + + pattern->coins[i] + .y; // The pattern is spawned at a random y position, but not too close to the screen edge + } +} + +void draw_coins(const COIN* coins, Canvas* const canvas, const GameSprites* sprites) { + canvas_set_color(canvas, ColorBlack); + for(int i = 0; i < COINS_MAX; ++i) { + if(coins[i].point.x > 0) { + canvas_set_color(canvas, ColorBlack); + canvas_draw_icon(canvas, coins[i].point.x, coins[i].point.y, sprites->coin); + + canvas_set_color(canvas, ColorWhite); + canvas_draw_icon(canvas, coins[i].point.x, coins[i].point.y, sprites->coin_infill); + } + } +} diff --git a/applications/external/jetpack_joyride/includes/coin.h b/applications/external/jetpack_joyride/includes/coin.h new file mode 100644 index 0000000000..41fd21ddc1 --- /dev/null +++ b/applications/external/jetpack_joyride/includes/coin.h @@ -0,0 +1,26 @@ +#ifndef COIN_H +#define COIN_H + +#include + +#include "point.h" +#include "barry.h" + +#define COINS_MAX 15 + +typedef struct { + float gravity; + POINT point; +} COIN; + +typedef struct { + int count; + POINT coins[COINS_MAX]; +} COIN_PATTERN; + +void coin_tick(COIN* const coins, BARRY* const barry, int* const poins); +void spawn_random_coin(COIN* const coins); +bool coin_colides(COIN* const coin, BARRY* const barry); +void draw_coins(const COIN* coins, Canvas* const canvas, const GameSprites* sprites); + +#endif // COIN_H \ No newline at end of file diff --git a/applications/external/jetpack_joyride/includes/game_sprites.h b/applications/external/jetpack_joyride/includes/game_sprites.h new file mode 100644 index 0000000000..d38494bf86 --- /dev/null +++ b/applications/external/jetpack_joyride/includes/game_sprites.h @@ -0,0 +1,35 @@ +#ifndef GAME_SPRITES_H +#define GAME_SPRITES_H + +#include "point.h" +#include + +#define SCREEN_WIDTH 128 +#define SCREEN_HEIGHT 64 + +#define BARRY_WIDTH 11 +#define BARRY_HEIGHT 15 + +#define MISSILE_WIDTH 26 +#define MISSILE_HEIGHT 12 + +#define SCIENTIST_WIDTH 9 +#define SCIENTIST_HEIGHT 14 + +#define COIN_WIDTH 7 + +typedef struct { + IconAnimation* barry; + const Icon* barry_infill; + const Icon* scientist_left; + const Icon* scientist_left_infill; + const Icon* scientist_right; + const Icon* scientist_right_infill; + const Icon* coin; + const Icon* coin_infill; + IconAnimation* missile; + IconAnimation* alert; + const Icon* missile_infill; +} GameSprites; + +#endif // GAME_SPRITES_H \ No newline at end of file diff --git a/applications/external/jetpack_joyride/includes/game_state.c b/applications/external/jetpack_joyride/includes/game_state.c new file mode 100644 index 0000000000..a8a9db6185 --- /dev/null +++ b/applications/external/jetpack_joyride/includes/game_state.c @@ -0,0 +1,5 @@ +#include "game_state.h" + +void game_state_tick(GameState* const game_state) { + game_state->distance++; +} diff --git a/applications/external/jetpack_joyride/includes/game_state.h b/applications/external/jetpack_joyride/includes/game_state.h new file mode 100644 index 0000000000..1e97aaf180 --- /dev/null +++ b/applications/external/jetpack_joyride/includes/game_state.h @@ -0,0 +1,34 @@ +#ifndef GAMESTATE_H +#define GAMESTATE_H + +#include +#include + +#include "barry.h" +#include "scientist.h" +#include "coin.h" +#include "particle.h" +#include "game_sprites.h" +#include "states.h" +#include "missile.h" +#include "background_assets.h" +typedef struct { + int total_coins; + int distance; + bool new_highscore; + BARRY barry; + COIN coins[COINS_MAX]; + PARTICLE particles[PARTICLES_MAX]; + SCIENTIST scientists[SCIENTISTS_MAX]; + MISSILE missiles[MISSILES_MAX]; + BackgroundAsset bg_assets[BG_ASSETS_MAX]; + State state; + GameSprites sprites; + FuriMutex* mutex; + FuriTimer* timer; + void (*death_handler)(); +} GameState; + +void game_state_tick(GameState* const game_state); + +#endif // GAMESTATE_H \ No newline at end of file diff --git a/applications/external/jetpack_joyride/includes/missile.c b/applications/external/jetpack_joyride/includes/missile.c new file mode 100644 index 0000000000..af47e8478d --- /dev/null +++ b/applications/external/jetpack_joyride/includes/missile.c @@ -0,0 +1,86 @@ +#include +#include + +#include +#include + +#include "states.h" +#include "game_sprites.h" +#include "missile.h" +#include "barry.h" + +void missile_tick(MISSILE* const missiles, BARRY* const barry, void (*death_handler)()) { + // Move missiles towards the player + for(int i = 0; i < MISSILES_MAX; i++) { + if(missiles[i].visible && missile_colides(&missiles[i], barry)) { + death_handler(); + } + if(missiles[i].visible) { + missiles[i].point.x -= 2; // move left by 2 units + if(missiles[i].point.x < -MISSILE_WIDTH) { // if the missile is out of screen + missiles[i].visible = false; // set missile as "inactive" + } + } + } +} + +void spawn_random_missile(MISSILE* const missiles) { + // Check for an available slot for a new missile + for(int i = 0; i < MISSILES_MAX; ++i) { + if(!missiles[i].visible) { + missiles[i].point.x = 2 * SCREEN_WIDTH; + missiles[i].point.y = rand() % (SCREEN_HEIGHT - MISSILE_HEIGHT); + missiles[i].visible = true; + break; + } + } +} + +void draw_missiles(const MISSILE* missiles, Canvas* const canvas, const GameSprites* sprites) { + for(int i = 0; i < MISSILES_MAX; ++i) { + if(missiles[i].visible) { + canvas_set_color(canvas, ColorBlack); + + if(missiles[i].point.x > 128) { + canvas_draw_icon_animation( + canvas, SCREEN_WIDTH - 7, missiles[i].point.y, sprites->alert); + } else { + canvas_draw_icon_animation( + canvas, missiles[i].point.x, missiles[i].point.y, sprites->missile); + + canvas_set_color(canvas, ColorWhite); + canvas_draw_icon( + canvas, missiles[i].point.x, missiles[i].point.y, sprites->missile_infill); + } + } + } +} + +bool missile_colides(MISSILE* const missile, BARRY* const barry) { + return !( + barry->point.x > + missile->point.x + MISSILE_WIDTH - 14 || // Barry is to the right of the missile + barry->point.x + BARRY_WIDTH - 3 < + missile->point.x || // Barry is to the left of the missile + barry->point.y > missile->point.y + MISSILE_HEIGHT || // Barry is below the missile + barry->point.y + BARRY_HEIGHT < missile->point.y); // Barry is above the missile +} + +int get_rocket_spawn_distance(int player_distance) { + // Define the start and end points for rocket spawn distance + int start_distance = 256; + int end_distance = 24; + + // Define the maximum player distance at which the spawn distance should be at its minimum + int max_player_distance = 5000; // Adjust this value based on your game's difficulty curve + + if(player_distance >= max_player_distance) { + return end_distance; + } + + // Calculate the linear interpolation factor + float t = (float)player_distance / max_player_distance; + + // Interpolate the rocket spawn distance + return start_distance + t * (end_distance - start_distance); +} diff --git a/applications/external/jetpack_joyride/includes/missile.h b/applications/external/jetpack_joyride/includes/missile.h new file mode 100644 index 0000000000..a5af4e885d --- /dev/null +++ b/applications/external/jetpack_joyride/includes/missile.h @@ -0,0 +1,24 @@ +#ifndef MISSILE_H +#define MISSILE_H + +#include +#include "game_sprites.h" + +#include "states.h" +#include "point.h" +#include "barry.h" + +#define MISSILES_MAX 5 + +typedef struct { + POINT point; + bool visible; +} MISSILE; + +void missile_tick(MISSILE* const missiles, BARRY* const barry, void (*death_handler)()); +void spawn_random_missile(MISSILE* const MISSILEs); +bool missile_colides(MISSILE* const MISSILE, BARRY* const barry); +int get_rocket_spawn_distance(int player_distance); +void draw_missiles(const MISSILE* missiles, Canvas* const canvas, const GameSprites* sprites); + +#endif // MISSILE_H \ No newline at end of file diff --git a/applications/external/jetpack_joyride/includes/particle.c b/applications/external/jetpack_joyride/includes/particle.c new file mode 100644 index 0000000000..cf8e6e0a60 --- /dev/null +++ b/applications/external/jetpack_joyride/includes/particle.c @@ -0,0 +1,57 @@ +#include + +#include "particle.h" +#include "scientist.h" +#include "barry.h" + +void particle_tick(PARTICLE* const particles, SCIENTIST* const scientists) { + // Move particles + for(int i = 0; i < PARTICLES_MAX; i++) { + if(particles[i].point.y > 0) { + particles[i].point.y += PARTICLE_VELOCITY; + + // Check collision with scientists + for(int j = 0; j < SCIENTISTS_MAX; j++) { + if(scientists[j].state == ScientistStateAlive && scientists[j].point.x > 0) { + // Check whether the particle lies within the scientist's bounding box + if(!(particles[i].point.x > scientists[j].point.x + SCIENTIST_WIDTH || + particles[i].point.x < scientists[j].point.x || + particles[i].point.y > scientists[j].point.y + SCIENTIST_HEIGHT || + particles[i].point.y < scientists[j].point.y)) { + scientists[j].state = ScientistStateDead; + // (*points) += 2; // Increase the score by 2 + } + } + } + + if(particles[i].point.x < 0 || particles[i].point.x > SCREEN_WIDTH || + particles[i].point.y < 0 || particles[i].point.y > SCREEN_HEIGHT) { + particles[i].point.y = 0; + } + } + } +} + +void spawn_random_particles(PARTICLE* const particles, BARRY* const barry) { + for(int i = 0; i < PARTICLES_MAX; i++) { + if(particles[i].point.y <= 0) { + particles[i].point.x = barry->point.x + (rand() % 4); + particles[i].point.y = barry->point.y + 14; + break; + } + } +} + +void draw_particles(const PARTICLE* particles, Canvas* const canvas) { + canvas_set_color(canvas, ColorBlack); + for(int i = 0; i < PARTICLES_MAX; i++) { + if(particles[i].point.y > 0) { + canvas_draw_line( + canvas, + particles[i].point.x, + particles[i].point.y, + particles[i].point.x, + particles[i].point.y + 3); + } + } +} diff --git a/applications/external/jetpack_joyride/includes/particle.h b/applications/external/jetpack_joyride/includes/particle.h new file mode 100644 index 0000000000..3442c9c4ec --- /dev/null +++ b/applications/external/jetpack_joyride/includes/particle.h @@ -0,0 +1,21 @@ + + +#ifndef PARTICLE_H +#define PARTICLE_H + +#include "point.h" +#include "scientist.h" +#include "barry.h" + +#define PARTICLES_MAX 50 +#define PARTICLE_VELOCITY 2 + +typedef struct { + POINT point; +} PARTICLE; + +void particle_tick(PARTICLE* const particles, SCIENTIST* const scientists); +void spawn_random_particles(PARTICLE* const particles, BARRY* const barry); +void draw_particles(const PARTICLE* particles, Canvas* const canvas); + +#endif // PARTICLE_H \ No newline at end of file diff --git a/applications/external/jetpack_joyride/includes/point.h b/applications/external/jetpack_joyride/includes/point.h new file mode 100644 index 0000000000..02c9a6ce4a --- /dev/null +++ b/applications/external/jetpack_joyride/includes/point.h @@ -0,0 +1,14 @@ +#ifndef POINT_H +#define POINT_H + +typedef struct { + int x; + int y; +} POINT; + +typedef struct { + float x; + float y; +} POINTF; + +#endif // POINT_H \ No newline at end of file diff --git a/applications/external/jetpack_joyride/includes/scientist.c b/applications/external/jetpack_joyride/includes/scientist.c new file mode 100644 index 0000000000..b1a8a14c03 --- /dev/null +++ b/applications/external/jetpack_joyride/includes/scientist.c @@ -0,0 +1,77 @@ +#include "scientist.h" +#include "game_sprites.h" + +#include +#include + +void scientist_tick(SCIENTIST* const scientists) { + for(int i = 0; i < SCIENTISTS_MAX; i++) { + if(scientists[i].visible) { + if(scientists[i].point.x < 64) scientists[i].velocity_x = 0.5f; + + scientists[i].point.x -= scientists[i].state == ScientistStateAlive ? + 1 - scientists[i].velocity_x : + 1; // move based on velocity_x + int width = (scientists[i].state == ScientistStateAlive) ? SCIENTIST_WIDTH : + SCIENTIST_HEIGHT; + if(scientists[i].point.x <= -width) { // if the scientist is out of screen + scientists[i].visible = false; + } + } + } +} + +void spawn_random_scientist(SCIENTIST* const scientists) { + float velocities[] = {-0.5f, 0.0f, 0.5f, -1.0f}; + // Check for an available slot for a new scientist + for(int i = 0; i < SCIENTISTS_MAX; ++i) { + if(!scientists[i].visible && + (rand() % 1000) < 10) { // Spawn rate is less frequent than coins + scientists[i].state = ScientistStateAlive; + scientists[i].point.x = 127; + scientists[i].point.y = 49; + scientists[i].velocity_x = velocities[rand() % 4]; + scientists[i].visible = true; + break; + } + } +} + +void draw_scientists(const SCIENTIST* scientists, Canvas* const canvas, const GameSprites* sprites) { + for(int i = 0; i < SCIENTISTS_MAX; ++i) { + if(scientists[i].visible) { + canvas_set_color(canvas, ColorBlack); + if(scientists[i].state == ScientistStateAlive) { + canvas_draw_icon( + canvas, + (int)scientists[i].point.x, + scientists[i].point.y, + scientists[i].velocity_x >= 0 ? sprites->scientist_right : + sprites->scientist_left); + + canvas_set_color(canvas, ColorWhite); + canvas_draw_icon( + canvas, + (int)scientists[i].point.x, + scientists[i].point.y, + scientists[i].velocity_x >= 0 ? sprites->scientist_right_infill : + sprites->scientist_left_infill); + + } else { + canvas_set_color(canvas, ColorBlack); + canvas_draw_icon( + canvas, + (int)scientists[i].point.x, + scientists[i].point.y + 5, + &I_dead_scientist); + + canvas_set_color(canvas, ColorWhite); + canvas_draw_icon( + canvas, + (int)scientists[i].point.x, + scientists[i].point.y + 5, + &I_dead_scientist_infill); + } + } + } +} \ No newline at end of file diff --git a/applications/external/jetpack_joyride/includes/scientist.h b/applications/external/jetpack_joyride/includes/scientist.h new file mode 100644 index 0000000000..a49e8028c7 --- /dev/null +++ b/applications/external/jetpack_joyride/includes/scientist.h @@ -0,0 +1,29 @@ +#ifndef SCIENTIST_H +#define SCIENTIST_H + +#include "point.h" +#include "game_sprites.h" +#include + +#define SCIENTIST_VELOCITY_MIN -0.5f +#define SCIENTIST_VELOCITY_MAX 0.5f + +#define SCIENTISTS_MAX 6 + +typedef enum { + ScientistStateAlive, + ScientistStateDead, +} ScientistState; + +typedef struct { + bool visible; + POINTF point; + float velocity_x; + ScientistState state; +} SCIENTIST; + +void scientist_tick(SCIENTIST* const scientist); +void spawn_random_scientist(SCIENTIST* const scientists); +void draw_scientists(const SCIENTIST* scientists, Canvas* const canvas, const GameSprites* sprites); + +#endif // SCIENTIST_H \ No newline at end of file diff --git a/applications/external/jetpack_joyride/includes/states.h b/applications/external/jetpack_joyride/includes/states.h new file mode 100644 index 0000000000..d58e3e1f66 --- /dev/null +++ b/applications/external/jetpack_joyride/includes/states.h @@ -0,0 +1,9 @@ +#ifndef STATE_H +#define STATE_H + +typedef enum { + GameStateLife, + GameStateGameOver, +} State; + +#endif // STATE_H \ No newline at end of file diff --git a/applications/external/jetpack_joyride/jetpack.c b/applications/external/jetpack_joyride/jetpack.c new file mode 100644 index 0000000000..c12f094c94 --- /dev/null +++ b/applications/external/jetpack_joyride/jetpack.c @@ -0,0 +1,379 @@ +#include + +#include +#include +#include +#include +#include +#include + +#include "includes/point.h" +#include "includes/barry.h" +#include "includes/scientist.h" +#include "includes/particle.h" +#include "includes/coin.h" +#include "includes/missile.h" +#include "includes/background_assets.h" + +#include "includes/game_state.h" + +#define TAG "Jetpack Joyride" +#define SAVING_DIRECTORY "/ext/apps/Games" +#define SAVING_FILENAME SAVING_DIRECTORY "/jetpack.save" +static GameState* global_state; + +typedef enum { + EventTypeTick, + EventTypeKey, +} EventType; + +typedef struct { + EventType type; + InputEvent input; +} GameEvent; + +typedef struct { + int max_distance; + int total_coins; +} SaveGame; + +static SaveGame save_game; + +static bool storage_game_state_load() { + Storage* storage = furi_record_open(RECORD_STORAGE); + File* file = storage_file_alloc(storage); + + uint16_t bytes_readed = 0; + if(storage_file_open(file, SAVING_FILENAME, FSAM_READ, FSOM_OPEN_EXISTING)) + bytes_readed = storage_file_read(file, &save_game, sizeof(SaveGame)); + storage_file_close(file); + storage_file_free(file); + furi_record_close(RECORD_STORAGE); + return bytes_readed == sizeof(SaveGame); +} + +static void storage_game_state_save() { + Storage* storage = furi_record_open(RECORD_STORAGE); + + if(storage_common_stat(storage, SAVING_DIRECTORY, NULL) == FSE_NOT_EXIST) { + if(!storage_simply_mkdir(storage, SAVING_DIRECTORY)) { + return; + } + } + + File* file = storage_file_alloc(storage); + if(storage_file_open(file, SAVING_FILENAME, FSAM_WRITE, FSOM_CREATE_ALWAYS)) { + storage_file_write(file, &save_game, sizeof(SaveGame)); + } + storage_file_close(file); + storage_file_free(file); + furi_record_close(RECORD_STORAGE); +} + +void handle_death() { + global_state->state = GameStateGameOver; + global_state->new_highscore = global_state->distance > save_game.max_distance; + + if(global_state->distance > save_game.max_distance) { + save_game.max_distance = global_state->distance; + } + + save_game.total_coins += global_state->total_coins; + + storage_game_state_save(); +} + +static void jetpack_game_state_init(GameState* const game_state) { + UNUSED(game_state); + UNUSED(storage_game_state_save); + BARRY barry; + barry.gravity = 0; + barry.point.x = 32 + 5; + barry.point.y = 32; + barry.isBoosting = false; + + GameSprites sprites; + sprites.barry = icon_animation_alloc(&A_barry); + sprites.barry_infill = &I_barry_infill; + + sprites.scientist_left = (&I_scientist_left); + sprites.scientist_left_infill = (&I_scientist_left_infill); + sprites.scientist_right = (&I_scientist_right); + sprites.scientist_right_infill = (&I_scientist_right_infill); + + sprites.coin = (&I_coin); + sprites.coin_infill = (&I_coin_infill); + + sprites.missile = icon_animation_alloc(&A_missile); + sprites.missile_infill = &I_missile_infill; + + sprites.alert = icon_animation_alloc(&A_alert); + + icon_animation_start(sprites.barry); + icon_animation_start(sprites.missile); + icon_animation_start(sprites.alert); + + game_state->barry = barry; + game_state->total_coins = 0; + game_state->distance = 0; + game_state->new_highscore = false; + game_state->sprites = sprites; + game_state->state = GameStateLife; + game_state->death_handler = handle_death; + + memset(game_state->bg_assets, 0, sizeof(game_state->bg_assets)); + + memset(game_state->scientists, 0, sizeof(game_state->scientists)); + memset(game_state->coins, 0, sizeof(game_state->coins)); + memset(game_state->particles, 0, sizeof(game_state->particles)); + memset(game_state->missiles, 0, sizeof(game_state->missiles)); +} + +static void jetpack_game_state_free(GameState* const game_state) { + icon_animation_free(game_state->sprites.barry); + icon_animation_free(game_state->sprites.missile); + icon_animation_free(game_state->sprites.alert); + + free(game_state); +} + +static void jetpack_game_tick(GameState* const game_state) { + if(game_state->state == GameStateGameOver) return; + barry_tick(&game_state->barry); + game_state_tick(game_state); + coin_tick(game_state->coins, &game_state->barry, &game_state->total_coins); + particle_tick(game_state->particles, game_state->scientists); + scientist_tick(game_state->scientists); + missile_tick(game_state->missiles, &game_state->barry, game_state->death_handler); + + background_assets_tick(game_state->bg_assets); + + // generate background every 64px aka. ticks + if(game_state->distance % 64 == 0 && rand() % 3 == 0) { + spawn_random_background_asset(game_state->bg_assets); + } + + if(game_state->distance % 48 == 0 && rand() % 2 == 0) { + spawn_random_coin(game_state->coins); + } + + if(game_state->distance % get_rocket_spawn_distance(game_state->distance) == 0 && + rand() % 2 == 0) { + spawn_random_missile(game_state->missiles); + } + + spawn_random_scientist(game_state->scientists); + + if(game_state->barry.isBoosting) { + spawn_random_particles(game_state->particles, &game_state->barry); + } +} + +static void jetpack_game_render_callback(Canvas* const canvas, void* ctx) { + furi_assert(ctx); + const GameState* game_state = ctx; + furi_mutex_acquire(game_state->mutex, FuriWaitForever); + + if(game_state->state == GameStateLife) { + canvas_set_bitmap_mode(canvas, false); + + draw_background_assets(game_state->bg_assets, canvas, game_state->distance); + + canvas_set_bitmap_mode(canvas, true); + + draw_coins(game_state->coins, canvas, &game_state->sprites); + draw_scientists(game_state->scientists, canvas, &game_state->sprites); + draw_particles(game_state->particles, canvas); + draw_missiles(game_state->missiles, canvas, &game_state->sprites); + + draw_barry(&game_state->barry, canvas, &game_state->sprites); + + canvas_set_color(canvas, ColorBlack); + canvas_set_font(canvas, FontSecondary); + char buffer[12]; + snprintf(buffer, sizeof(buffer), "%u m", game_state->distance / 10); + canvas_draw_str_aligned(canvas, 123, 15, AlignRight, AlignBottom, buffer); + + snprintf(buffer, sizeof(buffer), "$%u", game_state->total_coins); + canvas_draw_str_aligned(canvas, 5, 15, AlignLeft, AlignBottom, buffer); + } + + if(game_state->state == GameStateGameOver) { + // Show highscore + char buffer[64]; + + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignTop, "You flew"); + + snprintf( + buffer, + sizeof(buffer), + game_state->new_highscore ? "%u m (new best)" : "%u m", + game_state->distance / 10); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 64, 16, AlignCenter, AlignTop, buffer); + + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned(canvas, 64, 30, AlignCenter, AlignTop, "and collected"); + + snprintf(buffer, sizeof(buffer), "$%u", game_state->total_coins); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 64, 41, AlignCenter, AlignTop, buffer); + + snprintf( + buffer, + sizeof(buffer), + "Best: %u m, Tot: $%u", + save_game.max_distance / 10, + save_game.total_coins); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned(canvas, 64, 63, AlignCenter, AlignBottom, buffer); + + canvas_draw_rframe(canvas, 0, 3, 128, 49, 5); + + // char buffer[12]; + // snprintf(buffer, sizeof(buffer), "Dist: %u", game_state->distance); + // canvas_draw_str_aligned(canvas, 123, 12, AlignRight, AlignBottom, buffer); + + // snprintf(buffer, sizeof(buffer), "Score: %u", game_state->points); + // canvas_draw_str_aligned(canvas, 5, 12, AlignLeft, AlignBottom, buffer); + + // canvas_draw_str_aligned(canvas, 64, 34, AlignCenter, AlignCenter, "Highscore:"); + // snprintf(buffer, sizeof(buffer), "Dist: %u", save_game.max_distance); + // canvas_draw_str_aligned(canvas, 123, 50, AlignRight, AlignBottom, buffer); + + // snprintf(buffer, sizeof(buffer), "Score: %u", save_game.max_score); + // canvas_draw_str_aligned(canvas, 5, 50, AlignLeft, AlignBottom, buffer); + + // canvas_draw_str_aligned(canvas, 64, 32, AlignCenter, AlignCenter, "boom."); + + // if(furi_timer_is_running(game_state->timer)) { + // furi_timer_start(game_state->timer, 0); + // } + } + + // canvas_draw_frame(canvas, 0, 0, 128, 64); + + furi_mutex_release(game_state->mutex); +} + +static void jetpack_game_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { + furi_assert(event_queue); + + GameEvent event = {.type = EventTypeKey, .input = *input_event}; + furi_message_queue_put(event_queue, &event, FuriWaitForever); +} + +static void jetpack_game_update_timer_callback(FuriMessageQueue* event_queue) { + furi_assert(event_queue); + + GameEvent event = {.type = EventTypeTick}; + furi_message_queue_put(event_queue, &event, 0); +} + +int32_t jetpack_game_app(void* p) { + UNUSED(p); + int32_t return_code = 0; + + if(!storage_game_state_load()) { + memset(&save_game, 0, sizeof(save_game)); + } + + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(GameEvent)); + + GameState* game_state = malloc(sizeof(GameState)); + + global_state = game_state; + jetpack_game_state_init(game_state); + + game_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + if(!game_state->mutex) { + FURI_LOG_E(TAG, "cannot create mutex\r\n"); + return_code = 255; + goto free_and_exit; + } + + // Set system callbacks + ViewPort* view_port = view_port_alloc(); + view_port_draw_callback_set(view_port, jetpack_game_render_callback, game_state); + view_port_input_callback_set(view_port, jetpack_game_input_callback, event_queue); + + FuriTimer* timer = + furi_timer_alloc(jetpack_game_update_timer_callback, FuriTimerTypePeriodic, event_queue); + furi_timer_start(timer, furi_kernel_get_tick_frequency() / 25); + + game_state->timer = timer; + + // Open GUI and register view_port + Gui* gui = furi_record_open(RECORD_GUI); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + GameEvent event; + for(bool processing = true; processing;) { + FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); + furi_mutex_acquire(game_state->mutex, FuriWaitForever); + + if(event_status == FuriStatusOk) { + // press events + if(event.type == EventTypeKey) { + if(event.input.type == InputTypeRelease && event.input.key == InputKeyOk) { + game_state->barry.isBoosting = false; + } + + // Reset highscore, for debug purposes + if(event.input.type == InputTypeLong && event.input.key == InputKeyLeft) { + save_game.max_distance = 0; + save_game.total_coins = 0; + storage_game_state_save(); + } + + if(event.input.type == InputTypePress) { + switch(event.input.key) { + case InputKeyUp: + break; + case InputKeyDown: + break; + case InputKeyRight: + break; + case InputKeyLeft: + break; + case InputKeyOk: + if(game_state->state == GameStateGameOver) { + jetpack_game_state_init(game_state); + } + + if(game_state->state == GameStateLife) { + // Do something + game_state->barry.isBoosting = true; + } + + break; + case InputKeyBack: + processing = false; + break; + default: + break; + } + } + } else if(event.type == EventTypeTick) { + jetpack_game_tick(game_state); + } + } + + view_port_update(view_port); + furi_mutex_release(game_state->mutex); + } + + furi_timer_free(timer); + view_port_enabled_set(view_port, false); + gui_remove_view_port(gui, view_port); + furi_record_close(RECORD_GUI); + view_port_free(view_port); + furi_mutex_free(game_state->mutex); + +free_and_exit: + jetpack_game_state_free(game_state); + furi_message_queue_free(event_queue); + + return return_code; +} \ No newline at end of file diff --git a/applications/external/lightmeter/application.fam b/applications/external/lightmeter/application.fam index 7df664517d..fc46550fef 100644 --- a/applications/external/lightmeter/application.fam +++ b/applications/external/lightmeter/application.fam @@ -20,4 +20,7 @@ App( ), ], fap_icon_assets="icons", + fap_author="@oleksiikutuzov", + fap_version="1.0", + fap_description="Lightmeter app for photography based on BH1750 sensor", ) diff --git a/applications/external/mandelbrot/application.fam b/applications/external/mandelbrot/application.fam index 773835fe55..b5a3f4cccb 100644 --- a/applications/external/mandelbrot/application.fam +++ b/applications/external/mandelbrot/application.fam @@ -1,5 +1,5 @@ App( - appid="MandelbrotSet", + appid="mandelbrotset", name="Mandelbrot Set", apptype=FlipperAppType.EXTERNAL, entry_point="mandelbrot_app", @@ -8,5 +8,9 @@ App( stack_size=1 * 1024, order=130, fap_icon="Mandelbrot.png", - fap_category="Games", + fap_category="Media", + fap_author="@Possibly-Matt", + fap_weburl="https://github.com/Possibly-Matt", + fap_version="1.0", + fap_description="The Mandelbrot set is the set of all so-called (complex) numbers that meet Mandelbrots simple arithmetic criterion.", ) diff --git a/applications/external/metronome/application.fam b/applications/external/metronome/application.fam index 373f5f9b3a..e6439db7ba 100644 --- a/applications/external/metronome/application.fam +++ b/applications/external/metronome/application.fam @@ -7,8 +7,10 @@ App( "gui", ], fap_icon="metronome_icon.png", - fap_category="Music", - fap_icon_assets="images", + fap_category="Media", 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 f33c5f651f..18494098ed 100644 --- a/applications/external/metronome/gui_extensions.c +++ b/applications/external/metronome/gui_extensions.c @@ -1,6 +1,6 @@ #include #include -#include +#include //lib can only do bottom left/right void elements_button_top_left(Canvas* canvas, const char* str) { diff --git a/applications/external/metronome/images/ButtonUp_7x4.png b/applications/external/metronome/images/ButtonUp_7x4.png deleted file mode 100644 index 1be79328b4..0000000000 Binary files a/applications/external/metronome/images/ButtonUp_7x4.png and /dev/null differ diff --git a/applications/external/metronome/img/wave_left_4x14.png b/applications/external/metronome/img/wave_left_4x14.png deleted file mode 100644 index beb2a611d2..0000000000 Binary files a/applications/external/metronome/img/wave_left_4x14.png and /dev/null differ diff --git a/applications/external/metronome/img/wave_right_4x14.png b/applications/external/metronome/img/wave_right_4x14.png deleted file mode 100644 index af249ee5b9..0000000000 Binary files a/applications/external/metronome/img/wave_right_4x14.png and /dev/null differ diff --git a/applications/external/mfkey32/application.fam b/applications/external/mfkey32/application.fam index 75fa40bf60..33d932ce99 100644 --- a/applications/external/mfkey32/application.fam +++ b/applications/external/mfkey32/application.fam @@ -9,7 +9,7 @@ App( "storage", ], stack_size=1 * 1024, - fap_icon="mfkey.png", + fap_icon="images/mfkey.png", fap_category="NFC", fap_author="noproto", fap_icon_assets="images", diff --git a/applications/external/mfkey32/mfkey.png b/applications/external/mfkey32/mfkey.png deleted file mode 100644 index 52ab29efb9..0000000000 Binary files a/applications/external/mfkey32/mfkey.png and /dev/null differ diff --git a/applications/external/mfkey32/mfkey32.c b/applications/external/mfkey32/mfkey32.c index d4b2d3e4ab..5e790b01f6 100644 --- a/applications/external/mfkey32/mfkey32.c +++ b/applications/external/mfkey32/mfkey32.c @@ -1112,7 +1112,7 @@ void mfkey32(ProgramState* program_state) { } if(keyarray_size > 0) { // TODO: Should we use DolphinDeedNfcMfcAdd? - DOLPHIN_DEED(DolphinDeedNfcMfcAdd); + dolphin_deed(DolphinDeedNfcMfcAdd); } napi_mf_classic_nonce_array_free(nonce_arr); napi_mf_classic_dict_free(user_dict); diff --git a/applications/external/mifare_fuzzer/application.fam b/applications/external/mifare_fuzzer/application.fam index 13e1a72c92..8674505f7b 100644 --- a/applications/external/mifare_fuzzer/application.fam +++ b/applications/external/mifare_fuzzer/application.fam @@ -9,7 +9,10 @@ App( ], stack_size=4 * 1024, order=30, - fap_icon="images/mifare_fuzzer_10px.png", + fap_icon="mifare_fuzzer_10px.png", fap_category="NFC", - fap_icon_assets="images", + fap_author="@spheeere98", + fap_weburl="https://github.com/spheeere98/mifare_fuzzer", + fap_version="1.0", + fap_description="App emulates Mifare Classic cards with various UIDs to check how reader reacts on them", ) diff --git a/applications/external/mifare_fuzzer/images/mifare_fuzzer_10px.png b/applications/external/mifare_fuzzer/mifare_fuzzer_10px.png similarity index 100% rename from applications/external/mifare_fuzzer/images/mifare_fuzzer_10px.png rename to applications/external/mifare_fuzzer/mifare_fuzzer_10px.png diff --git a/applications/external/minesweeper/application.fam b/applications/external/minesweeper/application.fam index 0a40662799..07b3e97bbc 100644 --- a/applications/external/minesweeper/application.fam +++ b/applications/external/minesweeper/application.fam @@ -8,4 +8,7 @@ App( fap_category="Games", fap_icon="minesweeper_icon.png", order=35, + fap_author="@panki27 & @xMasterX", + fap_version="1.0", + fap_description="Minesweeper Game", ) diff --git a/applications/external/minesweeper/minesweeper.c b/applications/external/minesweeper/minesweeper.c index af056eabc2..a5a7d808bb 100644 --- a/applications/external/minesweeper/minesweeper.c +++ b/applications/external/minesweeper/minesweeper.c @@ -237,6 +237,9 @@ static bool game_won(Minesweeper* minesweeper_state) { message, furi_string_get_cstr(tempStr), 64, 32, AlignCenter, AlignCenter); dialog_message_set_buttons(message, NULL, "Play again", NULL); + // Call dolphin deed when we win the game + // dolphin_deed(DolphinDeedPluginGameWin); + DialogMessageButton choice = dialog_message_show(minesweeper_state->dialogs, message); dialog_message_free(message); furi_string_free(tempStr); @@ -391,6 +394,9 @@ int32_t minesweeper_app(void* p) { Gui* gui = furi_record_open(RECORD_GUI); gui_add_view_port(gui, view_port, GuiLayerFullscreen); + // Call dolphin deed on game start + // dolphin_deed(DolphinDeedPluginGameStart); + PluginEvent event; for(bool processing = true; processing;) { FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); diff --git a/applications/external/morse_code/application.fam b/applications/external/morse_code/application.fam index d6898100d6..1a221844ec 100644 --- a/applications/external/morse_code/application.fam +++ b/applications/external/morse_code/application.fam @@ -10,4 +10,7 @@ App( order=20, fap_icon="morse_code_10px.png", fap_category="Misc", + fap_author="@wh00hw & @xMasterX", + fap_version="1.0", + fap_description="Simple Morse Code parser", ) diff --git a/applications/external/mousejacker/images/badusb_10px.png b/applications/external/mousejacker/images/badusb_10px.png deleted file mode 100644 index 037474aa3b..0000000000 Binary files a/applications/external/mousejacker/images/badusb_10px.png and /dev/null differ diff --git a/applications/external/mousejacker/images/sub1_10px.png b/applications/external/mousejacker/images/sub1_10px.png deleted file mode 100644 index 5a25fdf4ef..0000000000 Binary files a/applications/external/mousejacker/images/sub1_10px.png and /dev/null differ diff --git a/applications/external/multi_converter/application.fam b/applications/external/multi_converter/application.fam index d8bfd93014..3fb2557b4b 100644 --- a/applications/external/multi_converter/application.fam +++ b/applications/external/multi_converter/application.fam @@ -8,4 +8,7 @@ App( order=160, fap_icon="converter_10px.png", fap_category="Misc", + fap_author="@theisolinearchip", + fap_version="1.0", + fap_description="A multi-unit converter written with an easy and expandable system for adding new units and conversion methods", ) diff --git a/applications/external/multi_fuzzer/application.fam b/applications/external/multi_fuzzer/application.fam new file mode 100644 index 0000000000..9a765fd083 --- /dev/null +++ b/applications/external/multi_fuzzer/application.fam @@ -0,0 +1,49 @@ +App( + appid="fuzzer_ibtn", + name="iButton Fuzzer", + apptype=FlipperAppType.EXTERNAL, + entry_point="fuzzer_start_ibtn", + requires=[ + "gui", + "storage", + "dialogs", + "input", + "notification", + ], + stack_size=2 * 1024, + fap_icon="ibutt_10px.png", + fap_category="Tools", + fap_private_libs=[ + Lib( + name="worker", + cdefines=["IBUTTON_PROTOCOL"], + ), + ], + fap_icon_assets="icons", + fap_icon_assets_symbol="fuzzer", +) + +App( + appid="fuzzer_rfid", + name="RFID Fuzzer", + apptype=FlipperAppType.EXTERNAL, + entry_point="fuzzer_start_rfid", + requires=[ + "gui", + "storage", + "dialogs", + "input", + "notification", + ], + stack_size=2 * 1024, + fap_icon="icons/rfid_10px.png", + fap_category="Tools", + fap_private_libs=[ + Lib( + name="worker", + cdefines=["RFID_125_PROTOCOL"], + ), + ], + fap_icon_assets="icons", + fap_icon_assets_symbol="fuzzer", +) diff --git a/applications/external/multi_fuzzer/fuzzer.c b/applications/external/multi_fuzzer/fuzzer.c new file mode 100644 index 0000000000..01a1d60df1 --- /dev/null +++ b/applications/external/multi_fuzzer/fuzzer.c @@ -0,0 +1,163 @@ +#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); + PacsFuzzerApp* app = context; + return scene_manager_handle_custom_event(app->scene_manager, event); +} + +static bool fuzzer_app_back_event_callback(void* context) { + furi_assert(context); + PacsFuzzerApp* app = context; + return scene_manager_handle_back_event(app->scene_manager); +} + +static void fuzzer_app_tick_event_callback(void* context) { + furi_assert(context); + PacsFuzzerApp* app = context; + scene_manager_handle_tick_event(app->scene_manager); +} + +PacsFuzzerApp* fuzzer_app_alloc() { + dolphin_deed(DolphinDeedPluginStart); + + PacsFuzzerApp* app = malloc(sizeof(PacsFuzzerApp)); + + app->fuzzer_state.menu_index = 0; + app->fuzzer_state.proto_index = 0; + + app->worker = fuzzer_worker_alloc(); + app->payload = fuzzer_payload_alloc(); + + app->file_path = furi_string_alloc(); + + // GUI + app->gui = furi_record_open(RECORD_GUI); + + // Dialog + app->dialogs = furi_record_open(RECORD_DIALOGS); + + // Open Notification record + app->notifications = furi_record_open(RECORD_NOTIFICATION); + + // View Dispatcher + app->view_dispatcher = view_dispatcher_alloc(); + + // Popup + app->popup = popup_alloc(); + view_dispatcher_add_view(app->view_dispatcher, FuzzerViewIDPopup, popup_get_view(app->popup)); + + // Main view + app->main_view = fuzzer_view_main_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, FuzzerViewIDMain, fuzzer_view_main_get_view(app->main_view)); + + // Attack view + app->attack_view = fuzzer_view_attack_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, FuzzerViewIDAttack, fuzzer_view_attack_get_view(app->attack_view)); + + // FieldEditor view + app->field_editor_view = fuzzer_view_field_editor_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, + FuzzerViewIDFieldEditor, + fuzzer_view_field_editor_get_view(app->field_editor_view)); + + app->scene_manager = scene_manager_alloc(&fuzzer_scene_handlers, app); + view_dispatcher_enable_queue(app->view_dispatcher); + + view_dispatcher_set_event_callback_context(app->view_dispatcher, app); + view_dispatcher_set_custom_event_callback( + app->view_dispatcher, fuzzer_app_custom_event_callback); + view_dispatcher_set_navigation_event_callback( + app->view_dispatcher, fuzzer_app_back_event_callback); + view_dispatcher_set_tick_event_callback( + app->view_dispatcher, fuzzer_app_tick_event_callback, 100); + + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + + scene_manager_next_scene(app->scene_manager, FuzzerSceneMain); + + return app; +} + +void fuzzer_app_free(PacsFuzzerApp* app) { + furi_assert(app); + + // Remote view + view_dispatcher_remove_view(app->view_dispatcher, FuzzerViewIDMain); + fuzzer_view_main_free(app->main_view); + + // Attack view + view_dispatcher_remove_view(app->view_dispatcher, FuzzerViewIDAttack); + fuzzer_view_attack_free(app->attack_view); + + // FieldEditor view + view_dispatcher_remove_view(app->view_dispatcher, FuzzerViewIDFieldEditor); + fuzzer_view_field_editor_free(app->field_editor_view); + + // Popup + view_dispatcher_remove_view(app->view_dispatcher, FuzzerViewIDPopup); + popup_free(app->popup); + + scene_manager_free(app->scene_manager); + view_dispatcher_free(app->view_dispatcher); + + // Dialog + furi_record_close(RECORD_DIALOGS); + + // Close records + furi_record_close(RECORD_GUI); + + // Notifications + furi_record_close(RECORD_NOTIFICATION); + app->notifications = NULL; + + furi_string_free(app->file_path); + + fuzzer_payload_free(app->payload); + fuzzer_worker_free(app->worker); + + free(app); +} + +int32_t fuzzer_start_ibtn(void* p) { + UNUSED(p); + PacsFuzzerApp* fuzzer_app = fuzzer_app_alloc(); + + FuzzerConsts app_const = { + .custom_dict_folder = EXT_PATH("ibutton_fuzzer"), + .custom_dict_extension = ".txt", + .key_extension = ".ibtn", + .path_key_folder = EXT_PATH("ibutton"), + .key_icon = &I_ibutt_10px, + }; + fuzzer_app->fuzzer_const = &app_const; + + view_dispatcher_run(fuzzer_app->view_dispatcher); + + fuzzer_app_free(fuzzer_app); + return 0; +} + +int32_t fuzzer_start_rfid(void* p) { + UNUSED(p); + PacsFuzzerApp* fuzzer_app = fuzzer_app_alloc(); + + FuzzerConsts app_const = { + .custom_dict_folder = EXT_PATH("lfrfid_fuzzer"), + .custom_dict_extension = ".txt", + .key_extension = ".rfid", + .path_key_folder = EXT_PATH("lfrfid"), + .key_icon = &I_125_10px, + }; + fuzzer_app->fuzzer_const = &app_const; + + view_dispatcher_run(fuzzer_app->view_dispatcher); + + fuzzer_app_free(fuzzer_app); + return 0; +} diff --git a/applications/external/multi_fuzzer/fuzzer_i.h b/applications/external/multi_fuzzer/fuzzer_i.h new file mode 100644 index 0000000000..c8308adbdf --- /dev/null +++ b/applications/external/multi_fuzzer/fuzzer_i.h @@ -0,0 +1,56 @@ +#pragma once + +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include "scenes/fuzzer_scene.h" +#include "views/main_menu.h" +#include "views/attack.h" +#include "views/field_editor.h" + +#include "helpers/fuzzer_types.h" +#include "lib/worker/fake_worker.h" + +#include +#include "fuzzer_icons.h" +#include + +#define FUZZ_TIME_DELAY_MAX (80) + +typedef struct { + const char* custom_dict_extension; + const char* custom_dict_folder; + const char* key_extension; + const char* path_key_folder; + const Icon* key_icon; +} FuzzerConsts; + +typedef struct { + Gui* gui; + NotificationApp* notifications; + + ViewDispatcher* view_dispatcher; + SceneManager* scene_manager; + + Popup* popup; + DialogsApp* dialogs; + FuzzerViewMain* main_view; + FuzzerViewAttack* attack_view; + FuzzerViewFieldEditor* field_editor_view; + + FuriString* file_path; + + FuzzerState fuzzer_state; + FuzzerConsts* fuzzer_const; + + FuzzerWorker* worker; + FuzzerPayload* payload; +} PacsFuzzerApp; diff --git a/applications/external/multi_fuzzer/helpers/fuzzer_custom_event.h b/applications/external/multi_fuzzer/helpers/fuzzer_custom_event.h new file mode 100644 index 0000000000..3211877226 --- /dev/null +++ b/applications/external/multi_fuzzer/helpers/fuzzer_custom_event.h @@ -0,0 +1,17 @@ +#pragma once + +typedef enum { + + // FuzzerCustomEvent + FuzzerCustomEventViewMainBack = 100, + FuzzerCustomEventViewMainOk, + FuzzerCustomEventViewMainPopupErr, + + FuzzerCustomEventViewAttackBack, + FuzzerCustomEventViewAttackOk, + // FuzzerCustomEventViewAttackTick, // now not use + FuzzerCustomEventViewAttackEnd, + + FuzzerCustomEventViewFieldEditorBack, + FuzzerCustomEventViewFieldEditorOk, +} FuzzerCustomEvent; \ No newline at end of file diff --git a/applications/external/multi_fuzzer/helpers/fuzzer_types.h b/applications/external/multi_fuzzer/helpers/fuzzer_types.h new file mode 100644 index 0000000000..bb608a5f14 --- /dev/null +++ b/applications/external/multi_fuzzer/helpers/fuzzer_types.h @@ -0,0 +1,30 @@ +#pragma once + +#include + +typedef struct { + uint8_t menu_index; + uint8_t proto_index; +} FuzzerState; + +typedef enum { + FuzzerAttackStateOff = 0, + FuzzerAttackStateIdle, + FuzzerAttackStateRunning, + FuzzerAttackStateEnd, + +} FuzzerAttackState; + +typedef enum { + FuzzerFieldEditorStateEditingOn = 0, + FuzzerFieldEditorStateEditingOff, + +} FuzzerFieldEditorState; + +typedef enum { + FuzzerViewIDPopup, + + FuzzerViewIDMain, + FuzzerViewIDAttack, + FuzzerViewIDFieldEditor, +} FuzzerViewID; \ No newline at end of file diff --git a/applications/external/ibtn_fuzzer/ibutt_10px.png b/applications/external/multi_fuzzer/ibutt_10px.png similarity index 100% rename from applications/external/ibtn_fuzzer/ibutt_10px.png rename to applications/external/multi_fuzzer/ibutt_10px.png diff --git a/applications/external/flipfrid/rfid_10px.png b/applications/external/multi_fuzzer/icons/rfid_10px.png similarity index 100% rename from applications/external/flipfrid/rfid_10px.png rename to applications/external/multi_fuzzer/icons/rfid_10px.png diff --git a/applications/external/multi_fuzzer/lib/worker/fake_worker.c b/applications/external/multi_fuzzer/lib/worker/fake_worker.c new file mode 100644 index 0000000000..07b0479b49 --- /dev/null +++ b/applications/external/multi_fuzzer/lib/worker/fake_worker.c @@ -0,0 +1,485 @@ +#include "fake_worker.h" +#include "protocol_i.h" + +#include + +#include +#include +#include + +#define TAG "Fuzzer worker" + +#if defined(RFID_125_PROTOCOL) + +#include +#include +#include + +#else + +#include +#include + +#endif + +#include + +struct FuzzerWorker { +#if defined(RFID_125_PROTOCOL) + LFRFIDWorker* proto_worker; + ProtocolId protocol_id; + ProtocolDict* protocols_items; +#else + iButtonWorker* proto_worker; + iButtonProtocolId protocol_id; // TODO + iButtonProtocols* protocols_items; + iButtonKey* key; +#endif + + const FuzzerProtocol* protocol; + FuzzerWorkerAttackType attack_type; + uint16_t timer_idle_time_ms; + uint16_t timer_emu_time_ms; + + uint8_t payload[MAX_PAYLOAD_SIZE]; + Stream* uids_stream; + uint16_t index; + uint8_t chusen_byte; + + bool treead_running; + bool in_emu_phase; + FuriTimer* timer; + + FuzzerWorkerUidChagedCallback tick_callback; + void* tick_context; + + FuzzerWorkerEndCallback end_callback; + void* end_context; +}; + +static bool fuzzer_worker_load_key(FuzzerWorker* instance, bool next) { + furi_assert(instance); + furi_assert(instance->protocol); + bool res = false; + + const FuzzerProtocol* protocol = instance->protocol; + + switch(instance->attack_type) { + case FuzzerWorkerAttackTypeDefaultDict: + if(next) { + instance->index++; + } + if(instance->index < protocol->dict.len) { + memcpy( + instance->payload, + &protocol->dict.val[instance->index * protocol->data_size], + protocol->data_size); + res = true; + } + break; + + case FuzzerWorkerAttackTypeLoadFileCustomUids: { + if(next) { + instance->index++; + } + uint8_t str_len = protocol->data_size * 2 + 1; + FuriString* data_str = furi_string_alloc(); + while(true) { + furi_string_reset(data_str); + if(!stream_read_line(instance->uids_stream, data_str)) { + stream_rewind(instance->uids_stream); + // TODO Check empty file & close stream and storage + break; + } else if(furi_string_get_char(data_str, 0) == '#') { + // Skip comment string + continue; + } else if(furi_string_size(data_str) != str_len) { + // Ignore strin with bad length + FURI_LOG_W(TAG, "Bad string length"); + continue; + } else { + FURI_LOG_D(TAG, "Uid candidate: \"%s\"", furi_string_get_cstr(data_str)); + bool parse_ok = true; + for(uint8_t i = 0; i < protocol->data_size; i++) { + if(!hex_char_to_uint8( + furi_string_get_cstr(data_str)[i * 2], + furi_string_get_cstr(data_str)[i * 2 + 1], + &instance->payload[i])) { + parse_ok = false; + break; + } + } + res = parse_ok; + } + break; + } + } + + break; + + case FuzzerWorkerAttackTypeLoadFile: + if(instance->payload[instance->index] != 0xFF) { + instance->payload[instance->index]++; + res = true; + } + + break; + + default: + break; + } +#if defined(RFID_125_PROTOCOL) + protocol_dict_set_data( + instance->protocols_items, instance->protocol_id, instance->payload, MAX_PAYLOAD_SIZE); +#else + ibutton_key_set_protocol_id(instance->key, instance->protocol_id); + iButtonEditableData data; + ibutton_protocols_get_editable_data(instance->protocols_items, instance->key, &data); + + // TODO check data.size logic + data.size = MAX_PAYLOAD_SIZE; + memcpy(data.ptr, instance->payload, MAX_PAYLOAD_SIZE); // data.size); +#endif + return res; +} + +static void fuzzer_worker_on_tick_callback(void* context) { + furi_assert(context); + + FuzzerWorker* instance = context; + + if(instance->in_emu_phase) { + if(instance->treead_running) { +#if defined(RFID_125_PROTOCOL) + lfrfid_worker_stop(instance->proto_worker); +#else + ibutton_worker_stop(instance->proto_worker); +#endif + } + instance->in_emu_phase = false; + furi_timer_start(instance->timer, furi_ms_to_ticks(instance->timer_idle_time_ms)); + } else { + if(!fuzzer_worker_load_key(instance, true)) { + fuzzer_worker_pause(instance); // XXX + if(instance->end_callback) { + instance->end_callback(instance->end_context); + } + } else { + if(instance->treead_running) { +#if defined(RFID_125_PROTOCOL) + lfrfid_worker_emulate_start(instance->proto_worker, instance->protocol_id); +#else + ibutton_worker_emulate_start(instance->proto_worker, instance->key); +#endif + } + instance->in_emu_phase = true; + furi_timer_start(instance->timer, furi_ms_to_ticks(instance->timer_emu_time_ms)); + if(instance->tick_callback) { + instance->tick_callback(instance->tick_context); + } + } + } +} + +void fuzzer_worker_get_current_key(FuzzerWorker* instance, FuzzerPayload* output_key) { + furi_assert(instance); + furi_assert(output_key); + furi_assert(instance->protocol); + + output_key->data_size = instance->protocol->data_size; + memcpy(output_key->data, instance->payload, instance->protocol->data_size); +} + +static void fuzzer_worker_set_protocol(FuzzerWorker* instance, FuzzerProtocolsID protocol_index) { + instance->protocol = &fuzzer_proto_items[protocol_index]; + +#if defined(RFID_125_PROTOCOL) + instance->protocol_id = + protocol_dict_get_protocol_by_name(instance->protocols_items, instance->protocol->name); +#else + // TODO iButtonProtocolIdInvalid check + instance->protocol_id = + ibutton_protocols_get_id_by_name(instance->protocols_items, instance->protocol->name); +#endif +} + +bool fuzzer_worker_init_attack_dict(FuzzerWorker* instance, FuzzerProtocolsID protocol_index) { + furi_assert(instance); + + bool res = false; + fuzzer_worker_set_protocol(instance, protocol_index); + + instance->attack_type = FuzzerWorkerAttackTypeDefaultDict; + instance->index = 0; + + if(!fuzzer_worker_load_key(instance, false)) { + instance->attack_type = FuzzerWorkerAttackTypeMax; + } else { + res = true; + } + + return res; +} + +bool fuzzer_worker_init_attack_file_dict( + FuzzerWorker* instance, + FuzzerProtocolsID protocol_index, + FuriString* file_path) { + furi_assert(instance); + furi_assert(file_path); + + bool res = false; + fuzzer_worker_set_protocol(instance, protocol_index); + + Storage* storage = furi_record_open(RECORD_STORAGE); + instance->uids_stream = buffered_file_stream_alloc(storage); + + if(!buffered_file_stream_open( + instance->uids_stream, furi_string_get_cstr(file_path), FSAM_READ, FSOM_OPEN_EXISTING)) { + buffered_file_stream_close(instance->uids_stream); + return res; + } + + instance->attack_type = FuzzerWorkerAttackTypeLoadFileCustomUids; + instance->index = 0; + + if(!fuzzer_worker_load_key(instance, false)) { + instance->attack_type = FuzzerWorkerAttackTypeMax; + buffered_file_stream_close(instance->uids_stream); + furi_record_close(RECORD_STORAGE); + } else { + res = true; + } + + return res; +} + +bool fuzzer_worker_init_attack_bf_byte( + FuzzerWorker* instance, + FuzzerProtocolsID protocol_index, + const FuzzerPayload* new_uid, + uint8_t chusen) { + furi_assert(instance); + + bool res = false; + fuzzer_worker_set_protocol(instance, protocol_index); + + instance->attack_type = FuzzerWorkerAttackTypeLoadFile; + instance->index = chusen; + + memcpy(instance->payload, new_uid->data, instance->protocol->data_size); + + res = true; + + return res; +} + +// TODO make it protocol independent +bool fuzzer_worker_load_key_from_file( + FuzzerWorker* instance, + FuzzerProtocolsID protocol_index, + const char* filename) { + furi_assert(instance); + + bool res = false; + fuzzer_worker_set_protocol(instance, protocol_index); + +#if defined(RFID_125_PROTOCOL) + ProtocolId loaded_proto_id = lfrfid_dict_file_load(instance->protocols_items, filename); + if(loaded_proto_id == PROTOCOL_NO) { + // Err Cant load file + FURI_LOG_W(TAG, "Cant load file"); + } else if(instance->protocol_id != loaded_proto_id) { // Err wrong protocol + FURI_LOG_W(TAG, "Wrong protocol"); + FURI_LOG_W( + TAG, + "Selected: %s Loaded: %s", + instance->protocol->name, + protocol_dict_get_name(instance->protocols_items, loaded_proto_id)); + } else { + protocol_dict_get_data( + instance->protocols_items, instance->protocol_id, instance->payload, MAX_PAYLOAD_SIZE); + res = true; + } +#else + if(!ibutton_protocols_load(instance->protocols_items, instance->key, filename)) { + // Err Cant load file + FURI_LOG_W(TAG, "Cant load file"); + } else { + if(instance->protocol_id != ibutton_key_get_protocol_id(instance->key)) { + // Err wrong protocol + FURI_LOG_W(TAG, "Wrong protocol"); + FURI_LOG_W( + TAG, + "Selected: %s Loaded: %s", + instance->protocol->name, + ibutton_protocols_get_name( + instance->protocols_items, ibutton_key_get_protocol_id(instance->key))); + } else { + iButtonEditableData data; + ibutton_protocols_get_editable_data(instance->protocols_items, instance->key, &data); + memcpy(instance->payload, data.ptr, data.size); + res = true; + } + } +#endif + + return res; +} + +FuzzerWorker* fuzzer_worker_alloc() { + FuzzerWorker* instance = malloc(sizeof(FuzzerWorker)); + +#if defined(RFID_125_PROTOCOL) + instance->protocols_items = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax); + + instance->proto_worker = lfrfid_worker_alloc(instance->protocols_items); +#else + instance->protocols_items = ibutton_protocols_alloc(); + instance->key = + ibutton_key_alloc(ibutton_protocols_get_max_data_size(instance->protocols_items)); + + instance->proto_worker = ibutton_worker_alloc(instance->protocols_items); +#endif + instance->attack_type = FuzzerWorkerAttackTypeMax; + instance->index = 0; + instance->treead_running = false; + instance->in_emu_phase = false; + + memset(instance->payload, 0x00, sizeof(instance->payload)); + + instance->timer_idle_time_ms = PROTOCOL_DEF_IDLE_TIME * 100; + instance->timer_emu_time_ms = PROTOCOL_DEF_EMU_TIME * 100; + + instance->timer = + furi_timer_alloc(fuzzer_worker_on_tick_callback, FuriTimerTypeOnce, instance); + + return instance; +} + +void fuzzer_worker_free(FuzzerWorker* instance) { + furi_assert(instance); + + fuzzer_worker_stop(instance); + + furi_timer_free(instance->timer); + +#if defined(RFID_125_PROTOCOL) + lfrfid_worker_free(instance->proto_worker); + + protocol_dict_free(instance->protocols_items); +#else + ibutton_worker_free(instance->proto_worker); + + ibutton_key_free(instance->key); + ibutton_protocols_free(instance->protocols_items); +#endif + + free(instance); +} + +bool fuzzer_worker_start(FuzzerWorker* instance, uint8_t idle_time, uint8_t emu_time) { + furi_assert(instance); + + if(instance->attack_type < FuzzerWorkerAttackTypeMax) { + if(idle_time == 0) { + instance->timer_idle_time_ms = 10; + } else { + instance->timer_idle_time_ms = idle_time * 100; + } + if(emu_time == 0) { + instance->timer_emu_time_ms = 10; + } else { + instance->timer_emu_time_ms = emu_time * 100; + } + + FURI_LOG_D( + TAG, + "Emu_time %u ms Idle_time %u ms", + instance->timer_emu_time_ms, + instance->timer_idle_time_ms); + + if(!instance->treead_running) { +#if defined(RFID_125_PROTOCOL) + lfrfid_worker_start_thread(instance->proto_worker); +#else + ibutton_worker_start_thread(instance->proto_worker); +#endif + FURI_LOG_D(TAG, "Worker Starting"); + instance->treead_running = true; + } else { + FURI_LOG_D(TAG, "Worker UnPaused"); + } + +#if defined(RFID_125_PROTOCOL) + // lfrfid_worker_start_thread(instance->proto_worker); + lfrfid_worker_emulate_start(instance->proto_worker, instance->protocol_id); +#else + // ibutton_worker_start_thread(instance->proto_worker); + ibutton_worker_emulate_start(instance->proto_worker, instance->key); +#endif + instance->in_emu_phase = true; + furi_timer_start(instance->timer, furi_ms_to_ticks(instance->timer_emu_time_ms)); + return true; + } + return false; +} + +void fuzzer_worker_pause(FuzzerWorker* instance) { + furi_assert(instance); + + furi_timer_stop(instance->timer); + + if(instance->treead_running) { +#if defined(RFID_125_PROTOCOL) + lfrfid_worker_stop(instance->proto_worker); +#else + ibutton_worker_stop(instance->proto_worker); +#endif + FURI_LOG_D(TAG, "Worker Paused"); + } +} + +void fuzzer_worker_stop(FuzzerWorker* instance) { + furi_assert(instance); + + furi_timer_stop(instance->timer); + + if(instance->treead_running) { +#if defined(RFID_125_PROTOCOL) + lfrfid_worker_stop(instance->proto_worker); + lfrfid_worker_stop_thread(instance->proto_worker); +#else + ibutton_worker_stop(instance->proto_worker); + ibutton_worker_stop_thread(instance->proto_worker); +#endif + FURI_LOG_D(TAG, "Worker Stopping"); + instance->treead_running = false; + } + + if(instance->attack_type == FuzzerWorkerAttackTypeLoadFileCustomUids) { + buffered_file_stream_close(instance->uids_stream); + furi_record_close(RECORD_STORAGE); + instance->attack_type = FuzzerWorkerAttackTypeMax; + } + + // TODO anything else +} + +void fuzzer_worker_set_uid_chaged_callback( + FuzzerWorker* instance, + FuzzerWorkerUidChagedCallback callback, + void* context) { + furi_assert(instance); + instance->tick_callback = callback; + instance->tick_context = context; +} + +void fuzzer_worker_set_end_callback( + FuzzerWorker* instance, + FuzzerWorkerEndCallback callback, + void* context) { + furi_assert(instance); + instance->end_callback = callback; + instance->end_context = context; +} diff --git a/applications/external/multi_fuzzer/lib/worker/fake_worker.h b/applications/external/multi_fuzzer/lib/worker/fake_worker.h new file mode 100644 index 0000000000..8b934f300a --- /dev/null +++ b/applications/external/multi_fuzzer/lib/worker/fake_worker.h @@ -0,0 +1,138 @@ +#pragma once + +#include + +#include "protocol.h" + +typedef enum { + FuzzerWorkerAttackTypeDefaultDict = 0, + FuzzerWorkerAttackTypeLoadFile, + FuzzerWorkerAttackTypeLoadFileCustomUids, + + FuzzerWorkerAttackTypeMax, +} FuzzerWorkerAttackType; + +typedef void (*FuzzerWorkerUidChagedCallback)(void* context); +typedef void (*FuzzerWorkerEndCallback)(void* context); + +typedef struct FuzzerWorker FuzzerWorker; + +/** + * Allocate FuzzerWorker + * + * @return FuzzerWorker* pointer to FuzzerWorker + */ +FuzzerWorker* fuzzer_worker_alloc(); + +/** + * Free FuzzerWorker + * + * @param instance Pointer to a FuzzerWorker + */ +void fuzzer_worker_free(FuzzerWorker* instance); + +/** + * Start or continue emulation + * + * @param instance Pointer to a FuzzerWorker + * @param idle_time Delay between emulations in tenths of a second + * @param emu_time Emulation time of one UID in tenths of a second + * @return bool True if emulation has started + */ +bool fuzzer_worker_start(FuzzerWorker* instance, uint8_t idle_time, uint8_t emu_time); + +/** + * Stop emulation and deinit worker + * + * @param instance Pointer to a FuzzerWorker + */ +void fuzzer_worker_stop(FuzzerWorker* instance); + +/** + * Suspend emulation + * + * @param instance Pointer to a FuzzerWorker + */ +void fuzzer_worker_pause(FuzzerWorker* instance); + +/** + * Init attack by default dictionary + * + * @param instance Pointer to a FuzzerWorker + * @param protocol_index index of the selected protocol + * @return bool True if initialization is successful + */ +bool fuzzer_worker_init_attack_dict(FuzzerWorker* instance, FuzzerProtocolsID protocol_index); + +/** + * Init attack by custom dictionary + * + * @param instance Pointer to a FuzzerWorker + * @param protocol_index index of the selected protocol + * @param file_path file path to the dictionary + * @return bool True if initialization is successful + */ +bool fuzzer_worker_init_attack_file_dict( + FuzzerWorker* instance, + FuzzerProtocolsID protocol_index, + FuriString* file_path); + +/** + * Init attack brute force one of byte + * + * @param instance Pointer to a FuzzerWorker + * @param protocol_index index of the selected protocol + * @param new_uid Pointer to a FuzzerPayload with UID for brute force + * @param chosen index of chusen byte + * @return bool True if initialization is successful + */ +bool fuzzer_worker_init_attack_bf_byte( + FuzzerWorker* instance, + FuzzerProtocolsID protocol_index, + const FuzzerPayload* new_uid, + uint8_t chusen); + +/** + * Get current UID + * + * @param instance Pointer to a FuzzerWorker + * @param output_key Pointer to a FuzzerPayload + */ +void fuzzer_worker_get_current_key(FuzzerWorker* instance, FuzzerPayload* output_key); + +/** + * Load UID from Flipper Format Key file + * + * @param instance Pointer to a FuzzerWorker + * @param protocol_index index of the selected protocol + * @param filename file path to the key file + * @return bool True if loading is successful + */ +bool fuzzer_worker_load_key_from_file( + FuzzerWorker* instance, + FuzzerProtocolsID protocol_index, + const char* filename); + +/** + * Set callback for uid changed + * + * @param instance Pointer to a FuzzerWorker + * @param callback Callback for uid changed + * @param context Context for callback + */ +void fuzzer_worker_set_uid_chaged_callback( + FuzzerWorker* instance, + FuzzerWorkerUidChagedCallback callback, + void* context); + +/** + * Set callback for end of emulation + * + * @param instance Pointer to a FuzzerWorker + * @param callback Callback for end of emulation + * @param context Context for callback + */ +void fuzzer_worker_set_end_callback( + FuzzerWorker* instance, + FuzzerWorkerEndCallback callback, + void* context); \ No newline at end of file diff --git a/applications/external/multi_fuzzer/lib/worker/protocol.c b/applications/external/multi_fuzzer/lib/worker/protocol.c new file mode 100644 index 0000000000..a64fe8767d --- /dev/null +++ b/applications/external/multi_fuzzer/lib/worker/protocol.c @@ -0,0 +1,291 @@ +#include "protocol_i.h" +#include "furi.h" + +// ####################### +// ## Ibutton Protocols ## +// ####################### +#define DS1990_DATA_SIZE (8) +#define Metakom_DATA_SIZE (4) +#define Cyfral_DATA_SIZE (2) + +const uint8_t uid_list_ds1990[][DS1990_DATA_SIZE] = { + {0x01, 0xBE, 0x40, 0x11, 0x5A, 0x36, 0x00, 0xE1}, //– код универсального ключа, для Vizit + {0x01, 0xBE, 0x40, 0x11, 0x5A, 0x56, 0x00, 0xBB}, //- проверен работает + {0x01, 0xBE, 0x40, 0x11, 0x00, 0x00, 0x00, 0x77}, //- проверен работает + {0x01, 0xBE, 0x40, 0x11, 0x0A, 0x00, 0x00, 0x1D}, //- проверен работает Визит иногда КЕЙМАНЫ + {0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x2F}, //- проверен(метаком, цифрал, ВИЗИТ). + {0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x9B}, //- проверен Визит, Метакомы, КОНДОР + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x14}, //???-Открываает 98% Метаком и некоторые Цифрал + {0x01, 0x00, 0x00, 0x00, 0x00, 0x90, 0x19, 0xFF}, //???-Отлично работает на старых домофонах + {0x01, 0x6F, 0x2E, 0x88, 0x8A, 0x00, 0x00, 0x4D}, //???-Открывать что-то должен + {0x01, 0x53, 0xD4, 0xFE, 0x00, 0x00, 0x7E, 0x88}, //???-Cyfral, Metakom + {0x01, 0x53, 0xD4, 0xFE, 0x00, 0x00, 0x00, 0x6F}, //???-домофоны Визит (Vizit) - до 99% + {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3D}, //???-домофоны Cyfral CCD-20 - до 70% + {0x01, 0x00, 0xBE, 0x11, 0xAA, 0x00, 0x00, 0xFB}, //???-домофоны Кейман (KEYMAN) + {0x01, 0x76, 0xB8, 0x2E, 0x0F, 0x00, 0x00, 0x5C}, //???-домофоны Форвард + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // Null bytes + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x14}, // Only FF + {0x01, 0x78, 0x00, 0x48, 0xFD, 0xFF, 0xFF, 0xD1}, // StarNew Uni5 + {0x01, 0xA9, 0xE4, 0x3C, 0x09, 0x00, 0x00, 0xE6}, // Eltis Uni +}; + +const uint8_t uid_list_metakom[][Metakom_DATA_SIZE] = { + {0x00, 0x00, 0x00, 0x00}, // Null bytes + {0xFF, 0xFF, 0xFF, 0xFF}, // Only FF + {0x11, 0x11, 0x11, 0x11}, // Only 11 + {0x22, 0x22, 0x22, 0x22}, // Only 22 + {0x33, 0x33, 0x33, 0x33}, // Only 33 + {0x44, 0x44, 0x44, 0x44}, // Only 44 + {0x55, 0x55, 0x55, 0x55}, // Only 55 + {0x66, 0x66, 0x66, 0x66}, // Only 66 + {0x77, 0x77, 0x77, 0x77}, // Only 77 + {0x88, 0x88, 0x88, 0x88}, // Only 88 + {0x99, 0x99, 0x99, 0x99}, // Only 99 + {0x12, 0x34, 0x56, 0x78}, // Incremental UID + {0x9A, 0x78, 0x56, 0x34}, // Decremental UID + {0x04, 0xd0, 0x9b, 0x0d}, // ?? + {0x34, 0x00, 0x29, 0x3d}, // ?? + {0x04, 0xdf, 0x00, 0x00}, // ?? + {0xCA, 0xCA, 0xCA, 0xCA}, // ?? +}; + +const uint8_t uid_list_cyfral[][Cyfral_DATA_SIZE] = { + {0x00, 0x00}, // Null bytes + {0xFF, 0xFF}, // Only FF + {0x11, 0x11}, // Only 11 + {0x22, 0x22}, // Only 22 + {0x33, 0x33}, // Only 33 + {0x44, 0x44}, // Only 44 + {0x55, 0x55}, // Only 55 + {0x66, 0x66}, // Only 66 + {0x77, 0x77}, // Only 77 + {0x88, 0x88}, // Only 88 + {0x99, 0x99}, // Only 99 + {0x12, 0x34}, // Incremental UID + {0x56, 0x34}, // Decremental UID + {0xCA, 0xCA}, // ?? + {0x8E, 0xC9}, // Elevator code + {0x6A, 0x50}, // VERY fresh code from smartkey +}; + +// ########################### +// ## Rfid_125khz Protocols ## +// ########################### +#define EM4100_DATA_SIZE (5) +#define HIDProx_DATA_SIZE (6) +#define PAC_DATA_SIZE (4) +#define H10301_DATA_SIZE (3) + +const uint8_t uid_list_em4100[][EM4100_DATA_SIZE] = { + {0x00, 0x00, 0x00, 0x00, 0x00}, // Null bytes + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, // Only FF + {0x11, 0x11, 0x11, 0x11, 0x11}, // Only 11 + {0x22, 0x22, 0x22, 0x22, 0x22}, // Only 22 + {0x33, 0x33, 0x33, 0x33, 0x33}, // Only 33 + {0x44, 0x44, 0x44, 0x44, 0x44}, // Only 44 + {0x55, 0x55, 0x55, 0x55, 0x55}, // Only 55 + {0x66, 0x66, 0x66, 0x66, 0x66}, // Only 66 + {0x77, 0x77, 0x77, 0x77, 0x77}, // Only 77 + {0x88, 0x88, 0x88, 0x88, 0x88}, // Only 88 + {0x99, 0x99, 0x99, 0x99, 0x99}, // Only 99 + {0x12, 0x34, 0x56, 0x78, 0x9A}, // Incremental UID + {0x9A, 0x78, 0x56, 0x34, 0x12}, // Decremental UID + {0x04, 0xd0, 0x9b, 0x0d, 0x6a}, // From arha + {0x34, 0x00, 0x29, 0x3d, 0x9e}, // From arha + {0x04, 0xdf, 0x00, 0x00, 0x01}, // From arha + {0xCA, 0xCA, 0xCA, 0xCA, 0xCA}, // From arha +}; + +const uint8_t uid_list_hid[][HIDProx_DATA_SIZE] = { + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // Null bytes + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, // Only FF + {0x11, 0x11, 0x11, 0x11, 0x11, 0x11}, // Only 11 + {0x22, 0x22, 0x22, 0x22, 0x22, 0x22}, // Only 22 + {0x33, 0x33, 0x33, 0x33, 0x33, 0x33}, // Only 33 + {0x44, 0x44, 0x44, 0x44, 0x44, 0x44}, // Only 44 + {0x55, 0x55, 0x55, 0x55, 0x55, 0x55}, // Only 55 + {0x66, 0x66, 0x66, 0x66, 0x66, 0x66}, // Only 66 + {0x77, 0x77, 0x77, 0x77, 0x77, 0x77}, // Only 77 + {0x88, 0x88, 0x88, 0x88, 0x88, 0x88}, // Only 88 + {0x99, 0x99, 0x99, 0x99, 0x99, 0x99}, // Only 99 + {0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC}, // Incremental UID + {0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12}, // Decremental UID + {0xCA, 0xCA, 0xCA, 0xCA, 0xCA, 0xCA}, // From arha +}; + +const uint8_t uid_list_pac[][PAC_DATA_SIZE] = { + {0x00, 0x00, 0x00, 0x00}, // Null bytes + {0xFF, 0xFF, 0xFF, 0xFF}, // Only FF + {0x11, 0x11, 0x11, 0x11}, // Only 11 + {0x22, 0x22, 0x22, 0x22}, // Only 22 + {0x33, 0x33, 0x33, 0x33}, // Only 33 + {0x44, 0x44, 0x44, 0x44}, // Only 44 + {0x55, 0x55, 0x55, 0x55}, // Only 55 + {0x66, 0x66, 0x66, 0x66}, // Only 66 + {0x77, 0x77, 0x77, 0x77}, // Only 77 + {0x88, 0x88, 0x88, 0x88}, // Only 88 + {0x99, 0x99, 0x99, 0x99}, // Only 99 + {0x12, 0x34, 0x56, 0x78}, // Incremental UID + {0x9A, 0x78, 0x56, 0x34}, // Decremental UID + {0x04, 0xd0, 0x9b, 0x0d}, // From arha + {0x34, 0x00, 0x29, 0x3d}, // From arha + {0x04, 0xdf, 0x00, 0x00}, // From arha + {0xCA, 0xCA, 0xCA, 0xCA}, // From arha +}; + +const uint8_t uid_list_h10301[][H10301_DATA_SIZE] = { + {0x00, 0x00, 0x00}, // Null bytes + {0xFF, 0xFF, 0xFF}, // Only FF + {0x11, 0x11, 0x11}, // Only 11 + {0x22, 0x22, 0x22}, // Only 22 + {0x33, 0x33, 0x33}, // Only 33 + {0x44, 0x44, 0x44}, // Only 44 + {0x55, 0x55, 0x55}, // Only 55 + {0x66, 0x66, 0x66}, // Only 66 + {0x77, 0x77, 0x77}, // Only 77 + {0x88, 0x88, 0x88}, // Only 88 + {0x99, 0x99, 0x99}, // Only 99 + {0x12, 0x34, 0x56}, // Incremental UID + {0x56, 0x34, 0x12}, // Decremental UID + {0xCA, 0xCA, 0xCA}, // From arha +}; + +#if defined(RFID_125_PROTOCOL) +const FuzzerProtocol fuzzer_proto_items[] = { + [EM4100] = + { + .name = "EM4100", + .data_size = EM4100_DATA_SIZE, + .dict = + { + .val = (const uint8_t*)&uid_list_em4100, + .len = COUNT_OF(uid_list_em4100), + }, + }, + [HIDProx] = + { + .name = "HIDProx", + .data_size = HIDProx_DATA_SIZE, + .dict = + { + .val = (const uint8_t*)&uid_list_hid, + .len = COUNT_OF(uid_list_hid), + }, + }, + [PAC] = + { + .name = "PAC/Stanley", + .data_size = PAC_DATA_SIZE, + .dict = + { + .val = (const uint8_t*)&uid_list_pac, + .len = COUNT_OF(uid_list_pac), + }, + }, + [H10301] = + { + .name = "H10301", + .data_size = H10301_DATA_SIZE, + .dict = + { + .val = (const uint8_t*)&uid_list_h10301, + .len = COUNT_OF(uid_list_h10301), + }, + }, +}; +#else +const FuzzerProtocol fuzzer_proto_items[] = { + [DS1990] = + { + .name = "DS1990", + .data_size = DS1990_DATA_SIZE, + .dict = + { + .val = (const uint8_t*)&uid_list_ds1990, + .len = COUNT_OF(uid_list_ds1990), + }, + }, + [Metakom] = + { + .name = "Metakom", + .data_size = Metakom_DATA_SIZE, + .dict = + { + .val = (const uint8_t*)&uid_list_metakom, + .len = COUNT_OF(uid_list_metakom), + }, + }, + [Cyfral] = + { + .name = "Cyfral", + .data_size = Cyfral_DATA_SIZE, + .dict = + { + .val = (const uint8_t*)&uid_list_cyfral, + .len = COUNT_OF(uid_list_cyfral), + }, + }, +}; +#endif + +typedef struct { + const char* menu_label; + FuzzerAttackId attack_id; +} FuzzerMenuItems; + +const FuzzerMenuItems fuzzer_menu_items[] = { + {"Default Values", FuzzerAttackIdDefaultValues}, +#ifdef RFID_125_PROTOCOL + {"BF Customer ID", FuzzerAttackIdBFCustomerID}, +#endif + {"Load File", FuzzerAttackIdLoadFile}, + {"Load UIDs from file", FuzzerAttackIdLoadFileCustomUids}, +}; + +FuzzerPayload* fuzzer_payload_alloc() { + FuzzerPayload* payload = malloc(sizeof(FuzzerPayload)); + payload->data = malloc(sizeof(payload->data[0]) * MAX_PAYLOAD_SIZE); + + return payload; +} + +void fuzzer_payload_free(FuzzerPayload* payload) { + furi_assert(payload); + + if(payload->data) { + free(payload->data); + } + free(payload); +} + +const char* fuzzer_proto_get_name(FuzzerProtocolsID index) { + return fuzzer_proto_items[index].name; +} + +uint8_t fuzzer_proto_get_count_of_protocols() { + return COUNT_OF(fuzzer_proto_items); +} + +uint8_t fuzzer_proto_get_max_data_size() { + return MAX_PAYLOAD_SIZE; +} + +uint8_t fuzzer_proto_get_def_emu_time() { + return PROTOCOL_DEF_EMU_TIME; +} + +uint8_t fuzzer_proto_get_def_idle_time() { + return PROTOCOL_DEF_IDLE_TIME; +} + +const char* fuzzer_proto_get_menu_label(uint8_t index) { + return fuzzer_menu_items[index].menu_label; +} + +FuzzerAttackId fuzzer_proto_get_attack_id_by_index(uint8_t index) { + return fuzzer_menu_items[index].attack_id; +} + +uint8_t fuzzer_proto_get_count_of_menu_items() { + return COUNT_OF(fuzzer_menu_items); +} diff --git a/applications/external/multi_fuzzer/lib/worker/protocol.h b/applications/external/multi_fuzzer/lib/worker/protocol.h new file mode 100644 index 0000000000..9c5315d00d --- /dev/null +++ b/applications/external/multi_fuzzer/lib/worker/protocol.h @@ -0,0 +1,89 @@ +#pragma once + +#include + +// #define RFID_125_PROTOCOL + +typedef struct FuzzerPayload FuzzerPayload; + +typedef enum { +#if defined(RFID_125_PROTOCOL) + EM4100, + HIDProx, + PAC, + H10301, +#else + DS1990, + Metakom, + Cyfral, +#endif +} FuzzerProtocolsID; + +typedef enum { + FuzzerAttackIdDefaultValues = 0, + FuzzerAttackIdLoadFile, + FuzzerAttackIdLoadFileCustomUids, + FuzzerAttackIdBFCustomerID, +} FuzzerAttackId; + +struct FuzzerPayload { + uint8_t* data; + uint8_t data_size; +}; + +/** + * Allocate FuzzerPayload + * + * @return FuzzerPayload* pointer to FuzzerPayload + */ +FuzzerPayload* fuzzer_payload_alloc(); + +/** + * Free FuzzerPayload + * + * @param instance Pointer to a FuzzerPayload + */ +void fuzzer_payload_free(FuzzerPayload*); + +/** + * Get maximum length of UID among all supported protocols + * @return Maximum length of UID + */ +uint8_t fuzzer_proto_get_max_data_size(); + +// TODO add description +uint8_t fuzzer_proto_get_def_emu_time(); +uint8_t fuzzer_proto_get_def_idle_time(); + +/** + * Get protocol name based on its index + * @param index protocol index + * @return pointer to a string containing the name + */ +const char* fuzzer_proto_get_name(FuzzerProtocolsID index); + +/** + * Get number of protocols + * @return number of protocols + */ +uint8_t fuzzer_proto_get_count_of_protocols(); + +/** + * Get menu label based on its index + * @param index menu index + * @return pointer to a string containing the menu label + */ +const char* fuzzer_proto_get_menu_label(uint8_t index); + +/** + * Get FuzzerAttackId based on its index + * @param index menu index + * @return FuzzerAttackId + */ +FuzzerAttackId fuzzer_proto_get_attack_id_by_index(uint8_t index); + +/** + * Get number of menu items + * @return number of menu items + */ +uint8_t fuzzer_proto_get_count_of_menu_items(); \ No newline at end of file diff --git a/applications/external/multi_fuzzer/lib/worker/protocol_i.h b/applications/external/multi_fuzzer/lib/worker/protocol_i.h new file mode 100644 index 0000000000..2f1c65fd75 --- /dev/null +++ b/applications/external/multi_fuzzer/lib/worker/protocol_i.h @@ -0,0 +1,43 @@ +#pragma once + +#include "protocol.h" + +#if defined(RFID_125_PROTOCOL) +#define MAX_PAYLOAD_SIZE (6) +#define PROTOCOL_DEF_IDLE_TIME (4) +#define PROTOCOL_DEF_EMU_TIME (5) +#define PROTOCOL_TIME_DELAY_MIN PROTOCOL_DEF_IDLE_TIME + PROTOCOL_DEF_EMU_TIME +#else +#define MAX_PAYLOAD_SIZE (8) +#define PROTOCOL_DEF_IDLE_TIME (2) +#define PROTOCOL_DEF_EMU_TIME (2) +#define PROTOCOL_TIME_DELAY_MIN PROTOCOL_DEF_IDLE_TIME + PROTOCOL_DEF_EMU_TIME +#endif + +typedef struct ProtoDict ProtoDict; +typedef struct FuzzerProtocol FuzzerProtocol; + +struct ProtoDict { + const uint8_t* val; + const uint8_t len; +}; + +struct FuzzerProtocol { + const char* name; + const uint8_t data_size; + const ProtoDict dict; +}; + +// #define MAX_PAYLOAD_SIZE 6 + +// #define FUZZ_TIME_DELAY_MIN (5) +// #define FUZZ_TIME_DELAY_DEFAULT (10) +// #define FUZZ_TIME_DELAY_MAX (70) + +// #define MAX_PAYLOAD_SIZE 8 + +// #define FUZZ_TIME_DELAY_MIN (4) +// #define FUZZ_TIME_DELAY_DEFAULT (8) +// #define FUZZ_TIME_DELAY_MAX (80) + +extern const FuzzerProtocol fuzzer_proto_items[]; \ No newline at end of file diff --git a/applications/external/multi_fuzzer/scenes/fuzzer_scene.c b/applications/external/multi_fuzzer/scenes/fuzzer_scene.c new file mode 100644 index 0000000000..0fe0f558dc --- /dev/null +++ b/applications/external/multi_fuzzer/scenes/fuzzer_scene.c @@ -0,0 +1,30 @@ +#include "fuzzer_scene.h" + +// Generate scene on_enter handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, +void (*const fuzzer_scene_on_enter_handlers[])(void*) = { +#include "fuzzer_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_event handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event, +bool (*const fuzzer_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = { +#include "fuzzer_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_exit handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit, +void (*const fuzzer_scene_on_exit_handlers[])(void* context) = { +#include "fuzzer_scene_config.h" +}; +#undef ADD_SCENE + +// Initialize scene handlers configuration structure +const SceneManagerHandlers fuzzer_scene_handlers = { + .on_enter_handlers = fuzzer_scene_on_enter_handlers, + .on_event_handlers = fuzzer_scene_on_event_handlers, + .on_exit_handlers = fuzzer_scene_on_exit_handlers, + .scene_num = FuzzerSceneNum, +}; diff --git a/applications/external/multi_fuzzer/scenes/fuzzer_scene.h b/applications/external/multi_fuzzer/scenes/fuzzer_scene.h new file mode 100644 index 0000000000..2806545105 --- /dev/null +++ b/applications/external/multi_fuzzer/scenes/fuzzer_scene.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +// Generate scene id and total number +#define ADD_SCENE(prefix, name, id) FuzzerScene##id, +typedef enum { +#include "fuzzer_scene_config.h" + FuzzerSceneNum, +} FuzzerScene; +#undef ADD_SCENE + +extern const SceneManagerHandlers fuzzer_scene_handlers; + +// Generate scene on_enter handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); +#include "fuzzer_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_event handlers declaration +#define ADD_SCENE(prefix, name, id) \ + bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event); +#include "fuzzer_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_exit handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context); +#include "fuzzer_scene_config.h" +#undef ADD_SCENE diff --git a/applications/external/multi_fuzzer/scenes/fuzzer_scene_attack.c b/applications/external/multi_fuzzer/scenes/fuzzer_scene_attack.c new file mode 100644 index 0000000000..6424e62b52 --- /dev/null +++ b/applications/external/multi_fuzzer/scenes/fuzzer_scene_attack.c @@ -0,0 +1,162 @@ +#include "../fuzzer_i.h" +#include "../helpers/fuzzer_custom_event.h" + +const NotificationSequence sequence_one_green_50_on_blink_blue = { + &message_red_255, + &message_delay_50, + &message_red_0, + &message_blink_start_10, + &message_blink_set_color_blue, + &message_do_not_reset, + NULL, +}; + +static void fuzzer_scene_attack_update_uid(PacsFuzzerApp* app) { + furi_assert(app); + furi_assert(app->worker); + furi_assert(app->attack_view); + + fuzzer_worker_get_current_key(app->worker, app->payload); + + fuzzer_view_attack_set_uid(app->attack_view, app->payload); +} + +static void fuzzer_scene_attack_set_state(PacsFuzzerApp* app, FuzzerAttackState state) { + furi_assert(app); + + scene_manager_set_scene_state(app->scene_manager, FuzzerSceneAttack, state); + switch(state) { + case FuzzerAttackStateIdle: + notification_message(app->notifications, &sequence_blink_stop); + fuzzer_view_attack_pause(app->attack_view); + break; + + case FuzzerAttackStateRunning: + notification_message(app->notifications, &sequence_blink_start_blue); + fuzzer_view_attack_start(app->attack_view); + break; + + case FuzzerAttackStateEnd: + notification_message(app->notifications, &sequence_blink_stop); + notification_message(app->notifications, &sequence_single_vibro); + fuzzer_view_attack_end(app->attack_view); + break; + + case FuzzerAttackStateOff: + notification_message(app->notifications, &sequence_blink_stop); + fuzzer_view_attack_stop(app->attack_view); + break; + } +} + +void fuzzer_scene_attack_worker_tick_callback(void* context) { + furi_assert(context); + PacsFuzzerApp* app = context; + + notification_message(app->notifications, &sequence_one_green_50_on_blink_blue); + fuzzer_scene_attack_update_uid(app); + + // view_dispatcher_send_custom_event(app->view_dispatcher, FuzzerCustomEventViewAttackTick); +} + +void fuzzer_scene_attack_worker_end_callback(void* context) { + furi_assert(context); + PacsFuzzerApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, FuzzerCustomEventViewAttackEnd); +} + +void fuzzer_scene_attack_callback(FuzzerCustomEvent event, void* context) { + furi_assert(context); + PacsFuzzerApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, event); +} + +void fuzzer_scene_attack_on_enter(void* context) { + furi_assert(context); + PacsFuzzerApp* app = context; + + fuzzer_view_attack_set_callback(app->attack_view, fuzzer_scene_attack_callback, app); + + fuzzer_worker_set_uid_chaged_callback( + app->worker, fuzzer_scene_attack_worker_tick_callback, app); + + fuzzer_worker_set_end_callback(app->worker, fuzzer_scene_attack_worker_end_callback, app); + + fuzzer_view_attack_reset_data( + app->attack_view, + fuzzer_proto_get_menu_label(app->fuzzer_state.menu_index), + fuzzer_proto_get_name(app->fuzzer_state.proto_index)); + + fuzzer_scene_attack_update_uid(app); + + scene_manager_set_scene_state(app->scene_manager, FuzzerSceneAttack, FuzzerAttackStateIdle); + + view_dispatcher_switch_to_view(app->view_dispatcher, FuzzerViewIDAttack); +} + +bool fuzzer_scene_attack_on_event(void* context, SceneManagerEvent event) { + furi_assert(context); + PacsFuzzerApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == FuzzerCustomEventViewAttackBack) { + if(scene_manager_get_scene_state(app->scene_manager, FuzzerSceneAttack) == + FuzzerAttackStateRunning) { + // Pause if attack running + fuzzer_worker_pause(app->worker); + + fuzzer_scene_attack_set_state(app, FuzzerAttackStateIdle); + } else { + // Exit + fuzzer_worker_stop(app->worker); + + fuzzer_scene_attack_set_state(app, FuzzerAttackStateOff); + if(!scene_manager_previous_scene(app->scene_manager)) { + scene_manager_stop(app->scene_manager); + view_dispatcher_stop(app->view_dispatcher); + } + } + consumed = true; + } else if(event.event == FuzzerCustomEventViewAttackOk) { + if(scene_manager_get_scene_state(app->scene_manager, FuzzerSceneAttack) == + FuzzerAttackStateIdle) { + // Start or Continue Attack + if(fuzzer_worker_start( + app->worker, + fuzzer_view_attack_get_time_delay(app->attack_view), + fuzzer_view_attack_get_emu_time(app->attack_view))) { + fuzzer_scene_attack_set_state(app, FuzzerAttackStateRunning); + } else { + // Error? + } + } else if( + scene_manager_get_scene_state(app->scene_manager, FuzzerSceneAttack) == + FuzzerAttackStateRunning) { + // Pause if attack running + fuzzer_worker_pause(app->worker); + + fuzzer_scene_attack_set_state(app, FuzzerAttackStateIdle); + } + consumed = true; + // } else if(event.event == FuzzerCustomEventViewAttackTick) { + // consumed = true; + } else if(event.event == FuzzerCustomEventViewAttackEnd) { + fuzzer_scene_attack_set_state(app, FuzzerAttackStateEnd); + consumed = true; + } + } + + return consumed; +} + +void fuzzer_scene_attack_on_exit(void* context) { + furi_assert(context); + PacsFuzzerApp* app = context; + + // XXX the scene has no descendants, and the return will be processed in on_event + // fuzzer_worker_stop(); + + fuzzer_worker_set_uid_chaged_callback(app->worker, NULL, NULL); + fuzzer_worker_set_end_callback(app->worker, NULL, NULL); +} diff --git a/applications/external/multi_fuzzer/scenes/fuzzer_scene_config.h b/applications/external/multi_fuzzer/scenes/fuzzer_scene_config.h new file mode 100644 index 0000000000..711ebe1c42 --- /dev/null +++ b/applications/external/multi_fuzzer/scenes/fuzzer_scene_config.h @@ -0,0 +1,3 @@ +ADD_SCENE(fuzzer, main, Main) +ADD_SCENE(fuzzer, attack, Attack) +ADD_SCENE(fuzzer, field_editor, FieldEditor) \ No newline at end of file diff --git a/applications/external/multi_fuzzer/scenes/fuzzer_scene_field_editor.c b/applications/external/multi_fuzzer/scenes/fuzzer_scene_field_editor.c new file mode 100644 index 0000000000..ccea123dc9 --- /dev/null +++ b/applications/external/multi_fuzzer/scenes/fuzzer_scene_field_editor.c @@ -0,0 +1,66 @@ +#include "../fuzzer_i.h" +#include "../helpers/fuzzer_custom_event.h" + +void fuzzer_scene_field_editor_callback(FuzzerCustomEvent event, void* context) { + furi_assert(context); + PacsFuzzerApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, event); +} + +void fuzzer_scene_field_editor_on_enter(void* context) { + furi_assert(context); + PacsFuzzerApp* app = context; + + fuzzer_view_field_editor_set_callback( + app->field_editor_view, fuzzer_scene_field_editor_callback, app); + + fuzzer_worker_get_current_key(app->worker, app->payload); + + switch(scene_manager_get_scene_state(app->scene_manager, FuzzerSceneFieldEditor)) { + case FuzzerFieldEditorStateEditingOn: + fuzzer_view_field_editor_reset_data(app->field_editor_view, app->payload, true); + break; + + case FuzzerFieldEditorStateEditingOff: + fuzzer_view_field_editor_reset_data(app->field_editor_view, app->payload, false); + break; + + default: + break; + } + + view_dispatcher_switch_to_view(app->view_dispatcher, FuzzerViewIDFieldEditor); +} + +bool fuzzer_scene_field_editor_on_event(void* context, SceneManagerEvent event) { + furi_assert(context); + PacsFuzzerApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == FuzzerCustomEventViewFieldEditorBack) { + if(!scene_manager_previous_scene(app->scene_manager)) { + scene_manager_stop(app->scene_manager); + view_dispatcher_stop(app->view_dispatcher); + } + consumed = true; + } else if(event.event == FuzzerCustomEventViewFieldEditorOk) { + fuzzer_view_field_editor_get_uid(app->field_editor_view, app->payload); + if(fuzzer_worker_init_attack_bf_byte( + app->worker, + app->fuzzer_state.proto_index, + app->payload, + fuzzer_view_field_editor_get_index(app->field_editor_view))) { + scene_manager_next_scene(app->scene_manager, FuzzerSceneAttack); + } + } + } + + return consumed; +} + +void fuzzer_scene_field_editor_on_exit(void* context) { + // furi_assert(context); + // PacsFuzzerApp* app = context; + UNUSED(context); +} diff --git a/applications/external/multi_fuzzer/scenes/fuzzer_scene_main.c b/applications/external/multi_fuzzer/scenes/fuzzer_scene_main.c new file mode 100644 index 0000000000..0d074a1219 --- /dev/null +++ b/applications/external/multi_fuzzer/scenes/fuzzer_scene_main.c @@ -0,0 +1,189 @@ +#include "../fuzzer_i.h" +#include "../helpers/fuzzer_custom_event.h" + +#include "../lib/worker/protocol.h" + +void fuzzer_scene_main_callback(FuzzerCustomEvent event, void* context) { + furi_assert(context); + PacsFuzzerApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, event); +} + +void fuzzer_scene_main_error_popup_callback(void* context) { + PacsFuzzerApp* app = context; + notification_message(app->notifications, &sequence_reset_rgb); + view_dispatcher_send_custom_event(app->view_dispatcher, FuzzerCustomEventViewMainPopupErr); +} + +static bool fuzzer_scene_main_load_custom_dict(void* context) { + furi_assert(context); + PacsFuzzerApp* app = context; + + FuzzerConsts* consts = app->fuzzer_const; + + furi_string_set_str(app->file_path, consts->custom_dict_folder); + + DialogsFileBrowserOptions browser_options; + dialog_file_browser_set_basic_options( + &browser_options, consts->custom_dict_extension, &I_rfid_10px); + browser_options.base_path = consts->custom_dict_folder; + browser_options.hide_ext = false; + + bool res = + dialog_file_browser_show(app->dialogs, app->file_path, app->file_path, &browser_options); + + return res; +} + +static bool fuzzer_scene_main_load_key(void* context) { + furi_assert(context); + PacsFuzzerApp* app = context; + + FuzzerConsts* consts = app->fuzzer_const; + + furi_string_set_str(app->file_path, consts->path_key_folder); + + DialogsFileBrowserOptions browser_options; + dialog_file_browser_set_basic_options( + &browser_options, consts->key_extension, consts->key_icon); + browser_options.base_path = consts->path_key_folder; + browser_options.hide_ext = true; + + bool res = + dialog_file_browser_show(app->dialogs, app->file_path, app->file_path, &browser_options); + + return res; +} + +static void fuzzer_scene_main_show_error(void* context, const char* erre_str) { + furi_assert(context); + PacsFuzzerApp* app = context; + popup_set_header(app->popup, erre_str, 64, 20, AlignCenter, AlignTop); + notification_message(app->notifications, &sequence_set_red_255); + notification_message(app->notifications, &sequence_double_vibro); + view_dispatcher_switch_to_view(app->view_dispatcher, FuzzerViewIDPopup); +} + +void fuzzer_scene_main_on_enter(void* context) { + furi_assert(context); + PacsFuzzerApp* app = context; + + fuzzer_view_main_set_callback(app->main_view, fuzzer_scene_main_callback, app); + + fuzzer_view_main_update_data(app->main_view, app->fuzzer_state); + + // Setup view + Popup* popup = app->popup; + // popup_set_icon(popup, 72, 17, &I_DolphinCommon_56x48); + popup_set_timeout(popup, 2500); + popup_set_context(popup, app); + popup_set_callback(popup, fuzzer_scene_main_error_popup_callback); + popup_enable_timeout(popup); + + view_dispatcher_switch_to_view(app->view_dispatcher, FuzzerViewIDMain); +} + +bool fuzzer_scene_main_on_event(void* context, SceneManagerEvent event) { + furi_assert(context); + PacsFuzzerApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == FuzzerCustomEventViewMainBack) { + if(!scene_manager_previous_scene(app->scene_manager)) { + scene_manager_stop(app->scene_manager); + view_dispatcher_stop(app->view_dispatcher); + } + consumed = true; + } else if(event.event == FuzzerCustomEventViewMainPopupErr) { + view_dispatcher_switch_to_view(app->view_dispatcher, FuzzerViewIDMain); + consumed = true; + } else if(event.event == FuzzerCustomEventViewMainOk) { + fuzzer_view_main_get_state(app->main_view, &app->fuzzer_state); + + // TODO error logic + bool loading_ok = false; + + switch(fuzzer_proto_get_attack_id_by_index(app->fuzzer_state.menu_index)) { + case FuzzerAttackIdDefaultValues: + loading_ok = + fuzzer_worker_init_attack_dict(app->worker, app->fuzzer_state.proto_index); + + if(!loading_ok) { + // error + fuzzer_scene_main_show_error(app, "Default dictionary\nis empty"); + } + break; + + case FuzzerAttackIdBFCustomerID: + // TODO + app->payload->data_size = fuzzer_proto_get_max_data_size(); + memset(app->payload->data, 0x00, app->payload->data_size); + + if(fuzzer_worker_init_attack_bf_byte( + app->worker, app->fuzzer_state.proto_index, app->payload, 0)) { + scene_manager_set_scene_state( + app->scene_manager, + FuzzerSceneFieldEditor, + FuzzerFieldEditorStateEditingOff); + scene_manager_next_scene(app->scene_manager, FuzzerSceneFieldEditor); + + } else { + // error + } + break; + + case FuzzerAttackIdLoadFile: + if(!fuzzer_scene_main_load_key(app)) { + break; + } else { + if(fuzzer_worker_load_key_from_file( + app->worker, + app->fuzzer_state.proto_index, + furi_string_get_cstr(app->file_path))) { + scene_manager_set_scene_state( + app->scene_manager, + FuzzerSceneFieldEditor, + FuzzerFieldEditorStateEditingOn); + scene_manager_next_scene(app->scene_manager, FuzzerSceneFieldEditor); + FURI_LOG_I("Scene", "Load ok"); + } else { + fuzzer_scene_main_show_error(app, "Unsupported protocol\nor broken file"); + FURI_LOG_W("Scene", "Load err"); + } + } + break; + + case FuzzerAttackIdLoadFileCustomUids: + if(!fuzzer_scene_main_load_custom_dict(app)) { + break; + } else { + loading_ok = fuzzer_worker_init_attack_file_dict( + app->worker, app->fuzzer_state.proto_index, app->file_path); + if(!loading_ok) { + fuzzer_scene_main_show_error(app, "Incorrect key format\nor length"); + // error + } + } + break; + + default: + fuzzer_scene_main_show_error(app, "Unsuported attack"); + break; + } + + if(loading_ok) { + scene_manager_next_scene(app->scene_manager, FuzzerSceneAttack); + } + consumed = true; + } + } + + return consumed; +} + +void fuzzer_scene_main_on_exit(void* context) { + // furi_assert(context); + // PacsFuzzerApp* app = context; + UNUSED(context); +} diff --git a/applications/external/multi_fuzzer/todo.md b/applications/external/multi_fuzzer/todo.md new file mode 100644 index 0000000000..4e3b0e17de --- /dev/null +++ b/applications/external/multi_fuzzer/todo.md @@ -0,0 +1,44 @@ +## Working Improvement + +#### Quality of life + +- [ ] Make the "Load File" independent of the current protocol +- [x] Add pause + - [ ] Switching UIDs if possible +- [x] Led and sound Notification + - [x] Led + - [x] Vibro + - [ ] Sound? +- [x] Error Notification + - [x] Custom UIDs dict loading + - [x] Key file loading + - [ ] Anything else + +#### App functionality + +- [x] Add `BFCustomerID` attack + - [x] Add the ability to select index +- [ ] Save key logic + +## Code Improvement + +- [ ] GUI + - [x] Rewrite `gui_const` logic + - [x] Icon in dialog + - [x] Description and buttons in `field_editor` view + - [ ] Protocol carousel in `main_menu` + - [x] prototype + - [x] Add the ability to edit emulation time and downtime separately + - [x] Decide on the display +- [x] UID + - [x] Simplify the storage and exchange of `uids.data` `uid.data_size` in `views` + - [x] Using `FuzzerPayload` to store the uid + - [x] `UID_MAX_SIZE` +- [x] Add pause + - [x] Fix `Custom dict` attack when ended +- [ ] Pause V2 + - [ ] Save logic + - [ ] Switching UIDs if possible +- [ ] Worker + - [ ] Use `prtocol_id` instead of protocol name + - [x] this can be simplified `fuzzer_proto_items` \ No newline at end of file diff --git a/applications/external/multi_fuzzer/views/attack.c b/applications/external/multi_fuzzer/views/attack.c new file mode 100644 index 0000000000..87aa9f659c --- /dev/null +++ b/applications/external/multi_fuzzer/views/attack.c @@ -0,0 +1,384 @@ +#include "attack.h" +#include "../fuzzer_i.h" + +#include +#include + +#define ATTACK_SCENE_MAX_UID_LENGTH 25 +#define UID_MAX_DISPLAYED_LEN (8U) +#define LIFT_RIGHT_OFFSET (3) + +struct FuzzerViewAttack { + View* view; + FuzzerViewAttackCallback callback; + void* context; +}; + +typedef struct { + uint8_t time_delay; // 1 = 100ms + uint8_t time_delay_min; // 1 = 100ms + uint8_t emu_time; // 1 = 100ms + uint8_t emu_time_min; // 1 = 100ms + bool td_emt_cursor; // false - time_delay, true - emu_time + const char* attack_name; + const char* protocol_name; + FuzzerAttackState attack_state; + FuriString* uid_str; +} FuzzerViewAttackModel; + +void fuzzer_view_attack_reset_data( + FuzzerViewAttack* view, + const char* attack_name, + const char* protocol_name) { + furi_assert(view); + + with_view_model( + view->view, + FuzzerViewAttackModel * model, + { + model->attack_name = attack_name; + model->protocol_name = protocol_name; + model->attack_state = FuzzerAttackStateIdle; + furi_string_set_str(model->uid_str, "Not_set"); + }, + true); +} + +void fuzzer_view_attack_set_uid(FuzzerViewAttack* view, const FuzzerPayload* uid) { + furi_assert(view); + furi_assert(uid->data); + + with_view_model( + view->view, + FuzzerViewAttackModel * model, + { + furi_string_printf(model->uid_str, "%02X", uid->data[0]); + for(uint8_t i = 1; i < uid->data_size; i++) { + furi_string_cat_printf(model->uid_str, ":%02X", uid->data[i]); + } + }, + true); +} + +void fuzzer_view_attack_start(FuzzerViewAttack* view) { + furi_assert(view); + + with_view_model( + view->view, + FuzzerViewAttackModel * model, + { model->attack_state = FuzzerAttackStateRunning; }, + true); +} + +void fuzzer_view_attack_stop(FuzzerViewAttack* view) { + furi_assert(view); + + with_view_model( + view->view, + FuzzerViewAttackModel * model, + { model->attack_state = FuzzerAttackStateOff; }, + true); +} + +void fuzzer_view_attack_pause(FuzzerViewAttack* view) { + furi_assert(view); + + with_view_model( + view->view, + FuzzerViewAttackModel * model, + { model->attack_state = FuzzerAttackStateIdle; }, + true); +} + +void fuzzer_view_attack_end(FuzzerViewAttack* view) { + furi_assert(view); + + with_view_model( + view->view, + FuzzerViewAttackModel * model, + { model->attack_state = FuzzerAttackStateEnd; }, + true); +} + +void fuzzer_view_attack_set_callback( + FuzzerViewAttack* view_attack, + FuzzerViewAttackCallback callback, + void* context) { + furi_assert(view_attack); + + view_attack->callback = callback; + view_attack->context = context; +} + +void fuzzer_view_attack_draw(Canvas* canvas, FuzzerViewAttackModel* model) { + char temp_str[50]; + + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 64, 2, AlignCenter, AlignTop, model->attack_name); + + uint16_t crt; + canvas_set_font(canvas, FontPrimary); + + if(!model->td_emt_cursor) { + canvas_set_font(canvas, FontSecondary); + snprintf(temp_str, sizeof(temp_str), "Time delay:"); + canvas_draw_str_aligned(canvas, LIFT_RIGHT_OFFSET, 21, AlignLeft, AlignBottom, temp_str); + crt = canvas_string_width(canvas, temp_str); + + canvas_set_font(canvas, FontPrimary); + snprintf( + temp_str, sizeof(temp_str), "%d.%d", model->time_delay / 10, model->time_delay % 10); + canvas_draw_str_aligned( + canvas, crt + LIFT_RIGHT_OFFSET + 3, 21, AlignLeft, AlignBottom, temp_str); + + canvas_set_font(canvas, FontSecondary); + snprintf( + temp_str, sizeof(temp_str), "EmT: %d.%d", model->emu_time / 10, model->emu_time % 10); + canvas_draw_str_aligned( + canvas, 128 - LIFT_RIGHT_OFFSET, 21, AlignRight, AlignBottom, temp_str); + } else { + canvas_set_font(canvas, FontSecondary); + snprintf( + temp_str, + sizeof(temp_str), + "TD: %d.%d", + model->time_delay / 10, + model->time_delay % 10); + + canvas_draw_str_aligned(canvas, LIFT_RIGHT_OFFSET, 21, AlignLeft, AlignBottom, temp_str); + + canvas_set_font(canvas, FontPrimary); + snprintf(temp_str, sizeof(temp_str), "%d.%d", model->emu_time / 10, model->emu_time % 10); + canvas_draw_str_aligned( + canvas, 128 - LIFT_RIGHT_OFFSET, 21, AlignRight, AlignBottom, temp_str); + crt = canvas_string_width(canvas, temp_str); + + canvas_set_font(canvas, FontSecondary); + snprintf(temp_str, sizeof(temp_str), "Emulation time:"); + canvas_draw_str_aligned( + canvas, 128 - LIFT_RIGHT_OFFSET - crt - 3, 21, AlignRight, AlignBottom, temp_str); + } + + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned(canvas, 64, 26, AlignCenter, AlignTop, model->protocol_name); + + canvas_set_font(canvas, FontPrimary); + if(128 < canvas_string_width(canvas, furi_string_get_cstr(model->uid_str))) { + canvas_set_font(canvas, FontSecondary); + } + canvas_draw_str_aligned( + canvas, 64, 38, AlignCenter, AlignTop, furi_string_get_cstr(model->uid_str)); + + canvas_set_font(canvas, FontSecondary); + if(model->attack_state == FuzzerAttackStateRunning) { + elements_button_center(canvas, "Stop"); + } else if(model->attack_state == FuzzerAttackStateIdle) { + if(model->td_emt_cursor) { + elements_button_center(canvas, "Start"); + elements_button_left(canvas, "EmT -"); + elements_button_right(canvas, "+ EmT"); + } else { + elements_button_center(canvas, "Start"); + elements_button_left(canvas, "TD -"); + elements_button_right(canvas, "+ TD"); + } + + } else if(model->attack_state == FuzzerAttackStateEnd) { + // elements_button_center(canvas, "Restart"); // Reset + elements_button_left(canvas, "Exit"); + } +} + +bool fuzzer_view_attack_input(InputEvent* event, void* context) { + furi_assert(context); + FuzzerViewAttack* view_attack = context; + + if(event->key == InputKeyBack && event->type == InputTypeShort) { + view_attack->callback(FuzzerCustomEventViewAttackBack, view_attack->context); + return true; + } else if(event->key == InputKeyOk && event->type == InputTypeShort) { + view_attack->callback(FuzzerCustomEventViewAttackOk, view_attack->context); + return true; + } else if(event->key == InputKeyLeft) { + with_view_model( + view_attack->view, + FuzzerViewAttackModel * model, + { + if(model->attack_state == FuzzerAttackStateIdle) { + if(!model->td_emt_cursor) { + // TimeDelay -- + if(event->type == InputTypeShort) { + if(model->time_delay > model->time_delay_min) { + model->time_delay--; + } + } else if(event->type == InputTypeLong) { + if((model->time_delay - 10) >= model->time_delay_min) { + model->time_delay -= 10; + } else { + model->time_delay = model->time_delay_min; + } + } + } else { + // EmuTime -- + if(event->type == InputTypeShort) { + if(model->emu_time > model->emu_time_min) { + model->emu_time--; + } + } else if(event->type == InputTypeLong) { + if((model->emu_time - 10) >= model->emu_time_min) { + model->emu_time -= 10; + } else { + model->emu_time = model->emu_time_min; + } + } + } + } else if( + (model->attack_state == FuzzerAttackStateEnd) && + (event->type == InputTypeShort)) { + // Exit if Ended + view_attack->callback(FuzzerCustomEventViewAttackBack, view_attack->context); + } + }, + true); + return true; + } else if(event->key == InputKeyRight) { + with_view_model( + view_attack->view, + FuzzerViewAttackModel * model, + { + if(model->attack_state == FuzzerAttackStateIdle) { + if(!model->td_emt_cursor) { + // TimeDelay ++ + if(event->type == InputTypeShort) { + if(model->time_delay < FUZZ_TIME_DELAY_MAX) { + model->time_delay++; + } + } else if(event->type == InputTypeLong) { + model->time_delay += 10; + if(model->time_delay > FUZZ_TIME_DELAY_MAX) { + model->time_delay = FUZZ_TIME_DELAY_MAX; + } + } + } else { + // EmuTime ++ + if(event->type == InputTypeShort) { + if(model->emu_time < FUZZ_TIME_DELAY_MAX) { + model->emu_time++; + } + } else if(event->type == InputTypeLong) { + model->emu_time += 10; + if(model->emu_time > FUZZ_TIME_DELAY_MAX) { + model->emu_time = FUZZ_TIME_DELAY_MAX; + } + } + } + } else { + // Nothing + } + }, + true); + return true; + } else if( + (event->key == InputKeyUp || event->key == InputKeyDown) && + event->type == InputTypeShort) { + with_view_model( + view_attack->view, + FuzzerViewAttackModel * model, + { model->td_emt_cursor = !model->td_emt_cursor; }, + true); + return true; + } + + return true; +} + +void fuzzer_view_attack_enter(void* context) { + furi_assert(context); +} + +void fuzzer_view_attack_exit(void* context) { + furi_assert(context); + FuzzerViewAttack* view_attack = context; + with_view_model( + view_attack->view, FuzzerViewAttackModel * model, { model->td_emt_cursor = false; }, true); +} + +FuzzerViewAttack* fuzzer_view_attack_alloc() { + if(fuzzer_proto_get_max_data_size() > UID_MAX_DISPLAYED_LEN) { + furi_crash("Maximum of displayed bytes exceeded"); + } + + FuzzerViewAttack* view_attack = malloc(sizeof(FuzzerViewAttack)); + + // View allocation and configuration + view_attack->view = view_alloc(); + view_allocate_model(view_attack->view, ViewModelTypeLocking, sizeof(FuzzerViewAttackModel)); + view_set_context(view_attack->view, view_attack); + view_set_draw_callback(view_attack->view, (ViewDrawCallback)fuzzer_view_attack_draw); + view_set_input_callback(view_attack->view, fuzzer_view_attack_input); + view_set_enter_callback(view_attack->view, fuzzer_view_attack_enter); + view_set_exit_callback(view_attack->view, fuzzer_view_attack_exit); + + with_view_model( + view_attack->view, + FuzzerViewAttackModel * model, + { + model->time_delay = fuzzer_proto_get_def_idle_time(); + model->time_delay_min = 0; // model->time_delay; + + model->emu_time = fuzzer_proto_get_def_emu_time(); + + model->emu_time_min = 2; // model->emu_time; + + model->uid_str = furi_string_alloc_set_str("Not_set"); + // malloc(ATTACK_SCENE_MAX_UID_LENGTH + 1); + model->attack_state = FuzzerAttackStateOff; + model->td_emt_cursor = false; + + // strcpy(model->uid_str, "Not_set"); + model->attack_name = "Not_set"; + model->protocol_name = "Not_set"; + }, + true); + return view_attack; +} + +void fuzzer_view_attack_free(FuzzerViewAttack* view_attack) { + furi_assert(view_attack); + + with_view_model( + view_attack->view, + FuzzerViewAttackModel * model, + { furi_string_free(model->uid_str); }, + true); + view_free(view_attack->view); + free(view_attack); +} + +View* fuzzer_view_attack_get_view(FuzzerViewAttack* view_attack) { + furi_assert(view_attack); + return view_attack->view; +} + +uint8_t fuzzer_view_attack_get_time_delay(FuzzerViewAttack* view) { + furi_assert(view); + uint8_t time_delay; + + with_view_model( + view->view, FuzzerViewAttackModel * model, { time_delay = model->time_delay; }, false); + + return time_delay; +} + +uint8_t fuzzer_view_attack_get_emu_time(FuzzerViewAttack* view) { + furi_assert(view); + uint8_t emu_time; + + with_view_model( + view->view, FuzzerViewAttackModel * model, { emu_time = model->emu_time; }, false); + + return emu_time; +} \ No newline at end of file diff --git a/applications/external/multi_fuzzer/views/attack.h b/applications/external/multi_fuzzer/views/attack.h new file mode 100644 index 0000000000..66e96d7d6c --- /dev/null +++ b/applications/external/multi_fuzzer/views/attack.h @@ -0,0 +1,42 @@ +#pragma once + +#include + +#include "../helpers/fuzzer_custom_event.h" +#include "../helpers/fuzzer_types.h" + +#include "../lib/worker/protocol.h" + +typedef struct FuzzerViewAttack FuzzerViewAttack; + +typedef void (*FuzzerViewAttackCallback)(FuzzerCustomEvent event, void* context); + +void fuzzer_view_attack_set_callback( + FuzzerViewAttack* view_attack, + FuzzerViewAttackCallback callback, + void* context); + +FuzzerViewAttack* fuzzer_view_attack_alloc(); + +void fuzzer_view_attack_free(FuzzerViewAttack* view_attack); + +View* fuzzer_view_attack_get_view(FuzzerViewAttack* view_attack); + +void fuzzer_view_attack_reset_data( + FuzzerViewAttack* view, + const char* attack_name, + const char* protocol_name); + +void fuzzer_view_attack_set_uid(FuzzerViewAttack* view, const FuzzerPayload* uid); + +void fuzzer_view_attack_start(FuzzerViewAttack* view); + +void fuzzer_view_attack_stop(FuzzerViewAttack* view); + +void fuzzer_view_attack_pause(FuzzerViewAttack* view); + +void fuzzer_view_attack_end(FuzzerViewAttack* view); + +uint8_t fuzzer_view_attack_get_time_delay(FuzzerViewAttack* view); + +uint8_t fuzzer_view_attack_get_emu_time(FuzzerViewAttack* view); \ No newline at end of file diff --git a/applications/external/multi_fuzzer/views/field_editor.c b/applications/external/multi_fuzzer/views/field_editor.c new file mode 100644 index 0000000000..bdce0a516e --- /dev/null +++ b/applications/external/multi_fuzzer/views/field_editor.c @@ -0,0 +1,358 @@ +#include "field_editor.h" +#include "../fuzzer_i.h" + +#include +#include +#include + +#define FIELD_EDITOR_V2 + +#define GUI_DISPLAY_WIDTH 128 +#define GUI_DISPLAY_HEIGHT 64 + +#define GUI_DISPLAY_HORIZONTAL_CENTER 64 +#define GUI_DISPLAY_VERTICAL_CENTER 32 + +#define UID_STR_LENGTH 25 + +#ifdef FIELD_EDITOR_V2 +#define EDITOR_STRING_Y 38 +#else +#define EDITOR_STRING_Y 50 +#endif + +struct FuzzerViewFieldEditor { + View* view; + FuzzerViewFieldEditorCallback callback; + void* context; +}; + +typedef struct { + uint8_t* uid; + uint8_t uid_size; + + FuriString* uid_str; + + uint8_t index; + bool lo; + bool allow_edit; +} FuzzerViewFieldEditorModel; + +void fuzzer_view_field_editor_set_callback( + FuzzerViewFieldEditor* view_edit, + FuzzerViewFieldEditorCallback callback, + void* context) { + furi_assert(view_edit); + + view_edit->callback = callback; + view_edit->context = context; +} + +void fuzzer_view_field_editor_reset_data( + FuzzerViewFieldEditor* view_edit, + const FuzzerPayload* new_uid, + bool allow_edit) { + furi_assert(view_edit); + furi_assert(new_uid->data); + + with_view_model( + view_edit->view, + FuzzerViewFieldEditorModel * model, + { + memcpy(model->uid, new_uid->data, new_uid->data_size); + model->index = 0; + model->lo = false; + model->uid_size = new_uid->data_size; + model->allow_edit = allow_edit; + }, + true); +} + +void fuzzer_view_field_editor_get_uid(FuzzerViewFieldEditor* view_edit, FuzzerPayload* output_uid) { + furi_assert(view_edit); + furi_assert(output_uid); + with_view_model( + view_edit->view, + FuzzerViewFieldEditorModel * model, + { + output_uid->data_size = model->uid_size; + memcpy(output_uid->data, model->uid, model->uid_size); + }, + true); +} + +uint8_t fuzzer_view_field_editor_get_index(FuzzerViewFieldEditor* view_edit) { + furi_assert(view_edit); + uint8_t index; + with_view_model( + view_edit->view, FuzzerViewFieldEditorModel * model, { index = model->index; }, true); + return index; +} + +void fuzzer_view_field_editor_draw(Canvas* canvas, FuzzerViewFieldEditorModel* model) { + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + +#ifdef FIELD_EDITOR_V2 + + canvas_set_font(canvas, FontSecondary); + if(model->allow_edit) { + canvas_draw_icon(canvas, 2, 4, &I_ButtonLeft_4x7); + canvas_draw_icon(canvas, 8, 4, &I_ButtonRight_4x7); + + canvas_draw_icon_ex(canvas, 62, 3, &I_Pin_arrow_up_7x9, IconRotation180); + canvas_draw_icon(canvas, 69, 3, &I_Pin_arrow_up_7x9); + + canvas_draw_str(canvas, 14, 10, "select byte"); + canvas_draw_str(canvas, 79, 10, "adjust byte"); + } else { + canvas_draw_icon(canvas, 35, 4, &I_ButtonLeft_4x7); + canvas_draw_icon(canvas, 41, 4, &I_ButtonRight_4x7); + canvas_draw_str(canvas, 49, 10, "select byte"); + } + + char msg_index[18]; + canvas_set_font(canvas, FontPrimary); + snprintf(msg_index, sizeof(msg_index), "Field index : %d", model->index); + + canvas_draw_str_aligned( + canvas, GUI_DISPLAY_HORIZONTAL_CENTER, 24, AlignCenter, AlignBottom, msg_index); + + canvas_set_font(canvas, FontSecondary); + canvas_draw_icon(canvas, 4, 52, &I_Pin_back_arrow_10x8); + canvas_draw_icon(canvas, 85, 52, &I_Ok_btn_9x9); + + canvas_draw_str(canvas, 16, 60, "Back"); + canvas_draw_str(canvas, 96, 60, "Attack"); +#else + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned( + canvas, + GUI_DISPLAY_HORIZONTAL_CENTER, + 5, + AlignCenter, + AlignTop, + "Left and right: select byte"); + canvas_draw_str_aligned( + canvas, + GUI_DISPLAY_HORIZONTAL_CENTER, + 15, + AlignCenter, + AlignTop, + "Up and down: adjust byte"); + + char msg_index[18]; + canvas_set_font(canvas, FontPrimary); + snprintf(msg_index, sizeof(msg_index), "Field index : %d", model->index); + canvas_draw_str_aligned( + canvas, GUI_DISPLAY_HORIZONTAL_CENTER, 28, AlignCenter, AlignTop, msg_index); +#endif + // ####### Editor ####### + FuriString* temp_s = model->uid_str; + canvas_set_font(canvas, FontSecondary); + + furi_string_reset(temp_s); + for(int i = -3; i != 0; i++) { + if(0 <= (model->index + i)) { + furi_string_cat_printf(temp_s, "%02X ", model->uid[model->index + i]); + } + } + canvas_draw_str_aligned( + canvas, 52, EDITOR_STRING_Y, AlignRight, AlignBottom, furi_string_get_cstr(temp_s)); + + furi_string_reset(temp_s); + for(int i = 1; i != 4; i++) { + if((model->index + i) < model->uid_size) { + furi_string_cat_printf(temp_s, " %02X", model->uid[model->index + i]); + } + } + canvas_draw_str_aligned( + canvas, 77, EDITOR_STRING_Y, AlignLeft, AlignBottom, furi_string_get_cstr(temp_s)); + + canvas_set_font(canvas, FontPrimary); + + furi_string_reset(temp_s); + furi_string_cat_printf(temp_s, "<%02X>", model->uid[model->index]); + canvas_draw_str_aligned( + canvas, + GUI_DISPLAY_HORIZONTAL_CENTER, + EDITOR_STRING_Y, + AlignCenter, + AlignBottom, + furi_string_get_cstr(temp_s)); + + uint16_t w = canvas_string_width(canvas, furi_string_get_cstr(temp_s)); + w -= 11; // '<' & '>' + w /= 2; + + if(model->allow_edit) { + if(model->lo) { + canvas_draw_line( + canvas, + GUI_DISPLAY_HORIZONTAL_CENTER + 1, + EDITOR_STRING_Y + 2, + GUI_DISPLAY_HORIZONTAL_CENTER + w, + EDITOR_STRING_Y + 2); + } else { + canvas_draw_line( + canvas, + GUI_DISPLAY_HORIZONTAL_CENTER - w, + EDITOR_STRING_Y + 2, + GUI_DISPLAY_HORIZONTAL_CENTER - 1, + EDITOR_STRING_Y + 2); + } + } else { + // canvas_draw_line( + // canvas, + // GUI_DISPLAY_HORIZONTAL_CENTER - w, + // EDITOR_STRING_Y + 2, + // GUI_DISPLAY_HORIZONTAL_CENTER + w, + // EDITOR_STRING_Y + 2); + } + // ####### Editor ####### +} + +bool fuzzer_view_field_editor_input(InputEvent* event, void* context) { + furi_assert(context); + FuzzerViewFieldEditor* view_edit = context; + + if(event->key == InputKeyBack && event->type == InputTypeShort) { + view_edit->callback(FuzzerCustomEventViewFieldEditorBack, view_edit->context); + return true; + } else if(event->key == InputKeyOk && event->type == InputTypeShort) { + view_edit->callback(FuzzerCustomEventViewFieldEditorOk, view_edit->context); + return true; + } else if(event->key == InputKeyLeft) { + with_view_model( + view_edit->view, + FuzzerViewFieldEditorModel * model, + { + if(event->type == InputTypeShort) { + if(!model->allow_edit) { + model->lo = false; + } + if(model->index > 0 || model->lo) { + if(!model->lo) { + model->index--; + } + model->lo = !model->lo; + } + } else if(event->type == InputTypeLong) { + model->index = 0; + model->lo = false; + } + }, + true); + return true; + } else if(event->key == InputKeyRight) { + with_view_model( + view_edit->view, + FuzzerViewFieldEditorModel * model, + { + if(event->type == InputTypeShort) { + if(!model->allow_edit) { + model->lo = true; + } + if(model->index < (model->uid_size - 1) || !model->lo) { + if(model->lo) { + model->index++; + } + model->lo = !model->lo; + } + } else if(event->type == InputTypeLong) { + model->index = model->uid_size - 1; + model->lo = true; + } + }, + true); + return true; + } else if(event->key == InputKeyUp) { + with_view_model( + view_edit->view, + FuzzerViewFieldEditorModel * model, + { + if(event->type == InputTypeShort && model->allow_edit) { + if(model->lo) { + model->uid[model->index] = (model->uid[model->index] & 0xF0) | + ((model->uid[model->index] + 1) & 0x0F); + } else { + model->uid[model->index] = ((model->uid[model->index] + 0x10) & 0xF0) | + (model->uid[model->index] & 0x0F); + } + } + }, + true); + return true; + } else if(event->key == InputKeyDown) { + with_view_model( + view_edit->view, + FuzzerViewFieldEditorModel * model, + { + if(event->type == InputTypeShort && model->allow_edit) { + if(model->lo) { + model->uid[model->index] = (model->uid[model->index] & 0xF0) | + ((model->uid[model->index] - 1) & 0x0F); + } else { + model->uid[model->index] = ((model->uid[model->index] - 0x10) & 0xF0) | + (model->uid[model->index] & 0x0F); + } + } + }, + true); + return true; + } + + return true; +} + +void fuzzer_view_field_editor_enter(void* context) { + furi_assert(context); +} + +void fuzzer_view_field_editor_exit(void* context) { + furi_assert(context); +} + +FuzzerViewFieldEditor* fuzzer_view_field_editor_alloc() { + FuzzerViewFieldEditor* view_edit = malloc(sizeof(FuzzerViewFieldEditor)); + + // View allocation and configuration + view_edit->view = view_alloc(); + view_allocate_model(view_edit->view, ViewModelTypeLocking, sizeof(FuzzerViewFieldEditorModel)); + view_set_context(view_edit->view, view_edit); + view_set_draw_callback(view_edit->view, (ViewDrawCallback)fuzzer_view_field_editor_draw); + view_set_input_callback(view_edit->view, fuzzer_view_field_editor_input); + view_set_enter_callback(view_edit->view, fuzzer_view_field_editor_enter); + view_set_exit_callback(view_edit->view, fuzzer_view_field_editor_exit); + + with_view_model( + view_edit->view, + FuzzerViewFieldEditorModel * model, + { + model->uid_str = furi_string_alloc(); + model->uid = malloc(fuzzer_proto_get_max_data_size()); + }, + true); + + return view_edit; +} + +void fuzzer_view_field_editor_free(FuzzerViewFieldEditor* view_edit) { + furi_assert(view_edit); + + with_view_model( + view_edit->view, + FuzzerViewFieldEditorModel * model, + { + furi_string_free(model->uid_str); + free(model->uid); + }, + true); + view_free(view_edit->view); + free(view_edit); +} + +View* fuzzer_view_field_editor_get_view(FuzzerViewFieldEditor* view_edit) { + furi_assert(view_edit); + return view_edit->view; +} \ No newline at end of file diff --git a/applications/external/multi_fuzzer/views/field_editor.h b/applications/external/multi_fuzzer/views/field_editor.h new file mode 100644 index 0000000000..d81538bf8d --- /dev/null +++ b/applications/external/multi_fuzzer/views/field_editor.h @@ -0,0 +1,29 @@ +#pragma once + +#include +#include "../helpers/fuzzer_custom_event.h" +#include "../lib/worker/protocol.h" + +typedef struct FuzzerViewFieldEditor FuzzerViewFieldEditor; + +typedef void (*FuzzerViewFieldEditorCallback)(FuzzerCustomEvent event, void* context); + +void fuzzer_view_field_editor_set_callback( + FuzzerViewFieldEditor* view_attack, + FuzzerViewFieldEditorCallback callback, + void* context); + +FuzzerViewFieldEditor* fuzzer_view_field_editor_alloc(); + +void fuzzer_view_field_editor_free(FuzzerViewFieldEditor* view_attack); + +View* fuzzer_view_field_editor_get_view(FuzzerViewFieldEditor* view_attack); + +void fuzzer_view_field_editor_reset_data( + FuzzerViewFieldEditor* view_edit, + const FuzzerPayload* new_uid, + bool allow_edit); + +void fuzzer_view_field_editor_get_uid(FuzzerViewFieldEditor* view_edit, FuzzerPayload* output_uid); + +uint8_t fuzzer_view_field_editor_get_index(FuzzerViewFieldEditor* view_edit); \ No newline at end of file diff --git a/applications/external/multi_fuzzer/views/main_menu.c b/applications/external/multi_fuzzer/views/main_menu.c new file mode 100644 index 0000000000..14422145b7 --- /dev/null +++ b/applications/external/multi_fuzzer/views/main_menu.c @@ -0,0 +1,235 @@ +#include "main_menu.h" +#include "../fuzzer_i.h" + +#include + +#include "../lib/worker/protocol.h" + +#define PROTOCOL_NAME_Y 12 +// #define PROTOCOL_CAROUSEL + +struct FuzzerViewMain { + View* view; + FuzzerViewMainCallback callback; + void* context; +}; + +typedef struct { + uint8_t proto_index; + uint8_t menu_index; + uint8_t proto_max; + uint8_t menu_max; +} FuzzerViewMainModel; + +void fuzzer_view_main_update_data(FuzzerViewMain* view, FuzzerState state) { + furi_assert(view); + with_view_model( + view->view, + FuzzerViewMainModel * model, + { + model->proto_index = state.proto_index; + model->menu_index = state.menu_index; + }, + true); +} + +void fuzzer_view_main_get_state(FuzzerViewMain* view, FuzzerState* state) { + furi_assert(view); + with_view_model( + view->view, + FuzzerViewMainModel * model, + { + state->proto_index = model->proto_index; + state->menu_index = model->menu_index; + }, + true); +} + +void fuzzer_view_main_set_callback( + FuzzerViewMain* view, + FuzzerViewMainCallback callback, + void* context) { + furi_assert(view); + + view->callback = callback; + view->context = context; +} + +void fuzzer_view_main_draw(Canvas* canvas, FuzzerViewMainModel* model) { + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + + if(model->menu_index > 0) { + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned( + canvas, + 64, + 24, + AlignCenter, + AlignTop, + fuzzer_proto_get_menu_label(model->menu_index - 1)); + } + + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned( + canvas, 64, 36, AlignCenter, AlignTop, fuzzer_proto_get_menu_label(model->menu_index)); + + if(model->menu_index < (model->menu_max - 1)) { + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned( + canvas, + 64, + 48, + AlignCenter, + AlignTop, + fuzzer_proto_get_menu_label(model->menu_index + 1)); + } + + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 27, PROTOCOL_NAME_Y, AlignCenter, AlignBottom, "<"); + canvas_draw_str_aligned( + canvas, + 64, + PROTOCOL_NAME_Y, + AlignCenter, + AlignBottom, + fuzzer_proto_get_name(model->proto_index)); + canvas_draw_str_aligned(canvas, 101, PROTOCOL_NAME_Y, AlignCenter, AlignBottom, ">"); + +#ifdef PROTOCOL_CAROUSEL + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned( + canvas, + 20, + PROTOCOL_NAME_Y, + AlignRight, + AlignBottom, + (model->proto_index > 0) ? fuzzer_proto_get_name(model->proto_index - 1) : + fuzzer_proto_get_name((model->proto_max - 1))); + canvas_draw_str_aligned( + canvas, + 108, + PROTOCOL_NAME_Y, + AlignLeft, + AlignBottom, + (model->proto_index < (model->proto_max - 1)) ? + fuzzer_proto_get_name(model->proto_index + 1) : + fuzzer_proto_get_name(0)); +#endif +} + +bool fuzzer_view_main_input(InputEvent* event, void* context) { + furi_assert(context); + FuzzerViewMain* view = context; + + if(event->key == InputKeyBack && + (event->type == InputTypeLong || event->type == InputTypeShort)) { + view->callback(FuzzerCustomEventViewMainBack, view->context); + return true; + } else if(event->key == InputKeyOk && event->type == InputTypeShort) { + view->callback(FuzzerCustomEventViewMainOk, view->context); + return true; + } else if(event->key == InputKeyDown && event->type == InputTypeShort) { + with_view_model( + view->view, + FuzzerViewMainModel * model, + { + if(model->menu_index < (model->menu_max - 1)) { + model->menu_index++; + } + }, + true); + return true; + } else if(event->key == InputKeyUp && event->type == InputTypeShort) { + with_view_model( + view->view, + FuzzerViewMainModel * model, + { + if(model->menu_index != 0) { + model->menu_index--; + } + }, + true); + return true; + } else if(event->key == InputKeyLeft && event->type == InputTypeShort) { + with_view_model( + view->view, + FuzzerViewMainModel * model, + { + if(model->proto_index != 0) { + model->proto_index--; + } else { + model->proto_index = (model->proto_max - 1); + } + }, + true); + return true; + } else if(event->key == InputKeyRight && event->type == InputTypeShort) { + with_view_model( + view->view, + FuzzerViewMainModel * model, + { + if(model->proto_index == (model->proto_max - 1)) { + model->proto_index = 0; + } else { + model->proto_index++; + } + }, + true); + return true; + } + + return true; +} + +void fuzzer_view_main_enter(void* context) { + furi_assert(context); +} + +void fuzzer_view_main_exit(void* context) { + furi_assert(context); +} + +FuzzerViewMain* fuzzer_view_main_alloc() { + FuzzerViewMain* view = malloc(sizeof(FuzzerViewMain)); + + // View allocation and configuration + view->view = view_alloc(); + view_allocate_model(view->view, ViewModelTypeLocking, sizeof(FuzzerViewMainModel)); + view_set_context(view->view, view); + view_set_draw_callback(view->view, (ViewDrawCallback)fuzzer_view_main_draw); + view_set_input_callback(view->view, fuzzer_view_main_input); + view_set_enter_callback(view->view, fuzzer_view_main_enter); + view_set_exit_callback(view->view, fuzzer_view_main_exit); + + with_view_model( + view->view, + FuzzerViewMainModel * model, + { + model->proto_index = 0; + model->proto_max = fuzzer_proto_get_count_of_protocols(); + model->menu_index = 0; + model->menu_max = fuzzer_proto_get_count_of_menu_items(); + }, + true); + return view; +} + +void fuzzer_view_main_free(FuzzerViewMain* view) { + furi_assert(view); + + // with_view_model( + // view->view, + // FuzzerViewMainModel * model, + // { + + // }, + // true); + view_free(view->view); + free(view); +} + +View* fuzzer_view_main_get_view(FuzzerViewMain* view) { + furi_assert(view); + return view->view; +} \ No newline at end of file diff --git a/applications/external/multi_fuzzer/views/main_menu.h b/applications/external/multi_fuzzer/views/main_menu.h new file mode 100644 index 0000000000..262d54405b --- /dev/null +++ b/applications/external/multi_fuzzer/views/main_menu.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include "../helpers/fuzzer_custom_event.h" +#include "../helpers/fuzzer_types.h" + +typedef struct FuzzerViewMain FuzzerViewMain; + +typedef void (*FuzzerViewMainCallback)(FuzzerCustomEvent event, void* context); + +void fuzzer_view_main_set_callback( + FuzzerViewMain* fuzzer_view_main, + FuzzerViewMainCallback callback, + void* context); + +FuzzerViewMain* fuzzer_view_main_alloc(); + +void fuzzer_view_main_free(FuzzerViewMain* view); + +View* fuzzer_view_main_get_view(FuzzerViewMain* view); + +void fuzzer_view_main_update_data(FuzzerViewMain* view, FuzzerState state); +void fuzzer_view_main_get_state(FuzzerViewMain* view, FuzzerState* state); \ No newline at end of file diff --git a/applications/external/music_beeper/application.fam b/applications/external/music_beeper/application.fam index 39b9babba4..404988ca6f 100644 --- a/applications/external/music_beeper/application.fam +++ b/applications/external/music_beeper/application.fam @@ -1,5 +1,5 @@ App( - appid="Music_Beeper", + appid="music_beeper", name="Music Beeper", apptype=FlipperAppType.EXTERNAL, entry_point="music_beeper_app", @@ -12,14 +12,5 @@ App( stack_size=2 * 1024, order=45, fap_icon="music_10px.png", - fap_icon_assets="icons", - fap_category="Music", -) - -App( - appid="music_beeper_start", - apptype=FlipperAppType.STARTUP, - entry_point="music_beeper_on_system_start", - requires=["music_beeper"], - order=30, + fap_category="Media", ) diff --git a/applications/external/music_beeper/music_beeper.c b/applications/external/music_beeper/music_beeper.c index f743e3c98c..15eba821b7 100644 --- a/applications/external/music_beeper/music_beeper.c +++ b/applications/external/music_beeper/music_beeper.c @@ -3,14 +3,14 @@ #include #include -#include +#include #include #include #include #define TAG "MusicBeeper" -#define MUSIC_BEEPER_APP_PATH_FOLDER ANY_PATH("music_player") +#define MUSIC_BEEPER_APP_PATH_FOLDER EXT_PATH("music_player") #define MUSIC_BEEPER_APP_EXTENSION "*" #define MUSIC_BEEPER_SEMITONE_HISTORY_SIZE 4 diff --git a/applications/external/music_player/application.fam b/applications/external/music_player/application.fam index fb040c601b..4d0858494e 100644 --- a/applications/external/music_player/application.fam +++ b/applications/external/music_player/application.fam @@ -7,18 +7,9 @@ App( "gui", "dialogs", ], - provides=["music_player_start"], stack_size=2 * 1024, order=45, - fap_icon="icons/music_10px.png", - fap_category="Music", - fap_icon_assets="icons", -) - -App( - appid="music_player_start", - apptype=FlipperAppType.STARTUP, - entry_point="music_player_on_system_start", - requires=["music_player"], - order=30, + fap_icon="music_10px.png", + fap_category="Media", + fap_libs=["music_worker"], ) diff --git a/applications/external/music_beeper/icons/music_10px.png b/applications/external/music_player/music_10px.png similarity index 100% rename from applications/external/music_beeper/icons/music_10px.png rename to applications/external/music_player/music_10px.png diff --git a/applications/external/music_player/music_player.c b/applications/external/music_player/music_player.c index 28127a575d..181eb60d6e 100644 --- a/applications/external/music_player/music_player.c +++ b/applications/external/music_player/music_player.c @@ -1,16 +1,16 @@ -#include "music_player_worker.h" +#include #include #include -#include +#include #include #include #include #define TAG "MusicPlayer" -#define MUSIC_PLAYER_APP_PATH_FOLDER ANY_PATH("music_player") +#define MUSIC_PLAYER_APP_PATH_FOLDER EXT_PATH("music_player") #define MUSIC_PLAYER_APP_EXTENSION "*" #define MUSIC_PLAYER_SEMITONE_HISTORY_SIZE 4 @@ -35,7 +35,7 @@ typedef struct { ViewPort* view_port; Gui* gui; - MusicPlayerWorker* worker; + MusicWorker* worker; } MusicPlayer; static const float MUSIC_PLAYER_VOLUMES[] = {0, .25, .5, .75, 1}; @@ -219,7 +219,7 @@ static void input_callback(InputEvent* input_event, void* ctx) { } } -static void music_player_worker_callback( +static void music_worker_callback( uint8_t semitone, uint8_t dots, uint8_t duration, @@ -251,7 +251,7 @@ static void music_player_worker_callback( void music_player_clear(MusicPlayer* instance) { memset(instance->model->duration_history, 0xff, MUSIC_PLAYER_SEMITONE_HISTORY_SIZE); memset(instance->model->semitone_history, 0xff, MUSIC_PLAYER_SEMITONE_HISTORY_SIZE); - music_player_worker_clear(instance->worker); + music_worker_clear(instance->worker); } MusicPlayer* music_player_alloc() { @@ -264,10 +264,9 @@ MusicPlayer* music_player_alloc() { instance->input_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); - instance->worker = music_player_worker_alloc(); - music_player_worker_set_volume( - instance->worker, MUSIC_PLAYER_VOLUMES[instance->model->volume]); - music_player_worker_set_callback(instance->worker, music_player_worker_callback, instance); + instance->worker = music_worker_alloc(); + music_worker_set_volume(instance->worker, MUSIC_PLAYER_VOLUMES[instance->model->volume]); + music_worker_set_callback(instance->worker, music_worker_callback, instance); music_player_clear(instance); @@ -287,7 +286,7 @@ void music_player_free(MusicPlayer* instance) { furi_record_close(RECORD_GUI); view_port_free(instance->view_port); - music_player_worker_free(instance->worker); + music_worker_free(instance->worker); furi_message_queue_free(instance->input_queue); @@ -325,12 +324,12 @@ int32_t music_player_app(void* p) { } } - if(!music_player_worker_load(music_player->worker, furi_string_get_cstr(file_path))) { + if(!music_worker_load(music_player->worker, furi_string_get_cstr(file_path))) { FURI_LOG_E(TAG, "Unable to load file"); break; } - music_player_worker_start(music_player->worker); + music_worker_start(music_player->worker); InputEvent input; while(furi_message_queue_get(music_player->input_queue, &input, FuriWaitForever) == @@ -344,11 +343,11 @@ int32_t music_player_app(void* p) { } else if(input.key == InputKeyUp) { if(music_player->model->volume < COUNT_OF(MUSIC_PLAYER_VOLUMES) - 1) music_player->model->volume++; - music_player_worker_set_volume( + music_worker_set_volume( music_player->worker, MUSIC_PLAYER_VOLUMES[music_player->model->volume]); } else if(input.key == InputKeyDown) { if(music_player->model->volume > 0) music_player->model->volume--; - music_player_worker_set_volume( + music_worker_set_volume( music_player->worker, MUSIC_PLAYER_VOLUMES[music_player->model->volume]); } @@ -356,7 +355,7 @@ int32_t music_player_app(void* p) { view_port_update(music_player->view_port); } - music_player_worker_stop(music_player->worker); + music_worker_stop(music_player->worker); if(p && strlen(p)) break; // Exit instead of going to browser if launched with arg music_player_clear(music_player); } while(1); diff --git a/applications/external/music_player/music_player_cli.c b/applications/external/music_player/music_player_cli.c deleted file mode 100644 index 90060d7ee0..0000000000 --- a/applications/external/music_player/music_player_cli.c +++ /dev/null @@ -1,48 +0,0 @@ -#include -#include -#include -#include "music_player_worker.h" - -static void music_player_cli(Cli* cli, FuriString* args, void* context) { - UNUSED(context); - MusicPlayerWorker* music_player_worker = music_player_worker_alloc(); - Storage* storage = furi_record_open(RECORD_STORAGE); - - do { - if(storage_common_stat(storage, furi_string_get_cstr(args), NULL) == FSE_OK) { - if(!music_player_worker_load(music_player_worker, furi_string_get_cstr(args))) { - printf("Failed to open file %s\r\n", furi_string_get_cstr(args)); - break; - } - } else { - if(!music_player_worker_load_rtttl_from_string( - music_player_worker, furi_string_get_cstr(args))) { - printf("Argument is not a file or RTTTL\r\n"); - break; - } - } - - printf("Press CTRL+C to stop\r\n"); - music_player_worker_set_volume(music_player_worker, 1.0f); - music_player_worker_start(music_player_worker); - while(!cli_cmd_interrupt_received(cli)) { - furi_delay_ms(50); - } - music_player_worker_stop(music_player_worker); - } while(0); - - furi_record_close(RECORD_STORAGE); - music_player_worker_free(music_player_worker); -} - -void music_player_on_system_start() { -#ifdef SRV_CLI - Cli* cli = furi_record_open(RECORD_CLI); - - cli_add_command(cli, "music_player", CliCommandFlagDefault, music_player_cli, NULL); - - furi_record_close(RECORD_CLI); -#else - UNUSED(music_player_cli); -#endif -} diff --git a/applications/external/music_player/music_player_worker.h b/applications/external/music_player/music_player_worker.h deleted file mode 100644 index acf824086c..0000000000 --- a/applications/external/music_player/music_player_worker.h +++ /dev/null @@ -1,46 +0,0 @@ -#pragma once - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef void (*MusicPlayerWorkerCallback)( - uint8_t semitone, - uint8_t dots, - uint8_t duration, - float position, - void* context); - -typedef struct MusicPlayerWorker MusicPlayerWorker; - -MusicPlayerWorker* music_player_worker_alloc(); - -void music_player_worker_clear(MusicPlayerWorker* instance); - -void music_player_worker_free(MusicPlayerWorker* instance); - -bool music_player_worker_load(MusicPlayerWorker* instance, const char* file_path); - -bool music_player_worker_load_fmf_from_file(MusicPlayerWorker* instance, const char* file_path); - -bool music_player_worker_load_rtttl_from_file(MusicPlayerWorker* instance, const char* file_path); - -bool music_player_worker_load_rtttl_from_string(MusicPlayerWorker* instance, const char* string); - -void music_player_worker_set_callback( - MusicPlayerWorker* instance, - MusicPlayerWorkerCallback callback, - void* context); - -void music_player_worker_set_volume(MusicPlayerWorker* instance, float volume); - -void music_player_worker_start(MusicPlayerWorker* instance); - -void music_player_worker_stop(MusicPlayerWorker* instance); - -#ifdef __cplusplus -} -#endif diff --git a/applications/external/music_tracker/application.fam b/applications/external/music_tracker/application.fam index fe4355e861..23bcee0790 100644 --- a/applications/external/music_tracker/application.fam +++ b/applications/external/music_tracker/application.fam @@ -9,5 +9,9 @@ App( stack_size=4 * 1024, order=20, fap_icon="zero_tracker.png", - fap_category="Music", + fap_category="Media", + fap_author="@DrZlo13", + fap_weburl="https://github.com/DrZlo13/flipper-zero-music-tracker", + fap_version="1.0", + fap_description="App plays hardcoded tracker song", ) diff --git a/applications/external/music_tracker/tracker_engine/speaker_hal.c b/applications/external/music_tracker/tracker_engine/speaker_hal.c index 0a506a4246..112439b1e5 100644 --- a/applications/external/music_tracker/tracker_engine/speaker_hal.c +++ b/applications/external/music_tracker/tracker_engine/speaker_hal.c @@ -71,6 +71,8 @@ void tracker_interrupt_init(float freq, FuriHalInterruptISR isr, void* context) tracker_isr = isr; tracker_isr_context = context; + furi_hal_bus_enable(FuriHalBusTIM2); + furi_hal_interrupt_set_isr(FuriHalInterruptIdTIM2, tracker_interrupt_cb, NULL); LL_TIM_InitTypeDef TIM_InitStruct = {0}; @@ -88,7 +90,7 @@ void tracker_interrupt_init(float freq, FuriHalInterruptISR isr, void* context) void tracker_interrupt_deinit() { FURI_CRITICAL_ENTER(); - LL_TIM_DeInit(TIM2); + furi_hal_bus_disable(FuriHalBusTIM2); FURI_CRITICAL_EXIT(); furi_hal_interrupt_set_isr(FuriHalInterruptIdTIM2, NULL, NULL); @@ -104,4 +106,4 @@ void tracker_debug_set(bool value) { void tracker_debug_deinit() { furi_hal_gpio_init(&gpio_ext_pc3, GpioModeAnalog, GpioPullNo, GpioSpeedLow); -} +} \ No newline at end of file diff --git a/applications/external/picopass/icons/Nfc_10px.png b/applications/external/nfc_magic/Nfc_10px.png similarity index 100% rename from applications/external/picopass/icons/Nfc_10px.png rename to applications/external/nfc_magic/Nfc_10px.png diff --git a/applications/external/nfc_magic/application.fam b/applications/external/nfc_magic/application.fam index 717387d58a..83a148f21b 100644 --- a/applications/external/nfc_magic/application.fam +++ b/applications/external/nfc_magic/application.fam @@ -10,7 +10,7 @@ App( ], stack_size=4 * 1024, order=30, - fap_icon="../../../assets/icons/Archive/Nfc_10px.png", + fap_icon="Nfc_10px.png", fap_category="NFC", fap_private_libs=[ Lib( diff --git a/applications/external/nfc_magic/assets/DolphinCommon_56x48.png b/applications/external/nfc_magic/assets/DolphinCommon_56x48.png deleted file mode 100644 index 089aaed835..0000000000 Binary files a/applications/external/nfc_magic/assets/DolphinCommon_56x48.png and /dev/null differ diff --git a/applications/external/nfc_magic/assets/NFC_manual_60x50.png b/applications/external/nfc_magic/assets/NFC_manual_60x50.png deleted file mode 100644 index 787c0bcfe0..0000000000 Binary files a/applications/external/nfc_magic/assets/NFC_manual_60x50.png and /dev/null differ diff --git a/applications/external/nfc_magic/nfc_magic.c b/applications/external/nfc_magic/nfc_magic.c index b14e34072d..0fce8bb66d 100644 --- a/applications/external/nfc_magic/nfc_magic.c +++ b/applications/external/nfc_magic/nfc_magic.c @@ -174,7 +174,7 @@ int32_t nfc_magic_app(void* p) { UNUSED(p); NfcMagic* nfc_magic = nfc_magic_alloc(); - DOLPHIN_DEED(DolphinDeedPluginStart); + dolphin_deed(DolphinDeedPluginStart); scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneStart); view_dispatcher_run(nfc_magic->view_dispatcher); diff --git a/applications/external/nfc_magic/nfc_magic_i.h b/applications/external/nfc_magic/nfc_magic_i.h index 4d6b89103d..e7875989ff 100644 --- a/applications/external/nfc_magic/nfc_magic_i.h +++ b/applications/external/nfc_magic/nfc_magic_i.h @@ -30,8 +30,9 @@ #include #include "nfc_magic_icons.h" +#include -#define NFC_APP_FOLDER ANY_PATH("nfc") +#define NFC_APP_FOLDER EXT_PATH("nfc") enum NfcMagicCustomEvent { // Reserve first 100 events for button types and indexes, starting from 0 @@ -46,6 +47,7 @@ enum NfcMagicCustomEvent { struct NfcMagicDevice { MagicType type; uint32_t cuid; + uint8_t uid_len; uint32_t password; }; diff --git a/applications/external/nfc_magic/nfc_magic_worker.c b/applications/external/nfc_magic/nfc_magic_worker.c index 4c3e7b2b6a..25af770493 100644 --- a/applications/external/nfc_magic/nfc_magic_worker.c +++ b/applications/external/nfc_magic/nfc_magic_worker.c @@ -110,14 +110,7 @@ void nfc_magic_worker_write(NfcMagicWorker* nfc_magic_worker) { } magic_activate(); if(magic_gen1_wupa()) { - if(!magic_gen1_data_access_cmd()) { - FURI_LOG_E( - TAG, "No card response to data access command (not a magic card)"); - nfc_magic_worker->callback( - NfcMagicWorkerEventWrongCard, nfc_magic_worker->context); - done = true; - break; - } + magic_gen1_data_access_cmd(); MfClassicData* mfc_data = &dev_data->mf_classic_data; for(size_t i = 0; i < 64; i++) { @@ -299,6 +292,7 @@ void nfc_magic_worker_write(NfcMagicWorker* nfc_magic_worker) { } void nfc_magic_worker_check(NfcMagicWorker* nfc_magic_worker) { + FuriHalNfcDevData nfc_data = {}; NfcMagicDevice* magic_dev = nfc_magic_worker->magic_dev; bool card_found_notified = false; uint8_t gen4_config[MAGIC_GEN4_CONFIG_LEN]; @@ -313,32 +307,44 @@ void nfc_magic_worker_check(NfcMagicWorker* nfc_magic_worker) { card_found_notified = true; } - furi_hal_nfc_activate_nfca(200, &magic_dev->cuid); - nfc_magic_worker->callback(NfcMagicWorkerEventSuccess, nfc_magic_worker->context); - break; - } - - magic_deactivate(); - furi_delay_ms(300); - magic_activate(); - - furi_hal_nfc_activate_nfca(200, &magic_dev->cuid); - if(magic_gen4_get_cfg(magic_dev->password, gen4_config)) { - magic_dev->type = MagicTypeGen4; - if(!card_found_notified) { - nfc_magic_worker->callback( - NfcMagicWorkerEventCardDetected, nfc_magic_worker->context); - card_found_notified = true; + if(furi_hal_nfc_detect(&nfc_data, 200)) { + magic_dev->cuid = nfc_data.a_data.cuid; + magic_dev->uid_len = nfc_data.uid_len; + } else { + // wrong BCC + magic_dev->uid_len = 4; } - nfc_magic_worker->callback(NfcMagicWorkerEventSuccess, nfc_magic_worker->context); break; - } + } else { + magic_deactivate(); + magic_activate(); + if(furi_hal_nfc_detect(&nfc_data, 200)) { + magic_dev->cuid = nfc_data.a_data.cuid; + magic_dev->uid_len = nfc_data.uid_len; + if(magic_gen4_get_cfg(magic_dev->password, gen4_config)) { + magic_dev->type = MagicTypeGen4; + if(!card_found_notified) { + nfc_magic_worker->callback( + NfcMagicWorkerEventCardDetected, nfc_magic_worker->context); + card_found_notified = true; + } - if(card_found_notified) { - nfc_magic_worker->callback( - NfcMagicWorkerEventNoCardDetected, nfc_magic_worker->context); - card_found_notified = false; + nfc_magic_worker->callback( + NfcMagicWorkerEventSuccess, nfc_magic_worker->context); + } else { + nfc_magic_worker->callback( + NfcMagicWorkerEventWrongCard, nfc_magic_worker->context); + card_found_notified = true; + } + break; + } else { + if(card_found_notified) { + nfc_magic_worker->callback( + NfcMagicWorkerEventNoCardDetected, nfc_magic_worker->context); + card_found_notified = false; + } + } } magic_deactivate(); diff --git a/applications/external/nfc_magic/scenes/nfc_magic_scene_file_select.c b/applications/external/nfc_magic/scenes/nfc_magic_scene_file_select.c index baa6bcccc2..04b7024ffb 100644 --- a/applications/external/nfc_magic/scenes/nfc_magic_scene_file_select.c +++ b/applications/external/nfc_magic/scenes/nfc_magic_scene_file_select.c @@ -8,7 +8,7 @@ static bool nfc_magic_scene_file_select_is_file_suitable(NfcMagic* nfc_magic) { case MagicTypeClassicDirectWrite: case MagicTypeClassicAPDU: if((nfc_dev->dev_data.mf_classic_data.type != MfClassicType1k) || - (nfc_dev->dev_data.nfc_data.uid_len != 4)) { + (nfc_dev->dev_data.nfc_data.uid_len != nfc_magic->dev->uid_len)) { return false; } return true; diff --git a/applications/external/nfc_magic/scenes/nfc_magic_scene_not_magic.c b/applications/external/nfc_magic/scenes/nfc_magic_scene_not_magic.c index b87f7f383e..b4f579f444 100644 --- a/applications/external/nfc_magic/scenes/nfc_magic_scene_not_magic.c +++ b/applications/external/nfc_magic/scenes/nfc_magic_scene_not_magic.c @@ -13,11 +13,10 @@ void nfc_magic_scene_not_magic_on_enter(void* context) { notification_message(nfc_magic->notifications, &sequence_error); - // widget_add_icon_element(widget, 73, 17, &I_DolphinCommon_56x48); widget_add_string_element( widget, 3, 4, AlignLeft, AlignTop, FontPrimary, "This is wrong card"); widget_add_string_multiline_element( - widget, 4, 17, AlignLeft, AlignTop, FontSecondary, "Not a magic\ncard"); + widget, 4, 17, AlignLeft, AlignTop, FontSecondary, "Not magic or unsupported\ncard"); widget_add_button_element( widget, GuiButtonTypeLeft, "Retry", nfc_magic_scene_not_magic_widget_callback, nfc_magic); diff --git a/applications/external/nfc_maker/application.fam b/applications/external/nfc_maker/application.fam new file mode 100644 index 0000000000..89bbb202e3 --- /dev/null +++ b/applications/external/nfc_maker/application.fam @@ -0,0 +1,14 @@ +App( + appid="nfc_maker", + name="NFC Maker", + apptype=FlipperAppType.EXTERNAL, + entry_point="nfc_maker", + cdefines=["APP_NFC_MAKER"], + requires=[ + "storage", + "gui", + ], + stack_size=1 * 1024, + fap_icon="nfc_maker_10px.png", + fap_category="NFC", +) diff --git a/applications/external/nfc_maker/nfc_maker.c b/applications/external/nfc_maker/nfc_maker.c new file mode 100644 index 0000000000..578054adea --- /dev/null +++ b/applications/external/nfc_maker/nfc_maker.c @@ -0,0 +1,81 @@ +#include "nfc_maker.h" + +static bool nfc_maker_custom_event_callback(void* context, uint32_t event) { + furi_assert(context); + NfcMaker* app = context; + return scene_manager_handle_custom_event(app->scene_manager, event); +} + +static bool nfc_maker_back_event_callback(void* context) { + furi_assert(context); + NfcMaker* app = context; + + return scene_manager_handle_back_event(app->scene_manager); +} + +NfcMaker* nfc_maker_alloc() { + NfcMaker* app = malloc(sizeof(NfcMaker)); + app->gui = furi_record_open(RECORD_GUI); + + // View Dispatcher and Scene Manager + app->view_dispatcher = view_dispatcher_alloc(); + app->scene_manager = scene_manager_alloc(&nfc_maker_scene_handlers, app); + view_dispatcher_enable_queue(app->view_dispatcher); + view_dispatcher_set_event_callback_context(app->view_dispatcher, app); + + view_dispatcher_set_custom_event_callback( + app->view_dispatcher, nfc_maker_custom_event_callback); + view_dispatcher_set_navigation_event_callback( + app->view_dispatcher, nfc_maker_back_event_callback); + + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + + // Gui Modules + app->submenu = submenu_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, NfcMakerViewSubmenu, submenu_get_view(app->submenu)); + + app->text_input = text_input_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, NfcMakerViewTextInput, text_input_get_view(app->text_input)); + + app->byte_input = byte_input_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, NfcMakerViewByteInput, byte_input_get_view(app->byte_input)); + + app->popup = popup_alloc(); + view_dispatcher_add_view(app->view_dispatcher, NfcMakerViewPopup, popup_get_view(app->popup)); + + return app; +} + +void nfc_maker_free(NfcMaker* app) { + furi_assert(app); + + // Gui modules + view_dispatcher_remove_view(app->view_dispatcher, NfcMakerViewSubmenu); + submenu_free(app->submenu); + view_dispatcher_remove_view(app->view_dispatcher, NfcMakerViewTextInput); + text_input_free(app->text_input); + view_dispatcher_remove_view(app->view_dispatcher, NfcMakerViewByteInput); + byte_input_free(app->byte_input); + view_dispatcher_remove_view(app->view_dispatcher, NfcMakerViewPopup); + popup_free(app->popup); + + // View Dispatcher and Scene Manager + view_dispatcher_free(app->view_dispatcher); + scene_manager_free(app->scene_manager); + + // Records + furi_record_close(RECORD_GUI); + free(app); +} + +extern int32_t nfc_maker(void* p) { + UNUSED(p); + NfcMaker* app = nfc_maker_alloc(); + scene_manager_next_scene(app->scene_manager, NfcMakerSceneMenu); + view_dispatcher_run(app->view_dispatcher); + nfc_maker_free(app); + return 0; +} diff --git a/applications/external/nfc_maker/nfc_maker.h b/applications/external/nfc_maker/nfc_maker.h new file mode 100644 index 0000000000..388dc71963 --- /dev/null +++ b/applications/external/nfc_maker/nfc_maker.h @@ -0,0 +1,59 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "scenes/nfc_maker_scene.h" +#include +#include +#include +#include + +#define TEXT_INPUT_LEN 248 +#define WIFI_INPUT_LEN 90 + +typedef enum { + WifiAuthenticationOpen = 0x01, + WifiAuthenticationWpa2Personal = 0x20, + WifiAuthenticationWpa2Enterprise = 0x10, + WifiAuthenticationWpaPersonal = 0x02, + WifiAuthenticationWpaEnterprise = 0x08, + WifiAuthenticationShared = 0x04, +} WifiAuthentication; + +typedef enum { + WifiEncryptionAes = 0x08, + WifiEncryptionWep = 0x02, + WifiEncryptionTkip = 0x04, + WifiEncryptionNone = 0x01, +} WifiEncryption; + +typedef struct { + Gui* gui; + SceneManager* scene_manager; + ViewDispatcher* view_dispatcher; + Submenu* submenu; + TextInput* text_input; + ByteInput* byte_input; + Popup* popup; + + uint8_t mac_buf[GAP_MAC_ADDR_SIZE]; + char text_buf[TEXT_INPUT_LEN]; + char pass_buf[WIFI_INPUT_LEN]; + char name_buf[TEXT_INPUT_LEN]; +} NfcMaker; + +typedef enum { + NfcMakerViewSubmenu, + NfcMakerViewTextInput, + NfcMakerViewByteInput, + NfcMakerViewPopup, +} NfcMakerView; diff --git a/applications/external/nfc_maker/nfc_maker_10px.png b/applications/external/nfc_maker/nfc_maker_10px.png new file mode 100644 index 0000000000..a9e2443f17 Binary files /dev/null and b/applications/external/nfc_maker/nfc_maker_10px.png differ diff --git a/applications/external/nfc_maker/scenes/nfc_maker_scene.c b/applications/external/nfc_maker/scenes/nfc_maker_scene.c new file mode 100644 index 0000000000..3a8b7d5025 --- /dev/null +++ b/applications/external/nfc_maker/scenes/nfc_maker_scene.c @@ -0,0 +1,30 @@ +#include "nfc_maker_scene.h" + +// Generate scene on_enter handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, +void (*const nfc_maker_on_enter_handlers[])(void*) = { +#include "nfc_maker_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_event handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event, +bool (*const nfc_maker_on_event_handlers[])(void* context, SceneManagerEvent event) = { +#include "nfc_maker_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_exit handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit, +void (*const nfc_maker_on_exit_handlers[])(void* context) = { +#include "nfc_maker_scene_config.h" +}; +#undef ADD_SCENE + +// Initialize scene handlers configuration structure +const SceneManagerHandlers nfc_maker_scene_handlers = { + .on_enter_handlers = nfc_maker_on_enter_handlers, + .on_event_handlers = nfc_maker_on_event_handlers, + .on_exit_handlers = nfc_maker_on_exit_handlers, + .scene_num = NfcMakerSceneNum, +}; diff --git a/applications/external/nfc_maker/scenes/nfc_maker_scene.h b/applications/external/nfc_maker/scenes/nfc_maker_scene.h new file mode 100644 index 0000000000..5ad69f2330 --- /dev/null +++ b/applications/external/nfc_maker/scenes/nfc_maker_scene.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +// Generate scene id and total number +#define ADD_SCENE(prefix, name, id) NfcMakerScene##id, +typedef enum { +#include "nfc_maker_scene_config.h" + NfcMakerSceneNum, +} NfcMakerScene; +#undef ADD_SCENE + +extern const SceneManagerHandlers nfc_maker_scene_handlers; + +// Generate scene on_enter handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); +#include "nfc_maker_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_event handlers declaration +#define ADD_SCENE(prefix, name, id) \ + bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event); +#include "nfc_maker_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_exit handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context); +#include "nfc_maker_scene_config.h" +#undef ADD_SCENE diff --git a/applications/external/nfc_maker/scenes/nfc_maker_scene_bluetooth.c b/applications/external/nfc_maker/scenes/nfc_maker_scene_bluetooth.c new file mode 100644 index 0000000000..4e70a184df --- /dev/null +++ b/applications/external/nfc_maker/scenes/nfc_maker_scene_bluetooth.c @@ -0,0 +1,57 @@ +#include "../nfc_maker.h" + +enum ByteInputResult { + ByteInputResultOk, +}; + +static void nfc_maker_scene_bluetooth_byte_input_callback(void* context) { + NfcMaker* app = context; + + view_dispatcher_send_custom_event(app->view_dispatcher, ByteInputResultOk); +} + +void nfc_maker_scene_bluetooth_on_enter(void* context) { + NfcMaker* app = context; + ByteInput* byte_input = app->byte_input; + + byte_input_set_header_text(byte_input, "Enter Bluetooth MAC:"); + + for(size_t i = 0; i < GAP_MAC_ADDR_SIZE; i++) { + app->mac_buf[i] = 0x69; + } + + byte_input_set_result_callback( + byte_input, + nfc_maker_scene_bluetooth_byte_input_callback, + NULL, + app, + app->mac_buf, + GAP_MAC_ADDR_SIZE); + + view_dispatcher_switch_to_view(app->view_dispatcher, NfcMakerViewByteInput); +} + +bool nfc_maker_scene_bluetooth_on_event(void* context, SceneManagerEvent event) { + NfcMaker* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + switch(event.event) { + case ByteInputResultOk: + furi_hal_bt_reverse_mac_addr(app->mac_buf); + scene_manager_next_scene(app->scene_manager, NfcMakerSceneName); + break; + default: + break; + } + } + + return consumed; +} + +void nfc_maker_scene_bluetooth_on_exit(void* context) { + NfcMaker* app = context; + byte_input_set_result_callback(app->byte_input, NULL, NULL, NULL, NULL, 0); + byte_input_set_header_text(app->byte_input, ""); +} diff --git a/applications/external/nfc_maker/scenes/nfc_maker_scene_config.h b/applications/external/nfc_maker/scenes/nfc_maker_scene_config.h new file mode 100644 index 0000000000..a89b4198c4 --- /dev/null +++ b/applications/external/nfc_maker/scenes/nfc_maker_scene_config.h @@ -0,0 +1,13 @@ +ADD_SCENE(nfc_maker, menu, Menu) +ADD_SCENE(nfc_maker, bluetooth, Bluetooth) +ADD_SCENE(nfc_maker, https, Https) +ADD_SCENE(nfc_maker, mail, Mail) +ADD_SCENE(nfc_maker, phone, Phone) +ADD_SCENE(nfc_maker, text, Text) +ADD_SCENE(nfc_maker, url, Url) +ADD_SCENE(nfc_maker, wifi, Wifi) +ADD_SCENE(nfc_maker, wifi_auth, WifiAuth) +ADD_SCENE(nfc_maker, wifi_encr, WifiEncr) +ADD_SCENE(nfc_maker, wifi_pass, WifiPass) +ADD_SCENE(nfc_maker, name, Name) +ADD_SCENE(nfc_maker, result, Result) diff --git a/applications/external/nfc_maker/scenes/nfc_maker_scene_https.c b/applications/external/nfc_maker/scenes/nfc_maker_scene_https.c new file mode 100644 index 0000000000..9697c0d1c0 --- /dev/null +++ b/applications/external/nfc_maker/scenes/nfc_maker_scene_https.c @@ -0,0 +1,53 @@ +#include "../nfc_maker.h" + +enum TextInputResult { + TextInputResultOk, +}; + +static void nfc_maker_scene_https_text_input_callback(void* context) { + NfcMaker* app = context; + + view_dispatcher_send_custom_event(app->view_dispatcher, TextInputResultOk); +} + +void nfc_maker_scene_https_on_enter(void* context) { + NfcMaker* app = context; + TextInput* text_input = app->text_input; + + text_input_set_header_text(text_input, "Enter Https Link:"); + + strlcpy(app->text_buf, "flipper-xtre.me", TEXT_INPUT_LEN); + + text_input_set_result_callback( + text_input, + nfc_maker_scene_https_text_input_callback, + app, + app->text_buf, + TEXT_INPUT_LEN, + true); + + view_dispatcher_switch_to_view(app->view_dispatcher, NfcMakerViewTextInput); +} + +bool nfc_maker_scene_https_on_event(void* context, SceneManagerEvent event) { + NfcMaker* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + switch(event.event) { + case TextInputResultOk: + scene_manager_next_scene(app->scene_manager, NfcMakerSceneName); + break; + default: + break; + } + } + + return consumed; +} + +void nfc_maker_scene_https_on_exit(void* context) { + NfcMaker* app = context; + text_input_reset(app->text_input); +} diff --git a/applications/external/nfc_maker/scenes/nfc_maker_scene_mail.c b/applications/external/nfc_maker/scenes/nfc_maker_scene_mail.c new file mode 100644 index 0000000000..5b76f56a26 --- /dev/null +++ b/applications/external/nfc_maker/scenes/nfc_maker_scene_mail.c @@ -0,0 +1,53 @@ +#include "../nfc_maker.h" + +enum TextInputResult { + TextInputResultOk, +}; + +static void nfc_maker_scene_mail_text_input_callback(void* context) { + NfcMaker* app = context; + + view_dispatcher_send_custom_event(app->view_dispatcher, TextInputResultOk); +} + +void nfc_maker_scene_mail_on_enter(void* context) { + NfcMaker* app = context; + TextInput* text_input = app->text_input; + + text_input_set_header_text(text_input, "Enter Mail Address:"); + + strlcpy(app->text_buf, "ben.dover@example.com", TEXT_INPUT_LEN); + + text_input_set_result_callback( + text_input, + nfc_maker_scene_mail_text_input_callback, + app, + app->text_buf, + TEXT_INPUT_LEN, + true); + + view_dispatcher_switch_to_view(app->view_dispatcher, NfcMakerViewTextInput); +} + +bool nfc_maker_scene_mail_on_event(void* context, SceneManagerEvent event) { + NfcMaker* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + switch(event.event) { + case TextInputResultOk: + scene_manager_next_scene(app->scene_manager, NfcMakerSceneName); + break; + default: + break; + } + } + + return consumed; +} + +void nfc_maker_scene_mail_on_exit(void* context) { + NfcMaker* app = context; + text_input_reset(app->text_input); +} diff --git a/applications/external/nfc_maker/scenes/nfc_maker_scene_menu.c b/applications/external/nfc_maker/scenes/nfc_maker_scene_menu.c new file mode 100644 index 0000000000..4268e7e2f1 --- /dev/null +++ b/applications/external/nfc_maker/scenes/nfc_maker_scene_menu.c @@ -0,0 +1,61 @@ +#include "../nfc_maker.h" + +void nfc_maker_scene_menu_submenu_callback(void* context, uint32_t index) { + NfcMaker* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +void nfc_maker_scene_menu_on_enter(void* context) { + NfcMaker* app = context; + Submenu* submenu = app->submenu; + + submenu_set_header(submenu, "NFC Tag Maker:"); + + submenu_add_item( + submenu, + "Bluetooth MAC", + NfcMakerSceneBluetooth, + nfc_maker_scene_menu_submenu_callback, + app); + + submenu_add_item( + submenu, "HTTPS Link", NfcMakerSceneHttps, nfc_maker_scene_menu_submenu_callback, app); + + submenu_add_item( + submenu, "Mail Address", NfcMakerSceneMail, nfc_maker_scene_menu_submenu_callback, app); + + submenu_add_item( + submenu, "Phone Number", NfcMakerScenePhone, nfc_maker_scene_menu_submenu_callback, app); + + submenu_add_item( + submenu, "Text Note", NfcMakerSceneText, nfc_maker_scene_menu_submenu_callback, app); + + submenu_add_item( + submenu, "Plain URL", NfcMakerSceneUrl, nfc_maker_scene_menu_submenu_callback, app); + + submenu_add_item( + submenu, "WiFi Login", NfcMakerSceneWifi, nfc_maker_scene_menu_submenu_callback, app); + + submenu_set_selected_item( + submenu, scene_manager_get_scene_state(app->scene_manager, NfcMakerSceneMenu)); + + view_dispatcher_switch_to_view(app->view_dispatcher, NfcMakerViewSubmenu); +} + +bool nfc_maker_scene_menu_on_event(void* context, SceneManagerEvent event) { + NfcMaker* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + scene_manager_set_scene_state(app->scene_manager, NfcMakerSceneMenu, event.event); + consumed = true; + scene_manager_next_scene(app->scene_manager, event.event); + } + + return consumed; +} + +void nfc_maker_scene_menu_on_exit(void* context) { + NfcMaker* app = context; + submenu_reset(app->submenu); +} diff --git a/applications/external/nfc_maker/scenes/nfc_maker_scene_name.c b/applications/external/nfc_maker/scenes/nfc_maker_scene_name.c new file mode 100644 index 0000000000..dd5170d94a --- /dev/null +++ b/applications/external/nfc_maker/scenes/nfc_maker_scene_name.c @@ -0,0 +1,57 @@ +#include "../nfc_maker.h" + +enum TextInputResult { + TextInputResultOk, +}; + +static void nfc_maker_scene_name_text_input_callback(void* context) { + NfcMaker* app = context; + + view_dispatcher_send_custom_event(app->view_dispatcher, TextInputResultOk); +} + +void nfc_maker_scene_name_on_enter(void* context) { + NfcMaker* app = context; + TextInput* text_input = app->text_input; + + text_input_set_header_text(text_input, "Name the NFC tag:"); + + set_random_name(app->name_buf, TEXT_INPUT_LEN); + + text_input_set_result_callback( + text_input, + nfc_maker_scene_name_text_input_callback, + app, + app->name_buf, + TEXT_INPUT_LEN, + true); + + ValidatorIsFile* validator_is_file = + validator_is_file_alloc_init(NFC_APP_FOLDER, NFC_APP_EXTENSION, NULL); + text_input_set_validator(text_input, validator_is_file_callback, validator_is_file); + + view_dispatcher_switch_to_view(app->view_dispatcher, NfcMakerViewTextInput); +} + +bool nfc_maker_scene_name_on_event(void* context, SceneManagerEvent event) { + NfcMaker* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + switch(event.event) { + case TextInputResultOk: + scene_manager_next_scene(app->scene_manager, NfcMakerSceneResult); + break; + default: + break; + } + } + + return consumed; +} + +void nfc_maker_scene_name_on_exit(void* context) { + NfcMaker* app = context; + text_input_reset(app->text_input); +} diff --git a/applications/external/nfc_maker/scenes/nfc_maker_scene_phone.c b/applications/external/nfc_maker/scenes/nfc_maker_scene_phone.c new file mode 100644 index 0000000000..4e70bcb09b --- /dev/null +++ b/applications/external/nfc_maker/scenes/nfc_maker_scene_phone.c @@ -0,0 +1,53 @@ +#include "../nfc_maker.h" + +enum TextInputResult { + TextInputResultOk, +}; + +static void nfc_maker_scene_phone_text_input_callback(void* context) { + NfcMaker* app = context; + + view_dispatcher_send_custom_event(app->view_dispatcher, TextInputResultOk); +} + +void nfc_maker_scene_phone_on_enter(void* context) { + NfcMaker* app = context; + TextInput* text_input = app->text_input; + + text_input_set_header_text(text_input, "Enter Phone Number:"); + + strlcpy(app->text_buf, "+", TEXT_INPUT_LEN); + + text_input_set_result_callback( + text_input, + nfc_maker_scene_phone_text_input_callback, + app, + app->text_buf, + TEXT_INPUT_LEN, + false); + + view_dispatcher_switch_to_view(app->view_dispatcher, NfcMakerViewTextInput); +} + +bool nfc_maker_scene_phone_on_event(void* context, SceneManagerEvent event) { + NfcMaker* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + switch(event.event) { + case TextInputResultOk: + scene_manager_next_scene(app->scene_manager, NfcMakerSceneName); + break; + default: + break; + } + } + + return consumed; +} + +void nfc_maker_scene_phone_on_exit(void* context) { + NfcMaker* app = context; + text_input_reset(app->text_input); +} diff --git a/applications/external/nfc_maker/scenes/nfc_maker_scene_result.c b/applications/external/nfc_maker/scenes/nfc_maker_scene_result.c new file mode 100644 index 0000000000..912bf3c9fc --- /dev/null +++ b/applications/external/nfc_maker/scenes/nfc_maker_scene_result.c @@ -0,0 +1,359 @@ +#include "../nfc_maker.h" + +enum PopupEvent { + PopupEventExit, +}; + +static void nfc_maker_scene_result_popup_callback(void* context) { + NfcMaker* app = context; + + view_dispatcher_send_custom_event(app->view_dispatcher, PopupEventExit); +} + +void nfc_maker_scene_result_on_enter(void* context) { + NfcMaker* app = context; + Popup* popup = app->popup; + bool success = false; + + FlipperFormat* file = flipper_format_file_alloc(furi_record_open(RECORD_STORAGE)); + FuriString* path = furi_string_alloc(); + furi_string_printf(path, NFC_APP_FOLDER "/%s" NFC_APP_EXTENSION, app->name_buf); + do { + if(!flipper_format_file_open_new(file, furi_string_get_cstr(path))) break; + + uint32_t pages = 42; + size_t size = pages * 4; + uint8_t* buf = malloc(size); + + if(!flipper_format_write_header_cstr(file, "Flipper NFC device", 3)) break; + if(!flipper_format_write_string_cstr(file, "Device type", "NTAG203")) break; + + // Serial number + buf[0] = 0x04; + furi_hal_random_fill_buf(&buf[1], 8); + uint8_t uid[7]; + memcpy(&uid[0], &buf[0], 3); + memcpy(&uid[3], &buf[4], 4); + + if(!flipper_format_write_hex(file, "UID", uid, sizeof(uid))) break; + if(!flipper_format_write_string_cstr(file, "ATQA", "00 44")) break; + if(!flipper_format_write_string_cstr(file, "SAK", "00")) break; + // TODO: Maybe randomize? + if(!flipper_format_write_string_cstr( + file, + "Signature", + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00")) + break; + if(!flipper_format_write_string_cstr(file, "Mifare version", "00 00 00 00 00 00 00 00")) + break; + + if(!flipper_format_write_string_cstr(file, "Counter 0", "0")) break; + if(!flipper_format_write_string_cstr(file, "Tearing 0", "00")) break; + if(!flipper_format_write_string_cstr(file, "Counter 1", "0")) break; + if(!flipper_format_write_string_cstr(file, "Tearing 1", "00")) break; + if(!flipper_format_write_string_cstr(file, "Counter 2", "0")) break; + if(!flipper_format_write_string_cstr(file, "Tearing 2", "00")) break; + if(!flipper_format_write_uint32(file, "Pages total", &pages, 1)) break; + + // Static data + buf[9] = 0x48; // Internal + buf[10] = 0x00; // Lock bytes + buf[11] = 0x00; // ... + buf[12] = 0xE1; // Capability container + buf[13] = 0x10; // ... + buf[14] = 0x12; // ... + buf[15] = 0x00; // ... + buf[16] = 0x01; // ... + buf[17] = 0x03; // ... + buf[18] = 0xA0; // ... + buf[19] = 0x10; // ... + buf[20] = 0x44; // ... + buf[21] = 0x03; // Message flags + + size_t msg_len = 0; + switch(scene_manager_get_scene_state(app->scene_manager, NfcMakerSceneMenu)) { + case NfcMakerSceneBluetooth: { + msg_len = 0x2B; + + buf[23] = 0xD2; + buf[24] = 0x20; + buf[25] = 0x08; + buf[26] = 0x61; + buf[27] = 0x70; + + buf[28] = 0x70; + buf[29] = 0x6C; + buf[30] = 0x69; + buf[31] = 0x63; + + buf[32] = 0x61; + buf[33] = 0x74; + buf[34] = 0x69; + buf[35] = 0x6F; + + buf[36] = 0x6E; + buf[37] = 0x2F; + buf[38] = 0x76; + buf[39] = 0x6E; + + buf[40] = 0x64; + buf[41] = 0x2E; + buf[42] = 0x62; + buf[43] = 0x6C; + + buf[44] = 0x75; + buf[45] = 0x65; + buf[46] = 0x74; + buf[47] = 0x6F; + + buf[48] = 0x6F; + buf[49] = 0x74; + buf[50] = 0x68; + buf[51] = 0x2E; + + buf[52] = 0x65; + buf[53] = 0x70; + buf[54] = 0x2E; + buf[55] = 0x6F; + + buf[56] = 0x6F; + buf[57] = 0x62; + buf[58] = 0x08; + buf[59] = 0x00; + + memcpy(&buf[60], app->mac_buf, GAP_MAC_ADDR_SIZE); + break; + } + case NfcMakerSceneHttps: { + uint8_t data_len = strnlen(app->text_buf, TEXT_INPUT_LEN); + msg_len = data_len + 5; + + buf[23] = 0xD1; + buf[24] = 0x01; + buf[25] = data_len + 1; + buf[26] = 0x55; + + buf[27] = 0x04; // Prepend "https://" + memcpy(&buf[28], app->text_buf, data_len); + break; + } + case NfcMakerSceneMail: { + uint8_t data_len = strnlen(app->text_buf, TEXT_INPUT_LEN); + msg_len = data_len + 5; + + buf[23] = 0xD1; + buf[24] = 0x01; + buf[25] = data_len + 1; + buf[26] = 0x55; + + buf[27] = 0x06; // Prepend "mailto:" + memcpy(&buf[28], app->text_buf, data_len); + break; + } + case NfcMakerScenePhone: { + uint8_t data_len = strnlen(app->text_buf, TEXT_INPUT_LEN); + msg_len = data_len + 5; + + buf[23] = 0xD1; + buf[24] = 0x01; + buf[25] = data_len + 1; + buf[26] = 0x55; + + buf[27] = 0x05; // Prepend "tel:" + memcpy(&buf[28], app->text_buf, data_len); + break; + } + case NfcMakerSceneText: { + uint8_t data_len = strnlen(app->text_buf, TEXT_INPUT_LEN); + msg_len = data_len + 7; + + buf[23] = 0xD1; + buf[24] = 0x01; + buf[25] = data_len + 3; + buf[26] = 0x54; + + buf[27] = 0x02; + buf[28] = 0x65; // e + buf[29] = 0x6E; // n + memcpy(&buf[30], app->text_buf, data_len); + break; + } + case NfcMakerSceneUrl: { + uint8_t data_len = strnlen(app->text_buf, TEXT_INPUT_LEN); + msg_len = data_len + 5; + + buf[23] = 0xD1; + buf[24] = 0x01; + buf[25] = data_len + 1; + buf[26] = 0x55; + + buf[27] = 0x00; // No prepend + memcpy(&buf[28], app->text_buf, data_len); + break; + } + case NfcMakerSceneWifi: { + uint8_t ssid_len = strnlen(app->text_buf, WIFI_INPUT_LEN); + uint8_t pass_len = strnlen(app->pass_buf, WIFI_INPUT_LEN); + uint8_t data_len = ssid_len + pass_len; + msg_len = data_len + 73; + + buf[23] = 0xD2; + buf[24] = 0x17; + buf[25] = data_len + 47; + buf[26] = 0x61; + buf[27] = 0x70; + + buf[28] = 0x70; + buf[29] = 0x6C; + buf[30] = 0x69; + buf[31] = 0x63; + + buf[32] = 0x61; + buf[33] = 0x74; + buf[34] = 0x69; + buf[35] = 0x6F; + + buf[36] = 0x6E; + buf[37] = 0x2F; + buf[38] = 0x76; + buf[39] = 0x6E; + + buf[40] = 0x64; + buf[41] = 0x2E; + buf[42] = 0x77; + buf[43] = 0x66; + + buf[44] = 0x61; + buf[45] = 0x2E; + buf[46] = 0x77; + buf[47] = 0x73; + + buf[48] = 0x63; + buf[49] = 0x10; + buf[50] = 0x0E; + buf[51] = 0x00; + + buf[52] = data_len + 43; + buf[53] = 0x10; + buf[54] = 0x26; + buf[55] = 0x00; + + buf[56] = 0x01; + buf[57] = 0x01; + buf[58] = 0x10; + buf[59] = 0x45; + + buf[60] = 0x00; + buf[61] = ssid_len; + memcpy(&buf[62], app->text_buf, ssid_len); + size_t ssid = 62 + ssid_len; + buf[ssid + 0] = 0x10; + buf[ssid + 1] = 0x03; + + buf[ssid + 2] = 0x00; + buf[ssid + 3] = 0x02; + buf[ssid + 4] = 0x00; + buf[ssid + 5] = + scene_manager_get_scene_state(app->scene_manager, NfcMakerSceneWifiAuth); + + buf[ssid + 6] = 0x10; + buf[ssid + 7] = 0x0F; + buf[ssid + 8] = 0x00; + buf[ssid + 9] = 0x02; + + buf[ssid + 10] = 0x00; + buf[ssid + 11] = + scene_manager_get_scene_state(app->scene_manager, NfcMakerSceneWifiEncr); + buf[ssid + 12] = 0x10; + buf[ssid + 13] = 0x27; + + buf[ssid + 14] = 0x00; + buf[ssid + 15] = pass_len; + memcpy(&buf[ssid + 16], app->pass_buf, pass_len); + size_t pass = ssid + 16 + pass_len; + buf[pass + 0] = 0x10; + buf[pass + 1] = 0x20; + + buf[pass + 2] = 0x00; + buf[pass + 3] = 0x06; + buf[pass + 4] = 0xFF; + buf[pass + 5] = 0xFF; + + buf[pass + 6] = 0xFF; + buf[pass + 7] = 0xFF; + buf[pass + 8] = 0xFF; + buf[pass + 9] = 0xFF; + + break; + } + default: + break; + } + + // Message length and terminator + buf[22] = msg_len; + size_t msg_end = 23 + msg_len; + buf[msg_end] = 0xFE; + + // Padding + for(size_t i = msg_end + 1; i < size; i++) { + buf[i] = 0x00; + } + + char str[16]; + bool ok = true; + for(size_t page = 0; page < pages; page++) { + snprintf(str, sizeof(str), "Page %u", page); + if(!flipper_format_write_hex(file, str, &buf[page * 4], 4)) { + ok = false; + break; + } + } + if(!ok) break; + + free(buf); + success = true; + + } while(false); + furi_string_free(path); + flipper_format_free(file); + furi_record_close(RECORD_STORAGE); + + if(success) { + popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); + popup_set_header(popup, "Saved!", 13, 22, AlignLeft, AlignBottom); + } else { + popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); + popup_set_header(popup, "Saved!", 13, 22, AlignLeft, AlignBottom); + } + popup_set_timeout(popup, 1500); + popup_set_context(popup, app); + popup_set_callback(popup, nfc_maker_scene_result_popup_callback); + popup_enable_timeout(popup); + + view_dispatcher_switch_to_view(app->view_dispatcher, NfcMakerViewPopup); +} + +bool nfc_maker_scene_result_on_event(void* context, SceneManagerEvent event) { + NfcMaker* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + switch(event.event) { + case PopupEventExit: + scene_manager_search_and_switch_to_previous_scene( + app->scene_manager, NfcMakerSceneMenu); + break; + default: + break; + } + } + + return consumed; +} + +void nfc_maker_scene_result_on_exit(void* context) { + NfcMaker* app = context; + popup_reset(app->popup); +} diff --git a/applications/external/nfc_maker/scenes/nfc_maker_scene_text.c b/applications/external/nfc_maker/scenes/nfc_maker_scene_text.c new file mode 100644 index 0000000000..17d84e0a95 --- /dev/null +++ b/applications/external/nfc_maker/scenes/nfc_maker_scene_text.c @@ -0,0 +1,53 @@ +#include "../nfc_maker.h" + +enum TextInputResult { + TextInputResultOk, +}; + +static void nfc_maker_scene_text_text_input_callback(void* context) { + NfcMaker* app = context; + + view_dispatcher_send_custom_event(app->view_dispatcher, TextInputResultOk); +} + +void nfc_maker_scene_text_on_enter(void* context) { + NfcMaker* app = context; + TextInput* text_input = app->text_input; + + text_input_set_header_text(text_input, "Enter Text Note:"); + + strlcpy(app->text_buf, "Lorem ipsum", TEXT_INPUT_LEN); + + text_input_set_result_callback( + text_input, + nfc_maker_scene_text_text_input_callback, + app, + app->text_buf, + TEXT_INPUT_LEN, + true); + + view_dispatcher_switch_to_view(app->view_dispatcher, NfcMakerViewTextInput); +} + +bool nfc_maker_scene_text_on_event(void* context, SceneManagerEvent event) { + NfcMaker* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + switch(event.event) { + case TextInputResultOk: + scene_manager_next_scene(app->scene_manager, NfcMakerSceneName); + break; + default: + break; + } + } + + return consumed; +} + +void nfc_maker_scene_text_on_exit(void* context) { + NfcMaker* app = context; + text_input_reset(app->text_input); +} diff --git a/applications/external/nfc_maker/scenes/nfc_maker_scene_url.c b/applications/external/nfc_maker/scenes/nfc_maker_scene_url.c new file mode 100644 index 0000000000..1250b46afa --- /dev/null +++ b/applications/external/nfc_maker/scenes/nfc_maker_scene_url.c @@ -0,0 +1,53 @@ +#include "../nfc_maker.h" + +enum TextInputResult { + TextInputResultOk, +}; + +static void nfc_maker_scene_url_text_input_callback(void* context) { + NfcMaker* app = context; + + view_dispatcher_send_custom_event(app->view_dispatcher, TextInputResultOk); +} + +void nfc_maker_scene_url_on_enter(void* context) { + NfcMaker* app = context; + TextInput* text_input = app->text_input; + + text_input_set_header_text(text_input, "Enter Plain URL:"); + + strlcpy(app->text_buf, "https://flipper-xtre.me", TEXT_INPUT_LEN); + + text_input_set_result_callback( + text_input, + nfc_maker_scene_url_text_input_callback, + app, + app->text_buf, + TEXT_INPUT_LEN, + true); + + view_dispatcher_switch_to_view(app->view_dispatcher, NfcMakerViewTextInput); +} + +bool nfc_maker_scene_url_on_event(void* context, SceneManagerEvent event) { + NfcMaker* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + switch(event.event) { + case TextInputResultOk: + scene_manager_next_scene(app->scene_manager, NfcMakerSceneName); + break; + default: + break; + } + } + + return consumed; +} + +void nfc_maker_scene_url_on_exit(void* context) { + NfcMaker* app = context; + text_input_reset(app->text_input); +} diff --git a/applications/external/nfc_maker/scenes/nfc_maker_scene_wifi.c b/applications/external/nfc_maker/scenes/nfc_maker_scene_wifi.c new file mode 100644 index 0000000000..68efaf7f6e --- /dev/null +++ b/applications/external/nfc_maker/scenes/nfc_maker_scene_wifi.c @@ -0,0 +1,55 @@ +#include "../nfc_maker.h" + +enum TextInputResult { + TextInputResultOk, +}; + +static void nfc_maker_scene_wifi_text_input_callback(void* context) { + NfcMaker* app = context; + + view_dispatcher_send_custom_event(app->view_dispatcher, TextInputResultOk); +} + +void nfc_maker_scene_wifi_on_enter(void* context) { + NfcMaker* app = context; + TextInput* text_input = app->text_input; + + text_input_set_header_text(text_input, "Enter WiFi SSID:"); + + strlcpy(app->text_buf, "Bill Wi the Science Fi", WIFI_INPUT_LEN); + + text_input_set_result_callback( + text_input, + nfc_maker_scene_wifi_text_input_callback, + app, + app->text_buf, + WIFI_INPUT_LEN, + true); + + view_dispatcher_switch_to_view(app->view_dispatcher, NfcMakerViewTextInput); +} + +bool nfc_maker_scene_wifi_on_event(void* context, SceneManagerEvent event) { + NfcMaker* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + switch(event.event) { + case TextInputResultOk: + scene_manager_set_scene_state( + app->scene_manager, NfcMakerSceneWifiAuth, WifiAuthenticationWpa2Personal); + scene_manager_next_scene(app->scene_manager, NfcMakerSceneWifiAuth); + break; + default: + break; + } + } + + return consumed; +} + +void nfc_maker_scene_wifi_on_exit(void* context) { + NfcMaker* app = context; + text_input_reset(app->text_input); +} diff --git a/applications/external/nfc_maker/scenes/nfc_maker_scene_wifi_auth.c b/applications/external/nfc_maker/scenes/nfc_maker_scene_wifi_auth.c new file mode 100644 index 0000000000..4cb90dabe9 --- /dev/null +++ b/applications/external/nfc_maker/scenes/nfc_maker_scene_wifi_auth.c @@ -0,0 +1,83 @@ +#include "../nfc_maker.h" + +void nfc_maker_scene_wifi_auth_submenu_callback(void* context, uint32_t index) { + NfcMaker* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +void nfc_maker_scene_wifi_auth_on_enter(void* context) { + NfcMaker* app = context; + Submenu* submenu = app->submenu; + + submenu_set_header(submenu, "Authentication Type:"); + + submenu_add_item( + submenu, "Open", WifiAuthenticationOpen, nfc_maker_scene_wifi_auth_submenu_callback, app); + + submenu_add_item( + submenu, + "WPA 2 Personal", + WifiAuthenticationWpa2Personal, + nfc_maker_scene_wifi_auth_submenu_callback, + app); + + submenu_add_item( + submenu, + "WPA 2 Enterprise", + WifiAuthenticationWpa2Enterprise, + nfc_maker_scene_wifi_auth_submenu_callback, + app); + + submenu_add_item( + submenu, + "WPA Personal", + WifiAuthenticationWpaPersonal, + nfc_maker_scene_wifi_auth_submenu_callback, + app); + + submenu_add_item( + submenu, + "WPA Enterprise", + WifiAuthenticationWpaEnterprise, + nfc_maker_scene_wifi_auth_submenu_callback, + app); + + submenu_add_item( + submenu, + "Shared", + WifiAuthenticationShared, + nfc_maker_scene_wifi_auth_submenu_callback, + app); + + submenu_set_selected_item( + submenu, scene_manager_get_scene_state(app->scene_manager, NfcMakerSceneWifiAuth)); + + view_dispatcher_switch_to_view(app->view_dispatcher, NfcMakerViewSubmenu); +} + +bool nfc_maker_scene_wifi_auth_on_event(void* context, SceneManagerEvent event) { + NfcMaker* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + scene_manager_set_scene_state(app->scene_manager, NfcMakerSceneWifiAuth, event.event); + consumed = true; + if(event.event == WifiAuthenticationOpen) { + scene_manager_set_scene_state( + app->scene_manager, NfcMakerSceneWifiEncr, WifiEncryptionNone); + strcpy(app->pass_buf, ""); + scene_manager_next_scene(app->scene_manager, NfcMakerSceneName); + } else { + scene_manager_set_scene_state( + app->scene_manager, NfcMakerSceneWifiEncr, WifiEncryptionAes); + scene_manager_next_scene(app->scene_manager, NfcMakerSceneWifiEncr); + } + } + + return consumed; +} + +void nfc_maker_scene_wifi_auth_on_exit(void* context) { + NfcMaker* app = context; + submenu_reset(app->submenu); +} diff --git a/applications/external/nfc_maker/scenes/nfc_maker_scene_wifi_encr.c b/applications/external/nfc_maker/scenes/nfc_maker_scene_wifi_encr.c new file mode 100644 index 0000000000..d1a21f51a5 --- /dev/null +++ b/applications/external/nfc_maker/scenes/nfc_maker_scene_wifi_encr.c @@ -0,0 +1,48 @@ +#include "../nfc_maker.h" + +void nfc_maker_scene_wifi_encr_submenu_callback(void* context, uint32_t index) { + NfcMaker* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +void nfc_maker_scene_wifi_encr_on_enter(void* context) { + NfcMaker* app = context; + Submenu* submenu = app->submenu; + + submenu_set_header(submenu, "Encryption Type:"); + + submenu_add_item( + submenu, "AES", WifiEncryptionAes, nfc_maker_scene_wifi_encr_submenu_callback, app); + + submenu_add_item( + submenu, "WEP", WifiEncryptionWep, nfc_maker_scene_wifi_encr_submenu_callback, app); + + submenu_add_item( + submenu, "TKIP", WifiEncryptionTkip, nfc_maker_scene_wifi_encr_submenu_callback, app); + + submenu_add_item( + submenu, "None", WifiEncryptionNone, nfc_maker_scene_wifi_encr_submenu_callback, app); + + submenu_set_selected_item( + submenu, scene_manager_get_scene_state(app->scene_manager, NfcMakerSceneWifiEncr)); + + view_dispatcher_switch_to_view(app->view_dispatcher, NfcMakerViewSubmenu); +} + +bool nfc_maker_scene_wifi_encr_on_event(void* context, SceneManagerEvent event) { + NfcMaker* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + scene_manager_set_scene_state(app->scene_manager, NfcMakerSceneWifiEncr, event.event); + consumed = true; + scene_manager_next_scene(app->scene_manager, NfcMakerSceneWifiPass); + } + + return consumed; +} + +void nfc_maker_scene_wifi_encr_on_exit(void* context) { + NfcMaker* app = context; + submenu_reset(app->submenu); +} diff --git a/applications/external/nfc_maker/scenes/nfc_maker_scene_wifi_pass.c b/applications/external/nfc_maker/scenes/nfc_maker_scene_wifi_pass.c new file mode 100644 index 0000000000..3f5b4bd769 --- /dev/null +++ b/applications/external/nfc_maker/scenes/nfc_maker_scene_wifi_pass.c @@ -0,0 +1,53 @@ +#include "../nfc_maker.h" + +enum TextInputResult { + TextInputResultOk, +}; + +static void nfc_maker_scene_wifi_pass_text_input_callback(void* context) { + NfcMaker* app = context; + + view_dispatcher_send_custom_event(app->view_dispatcher, TextInputResultOk); +} + +void nfc_maker_scene_wifi_pass_on_enter(void* context) { + NfcMaker* app = context; + TextInput* text_input = app->text_input; + + text_input_set_header_text(text_input, "Enter WiFi Password:"); + + strlcpy(app->pass_buf, "244466666", WIFI_INPUT_LEN); + + text_input_set_result_callback( + text_input, + nfc_maker_scene_wifi_pass_text_input_callback, + app, + app->pass_buf, + WIFI_INPUT_LEN, + true); + + view_dispatcher_switch_to_view(app->view_dispatcher, NfcMakerViewTextInput); +} + +bool nfc_maker_scene_wifi_pass_on_event(void* context, SceneManagerEvent event) { + NfcMaker* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + switch(event.event) { + case TextInputResultOk: + scene_manager_next_scene(app->scene_manager, NfcMakerSceneName); + break; + default: + break; + } + } + + return consumed; +} + +void nfc_maker_scene_wifi_pass_on_exit(void* context) { + NfcMaker* app = context; + text_input_reset(app->text_input); +} diff --git a/applications/external/nfc_rfid_detector/application.fam b/applications/external/nfc_rfid_detector/application.fam new file mode 100644 index 0000000000..70c91bc843 --- /dev/null +++ b/applications/external/nfc_rfid_detector/application.fam @@ -0,0 +1,13 @@ +App( + appid="nfc_rfid_detector", + name="NFC/RFID detector", + apptype=FlipperAppType.EXTERNAL, + targets=["f7"], + entry_point="nfc_rfid_detector_app", + requires=["gui"], + stack_size=4 * 1024, + order=50, + fap_icon="nfc_rfid_detector_10px.png", + fap_category="Tools", + fap_icon_assets="images", +) diff --git a/applications/external/nfc_rfid_detector/helpers/nfc_rfid_detector_event.h b/applications/external/nfc_rfid_detector/helpers/nfc_rfid_detector_event.h new file mode 100644 index 0000000000..bbffe2938e --- /dev/null +++ b/applications/external/nfc_rfid_detector/helpers/nfc_rfid_detector_event.h @@ -0,0 +1,7 @@ +#pragma once + +typedef enum { + //NfcRfidDetectorCustomEvent + NfcRfidDetectorCustomEventStartId = 100, + +} NfcRfidDetectorCustomEvent; diff --git a/applications/external/nfc_rfid_detector/helpers/nfc_rfid_detector_types.h b/applications/external/nfc_rfid_detector/helpers/nfc_rfid_detector_types.h new file mode 100644 index 0000000000..5d44b09b7f --- /dev/null +++ b/applications/external/nfc_rfid_detector/helpers/nfc_rfid_detector_types.h @@ -0,0 +1,15 @@ +#pragma once + +#include +#include + +#define NFC_RFID_DETECTOR_VERSION_APP "0.1" +#define NFC_RFID_DETECTOR_DEVELOPED "SkorP" +#define NFC_RFID_DETECTOR_GITHUB "https://github.com/flipperdevices/flipperzero-firmware" + +typedef enum { + NfcRfidDetectorViewVariableItemList, + NfcRfidDetectorViewSubmenu, + NfcRfidDetectorViewFieldPresence, + NfcRfidDetectorViewWidget, +} NfcRfidDetectorView; diff --git a/applications/external/nfc_rfid_detector/images/NFC_detect_45x30.png b/applications/external/nfc_rfid_detector/images/NFC_detect_45x30.png new file mode 100644 index 0000000000..9d8a6f2abf Binary files /dev/null and b/applications/external/nfc_rfid_detector/images/NFC_detect_45x30.png differ diff --git a/applications/external/nfc_rfid_detector/images/Rfid_detect_45x30.png b/applications/external/nfc_rfid_detector/images/Rfid_detect_45x30.png new file mode 100644 index 0000000000..35c205049b Binary files /dev/null and b/applications/external/nfc_rfid_detector/images/Rfid_detect_45x30.png differ diff --git a/applications/external/nfc_rfid_detector/nfc_rfid_detector_10px.png b/applications/external/nfc_rfid_detector/nfc_rfid_detector_10px.png new file mode 100644 index 0000000000..7e875e028d Binary files /dev/null and b/applications/external/nfc_rfid_detector/nfc_rfid_detector_10px.png differ diff --git a/applications/external/nfc_rfid_detector/nfc_rfid_detector_app.c b/applications/external/nfc_rfid_detector/nfc_rfid_detector_app.c new file mode 100644 index 0000000000..cba8b60856 --- /dev/null +++ b/applications/external/nfc_rfid_detector/nfc_rfid_detector_app.c @@ -0,0 +1,108 @@ +#include "nfc_rfid_detector_app_i.h" + +#include +#include + +static bool nfc_rfid_detector_app_custom_event_callback(void* context, uint32_t event) { + furi_assert(context); + NfcRfidDetectorApp* app = context; + return scene_manager_handle_custom_event(app->scene_manager, event); +} + +static bool nfc_rfid_detector_app_back_event_callback(void* context) { + furi_assert(context); + NfcRfidDetectorApp* app = context; + return scene_manager_handle_back_event(app->scene_manager); +} + +static void nfc_rfid_detector_app_tick_event_callback(void* context) { + furi_assert(context); + NfcRfidDetectorApp* app = context; + scene_manager_handle_tick_event(app->scene_manager); +} + +NfcRfidDetectorApp* nfc_rfid_detector_app_alloc() { + NfcRfidDetectorApp* app = malloc(sizeof(NfcRfidDetectorApp)); + + // GUI + app->gui = furi_record_open(RECORD_GUI); + + // View Dispatcher + app->view_dispatcher = view_dispatcher_alloc(); + app->scene_manager = scene_manager_alloc(&nfc_rfid_detector_scene_handlers, app); + view_dispatcher_enable_queue(app->view_dispatcher); + + view_dispatcher_set_event_callback_context(app->view_dispatcher, app); + view_dispatcher_set_custom_event_callback( + app->view_dispatcher, nfc_rfid_detector_app_custom_event_callback); + view_dispatcher_set_navigation_event_callback( + app->view_dispatcher, nfc_rfid_detector_app_back_event_callback); + view_dispatcher_set_tick_event_callback( + app->view_dispatcher, nfc_rfid_detector_app_tick_event_callback, 100); + + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + + // Open Notification record + app->notifications = furi_record_open(RECORD_NOTIFICATION); + + // SubMenu + app->submenu = submenu_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, NfcRfidDetectorViewSubmenu, submenu_get_view(app->submenu)); + + // Widget + app->widget = widget_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, NfcRfidDetectorViewWidget, widget_get_view(app->widget)); + + // Field Presence + app->nfc_rfid_detector_field_presence = nfc_rfid_detector_view_field_presence_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, + NfcRfidDetectorViewFieldPresence, + nfc_rfid_detector_view_field_presence_get_view(app->nfc_rfid_detector_field_presence)); + + scene_manager_next_scene(app->scene_manager, NfcRfidDetectorSceneStart); + + return app; +} + +void nfc_rfid_detector_app_free(NfcRfidDetectorApp* app) { + furi_assert(app); + + // Submenu + view_dispatcher_remove_view(app->view_dispatcher, NfcRfidDetectorViewSubmenu); + submenu_free(app->submenu); + + // Widget + view_dispatcher_remove_view(app->view_dispatcher, NfcRfidDetectorViewWidget); + widget_free(app->widget); + + // Field Presence + view_dispatcher_remove_view(app->view_dispatcher, NfcRfidDetectorViewFieldPresence); + nfc_rfid_detector_view_field_presence_free(app->nfc_rfid_detector_field_presence); + + // View dispatcher + view_dispatcher_free(app->view_dispatcher); + scene_manager_free(app->scene_manager); + + // Notifications + furi_record_close(RECORD_NOTIFICATION); + app->notifications = NULL; + + // Close records + furi_record_close(RECORD_GUI); + + free(app); +} + +int32_t nfc_rfid_detector_app(void* p) { + UNUSED(p); + NfcRfidDetectorApp* nfc_rfid_detector_app = nfc_rfid_detector_app_alloc(); + + view_dispatcher_run(nfc_rfid_detector_app->view_dispatcher); + + nfc_rfid_detector_app_free(nfc_rfid_detector_app); + + return 0; +} diff --git a/applications/external/nfc_rfid_detector/nfc_rfid_detector_app_i.c b/applications/external/nfc_rfid_detector/nfc_rfid_detector_app_i.c new file mode 100644 index 0000000000..c59d40d50c --- /dev/null +++ b/applications/external/nfc_rfid_detector/nfc_rfid_detector_app_i.c @@ -0,0 +1,40 @@ +#include "nfc_rfid_detector_app_i.h" + +#include + +#define TAG "NfcRfidDetector" + +void nfc_rfid_detector_app_field_presence_start(NfcRfidDetectorApp* app) { + furi_assert(app); + + // start the field presence rfid detection + furi_hal_rfid_field_detect_start(); + + // start the field presence nfc detection + furi_hal_nfc_exit_sleep(); + furi_hal_nfc_field_detect_start(); +} + +void nfc_rfid_detector_app_field_presence_stop(NfcRfidDetectorApp* app) { + furi_assert(app); + + // stop the field presence rfid detection + furi_hal_rfid_field_detect_stop(); + + // stop the field presence nfc detection + furi_hal_nfc_start_sleep(); +} + +bool nfc_rfid_detector_app_field_presence_is_nfc(NfcRfidDetectorApp* app) { + furi_assert(app); + + // check if the field presence is nfc + return furi_hal_nfc_field_is_present(); +} + +bool nfc_rfid_detector_app_field_presence_is_rfid(NfcRfidDetectorApp* app, uint32_t* frequency) { + furi_assert(app); + + // check if the field presence is rfid + return furi_hal_rfid_field_is_present(frequency); +} \ No newline at end of file diff --git a/applications/external/nfc_rfid_detector/nfc_rfid_detector_app_i.h b/applications/external/nfc_rfid_detector/nfc_rfid_detector_app_i.h new file mode 100644 index 0000000000..72cb126d4a --- /dev/null +++ b/applications/external/nfc_rfid_detector/nfc_rfid_detector_app_i.h @@ -0,0 +1,30 @@ +#pragma once + +#include "helpers/nfc_rfid_detector_types.h" +#include "helpers/nfc_rfid_detector_event.h" + +#include "scenes/nfc_rfid_detector_scene.h" +#include +#include +#include +#include +#include +#include +#include "views/nfc_rfid_detector_view_field_presence.h" + +typedef struct NfcRfidDetectorApp NfcRfidDetectorApp; + +struct NfcRfidDetectorApp { + Gui* gui; + ViewDispatcher* view_dispatcher; + SceneManager* scene_manager; + NotificationApp* notifications; + Submenu* submenu; + Widget* widget; + NfcRfidDetectorFieldPresence* nfc_rfid_detector_field_presence; +}; + +void nfc_rfid_detector_app_field_presence_start(NfcRfidDetectorApp* app); +void nfc_rfid_detector_app_field_presence_stop(NfcRfidDetectorApp* app); +bool nfc_rfid_detector_app_field_presence_is_nfc(NfcRfidDetectorApp* app); +bool nfc_rfid_detector_app_field_presence_is_rfid(NfcRfidDetectorApp* app, uint32_t* frequency); \ No newline at end of file diff --git a/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene.c b/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene.c new file mode 100644 index 0000000000..d75eb2884f --- /dev/null +++ b/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene.c @@ -0,0 +1,31 @@ +#include "../nfc_rfid_detector_app_i.h" + +// Generate scene on_enter handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, +void (*const nfc_rfid_detector_scene_on_enter_handlers[])(void*) = { +#include "nfc_rfid_detector_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_event handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event, +bool (*const nfc_rfid_detector_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = + { +#include "nfc_rfid_detector_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_exit handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit, +void (*const nfc_rfid_detector_scene_on_exit_handlers[])(void* context) = { +#include "nfc_rfid_detector_scene_config.h" +}; +#undef ADD_SCENE + +// Initialize scene handlers configuration structure +const SceneManagerHandlers nfc_rfid_detector_scene_handlers = { + .on_enter_handlers = nfc_rfid_detector_scene_on_enter_handlers, + .on_event_handlers = nfc_rfid_detector_scene_on_event_handlers, + .on_exit_handlers = nfc_rfid_detector_scene_on_exit_handlers, + .scene_num = NfcRfidDetectorSceneNum, +}; diff --git a/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene.h b/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene.h new file mode 100644 index 0000000000..74d324b4d3 --- /dev/null +++ b/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +// Generate scene id and total number +#define ADD_SCENE(prefix, name, id) NfcRfidDetectorScene##id, +typedef enum { +#include "nfc_rfid_detector_scene_config.h" + NfcRfidDetectorSceneNum, +} NfcRfidDetectorScene; +#undef ADD_SCENE + +extern const SceneManagerHandlers nfc_rfid_detector_scene_handlers; + +// Generate scene on_enter handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); +#include "nfc_rfid_detector_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_event handlers declaration +#define ADD_SCENE(prefix, name, id) \ + bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event); +#include "nfc_rfid_detector_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_exit handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context); +#include "nfc_rfid_detector_scene_config.h" +#undef ADD_SCENE diff --git a/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_about.c b/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_about.c new file mode 100644 index 0000000000..ddcb8aac0f --- /dev/null +++ b/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_about.c @@ -0,0 +1,69 @@ +#include "../nfc_rfid_detector_app_i.h" + +void nfc_rfid_detector_scene_about_widget_callback( + GuiButtonType result, + InputType type, + void* context) { + NfcRfidDetectorApp* app = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(app->view_dispatcher, result); + } +} + +void nfc_rfid_detector_scene_about_on_enter(void* context) { + NfcRfidDetectorApp* app = context; + + FuriString* temp_str; + temp_str = furi_string_alloc(); + furi_string_printf(temp_str, "\e#%s\n", "Information"); + + furi_string_cat_printf(temp_str, "Version: %s\n", NFC_RFID_DETECTOR_VERSION_APP); + furi_string_cat_printf(temp_str, "Developed by: %s\n", NFC_RFID_DETECTOR_DEVELOPED); + furi_string_cat_printf(temp_str, "Github: %s\n\n", NFC_RFID_DETECTOR_GITHUB); + + furi_string_cat_printf(temp_str, "\e#%s\n", "Description"); + furi_string_cat_printf( + temp_str, + "This application allows\nyou to determine what\ntype of electromagnetic\nfield the reader is using.\nFor LF RFID you can also\nsee the carrier frequency\n\n"); + + widget_add_text_box_element( + app->widget, + 0, + 0, + 128, + 14, + AlignCenter, + AlignBottom, + "\e#\e! \e!\n", + false); + widget_add_text_box_element( + app->widget, + 0, + 2, + 128, + 14, + AlignCenter, + AlignBottom, + "\e#\e! NFC/RFID detector \e!\n", + false); + widget_add_text_scroll_element(app->widget, 0, 16, 128, 50, furi_string_get_cstr(temp_str)); + furi_string_free(temp_str); + + view_dispatcher_switch_to_view(app->view_dispatcher, NfcRfidDetectorViewWidget); +} + +bool nfc_rfid_detector_scene_about_on_event(void* context, SceneManagerEvent event) { + NfcRfidDetectorApp* app = context; + bool consumed = false; + UNUSED(app); + UNUSED(event); + + return consumed; +} + +void nfc_rfid_detector_scene_about_on_exit(void* context) { + NfcRfidDetectorApp* app = context; + + // Clear views + widget_reset(app->widget); +} diff --git a/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_config.h b/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_config.h new file mode 100644 index 0000000000..ab49ad5c2c --- /dev/null +++ b/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_config.h @@ -0,0 +1,3 @@ +ADD_SCENE(nfc_rfid_detector, start, Start) +ADD_SCENE(nfc_rfid_detector, about, About) +ADD_SCENE(nfc_rfid_detector, field_presence, FieldPresence) diff --git a/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_field_presence.c b/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_field_presence.c new file mode 100644 index 0000000000..ec53b5a0a6 --- /dev/null +++ b/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_field_presence.c @@ -0,0 +1,60 @@ +#include "../nfc_rfid_detector_app_i.h" +#include "../views/nfc_rfid_detector_view_field_presence.h" + +void nfc_rfid_detector_scene_field_presence_callback( + NfcRfidDetectorCustomEvent event, + void* context) { + furi_assert(context); + NfcRfidDetectorApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, event); +} + +static const NotificationSequence notification_app_display_on = { + + &message_display_backlight_on, + NULL, +}; + +static void nfc_rfid_detector_scene_field_presence_update(void* context) { + furi_assert(context); + NfcRfidDetectorApp* app = context; + + uint32_t frequency = 0; + bool nfc_field = nfc_rfid_detector_app_field_presence_is_nfc(app); + bool rfid_field = nfc_rfid_detector_app_field_presence_is_rfid(app, &frequency); + + if(nfc_field || rfid_field) + notification_message(app->notifications, ¬ification_app_display_on); + + nfc_rfid_detector_view_field_presence_update( + app->nfc_rfid_detector_field_presence, nfc_field, rfid_field, frequency); +} + +void nfc_rfid_detector_scene_field_presence_on_enter(void* context) { + furi_assert(context); + NfcRfidDetectorApp* app = context; + + // Start detection of field presence + nfc_rfid_detector_app_field_presence_start(app); + + view_dispatcher_switch_to_view(app->view_dispatcher, NfcRfidDetectorViewFieldPresence); +} + +bool nfc_rfid_detector_scene_field_presence_on_event(void* context, SceneManagerEvent event) { + furi_assert(context); + NfcRfidDetectorApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeTick) { + nfc_rfid_detector_scene_field_presence_update(app); + } + + return consumed; +} + +void nfc_rfid_detector_scene_field_presence_on_exit(void* context) { + furi_assert(context); + NfcRfidDetectorApp* app = context; + // Stop detection of field presence + nfc_rfid_detector_app_field_presence_stop(app); +} diff --git a/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_start.c b/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_start.c new file mode 100644 index 0000000000..7b71bd9735 --- /dev/null +++ b/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_start.c @@ -0,0 +1,58 @@ +#include "../nfc_rfid_detector_app_i.h" + +typedef enum { + SubmenuIndexNfcRfidDetectorFieldPresence, + SubmenuIndexNfcRfidDetectorAbout, +} SubmenuIndex; + +void nfc_rfid_detector_scene_start_submenu_callback(void* context, uint32_t index) { + NfcRfidDetectorApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +void nfc_rfid_detector_scene_start_on_enter(void* context) { + UNUSED(context); + NfcRfidDetectorApp* app = context; + Submenu* submenu = app->submenu; + + submenu_add_item( + submenu, + "Detect field type", + SubmenuIndexNfcRfidDetectorFieldPresence, + nfc_rfid_detector_scene_start_submenu_callback, + app); + submenu_add_item( + submenu, + "About", + SubmenuIndexNfcRfidDetectorAbout, + nfc_rfid_detector_scene_start_submenu_callback, + app); + + submenu_set_selected_item( + submenu, scene_manager_get_scene_state(app->scene_manager, NfcRfidDetectorSceneStart)); + + view_dispatcher_switch_to_view(app->view_dispatcher, NfcRfidDetectorViewSubmenu); +} + +bool nfc_rfid_detector_scene_start_on_event(void* context, SceneManagerEvent event) { + NfcRfidDetectorApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexNfcRfidDetectorAbout) { + scene_manager_next_scene(app->scene_manager, NfcRfidDetectorSceneAbout); + consumed = true; + } else if(event.event == SubmenuIndexNfcRfidDetectorFieldPresence) { + scene_manager_next_scene(app->scene_manager, NfcRfidDetectorSceneFieldPresence); + consumed = true; + } + scene_manager_set_scene_state(app->scene_manager, NfcRfidDetectorSceneStart, event.event); + } + + return consumed; +} + +void nfc_rfid_detector_scene_start_on_exit(void* context) { + NfcRfidDetectorApp* app = context; + submenu_reset(app->submenu); +} diff --git a/applications/external/nfc_rfid_detector/views/nfc_rfid_detector_view_field_presence.c b/applications/external/nfc_rfid_detector/views/nfc_rfid_detector_view_field_presence.c new file mode 100644 index 0000000000..faf253977c --- /dev/null +++ b/applications/external/nfc_rfid_detector/views/nfc_rfid_detector_view_field_presence.c @@ -0,0 +1,165 @@ +#include "nfc_rfid_detector_view_field_presence.h" +#include "../nfc_rfid_detector_app_i.h" +#include +#include + +#include +#include + +#define FIELD_FOUND_WEIGHT 5 + +typedef enum { + NfcRfidDetectorTypeFieldPresenceNfc, + NfcRfidDetectorTypeFieldPresenceRfid, +} NfcRfidDetectorTypeFieldPresence; + +static const Icon* NfcRfidDetectorFieldPresenceIcons[] = { + [NfcRfidDetectorTypeFieldPresenceNfc] = &I_NFC_detect_45x30, + [NfcRfidDetectorTypeFieldPresenceRfid] = &I_Rfid_detect_45x30, +}; + +struct NfcRfidDetectorFieldPresence { + View* view; +}; + +typedef struct { + uint8_t nfc_field; + uint8_t rfid_field; + uint32_t rfid_frequency; +} NfcRfidDetectorFieldPresenceModel; + +void nfc_rfid_detector_view_field_presence_update( + NfcRfidDetectorFieldPresence* instance, + bool nfc_field, + bool rfid_field, + uint32_t rfid_frequency) { + furi_assert(instance); + with_view_model( + instance->view, + NfcRfidDetectorFieldPresenceModel * model, + { + if(nfc_field) { + model->nfc_field = FIELD_FOUND_WEIGHT; + } else if(model->nfc_field) { + model->nfc_field--; + } + if(rfid_field) { + model->rfid_field = FIELD_FOUND_WEIGHT; + model->rfid_frequency = rfid_frequency; + } else if(model->rfid_field) { + model->rfid_field--; + } + }, + true); +} + +void nfc_rfid_detector_view_field_presence_draw( + Canvas* canvas, + NfcRfidDetectorFieldPresenceModel* model) { + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + + if(!model->nfc_field && !model->rfid_field) { + canvas_draw_icon(canvas, 0, 16, &I_Modern_reader_18x34); + canvas_draw_icon(canvas, 22, 12, &I_Move_flipper_26x39); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 56, 36, "Touch the reader"); + } else { + if(model->nfc_field) { + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 21, 10, "NFC"); + canvas_draw_icon( + canvas, + 9, + 17, + NfcRfidDetectorFieldPresenceIcons[NfcRfidDetectorTypeFieldPresenceNfc]); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 9, 62, "13,56 MHz"); + } + + if(model->rfid_field) { + char str[16]; + snprintf(str, sizeof(str), "%.02f KHz", (double)model->rfid_frequency / 1000); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 76, 10, "LF RFID"); + canvas_draw_icon( + canvas, + 71, + 17, + NfcRfidDetectorFieldPresenceIcons[NfcRfidDetectorTypeFieldPresenceRfid]); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 69, 62, str); + } + } +} + +bool nfc_rfid_detector_view_field_presence_input(InputEvent* event, void* context) { + furi_assert(context); + NfcRfidDetectorFieldPresence* instance = context; + UNUSED(instance); + + if(event->key == InputKeyBack) { + return false; + } + + return true; +} + +void nfc_rfid_detector_view_field_presence_enter(void* context) { + furi_assert(context); + NfcRfidDetectorFieldPresence* instance = context; + with_view_model( + instance->view, + NfcRfidDetectorFieldPresenceModel * model, + { + model->nfc_field = 0; + model->rfid_field = 0; + model->rfid_frequency = 0; + }, + true); +} + +void nfc_rfid_detector_view_field_presence_exit(void* context) { + furi_assert(context); + NfcRfidDetectorFieldPresence* instance = context; + UNUSED(instance); +} + +NfcRfidDetectorFieldPresence* nfc_rfid_detector_view_field_presence_alloc() { + NfcRfidDetectorFieldPresence* instance = malloc(sizeof(NfcRfidDetectorFieldPresence)); + + // View allocation and configuration + instance->view = view_alloc(); + + view_allocate_model( + instance->view, ViewModelTypeLocking, sizeof(NfcRfidDetectorFieldPresenceModel)); + view_set_context(instance->view, instance); + view_set_draw_callback( + instance->view, (ViewDrawCallback)nfc_rfid_detector_view_field_presence_draw); + view_set_input_callback(instance->view, nfc_rfid_detector_view_field_presence_input); + view_set_enter_callback(instance->view, nfc_rfid_detector_view_field_presence_enter); + view_set_exit_callback(instance->view, nfc_rfid_detector_view_field_presence_exit); + + with_view_model( + instance->view, + NfcRfidDetectorFieldPresenceModel * model, + { + model->nfc_field = 0; + model->rfid_field = 0; + model->rfid_frequency = 0; + }, + true); + return instance; +} + +void nfc_rfid_detector_view_field_presence_free(NfcRfidDetectorFieldPresence* instance) { + furi_assert(instance); + + view_free(instance->view); + free(instance); +} + +View* nfc_rfid_detector_view_field_presence_get_view(NfcRfidDetectorFieldPresence* instance) { + furi_assert(instance); + return instance->view; +} diff --git a/applications/external/nfc_rfid_detector/views/nfc_rfid_detector_view_field_presence.h b/applications/external/nfc_rfid_detector/views/nfc_rfid_detector_view_field_presence.h new file mode 100644 index 0000000000..0ddb4e2cd5 --- /dev/null +++ b/applications/external/nfc_rfid_detector/views/nfc_rfid_detector_view_field_presence.h @@ -0,0 +1,19 @@ +#pragma once + +#include +#include "../helpers/nfc_rfid_detector_types.h" +#include "../helpers/nfc_rfid_detector_event.h" + +typedef struct NfcRfidDetectorFieldPresence NfcRfidDetectorFieldPresence; + +void nfc_rfid_detector_view_field_presence_update( + NfcRfidDetectorFieldPresence* instance, + bool nfc_field, + bool rfid_field, + uint32_t rfid_frequency); + +NfcRfidDetectorFieldPresence* nfc_rfid_detector_view_field_presence_alloc(); + +void nfc_rfid_detector_view_field_presence_free(NfcRfidDetectorFieldPresence* instance); + +View* nfc_rfid_detector_view_field_presence_get_view(NfcRfidDetectorFieldPresence* instance); diff --git a/applications/external/nightstand/application.fam b/applications/external/nightstand/application.fam index 1fb5720544..28b8b8694b 100644 --- a/applications/external/nightstand/application.fam +++ b/applications/external/nightstand/application.fam @@ -8,4 +8,8 @@ App( fap_icon="clock.png", fap_category="Misc", order=81, + fap_author="@nymda & @Willy-JL", + fap_weburl="https://github.com/nymda/FlipperNightStand", + fap_version="1.0", + fap_description="Clock with screen brightness controls", ) diff --git a/applications/external/nrf24batch/LICENSE b/applications/external/nrf24batch/LICENSE new file mode 100644 index 0000000000..f288702d2f --- /dev/null +++ b/applications/external/nrf24batch/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/nrf24batch/application.fam b/applications/external/nrf24batch/application.fam new file mode 100644 index 0000000000..ff8e5546ed --- /dev/null +++ b/applications/external/nrf24batch/application.fam @@ -0,0 +1,20 @@ +App( + appid="nrf24batch", + name="[NRF24] Batch", + apptype=FlipperAppType.EXTERNAL, + entry_point="nrf24batch_app", + cdefines=["APP_NRF24BATCH"], + requires=["gui"], + stack_size=2 * 1024, + order=60, + fap_icon="nrf24batch_10px.png", + fap_category="GPIO", + fap_private_libs=[ + Lib( + name="nrf24", + sources=[ + "nrf24.c", + ], + ), + ], +) diff --git a/applications/external/nrf24batch/lib/nrf24/nrf24.c b/applications/external/nrf24batch/lib/nrf24/nrf24.c new file mode 100644 index 0000000000..789002f80d --- /dev/null +++ b/applications/external/nrf24batch/lib/nrf24/nrf24.c @@ -0,0 +1,365 @@ +// Modified by vad7, 24.02.2023 +// +#include "nrf24.h" +#include +#include +#include +#include +#include + +void nrf24_init() { + furi_hal_spi_bus_handle_init(nrf24_HANDLE); + furi_hal_spi_acquire(nrf24_HANDLE); + furi_hal_gpio_init(nrf24_CE_PIN, GpioModeOutputPushPull, GpioPullUp, GpioSpeedVeryHigh); + furi_hal_gpio_write(nrf24_CE_PIN, false); +} + +void nrf24_deinit() { + furi_hal_spi_release(nrf24_HANDLE); + furi_hal_spi_bus_handle_deinit(nrf24_HANDLE); + furi_hal_gpio_write(nrf24_CE_PIN, false); + furi_hal_gpio_init(nrf24_CE_PIN, GpioModeAnalog, GpioPullNo, GpioSpeedLow); +} + +void nrf24_spi_trx( + FuriHalSpiBusHandle* handle, + uint8_t* tx, + uint8_t* rx, + uint8_t size) { + furi_hal_gpio_write(handle->cs, false); + furi_hal_spi_bus_trx(handle, tx, rx, size, nrf24_TIMEOUT); + furi_hal_gpio_write(handle->cs, true); +} + +uint8_t nrf24_write_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t data) { + uint8_t buf[] = {W_REGISTER | (REGISTER_MASK & reg), data}; + nrf24_spi_trx(handle, buf, buf, 2); + //FURI_LOG_D("NRF_WR", " #%02X=%02X", reg, data); + return buf[0]; +} + +uint8_t nrf24_write_buf_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* data, uint8_t size) { + uint8_t buf[size + 1]; + buf[0] = W_REGISTER | (REGISTER_MASK & reg); + memcpy(&buf[1], data, size); + nrf24_spi_trx(handle, buf, buf, size + 1); + //FURI_LOG_D("NRF_WR", " #%02X(%02X)=0x%02X%02X%02X%02X%02X", reg, size, data[0], data[1], data[2], data[3], data[4] ); + return buf[0]; +} + +uint8_t nrf24_read_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* data, uint8_t size) { + uint8_t buf[size + 1]; + memset(buf, 0, size + 1); + buf[0] = R_REGISTER | (REGISTER_MASK & reg); + nrf24_spi_trx(handle, buf, buf, size + 1); + memcpy(data, &buf[1], size); + return buf[0]; +} + +uint8_t nrf24_read_register(FuriHalSpiBusHandle* handle, uint8_t reg) { + uint8_t buf[] = { R_REGISTER | (REGISTER_MASK & reg), 0 }; + nrf24_spi_trx(handle, buf, buf, 2); + return buf[1]; +} + +uint8_t nrf24_flush_rx(FuriHalSpiBusHandle* handle) { + uint8_t tx[] = {FLUSH_RX}; + uint8_t rx[] = {0}; + nrf24_spi_trx(handle, tx, rx, 1); + return rx[0]; +} + +uint8_t nrf24_flush_tx(FuriHalSpiBusHandle* handle) { + uint8_t tx[] = {FLUSH_TX}; + uint8_t rx[] = {0}; + nrf24_spi_trx(handle, tx, rx, 1); + return rx[0]; +} + +uint8_t nrf24_get_maclen(FuriHalSpiBusHandle* handle) { + uint8_t maclen; + nrf24_read_reg(handle, REG_SETUP_AW, &maclen, 1); + maclen &= 3; + return maclen + 2; +} + +uint8_t nrf24_set_maclen(FuriHalSpiBusHandle* handle, uint8_t maclen) { + assert(maclen > 1 && maclen < 6); + uint8_t status = 0; + status = nrf24_write_reg(handle, REG_SETUP_AW, maclen - 2); + return status; +} + +uint8_t nrf24_status(FuriHalSpiBusHandle* handle) { + uint8_t tx = RF24_NOP; + nrf24_spi_trx(handle, &tx, &tx, 1); + return tx; +} + +uint32_t nrf24_get_rate(FuriHalSpiBusHandle* handle) { + uint8_t setup = 0; + uint32_t rate = 0; + nrf24_read_reg(handle, REG_RF_SETUP, &setup, 1); + setup &= 0x28; + if(setup == 0x20) + rate = 250000; // 250kbps + else if(setup == 0x08) + rate = 2000000; // 2Mbps + else if(setup == 0x00) + rate = 1000000; // 1Mbps + + return rate; +} + +uint8_t nrf24_set_rate(FuriHalSpiBusHandle* handle, uint32_t rate) { + uint8_t r6 = 0; + uint8_t status = 0; + if(!rate) rate = 2000000; + + nrf24_read_reg(handle, REG_RF_SETUP, &r6, 1); // RF_SETUP register + r6 = r6 & (~0x28); // Clear rate fields. + if(rate == 2000000) + r6 = r6 | 0x08; + else if(rate == 1000000) + r6 = r6; + else if(rate == 250000) + r6 = r6 | 0x20; + + status = nrf24_write_reg(handle, REG_RF_SETUP, r6); // Write new rate. + return status; +} + +uint8_t nrf24_get_chan(FuriHalSpiBusHandle* handle) { + uint8_t channel = 0; + nrf24_read_reg(handle, REG_RF_CH, &channel, 1); + return channel; +} + +uint8_t nrf24_set_chan(FuriHalSpiBusHandle* handle, uint8_t chan) { + uint8_t status; + status = nrf24_write_reg(handle, REG_RF_CH, chan); + return status; +} + +uint8_t nrf24_get_src_mac(FuriHalSpiBusHandle* handle, uint8_t* mac) { + uint8_t size = 0; + uint8_t status = 0; + size = nrf24_get_maclen(handle); + status = nrf24_read_reg(handle, REG_RX_ADDR_P0, mac, size); + return status; +} + +uint8_t nrf24_set_src_mac(FuriHalSpiBusHandle* handle, uint8_t* mac, uint8_t size) { + uint8_t status = 0; + uint8_t clearmac[] = {0, 0, 0, 0, 0}; + nrf24_set_maclen(handle, size); + nrf24_write_buf_reg(handle, REG_RX_ADDR_P0, clearmac, 5); + status = nrf24_write_buf_reg(handle, REG_RX_ADDR_P0, mac, size); + return status; +} + +uint8_t nrf24_get_dst_mac(FuriHalSpiBusHandle* handle, uint8_t* mac) { + uint8_t size = 0; + uint8_t status = 0; + size = nrf24_get_maclen(handle); + status = nrf24_read_reg(handle, REG_TX_ADDR, mac, size); + return status; +} + +uint8_t nrf24_set_dst_mac(FuriHalSpiBusHandle* handle, uint8_t* mac, uint8_t size) { + uint8_t status = 0; + uint8_t clearmac[] = {0, 0, 0, 0, 0}; + nrf24_set_maclen(handle, size); + nrf24_write_buf_reg(handle, REG_TX_ADDR, clearmac, 5); + status = nrf24_write_buf_reg(handle, REG_TX_ADDR, mac, size); + return status; +} + +uint8_t nrf24_get_packetlen(FuriHalSpiBusHandle* handle, uint8_t pipe) { + uint8_t len = 0; + if(pipe > 5) pipe = 0; + nrf24_read_reg(handle, RX_PW_P0 + pipe, &len, 1); + return len; +} + +uint8_t nrf24_set_packetlen(FuriHalSpiBusHandle* handle, uint8_t len) { + uint8_t status = 0; + status = nrf24_write_reg(handle, RX_PW_P0, len); + return status; +} + +// packet_size: 0 - dyn payload (read from PL_WID), 1 - read from pipe size, >1 - override +// Return STATUS reg + additional: RX_DR - new data available, 0x80 - NRF24 hardware error +uint8_t nrf24_rxpacket(FuriHalSpiBusHandle* handle, uint8_t* packet, uint8_t* ret_packetsize, uint8_t packet_size) { + uint8_t status = 0; + uint8_t buf[33]; // 32 max payload size + 1 for command + + status = nrf24_status(handle); + if(!(status & RX_DR)) { + uint8_t st = nrf24_read_register(handle, REG_FIFO_STATUS); + if(st == 0xFF || st == 0) return 0x80; // hardware error + if((st & 1) == 0) { + FURI_LOG_D("NRF", "FIFO PKT"); + status |= RX_DR; // packet in FIFO buffer + } + } + if(status & RX_DR) { + if(status & 0x80) return 0x80; // hardware error + if(packet_size == 1) + packet_size = nrf24_get_packetlen(handle, (status >> 1) & 7); + else if(packet_size == 0){ + buf[0] = R_RX_PL_WID; buf[1] = 0xFF; + nrf24_spi_trx(handle, buf, buf, 2); + packet_size = buf[1]; + } + if(packet_size > 32 || packet_size == 0) packet_size = 32; + memset(buf, 0, packet_size + 1); + buf[0] = R_RX_PAYLOAD; + nrf24_spi_trx(handle, buf, buf, packet_size + 1); + memcpy(packet, &buf[1], packet_size); + nrf24_write_reg(handle, REG_STATUS, RX_DR); // clear RX_DR + } + if(status & (MAX_RT)) { // MAX_RT + nrf24_write_reg(handle, REG_STATUS, (MAX_RT)); // clear MAX_RT. + } + + *ret_packetsize = packet_size; + return status; +} + +// Return 0 when error +uint8_t nrf24_txpacket(FuriHalSpiBusHandle* handle, uint8_t* payload, uint8_t size, bool ack) { + uint8_t status = 0; + uint8_t buf[size + 1]; + buf[0] = ack ? W_TX_PAYLOAD : W_TX_PAYLOAD_NOACK; + memcpy(&buf[1], payload, size); + nrf24_set_tx_mode(handle); + nrf24_spi_trx(handle, buf, buf, size + 1); + uint32_t start_time = furi_get_tick(); + do { + furi_delay_us(100); + status = nrf24_status(handle); + } while(!(status & (TX_DS | MAX_RT)) && furi_get_tick() - start_time < 100UL); + if(status & MAX_RT) { + if(furi_log_get_level() == FuriLogLevelDebug) FURI_LOG_D("NRF", "MAX RT: %X (%X)", nrf24_read_register(handle, REG_OBSERVE_TX), status); + nrf24_flush_tx(handle); + } + furi_hal_gpio_write(nrf24_CE_PIN, false); + //nrf24_set_idle(handle); + if(status & (TX_DS | MAX_RT)) nrf24_write_reg(handle, REG_STATUS, TX_DS | MAX_RT); + return status & TX_DS; +} + +uint8_t nrf24_power_up(FuriHalSpiBusHandle* handle) { + uint8_t status = 0; + uint8_t cfg = 0; + nrf24_read_reg(handle, REG_CONFIG, &cfg, 1); + cfg = cfg | 2; + status = nrf24_write_reg(handle, REG_CONFIG, cfg); + //furi_delay_ms(1000); + return status; +} + +uint8_t nrf24_set_idle(FuriHalSpiBusHandle* handle) { + uint8_t status = 0; + uint8_t cfg = 0; + nrf24_read_reg(handle, REG_CONFIG, &cfg, 1); + cfg &= 0xfc; // clear bottom two bits to power down the radio + status = nrf24_write_reg(handle, REG_CONFIG, cfg); + furi_hal_gpio_write(nrf24_CE_PIN, false); + return status; +} + +uint8_t nrf24_set_rx_mode(FuriHalSpiBusHandle* handle) { + uint8_t cfg = 0; + cfg = nrf24_read_register(handle, REG_CONFIG); + cfg |= 0x03; // PWR_UP, and PRIM_RX + cfg = nrf24_write_reg(handle, REG_CONFIG, cfg); + furi_hal_gpio_write(nrf24_CE_PIN, true); + return cfg; +} + +uint8_t nrf24_set_tx_mode(FuriHalSpiBusHandle* handle) { + uint8_t reg; + furi_hal_gpio_write(nrf24_CE_PIN, false); + //nrf24_write_reg(handle, REG_STATUS, TX_DS | MAX_RT); + reg = nrf24_read_register(handle, REG_CONFIG); + reg &= ~0x01; // disable PRIM_RX + reg |= 0x02; // PWR_UP + reg = nrf24_write_reg(handle, REG_CONFIG, reg); + furi_hal_gpio_write(nrf24_CE_PIN, true); + return reg; +} + +void hexlify(uint8_t* in, uint8_t size, char* out) { + memset(out, 0, size * 2); + for(int i = 0; i < size; i++) + snprintf(out + strlen(out), sizeof(out + strlen(out)), "%02X", in[i]); +} + +uint64_t bytes_to_int64(uint8_t* bytes, uint8_t size, bool bigendian) { + uint64_t ret = 0; + for(int i = 0; i < size; i++) + if(bigendian) + ret |= bytes[i] << ((size - 1 - i) * 8); + else + ret |= bytes[i] << (i * 8); + + return ret; +} + +void int64_to_bytes(uint64_t val, uint8_t* out, bool bigendian) { + for(int i = 0; i < 8; i++) { + if(bigendian) + out[i] = (val >> ((7 - i) * 8)) & 0xff; + else + out[i] = (val >> (i * 8)) & 0xff; + } +} + +uint32_t bytes_to_int32(uint8_t* bytes, bool bigendian) { + uint32_t ret = 0; + for(int i = 0; i < 4; i++) + if(bigendian) + ret |= bytes[i] << ((3 - i) * 8); + else + ret |= bytes[i] << (i * 8); + + return ret; +} + +void int32_to_bytes(uint32_t val, uint8_t* out, bool bigendian) { + for(int i = 0; i < 4; i++) { + if(bigendian) + out[i] = (val >> ((3 - i) * 8)) & 0xff; + else + out[i] = (val >> (i * 8)) & 0xff; + } +} + +uint64_t bytes_to_int16(uint8_t* bytes, bool bigendian) { + uint16_t ret = 0; + for(int i = 0; i < 2; i++) + if(bigendian) + ret |= bytes[i] << ((1 - i) * 8); + else + ret |= bytes[i] << (i * 8); + + return ret; +} + +void int16_to_bytes(uint16_t val, uint8_t* out, bool bigendian) { + for(int i = 0; i < 2; i++) { + if(bigendian) + out[i] = (val >> ((1 - i) * 8)) & 0xff; + else + out[i] = (val >> (i * 8)) & 0xff; + } +} + +uint8_t nrf24_set_mac(uint8_t mac_addr, uint8_t *mac, uint8_t mlen) +{ + uint8_t addr[5]; + for(int i = 0; i < mlen; i++) addr[i] = mac[mlen - i - 1]; + return nrf24_write_buf_reg(nrf24_HANDLE, mac_addr, addr, mlen); +} \ No newline at end of file diff --git a/applications/external/nrf24batch/lib/nrf24/nrf24.h b/applications/external/nrf24batch/lib/nrf24/nrf24.h new file mode 100644 index 0000000000..a05ddbebc3 --- /dev/null +++ b/applications/external/nrf24batch/lib/nrf24/nrf24.h @@ -0,0 +1,386 @@ +#pragma once +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define R_REGISTER 0x00 +#define W_REGISTER 0x20 +#define REGISTER_MASK 0x1F +#define ACTIVATE 0x50 +#define R_RX_PL_WID 0x60 +#define R_RX_PAYLOAD 0x61 +#define W_TX_PAYLOAD 0xA0 +#define W_TX_PAYLOAD_NOACK 0xB0 +#define W_ACK_PAYLOAD 0xA8 +#define FLUSH_TX 0xE1 +#define FLUSH_RX 0xE2 +#define REUSE_TX_PL 0xE3 +#define RF24_NOP 0xFF + +#define REG_CONFIG 0x00 +#define REG_EN_AA 0x01 +#define REG_EN_RXADDR 0x02 +#define REG_SETUP_AW 0x03 +#define REG_SETUP_RETR 0x04 +#define REG_DYNPD 0x1C +#define REG_FEATURE 0x1D +#define REG_RF_SETUP 0x06 +#define REG_STATUS 0x07 +#define REG_RX_ADDR_P0 0x0A +#define REG_RX_ADDR_P1 0x0B +#define REG_RX_ADDR_P2 0x0C +#define REG_RX_ADDR_P3 0x0D +#define REG_RX_ADDR_P4 0x0E +#define REG_RX_ADDR_P5 0x0F +#define REG_RF_CH 0x05 +#define REG_TX_ADDR 0x10 +#define REG_FIFO_STATUS 0x17 +#define REG_OBSERVE_TX 0x08 + +#define RX_PW_P0 0x11 +#define RX_PW_P1 0x12 +#define RX_PW_P2 0x13 +#define RX_PW_P3 0x14 +#define RX_PW_P4 0x15 +#define RX_PW_P5 0x16 +#define RX_DR 0x40 +#define TX_DS 0x20 +#define MAX_RT 0x10 +#define NRF24_EN_DYN_ACK 0x01 + +#define nrf24_TIMEOUT 500 +#define nrf24_CE_PIN &gpio_ext_pb2 +#define nrf24_HANDLE &furi_hal_spi_bus_handle_external + +/* Low level API */ + +/** Write device register + * + * @param handle - pointer to FuriHalSpiHandle + * @param reg - register + * @param data - data to write + * + * @return device status + */ +uint8_t nrf24_write_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t data); + +/** Write buffer to device register + * + * @param handle - pointer to FuriHalSpiHandle + * @param reg - register + * @param data - data to write + * @param size - size of data to write + * + * @return device status + */ +uint8_t nrf24_write_buf_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* data, uint8_t size); + +/** Read device register + * + * @param handle - pointer to FuriHalSpiHandle + * @param reg - register + * @param[out] data - pointer to data + * + * @return device status + */ +uint8_t nrf24_read_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* data, uint8_t size); + +// Read single register (1 byte) +uint8_t nrf24_read_register(FuriHalSpiBusHandle* handle, uint8_t reg); + +/** Power up the radio for operation + * + * @param handle - pointer to FuriHalSpiHandle + * + * @return device status + */ +uint8_t nrf24_power_up(FuriHalSpiBusHandle* handle); + +/** Power down the radio + * + * @param handle - pointer to FuriHalSpiHandle + * + * @return device status + */ +uint8_t nrf24_set_idle(FuriHalSpiBusHandle* handle); + +/** Sets the radio to RX mode + * + * @param handle - pointer to FuriHalSpiHandle + * + * @return device status + */ +uint8_t nrf24_set_rx_mode(FuriHalSpiBusHandle* handle); + +/** Sets the radio to TX mode + * + * @param handle - pointer to FuriHalSpiHandle + * + * @return device status + */ +uint8_t nrf24_set_tx_mode(FuriHalSpiBusHandle* handle); + +/*=============================================================================================================*/ + +/* High level API */ + +/** Must call this before using any other nrf24 API + * + */ +void nrf24_init(); + +/** Must call this when we end using nrf24 device + * + */ +void nrf24_deinit(); + +/** Send flush rx command + * + * @param handle - pointer to FuriHalSpiHandle + * + * @return device status + */ +uint8_t nrf24_flush_rx(FuriHalSpiBusHandle* handle); + +/** Send flush tx command + * + * @param handle - pointer to FuriHalSpiHandle + * + * @return device status + */ +uint8_t nrf24_flush_tx(FuriHalSpiBusHandle* handle); + +/** Gets the RX packet length in data pipe 0 + * + * @param handle - pointer to FuriHalSpiHandle + * pipe - pipe index (0..5) + * @return packet length in data pipe 0 + */ +uint8_t nrf24_get_packetlen(FuriHalSpiBusHandle* handle, uint8_t pipe); + +/** Sets the RX packet length in data pipe 0 + * + * @param handle - pointer to FuriHalSpiHandle + * @param len - length to set + * + * @return device status + */ +uint8_t nrf24_set_packetlen(FuriHalSpiBusHandle* handle, uint8_t len); + +/** Gets configured length of MAC address + * + * @param handle - pointer to FuriHalSpiHandle + * + * @return MAC address length + */ +uint8_t nrf24_get_maclen(FuriHalSpiBusHandle* handle); + +/** Sets configured length of MAC address + * + * @param handle - pointer to FuriHalSpiHandle + * @param maclen - length to set MAC address to, must be greater than 1 and less than 6 + * + * @return MAC address length + */ +uint8_t nrf24_set_maclen(FuriHalSpiBusHandle* handle, uint8_t maclen); + +/** Gets the current status flags from the STATUS register + * + * @param handle - pointer to FuriHalSpiHandle + * + * @return status flags + */ +uint8_t nrf24_status(FuriHalSpiBusHandle* handle); + +/** Gets the current transfer rate + * + * @param handle - pointer to FuriHalSpiHandle + * + * @return transfer rate in bps + */ +uint32_t nrf24_get_rate(FuriHalSpiBusHandle* handle); + +/** Sets the transfer rate + * + * @param handle - pointer to FuriHalSpiHandle + * @param rate - the transfer rate in bps + * + * @return device status + */ +uint8_t nrf24_set_rate(FuriHalSpiBusHandle* handle, uint32_t rate); + +/** Gets the current channel + * In nrf24, the channel number is multiplied times 1MHz and added to 2400MHz to get the frequency + * + * @param handle - pointer to FuriHalSpiHandle + * + * @return channel + */ +uint8_t nrf24_get_chan(FuriHalSpiBusHandle* handle); + +/** Sets the channel + * + * @param handle - pointer to FuriHalSpiHandle + * @param frequency - the frequency in hertz + * + * @return device status + */ +uint8_t nrf24_set_chan(FuriHalSpiBusHandle* handle, uint8_t chan); + +/** Gets the source mac address + * + * @param handle - pointer to FuriHalSpiHandle + * @param[out] mac - the source mac address + * + * @return device status + */ +uint8_t nrf24_get_src_mac(FuriHalSpiBusHandle* handle, uint8_t* mac); + +/** Sets the source mac address + * + * @param handle - pointer to FuriHalSpiHandle + * @param mac - the mac address to set + * @param size - the size of the mac address (2 to 5) + * + * @return device status + */ +uint8_t nrf24_set_src_mac(FuriHalSpiBusHandle* handle, uint8_t* mac, uint8_t size); + +/** Gets the dest mac address + * + * @param handle - pointer to FuriHalSpiHandle + * @param[out] mac - the source mac address + * + * @return device status + */ +uint8_t nrf24_get_dst_mac(FuriHalSpiBusHandle* handle, uint8_t* mac); + +/** Sets the dest mac address + * + * @param handle - pointer to FuriHalSpiHandle + * @param mac - the mac address to set + * @param size - the size of the mac address (2 to 5) + * + * @return device status + */ +uint8_t nrf24_set_dst_mac(FuriHalSpiBusHandle* handle, uint8_t* mac, uint8_t size); + +/** Reads RX packet + * + * @param handle - pointer to FuriHalSpiHandle + * @param[out] packet - the packet contents + * @param[out] ret_packetsize - size of the received packet + * @param packet_size: >1 - size, 1 - packet length is determined by RX_PW_P0 register, 0 - it is determined by dynamic payload length command + * + * @return device status + */ +uint8_t + nrf24_rxpacket(FuriHalSpiBusHandle* handle, uint8_t* packet, uint8_t* ret_packetsize, uint8_t packet_size_flag); + +/** Sends TX packet + * + * @param handle - pointer to FuriHalSpiHandle + * @param packet - the packet contents + * @param size - packet size + * @param ack - boolean to determine whether an ACK is required for the packet or not + * + * @return device status + */ +uint8_t nrf24_txpacket(FuriHalSpiBusHandle* handle, uint8_t* payload, uint8_t size, bool ack); + +/** Configure the radio + * This is not comprehensive, but covers a lot of the common configuration options that may be changed + * @param handle - pointer to FuriHalSpiHandle + * @param rate - transfer rate in Mbps (1 or 2) + * @param srcmac - source mac address + * @param dstmac - destination mac address + * @param maclen - length of mac address + * @param channel - channel to tune to + * @param noack - if true, disable auto-acknowledge + * @param disable_aa - if true, disable ShockBurst + * + */ +void nrf24_configure( + FuriHalSpiBusHandle* handle, + uint8_t rate, + uint8_t* srcmac, + uint8_t* dstmac, + uint8_t maclen, + uint8_t channel, + bool noack, + bool disable_aa); + +// Set mac address (MSB first), Return: Status +uint8_t nrf24_set_mac(uint8_t mac_addr, uint8_t *mac, uint8_t mlen); + +/** Configures the radio for "promiscuous mode" and primes it for rx + * This is not an actual mode of the nrf24, but this function exploits a few bugs in the chip that allows it to act as if it were. + * See http://travisgoodspeed.blogspot.com/2011/02/promiscuity-is-nrf24l01s-duty.html for details. + * @param handle - pointer to FuriHalSpiHandle + * @param channel - channel to tune to + * @param rate - transfer rate in Mbps (1 or 2) + */ +void nrf24_init_promisc_mode(FuriHalSpiBusHandle* handle, uint8_t channel, uint8_t rate); + +/** Listens for a packet and returns first possible address sniffed + * Call this only after calling nrf24_init_promisc_mode + * @param handle - pointer to FuriHalSpiHandle + * @param maclen - length of target mac address + * @param[out] addresses - sniffed address + * + * @return success + */ +bool nrf24_sniff_address(FuriHalSpiBusHandle* handle, uint8_t maclen, uint8_t* address); + +/** Sends ping packet on each channel for designated tx mac looking for ack + * + * @param handle - pointer to FuriHalSpiHandle + * @param srcmac - source address + * @param dstmac - destination address + * @param maclen - length of address + * @param rate - transfer rate in Mbps (1 or 2) + * @param min_channel - channel to start with + * @param max_channel - channel to end at + * @param autoinit - if true, automatically configure radio for this channel + * + * @return channel that the address is listening on, if this value is above the max_channel param, it failed + */ +uint8_t nrf24_find_channel( + FuriHalSpiBusHandle* handle, + uint8_t* srcmac, + uint8_t* dstmac, + uint8_t maclen, + uint8_t rate, + uint8_t min_channel, + uint8_t max_channel, + bool autoinit); + +/** Converts 64 bit value into uint8_t array + * @param val - 64-bit integer + * @param[out] out - bytes out + * @param bigendian - if true, convert as big endian, otherwise little endian + */ +void int64_to_bytes(uint64_t val, uint8_t* out, bool bigendian); + +/** Converts 32 bit value into uint8_t array + * @param val - 32-bit integer + * @param[out] out - bytes out + * @param bigendian - if true, convert as big endian, otherwise little endian + */ +void int32_to_bytes(uint32_t val, uint8_t* out, bool bigendian); + +/** Converts uint8_t array into 32 bit value + * @param bytes - uint8_t array + * @param bigendian - if true, convert as big endian, otherwise little endian + * + * @return 32-bit value + */ +uint32_t bytes_to_int32(uint8_t* bytes, bool bigendian); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/applications/external/nrf24batch/nrf24batch.c b/applications/external/nrf24batch/nrf24batch.c new file mode 100644 index 0000000000..a41de8210f --- /dev/null +++ b/applications/external/nrf24batch/nrf24batch.c @@ -0,0 +1,1958 @@ +// +// Written by vad7, 10.01.2023. vad7@yahoo.com +// +#include "nrf24batch.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TAG "nrf24batch" +#define VERSION "1.9" + +#define SCAN_APP_PATH_FOLDER STORAGE_APP_DATA_PATH_PREFIX +#define LOG_FILEEXT ".txt" +#define NRF_READ_TIMEOUT 300UL // ms +#define WORK_PERIOD 2 // ms, Timer period +#define MAX_CHANNEL 125 +#define FONT_5x7_SCREEN_WIDTH 25 +#define NRF_EN_DYN_ACK 0 // does not work on some nrf24l01+ chips, (0/1) + +const char SettingsFld_Info[] = "Info:"; +const char SettingsFld_Ch[] = "Ch:"; +const char SettingsFld_Rate[] = "Rate:"; +const char SettingsFld_DPL[] = "DPL:"; +const char SettingsFld_CRC[] = "CRC:"; +const char SettingsFld_RETR[] = "RETR:"; +const char SettingsFld_Address[] = "Address:"; +const char SettingsFld_Resend[] = "Resend:"; +const char SettingsFld_Delay[] = "Delay_ms:"; +const char SettingsFld_WriteStart[] = "Write start:"; +const char SettingsFld_Payload[] = "Payload struct:"; +const char SettingsFld_ReadDefault[] = "R default:"; +const char SettingsFld_WriteDefault[] = "W default:"; +const char SettingsFld_Read[] = "R:"; // Read cmd +const char SettingsFld_Write[] = "W:"; // Write cmd +const char SettingsFld_Set[] = "S:"; // Set cmd (like Write but without "Write start" packet) +const char SettingsFld_ReadBatch[] = "RBatch:"; +const char SettingsFld_WriteBatch[] = "WBatch:"; +const char SettingsFld_SetBatch[] = "SBatch:"; +const char SettingsFld_Listen[] = "Listen:"; +const char SettingsFld_ReadCmdRepeatPeriod[] = "ReadCmd repeat:"; +const char AskQuestion_Save[] = "SAVE BATCH?"; +#define Settings_i 'i' +#define Settings_n 'n' +#define VAR_EMPTY ((int32_t)0x80000000) + +nRF24Batch* APP; +uint8_t what_doing = 0; // 0 - setup, 1 - cmd list, 2 - read/write/listen cmd +enum { + rwt_set_batch = 0, // fast send packet without question + rwt_read_batch, // Send read cmd and wait for answer in batch + rwt_read_cmd, // Send read cmd and wait for answer + rwt_write_batch, // Send write cmd (with Write start pkt if available) with a question before it + rwt_listen, // Listen mode (wait incoming pkts) + rwt_max +}; +uint8_t rw_type = rwt_read_batch; // What to do: rwt_* +enum { sst_none = 0, sst_sending, sst_receiving, sst_ok, sst_error, sst_timeout }; +uint8_t send_status = sst_none; // sst_* +bool cmd_array = false; +uint8_t cmd_array_idx; +uint8_t cmd_array_cnt = 0; +bool cmd_array_hex; +uint8_t save_settings = 0; +uint16_t view_cmd[rwt_max - 1] = {0}; // SetBatch, ReadBatch, Read, WriteBatch +uint8_t view_x = 0; +char screen_buf[64]; +char Info[35] = ""; +char file_name[FONT_5x7_SCREEN_WIDTH]; +char ERR_STR[FONT_5x7_SCREEN_WIDTH]; +uint8_t ERR = 0; +uint8_t NRF_rate; // 0 - 250Kbps, 1 - 1Mbps, 2 - 2Mbps +uint8_t NRF_channel; // 0..125 +uint8_t NRF_DPL; // 1 - Dynamic Payload Length +uint8_t NRF_CRC; // 1 - No, 1 - CRC 1byte, 2 - CRC 2byte +uint8_t NRF_RETR = ((0b0011 << 4) | 0b1111); // Automatic Retransmission, ARD, ARC +uint8_t NRF_Payload; // Payload len in bytes, 0..32 +bool NRF_ERROR = 0; +uint8_t NRF_INITED = 0; // 0 - not, 1 - rw, rwt_listen - listen +bool NRF_BOARD_POWER_5V = false; +uint8_t NRF_last_packet_send_st = 0; +uint8_t NRF_resend = 1; // number of transaction attempts +int8_t NRF_repeat = 0; // count number of repeated requests (until < NRF_resend) +uint32_t NRF_time; +uint16_t ReadCmdRepeatPeriod = 10; // s +bool ReadRepeat = false; +uint32_t delay_between_pkt = 10; // ms + +uint8_t addr[5]; // nRF24 address, MSB first +uint8_t addr_len = 0; // 2..5 +uint8_t payload[32]; +uint8_t payload_receive[32]; +uint8_t payload_struct[32]; // sizeof(1..4) in bytes of each field, example: 2,1,1 +uint8_t payload_fields = 0; +uint8_t payload_size = 0; // bytes +uint16_t view_Batch = 0; // view pos in Batch or inside WriteBatch (Log[view_Batch]) +uint16_t view_WriteBatch = 0; // view pos of WriteBatch list +uint8_t setup_cursor = 0; // cursor position on Setup scr +uint8_t Edit = 0; +char* Edit_pos; +char* Edit_start; +bool Edit_hex; // 0 - decimal, 1 - hex +bool Edited = false; // list of cmds edited + +Stream* file_stream = NULL; +FuriString* ReadDefault = NULL; +FuriString* WriteDefault = NULL; +FuriString* WriteStart = NULL; +FuriString* Constants = NULL; // text of STR=x +uint8_t listen_addr[5]; +uint8_t listen_addr_len = 0; +char* ListenFields = NULL; // ptr to string: field1,field2,... max 5 field now +bool ListenNew; +FuriHalRtcDateTime ListenLastTime = {0}; +uint32_t ListenPrev = 0; +uint32_t ListenLast = 0; +FuriString** Read_cmd = NULL; // Names of read cmd +uint16_t Read_cmd_Total = 0; +FuriString** Log = NULL; // Strings: var=n +uint16_t Log_Total = 0; +FuriString** ReadBatch_cmd = NULL; // Names of read batch cmd +uint16_t ReadBatch_cmd_Total = 0; +char* ReadBatch_cmd_curr = NULL; // =0xFFFFFFFF - finish +FuriString** WriteBatch_cmd = NULL; // Names of write batch cmd +uint16_t WriteBatch_cmd_Total = 0; +uint16_t WriteBatch_cmd_curr = 0; // == _Total - finish +FuriString** SetBatch_cmd = NULL; // Names of set batch cmd +uint16_t SetBatch_cmd_Total = 0; +uint16_t SetBatch_cmd_curr = 0; // == _Total - finish + +#define POWER_READ_PERIOD 501 // ms +uint16_t pwr_read_timer = 0; +int Current = 0; +int CurrentStart = 0; + +enum { ask_write_batch = 1, ask_save_batch, ask_skip_cmd, ask_return, ask_exit }; +uint8_t ask_question = 0; // 1 - Ask now - ask_* +uint8_t ask_question_answer = 0; // 0 - no, 1 - yes + +static bool ask_fill_screen_buf(void) { + if(ask_question == ask_write_batch) + strcpy(screen_buf, "RUN WRITE BATCH?"); + else if(ask_question == ask_save_batch) + strcpy(screen_buf, "SAVE AS WRITE BATCH?"); + else if(ask_question == ask_skip_cmd) + strcpy(screen_buf, "SKIP CMD?"); + else if(ask_question == ask_return) + strcpy(screen_buf, "RETURN?"); + else if(ask_question == ask_exit) + strcpy(screen_buf, "EXIT?"); + else + return false; + strcat(screen_buf, ask_question_answer ? " YES" : " NO"); + return true; +} + +//#define MIN(a, b) ((a= '0' && c <= '9') return true; + if(hex) { + c &= ~0x20; + if(c >= 'A' && c <= 'F') return true; + } else if(c == '-') + return true; + return false; +} + +// Return num bytes in array +static uint8_t ConvertHexToArray(char* hex, uint8_t* array, uint8_t maxlen) { + uint8_t len = 0; + while(maxlen) { + uint8_t ch = *hex++; + if(ch < ' ') break; + if(ch < '0') continue; + *array++ = (GetHexVal(ch) << 4) + GetHexVal(*hex++); + len++; + maxlen--; + } + return len; +} + +int32_t str_to_int(char* p) { + if(*(p + 1) == 'x') { // hex + return strtol(p + 2, NULL, 16); + } else + return strtol(p, NULL, 10); +} + +void str_rtrim(char* p) { + char* delim_col = strchr(p, '\r'); + if(delim_col) + *delim_col = '\0'; + else { + delim_col = strchr(p, '\n'); + if(delim_col) *delim_col = '\0'; + } +} + +static void add_to_str_hex_bytes(char* out, uint8_t* arr, int bytes) { + if(bytes <= 0) return; + out += strlen(out); + do { + snprintf(out, 3, "%02X", *arr++); + out += 2; + } while(--bytes); +} + +void Edit_insert_digit(char new) { + if(*Edit_pos == '-') return; + if(what_doing <= 1) { + if(strlen(Edit_start) < (what_doing == 0 && setup_cursor == 2 ? 3 : 5 * 2)) { + memmove(Edit_pos + 1, Edit_pos, strlen(Edit_pos) + 1); + *Edit_pos = new; + } + } else { + FuriString* fs = Log[view_Batch]; + FuriString* ns = furi_string_alloc(); + if(ns) { + uint16_t len = Edit_pos - (char*)furi_string_get_cstr(fs); + furi_string_set_n(ns, fs, 0, len); + furi_string_cat_printf(ns, "%c", new); + furi_string_cat_str(ns, Edit_pos); + Log[view_Batch] = ns; + Edit_pos = (char*)furi_string_get_cstr(ns); + Edit_start = Edit_pos + (Edit_start - (char*)furi_string_get_cstr(fs)); + Edit_pos += len; + furi_string_free(fs); + } + } +} + +int32_t get_payload_receive_field(uint8_t* var, uint8_t size) { + if(size <= 1) + return *var; + else if(size == 2) + return *(int16_t*)var; + else if(size == 3) + return (*(uint32_t*)var) & 0xFFFFFF; + else + return *(int32_t*)var; +} + +void free_Log() { + if(Log_Total) { + for(uint16_t i = 0; i < Log_Total; i++) + if(Log[i]) furi_string_free(Log[i]); + Log_Total = 0; + } + if(Log) { + free(Log); + Log = NULL; + } +} + +void free_store(void) { + if(Constants) { + furi_string_free(Constants); + Constants = NULL; + } + if(ReadDefault) { + furi_string_free(ReadDefault); + ReadDefault = NULL; + } + if(WriteDefault) { + furi_string_free(WriteDefault); + WriteDefault = NULL; + } + if(WriteStart) { + furi_string_free(WriteStart); + WriteDefault = NULL; + } + if(ListenFields) { + free(ListenFields); + ListenFields = NULL; + } + if(Read_cmd_Total) { + for(uint16_t i = 0; i < Read_cmd_Total; i++) furi_string_free(Read_cmd[i]); + Read_cmd_Total = 0; + } + if(Read_cmd) { + free(Read_cmd); + Read_cmd = NULL; + } + if(ReadBatch_cmd_Total) { + for(uint16_t i = 0; i < ReadBatch_cmd_Total; i++) furi_string_free(ReadBatch_cmd[i]); + ReadBatch_cmd_Total = 0; + } + if(ReadBatch_cmd) { + free(ReadBatch_cmd); + ReadBatch_cmd = NULL; + } + if(WriteBatch_cmd_Total) { + for(uint16_t i = 0; i < WriteBatch_cmd_Total; i++) furi_string_free(WriteBatch_cmd[i]); + WriteBatch_cmd_Total = 0; + } + if(WriteBatch_cmd) { + free(WriteBatch_cmd); + WriteBatch_cmd = NULL; + } + if(SetBatch_cmd_Total) { + for(uint16_t i = 0; i < SetBatch_cmd_Total; i++) furi_string_free(SetBatch_cmd[i]); + SetBatch_cmd_Total = 0; + } + if(SetBatch_cmd) { + free(SetBatch_cmd); + SetBatch_cmd = NULL; + } + free_Log(); +} + +void update_power(void) { + Current = furi_hal_power_get_battery_current(FuriHalPowerICFuelGauge) * 1000; +} + +void check_en_power_5V(void) { + if(!furi_hal_power_is_otg_enabled() && !furi_hal_power_is_charging()) { + FURI_LOG_D("PWR", "NO 5V, TURN ON"); + notification_message(APP->notification, &sequence_blink_yellow_100); + furi_delay_ms(10); + update_power(); + CurrentStart = Current; + furi_hal_power_enable_otg(); + NRF_BOARD_POWER_5V = true; + furi_delay_ms(100); + NRF_INITED = 0; + } +} + +static bool select_settings_file() { + DialogsApp* dialogs = furi_record_open("dialogs"); + bool result = false; + FuriString* path; + path = furi_string_alloc(); + furi_string_set(path, SCAN_APP_PATH_FOLDER); + DialogsFileBrowserOptions browser_options; + dialog_file_browser_set_basic_options(&browser_options, ".txt", NULL); + browser_options.hide_ext = false; + bool ret = dialog_file_browser_show(dialogs, path, path, &browser_options); + furi_record_close("dialogs"); + if(ret) { + if(!file_stream_open( + file_stream, furi_string_get_cstr(path), FSAM_READ_WRITE, FSOM_OPEN_EXISTING)) { + FURI_LOG_D(TAG, "Cannot open file \"%s\"", furi_string_get_cstr(path)); + file_stream_close(file_stream); + } else { + FURI_LOG_D(TAG, "Open file \"%s\"", furi_string_get_cstr(path)); + strncpy( + file_name, + furi_string_get_cstr(path) + sizeof(SCAN_APP_PATH_FOLDER), + sizeof(file_name)); + result = true; + } + } + furi_string_free(path); + return result; +} + +static void prepare_nrf24(void) { + check_en_power_5V(); + if(!NRF_INITED || rw_type == rwt_listen || NRF_INITED == rwt_listen) { + FURI_LOG_D("NRF", "Prepare"); + uint8_t adrlen, *adr; + if(rw_type == rwt_listen) { + adrlen = listen_addr_len; + adr = listen_addr; + NRF_INITED = rwt_listen; + } else { + adrlen = addr_len; + adr = addr; + NRF_INITED = 1; + } + furi_hal_gpio_write(nrf24_CE_PIN, false); + nrf24_set_mac(REG_RX_ADDR_P0, adr, adrlen); + uint8_t tmp[5] = {0}; + nrf24_read_reg(nrf24_HANDLE, REG_RX_ADDR_P0, tmp, adrlen); + for(uint8_t i = 0; i < adrlen / 2; i++) { + uint8_t tb = tmp[i]; + tmp[i] = tmp[adrlen - i - 1]; + tmp[adrlen - i - 1] = tb; + } + NRF_ERROR = memcmp(adr, tmp, adrlen) != 0; + if(NRF_ERROR) { + NRF_INITED = 0; + return; + } + // EN_DYN_ACK(0x01) option for W_TX_PAYLOAD_NOACK cmd broke AA on some fake nRF24l01+, i.e. set it to 0 + nrf24_write_reg( + nrf24_HANDLE, + REG_FEATURE, + NRF_EN_DYN_ACK + + (NRF_DPL ? 4 : + 0)); // Dynamic Payload, Payload with ACK, W_TX_PAYLOAD_NOACK command + nrf24_write_reg(nrf24_HANDLE, REG_RF_CH, NRF_channel); + nrf24_write_reg( + nrf24_HANDLE, + REG_RF_SETUP, + (NRF_rate == 0 ? 0b00100000 : + NRF_rate == 1 ? 0 : + 0b00001000) | + 0b111); // +TX high power + nrf24_write_reg( + nrf24_HANDLE, + REG_CONFIG, + 0x70 | ((NRF_CRC == 1 ? 0b1000 : + NRF_CRC == 2 ? 0b1100 : + 0))); // Mask all interrupts + nrf24_write_reg( + nrf24_HANDLE, REG_SETUP_RETR, NRF_RETR); // Automatic Retransmission, ARD<<4 + ARC + nrf24_write_reg(nrf24_HANDLE, REG_EN_AA, 0x01); // Auto acknowledgement + nrf24_write_reg(nrf24_HANDLE, REG_DYNPD, NRF_DPL ? 0x3F : 0); // Enable dynamic payload reg + nrf24_write_reg(nrf24_HANDLE, RX_PW_P0, payload_size); + nrf24_set_maclen(nrf24_HANDLE, adrlen); + nrf24_set_mac(REG_TX_ADDR, adr, adrlen); + nrf24_write_reg(nrf24_HANDLE, REG_EN_RXADDR, 1); + //nrf24_set_idle(nrf24_HANDLE); + } + nrf24_flush_tx(nrf24_HANDLE); + nrf24_flush_rx(nrf24_HANDLE); + nrf24_write_reg(nrf24_HANDLE, REG_STATUS, MAX_RT | RX_DR | TX_DS); +} + +// true - ok +uint8_t nrf24_send_packet() { + if(furi_log_get_level() == FuriLogLevelDebug) { + char buf[65]; + buf[0] = 0; + add_to_str_hex_bytes(buf, payload, payload_size); + FURI_LOG_D(TAG, "SEND: %s", buf); + } + //nrf24_flush_tx(nrf24_HANDLE); + //nrf24_write_reg(nrf24_HANDLE, REG_STATUS, RX_DR | TX_DS | MAX_RT); + NRF_last_packet_send_st = nrf24_txpacket(nrf24_HANDLE, payload, payload_size, true); // ACK + if(NRF_last_packet_send_st) { + if((rw_type == rwt_read_cmd || rw_type == rwt_read_batch) && + send_status == sst_sending) { // Read + nrf24_set_rx_mode(nrf24_HANDLE); + send_status = sst_receiving; // receiving + } + } else + notification_message(APP->notification, &sequence_blink_red_100); + NRF_time = furi_get_tick(); + FURI_LOG_D( + TAG, + "Send packet: %d%s", + NRF_last_packet_send_st, + send_status == sst_receiving ? ", Receiving" : ""); + return NRF_last_packet_send_st; +} + +uint8_t nrf24_resend_read_packet() { + if(Log_Total && !cmd_array) { + FuriString* str = Log[Log_Total - 1]; + char* p = strstr(furi_string_get_cstr(str), ": "); + if(p) { + if(strncmp(p + 2, "0x", 2) == 0) p += 2; + furi_string_left(str, p - furi_string_get_cstr(str) + 2); + } + } + return nrf24_send_packet(); +} + +// true - new packet +bool nrf24_read_newpacket() { + bool found = false; + uint8_t packetsize; + uint8_t st = + nrf24_rxpacket(nrf24_HANDLE, payload_receive, &packetsize, NRF_DPL ? 0 : payload_size); + if(st & RX_DR) { + NRF_time = furi_get_tick(); + if(furi_log_get_level() == FuriLogLevelDebug) { + char buf[65]; + buf[0] = 0; + add_to_str_hex_bytes(buf, payload_receive, packetsize); + FURI_LOG_D(TAG, "READ(%X): %s", st, buf); + } + if(Log_Total) { + FuriString* str = Log[Log_Total - 1]; + uint8_t size = 1; + char* p = strchr((char*)furi_string_get_cstr(str), '*'); + if(p) { + p++; + if(*p == '=') + size = 0; // string + else { + size = *p - '0'; + if(size > 4) size = 0; + } + } + int32_t var = get_payload_receive_field(payload_receive, size); + //FURI_LOG_D(TAG, "VAR(%d): %ld", size, var); + if(size == 0) + furi_string_cat_printf(str, "%c", (char)var); + else { + char hex[9]; + hex[0] = '\0'; + add_to_str_hex_bytes(hex, (uint8_t*)&var, size); + if((cmd_array && cmd_array_hex) || furi_string_end_with_str(str, "0x")) + furi_string_cat_str(str, hex); + else { + if(var >= 0 && var <= 9) + furi_string_cat_printf(str, "%ld", var); + else + furi_string_cat_printf(str, "%ld (%s)", var, hex); + } + } + if(cmd_array) { + if(--cmd_array_cnt) { + furi_string_cat_str(str, ","); + if(cmd_array_hex) furi_string_cat_str(str, "0x"); + payload[cmd_array_idx] += size; // next array element + NRF_repeat = -1; + send_status = sst_sending; // Will be send after delay_between_pkt + } else + send_status = sst_ok; + } else { + if(size == 0) { // string, until '\0' + if(var == 0) send_status = sst_ok; + } else + send_status = sst_ok; + } + } + //notification_message(APP->notification, &sequence_blink_white_100); + found = true; + } else if(st & 0x80) { // NRF24 hardware error + NRF_ERROR = 1; + NRF_INITED = 0; + } + return found; +} + +// Search in constatnt pull (Const1=n; Const2=n;...) +// VAR_EMPTY - not found +int32_t subs_constant(char* p, uint8_t len) { + char* c = (char*)furi_string_get_cstr(Constants); + while((c = strchr(c, *p))) { + if(strncmp(c, p, len) != 0) { + c++; + continue; + } + if(c == (char*)furi_string_get_cstr(Constants) || *(c - 1) == ';' || *(c - 1) <= ' ') { + c += len; + if(*c == '=') { + c++; + return str_to_int(c); + } + } else + c += len; + } + return VAR_EMPTY; +} + +// fill payload with default = p +// if var_n = VAR_EMPTY - skip filling var_* +bool fill_payload(char* p, uint8_t* idx_i, int32_t var_n) { + if(idx_i) *idx_i = 255; + uint8_t fld = 0; // field # + uint8_t idx = 0; // byte index + do { + int32_t b = 0; + char* end = strchr(p, ','); + if(*p >= '0' && *p <= '9') { // Number found + b = str_to_int(p); + } else if(*p == 'i' && *(p + 1) == ':') { // 'i:' array index + b = str_to_int(p + 2); + if(idx_i) *idx_i = idx; + } else if(*p == 'n' && *(p + 1) < '0') { // var_n + if(var_n != VAR_EMPTY) b = var_n; + } else if(*p >= 'A') { // constant found + uint8_t len; + if(end) + len = end - p; + else { + len = strlen(p); + if(*(p + len - 1) == '#') len--; + } + b = subs_constant(p, len); + if(b == VAR_EMPTY) { + ERR = 1; + memset(ERR_STR, 0, sizeof(ERR_STR)); + strcpy(ERR_STR, "No "); + strncpy(ERR_STR + strlen(ERR_STR), p, sizeof(ERR_STR) - 4); + FURI_LOG_D(TAG, "Constant not found: %s", p); + return false; + } + } else if(end == p) { + idx += payload_struct[fld]; + } else if(*p == '#') { // value in Hexadecimal, end string + break; + } else { + ERR = 2; + strcpy(ERR_STR, "char: "); + uint8_t l = strlen(ERR_STR); + ERR_STR[l] = *p; + ERR_STR[l + 1] = '\0'; + FURI_LOG_D(TAG, "Wrong format char(%c)", *p); + return false; + } + if(end != p) { + payload[idx++] = b; + if(payload_struct[fld] > 1) payload[idx++] = b >> 8; + if(payload_struct[fld] > 2) payload[idx++] = b >> 16; + if(payload_struct[fld] > 3) payload[idx++] = b >> 24; + } + if(++fld == payload_fields || idx >= sizeof(payload) || end == NULL) break; + p = end + 1; + } while(1); + return true; +} + +// Cmd: "name=payload" +bool Run_Read_cmd(FuriString* cmd) { + char* p = (char*)furi_string_get_cstr(cmd); + p = strchr(p, '='); + if(p == NULL) return false; + if(Log == NULL) + Log = malloc(sizeof(Log)); + else + Log = realloc(Log, sizeof(Log) * (Log_Total + 1)); + if(Log == NULL) { + ERR = 3; + strcpy(ERR_STR, "Memory low"); + FURI_LOG_D(TAG, ERR_STR); + return false; + } + FuriString* fs = furi_string_alloc(); + furi_string_set_strn( + fs, (char*)furi_string_get_cstr(cmd), p - (char*)furi_string_get_cstr(cmd)); + furi_string_cat_str(fs, ": "); + bool hexval; + if((hexval = *(p + strlen(p) - 1) == '#')) + furi_string_cat_str(fs, "0x"); // value in Hex format + Log[Log_Total++] = fs; + p++; + memset(payload, 0, sizeof(payload)); + if(ReadDefault && !fill_payload((char*)furi_string_get_cstr(ReadDefault), NULL, VAR_EMPTY)) + return false; + if(!fill_payload(p, &cmd_array_idx, VAR_EMPTY)) return false; + memset(payload_receive, 0, sizeof(payload_receive)); + cmd_array = false; + if(*(p - 2) == ']' && cmd_array_idx != 255) { // array + p = strchr(furi_string_get_cstr(cmd), '['); + if(p) { + cmd_array_cnt = str_to_int(p + 1); + if(cmd_array_cnt > 1) { + cmd_array_hex = hexval; + cmd_array = true; // array + } + } + } + prepare_nrf24(); + if(NRF_ERROR) return false; + what_doing = 2; + NRF_repeat = 0; + send_status = sst_sending; // Read - sending + nrf24_send_packet(); + return true; +} + +// run commands one by one, true - command running +bool Run_ReadBatch_cmd(FuriString* cmd) { + char* p; + if(cmd) { + p = strchr((char*)furi_string_get_cstr(cmd), ':'); + if(p == NULL) { + ERR = 5; + strcpy(ERR_STR, "WRONG FORMAT"); + return false; + } + p += 2; + ReadBatch_cmd_curr = NULL; + free_Log(); + } else { + if(ReadBatch_cmd_curr) + p = ReadBatch_cmd_curr; + else + return false; + } + char* end = strchr(p, ';'); + uint8_t len; + if(end) + len = end - p; + else { + str_rtrim(p); + len = strlen(p); + } + for(uint16_t i = 0; i < Read_cmd_Total; i++) { + FuriString* fs = Read_cmd[i]; + if(strncmp((char*)furi_string_get_cstr(fs), p, len) == 0) { + char c = *((char*)furi_string_get_cstr(fs) + len); + if(c != '=' && c != '*' && c != '[') continue; + if(end) + ReadBatch_cmd_curr = end + 1; + else + ReadBatch_cmd_curr = (char*)0xFFFFFFFF; + if(!Run_Read_cmd(fs)) break; + return true; + } + } + if(NRF_ERROR) return false; + if(ERR == 0) { + ERR = 4; + strcpy(ERR_STR, "NOT FOUND"); + FuriString* fs = furi_string_alloc(); + furi_string_set_strn(fs, p, len); + Log[Log_Total++] = fs; + FURI_LOG_D(TAG, "CMD %s: %s", ERR_STR, p); + } + view_Batch = Log_Total ? Log_Total - 1 : 0; + return false; +} + +void Prepare_Write_cmd(FuriString* cmd) { + free_Log(); + if(cmd == NULL) return; + char *end, *p = strchr((char*)furi_string_get_cstr(cmd), ':'); + if(p == NULL) { + ERR = 8; + strcpy(ERR_STR, "Wrong batch"); + FURI_LOG_D(TAG, ERR_STR); + return; + } + p += 2; + Log = malloc(sizeof(Log)); + do { + end = strchr(p, ';'); + uint8_t len; + if(end) { + len = end - p; + end++; + } else { + str_rtrim(p); + len = strlen(p); + } + FuriString* fs = furi_string_alloc(); + if(Log_Total) Log = realloc(Log, sizeof(Log) * (Log_Total + 1)); + if(Log == NULL) { + ERR = 3; + strcpy(ERR_STR, "Memory low"); + FURI_LOG_D(TAG, ERR_STR); + return; + } + furi_string_set_strn(fs, p, len); + Log[Log_Total++] = fs; + } while((p = end)); +} + +// Write / Set batch +bool Run_WriteBatch_cmd() { + if(Log_Total == 0) return false; + uint16_t cmd_curr = rw_type == rwt_write_batch ? WriteBatch_cmd_curr : SetBatch_cmd_curr; + if(cmd_curr == 0) { // first + prepare_nrf24(); + if(NRF_ERROR) return false; + if(rw_type == rwt_write_batch && WriteStart) { + if(!fill_payload((char*)furi_string_get_cstr(WriteStart), NULL, VAR_EMPTY)) + return false; + send_status = sst_sending; + if(!nrf24_send_packet()) return false; + } + } + char* p = (char*)furi_string_get_cstr(Log[cmd_curr]); + uint16_t len = furi_string_size(Log[cmd_curr]); + char* arr = NULL; + cmd_array = false; + int32_t new = 0; + for(uint16_t i = 0; i < len; i++) { + if(p[i] == '=') { + len = i; + char* p2 = p + i + 1; + if(*p2 == '{') { + arr = ++p2; // array + cmd_array = true; + } + new = str_to_int(p2); + break; + } + } + FURI_LOG_D( + TAG, "%cBatch: =%d, (%d)%s", rw_type == rwt_write_batch ? 'W' : 'S', (int)new, len, p); + char *w, *delim_col, i, size; + FuriString* str = furi_string_alloc(); + stream_rewind(file_stream); + while(stream_read_line(file_stream, str)) { + w = (char*)furi_string_get_cstr(str); + if(strncmp(w, SettingsFld_Write, sizeof(SettingsFld_Write) - 1) == 0) + w += sizeof(SettingsFld_Write); + else if(strncmp(w, SettingsFld_Set, sizeof(SettingsFld_Set) - 1) == 0) + w += sizeof(SettingsFld_Set); + else + continue; + delim_col = strchr(w, '='); + if(delim_col == NULL) continue; + size = 1; + if(*(delim_col - 2) == '*') { + if(len != delim_col - w - 2) continue; + size = *(delim_col - 1) - '0'; + new = new&(size == 1 ? 0xFF : size == 2 ? 0xFFFF : size == 3 ? 0xFFFFFF : 0xFFFFFFFF); + } else if(len != delim_col - w) + continue; + if(strncmp(p, w, len) != 0) continue; + delim_col++; + str_rtrim(delim_col); + cmd_array_cnt = 255; + do { + memset(payload, 0, sizeof(payload)); + if(WriteDefault && + !fill_payload((char*)furi_string_get_cstr(WriteDefault), NULL, new)) { + view_Batch = cmd_curr; + return false; + } + if(!fill_payload(delim_col, &cmd_array_idx, VAR_EMPTY)) { + view_Batch = cmd_curr; + return false; + } + if(cmd_array && cmd_array_idx != 255) { + if(cmd_array_cnt != 255) payload[cmd_array_idx] = cmd_array_cnt; + } else + cmd_array = false; + send_status = sst_sending; + NRF_repeat = 0; + i = 0; + do { + if(nrf24_send_packet()) break; + furi_delay_ms(delay_between_pkt); + } while(i++ < NRF_resend); + if(i < NRF_resend || i == 0) { // ok + if(cmd_array) { // array + for(; arr != NULL;) { + if(*arr == ',') break; + if(*arr == '}' || *arr < ' ') + arr = NULL; + else + arr++; + } + if(arr == NULL) { + send_status = sst_ok; + break; + } + arr++; + new = str_to_int(arr); + cmd_array_cnt = payload[cmd_array_idx] + size; + //furi_delay_ms(delay_between_pkt); // do it fast + continue; // send next array element + } else + send_status = sst_ok; + } + break; + } while(1); + if(send_status != sst_ok) { + send_status = sst_error; + view_Batch = cmd_curr; + return false; + } + return true; + } + ERR = 7; + strcpy(ERR_STR, "NOT FOUND!"); + send_status = sst_error; + view_Batch = cmd_curr; + return false; +} + +// Return 0 - success, otherwise an error +static uint8_t load_settings_file() { + uint8_t err = 0; + FURI_LOG_D(TAG, "Loading settings file"); + FuriString* str = furi_string_alloc(); + free_store(); + Info[0] = '\0'; + NRF_INITED = false; + while(stream_read_line(file_stream, str)) { + char* p = (char*)furi_string_get_cstr(str); + if(*p <= ' ') continue; + //char* delim_eq = strchr(p, '='); + char* delim_col = strchr(p, ':'); + if(delim_col == NULL) { // Constant found - no ':' + if(Constants == NULL) { + Constants = furi_string_alloc_set(str); + } else + furi_string_cat(Constants, str); + } else { + str_rtrim(p); + if(strncmp(p, SettingsFld_Rate, sizeof(SettingsFld_Rate) - 1) == 0) { + NRF_rate = str_to_int(p + sizeof(SettingsFld_Rate)); + } else if(strncmp(p, SettingsFld_Info, sizeof(SettingsFld_Info) - 1) == 0) { + strncpy(Info, p + sizeof(SettingsFld_Info), sizeof(Info) - 1); + } else if(strncmp(p, SettingsFld_Ch, sizeof(SettingsFld_Ch) - 1) == 0) { + NRF_channel = str_to_int(p + sizeof(SettingsFld_Ch)); + } else if(strncmp(p, SettingsFld_Address, sizeof(SettingsFld_Address) - 1) == 0) { + p += sizeof(SettingsFld_Address); + addr_len = ConvertHexToArray(p, addr, 5); + } else if(strncmp(p, SettingsFld_CRC, sizeof(SettingsFld_CRC) - 1) == 0) { + NRF_CRC = str_to_int(p + sizeof(SettingsFld_CRC)); + } else if(strncmp(p, SettingsFld_DPL, sizeof(SettingsFld_DPL) - 1) == 0) { + NRF_DPL = str_to_int(p + sizeof(SettingsFld_DPL)); + } else if(strncmp(p, SettingsFld_RETR, sizeof(SettingsFld_RETR) - 1) == 0) { + NRF_RETR = str_to_int(p + sizeof(SettingsFld_RETR)); + } else if(strncmp(p, SettingsFld_Resend, sizeof(SettingsFld_Resend) - 1) == 0) { + NRF_resend = str_to_int(p + sizeof(SettingsFld_Resend)); + } else if(strncmp(p, SettingsFld_Delay, sizeof(SettingsFld_Delay) - 1) == 0) { + delay_between_pkt = str_to_int(p + sizeof(SettingsFld_Delay)); + } else if( + strncmp( + p, + SettingsFld_ReadCmdRepeatPeriod, + sizeof(SettingsFld_ReadCmdRepeatPeriod) - 1) == 0) { + ReadCmdRepeatPeriod = str_to_int(p + sizeof(SettingsFld_ReadCmdRepeatPeriod)); + } else if(strncmp(p, SettingsFld_Payload, sizeof(SettingsFld_Payload) - 1) == 0) { + p += sizeof(SettingsFld_Payload); + payload_fields = 0; + payload_size = 0; + do { + uint8_t b = str_to_int(p); + if(b < 1 || b > 4) { + FURI_LOG_D(TAG, "Wrong payload format (%d)", b); + err = 3; + break; + } + payload_struct[payload_fields++] = b; + payload_size += b; + if(payload_fields == sizeof(payload_struct) - 1) break; + if((p = strchr(p, ',')) == NULL) break; + p++; + } while(1); + FURI_LOG_D( + TAG, + "Payload fields %d: %d,%d,%d", + payload_fields, + payload_struct[0], + payload_struct[1], + payload_struct[2]); + } else if(strncmp(p, SettingsFld_ReadDefault, sizeof(SettingsFld_ReadDefault) - 1) == 0) { + ReadDefault = furi_string_alloc_set_str(p + sizeof(SettingsFld_ReadDefault)); + } else if(strncmp(p, SettingsFld_WriteStart, sizeof(SettingsFld_WriteStart) - 1) == 0) { + WriteStart = furi_string_alloc_set_str(p + sizeof(SettingsFld_WriteStart)); + } else if(strncmp(p, SettingsFld_WriteDefault, sizeof(SettingsFld_WriteDefault) - 1) == 0) { + WriteDefault = furi_string_alloc_set_str(p + sizeof(SettingsFld_WriteDefault)); + } else if(strncmp(p, SettingsFld_Read, sizeof(SettingsFld_Read) - 1) == 0) { + p += sizeof(SettingsFld_Read); + if(Read_cmd == NULL) + Read_cmd = malloc(sizeof(Read_cmd)); + else { + Read_cmd = realloc(Read_cmd, sizeof(Read_cmd) * (Read_cmd_Total + 1)); + } + if(Read_cmd == NULL) { + FURI_LOG_D(TAG, "Memory low, err 4"); + err = 4; + break; + } + Read_cmd[Read_cmd_Total++] = furi_string_alloc_set_str(p); + } else if(strncmp(p, SettingsFld_ReadBatch, sizeof(SettingsFld_ReadBatch) - 1) == 0) { + p += sizeof(SettingsFld_ReadBatch); + if(ReadBatch_cmd == NULL) + ReadBatch_cmd = malloc(sizeof(ReadBatch_cmd)); + else { + ReadBatch_cmd = + realloc(ReadBatch_cmd, sizeof(ReadBatch_cmd) * (ReadBatch_cmd_Total + 1)); + } + if(ReadBatch_cmd == NULL) { + FURI_LOG_D(TAG, "Memory low, err 5"); + err = 5; + break; + } + ReadBatch_cmd[ReadBatch_cmd_Total++] = furi_string_alloc_set_str(p); + } else if(strncmp(p, SettingsFld_WriteBatch, sizeof(SettingsFld_WriteBatch) - 1) == 0) { + p += sizeof(SettingsFld_WriteBatch); + if(WriteBatch_cmd == NULL) + WriteBatch_cmd = malloc(sizeof(WriteBatch_cmd)); + else { + WriteBatch_cmd = realloc( + WriteBatch_cmd, sizeof(WriteBatch_cmd) * (WriteBatch_cmd_Total + 1)); + } + if(WriteBatch_cmd == NULL) { + FURI_LOG_D(TAG, "Memory low, err 6"); + err = 6; + break; + } + WriteBatch_cmd[WriteBatch_cmd_Total++] = furi_string_alloc_set_str(p); + } else if(strncmp(p, SettingsFld_SetBatch, sizeof(SettingsFld_SetBatch) - 1) == 0) { + p += sizeof(SettingsFld_SetBatch); + if(SetBatch_cmd == NULL) + SetBatch_cmd = malloc(sizeof(SetBatch_cmd)); + else { + SetBatch_cmd = + realloc(SetBatch_cmd, sizeof(SetBatch_cmd) * (SetBatch_cmd_Total + 1)); + } + if(SetBatch_cmd == NULL) { + FURI_LOG_D(TAG, "Memory low, err 7"); + err = 7; + break; + } + SetBatch_cmd[SetBatch_cmd_Total++] = furi_string_alloc_set_str(p); + } else if(strncmp(p, SettingsFld_Listen, sizeof(SettingsFld_Listen) - 1) == 0) { + p += sizeof(SettingsFld_Listen); + char* p2 = strchr(p, '='); + if(p2) { + listen_addr_len = ConvertHexToArray(p, listen_addr, (p2 - p) / 2); + p2++; + uint8_t len = strlen(p2); + ListenFields = malloc(len + 1); + if(ListenFields) memcpy(ListenFields, p2, len); + } + } + } + } + furi_string_free(str); + return err; +} + +static void save_batch(void) { + FURI_LOG_D(TAG, "Save Batch"); + char *p, *p2; + stream_seek(file_stream, 0, StreamOffsetFromEnd); + FuriHalRtcDateTime dt; + furi_hal_rtc_get_datetime(&dt); + stream_write_format(file_stream, "\n%s ", SettingsFld_WriteBatch); + p = (char*)furi_string_get_cstr(ReadBatch_cmd[view_cmd[rwt_read_batch]]); + p2 = strchr(p, ':'); + if(p2 == NULL) p2 = p + strlen(p); + stream_write(file_stream, (uint8_t*)p, p2 - p); + stream_write_format( + file_stream, + " %02d.%02d.%02d %02d.%02d: ", + dt.day, + dt.month, + dt.year % 100, + dt.hour, + dt.minute); + for(uint16_t i = 0; i < Log_Total; i++) { + p = (char*)furi_string_get_cstr(Log[i]); + p2 = strchr(p, ':'); + if(p2 && *(p2 - 1) != '*') { // skip string + if(*(p2 - 1) == ']') { // array + char* p3 = strchr(p, '['); + if(p3) { + stream_write(file_stream, (uint8_t*)p, p3 - p - (*(p3 - 2) == '*' ? 2 : 0)); + stream_write_cstring(file_stream, "={"); + p = (p2 += 2); + do { + while(is_digit(p2, true) || *p2 == 'x') p2++; + stream_write(file_stream, (uint8_t*)p, p2 - p); + char c = *p2; + if(c == '\0') break; + if(c != ',') { + p2 = strchr(p2, ','); + if(p2 == NULL) break; + } + stream_write_char(file_stream, ','); + p = ++p2; + } while(1); + stream_write_char(file_stream, '}'); + } + } else { + stream_write(file_stream, (uint8_t*)p, p2 - p - (*(p2 - 2) == '*' ? 2 : 0)); + stream_write_char(file_stream, '='); + p2 += 2; + p = strchr(p2, ' '); + if(p == NULL) p = p2 + strlen(p2); + stream_write(file_stream, (uint8_t*)p2, p - p2); + } + if(i < Log_Total - 1) stream_write_char(file_stream, ';'); + } + } +} + +static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { + furi_assert(event_queue); + PluginEvent event = {.type = EventTypeKey, .input = *input_event}; + furi_message_queue_put(event_queue, &event, FuriWaitForever); +} + +void display_remove_asterisk(char* fsp, uint8_t vx) { + char* p2 = strchr(fsp, '*'); + if(p2) { // remove '*' or '*n' + int pos = p2 - fsp; + if((pos -= vx) < 0) pos = 0; + char c = *(p2 + 1); + if(*(screen_buf + pos)) + memmove( + screen_buf + pos, + screen_buf + pos + (c == ':' || c == '=' ? 1 : 2), + FONT_5x7_SCREEN_WIDTH + 1 + 2 - pos); + } +} + +void render_display_list( + Canvas* const canvas, + FuriString*** fsa, + char delim, + uint16_t view_pos, + uint16_t max_i) { + uint16_t page = view_pos & ~7; + char *p, *end; + uint16_t y, len; + for(uint8_t i = 0; i < 8 && page + i < max_i; i++) { + y = 14 + i * 7; + p = (char*)furi_string_get_cstr((*fsa)[page + i]); + end = strchr(p, delim); + if(end) { + len = MIN(end - p, view_x); + len = MIN(end - p - len, FONT_5x7_SCREEN_WIDTH); + strncpy(screen_buf, p + view_x, len); + screen_buf[len] = '\0'; + display_remove_asterisk(p, MIN(end - p, view_x)); + canvas_draw_str(canvas, 5, y, screen_buf); + } + if((view_pos & 7) == i) { + canvas_draw_str(canvas, 0, y, ">"); + canvas_draw_str(canvas, -1, y, ">"); + } + } +} + +void display_edit_ttf_font(Canvas* const canvas, uint8_t start_x, uint8_t start_y) { + screen_buf[0] = *Edit_pos; + screen_buf[1] = '\0'; + int n = canvas_string_width(canvas, screen_buf); + int len = Edit_pos - Edit_start; + memcpy(screen_buf, Edit_start, len); + screen_buf[len] = '\0'; + int x = start_x + canvas_string_width(canvas, screen_buf); + int len2 = strlen(Edit_pos); + memcpy(screen_buf + len, Edit_pos, len2); + screen_buf[len + len2] = '\0'; + canvas_draw_str(canvas, start_x, start_y, screen_buf); + start_y += 1; + canvas_draw_line( + canvas, x + (len ? 1 : 0), start_y, x + n + (*Edit_pos == '1' && len ? 1 : 0), start_y); +} + +void display_add_status(void) { + if(NRF_ERROR) + strcat(screen_buf, "nRF24 ERROR!"); + else if(ERR) + snprintf(screen_buf, sizeof(screen_buf), "ERROR %s", ERR_STR); + else if(send_status == sst_error) + strcat(screen_buf, "NO ACK!"); + else if(send_status == sst_timeout) + strcat(screen_buf, "TIMEOUT!"); + else if(send_status == sst_none) + ; + else if(send_status == sst_sending) + strcat(screen_buf, "sending"); + else if(send_status == sst_receiving) + strcat(screen_buf, "receiving"); + else if( + send_status == sst_ok && + (rw_type == rwt_read_cmd || + (rw_type == rwt_read_batch && (uint32_t)ReadBatch_cmd_curr == 0xFFFFFFFF) || + (rw_type == rwt_set_batch && SetBatch_cmd_curr == Log_Total) || + (rw_type == rwt_write_batch && WriteBatch_cmd_curr == Log_Total))) + strcat(screen_buf, "OK"); + else + strcat(screen_buf, "working"); +} + +static void render_callback(Canvas* const canvas, void* ctx) { + if(ctx == NULL) return; + const PluginState* plugin_state = ctx; + if(furi_mutex_acquire(plugin_state->mutex, 5) != FuriStatusOk) return; + + //canvas_draw_frame(canvas, 0, 0, 128, 64); // border around the edge of the screen + if(what_doing == 0) { + canvas_set_font(canvas, FontSecondary); // 8x10 font, 6 lines + snprintf(screen_buf, sizeof(screen_buf), "%s: %s", addr_len ? "File" : "Open", file_name); + canvas_draw_str(canvas, 8, 10, screen_buf); + if(addr_len) { + if(Edit) { + if(setup_cursor == 1) + display_edit_ttf_font(canvas, 45, 20); + else if(setup_cursor == 2) + display_edit_ttf_font(canvas, 55, 30); + } + if(!Edit || setup_cursor != 1) { + screen_buf[0] = '\0'; + add_to_str_hex_bytes(screen_buf, addr, addr_len); + canvas_draw_str(canvas, 45, 20, screen_buf); + } + if(!Edit || setup_cursor != 2) { + snprintf(screen_buf, sizeof(screen_buf), "%d", NRF_channel); + canvas_draw_str(canvas, 55, 30, screen_buf); + } + canvas_draw_str(canvas, 8, 20, "Address:"); + snprintf(screen_buf, sizeof(screen_buf), "Rate: %d, Ch:", NRF_rate); + canvas_draw_str(canvas, 8, 30, screen_buf); + snprintf( + screen_buf, + sizeof(screen_buf), + "RB: %d, R: %d, WB: %d", + ReadBatch_cmd_Total, + Read_cmd_Total, + WriteBatch_cmd_Total); + canvas_draw_str(canvas, 8, 40, screen_buf); + canvas_draw_str(canvas, 0, 64, Info); + canvas_draw_str(canvas, 0, 10 + setup_cursor * 10, ">"); + } else { + snprintf(screen_buf, sizeof(screen_buf), "Ver. %s, vad7", VERSION); + canvas_draw_str(canvas, 10, 60, screen_buf); + canvas_draw_str(canvas, 0, 10, ">"); + } + } else if(what_doing == 1) { + if(rw_type == rwt_listen) { + canvas_set_font(canvas, FontSecondary); // 8x10 font, 6 lines + canvas_draw_str(canvas, 0, 10, "Listen mode"); + canvas_draw_str(canvas, 0, 25, "Address:"); + if(Edit) + display_edit_ttf_font(canvas, 40, 25); + else if(listen_addr_len) { + screen_buf[0] = '\0'; + add_to_str_hex_bytes(screen_buf, listen_addr, listen_addr_len); + canvas_draw_str(canvas, 40, 25, screen_buf); + } + snprintf( + screen_buf, + sizeof(screen_buf), + "I: %d +(%d) mA", + Current - CurrentStart, + CurrentStart); + canvas_draw_str(canvas, 0, 60, screen_buf); + } else { + canvas_set_font(canvas, FontBatteryPercent); // 5x7 font, 9 lines, 25 cols + if(rw_type == rwt_read_batch) { + canvas_draw_str(canvas, 0, 7, "Read Batch:"); + render_display_list( + canvas, &ReadBatch_cmd, ':', view_cmd[rw_type], ReadBatch_cmd_Total); + } else if(rw_type == rwt_read_cmd) { + canvas_draw_str(canvas, 0, 7, "Read Command:"); + render_display_list(canvas, &Read_cmd, '=', view_cmd[rw_type], Read_cmd_Total); + } else if(rw_type == rwt_write_batch) { + if(!ask_fill_screen_buf()) strcpy(screen_buf, "Write Batch:"); + canvas_draw_str(canvas, 0, 7, screen_buf); + render_display_list( + canvas, &WriteBatch_cmd, ':', view_cmd[rw_type], WriteBatch_cmd_Total); + } else if(rw_type == rwt_set_batch) { + strcpy(screen_buf, "Set: "); + display_add_status(); + canvas_draw_str(canvas, 0, 7, screen_buf); + render_display_list( + canvas, &SetBatch_cmd, ':', view_cmd[rw_type], SetBatch_cmd_Total); + } + } + } else { // what_doing == 2 + if(rw_type == rwt_listen) { + canvas_set_font(canvas, FontSecondary); // 8x10 font, 6 lines + strcpy(screen_buf, "Listen: "); + if(NRF_ERROR) + strcat(screen_buf, "nRF24 ERROR!"); + else if(ListenNew) { + snprintf( + screen_buf + strlen(screen_buf), + 16, + "%02d:%02d:%02d", + ListenLastTime.hour, + ListenLastTime.minute, + ListenLastTime.second); + if(ListenPrev) + snprintf( + screen_buf + strlen(screen_buf), 16, " (%lu)", ListenLast - ListenPrev); + } else + strcat(screen_buf, "receiving"); + snprintf(screen_buf + strlen(screen_buf), 16, " %dmA", Current - CurrentStart); + canvas_draw_str(canvas, 0, 10, screen_buf); + if(ListenFields) { + char *p2, *p = ListenFields; + uint8_t hex, len, *pld = payload_receive; + for(uint8_t i = 0; i < 5 && *p; i++) { + hex = false; + p2 = strchr(p, ','); + if(p2 == NULL) p2 = p + strlen(p); + if(*(p2 - 1) == '#') hex = true; + memcpy(screen_buf, p, len = p2 - p); + strcpy(screen_buf + len, ": "); + if(ListenNew) { + len = payload_struct[i]; + int32_t n = get_payload_receive_field(pld, len); + if(hex) { + strcat(screen_buf, "0x"); + add_to_str_hex_bytes(screen_buf, pld, len); + } else { + snprintf(screen_buf + strlen(screen_buf), 20, "%ld", n); + if(n > 9) { + strcat(screen_buf, " ("); + add_to_str_hex_bytes(screen_buf, pld, len); + strcat(screen_buf, ")"); + } + } + pld += len; + } + canvas_draw_str(canvas, 0, 20 + i * 10, screen_buf); + if(*p2 == '\0') break; + p = p2 + 1; + } + } + } else if(rw_type == rwt_read_cmd) { // Read command + canvas_set_font(canvas, FontSecondary); // 8x10 font, 6 lines + if(!ask_fill_screen_buf()) { + strcpy(screen_buf, "Read "); + strcat(screen_buf, ReadRepeat ? "rep: " : "cmd: "); + } + display_add_status(); + canvas_draw_str(canvas, 0, 10, screen_buf); + if(Log_Total) { + char* p = (char*)furi_string_get_cstr(Log[Log_Total - 1]); + uint8_t vx = MIN(view_x, strlen(p)); + strncpy(screen_buf, p + vx, 30); + display_remove_asterisk(p, vx); + canvas_draw_str(canvas, 0, 15 + 10, screen_buf); + } + } else if(rw_type == rwt_set_batch) { + canvas_set_font(canvas, FontBatteryPercent); // 5x7 font, 9 lines, 25 cols + strcpy(screen_buf, "Set: "); + display_add_status(); + canvas_draw_str(canvas, 0, 7, screen_buf); + render_display_list(canvas, &SetBatch_cmd, ':', view_cmd[rw_type], SetBatch_cmd_Total); + } else { // rwt_read_batch, rwt_write_batch + canvas_set_font(canvas, FontBatteryPercent); // 5x7 font, 9 lines, 25 cols + if(!ask_fill_screen_buf()) { + strcpy(screen_buf, rw_type == rwt_read_batch ? "Read Batch: " : "Write: "); + if(rw_type == rwt_read_batch || send_status != sst_none) { + display_add_status(); + } else if(rw_type == rwt_write_batch) { + char* p = + (char*)furi_string_get_cstr(WriteBatch_cmd[view_cmd[rwt_write_batch]]); + char* end = strchr(p, ':'); + if(end) { + uint8_t len = end - p; + uint8_t lenb = strlen(screen_buf); + end = screen_buf + lenb; + lenb = FONT_5x7_SCREEN_WIDTH - lenb; + if(len > lenb) { + if(view_x < len) { + strncpy(end, p + view_x, len = MIN(lenb, len - view_x)); + end[len] = '\0'; + } + } else { + strncpy(end, p, len); + end[len] = '\0'; + } + } + } + } + canvas_draw_str(canvas, 0, 7, screen_buf); + if(Log_Total) { + char* p; + uint16_t y, page = view_Batch & ~7; + uint8_t vx; + for(uint8_t i = 0; i < 8 && page + i < Log_Total; i++) { + p = (char*)furi_string_get_cstr(Log[page + i]); + y = 14 + i * 7; + vx = MIN(view_x, strlen(p)); + if((view_Batch & 7) == i) { + canvas_draw_str(canvas, 0, y, ">"); + canvas_draw_str(canvas, -1, y, ">"); + if(Edit) { + int n = Edit_pos - p - vx - (FONT_5x7_SCREEN_WIDTH - 4); + if(n > 0) vx += n; // fix out of screen + int x = 6 + (Edit_pos - p - vx) * 5; + canvas_draw_line(canvas, x - 1, y, x - 1, y - 1); + canvas_draw_line(canvas, x - 1, y, n = x + 1 * 5, y); + canvas_draw_line(canvas, n, y, n, y - 1); + } + } + strncpy(screen_buf, p + vx, FONT_5x7_SCREEN_WIDTH + 2); + screen_buf[FONT_5x7_SCREEN_WIDTH + 2] = '\0'; + display_remove_asterisk(p, vx); + canvas_draw_str(canvas, 6, y, screen_buf); + } + } + } + } + furi_mutex_release(plugin_state->mutex); +} + +void work_timer_callback(void* ctx) { + if(ctx == NULL) return; + if(rw_type == rwt_listen && (pwr_read_timer += WORK_PERIOD) >= POWER_READ_PERIOD) { + pwr_read_timer = 0; + update_power(); + } + if(what_doing == 2) { + const PluginState* plugin_state = ctx; + if(furi_mutex_acquire(plugin_state->mutex, 0) != FuriStatusOk) return; + if(rw_type == rwt_write_batch || rw_type == rwt_set_batch) { + uint16_t* cmd_curr = rw_type == rwt_set_batch ? &SetBatch_cmd_curr : + &WriteBatch_cmd_curr; + if(ERR == 0 && furi_get_tick() - NRF_time >= delay_between_pkt) { + if(send_status == sst_ok) { + if(ERR == 0 && *cmd_curr < Log_Total) { + if(++(*cmd_curr) < Log_Total) + Run_WriteBatch_cmd(); + else { // finished ok + if(rw_type == rwt_set_batch) what_doing = 1; + Edited = false; + } + } + } else if(send_status == sst_sending) { + if(NRF_repeat++ < NRF_resend) + nrf24_send_packet(); + else { + view_Batch = *cmd_curr; + send_status = sst_error; // error NO_ACK + } + } + } + if((ERR || send_status == sst_error) && rw_type == rwt_set_batch) { + what_doing = 1; + } + // ReadBatch or ReadCmd + } else if(send_status == sst_sending) { // sending + if(furi_get_tick() - NRF_time > delay_between_pkt) { + if(NRF_repeat++ < NRF_resend) { + nrf24_resend_read_packet(); + } else + send_status = sst_error; // error NO_ACK + } + } else if(send_status == sst_receiving) { // receiving + for(uint8_t i = 0; i < 3; i++) { + if(nrf24_read_newpacket()) { + if(rw_type == rwt_listen) { + ListenPrev = ListenLast; + furi_hal_rtc_get_datetime(&ListenLastTime); + ListenLast = furi_hal_rtc_datetime_to_timestamp(&ListenLastTime); + ListenNew = true; + } else if(send_status != sst_receiving) + break; + } else { + if(rw_type != rwt_listen && furi_get_tick() - NRF_time > NRF_READ_TIMEOUT) { + if(NRF_repeat++ < NRF_resend) { + send_status = sst_sending; + nrf24_resend_read_packet(); + } else { + FURI_LOG_D(TAG, "TIMEOUT: %lu", furi_get_tick() - NRF_time); + send_status = sst_timeout; + } + } + break; + } + } + } else if(send_status == sst_ok) { + if(rw_type == rwt_read_batch) { + if((uint32_t)ReadBatch_cmd_curr != 0xFFFFFFFF && ERR == 0 && + furi_get_tick() - NRF_time >= delay_between_pkt) { + Run_ReadBatch_cmd(NULL); + } + } + } + furi_mutex_release(plugin_state->mutex); + } +} + +void next_rw_type(int8_t add) { + do { + rw_type += add; + if(rw_type >= rwt_max) { + if(add > 0) + rw_type = 0; + else + rw_type = rwt_max - 1; + } + if(rw_type == rwt_set_batch && SetBatch_cmd_Total) break; + if(rw_type == rwt_read_batch && ReadBatch_cmd_Total) break; + if(rw_type == rwt_read_cmd && Read_cmd_Total) break; + if(rw_type == rwt_write_batch && WriteBatch_cmd_Total) break; + } while(rw_type != rwt_listen); + send_status = sst_none; +} + +void next_view_cmd(int8_t add) { + if(rw_type == rwt_listen) return; + uint16_t max = + (rw_type == rwt_read_batch ? ReadBatch_cmd_Total : + rw_type == rwt_read_cmd ? Read_cmd_Total : + rw_type == rwt_set_batch ? SetBatch_cmd_Total : + WriteBatch_cmd_Total); + if((view_cmd[rw_type] += add) >= max) view_cmd[rw_type] = add > 0 ? 0 : max - 1; +} + +int32_t nrf24batch_app(void* p) { + UNUSED(p); + APP = malloc(sizeof(nRF24Batch)); + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent)); + APP->plugin_state = malloc(sizeof(PluginState)); + APP->plugin_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + if(!APP->plugin_state->mutex) { + furi_message_queue_free(event_queue); + FURI_LOG_E(TAG, "cannot create mutex"); + free(APP->plugin_state); + return 255; + } + // Set system callbacks + ViewPort* view_port = view_port_alloc(); + view_port_draw_callback_set(view_port, render_callback, APP->plugin_state); + view_port_input_callback_set(view_port, input_callback, event_queue); + + // Open GUI and register view_port + APP->gui = furi_record_open(RECORD_GUI); + gui_add_view_port(APP->gui, view_port, GuiLayerFullscreen); + APP->notification = furi_record_open(RECORD_NOTIFICATION); + APP->storage = furi_record_open(RECORD_STORAGE); + storage_common_migrate(APP->storage, EXT_PATH("nrf24batch"), SCAN_APP_PATH_FOLDER); + storage_common_mkdir(APP->storage, SCAN_APP_PATH_FOLDER); + file_stream = file_stream_alloc(APP->storage); + FuriTimer* work_timer = + furi_timer_alloc(work_timer_callback, FuriTimerTypePeriodic, APP->plugin_state); + furi_timer_start(work_timer, WORK_PERIOD); + nrf24_init(); + check_en_power_5V(); + + PluginEvent event; + for(bool processing = true; processing;) { + FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); + furi_mutex_acquire(APP->plugin_state->mutex, FuriWaitForever); + + static FuriLogLevel FuriLogLevel = FuriLogLevelDefault; + if(furi_log_get_level() != FuriLogLevel) { + FuriLogLevel = furi_log_get_level(); + if(FuriLogLevel == FuriLogLevelDebug) + furi_hal_uart_set_br(FuriHalUartIdUSART1, 1843200); + } + if(what_doing == 2 && rw_type == rwt_read_cmd && ReadRepeat && + furi_get_tick() - NRF_time > (uint32_t)(ReadCmdRepeatPeriod * 1000)) { + ERR = 0; + free_Log(); + Run_Read_cmd(Read_cmd[view_cmd[rwt_read_cmd]]); + notification_message(APP->notification, &sequence_blink_blue_100); + } + + if(event_status == FuriStatusOk) { + // press events + if(event.type == EventTypeKey) { + switch(event.input.key) { + case InputKeyUp: + if(event.input.type == InputTypeShort || event.input.type == InputTypeRepeat) { + if(!ask_question) { + if(Edit) { + char c = *Edit_pos; + if(Edit_hex) { + if(c == '9') + *Edit_pos = 'A'; + else if(c < 'F' || c < '9') + *Edit_pos = c + 1; + } else { + if(c == '-') + *Edit_pos = '0'; + else if(c < '9') + *Edit_pos = c + 1; + else { + c = *(Edit_pos - 1); + if(Edit_pos > Edit_start && c < '9' && c >= '0') { + *Edit_pos = '0'; + (*(Edit_pos - 1)) = c + 1; + } + } + } + } else if(what_doing == 0) { + if(addr_len) { + if(setup_cursor > 0) + setup_cursor--; + else + setup_cursor = 2; + } + } else if(what_doing == 1) + next_view_cmd(-1); + else if(what_doing == 2) + if(view_Batch) view_Batch--; + } + } + break; + case InputKeyDown: + if(event.input.type == InputTypeShort || event.input.type == InputTypeRepeat) { + if(!ask_question) { + if(Edit) { + if(*Edit_pos != '-') { + if(Edit_hex && *Edit_pos == 'A') + (*Edit_pos) = '9'; + else if(*Edit_pos > '0') + (*Edit_pos)--; + else if(!Edit_hex) { + if(Edit_pos > Edit_start) { + if(*(Edit_pos - 1) > '0' && *(Edit_pos - 1) <= '9') { + *Edit_pos = '9'; + (*(Edit_pos - 1))--; + } + } else + Edit_insert_digit('-'); // negative + } + } + } else if(what_doing == 0) { + if(addr_len) { + if(setup_cursor < 2) + setup_cursor++; + else + setup_cursor = 0; + } + } else if(what_doing == 1) + next_view_cmd(+1); + else if(what_doing == 2) + if(view_Batch < Log_Total - 1) view_Batch++; + } + } + break; + case InputKeyLeft: + if(event.input.type == InputTypeShort || event.input.type == InputTypeRepeat) { + if(ask_question) { + if(event.input.type == InputTypeShort) ask_question_answer ^= 1; + } else if(Edit) { + if(Edit_pos > Edit_start) { + if(is_digit(Edit_pos - 1, Edit_hex)) + Edit_pos--; + else if(*(Edit_pos - 1) == 'x' && *(Edit_pos - 3) == ',') + Edit_pos -= 4; + else if(*(Edit_pos - 1) == ',') + Edit_pos -= 2; + } + } else if(what_doing == 0) { + if(addr_len) { + rw_type = rwt_listen; + what_doing = 1; + } + } else if(what_doing == 1) { + if(event.input.type == InputTypeShort) + next_rw_type(-1); + else if(view_x) + view_x--; + } else if(what_doing == 2) + if(view_x) view_x--; + } else if(event.input.type == InputTypeLong) { + if(!ask_question && view_x == 0 && what_doing == 2 && + (rw_type == rwt_write_batch || rw_type == rwt_read_batch) && + Log_Total && Log[view_Batch] != NULL) { + ask_question = ask_skip_cmd; + ask_question_answer = 1; + } + } + break; + case InputKeyRight: + if(event.input.type == InputTypeShort || event.input.type == InputTypeRepeat) { + if(ask_question) { + ask_question_answer ^= 1; + } else if(Edit) { + if(is_digit(Edit_pos + 1, Edit_hex)) + Edit_pos++; + else if(*(Edit_pos + 1) == ',') { + Edit_pos += 2; + if(*(Edit_pos + 1) == 'x') Edit_pos += 2; + } + } else if(what_doing == 0) { + if(addr_len) { + rw_type = rwt_set_batch; + what_doing = 1; + } + } else if(what_doing == 1) { + if(event.input.type == InputTypeShort) + next_rw_type(+1); + else + view_x++; + } else if(what_doing == 2) + view_x++; + } + break; + case InputKeyOk: + if(event.input.type == InputTypeShort) { + if(ask_question) { + if(ask_question_answer) { + if(ask_question == ask_save_batch) { + save_batch(); + } else if(ask_question == ask_write_batch) { + if(WriteBatch_cmd_Total) { + if(what_doing == 1) { + Prepare_Write_cmd( + WriteBatch_cmd[view_cmd[rwt_write_batch]]); + send_status = sst_none; + Edited = false; + view_x = 0; + view_Batch = 0; + } + if(Log_Total) { + ERR = 0; + WriteBatch_cmd_curr = 0; + Run_WriteBatch_cmd(); + what_doing = 2; + } + } + } else if(ask_question == ask_skip_cmd) { + if(rw_type == rwt_write_batch || rw_type == rwt_read_batch) { + furi_string_free(Log[view_Batch]); + if(view_Batch < Log_Total - 1) + memmove( + &Log[view_Batch], + &Log[view_Batch + 1], + sizeof(Log) * (Log_Total - view_Batch - 1)); + else + view_Batch--; + Log_Total--; + } + } else if(ask_question == ask_exit) { + processing = false; + } else if(ask_question == ask_return) { + if(what_doing == 2) { + ERR = 0; + send_status = sst_none; + Edited = false; + what_doing--; + } + } + } + ask_question = 0; + } else if(Edit) { // insert digit + Edit_insert_digit('0'); + } else if(what_doing == 0) { + if(setup_cursor == 0) { // open file + file_stream_close(file_stream); + if(select_settings_file()) { + uint8_t err = load_settings_file(); + if(err) + snprintf( + file_name, sizeof(file_name), "LOAD ERROR #%d", err); + } + } else if(setup_cursor == 1) { // change address + char* ebuf = (char*)payload; + ebuf[0] = '\0'; + add_to_str_hex_bytes(ebuf, addr, addr_len); + Edit_hex = true; + Edit_pos = ebuf + strlen(ebuf) - 1; + Edit_start = ebuf; + Edit = 1; + NRF_INITED = 0; + } else if(setup_cursor == 2) { // change channel + char* ebuf = (char*)payload; + snprintf(ebuf, sizeof(payload), "%d", NRF_channel); + Edit_hex = false; + Edit_pos = ebuf + strlen(ebuf) - 1; + Edit_start = ebuf; + Edit = 1; + NRF_INITED = 0; + } + } else if(what_doing == 1) { + if(rw_type == rwt_set_batch) { + if(SetBatch_cmd_Total) { + ERR = 0; + send_status = sst_none; + Prepare_Write_cmd(SetBatch_cmd[view_cmd[rwt_set_batch]]); + if(!ERR) { + SetBatch_cmd_curr = 0; + Run_WriteBatch_cmd(); + what_doing = 2; + } + Edited = false; + } + } else if(rw_type == rwt_read_batch) { + if(ReadBatch_cmd_Total) { + ERR = 0; + Run_ReadBatch_cmd(ReadBatch_cmd[view_cmd[rwt_read_batch]]); + view_x = 0; + view_Batch = 0; + what_doing = 2; + } + } else if(rw_type == rwt_read_cmd) { + if(Read_cmd_Total) { + ERR = 0; + free_Log(); + Run_Read_cmd(Read_cmd[view_cmd[rwt_read_cmd]]); + view_x = 0; + what_doing = 2; + } + } else if(rw_type == rwt_write_batch) { + if(WriteBatch_cmd_Total) { + ERR = 0; + Prepare_Write_cmd(WriteBatch_cmd[view_cmd[rwt_write_batch]]); + send_status = sst_none; + Edited = false; + view_x = 0; + view_Batch = 0; + what_doing = 2; + } + } else if(rw_type == rwt_listen) { + if(listen_addr_len) { + free_Log(); + prepare_nrf24(); + if(!NRF_ERROR) { + nrf24_set_rx_mode(nrf24_HANDLE); + ListenNew = false; + send_status = sst_receiving; // receiving + } + what_doing = 2; + } + } + } else if(what_doing == 2) { + if(rw_type == rwt_read_cmd) { + ERR = 0; + free_Log(); + Run_Read_cmd(Read_cmd[view_cmd[rwt_read_cmd]]); + } else if(Log_Total) { + if(rw_type == rwt_read_batch) { + ask_question = ask_save_batch; + ask_question_answer = 0; + } else if(rw_type == rwt_write_batch) { + ask_question = ask_write_batch; + ask_question_answer = 0; + } + } + } + } else if(event.input.type == InputTypeLong) { + if(Edit) { // delete + if(what_doing <= 1) { + if(strlen(Edit_start) > 1) { + memmove(Edit_pos, Edit_pos + 1, strlen(Edit_pos)); + if(*Edit_pos == '\0') Edit_pos--; + } + } else { + FuriString* fs = Log[view_Batch]; + if(is_digit(Edit_pos + 1, Edit_hex) || + (Edit_pos > Edit_start && is_digit(Edit_pos - 1, Edit_hex))) { + memmove(Edit_pos, Edit_pos + 1, strlen(Edit_pos)); + if(*Edit_pos == '\0') Edit_pos--; + furi_string_left(fs, furi_string_size(fs) - 1); + } + } + } else if(what_doing == 1) { + if(rw_type == rwt_listen) { + if(listen_addr_len) { + char* ebuf = (char*)payload; + ebuf[0] = '\0'; + add_to_str_hex_bytes(ebuf, listen_addr, listen_addr_len); + Edit_hex = true; + Edit_pos = ebuf + strlen(ebuf) - 1; + Edit_start = ebuf; + Edit = 1; + NRF_INITED = 0; + } + } else if(rw_type == rwt_read_batch) { + if(ReadBatch_cmd_Total) { + ERR = 0; + Run_ReadBatch_cmd(ReadBatch_cmd[view_cmd[rwt_read_batch]]); + view_x = 0; + view_Batch = 0; + what_doing = 2; + } + } else if(rw_type == rwt_write_batch) { + ask_question = ask_write_batch; + ask_question_answer = 0; + } + } else if(what_doing == 2) { + if(rw_type == rwt_read_cmd) { + ReadRepeat = !ReadRepeat; + } else if(Log_Total) { + if(rw_type == rwt_write_batch) { + if(!Edit) { + Edit = 0; + Edit_hex = 0; + char* s = (char*)furi_string_get_cstr(Log[view_Batch]); + char* p = strchr(s, '='); + if(p) { + p++; + if(*p == '{') p++; // array + if(*(p + 1) == 'x') { + p += 2; + Edit_hex = 1; // hex + } + if(is_digit(p, Edit_hex)) { + Edit_start = p; + while(is_digit(p, Edit_hex)) p++; + Edit_pos = p - 1; + Edited = true; + Edit = 1; + } + } + } + } else if(rw_type == rwt_read_batch) { + ask_question = ask_save_batch; + ask_question_answer = 0; + } + } + } + } + break; + case InputKeyBack: + if(event.input.type == InputTypeLong) { + if(what_doing == 2 && Edited) { + if(!ask_question) ask_question_answer = 1; + ask_question = ask_exit; + } else + processing = false; + } else if(event.input.type == InputTypeShort) { + if(ask_question) + ask_question = 0; + else if(Edit) { + if(what_doing == 0) { + if(setup_cursor == 1) { + addr_len = ConvertHexToArray((char*)payload, addr, 5); + } else if(setup_cursor == 2) { + NRF_channel = str_to_int((char*)payload); + if(NRF_channel > MAX_CHANNEL) NRF_channel = MAX_CHANNEL; + } + } else if(what_doing == 1 && rw_type == rwt_listen) { + listen_addr_len = + ConvertHexToArray((char*)payload, listen_addr, 5); + } + Edit = 0; + } else { + if(what_doing == 2 && Edited) { + ask_question = ask_return; + ask_question_answer = 1; + } else if(what_doing != 0) { + if(what_doing) what_doing--; + if(what_doing == 0) rw_type = rwt_read_batch; + if(what_doing <= 1) view_x = 0; + ERR = 0; + send_status = sst_none; + } + } + } + break; + default: + break; + } + } + } + + view_port_update(view_port); + furi_mutex_release(APP->plugin_state->mutex); + } + nrf24_set_idle(nrf24_HANDLE); + nrf24_deinit(); + if(NRF_BOARD_POWER_5V) furi_hal_power_disable_otg(); + + view_port_enabled_set(view_port, false); + gui_remove_view_port(APP->gui, view_port); + furi_record_close(RECORD_GUI); + furi_record_close(RECORD_NOTIFICATION); + furi_record_close(RECORD_STORAGE); + if(file_stream) { + file_stream_close(file_stream); + stream_free(file_stream); + } + view_port_free(view_port); + furi_message_queue_free(event_queue); + free_store(); + furi_timer_stop(work_timer); + furi_timer_free(work_timer); + furi_mutex_free(APP->plugin_state->mutex); + free(APP->plugin_state); + free(APP); + return 0; +} diff --git a/applications/external/nrf24batch/nrf24batch.h b/applications/external/nrf24batch/nrf24batch.h new file mode 100644 index 0000000000..02db0a4024 --- /dev/null +++ b/applications/external/nrf24batch/nrf24batch.h @@ -0,0 +1,32 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef enum { + EventTypeTick, + EventTypeKey, +} EventType; + +typedef struct { + EventType type; + InputEvent input; +} PluginEvent; + +typedef struct { + FuriMutex* mutex; +} PluginState; + +typedef struct { + Gui* gui; + Storage* storage; + NotificationApp* notification; + PluginState* plugin_state; +} nRF24Batch; diff --git a/applications/external/nrfsniff/nrfsniff_10px.png b/applications/external/nrf24batch/nrf24batch_10px.png similarity index 100% rename from applications/external/nrfsniff/nrfsniff_10px.png rename to applications/external/nrf24batch/nrf24batch_10px.png diff --git a/applications/external/mousejacker/application.fam b/applications/external/nrf24mousejacker/application.fam similarity index 69% rename from applications/external/mousejacker/application.fam rename to applications/external/nrf24mousejacker/application.fam index 3b8ae7104f..f5a9b9dbc6 100644 --- a/applications/external/mousejacker/application.fam +++ b/applications/external/nrf24mousejacker/application.fam @@ -1,5 +1,5 @@ App( - appid="nrf24_mouse_jacker", + appid="nrf24mousejacker", name="[NRF24] Mouse Jacker", apptype=FlipperAppType.EXTERNAL, entry_point="mousejacker_app", @@ -11,7 +11,9 @@ App( order=60, fap_icon="mouse_10px.png", fap_category="GPIO", - fap_icon_assets="images", + fap_author="@mothball187 & @xMasterX", + fap_version="1.0", + fap_description="App works with NRF24 Sniffer app to perform mousejack attacks", fap_private_libs=[ Lib( name="nrf24", diff --git a/applications/external/mousejacker/lib/nrf24/nrf24.c b/applications/external/nrf24mousejacker/lib/nrf24/nrf24.c similarity index 100% rename from applications/external/mousejacker/lib/nrf24/nrf24.c rename to applications/external/nrf24mousejacker/lib/nrf24/nrf24.c diff --git a/applications/external/mousejacker/lib/nrf24/nrf24.h b/applications/external/nrf24mousejacker/lib/nrf24/nrf24.h similarity index 100% rename from applications/external/mousejacker/lib/nrf24/nrf24.h rename to applications/external/nrf24mousejacker/lib/nrf24/nrf24.h diff --git a/applications/external/nrf24mousejacker/mouse_10px.png b/applications/external/nrf24mousejacker/mouse_10px.png new file mode 100644 index 0000000000..94c3a7a141 Binary files /dev/null and b/applications/external/nrf24mousejacker/mouse_10px.png differ diff --git a/applications/external/mousejacker/mousejacker.c b/applications/external/nrf24mousejacker/mousejacker.c similarity index 97% rename from applications/external/mousejacker/mousejacker.c rename to applications/external/nrf24mousejacker/mousejacker.c index 24bc7c67d4..31ed1bcabc 100644 --- a/applications/external/mousejacker/mousejacker.c +++ b/applications/external/nrf24mousejacker/mousejacker.c @@ -10,15 +10,15 @@ #include #include #include "mousejacker_ducky.h" -#include +#include #include #define TAG "mousejacker" #define LOGITECH_MAX_CHANNEL 85 -#define NRFSNIFF_APP_PATH_FOLDER "/ext/nrfsniff" +#define NRFSNIFF_APP_PATH_FOLDER EXT_PATH("apps_data/nrf24sniff") #define NRFSNIFF_APP_PATH_EXTENSION ".txt" #define NRFSNIFF_APP_FILENAME "addresses.txt" -#define MOUSEJACKER_APP_PATH_FOLDER "/ext/mousejacker" +#define MOUSEJACKER_APP_PATH_FOLDER STORAGE_APP_DATA_PATH_PREFIX #define MOUSEJACKER_APP_PATH_EXTENSION ".txt" #define MAX_ADDRS 100 @@ -112,7 +112,7 @@ static bool open_ducky_script(Stream* stream, PluginState* plugin_state) { DialogsFileBrowserOptions browser_options; dialog_file_browser_set_basic_options( - &browser_options, MOUSEJACKER_APP_PATH_EXTENSION, &I_badusb_10px); + &browser_options, MOUSEJACKER_APP_PATH_EXTENSION, &I_badkb_10px); browser_options.hide_ext = false; bool ret = dialog_file_browser_show(dialogs, path, path, &browser_options); @@ -287,7 +287,7 @@ void start_mjthread(PluginState* plugin_state) { int32_t mousejacker_app(void* p) { UNUSED(p); FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent)); - DOLPHIN_DEED(DolphinDeedPluginStart); + dolphin_deed(DolphinDeedPluginStart); PluginState* plugin_state = malloc(sizeof(PluginState)); mousejacker_state_init(plugin_state); @@ -309,6 +309,8 @@ int32_t mousejacker_app(void* p) { gui_add_view_port(gui, view_port, GuiLayerFullscreen); plugin_state->storage = furi_record_open(RECORD_STORAGE); + storage_common_migrate( + plugin_state->storage, EXT_PATH("mousejacker"), MOUSEJACKER_APP_PATH_FOLDER); storage_common_mkdir(plugin_state->storage, MOUSEJACKER_APP_PATH_FOLDER); plugin_state->file_stream = file_stream_alloc(plugin_state->storage); diff --git a/applications/external/mousejacker/mousejacker_ducky.c b/applications/external/nrf24mousejacker/mousejacker_ducky.c similarity index 100% rename from applications/external/mousejacker/mousejacker_ducky.c rename to applications/external/nrf24mousejacker/mousejacker_ducky.c diff --git a/applications/external/mousejacker/mousejacker_ducky.h b/applications/external/nrf24mousejacker/mousejacker_ducky.h similarity index 100% rename from applications/external/mousejacker/mousejacker_ducky.h rename to applications/external/nrf24mousejacker/mousejacker_ducky.h diff --git a/applications/external/nrf24scan/Distr/nrf24scan/addresses.txt b/applications/external/nrf24scan/Distr/nrf24scan/addresses.txt deleted file mode 100644 index e5bd3ed326..0000000000 --- a/applications/external/nrf24scan/Distr/nrf24scan/addresses.txt +++ /dev/null @@ -1,11 +0,0 @@ -Rate: 1 -Ch: 2 -ESB: 1 -DPL: 0 -CRC: 2 -Payload: 4 -P0: C8C8C0 -P1: C8C8C1 -P2: C2 -P3: C3 -P4: E5 diff --git a/applications/external/nrf24scan/Distr/nrf24scan/sniff.txt b/applications/external/nrf24scan/Distr/nrf24scan/sniff.txt deleted file mode 100644 index fa8c5ba9f8..0000000000 --- a/applications/external/nrf24scan/Distr/nrf24scan/sniff.txt +++ /dev/null @@ -1,5 +0,0 @@ -SNIFF -ESB: 1 -CRC: 2 -P0: 00AA -P1: 0055 diff --git a/applications/external/nrf24scan/application.fam b/applications/external/nrf24scan/application.fam index c346112e2b..1be252f7c6 100644 --- a/applications/external/nrf24scan/application.fam +++ b/applications/external/nrf24scan/application.fam @@ -1,5 +1,5 @@ App( - appid="Nrf24_Scanner", + appid="nrf24scan", name="[NRF24] Scanner", apptype=FlipperAppType.EXTERNAL, entry_point="nrf24scan_app", diff --git a/applications/external/nrf24scan/nrf24scan.c b/applications/external/nrf24scan/nrf24scan.c index d7a8b3d31e..0bd4b6b3bc 100644 --- a/applications/external/nrf24scan/nrf24scan.c +++ b/applications/external/nrf24scan/nrf24scan.c @@ -19,7 +19,7 @@ #define MAX_CHANNEL 125 #define MAX_ADDR 6 -#define SCAN_APP_PATH_FOLDER "/ext/nrf24scan" +#define SCAN_APP_PATH_FOLDER STORAGE_APP_DATA_PATH_PREFIX #define SETTINGS_FILENAME "addresses.txt" // Settings file format (1 parameter per line): // SNIFF - if present then sniff mode // Rate: 0/1/2 - rate in Mbps (=0.25/1/2) @@ -1332,7 +1332,7 @@ static void render_callback(Canvas* const canvas, void* ctx) { int32_t nrf24scan_app(void* p) { UNUSED(p); APP = malloc(sizeof(Nrf24Scan)); - DOLPHIN_DEED(DolphinDeedPluginStart); + dolphin_deed(DolphinDeedPluginStart); APP->event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent)); PluginState* plugin_state = malloc(sizeof(PluginState)); plugin_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); @@ -1373,6 +1373,7 @@ int32_t nrf24scan_app(void* p) { gui_add_view_port(APP->gui, APP->view_port, GuiLayerFullscreen); APP->notification = furi_record_open(RECORD_NOTIFICATION); APP->storage = furi_record_open(RECORD_STORAGE); + storage_common_migrate(APP->storage, EXT_PATH("nrf24scan"), SCAN_APP_PATH_FOLDER); storage_common_mkdir(APP->storage, SCAN_APP_PATH_FOLDER); Stream* file_stream = file_stream_alloc(APP->storage); FuriString* path = furi_string_alloc(); @@ -1436,8 +1437,13 @@ int32_t nrf24scan_app(void* p) { else menu_selected = 0; } else if(what_doing == 1) { - view_log_arr_idx += event.input.type == InputTypeRepeat ? 10 : 1; - if(view_log_arr_idx >= log_arr_idx) view_log_arr_idx = log_arr_idx - 1; + if(log_arr_idx == 0) + view_log_arr_idx = 0; + else { + view_log_arr_idx += event.input.type == InputTypeRepeat ? 10 : 1; + if(view_log_arr_idx >= log_arr_idx) + view_log_arr_idx = log_arr_idx - 1; + } } else if(what_doing == 2) { if(view_found < found_total / 7) view_found++; } diff --git a/applications/external/nrfsniff/application.fam b/applications/external/nrf24sniff/application.fam similarity index 64% rename from applications/external/nrfsniff/application.fam rename to applications/external/nrf24sniff/application.fam index 5e0299cb4f..244677e389 100644 --- a/applications/external/nrfsniff/application.fam +++ b/applications/external/nrf24sniff/application.fam @@ -1,5 +1,5 @@ App( - appid="nrf24_sniffer", + appid="nrf24sniff", name="[NRF24] Sniffer", apptype=FlipperAppType.EXTERNAL, entry_point="nrfsniff_app", @@ -8,6 +8,9 @@ App( order=60, fap_icon="nrfsniff_10px.png", fap_category="GPIO", + fap_author="@mothball187 & @xMasterX", + fap_version="1.0", + fap_description="App captures addresses to use with NRF24 Mouse Jacker app to perform mousejack attacks", fap_private_libs=[ Lib( name="nrf24", diff --git a/applications/external/nrfsniff/lib/nrf24/nrf24.c b/applications/external/nrf24sniff/lib/nrf24/nrf24.c similarity index 100% rename from applications/external/nrfsniff/lib/nrf24/nrf24.c rename to applications/external/nrf24sniff/lib/nrf24/nrf24.c diff --git a/applications/external/nrfsniff/lib/nrf24/nrf24.h b/applications/external/nrf24sniff/lib/nrf24/nrf24.h similarity index 100% rename from applications/external/nrfsniff/lib/nrf24/nrf24.h rename to applications/external/nrf24sniff/lib/nrf24/nrf24.h diff --git a/applications/external/nrfsniff/nrfsniff.c b/applications/external/nrf24sniff/nrfsniff.c similarity index 98% rename from applications/external/nrfsniff/nrfsniff.c rename to applications/external/nrf24sniff/nrfsniff.c index 1c2ea543f4..9d5fffe809 100644 --- a/applications/external/nrfsniff/nrfsniff.c +++ b/applications/external/nrf24sniff/nrfsniff.c @@ -15,7 +15,7 @@ #define MAX_ADDRS 100 #define MAX_CONFIRMED 32 -#define NRFSNIFF_APP_PATH_FOLDER "/ext/nrfsniff" +#define NRFSNIFF_APP_PATH_FOLDER STORAGE_APP_DATA_PATH_PREFIX #define NRFSNIFF_APP_FILENAME "addresses.txt" #define TAG "nrfsniff" @@ -315,7 +315,7 @@ static void start_sniffing() { int32_t nrfsniff_app(void* p) { UNUSED(p); - DOLPHIN_DEED(DolphinDeedPluginStart); + dolphin_deed(DolphinDeedPluginStart); uint8_t address[5] = {0}; uint32_t start = 0; hexlify(address, 5, top_address); @@ -343,6 +343,7 @@ int32_t nrfsniff_app(void* p) { NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); Storage* storage = furi_record_open(RECORD_STORAGE); + storage_common_migrate(storage, EXT_PATH("nrfsniff"), NRFSNIFF_APP_PATH_FOLDER); storage_common_mkdir(storage, NRFSNIFF_APP_PATH_FOLDER); PluginEvent event; diff --git a/applications/external/nrf24sniff/nrfsniff_10px.png b/applications/external/nrf24sniff/nrfsniff_10px.png new file mode 100644 index 0000000000..348b35eca7 Binary files /dev/null and b/applications/external/nrf24sniff/nrfsniff_10px.png differ diff --git a/applications/external/ocarina/application.fam b/applications/external/ocarina/application.fam index 192cb2f161..686634023a 100644 --- a/applications/external/ocarina/application.fam +++ b/applications/external/ocarina/application.fam @@ -1,5 +1,5 @@ App( - appid="Ocarina", + appid="ocarina", name="Ocarina", apptype=FlipperAppType.EXTERNAL, entry_point="ocarina_app", @@ -7,7 +7,10 @@ App( requires=["gui"], stack_size=1 * 1024, order=30, - fap_icon="icons/music_10px.png", - fap_category="Music", - fap_icon_assets="icons", + fap_icon="music_10px.png", + fap_category="Media", + fap_author="@invalidna-me", + fap_weburl="https://github.com/invalidna-me/flipperzero-ocarina", + fap_version="1.0", + fap_description="A basic Ocarina (of Time), Controls are the same as the N64 version of the Ocarina of Time", ) diff --git a/applications/external/ocarina/icons/music_10px.png b/applications/external/ocarina/icons/music_10px.png deleted file mode 100644 index d41eb0db8c..0000000000 Binary files a/applications/external/ocarina/icons/music_10px.png and /dev/null differ diff --git a/applications/external/music_player/icons/music_10px.png b/applications/external/ocarina/music_10px.png similarity index 100% rename from applications/external/music_player/icons/music_10px.png rename to applications/external/ocarina/music_10px.png diff --git a/applications/external/orgasmotron/application.fam b/applications/external/orgasmotron/application.fam index 2fd4563398..29d968fb53 100644 --- a/applications/external/orgasmotron/application.fam +++ b/applications/external/orgasmotron/application.fam @@ -1,5 +1,5 @@ App( - appid="Orgasmotron", + appid="orgasmotron", name="Orgasmotron", apptype=FlipperAppType.EXTERNAL, entry_point="orgasmotron_app", diff --git a/applications/external/orgasmotron/orgasmotron.c b/applications/external/orgasmotron/orgasmotron.c index 80c7d9e425..fd66d7b04c 100644 --- a/applications/external/orgasmotron/orgasmotron.c +++ b/applications/external/orgasmotron/orgasmotron.c @@ -16,12 +16,9 @@ void vibro_test_draw_callback(Canvas* canvas, void* ctx) { canvas_set_font(canvas, FontPrimary); canvas_draw_str(canvas, 2, 10, "Vibro Modes"); canvas_set_font(canvas, FontSecondary); - canvas_draw_str(canvas, 2, 22, "LEFT: strong / RIGHT: Soft"); - canvas_set_font(canvas, FontSecondary); - canvas_draw_str(canvas, 2, 34, "UP: Pulsed"); - canvas_set_font(canvas, FontSecondary); - canvas_draw_str(canvas, 2, 46, "DOWN Pleasure combo"); - canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 2, 22, "UP: Pulsed"); + canvas_draw_str(canvas, 2, 34, "LEFT: strong / RIGHT: Soft"); + canvas_draw_str(canvas, 2, 46, "DOWN: Pleasure combo"); canvas_draw_str(canvas, 2, 58, "OK: Pause"); } @@ -59,20 +56,16 @@ int32_t orgasmotron_app(void* p) { NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); InputEvent event; - //int mode = 0; bool processing = true; - //while(furi_message_queue_get(event_queue, &event, FuriWaitForever) == FuriStatusOk) { + size_t i = 0; while(processing) { - FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); + FuriStatus event_status = furi_message_queue_get(event_queue, &event, 50); furi_mutex_acquire(plugin_state->mutex, FuriWaitForever); if(event_status == FuriStatusOk) { if(event.key == InputKeyBack && event.type == InputTypeShort) { //Exit Application - notification_message(notification, &sequence_reset_vibro); - notification_message(notification, &sequence_reset_green); plugin_state->mode = 0; processing = false; - //break; } if(event.key == InputKeyOk && (event.type == InputTypePress || event.type == InputTypeRelease)) { @@ -80,57 +73,80 @@ int32_t orgasmotron_app(void* p) { } if(event.key == InputKeyLeft && (event.type == InputTypePress || event.type == InputTypeRelease)) { + notification_message(notification, &sequence_set_green_255); plugin_state->mode = 1; } if(event.key == InputKeyRight && (event.type == InputTypePress || event.type == InputTypeRelease)) { + notification_message(notification, &sequence_set_green_255); plugin_state->mode = 3; } if(event.key == InputKeyUp && (event.type == InputTypePress || event.type == InputTypeRelease)) { + notification_message(notification, &sequence_set_green_255); plugin_state->mode = 2; } if(event.key == InputKeyDown && (event.type == InputTypePress || event.type == InputTypeRelease)) { + notification_message(notification, &sequence_set_green_255); plugin_state->mode = 4; } + i = 0; } if(plugin_state->mode == 0) { //Stop Vibration - notification_message(notification, &sequence_reset_vibro); - notification_message(notification, &sequence_reset_green); + if(i == 0) { + notification_message(notification, &sequence_reset_vibro); + notification_message(notification, &sequence_reset_green); + i++; + } } else if(plugin_state->mode == 1) { //Full power - notification_message(notification, &sequence_set_vibro_on); - notification_message(notification, &sequence_set_green_255); + if(i == 0) { + notification_message(notification, &sequence_set_vibro_on); + i++; + } } else if(plugin_state->mode == 2) { //Pulsed Vibration - notification_message(notification, &sequence_set_vibro_on); - notification_message(notification, &sequence_set_green_255); - delay(100); - notification_message(notification, &sequence_reset_vibro); + i++; + if(i == 1) { + notification_message(notification, &sequence_set_vibro_on); + } + if(i == 3) { + notification_message(notification, &sequence_reset_vibro); + } + if(i == 4) { + i = 0; + } } else if(plugin_state->mode == 3) { //Soft power - notification_message(notification, &sequence_set_vibro_on); - notification_message(notification, &sequence_set_green_255); - delay(50); - notification_message(notification, &sequence_reset_vibro); - } else if(plugin_state->mode == 4) { - //Special Sequence - for(int i = 0; i < 15; i++) { + i++; + if(i == 1) { notification_message(notification, &sequence_set_vibro_on); - notification_message(notification, &sequence_set_green_255); - delay(50); + } + if(i == 2) { notification_message(notification, &sequence_reset_vibro); - delay(50); + i = 0; } - for(int i = 0; i < 2; i++) { - notification_message(notification, &sequence_set_vibro_on); - notification_message(notification, &sequence_set_green_255); - delay(400); + } else if(plugin_state->mode == 4) { + //Special Sequence + i++; + if(i < 23) { + if(i % 2) { + notification_message(notification, &sequence_set_vibro_on); + } else { + notification_message(notification, &sequence_reset_vibro); + } + } else if(i < 40) { + if(i == 24 || i == 33) { + notification_message(notification, &sequence_set_vibro_on); + } else if(i == 32) { + notification_message(notification, &sequence_reset_vibro); + } + } else if(i == 41) { notification_message(notification, &sequence_reset_vibro); - delay(50); + i = 0; } } furi_mutex_release(plugin_state->mutex); diff --git a/applications/external/paint/application.fam b/applications/external/paint/application.fam index d727ab2d23..a6f154bacb 100644 --- a/applications/external/paint/application.fam +++ b/applications/external/paint/application.fam @@ -1,5 +1,5 @@ App( - appid="Paint", + appid="paint", name="Paint", apptype=FlipperAppType.EXTERNAL, entry_point="paint_app", @@ -8,5 +8,9 @@ App( stack_size=2 * 1024, order=175, fap_icon="paintIcon.png", - fap_category="Misc", + fap_category="Media", + fap_author="@n-o-T-I-n-s-a-n-e", + fap_weburl="https://github.com/n-o-T-I-n-s-a-n-e", + fap_version="1.0", + fap_description="A basic Paint app, Click Ok to draw dot, hold Ok to enable drawing continuously, hold Back to clear the screen", ) diff --git a/applications/external/passgen/application.fam b/applications/external/passgen/application.fam index 6a9652dc10..ededadaecb 100644 --- a/applications/external/passgen/application.fam +++ b/applications/external/passgen/application.fam @@ -6,7 +6,7 @@ App( requires=[ "gui", ], - fap_category="Tools", + fap_category="Misc", fap_icon="icons/passgen_icon.png", fap_icon_assets="icons", ) diff --git a/applications/external/passgen/icons/Ok_btn_9x9.png b/applications/external/passgen/icons/Ok_btn_9x9.png deleted file mode 100644 index 9a1539da20..0000000000 Binary files a/applications/external/passgen/icons/Ok_btn_9x9.png and /dev/null differ diff --git a/applications/external/passgen/icons/Pin_back_arrow_10x8.png b/applications/external/passgen/icons/Pin_back_arrow_10x8.png deleted file mode 100644 index 3bafabd144..0000000000 Binary files a/applications/external/passgen/icons/Pin_back_arrow_10x8.png and /dev/null differ diff --git a/applications/external/passgen/passgen.c b/applications/external/passgen/passgen.c index 12cdc10fb6..be6656f7a2 100644 --- a/applications/external/passgen/passgen.c +++ b/applications/external/passgen/passgen.c @@ -5,6 +5,7 @@ #include #include #include +#include #define PASSGEN_MAX_LENGTH 16 #define PASSGEN_CHARACTERS_LENGTH (26 * 4) diff --git a/applications/external/picopass/application.fam b/applications/external/picopass/application.fam index c5087b8045..b14427f2e4 100644 --- a/applications/external/picopass/application.fam +++ b/applications/external/picopass/application.fam @@ -18,5 +18,4 @@ App( name="loclass", ), ], - fap_icon_assets="icons", ) diff --git a/applications/external/picopass/icons/DolphinMafia_115x62.png b/applications/external/picopass/icons/DolphinMafia_115x62.png deleted file mode 100644 index 66fdb40ff2..0000000000 Binary files a/applications/external/picopass/icons/DolphinMafia_115x62.png and /dev/null differ diff --git a/applications/external/picopass/icons/DolphinNice_96x59.png b/applications/external/picopass/icons/DolphinNice_96x59.png deleted file mode 100644 index a299d36302..0000000000 Binary files a/applications/external/picopass/icons/DolphinNice_96x59.png and /dev/null differ diff --git a/applications/external/picopass/icons/RFIDDolphinReceive_97x61.png b/applications/external/picopass/icons/RFIDDolphinReceive_97x61.png deleted file mode 100644 index e1f5f9f801..0000000000 Binary files a/applications/external/picopass/icons/RFIDDolphinReceive_97x61.png and /dev/null differ diff --git a/applications/external/picopass/icons/RFIDDolphinSend_97x61.png b/applications/external/picopass/icons/RFIDDolphinSend_97x61.png deleted file mode 100644 index 380a970d90..0000000000 Binary files a/applications/external/picopass/icons/RFIDDolphinSend_97x61.png and /dev/null differ diff --git a/applications/external/picopass/lib/loclass/optimized_cipher.c b/applications/external/picopass/lib/loclass/optimized_cipher.c index 94df07bae8..01d48817dd 100644 --- a/applications/external/picopass/lib/loclass/optimized_cipher.c +++ b/applications/external/picopass/lib/loclass/optimized_cipher.c @@ -280,7 +280,22 @@ void loclass_opt_doTagMAC_2( loclass_opt_output(div_key_p, &_init, mac); } -void loclass_iclass_calc_div_key(uint8_t* csn, uint8_t* key, uint8_t* div_key, bool elite) { +void loclass_opt_doBothMAC_2( + LoclassState_t _init, + uint8_t* nr, + uint8_t rmac[4], + uint8_t tmac[4], + const uint8_t* div_key_p) { + loclass_opt_suc(div_key_p, &_init, nr, 4, false); + // Save internal state for reuse before outputting + LoclassState_t nr_state = _init; + loclass_opt_output(div_key_p, &_init, rmac); + // Feed the 32 0 bits for the tag mac + loclass_opt_suc(div_key_p, &nr_state, NULL, 0, true); + loclass_opt_output(div_key_p, &nr_state, tmac); +} + +void loclass_iclass_calc_div_key(uint8_t* csn, const uint8_t* key, uint8_t* div_key, bool elite) { if(elite) { uint8_t keytable[128] = {0}; uint8_t key_index[8] = {0}; diff --git a/applications/external/picopass/lib/loclass/optimized_cipher.h b/applications/external/picopass/lib/loclass/optimized_cipher.h index 2158f0acf7..c96c97d8ae 100644 --- a/applications/external/picopass/lib/loclass/optimized_cipher.h +++ b/applications/external/picopass/lib/loclass/optimized_cipher.h @@ -93,6 +93,21 @@ void loclass_opt_doTagMAC_2( uint8_t mac[4], const uint8_t* div_key_p); +/** + * The same as loclass_opt_doTagMAC_2, but calculates both the reader and tag MACs at the same time + * @param _init - precalculated cipher state + * @param nr - the reader challenge + * @param rmac - where to store the reader MAC + * @param tmac - where to store the tag MAC + * @param div_key_p - the key to use + */ +void loclass_opt_doBothMAC_2( + LoclassState_t _init, + uint8_t* nr, + uint8_t rmac[4], + uint8_t tmac[4], + const uint8_t* div_key_p); + void loclass_doMAC_N(uint8_t* in_p, uint8_t in_size, uint8_t* div_key_p, uint8_t mac[4]); -void loclass_iclass_calc_div_key(uint8_t* csn, uint8_t* key, uint8_t* div_key, bool elite); +void loclass_iclass_calc_div_key(uint8_t* csn, const uint8_t* key, uint8_t* div_key, bool elite); #endif // OPTIMIZED_CIPHER_H diff --git a/applications/external/picopass/lib/loclass/optimized_elite.c b/applications/external/picopass/lib/loclass/optimized_elite.c index 34e9870602..e198a410b1 100644 --- a/applications/external/picopass/lib/loclass/optimized_elite.c +++ b/applications/external/picopass/lib/loclass/optimized_elite.c @@ -153,7 +153,7 @@ Definition 14. Define the rotate key function loclass_rk : (F 82 ) 8 × N → (F loclass_rk(x [0] . . . x [7] , 0) = x [0] . . . x [7] loclass_rk(x [0] . . . x [7] , n + 1) = loclass_rk(loclass_rl(x [0] ) . . . loclass_rl(x [7] ), n) **/ -static void loclass_rk(uint8_t* key, uint8_t n, uint8_t* outp_key) { +static void loclass_rk(const uint8_t* key, uint8_t n, uint8_t* outp_key) { memcpy(outp_key, key, 8); uint8_t j; while(n-- > 0) { @@ -172,7 +172,7 @@ static void loclass_desdecrypt_iclass(uint8_t* iclass_key, uint8_t* input, uint8 mbedtls_des_crypt_ecb(&loclass_ctx_dec, input, output); } -static void loclass_desencrypt_iclass(uint8_t* iclass_key, uint8_t* input, uint8_t* output) { +static void loclass_desencrypt_iclass(const uint8_t* iclass_key, uint8_t* input, uint8_t* output) { uint8_t key_std_format[8] = {0}; loclass_permutekey_rev(iclass_key, key_std_format); mbedtls_des_setkey_enc(&loclass_ctx_enc, key_std_format); @@ -185,7 +185,7 @@ static void loclass_desencrypt_iclass(uint8_t* iclass_key, uint8_t* input, uint8 * @param loclass_hash1 loclass_hash1 * @param key_sel output key_sel=h[loclass_hash1[i]] */ -void loclass_hash2(uint8_t* key64, uint8_t* outp_keytable) { +void loclass_hash2(const uint8_t* key64, uint8_t* outp_keytable) { /** *Expected: * High Security Key Table diff --git a/applications/external/picopass/lib/loclass/optimized_elite.h b/applications/external/picopass/lib/loclass/optimized_elite.h index 5343ebb074..fba512a864 100644 --- a/applications/external/picopass/lib/loclass/optimized_elite.h +++ b/applications/external/picopass/lib/loclass/optimized_elite.h @@ -53,6 +53,6 @@ void loclass_permutekey_rev(const uint8_t key[8], uint8_t dest[8]); * @param k output */ void loclass_hash1(const uint8_t* csn, uint8_t* k); -void loclass_hash2(uint8_t* key64, uint8_t* outp_keytable); +void loclass_hash2(const uint8_t* key64, uint8_t* outp_keytable); #endif diff --git a/applications/external/picopass/loclass_writer.c b/applications/external/picopass/loclass_writer.c new file mode 100644 index 0000000000..273fa67eb3 --- /dev/null +++ b/applications/external/picopass/loclass_writer.c @@ -0,0 +1,100 @@ +#include "loclass_writer.h" + +#include +#include +#include +#include +#include + +struct LoclassWriter { + Stream* file_stream; +}; + +#define LOCLASS_LOGS_PATH EXT_PATH("apps_data/picopass/.loclass.log") + +LoclassWriter* loclass_writer_alloc() { + LoclassWriter* instance = malloc(sizeof(LoclassWriter)); + Storage* storage = furi_record_open(RECORD_STORAGE); + instance->file_stream = buffered_file_stream_alloc(storage); + if(!buffered_file_stream_open( + instance->file_stream, LOCLASS_LOGS_PATH, FSAM_WRITE, FSOM_OPEN_APPEND)) { + buffered_file_stream_close(instance->file_stream); + stream_free(instance->file_stream); + free(instance); + instance = NULL; + } + + furi_record_close(RECORD_STORAGE); + + return instance; +} + +void loclass_writer_free(LoclassWriter* instance) { + furi_assert(instance != NULL); + + buffered_file_stream_close(instance->file_stream); + stream_free(instance->file_stream); + free(instance); +} + +bool loclass_writer_write_start_stop(LoclassWriter* instance, bool start) { + FuriHalRtcDateTime curr_dt; + furi_hal_rtc_get_datetime(&curr_dt); + uint32_t curr_ts = furi_hal_rtc_datetime_to_timestamp(&curr_dt); + + FuriString* str = furi_string_alloc_printf( + "loclass-v1-info ts %lu %s\n", curr_ts, start ? "started" : "finished"); + bool write_success = stream_write_string(instance->file_stream, str); + furi_string_free(str); + return write_success; +} + +bool loclass_writer_write_params( + LoclassWriter* instance, + uint8_t log_no, + const uint8_t csn[8], + const uint8_t epurse[8], + const uint8_t nr[4], + const uint8_t mac[4]) { + furi_assert(instance != NULL); + + FuriHalRtcDateTime curr_dt; + furi_hal_rtc_get_datetime(&curr_dt); + uint32_t curr_ts = furi_hal_rtc_datetime_to_timestamp(&curr_dt); + + FuriString* str = furi_string_alloc_printf( + "loclass-v1-mac ts %lu no %u " + "csn %02x%02x%02x%02x%02x%02x%02x%02x " + "cc %02x%02x%02x%02x%02x%02x%02x%02x " + "nr %02x%02x%02x%02x " + "mac %02x%02x%02x%02x\n", + curr_ts, + log_no, + csn[0], + csn[1], + csn[2], + csn[3], + csn[4], + csn[5], + csn[6], + csn[7], + epurse[0], + epurse[1], + epurse[2], + epurse[3], + epurse[4], + epurse[5], + epurse[6], + epurse[7], + nr[0], + nr[1], + nr[2], + nr[3], + mac[0], + mac[1], + mac[2], + mac[3]); + bool write_success = stream_write_string(instance->file_stream, str); + furi_string_free(str); + return write_success; +} \ No newline at end of file diff --git a/applications/external/picopass/loclass_writer.h b/applications/external/picopass/loclass_writer.h new file mode 100644 index 0000000000..dd7a4560cb --- /dev/null +++ b/applications/external/picopass/loclass_writer.h @@ -0,0 +1,20 @@ +#pragma once + +#include +#include + +typedef struct LoclassWriter LoclassWriter; + +LoclassWriter* loclass_writer_alloc(); + +void loclass_writer_free(LoclassWriter* instance); + +bool loclass_writer_write_start_stop(LoclassWriter* instance, bool start); + +bool loclass_writer_write_params( + LoclassWriter* instance, + uint8_t log_no, + const uint8_t csn[8], + const uint8_t epurse[8], + const uint8_t nr[4], + const uint8_t mac[4]); diff --git a/applications/external/picopass/picopass.c b/applications/external/picopass/picopass.c index e3271ae483..52ceab08aa 100644 --- a/applications/external/picopass/picopass.c +++ b/applications/external/picopass/picopass.c @@ -80,6 +80,10 @@ Picopass* picopass_alloc() { PicopassViewDictAttack, dict_attack_get_view(picopass->dict_attack)); + picopass->loclass = loclass_alloc(); + view_dispatcher_add_view( + picopass->view_dispatcher, PicopassViewLoclass, loclass_get_view(picopass->loclass)); + return picopass; } @@ -113,6 +117,9 @@ void picopass_free(Picopass* picopass) { view_dispatcher_remove_view(picopass->view_dispatcher, PicopassViewDictAttack); dict_attack_free(picopass->dict_attack); + view_dispatcher_remove_view(picopass->view_dispatcher, PicopassViewLoclass); + loclass_free(picopass->loclass); + // Worker picopass_worker_stop(picopass->worker); picopass_worker_free(picopass->worker); @@ -154,6 +161,13 @@ static const NotificationSequence picopass_sequence_blink_start_cyan = { NULL, }; +static const NotificationSequence picopass_sequence_blink_start_magenta = { + &message_blink_start_10, + &message_blink_set_color_magenta, + &message_do_not_reset, + NULL, +}; + static const NotificationSequence picopass_sequence_blink_stop = { &message_blink_stop, NULL, @@ -163,6 +177,10 @@ void picopass_blink_start(Picopass* picopass) { notification_message(picopass->notifications, &picopass_sequence_blink_start_cyan); } +void picopass_blink_emulate_start(Picopass* picopass) { + notification_message(picopass->notifications, &picopass_sequence_blink_start_magenta); +} + void picopass_blink_stop(Picopass* picopass) { notification_message(picopass->notifications, &picopass_sequence_blink_stop); } @@ -183,7 +201,7 @@ void picopass_show_loading_popup(void* context, bool show) { static void picopass_migrate_from_old_folder() { Storage* storage = furi_record_open(RECORD_STORAGE); - storage_common_migrate(storage, "/ext/picopass", STORAGE_APP_DATA_PATH_PREFIX); + storage_common_migrate(storage, EXT_PATH("picopass"), STORAGE_APP_DATA_PATH_PREFIX); furi_record_close(RECORD_STORAGE); } @@ -201,7 +219,7 @@ int32_t picopass_app(void* p) { UNUSED(p); picopass_migrate_from_old_folder(); - DOLPHIN_DEED(DolphinDeedPluginStart); + dolphin_deed(DolphinDeedPluginStart); Picopass* picopass = picopass_alloc(); scene_manager_next_scene(picopass->scene_manager, PicopassSceneStart); diff --git a/applications/external/picopass/picopass_device.c b/applications/external/picopass/picopass_device.c index 53778cfb32..c4cb6aed05 100644 --- a/applications/external/picopass/picopass_device.c +++ b/applications/external/picopass/picopass_device.c @@ -2,7 +2,7 @@ #include #include -#include +#include #define TAG "PicopassDevice" @@ -67,13 +67,14 @@ static bool picopass_device_save_file( if(!flipper_format_write_uint32(file, "Facility Code", &fc, 1)) break; if(!flipper_format_write_uint32(file, "Card Number", &cn, 1)) break; if(!flipper_format_write_hex( - file, "Credential", pacs->credential, PICOPASS_BLOCK_LEN)) + file, "Credential", pacs->credential, RFAL_PICOPASS_BLOCK_LEN)) break; if(pacs->pin_length > 0) { - if(!flipper_format_write_hex(file, "PIN\t\t", pacs->pin0, PICOPASS_BLOCK_LEN)) + if(!flipper_format_write_hex( + file, "PIN\t\t", pacs->pin0, RFAL_PICOPASS_BLOCK_LEN)) break; if(!flipper_format_write_hex( - file, "PIN(cont.)\t", pacs->pin1, PICOPASS_BLOCK_LEN)) + file, "PIN(cont.)\t", pacs->pin1, RFAL_PICOPASS_BLOCK_LEN)) break; } } @@ -86,7 +87,10 @@ static bool picopass_device_save_file( for(size_t i = 0; i < app_limit; i++) { furi_string_printf(temp_str, "Block %d", i); if(!flipper_format_write_hex( - file, furi_string_get_cstr(temp_str), AA1[i].data, PICOPASS_BLOCK_LEN)) { + file, + furi_string_get_cstr(temp_str), + AA1[i].data, + RFAL_PICOPASS_BLOCK_LEN)) { block_saved = false; break; } @@ -124,7 +128,7 @@ bool picopass_device_save(PicopassDevice* dev, const char* dev_name) { return picopass_device_save_file( dev, dev_name, STORAGE_APP_DATA_PATH_PREFIX, PICOPASS_APP_EXTENSION, true); } else if(dev->format == PicopassDeviceSaveFormatLF) { - return picopass_device_save_file(dev, dev_name, ANY_PATH("lfrfid"), ".rfid", true); + return picopass_device_save_file(dev, dev_name, EXT_PATH("lfrfid"), ".rfid", true); } return false; @@ -160,7 +164,7 @@ static bool picopass_device_load_data(PicopassDevice* dev, FuriString* path, boo for(size_t i = 0; i < 6; i++) { furi_string_printf(temp_str, "Block %d", i); if(!flipper_format_read_hex( - file, furi_string_get_cstr(temp_str), AA1[i].data, PICOPASS_BLOCK_LEN)) { + file, furi_string_get_cstr(temp_str), AA1[i].data, RFAL_PICOPASS_BLOCK_LEN)) { block_read = false; break; } @@ -172,7 +176,7 @@ static bool picopass_device_load_data(PicopassDevice* dev, FuriString* path, boo for(size_t i = 6; i < app_limit; i++) { furi_string_printf(temp_str, "Block %d", i); if(!flipper_format_read_hex( - file, furi_string_get_cstr(temp_str), AA1[i].data, PICOPASS_BLOCK_LEN)) { + file, furi_string_get_cstr(temp_str), AA1[i].data, RFAL_PICOPASS_BLOCK_LEN)) { block_read = false; break; } @@ -335,9 +339,9 @@ ReturnCode picopass_device_parse_credential(PicopassBlock* AA1, PicopassPacs* pa } } else if(pacs->encryption == PicopassDeviceEncryptionNone) { FURI_LOG_D(TAG, "No Encryption"); - memcpy(pacs->credential, AA1[7].data, PICOPASS_BLOCK_LEN); - memcpy(pacs->pin0, AA1[8].data, PICOPASS_BLOCK_LEN); - memcpy(pacs->pin1, AA1[9].data, PICOPASS_BLOCK_LEN); + memcpy(pacs->credential, AA1[7].data, RFAL_PICOPASS_BLOCK_LEN); + memcpy(pacs->pin0, AA1[8].data, RFAL_PICOPASS_BLOCK_LEN); + memcpy(pacs->pin1, AA1[9].data, RFAL_PICOPASS_BLOCK_LEN); } else if(pacs->encryption == PicopassDeviceEncryptionDES) { FURI_LOG_D(TAG, "DES Encrypted"); } else { diff --git a/applications/external/picopass/picopass_device.h b/applications/external/picopass/picopass_device.h index 7fc35ebda1..96a586a938 100644 --- a/applications/external/picopass/picopass_device.h +++ b/applications/external/picopass/picopass_device.h @@ -7,23 +7,38 @@ #include #include "rfal_picopass.h" +#include "loclass_writer.h" #include #include #include "helpers/iclass_elite_dict.h" #define PICOPASS_DEV_NAME_MAX_LEN 22 #define PICOPASS_READER_DATA_MAX_SIZE 64 -#define PICOPASS_BLOCK_LEN 8 #define PICOPASS_MAX_APP_LIMIT 32 #define PICOPASS_CSN_BLOCK_INDEX 0 #define PICOPASS_CONFIG_BLOCK_INDEX 1 -#define PICOPASS_EPURSE_BLOCK_INDEX 2 -#define PICOPASS_KD_BLOCK_INDEX 3 -#define PICOPASS_KC_BLOCK_INDEX 4 -#define PICOPASS_AIA_BLOCK_INDEX 5 -#define PICOPASS_PACS_CFG_BLOCK_INDEX 6 - +// These definitions for blocks above 2 only hold for secure cards. +#define PICOPASS_SECURE_EPURSE_BLOCK_INDEX 2 +#define PICOPASS_SECURE_KD_BLOCK_INDEX 3 +#define PICOPASS_SECURE_KC_BLOCK_INDEX 4 +#define PICOPASS_SECURE_AIA_BLOCK_INDEX 5 +// Non-secure cards instead have an AIA at block 2 +#define PICOPASS_NONSECURE_AIA_BLOCK_INDEX 2 +// Only iClass cards +#define PICOPASS_ICLASS_PACS_CFG_BLOCK_INDEX 6 + +// Personalization Mode +#define PICOPASS_FUSE_PERS 0x80 +// Crypt1 // 1+1 (crypt1+crypt0) means secured and keys changable +#define PICOPASS_FUSE_CRYPT1 0x10 +// Crypt0 // 1+0 means secure and keys locked, 0+1 means not secured, 0+0 means disable auth entirely +#define PICOPASS_FUSE_CRTPT0 0x08 +#define PICOPASS_FUSE_CRYPT10 (PICOPASS_FUSE_CRYPT1 | PICOPASS_FUSE_CRTPT0) +// Read Access, 1 meanns anonymous read enabled, 0 means must auth to read applicaion +#define PICOPASS_FUSE_RA 0x01 + +#define PICOPASS_APP_FOLDER EXT_PATH("picopass") #define PICOPASS_APP_EXTENSION ".picopass" #define PICOPASS_APP_SHADOW_EXTENSION ".pas" @@ -49,6 +64,13 @@ typedef enum { PicopassDeviceSaveFormatLF, } PicopassDeviceSaveFormat; +typedef enum { + PicopassEmulatorStateHalt, + PicopassEmulatorStateIdle, + PicopassEmulatorStateActive, + PicopassEmulatorStateSelected, +} PicopassEmulatorState; + typedef struct { bool valid; uint8_t bitLength; @@ -71,7 +93,7 @@ typedef struct { } PicopassPacs; typedef struct { - uint8_t data[PICOPASS_BLOCK_LEN]; + uint8_t data[RFAL_PICOPASS_BLOCK_LEN]; } PicopassBlock; typedef struct { @@ -80,6 +102,15 @@ typedef struct { IclassEliteDictAttackData iclass_elite_dict_attack_data; } PicopassDeviceData; +typedef struct { + PicopassEmulatorState state; + LoclassState_t cipher_state; + uint8_t key_block_num; // in loclass mode used to store csn# + bool loclass_mode; + bool loclass_got_std_key; + LoclassWriter* loclass_writer; +} PicopassEmulatorCtx; + typedef struct { Storage* storage; DialogsApp* dialogs; diff --git a/applications/external/picopass/picopass_i.h b/applications/external/picopass/picopass_i.h index 9147cfa0cf..88c2140ee7 100644 --- a/applications/external/picopass/picopass_i.h +++ b/applications/external/picopass/picopass_i.h @@ -22,13 +22,18 @@ #include "scenes/picopass_scene.h" #include "views/dict_attack.h" +#include "views/loclass.h" #include #include -#include +#include #define PICOPASS_TEXT_STORE_SIZE 128 +#define LOCLASS_NUM_CSNS 9 +// Collect 2 MACs per CSN to account for keyroll modes +#define LOCLASS_MACS_TO_COLLECT (LOCLASS_NUM_CSNS * 2) + enum PicopassCustomEvent { // Reserve first 100 events for button types and indexes, starting from 0 PicopassCustomEventReserved = 100, @@ -63,6 +68,7 @@ struct Picopass { TextInput* text_input; Widget* widget; DictAttack* dict_attack; + Loclass* loclass; }; typedef enum { @@ -72,6 +78,7 @@ typedef enum { PicopassViewTextInput, PicopassViewWidget, PicopassViewDictAttack, + PicopassViewLoclass, } PicopassView; Picopass* picopass_alloc(); @@ -82,6 +89,8 @@ void picopass_text_store_clear(Picopass* picopass); void picopass_blink_start(Picopass* picopass); +void picopass_blink_emulate_start(Picopass* picopass); + void picopass_blink_stop(Picopass* picopass); void picopass_show_loading_popup(void* context, bool show); diff --git a/applications/external/picopass/picopass_keys.h b/applications/external/picopass/picopass_keys.h index 2b5dba6610..dc43fc68bc 100644 --- a/applications/external/picopass/picopass_keys.h +++ b/applications/external/picopass/picopass_keys.h @@ -2,9 +2,9 @@ #include "picopass_device.h" -extern const uint8_t picopass_iclass_key[PICOPASS_BLOCK_LEN]; -extern const uint8_t picopass_factory_credit_key[PICOPASS_BLOCK_LEN]; -extern const uint8_t picopass_factory_debit_key[PICOPASS_BLOCK_LEN]; -extern const uint8_t picopass_xice_key[PICOPASS_BLOCK_LEN]; -extern const uint8_t picopass_xicl_key[PICOPASS_BLOCK_LEN]; -extern const uint8_t picopass_xics_key[PICOPASS_BLOCK_LEN]; +extern const uint8_t picopass_iclass_key[RFAL_PICOPASS_BLOCK_LEN]; +extern const uint8_t picopass_factory_credit_key[RFAL_PICOPASS_BLOCK_LEN]; +extern const uint8_t picopass_factory_debit_key[RFAL_PICOPASS_BLOCK_LEN]; +extern const uint8_t picopass_xice_key[RFAL_PICOPASS_BLOCK_LEN]; +extern const uint8_t picopass_xicl_key[RFAL_PICOPASS_BLOCK_LEN]; +extern const uint8_t picopass_xics_key[RFAL_PICOPASS_BLOCK_LEN]; diff --git a/applications/external/picopass/picopass_worker.c b/applications/external/picopass/picopass_worker.c index 5e85e6edac..815d10cbbf 100644 --- a/applications/external/picopass/picopass_worker.c +++ b/applications/external/picopass/picopass_worker.c @@ -1,9 +1,25 @@ #include "picopass_worker_i.h" #include +#include #define TAG "PicopassWorker" +#define HAS_MASK(x, b) ((x & b) == b) + +// CSNs from Proxmark3 repo +static const uint8_t loclass_csns[LOCLASS_NUM_CSNS][RFAL_PICOPASS_BLOCK_LEN] = { + {0x01, 0x0A, 0x0F, 0xFF, 0xF7, 0xFF, 0x12, 0xE0}, + {0x0C, 0x06, 0x0C, 0xFE, 0xF7, 0xFF, 0x12, 0xE0}, + {0x10, 0x97, 0x83, 0x7B, 0xF7, 0xFF, 0x12, 0xE0}, + {0x13, 0x97, 0x82, 0x7A, 0xF7, 0xFF, 0x12, 0xE0}, + {0x07, 0x0E, 0x0D, 0xF9, 0xF7, 0xFF, 0x12, 0xE0}, + {0x14, 0x96, 0x84, 0x76, 0xF7, 0xFF, 0x12, 0xE0}, + {0x17, 0x96, 0x85, 0x71, 0xF7, 0xFF, 0x12, 0xE0}, + {0xCE, 0xC5, 0x0F, 0x77, 0xF7, 0xFF, 0x12, 0xE0}, + {0xD2, 0x5A, 0x82, 0xF8, 0xF7, 0xFF, 0x12, 0xE0}, +}; + static void picopass_worker_enable_field() { furi_hal_nfc_ll_txrx_on(); furi_hal_nfc_exit_sleep(); @@ -68,6 +84,21 @@ void picopass_worker_stop(PicopassWorker* picopass_worker) { furi_assert(picopass_worker); furi_assert(picopass_worker->thread); + if(furi_thread_get_state(picopass_worker->thread) == FuriThreadStateStopped) { + return; + } + + if(picopass_worker->state == PicopassWorkerStateBroken || + picopass_worker->state == PicopassWorkerStateReady) { + return; + } + + if(picopass_worker->state != PicopassWorkerStateEmulate && + picopass_worker->state != PicopassWorkerStateLoclass) { + // Can't do this while emulating in transparent mode as SPI isn't active + picopass_worker_disable_field(ERR_NONE); + } + if(furi_thread_get_state(picopass_worker->thread) != FuriThreadStateStopped) { picopass_worker_change_state(picopass_worker, PicopassWorkerStateStop); furi_thread_join(picopass_worker->thread); @@ -153,19 +184,19 @@ ReturnCode picopass_read_preauth(PicopassBlock* AA1) { AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[7]); rfalPicoPassReadBlockRes aia; - rfalPicoPassPollerReadBlock(PICOPASS_AIA_BLOCK_INDEX, &aia); - memcpy(AA1[PICOPASS_AIA_BLOCK_INDEX].data, aia.data, sizeof(aia.data)); + rfalPicoPassPollerReadBlock(PICOPASS_SECURE_AIA_BLOCK_INDEX, &aia); + memcpy(AA1[PICOPASS_SECURE_AIA_BLOCK_INDEX].data, aia.data, sizeof(aia.data)); FURI_LOG_D( TAG, "aia %02x%02x%02x%02x%02x%02x%02x%02x", - AA1[PICOPASS_AIA_BLOCK_INDEX].data[0], - AA1[PICOPASS_AIA_BLOCK_INDEX].data[1], - AA1[PICOPASS_AIA_BLOCK_INDEX].data[2], - AA1[PICOPASS_AIA_BLOCK_INDEX].data[3], - AA1[PICOPASS_AIA_BLOCK_INDEX].data[4], - AA1[PICOPASS_AIA_BLOCK_INDEX].data[5], - AA1[PICOPASS_AIA_BLOCK_INDEX].data[6], - AA1[PICOPASS_AIA_BLOCK_INDEX].data[7]); + AA1[PICOPASS_SECURE_AIA_BLOCK_INDEX].data[0], + AA1[PICOPASS_SECURE_AIA_BLOCK_INDEX].data[1], + AA1[PICOPASS_SECURE_AIA_BLOCK_INDEX].data[2], + AA1[PICOPASS_SECURE_AIA_BLOCK_INDEX].data[3], + AA1[PICOPASS_SECURE_AIA_BLOCK_INDEX].data[4], + AA1[PICOPASS_SECURE_AIA_BLOCK_INDEX].data[5], + AA1[PICOPASS_SECURE_AIA_BLOCK_INDEX].data[6], + AA1[PICOPASS_SECURE_AIA_BLOCK_INDEX].data[7]); return ERR_NONE; } @@ -181,7 +212,7 @@ static ReturnCode PicopassPacs* pacs = &dev_data->pacs; uint8_t* csn = AA1[PICOPASS_CSN_BLOCK_INDEX].data; - uint8_t* div_key = AA1[PICOPASS_KD_BLOCK_INDEX].data; + uint8_t* div_key = AA1[PICOPASS_SECURE_KD_BLOCK_INDEX].data; ReturnCode err = ERR_PARAM; @@ -189,7 +220,7 @@ static ReturnCode uint8_t ccnr[12] = {0}; size_t index = 0; - uint8_t key[PICOPASS_BLOCK_LEN] = {0}; + uint8_t key[RFAL_PICOPASS_BLOCK_LEN] = {0}; if(!iclass_elite_dict_check_presence(dict_type)) { FURI_LOG_E(TAG, "Dictionary not found"); @@ -230,7 +261,7 @@ static ReturnCode err = rfalPicoPassPollerCheck(mac, &chkRes); if(err == ERR_NONE) { - memcpy(pacs->key, key, PICOPASS_BLOCK_LEN); + memcpy(pacs->key, key, RFAL_PICOPASS_BLOCK_LEN); break; } @@ -274,7 +305,7 @@ ReturnCode picopass_read_card(PicopassBlock* AA1) { PICOPASS_MAX_APP_LIMIT; for(size_t i = 2; i < app_limit; i++) { - if(i == PICOPASS_KD_BLOCK_INDEX) { + if(i == PICOPASS_SECURE_KD_BLOCK_INDEX) { // Skip over Kd block which is populated earlier (READ of Kd returns all FF's) continue; } @@ -349,7 +380,7 @@ ReturnCode picopass_write_card(PicopassBlock* AA1) { FURI_LOG_D(TAG, "rfalPicoPassPollerWriteBlock %d", i); uint8_t data[9] = {0}; data[0] = i; - memcpy(data + 1, AA1[i].data, RFAL_PICOPASS_MAX_BLOCK_LEN); + memcpy(data + 1, AA1[i].data, RFAL_PICOPASS_BLOCK_LEN); loclass_doMAC_N(data, sizeof(data), div_key, mac); FURI_LOG_D( TAG, @@ -408,12 +439,12 @@ ReturnCode picopass_write_block(PicopassBlock* AA1, uint8_t blockNo, uint8_t* ne } memcpy(ccnr, rcRes.CCNR, sizeof(rcRes.CCNR)); // last 4 bytes left 0 - if(memcmp(selRes.CSN, AA1[PICOPASS_CSN_BLOCK_INDEX].data, PICOPASS_BLOCK_LEN) != 0) { + if(memcmp(selRes.CSN, AA1[PICOPASS_CSN_BLOCK_INDEX].data, RFAL_PICOPASS_BLOCK_LEN) != 0) { FURI_LOG_E(TAG, "Wrong CSN for write"); return ERR_REQUEST; } - loclass_opt_doReaderMAC(ccnr, AA1[PICOPASS_KD_BLOCK_INDEX].data, mac); + loclass_opt_doReaderMAC(ccnr, AA1[PICOPASS_SECURE_KD_BLOCK_INDEX].data, mac); err = rfalPicoPassPollerCheck(mac, &chkRes); if(err != ERR_NONE) { FURI_LOG_E(TAG, "rfalPicoPassPollerCheck error %d", err); @@ -431,7 +462,7 @@ ReturnCode picopass_write_block(PicopassBlock* AA1, uint8_t blockNo, uint8_t* ne newBlock[5], newBlock[6], newBlock[7]}; - loclass_doMAC_N(data, sizeof(data), AA1[PICOPASS_KD_BLOCK_INDEX].data, mac); + loclass_doMAC_N(data, sizeof(data), AA1[PICOPASS_SECURE_KD_BLOCK_INDEX].data, mac); FURI_LOG_D( TAG, "loclass_doMAC_N %d %02x%02x%02x%02x%02x%02x%02x%02x %02x%02x%02x%02x", @@ -484,7 +515,7 @@ void picopass_worker_elite_dict_attack(PicopassWorker* picopass_worker) { uint8_t ccnr[12] = {0}; size_t index = 0; - uint8_t key[PICOPASS_BLOCK_LEN] = {0}; + uint8_t key[RFAL_PICOPASS_BLOCK_LEN] = {0}; // Load dictionary IclassEliteDict* dict = dict_attack_data->dict; @@ -541,7 +572,7 @@ void picopass_worker_elite_dict_attack(PicopassWorker* picopass_worker) { memcpy(ccnr, rcRes.CCNR, sizeof(rcRes.CCNR)); // last 4 bytes left 0 uint8_t* csn = AA1[PICOPASS_CSN_BLOCK_INDEX].data; - uint8_t* div_key = AA1[PICOPASS_KD_BLOCK_INDEX].data; + uint8_t* div_key = AA1[PICOPASS_SECURE_KD_BLOCK_INDEX].data; loclass_iclass_calc_div_key(csn, key, div_key, elite); loclass_opt_doReaderMAC(ccnr, div_key, mac); @@ -549,7 +580,7 @@ void picopass_worker_elite_dict_attack(PicopassWorker* picopass_worker) { err = rfalPicoPassPollerCheck(mac, &chkRes); if(err == ERR_NONE) { FURI_LOG_I(TAG, "Found key"); - memcpy(pacs->key, key, PICOPASS_BLOCK_LEN); + memcpy(pacs->key, key, RFAL_PICOPASS_BLOCK_LEN); err = picopass_read_card(AA1); if(err != ERR_NONE) { FURI_LOG_E(TAG, "picopass_read_card error %d", err); @@ -587,15 +618,22 @@ void picopass_worker_elite_dict_attack(PicopassWorker* picopass_worker) { int32_t picopass_worker_task(void* context) { PicopassWorker* picopass_worker = context; - picopass_worker_enable_field(); if(picopass_worker->state == PicopassWorkerStateDetect) { + picopass_worker_enable_field(); picopass_worker_detect(picopass_worker); } else if(picopass_worker->state == PicopassWorkerStateWrite) { + picopass_worker_enable_field(); picopass_worker_write(picopass_worker); } else if(picopass_worker->state == PicopassWorkerStateWriteKey) { + picopass_worker_enable_field(); picopass_worker_write_key(picopass_worker); } else if(picopass_worker->state == PicopassWorkerStateEliteDictAttack) { + picopass_worker_enable_field(); picopass_worker_elite_dict_attack(picopass_worker); + } else if(picopass_worker->state == PicopassWorkerStateEmulate) { + picopass_worker_emulate(picopass_worker, false); + } else if(picopass_worker->state == PicopassWorkerStateLoclass) { + picopass_worker_emulate(picopass_worker, true); } else if(picopass_worker->state == PicopassWorkerStateStop) { FURI_LOG_D(TAG, "Worker state stop"); // no-op @@ -717,9 +755,9 @@ void picopass_worker_write_key(PicopassWorker* picopass_worker) { uint8_t* csn = AA1[PICOPASS_CSN_BLOCK_INDEX].data; uint8_t* configBlock = AA1[PICOPASS_CONFIG_BLOCK_INDEX].data; uint8_t fuses = configBlock[7]; - uint8_t* oldKey = AA1[PICOPASS_KD_BLOCK_INDEX].data; + uint8_t* oldKey = AA1[PICOPASS_SECURE_KD_BLOCK_INDEX].data; - uint8_t newKey[PICOPASS_BLOCK_LEN] = {0}; + uint8_t newKey[RFAL_PICOPASS_BLOCK_LEN] = {0}; loclass_iclass_calc_div_key(csn, pacs->key, newKey, false); if((fuses & 0x80) == 0x80) { @@ -727,14 +765,14 @@ void picopass_worker_write_key(PicopassWorker* picopass_worker) { } else { FURI_LOG_D(TAG, "XOR write for application mode key change"); // XOR when in application mode - for(size_t i = 0; i < PICOPASS_BLOCK_LEN; i++) { + for(size_t i = 0; i < RFAL_PICOPASS_BLOCK_LEN; i++) { newKey[i] ^= oldKey[i]; } } while(picopass_worker->state == PicopassWorkerStateWriteKey) { if(picopass_detect_card(1000) == ERR_NONE) { - err = picopass_write_block(AA1, PICOPASS_KD_BLOCK_INDEX, newKey); + err = picopass_write_block(AA1, PICOPASS_SECURE_KD_BLOCK_INDEX, newKey); if(err != ERR_NONE) { FURI_LOG_E(TAG, "picopass_write_block error %d", err); nextState = PicopassWorkerEventFail; @@ -749,3 +787,484 @@ void picopass_worker_write_key(PicopassWorker* picopass_worker) { furi_delay_ms(100); } } + +// from proxmark3 armsrc/iclass.c rotateCSN +static void picopass_anticoll_csn(uint8_t* rotated_csn, const uint8_t* original_csn) { + for(uint8_t i = 0; i < 8; i++) { + rotated_csn[i] = (original_csn[i] >> 3) | (original_csn[(i + 1) % 8] << 5); + } +} + +static void picopass_append_crc(uint8_t* buf, uint16_t size) { + uint16_t crc = rfalPicoPassCalculateCcitt(0xE012, buf, size); + + buf[size] = crc & 0xFF; + buf[size + 1] = crc >> 8; +} + +static inline void picopass_emu_read_blocks( + NfcVData* nfcv_data, + uint8_t* buf, + uint8_t block_num, + uint8_t block_count) { + memcpy( + buf, + nfcv_data->data + (block_num * RFAL_PICOPASS_BLOCK_LEN), + block_count * RFAL_PICOPASS_BLOCK_LEN); +} + +static inline void picopass_emu_write_blocks( + NfcVData* nfcv_data, + const uint8_t* buf, + uint8_t block_num, + uint8_t block_count) { + memcpy( + nfcv_data->data + (block_num * RFAL_PICOPASS_BLOCK_LEN), + buf, + block_count * RFAL_PICOPASS_BLOCK_LEN); +} + +static void picopass_init_cipher_state(NfcVData* nfcv_data, PicopassEmulatorCtx* ctx) { + uint8_t cc[RFAL_PICOPASS_BLOCK_LEN]; + uint8_t key[RFAL_PICOPASS_BLOCK_LEN]; + + picopass_emu_read_blocks(nfcv_data, cc, PICOPASS_SECURE_EPURSE_BLOCK_INDEX, 1); + picopass_emu_read_blocks(nfcv_data, key, ctx->key_block_num, 1); + + ctx->cipher_state = loclass_opt_doTagMAC_1(cc, key); +} + +static void + loclass_update_csn(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data, PicopassEmulatorCtx* ctx) { + // collect two nonces in a row for each CSN + uint8_t csn_num = (ctx->key_block_num / 2) % LOCLASS_NUM_CSNS; + memcpy(nfc_data->uid, loclass_csns[csn_num], RFAL_PICOPASS_BLOCK_LEN); + picopass_emu_write_blocks(nfcv_data, loclass_csns[csn_num], PICOPASS_CSN_BLOCK_INDEX, 1); +} + +static void picopass_emu_handle_packet( + FuriHalNfcTxRxContext* tx_rx, + FuriHalNfcDevData* nfc_data, + void* nfcv_data_in) { + NfcVData* nfcv_data = (NfcVData*)nfcv_data_in; + PicopassEmulatorCtx* ctx = nfcv_data->emu_protocol_ctx; + uint8_t response[34]; + uint8_t response_length = 0; + uint8_t key_block_num = PICOPASS_SECURE_KD_BLOCK_INDEX; + + const uint8_t block_ff[8] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + + if(nfcv_data->frame_length < 1) { + return; + } + + switch(nfcv_data->frame[0]) { + case RFAL_PICOPASS_CMD_ACTALL: // No args + if(nfcv_data->frame_length != 1) { + return; + } + + if(ctx->state != PicopassEmulatorStateHalt) { + ctx->state = PicopassEmulatorStateActive; + } + + // Send SOF only + break; + case RFAL_PICOPASS_CMD_ACT: // No args + if(nfcv_data->frame_length != 1 || ctx->state != PicopassEmulatorStateActive) { + return; + } + + // Send SOF only + break; + case RFAL_PICOPASS_CMD_HALT: // No args + if(nfcv_data->frame_length != 1 || ctx->state != PicopassEmulatorStateSelected) { + return; + } + + // Technically we should go to StateHalt, but since we can't detect the field dropping we drop to idle instead + ctx->state = PicopassEmulatorStateIdle; + + // Send SOF only + break; + case RFAL_PICOPASS_CMD_READ_OR_IDENTIFY: + if(nfcv_data->frame_length == 1 && + ctx->state == PicopassEmulatorStateActive) { // PICOPASS_CMD_IDENTIFY + // ASNB(8) CRC16(2) + picopass_anticoll_csn(response, nfc_data->uid); + picopass_append_crc(response, RFAL_PICOPASS_BLOCK_LEN); + response_length = RFAL_PICOPASS_BLOCK_LEN + 2; + break; + } else if( + nfcv_data->frame_length == 4 && + ctx->state == PicopassEmulatorStateSelected) { // PICOPASS_CMD_READ ADDRESS(1) CRC16(2) + if(nfcv_data->frame[1] >= PICOPASS_MAX_APP_LIMIT) { + return; + } + + // TODO: Check CRC? + // TODO: Check auth? + + // DATA(8) CRC16(2) + if(nfcv_data->frame[1] == PICOPASS_SECURE_KD_BLOCK_INDEX || + nfcv_data->frame[1] == PICOPASS_SECURE_KC_BLOCK_INDEX) { + // Reading Kd or Kc blocks always returns FF's + memcpy(response, block_ff, RFAL_PICOPASS_BLOCK_LEN); + } else { + picopass_emu_read_blocks(nfcv_data, response, nfcv_data->frame[1], 1); + } + picopass_append_crc(response, RFAL_PICOPASS_BLOCK_LEN); + response_length = RFAL_PICOPASS_BLOCK_LEN + 2; + break; + } + + return; + case RFAL_PICOPASS_CMD_READ4: // ADDRESS(1) CRC16(2) + if(nfcv_data->frame_length != 4 || ctx->state != PicopassEmulatorStateSelected || + nfcv_data->frame[1] + 4 >= PICOPASS_MAX_APP_LIMIT) { + return; + } + + // TODO: Check CRC? + // TODO: Check auth? + + uint8_t blockNum = nfcv_data->frame[1]; + + // DATA(32) CRC16(2) + picopass_emu_read_blocks(nfcv_data, response, blockNum, 4); + if(blockNum == 4) { + // Kc is block 4, so just redact first block of response + memcpy(response, block_ff, RFAL_PICOPASS_BLOCK_LEN); + } else if(blockNum < 4) { + // Kd is block 3 + uint8_t* kdOffset = response + ((3 - blockNum) * RFAL_PICOPASS_BLOCK_LEN); + memcpy(kdOffset, block_ff, RFAL_PICOPASS_BLOCK_LEN); + if(blockNum != 0) { + // Redact Kc + memcpy(kdOffset + RFAL_PICOPASS_BLOCK_LEN, block_ff, RFAL_PICOPASS_BLOCK_LEN); + } + } + picopass_append_crc(response, RFAL_PICOPASS_BLOCK_LEN * 4); + response_length = (RFAL_PICOPASS_BLOCK_LEN * 4) + 2; + break; + case RFAL_PICOPASS_CMD_SELECT: // ASNB(8)|SERIALNB(8) + if(nfcv_data->frame_length != 9) { + return; + } + + uint8_t select_csn[RFAL_PICOPASS_BLOCK_LEN]; + if(ctx->state == PicopassEmulatorStateHalt || ctx->state == PicopassEmulatorStateIdle) { + memcpy(select_csn, nfc_data->uid, RFAL_PICOPASS_BLOCK_LEN); + } else { + picopass_anticoll_csn(select_csn, nfc_data->uid); + } + + if(memcmp(nfcv_data->frame + 1, select_csn, RFAL_PICOPASS_BLOCK_LEN)) { + if(ctx->state == PicopassEmulatorStateActive) { + ctx->state = PicopassEmulatorStateIdle; + } else if(ctx->state == PicopassEmulatorStateSelected) { + // Technically we should go to StateHalt, but since we can't detect the field dropping we drop to idle instead + ctx->state = PicopassEmulatorStateIdle; + } + + return; + } + + ctx->state = PicopassEmulatorStateSelected; + + // SERIALNB(8) CRC16(2) + memcpy(response, nfc_data->uid, RFAL_PICOPASS_BLOCK_LEN); + picopass_append_crc(response, RFAL_PICOPASS_BLOCK_LEN); + + response_length = RFAL_PICOPASS_BLOCK_LEN + 2; + break; + case RFAL_PICOPASS_CMD_READCHECK_KC: // ADDRESS(1) + key_block_num = PICOPASS_SECURE_KC_BLOCK_INDEX; + // fallthrough + case RFAL_PICOPASS_CMD_READCHECK_KD: // ADDRESS(1) + if(nfcv_data->frame_length != 2 || + nfcv_data->frame[1] != PICOPASS_SECURE_EPURSE_BLOCK_INDEX || + ctx->state != PicopassEmulatorStateSelected) { + return; + } + + if(ctx->key_block_num != key_block_num && !ctx->loclass_mode) { + ctx->key_block_num = key_block_num; + picopass_init_cipher_state(nfcv_data, ctx); + } + + // DATA(8) + picopass_emu_read_blocks(nfcv_data, response, nfcv_data->frame[1], 1); + response_length = RFAL_PICOPASS_BLOCK_LEN; + break; + case RFAL_PICOPASS_CMD_CHECK: // CHALLENGE(4) READERSIGNATURE(4) + if(nfcv_data->frame_length != 9 || ctx->state != PicopassEmulatorStateSelected) { + return; + } + + if(ctx->loclass_mode) { + // LOCLASS Reader attack mode + + // Copy EPURSE + uint8_t cc[RFAL_PICOPASS_BLOCK_LEN]; + picopass_emu_read_blocks(nfcv_data, cc, PICOPASS_SECURE_EPURSE_BLOCK_INDEX, 1); + + // Check if the nonce is from a standard key + uint8_t key[RFAL_PICOPASS_BLOCK_LEN]; + loclass_iclass_calc_div_key(nfc_data->uid, picopass_iclass_key, key, false); + ctx->cipher_state = loclass_opt_doTagMAC_1(cc, key); + + uint8_t rmac[4]; + loclass_opt_doBothMAC_2(ctx->cipher_state, nfcv_data->frame + 1, rmac, response, key); + + if(!memcmp(nfcv_data->frame + 5, rmac, 4)) { + // MAC from reader matches Standard Key, keyroll mode or non-elite keyed reader. + // Either way no point logging it. + + FURI_LOG_W(TAG, "loclass: standard key detected during collection"); + ctx->loclass_got_std_key = true; + + ctx->state = PicopassEmulatorStateIdle; + return; + } + + // Copy CHALLENGE (nr) and READERSIGNATURE (mac) from frame + uint8_t nr[4]; + memcpy(nr, nfcv_data->frame + 1, 4); + uint8_t mac[4]; + memcpy(mac, nfcv_data->frame + 5, 4); + + FURI_LOG_I(TAG, "loclass: got nr/mac pair"); + loclass_writer_write_params( + ctx->loclass_writer, ctx->key_block_num, nfc_data->uid, cc, nr, mac); + + // Rotate to the next CSN + ctx->key_block_num = (ctx->key_block_num + 1) % (LOCLASS_NUM_CSNS * 2); + loclass_update_csn(nfc_data, nfcv_data, ctx); + + ctx->state = PicopassEmulatorStateIdle; + + return; + } + + uint8_t key[RFAL_PICOPASS_BLOCK_LEN]; + picopass_emu_read_blocks(nfcv_data, key, ctx->key_block_num, 1); + + uint8_t rmac[4]; + loclass_opt_doBothMAC_2(ctx->cipher_state, nfcv_data->frame + 1, rmac, response, key); + + if(memcmp(nfcv_data->frame + 5, rmac, 4)) { + // Bad MAC from reader, do not send a response. + FURI_LOG_I(TAG, "Got bad MAC from reader"); + return; + } + + // CHIPRESPONSE(4) + response_length = 4; + break; + case RFAL_PICOPASS_CMD_UPDATE: // ADDRESS(1) DATA(8) SIGN(4)|CRC16(2) + if((nfcv_data->frame_length != 12 && nfcv_data->frame_length != 14) || + ctx->state != PicopassEmulatorStateSelected) { + return; + } + + if(nfcv_data->frame[1] >= PICOPASS_MAX_APP_LIMIT) { + return; + } + + uint8_t cfgBlock[RFAL_PICOPASS_BLOCK_LEN]; + picopass_emu_read_blocks(nfcv_data, cfgBlock, PICOPASS_CONFIG_BLOCK_INDEX, 1); + bool persMode = HAS_MASK(cfgBlock[7], PICOPASS_FUSE_PERS); + + if((nfcv_data->frame[1] == PICOPASS_CSN_BLOCK_INDEX) // CSN is always read only + || + (!persMode && + !HAS_MASK(cfgBlock[3], 0x80)) // Chip is in RO mode, no updated possible (even ePurse) + || (!persMode && + nfcv_data->frame[1] == + PICOPASS_SECURE_AIA_BLOCK_INDEX) // AIA can only be set in personalisation mode + || (!persMode && + (nfcv_data->frame[1] == PICOPASS_SECURE_KD_BLOCK_INDEX || + nfcv_data->frame[1] == PICOPASS_SECURE_KC_BLOCK_INDEX) && + (!HAS_MASK(cfgBlock[7], PICOPASS_FUSE_CRYPT10)))) { + return; // TODO: Is this the right response? + } + + if(nfcv_data->frame[1] >= 6 && nfcv_data->frame[1] <= 12) { + if(!HAS_MASK( + cfgBlock[3], + 1 << (nfcv_data->frame[1] - 6))) { // bit0 is block6, up to bit6 being block12 + // Block is marked as read-only, deny writing + return; // TODO: Is this the right response? + } + } + + // TODO: Check CRC/SIGN depending on if in secure mode + // Check correct key + // -> Kd only allows decrementing e-Purse + // -> per-app controlled by key access config + //bool keyAccess = HAS_MASK(cfgBlock[5], 0x01); + // -> must auth with that key to change it + + uint8_t blockOffset = nfcv_data->frame[1]; + uint8_t block[RFAL_PICOPASS_BLOCK_LEN]; + switch(nfcv_data->frame[1]) { + case PICOPASS_CONFIG_BLOCK_INDEX: + block[0] = cfgBlock[0]; // Applications Limit + block[1] = cfgBlock[1] & nfcv_data->frame[3]; // OTP + block[2] = cfgBlock[2] & nfcv_data->frame[4]; // OTP + block[3] = cfgBlock[3] & nfcv_data->frame[5]; // Block Write Lock + block[4] = cfgBlock[4]; // Chip Config + block[5] = cfgBlock[5]; // Memory Config + block[6] = nfcv_data->frame[8]; // EAS + block[7] = cfgBlock[7]; // Fuses + + // Some parts allow w (but not e) if in persMode + if(persMode) { + block[0] &= nfcv_data->frame[2]; // Applications Limit + block[4] &= nfcv_data->frame[6]; // Chip Config + block[5] &= nfcv_data->frame[7]; // Memory Config + block[7] &= nfcv_data->frame[9]; // Fuses + } else { + // Fuses allows setting Crypt1/0 from 1 to 0 only during application mode + block[7] &= nfcv_data->frame[9] | ~PICOPASS_FUSE_CRYPT10; + } + break; + case PICOPASS_SECURE_EPURSE_BLOCK_INDEX: + // ePurse updates swap first and second half of the block each update + memcpy(block + 4, nfcv_data->frame + 2, 4); + memcpy(block, nfcv_data->frame + 6, 4); + break; + case PICOPASS_SECURE_KD_BLOCK_INDEX: + // fallthrough + case PICOPASS_SECURE_KC_BLOCK_INDEX: + if(!persMode) { + picopass_emu_read_blocks(nfcv_data, block, blockOffset, 1); + for(uint8_t i = 0; i < sizeof(RFAL_PICOPASS_BLOCK_LEN); i++) + block[i] ^= nfcv_data->frame[i + 2]; + break; + } + // Use default case when in personalisation mode + // fallthrough + default: + memcpy(block, nfcv_data->frame + 2, RFAL_PICOPASS_BLOCK_LEN); + break; + } + + picopass_emu_write_blocks(nfcv_data, block, blockOffset, 1); + + if((nfcv_data->frame[1] == ctx->key_block_num || + nfcv_data->frame[1] == PICOPASS_SECURE_EPURSE_BLOCK_INDEX) && + !ctx->loclass_mode) + picopass_init_cipher_state(nfcv_data, ctx); + + // DATA(8) CRC16(2) + if(nfcv_data->frame[1] == PICOPASS_SECURE_KD_BLOCK_INDEX || + nfcv_data->frame[1] == PICOPASS_SECURE_KD_BLOCK_INDEX) { + // Key updates always return FF's + memcpy(response, block_ff, RFAL_PICOPASS_BLOCK_LEN); + } else { + memcpy(response, block, RFAL_PICOPASS_BLOCK_LEN); + } + picopass_append_crc(response, RFAL_PICOPASS_BLOCK_LEN); + response_length = RFAL_PICOPASS_BLOCK_LEN + 2; + break; + case RFAL_PICOPASS_CMD_PAGESEL: // PAGE(1) CRC16(2) + // Chips with a single page do not answer to this command + // BLOCK1(8) CRC16(2) + return; + case RFAL_PICOPASS_CMD_DETECT: + // TODO - not used by iClass though + return; + default: + return; + } + + NfcVSendFlags flags = NfcVSendFlagsSof | NfcVSendFlagsOneSubcarrier | NfcVSendFlagsHighRate; + if(response_length > 0) { + flags |= NfcVSendFlagsEof; + } + + nfcv_emu_send( + tx_rx, + nfcv_data, + response, + response_length, + flags, + nfcv_data->eof_timestamp + NFCV_FDT_FC(4000)); // 3650 is ~254uS 4000 is ~283uS +} + +void picopass_worker_emulate(PicopassWorker* picopass_worker, bool loclass_mode) { + FuriHalNfcTxRxContext tx_rx = {}; + PicopassEmulatorCtx emu_ctx = { + .state = PicopassEmulatorStateIdle, + .key_block_num = PICOPASS_SECURE_KD_BLOCK_INDEX, + .loclass_mode = loclass_mode, + .loclass_got_std_key = false, + .loclass_writer = NULL, + }; + FuriHalNfcDevData nfc_data = { + .uid_len = RFAL_PICOPASS_UID_LEN, + }; + NfcVData* nfcv_data = malloc(sizeof(NfcVData)); + nfcv_data->block_size = RFAL_PICOPASS_BLOCK_LEN; + nfcv_data->emu_protocol_ctx = &emu_ctx; + nfcv_data->emu_protocol_handler = &picopass_emu_handle_packet; + + PicopassDeviceData* dev_data = picopass_worker->dev_data; + PicopassBlock* blocks = dev_data->AA1; + + if(loclass_mode) { + // Setup blocks for loclass attack + emu_ctx.key_block_num = 0; + loclass_update_csn(&nfc_data, nfcv_data, &emu_ctx); + + uint8_t conf[8] = {0x12, 0xFF, 0xFF, 0xFF, 0x7F, 0x1F, 0xFF, 0x3C}; + picopass_emu_write_blocks(nfcv_data, conf, PICOPASS_CONFIG_BLOCK_INDEX, 1); + + uint8_t epurse[8] = {0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + picopass_emu_write_blocks(nfcv_data, epurse, PICOPASS_SECURE_EPURSE_BLOCK_INDEX, 1); + + uint8_t aia[8] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + picopass_emu_write_blocks(nfcv_data, aia, PICOPASS_SECURE_AIA_BLOCK_INDEX, 1); + + emu_ctx.loclass_writer = loclass_writer_alloc(); + loclass_writer_write_start_stop(emu_ctx.loclass_writer, true); + } else { + memcpy(nfc_data.uid, blocks[PICOPASS_CSN_BLOCK_INDEX].data, RFAL_PICOPASS_BLOCK_LEN); + memcpy(nfcv_data->data, blocks, sizeof(dev_data->AA1)); + picopass_init_cipher_state(nfcv_data, &emu_ctx); + } + + uint8_t last_loclass_csn_num = 0; + bool loclass_got_std_key = false; + + nfcv_emu_init(&nfc_data, nfcv_data); + while(picopass_worker->state == PicopassWorkerStateEmulate || + picopass_worker->state == PicopassWorkerStateLoclass) { + if(nfcv_emu_loop(&tx_rx, &nfc_data, nfcv_data, 500)) { + if(picopass_worker->callback) { + if((loclass_mode) && (last_loclass_csn_num != emu_ctx.key_block_num)) { + last_loclass_csn_num = emu_ctx.key_block_num; + picopass_worker->callback( + PicopassWorkerEventLoclassGotMac, picopass_worker->context); + } else if((loclass_mode) && !loclass_got_std_key && emu_ctx.loclass_got_std_key) { + loclass_got_std_key = true; + picopass_worker->callback( + PicopassWorkerEventLoclassGotStandardKey, picopass_worker->context); + } else { + picopass_worker->callback( + PicopassWorkerEventSuccess, picopass_worker->context); + } + } + } + } + + if(emu_ctx.loclass_writer) { + loclass_writer_write_start_stop(emu_ctx.loclass_writer, false); + loclass_writer_free(emu_ctx.loclass_writer); + } + + nfcv_emu_deinit(nfcv_data); + free(nfcv_data); +} diff --git a/applications/external/picopass/picopass_worker.h b/applications/external/picopass/picopass_worker.h index e9d37481b1..642e4c9620 100644 --- a/applications/external/picopass/picopass_worker.h +++ b/applications/external/picopass/picopass_worker.h @@ -15,6 +15,8 @@ typedef enum { PicopassWorkerStateWrite, PicopassWorkerStateWriteKey, PicopassWorkerStateEliteDictAttack, + PicopassWorkerStateEmulate, + PicopassWorkerStateLoclass, // Transition PicopassWorkerStateStop, } PicopassWorkerState; @@ -32,6 +34,8 @@ typedef enum { PicopassWorkerEventCardDetected, PicopassWorkerEventNewDictKeyBatch, PicopassWorkerEventNoDictFound, + PicopassWorkerEventLoclassGotMac, + PicopassWorkerEventLoclassGotStandardKey, } PicopassWorkerEvent; typedef void (*PicopassWorkerCallback)(PicopassWorkerEvent event, void* context); diff --git a/applications/external/picopass/picopass_worker_i.h b/applications/external/picopass/picopass_worker_i.h index f41cfce45d..5e51b1cc6c 100644 --- a/applications/external/picopass/picopass_worker_i.h +++ b/applications/external/picopass/picopass_worker_i.h @@ -1,6 +1,7 @@ #pragma once #include "picopass_worker.h" +#include "loclass_writer.h" #include "picopass_i.h" #include @@ -32,3 +33,4 @@ int32_t picopass_worker_task(void* context); void picopass_worker_detect(PicopassWorker* picopass_worker); void picopass_worker_write(PicopassWorker* picopass_worker); void picopass_worker_write_key(PicopassWorker* picopass_worker); +void picopass_worker_emulate(PicopassWorker* picopass_worker, bool loclass_mode); diff --git a/applications/external/picopass/rfal_picopass.c b/applications/external/picopass/rfal_picopass.c index e8ca644032..983a11eab2 100644 --- a/applications/external/picopass/rfal_picopass.c +++ b/applications/external/picopass/rfal_picopass.c @@ -29,8 +29,7 @@ static uint16_t rfalPicoPassUpdateCcitt(uint16_t crcSeed, uint8_t dataByte) { return crc; } -static uint16_t - rfalPicoPassCalculateCcitt(uint16_t preloadValue, const uint8_t* buf, uint16_t length) { +uint16_t rfalPicoPassCalculateCcitt(uint16_t preloadValue, const uint8_t* buf, uint16_t length) { uint16_t crc = preloadValue; uint16_t index; @@ -73,7 +72,7 @@ FuriHalNfcReturn rfalPicoPassPollerCheckPresence(void) { FuriHalNfcReturn rfalPicoPassPollerIdentify(rfalPicoPassIdentifyRes* idRes) { FuriHalNfcReturn ret; - uint8_t txBuf[1] = {RFAL_PICOPASS_CMD_IDENTIFY}; + uint8_t txBuf[1] = {RFAL_PICOPASS_CMD_READ_OR_IDENTIFY}; uint16_t recvLen = 0; uint32_t flags = RFAL_PICOPASS_TXRX_FLAGS; uint32_t fwt = furi_hal_nfc_ll_ms2fc(20); @@ -119,7 +118,7 @@ FuriHalNfcReturn rfalPicoPassPollerSelect(uint8_t* csn, rfalPicoPassSelectRes* s FuriHalNfcReturn rfalPicoPassPollerReadCheck(rfalPicoPassReadCheckRes* rcRes) { FuriHalNfcReturn ret; - uint8_t txBuf[2] = {RFAL_PICOPASS_CMD_READCHECK, 0x02}; + uint8_t txBuf[2] = {RFAL_PICOPASS_CMD_READCHECK_KD, 0x02}; uint16_t recvLen = 0; uint32_t flags = RFAL_PICOPASS_TXRX_FLAGS; uint32_t fwt = furi_hal_nfc_ll_ms2fc(20); @@ -171,7 +170,7 @@ FuriHalNfcReturn rfalPicoPassPollerCheck(uint8_t* mac, rfalPicoPassCheckRes* chk FuriHalNfcReturn rfalPicoPassPollerReadBlock(uint8_t blockNum, rfalPicoPassReadBlockRes* readRes) { FuriHalNfcReturn ret; - uint8_t txBuf[4] = {RFAL_PICOPASS_CMD_READ, 0, 0, 0}; + uint8_t txBuf[4] = {RFAL_PICOPASS_CMD_READ_OR_IDENTIFY, 0, 0, 0}; txBuf[1] = blockNum; uint16_t crc = rfalPicoPassCalculateCcitt(0xE012, txBuf + 1, 1); memcpy(txBuf + 2, &crc, sizeof(uint16_t)); @@ -194,8 +193,8 @@ FuriHalNfcReturn rfalPicoPassPollerReadBlock(uint8_t blockNum, rfalPicoPassReadB FuriHalNfcReturn rfalPicoPassPollerWriteBlock(uint8_t blockNum, uint8_t data[8], uint8_t mac[4]) { FuriHalNfcReturn ret; - uint8_t txBuf[14] = {RFAL_PICOPASS_CMD_WRITE, blockNum, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - memcpy(txBuf + 2, data, RFAL_PICOPASS_MAX_BLOCK_LEN); + uint8_t txBuf[14] = {RFAL_PICOPASS_CMD_UPDATE, blockNum, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + memcpy(txBuf + 2, data, RFAL_PICOPASS_BLOCK_LEN); memcpy(txBuf + 10, mac, 4); uint16_t recvLen = 0; diff --git a/applications/external/picopass/rfal_picopass.h b/applications/external/picopass/rfal_picopass.h index 6926b2a79d..6265884d63 100644 --- a/applications/external/picopass/rfal_picopass.h +++ b/applications/external/picopass/rfal_picopass.h @@ -3,16 +3,41 @@ #include #define RFAL_PICOPASS_UID_LEN 8 -#define RFAL_PICOPASS_MAX_BLOCK_LEN 8 +#define RFAL_PICOPASS_BLOCK_LEN 8 enum { + // PicoPass command bytes: + // Low nibble used for command + // High nibble used for options and checksum (MSB) + // The only option we care about in 15693 mode is the key + // which is only used by READCHECK, so for simplicity we + // don't bother breaking down the command and flags into parts + + // READ: ADDRESS(1) CRC16(2) -> DATA(8) CRC16(2) + // IDENTIFY: No args -> ASNB(8) CRC16(2) + RFAL_PICOPASS_CMD_READ_OR_IDENTIFY = 0x0C, + // ADDRESS(1) CRC16(2) -> DATA(32) CRC16(2) + RFAL_PICOPASS_CMD_READ4 = 0x06, + // ADDRESS(1) DATA(8) SIGN(4)|CRC16(2) -> DATA(8) CRC16(2) + RFAL_PICOPASS_CMD_UPDATE = 0x87, + // ADDRESS(1) -> DATA(8) + RFAL_PICOPASS_CMD_READCHECK_KD = 0x88, + // ADDRESS(1) -> DATA(8) + RFAL_PICOPASS_CMD_READCHECK_KC = 0x18, + // CHALLENGE(4) READERSIGNATURE(4) -> CHIPRESPONSE(4) + RFAL_PICOPASS_CMD_CHECK = 0x05, + // No args -> SOF RFAL_PICOPASS_CMD_ACTALL = 0x0A, - RFAL_PICOPASS_CMD_IDENTIFY = 0x0C, + // No args -> SOF + RFAL_PICOPASS_CMD_ACT = 0x8E, + // ASNB(8)|SERIALNB(8) -> SERIALNB(8) CRC16(2) RFAL_PICOPASS_CMD_SELECT = 0x81, - RFAL_PICOPASS_CMD_READCHECK = 0x88, - RFAL_PICOPASS_CMD_CHECK = 0x05, - RFAL_PICOPASS_CMD_READ = 0x0C, - RFAL_PICOPASS_CMD_WRITE = 0x87, + // No args -> SERIALNB(8) CRC16(2) + RFAL_PICOPASS_CMD_DETECT = 0x0F, + // No args -> SOF + RFAL_PICOPASS_CMD_HALT = 0x00, + // PAGE(1) CRC16(2) -> BLOCK1(8) CRC16(2) + RFAL_PICOPASS_CMD_PAGESEL = 0x84, }; typedef struct { @@ -34,10 +59,12 @@ typedef struct { } rfalPicoPassCheckRes; typedef struct { - uint8_t data[RFAL_PICOPASS_MAX_BLOCK_LEN]; + uint8_t data[RFAL_PICOPASS_BLOCK_LEN]; uint8_t crc[2]; } rfalPicoPassReadBlockRes; +uint16_t rfalPicoPassCalculateCcitt(uint16_t preloadValue, const uint8_t* buf, uint16_t length); + FuriHalNfcReturn rfalPicoPassPollerInitialize(void); FuriHalNfcReturn rfalPicoPassPollerCheckPresence(void); FuriHalNfcReturn rfalPicoPassPollerIdentify(rfalPicoPassIdentifyRes* idRes); diff --git a/applications/external/picopass/scenes/picopass_scene_config.h b/applications/external/picopass/scenes/picopass_scene_config.h index 8ea9704985..6156ed689a 100644 --- a/applications/external/picopass/scenes/picopass_scene_config.h +++ b/applications/external/picopass/scenes/picopass_scene_config.h @@ -15,3 +15,5 @@ ADD_SCENE(picopass, read_factory_success, ReadFactorySuccess) ADD_SCENE(picopass, write_key, WriteKey) ADD_SCENE(picopass, key_menu, KeyMenu) ADD_SCENE(picopass, elite_dict_attack, EliteDictAttack) +ADD_SCENE(picopass, emulate, Emulate) +ADD_SCENE(picopass, loclass, Loclass) diff --git a/applications/external/picopass/scenes/picopass_scene_device_info.c b/applications/external/picopass/scenes/picopass_scene_device_info.c index 41caeabf5a..41d0bad817 100644 --- a/applications/external/picopass/scenes/picopass_scene_device_info.c +++ b/applications/external/picopass/scenes/picopass_scene_device_info.c @@ -19,16 +19,16 @@ void picopass_scene_device_info_on_enter(void* context) { FuriString* wiegand_str = furi_string_alloc(); FuriString* sio_str = furi_string_alloc(); - DOLPHIN_DEED(DolphinDeedNfcReadSuccess); + dolphin_deed(DolphinDeedNfcReadSuccess); // Setup view PicopassBlock* AA1 = picopass->dev->dev_data.AA1; PicopassPacs* pacs = &picopass->dev->dev_data.pacs; Widget* widget = picopass->widget; - uint8_t csn[PICOPASS_BLOCK_LEN] = {0}; - memcpy(csn, AA1[PICOPASS_CSN_BLOCK_INDEX].data, PICOPASS_BLOCK_LEN); - for(uint8_t i = 0; i < PICOPASS_BLOCK_LEN; i++) { + uint8_t csn[RFAL_PICOPASS_BLOCK_LEN] = {0}; + memcpy(csn, AA1[PICOPASS_CSN_BLOCK_INDEX].data, RFAL_PICOPASS_BLOCK_LEN); + for(uint8_t i = 0; i < RFAL_PICOPASS_BLOCK_LEN; i++) { furi_string_cat_printf(csn_str, "%02X ", csn[i]); } @@ -42,7 +42,7 @@ void picopass_scene_device_info_on_enter(void* context) { bytesLength++; } furi_string_set(credential_str, ""); - for(uint8_t i = PICOPASS_BLOCK_LEN - bytesLength; i < PICOPASS_BLOCK_LEN; i++) { + for(uint8_t i = RFAL_PICOPASS_BLOCK_LEN - bytesLength; i < RFAL_PICOPASS_BLOCK_LEN; i++) { furi_string_cat_printf(credential_str, " %02X", pacs->credential[i]); } diff --git a/applications/external/picopass/scenes/picopass_scene_emulate.c b/applications/external/picopass/scenes/picopass_scene_emulate.c new file mode 100644 index 0000000000..4e0ed073b0 --- /dev/null +++ b/applications/external/picopass/scenes/picopass_scene_emulate.c @@ -0,0 +1,58 @@ +#include "../picopass_i.h" +#include + +void picopass_emulate_worker_callback(PicopassWorkerEvent event, void* context) { + furi_assert(context); + Picopass* picopass = context; + view_dispatcher_send_custom_event(picopass->view_dispatcher, event); +} + +void picopass_scene_emulate_on_enter(void* context) { + Picopass* picopass = context; + dolphin_deed(DolphinDeedNfcEmulate); + + Widget* widget = picopass->widget; + widget_reset(widget); + widget_add_icon_element(widget, 0, 3, &I_RFIDDolphinSend_97x61); + widget_add_string_element(widget, 89, 32, AlignCenter, AlignTop, FontPrimary, "Emulating"); + widget_add_string_element(widget, 89, 42, AlignCenter, AlignTop, FontPrimary, "PicoPass"); + + // Setup view + view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewWidget); + + // Start worker + picopass_worker_start( + picopass->worker, + PicopassWorkerStateEmulate, + &picopass->dev->dev_data, + picopass_emulate_worker_callback, + picopass); + + picopass_blink_emulate_start(picopass); +} + +bool picopass_scene_emulate_on_event(void* context, SceneManagerEvent event) { + Picopass* picopass = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == PicopassCustomEventWorkerExit) { + consumed = true; + } + } else if(event.type == SceneManagerEventTypeBack) { + consumed = scene_manager_previous_scene(picopass->scene_manager); + } + return consumed; +} + +void picopass_scene_emulate_on_exit(void* context) { + Picopass* picopass = context; + + picopass_blink_stop(picopass); + + // Stop worker + picopass_worker_stop(picopass->worker); + + // Clear view + widget_reset(picopass->widget); +} diff --git a/applications/external/picopass/scenes/picopass_scene_key_menu.c b/applications/external/picopass/scenes/picopass_scene_key_menu.c index 8aac6cb249..c9456468bb 100644 --- a/applications/external/picopass/scenes/picopass_scene_key_menu.c +++ b/applications/external/picopass/scenes/picopass_scene_key_menu.c @@ -59,25 +59,25 @@ bool picopass_scene_key_menu_on_event(void* context, SceneManagerEvent event) { if(event.event == SubmenuIndexWriteStandard) { scene_manager_set_scene_state( picopass->scene_manager, PicopassSceneKeyMenu, SubmenuIndexWriteStandard); - memcpy(picopass->dev->dev_data.pacs.key, picopass_iclass_key, PICOPASS_BLOCK_LEN); + memcpy(picopass->dev->dev_data.pacs.key, picopass_iclass_key, RFAL_PICOPASS_BLOCK_LEN); scene_manager_next_scene(picopass->scene_manager, PicopassSceneWriteKey); consumed = true; } else if(event.event == SubmenuIndexWriteiCE) { scene_manager_set_scene_state( picopass->scene_manager, PicopassSceneKeyMenu, SubmenuIndexWriteiCE); - memcpy(picopass->dev->dev_data.pacs.key, picopass_xice_key, PICOPASS_BLOCK_LEN); + memcpy(picopass->dev->dev_data.pacs.key, picopass_xice_key, RFAL_PICOPASS_BLOCK_LEN); scene_manager_next_scene(picopass->scene_manager, PicopassSceneWriteKey); consumed = true; } else if(event.event == SubmenuIndexWriteiCL) { scene_manager_set_scene_state( picopass->scene_manager, PicopassSceneKeyMenu, SubmenuIndexWriteiCE); - memcpy(picopass->dev->dev_data.pacs.key, picopass_xicl_key, PICOPASS_BLOCK_LEN); + memcpy(picopass->dev->dev_data.pacs.key, picopass_xicl_key, RFAL_PICOPASS_BLOCK_LEN); scene_manager_next_scene(picopass->scene_manager, PicopassSceneWriteKey); consumed = true; } else if(event.event == SubmenuIndexWriteiCS) { scene_manager_set_scene_state( picopass->scene_manager, PicopassSceneKeyMenu, SubmenuIndexWriteiCE); - memcpy(picopass->dev->dev_data.pacs.key, picopass_xics_key, PICOPASS_BLOCK_LEN); + memcpy(picopass->dev->dev_data.pacs.key, picopass_xics_key, RFAL_PICOPASS_BLOCK_LEN); scene_manager_next_scene(picopass->scene_manager, PicopassSceneWriteKey); consumed = true; } diff --git a/applications/external/picopass/scenes/picopass_scene_loclass.c b/applications/external/picopass/scenes/picopass_scene_loclass.c new file mode 100644 index 0000000000..01e2455573 --- /dev/null +++ b/applications/external/picopass/scenes/picopass_scene_loclass.c @@ -0,0 +1,80 @@ +#include "../picopass_i.h" +#include + +void picopass_loclass_worker_callback(PicopassWorkerEvent event, void* context) { + furi_assert(context); + Picopass* picopass = context; + view_dispatcher_send_custom_event(picopass->view_dispatcher, event); +} + +void picopass_loclass_result_callback(void* context) { + furi_assert(context); + Picopass* picopass = context; + view_dispatcher_send_custom_event(picopass->view_dispatcher, PicopassCustomEventViewExit); +} + +void picopass_scene_loclass_on_enter(void* context) { + Picopass* picopass = context; + dolphin_deed(DolphinDeedNfcEmulate); + + scene_manager_set_scene_state(picopass->scene_manager, PicopassSceneLoclass, 0); + + loclass_set_callback(picopass->loclass, picopass_loclass_result_callback, picopass); + + // Start worker + picopass_worker_start( + picopass->worker, + PicopassWorkerStateLoclass, + &picopass->dev->dev_data, + picopass_loclass_worker_callback, + picopass); + + picopass_blink_emulate_start(picopass); + + loclass_set_header(picopass->loclass, "Loclass"); + + view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewLoclass); +} + +bool picopass_scene_loclass_on_event(void* context, SceneManagerEvent event) { + Picopass* picopass = context; + bool consumed = false; + + uint32_t loclass_macs_collected = + scene_manager_get_scene_state(picopass->scene_manager, PicopassSceneLoclass); + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == PicopassWorkerEventLoclassGotMac) { + loclass_macs_collected++; + scene_manager_set_scene_state( + picopass->scene_manager, PicopassSceneLoclass, loclass_macs_collected); + loclass_set_num_macs(picopass->loclass, loclass_macs_collected); + if(loclass_macs_collected >= LOCLASS_MACS_TO_COLLECT) { + scene_manager_previous_scene(picopass->scene_manager); + } + consumed = true; + } else if(event.event == PicopassWorkerEventLoclassGotStandardKey) { + loclass_set_header(picopass->loclass, "Loclass (Got Std Key)"); + consumed = true; + } else if(event.event == PicopassCustomEventViewExit) { + consumed = scene_manager_previous_scene(picopass->scene_manager); + } + } else if(event.type == SceneManagerEventTypeBack) { + consumed = scene_manager_previous_scene(picopass->scene_manager); + } + return consumed; +} + +void picopass_scene_loclass_on_exit(void* context) { + Picopass* picopass = context; + + picopass_blink_stop(picopass); + + // Stop worker + picopass_worker_stop(picopass->worker); + + loclass_reset(picopass->loclass); + + // Clear view + widget_reset(picopass->widget); +} diff --git a/applications/external/picopass/scenes/picopass_scene_read_card.c b/applications/external/picopass/scenes/picopass_scene_read_card.c index 96ec7c668b..fabce52b66 100644 --- a/applications/external/picopass/scenes/picopass_scene_read_card.c +++ b/applications/external/picopass/scenes/picopass_scene_read_card.c @@ -10,7 +10,7 @@ void picopass_read_card_worker_callback(PicopassWorkerEvent event, void* context void picopass_scene_read_card_on_enter(void* context) { Picopass* picopass = context; - DOLPHIN_DEED(DolphinDeedNfcRead); + dolphin_deed(DolphinDeedNfcRead); // Setup view Popup* popup = picopass->popup; @@ -38,7 +38,7 @@ bool picopass_scene_read_card_on_event(void* context, SceneManagerEvent event) { if(memcmp( picopass->dev->dev_data.pacs.key, picopass_factory_debit_key, - PICOPASS_BLOCK_LEN) == 0) { + RFAL_PICOPASS_BLOCK_LEN) == 0) { scene_manager_next_scene(picopass->scene_manager, PicopassSceneReadFactorySuccess); } else { scene_manager_next_scene(picopass->scene_manager, PicopassSceneReadCardSuccess); diff --git a/applications/external/picopass/scenes/picopass_scene_read_card_success.c b/applications/external/picopass/scenes/picopass_scene_read_card_success.c index cc18ac066a..2f80cd7b9a 100644 --- a/applications/external/picopass/scenes/picopass_scene_read_card_success.c +++ b/applications/external/picopass/scenes/picopass_scene_read_card_success.c @@ -21,7 +21,7 @@ void picopass_scene_read_card_success_on_enter(void* context) { FuriString* wiegand_str = furi_string_alloc(); FuriString* sio_str = furi_string_alloc(); - DOLPHIN_DEED(DolphinDeedNfcReadSuccess); + dolphin_deed(DolphinDeedNfcReadSuccess); // Send notification notification_message(picopass->notifications, &sequence_success); @@ -31,15 +31,15 @@ void picopass_scene_read_card_success_on_enter(void* context) { PicopassPacs* pacs = &picopass->dev->dev_data.pacs; Widget* widget = picopass->widget; - uint8_t csn[PICOPASS_BLOCK_LEN] = {0}; - memcpy(csn, AA1[PICOPASS_CSN_BLOCK_INDEX].data, PICOPASS_BLOCK_LEN); - for(uint8_t i = 0; i < PICOPASS_BLOCK_LEN; i++) { + uint8_t csn[RFAL_PICOPASS_BLOCK_LEN] = {0}; + memcpy(csn, AA1[PICOPASS_CSN_BLOCK_INDEX].data, RFAL_PICOPASS_BLOCK_LEN); + for(uint8_t i = 0; i < RFAL_PICOPASS_BLOCK_LEN; i++) { furi_string_cat_printf(csn_str, "%02X", csn[i]); } - bool no_key = picopass_is_memset(pacs->key, 0x00, PICOPASS_BLOCK_LEN); - bool empty = - picopass_is_memset(AA1[PICOPASS_PACS_CFG_BLOCK_INDEX].data, 0xFF, PICOPASS_BLOCK_LEN); + bool no_key = picopass_is_memset(pacs->key, 0x00, RFAL_PICOPASS_BLOCK_LEN); + bool empty = picopass_is_memset( + AA1[PICOPASS_ICLASS_PACS_CFG_BLOCK_INDEX].data, 0xFF, RFAL_PICOPASS_BLOCK_LEN); if(no_key) { furi_string_cat_printf(wiegand_str, "Read Failed"); @@ -78,7 +78,7 @@ void picopass_scene_read_card_success_on_enter(void* context) { } else { size_t bytesLength = 1 + pacs->record.bitLength / 8; furi_string_set(credential_str, ""); - for(uint8_t i = PICOPASS_BLOCK_LEN - bytesLength; i < PICOPASS_BLOCK_LEN; i++) { + for(uint8_t i = RFAL_PICOPASS_BLOCK_LEN - bytesLength; i < RFAL_PICOPASS_BLOCK_LEN; i++) { furi_string_cat_printf(credential_str, " %02X", pacs->credential[i]); } @@ -99,9 +99,9 @@ void picopass_scene_read_card_success_on_enter(void* context) { } furi_string_cat_printf(sio_str, "Key: "); - uint8_t key[PICOPASS_BLOCK_LEN]; - memcpy(key, &pacs->key, PICOPASS_BLOCK_LEN); - for(uint8_t i = 0; i < PICOPASS_BLOCK_LEN; i++) { + uint8_t key[RFAL_PICOPASS_BLOCK_LEN]; + memcpy(key, &pacs->key, RFAL_PICOPASS_BLOCK_LEN); + for(uint8_t i = 0; i < RFAL_PICOPASS_BLOCK_LEN; i++) { furi_string_cat_printf(sio_str, "%02X", key[i]); } } diff --git a/applications/external/picopass/scenes/picopass_scene_read_factory_success.c b/applications/external/picopass/scenes/picopass_scene_read_factory_success.c index bc07bb9530..2ee6b253a6 100644 --- a/applications/external/picopass/scenes/picopass_scene_read_factory_success.c +++ b/applications/external/picopass/scenes/picopass_scene_read_factory_success.c @@ -19,7 +19,7 @@ void picopass_scene_read_factory_success_on_enter(void* context) { FuriString* title = furi_string_alloc_set("Factory Default"); FuriString* subtitle = furi_string_alloc_set(""); - DOLPHIN_DEED(DolphinDeedNfcReadSuccess); + dolphin_deed(DolphinDeedNfcReadSuccess); // Send notification notification_message(picopass->notifications, &sequence_success); @@ -64,7 +64,7 @@ bool picopass_scene_read_factory_success_on_event(void* context, SceneManagerEve if(event.event == GuiButtonTypeLeft) { consumed = scene_manager_previous_scene(picopass->scene_manager); } else if(event.event == GuiButtonTypeCenter) { - memcpy(picopass->dev->dev_data.pacs.key, picopass_iclass_key, PICOPASS_BLOCK_LEN); + memcpy(picopass->dev->dev_data.pacs.key, picopass_iclass_key, RFAL_PICOPASS_BLOCK_LEN); scene_manager_next_scene(picopass->scene_manager, PicopassSceneWriteKey); consumed = true; } diff --git a/applications/external/picopass/scenes/picopass_scene_save_success.c b/applications/external/picopass/scenes/picopass_scene_save_success.c index e92d91fb44..3b0a1cadd2 100644 --- a/applications/external/picopass/scenes/picopass_scene_save_success.c +++ b/applications/external/picopass/scenes/picopass_scene_save_success.c @@ -8,7 +8,7 @@ void picopass_scene_save_success_popup_callback(void* context) { void picopass_scene_save_success_on_enter(void* context) { Picopass* picopass = context; - DOLPHIN_DEED(DolphinDeedNfcSave); + dolphin_deed(DolphinDeedNfcSave); // Setup view Popup* popup = picopass->popup; diff --git a/applications/external/picopass/scenes/picopass_scene_saved_menu.c b/applications/external/picopass/scenes/picopass_scene_saved_menu.c index 90a27ee816..401f43f9b2 100644 --- a/applications/external/picopass/scenes/picopass_scene_saved_menu.c +++ b/applications/external/picopass/scenes/picopass_scene_saved_menu.c @@ -4,6 +4,7 @@ enum SubmenuIndex { SubmenuIndexDelete, SubmenuIndexInfo, SubmenuIndexWrite, + SubmenuIndexEmulate, }; void picopass_scene_saved_menu_submenu_callback(void* context, uint32_t index) { @@ -26,6 +27,12 @@ void picopass_scene_saved_menu_on_enter(void* context) { submenu, "Info", SubmenuIndexInfo, picopass_scene_saved_menu_submenu_callback, picopass); submenu_add_item( submenu, "Write", SubmenuIndexWrite, picopass_scene_saved_menu_submenu_callback, picopass); + submenu_add_item( + submenu, + "Emulate", + SubmenuIndexEmulate, + picopass_scene_saved_menu_submenu_callback, + picopass); submenu_set_selected_item( picopass->submenu, @@ -51,6 +58,9 @@ bool picopass_scene_saved_menu_on_event(void* context, SceneManagerEvent event) } else if(event.event == SubmenuIndexWrite) { scene_manager_next_scene(picopass->scene_manager, PicopassSceneWriteCard); consumed = true; + } else if(event.event == SubmenuIndexEmulate) { + scene_manager_next_scene(picopass->scene_manager, PicopassSceneEmulate); + consumed = true; } } diff --git a/applications/external/picopass/scenes/picopass_scene_start.c b/applications/external/picopass/scenes/picopass_scene_start.c index 8f7b627aaf..cfd758ed5c 100644 --- a/applications/external/picopass/scenes/picopass_scene_start.c +++ b/applications/external/picopass/scenes/picopass_scene_start.c @@ -3,6 +3,7 @@ enum SubmenuIndex { SubmenuIndexRead, SubmenuIndexEliteDictAttack, SubmenuIndexSaved, + SubmenuIndexLoclass, }; void picopass_scene_start_submenu_callback(void* context, uint32_t index) { @@ -24,6 +25,9 @@ void picopass_scene_start_on_enter(void* context) { submenu_add_item( submenu, "Saved", SubmenuIndexSaved, picopass_scene_start_submenu_callback, picopass); + submenu_add_item( + submenu, "Loclass", SubmenuIndexLoclass, picopass_scene_start_submenu_callback, picopass); + submenu_set_selected_item( submenu, scene_manager_get_scene_state(picopass->scene_manager, PicopassSceneStart)); picopass_device_clear(picopass->dev); @@ -52,6 +56,11 @@ bool picopass_scene_start_on_event(void* context, SceneManagerEvent event) { picopass->scene_manager, PicopassSceneStart, SubmenuIndexEliteDictAttack); scene_manager_next_scene(picopass->scene_manager, PicopassSceneEliteDictAttack); consumed = true; + } else if(event.event == SubmenuIndexLoclass) { + scene_manager_set_scene_state( + picopass->scene_manager, PicopassSceneLoclass, PicopassSceneLoclass); + scene_manager_next_scene(picopass->scene_manager, PicopassSceneLoclass); + consumed = true; } } diff --git a/applications/external/picopass/scenes/picopass_scene_write_card.c b/applications/external/picopass/scenes/picopass_scene_write_card.c index a905dca955..ce396fc10e 100644 --- a/applications/external/picopass/scenes/picopass_scene_write_card.c +++ b/applications/external/picopass/scenes/picopass_scene_write_card.c @@ -9,7 +9,7 @@ void picopass_write_card_worker_callback(PicopassWorkerEvent event, void* contex void picopass_scene_write_card_on_enter(void* context) { Picopass* picopass = context; - DOLPHIN_DEED(DolphinDeedNfcSave); + dolphin_deed(DolphinDeedNfcSave); // Setup view Popup* popup = picopass->popup; diff --git a/applications/external/picopass/scenes/picopass_scene_write_card_success.c b/applications/external/picopass/scenes/picopass_scene_write_card_success.c index 4bbca816aa..cd760272fe 100644 --- a/applications/external/picopass/scenes/picopass_scene_write_card_success.c +++ b/applications/external/picopass/scenes/picopass_scene_write_card_success.c @@ -18,7 +18,7 @@ void picopass_scene_write_card_success_on_enter(void* context) { Widget* widget = picopass->widget; FuriString* str = furi_string_alloc_set("Write Success!"); - DOLPHIN_DEED(DolphinDeedNfcReadSuccess); + dolphin_deed(DolphinDeedNfcReadSuccess); // Send notification notification_message(picopass->notifications, &sequence_success); diff --git a/applications/external/picopass/scenes/picopass_scene_write_key.c b/applications/external/picopass/scenes/picopass_scene_write_key.c index 0f417e1c3f..806a2b5a85 100644 --- a/applications/external/picopass/scenes/picopass_scene_write_key.c +++ b/applications/external/picopass/scenes/picopass_scene_write_key.c @@ -9,7 +9,7 @@ void picopass_write_key_worker_callback(PicopassWorkerEvent event, void* context void picopass_scene_write_key_on_enter(void* context) { Picopass* picopass = context; - DOLPHIN_DEED(DolphinDeedNfcSave); + dolphin_deed(DolphinDeedNfcSave); // Setup view Popup* popup = picopass->popup; diff --git a/applications/external/picopass/views/loclass.c b/applications/external/picopass/views/loclass.c new file mode 100644 index 0000000000..4158019a83 --- /dev/null +++ b/applications/external/picopass/views/loclass.c @@ -0,0 +1,106 @@ +#include "loclass.h" +#include "../picopass_worker_i.h" + +#include + +struct Loclass { + View* view; + LoclassCallback callback; + void* context; +}; + +typedef struct { + FuriString* header; + uint8_t num_macs; +} LoclassViewModel; + +static void loclass_draw_callback(Canvas* canvas, void* model) { + LoclassViewModel* m = model; + + char draw_str[32] = {}; + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned(canvas, 64, 0, AlignCenter, AlignTop, furi_string_get_cstr(m->header)); + + float progress = m->num_macs == 0 ? 0 : + (float)(m->num_macs) / (float)(LOCLASS_MACS_TO_COLLECT); + + if(progress > 1.0) { + progress = 1.0; + } + + snprintf(draw_str, sizeof(draw_str), "%d/%d", m->num_macs, LOCLASS_MACS_TO_COLLECT); + + elements_progress_bar_with_text(canvas, 0, 20, 128, progress, draw_str); + + elements_button_center(canvas, "Skip"); +} + +static bool loclass_input_callback(InputEvent* event, void* context) { + Loclass* loclass = context; + bool consumed = false; + if(event->type == InputTypeShort && event->key == InputKeyOk) { + if(loclass->callback) { + loclass->callback(loclass->context); + } + consumed = true; + } + return consumed; +} + +Loclass* loclass_alloc() { + Loclass* loclass = malloc(sizeof(Loclass)); + loclass->view = view_alloc(); + view_allocate_model(loclass->view, ViewModelTypeLocking, sizeof(LoclassViewModel)); + view_set_draw_callback(loclass->view, loclass_draw_callback); + view_set_input_callback(loclass->view, loclass_input_callback); + view_set_context(loclass->view, loclass); + with_view_model( + loclass->view, LoclassViewModel * model, { model->header = furi_string_alloc(); }, false); + return loclass; +} + +void loclass_free(Loclass* loclass) { + furi_assert(loclass); + with_view_model( + loclass->view, LoclassViewModel * model, { furi_string_free(model->header); }, false); + view_free(loclass->view); + free(loclass); +} + +void loclass_reset(Loclass* loclass) { + furi_assert(loclass); + with_view_model( + loclass->view, + LoclassViewModel * model, + { + model->num_macs = 0; + furi_string_reset(model->header); + }, + false); +} + +View* loclass_get_view(Loclass* loclass) { + furi_assert(loclass); + return loclass->view; +} + +void loclass_set_callback(Loclass* loclass, LoclassCallback callback, void* context) { + furi_assert(loclass); + furi_assert(callback); + loclass->callback = callback; + loclass->context = context; +} + +void loclass_set_header(Loclass* loclass, const char* header) { + furi_assert(loclass); + furi_assert(header); + + with_view_model( + loclass->view, LoclassViewModel * model, { furi_string_set(model->header, header); }, true); +} + +void loclass_set_num_macs(Loclass* loclass, uint16_t num_macs) { + furi_assert(loclass); + with_view_model( + loclass->view, LoclassViewModel * model, { model->num_macs = num_macs; }, true); +} diff --git a/applications/external/picopass/views/loclass.h b/applications/external/picopass/views/loclass.h new file mode 100644 index 0000000000..0e39b6083b --- /dev/null +++ b/applications/external/picopass/views/loclass.h @@ -0,0 +1,22 @@ +#pragma once +#include +#include +#include + +typedef struct Loclass Loclass; + +typedef void (*LoclassCallback)(void* context); + +Loclass* loclass_alloc(); + +void loclass_free(Loclass* loclass); + +void loclass_reset(Loclass* loclass); + +View* loclass_get_view(Loclass* loclass); + +void loclass_set_callback(Loclass* loclass, LoclassCallback callback, void* context); + +void loclass_set_header(Loclass* loclass, const char* header); + +void loclass_set_num_macs(Loclass* loclass, uint16_t num_macs); diff --git a/applications/external/pocsag_pager/application.fam b/applications/external/pocsag_pager/application.fam index 3cef053749..cb893f66fb 100644 --- a/applications/external/pocsag_pager/application.fam +++ b/applications/external/pocsag_pager/application.fam @@ -9,4 +9,7 @@ App( fap_icon="pocsag_pager_10px.png", fap_category="Sub-GHz", fap_icon_assets="images", + fap_author="@xMasterX & @Shmuma", + fap_version="1.0", + fap_description="App can capture POCSAG 1200 messages on CC1101 supported frequencies.", ) diff --git a/applications/external/pocsag_pager/images/Fishing_123x52.png b/applications/external/pocsag_pager/images/Fishing_123x52.png deleted file mode 100644 index 1e365de8f4..0000000000 Binary files a/applications/external/pocsag_pager/images/Fishing_123x52.png and /dev/null differ diff --git a/applications/external/pocsag_pager/images/Lock_7x8.png b/applications/external/pocsag_pager/images/Lock_7x8.png deleted file mode 100644 index f7c9ca2c70..0000000000 Binary files a/applications/external/pocsag_pager/images/Lock_7x8.png and /dev/null differ diff --git a/applications/external/pocsag_pager/images/Pin_back_arrow_10x8.png b/applications/external/pocsag_pager/images/Pin_back_arrow_10x8.png deleted file mode 100644 index 3bafabd144..0000000000 Binary files a/applications/external/pocsag_pager/images/Pin_back_arrow_10x8.png and /dev/null differ diff --git a/applications/external/pocsag_pager/images/Scanning_123x52.png b/applications/external/pocsag_pager/images/Scanning_123x52.png deleted file mode 100644 index ec785948d0..0000000000 Binary files a/applications/external/pocsag_pager/images/Scanning_123x52.png and /dev/null differ diff --git a/applications/external/pocsag_pager/images/Unlock_7x8.png b/applications/external/pocsag_pager/images/Unlock_7x8.png deleted file mode 100644 index 9d82b4daf3..0000000000 Binary files a/applications/external/pocsag_pager/images/Unlock_7x8.png and /dev/null differ diff --git a/applications/external/pocsag_pager/images/WarningDolphin_45x42.png b/applications/external/pocsag_pager/images/WarningDolphin_45x42.png deleted file mode 100644 index d766ffbb44..0000000000 Binary files a/applications/external/pocsag_pager/images/WarningDolphin_45x42.png and /dev/null differ diff --git a/applications/external/pocsag_pager/pocsag_pager_app.c b/applications/external/pocsag_pager/pocsag_pager_app.c index b52b2f37fc..dc61c35f9e 100644 --- a/applications/external/pocsag_pager/pocsag_pager_app.c +++ b/applications/external/pocsag_pager/pocsag_pager_app.c @@ -202,7 +202,7 @@ int32_t pocsag_pager_app(void* p) { UNUSED(p); POCSAGPagerApp* pocsag_pager_app = pocsag_pager_app_alloc(); - DOLPHIN_DEED(DolphinDeedPluginStart); + dolphin_deed(DolphinDeedPluginStart); view_dispatcher_run(pocsag_pager_app->view_dispatcher); pocsag_pager_app_free(pocsag_pager_app); diff --git a/applications/external/pocsag_pager/views/pocsag_pager_receiver.c b/applications/external/pocsag_pager/views/pocsag_pager_receiver.c index 64939a9564..fd4461cee6 100644 --- a/applications/external/pocsag_pager/views/pocsag_pager_receiver.c +++ b/applications/external/pocsag_pager/views/pocsag_pager_receiver.c @@ -1,6 +1,7 @@ #include "pocsag_pager_receiver.h" #include "../pocsag_pager_app_i.h" #include +#include #include #include diff --git a/applications/external/pocsag_pager/views/pocsag_pager_receiver_info.c b/applications/external/pocsag_pager/views/pocsag_pager_receiver_info.c index 4811f39029..91ee69ab52 100644 --- a/applications/external/pocsag_pager/views/pocsag_pager_receiver_info.c +++ b/applications/external/pocsag_pager/views/pocsag_pager_receiver_info.c @@ -1,6 +1,7 @@ #include "pocsag_pager_receiver.h" #include "../pocsag_pager_app_i.h" #include "pocsag_pager_icons.h" +#include #include "../protocols/pcsg_generic.h" #include #include diff --git a/applications/external/pomodoro/application.fam b/applications/external/pomodoro/application.fam index 847d916060..e0d4e9ec04 100644 --- a/applications/external/pomodoro/application.fam +++ b/applications/external/pomodoro/application.fam @@ -5,7 +5,11 @@ App( entry_point="flipp_pomodoro_app", requires=["gui", "notification", "dolphin"], stack_size=1 * 1024, - fap_category="Tools", + fap_category="Misc", fap_icon_assets="images", fap_icon="flipp_pomodoro_10.png", + fap_author="@Th3Un1q3", + fap_weburl="https://github.com/Th3Un1q3/flipp_pomodoro", + fap_version="1.0", + fap_description="Boost Your Productivity with the Pomodoro Timer", ) diff --git a/applications/external/pong/application.fam b/applications/external/pong/application.fam index 95484b6e76..02dcfd6754 100644 --- a/applications/external/pong/application.fam +++ b/applications/external/pong/application.fam @@ -10,4 +10,8 @@ App( stack_size=1 * 1024, fap_icon="pong.png", fap_category="Games", + fap_author="@nmrr & @SimplyMinimal", + fap_weburl="https://github.com/nmrr/flipperzero-pong", + fap_version="1.0", + fap_description="Simple pong game", ) diff --git a/applications/external/pong/flipper_pong.c b/applications/external/pong/flipper_pong.c index 53c6a7e27e..55b371ad55 100644 --- a/applications/external/pong/flipper_pong.c +++ b/applications/external/pong/flipper_pong.c @@ -15,7 +15,8 @@ #define PAD_SIZE_X 3 #define PAD_SIZE_Y 8 -#define PLAYER1_PAD_SPEED 2 +#define PLAYER1_PAD_SPEED 4 + #define PLAYER2_PAD_SPEED 2 #define BALL_SIZE 4 @@ -38,22 +39,29 @@ typedef struct Players { static void draw_callback(Canvas* canvas, void* ctx) { furi_assert(ctx); - Players* players = ctx; - furi_mutex_acquire(players->mutex, FuriWaitForever); + Players* playersMutex = ctx; + furi_mutex_acquire(playersMutex->mutex, FuriWaitForever); canvas_draw_frame(canvas, 0, 0, 128, 64); - canvas_draw_box(canvas, players->player1_X, players->player1_Y, PAD_SIZE_X, PAD_SIZE_Y); - canvas_draw_box(canvas, players->player2_X, players->player2_Y, PAD_SIZE_X, PAD_SIZE_Y); - canvas_draw_box(canvas, players->ball_X, players->ball_Y, BALL_SIZE, BALL_SIZE); + canvas_draw_box( + canvas, playersMutex->player1_X, playersMutex->player1_Y, PAD_SIZE_X, PAD_SIZE_Y); + canvas_draw_box( + canvas, playersMutex->player2_X, playersMutex->player2_Y, PAD_SIZE_X, PAD_SIZE_Y); + canvas_draw_box(canvas, playersMutex->ball_X, playersMutex->ball_Y, BALL_SIZE, BALL_SIZE); canvas_set_font(canvas, FontPrimary); canvas_set_font_direction(canvas, CanvasDirectionBottomToTop); char buffer[16]; - snprintf(buffer, sizeof(buffer), "%u - %u", players->player1_score, players->player2_score); + snprintf( + buffer, + sizeof(buffer), + "%u - %u", + playersMutex->player1_score, + playersMutex->player2_score); canvas_draw_str_aligned( canvas, SCREEN_SIZE_X / 2 + 15, SCREEN_SIZE_Y / 2 + 2, AlignCenter, AlignTop, buffer); - furi_mutex_release(players->mutex); + furi_mutex_release(playersMutex->mutex); } static void input_callback(InputEvent* input_event, void* ctx) { @@ -93,7 +101,8 @@ uint8_t changeDirection() { return randomuint8[0]; } -int32_t flipper_pong_app() { +int32_t flipper_pong_app(void* p) { + UNUSED(p); EventApp event; FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(EventApp)); @@ -120,7 +129,7 @@ int32_t flipper_pong_app() { } ViewPort* view_port = view_port_alloc(); - view_port_draw_callback_set(view_port, draw_callback, &players.mutex); + view_port_draw_callback_set(view_port, draw_callback, &players); view_port_input_callback_set(view_port, input_callback, event_queue); Gui* gui = furi_record_open(RECORD_GUI); @@ -143,6 +152,7 @@ int32_t flipper_pong_app() { if(event.type == EventTypeInput) { if(event.input.key == InputKeyBack) { furi_mutex_release(players.mutex); + notification_message(notification, &sequence_set_only_green_255); break; } else if(event.input.key == InputKeyUp) { if(players.player1_Y >= 1 + PLAYER1_PAD_SPEED) diff --git a/applications/external/protoview/app.c b/applications/external/protoview/app.c index 70205a0a43..0f72fb9271 100644 --- a/applications/external/protoview/app.c +++ b/applications/external/protoview/app.c @@ -262,7 +262,7 @@ static bool keyboard_view_dispatcher_navigation_callback(void* ctx) { int32_t protoview_app_entry(void* p) { UNUSED(p); ProtoViewApp* app = protoview_app_alloc(); - DOLPHIN_DEED(DolphinDeedPluginStart); + dolphin_deed(DolphinDeedPluginStart); /* Create a timer. We do data analysis in the callback. */ FuriTimer* timer = furi_timer_alloc(timer_callback, FuriTimerTypePeriodic, app); diff --git a/applications/external/protoview/application.fam b/applications/external/protoview/application.fam index 329a95c8a8..7dbb7ea311 100644 --- a/applications/external/protoview/application.fam +++ b/applications/external/protoview/application.fam @@ -8,4 +8,7 @@ App( order=50, fap_icon="appicon.png", fap_category="Sub-GHz", + fap_author="@antirez & (fixes by @xMasterX)", + fap_version="1.0", + fap_description="Digital signal detection, visualization, editing and reply tool", ) diff --git a/applications/external/protoview/signal_file.c b/applications/external/protoview/signal_file.c index c60a6a181b..e7934d04bf 100644 --- a/applications/external/protoview/signal_file.c +++ b/applications/external/protoview/signal_file.c @@ -48,8 +48,11 @@ bool save_signal(ProtoViewApp* app, const char* filename) { for(int j = 0; regs[j]; j += 2) { furi_string_cat_printf(custom, "%02X %02X ", (int)regs[j], (int)regs[j + 1]); } - size_t len = furi_string_size(file_content); - furi_string_set_char(custom, len - 1, '\n'); + // 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"); furi_string_cat(file_content, custom); furi_string_free(custom); } diff --git a/applications/external/qrcode/application.fam b/applications/external/qrcode/application.fam index 3aac3f18d3..abf06041b2 100644 --- a/applications/external/qrcode/application.fam +++ b/applications/external/qrcode/application.fam @@ -13,7 +13,7 @@ App( "gui", "dialogs", ], - fap_category="Tools", + fap_category="Misc", fap_icon="icons/qrcode_10px.png", fap_icon_assets="icons", ) diff --git a/applications/external/qrcode/qrcode_app.c b/applications/external/qrcode/qrcode_app.c index 056f04eb98..c605c249e7 100644 --- a/applications/external/qrcode/qrcode_app.c +++ b/applications/external/qrcode/qrcode_app.c @@ -11,7 +11,7 @@ #include "qrcode.h" #define TAG "qrcode" -#define QRCODE_FOLDER ANY_PATH("qrcodes") +#define QRCODE_FOLDER STORAGE_APP_DATA_PATH_PREFIX #define QRCODE_EXTENSION ".qrcode" #define QRCODE_FILETYPE "QRCode" #define QRCODE_FILE_VERSION 0 @@ -518,6 +518,9 @@ static void qrcode_app_free(QRCodeApp* instance) { int32_t qrcode_app(void* p) { QRCodeApp* instance = qrcode_app_alloc(); FuriString* file_path = furi_string_alloc(); + Storage* storage = furi_record_open(RECORD_STORAGE); + storage_common_migrate(storage, EXT_PATH("qrcodes"), QRCODE_FOLDER); + furi_record_close(RECORD_STORAGE); do { if(p && strlen(p)) { diff --git a/applications/external/reversi/LICENSE b/applications/external/reversi/LICENSE new file mode 100644 index 0000000000..70a4dd1b36 --- /dev/null +++ b/applications/external/reversi/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Eugene Kirzhanov + +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/reversi/application.fam b/applications/external/reversi/application.fam new file mode 100644 index 0000000000..5401bc489c --- /dev/null +++ b/applications/external/reversi/application.fam @@ -0,0 +1,19 @@ +App( + appid="reversi", + name="Reversi", + apptype=FlipperAppType.EXTERNAL, + entry_point="game_reversi_app", + cdefines=["APP_GAME_REVERSI"], + requires=[ + "gui", + ], + stack_size=1 * 1024, + order=90, + fap_icon="game_reversi.png", + fap_category="Games", + fap_icon_assets_symbol="game_reversi", + fap_author="@dimat", + fap_weburl="https://github.com/zyuhel/flipperzero-racegame", + fap_version="1.0", + fap_description="Reversi game, the game controls should be intuitive. Longs press on OK opens the menu to start a new game.", +) diff --git a/applications/external/reversi/game_reversi.c b/applications/external/reversi/game_reversi.c new file mode 100644 index 0000000000..9b08027060 --- /dev/null +++ b/applications/external/reversi/game_reversi.c @@ -0,0 +1,350 @@ +// Game "Reversi" for Flipper Zero +// Copyright 2023 Dmitry Matyukhin + +#include +#include +#include +#include +#include +#include "reversi.h" + +#define FRAME_LEFT 3 +#define FRAME_TOP 3 +#define FRAME_CELL_SIZE 7 + +#define SAVING_FILENAME APP_DATA_PATH("game_reversi.save") + +typedef enum { AppScreenGame, AppScreenMenu } AppScreen; + +typedef struct { + GameState game; + AppScreen screen; + uint8_t selected_menu_item; + FuriMutex* mutex; +} AppState; + +#define MENU_ITEMS_COUNT 2 +static const char* popup_menu_strings[] = {"Resume", "New Game"}; + +static void draw_menu(Canvas* const canvas, const AppState* app_state); +static void gray_canvas(Canvas* const canvas); + +static void input_callback(InputEvent* input_event, void* ctx) { + furi_assert(ctx); + FuriMessageQueue* event_queue = ctx; + furi_message_queue_put(event_queue, input_event, FuriWaitForever); +} + +static void draw_callback(Canvas* const canvas, void* ctx) { + furi_assert(ctx); + const AppState* app_state = ctx; + furi_mutex_acquire(app_state->mutex, FuriWaitForever); + + const GameState* game_state = &app_state->game; + + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + + for(uint8_t i = 0; i <= BOARD_SIZE; i++) { + canvas_draw_line( + canvas, + FRAME_LEFT + FRAME_CELL_SIZE * i, + FRAME_TOP, + FRAME_LEFT + FRAME_CELL_SIZE * i, + FRAME_TOP + FRAME_CELL_SIZE * BOARD_SIZE); + canvas_draw_line( + canvas, + FRAME_LEFT, + FRAME_TOP + FRAME_CELL_SIZE * i, + FRAME_LEFT + FRAME_CELL_SIZE * BOARD_SIZE, + FRAME_TOP + FRAME_CELL_SIZE * i); + } + // + // draw cursor + canvas_set_color(canvas, ColorWhite); + canvas_draw_frame( + canvas, + FRAME_LEFT + FRAME_CELL_SIZE * game_state->cursor_x, + FRAME_TOP + FRAME_CELL_SIZE * game_state->cursor_y, + FRAME_CELL_SIZE + 1, + FRAME_CELL_SIZE + 1); + + canvas_set_color(canvas, ColorBlack); + // draw pieces + int blacks = 0, whites = 0; + const int radius = FRAME_CELL_SIZE >> 1; + for(uint8_t i = 0; i < BOARD_SIZE; i++) { + for(uint8_t j = 0; j < BOARD_SIZE; j++) { + if(!game_state->board[i][j]) { + continue; + } + if(game_state->board[i][j] == BLACK) { + canvas_draw_disc( + canvas, + FRAME_LEFT + FRAME_CELL_SIZE * i + radius + 1, + FRAME_TOP + FRAME_CELL_SIZE * j + radius + 1, + radius); + blacks++; + } else { + canvas_draw_circle( + canvas, + FRAME_LEFT + FRAME_CELL_SIZE * i + radius + 1, + FRAME_TOP + FRAME_CELL_SIZE * j + radius + 1, + radius); + whites++; + } + } + } + + canvas_set_font(canvas, FontPrimary); + // draw score + char score_str[25]; + memset(score_str, 0, sizeof(score_str)); + snprintf(score_str, sizeof(score_str), "%d - %d", whites, blacks); + + canvas_draw_str_aligned(canvas, 70, 3, AlignLeft, AlignTop, score_str); + + canvas_set_font(canvas, FontSecondary); + if(game_state->is_game_over) { + canvas_draw_str_aligned(canvas, 70, 20, AlignLeft, AlignTop, "Game over"); + + canvas_draw_str_aligned( + canvas, + 70, + FRAME_TOP + FRAME_CELL_SIZE * BOARD_SIZE, + AlignLeft, + AlignBottom, + "Press OK"); + + canvas_set_font(canvas, FontPrimary); + + if(whites == blacks) { + canvas_draw_str_aligned(canvas, 70, 30, AlignLeft, AlignTop, "DRAW"); + } else if( + ((game_state->human_color == WHITE) && whites > blacks) || + ((game_state->human_color == BLACK) && blacks > whites)) { + canvas_draw_str_aligned(canvas, 70, 30, AlignLeft, AlignTop, "YOU WIN"); + } else { + canvas_draw_str_aligned(canvas, 70, 30, AlignLeft, AlignTop, "YOU LOSE"); + } + } else if(game_state->current_player == game_state->human_color) { + canvas_draw_str_aligned(canvas, 70, 12, AlignLeft, AlignTop, "Your turn"); + } else { + canvas_draw_str_aligned(canvas, 70, 12, AlignLeft, AlignTop, "Computer turn"); + } + + if(app_state->screen == AppScreenMenu) { + draw_menu(canvas, app_state); + } + + furi_mutex_release(app_state->mutex); +} + +static void draw_menu(Canvas* const canvas, const AppState* app_state) { + gray_canvas(canvas); + canvas_set_color(canvas, ColorWhite); + canvas_draw_rbox(canvas, 28, 16, 72, 32, 4); + canvas_set_color(canvas, ColorBlack); + canvas_draw_rframe(canvas, 28, 16, 72, 32, 4); + + for(int i = 0; i < MENU_ITEMS_COUNT; i++) { + if(i == app_state->selected_menu_item) { + canvas_set_color(canvas, ColorBlack); + canvas_draw_box(canvas, 34, 20 + 12 * i, 60, 12); + } + + canvas_set_color(canvas, i == app_state->selected_menu_item ? ColorWhite : ColorBlack); + canvas_draw_str_aligned( + canvas, 64, 26 + 12 * i, AlignCenter, AlignCenter, popup_menu_strings[i]); + } +} + +static void gray_canvas(Canvas* const canvas) { + canvas_set_color(canvas, ColorWhite); + for(int x = 0; x < 128; x += 2) { + for(int y = 0; y < 64; y++) { + canvas_draw_dot(canvas, x + (y % 2 == 1 ? 0 : 1), y); + } + } +} + +bool load_game(GameState* game_state) { + Storage* storage = furi_record_open(RECORD_STORAGE); + storage_common_migrate(storage, EXT_PATH("apps/Games/game_reversi.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(GameState)); + } + storage_file_close(file); + storage_file_free(file); + + furi_record_close(RECORD_STORAGE); + + return bytes_readed == sizeof(GameState); +} + +void save_game(const GameState* game_state) { + Storage* storage = furi_record_open(RECORD_STORAGE); + + File* file = storage_file_alloc(storage); + if(storage_file_open(file, SAVING_FILENAME, FSAM_WRITE, FSOM_CREATE_ALWAYS)) { + storage_file_write(file, game_state, sizeof(GameState)); + } + storage_file_close(file); + storage_file_free(file); + + furi_record_close(RECORD_STORAGE); +} + +bool handle_key_game(GameState* game_state, InputKey key) { + switch(key) { + case InputKeyBack: + save_game(game_state); + return false; + break; + case InputKeyOk: + if(game_state->is_game_over) { + init_game(game_state); + save_game(game_state); + } else { + human_move(game_state); + } + break; + case InputKeyUp: + if(game_state->cursor_y > 0) { + game_state->cursor_y--; + } else { + game_state->cursor_y = BOARD_SIZE - 1; + } + break; + case InputKeyDown: + if(game_state->cursor_y < BOARD_SIZE - 1) { + game_state->cursor_y++; + } else { + game_state->cursor_y = 0; + } + break; + case InputKeyLeft: + if(game_state->cursor_x > 0) { + game_state->cursor_x--; + } else { + game_state->cursor_x = BOARD_SIZE - 1; + } + break; + case InputKeyRight: + if(game_state->cursor_x < BOARD_SIZE - 1) { + game_state->cursor_x++; + } else { + game_state->cursor_x = 0; + } + break; + default: + break; + } + return true; +} + +bool handle_key_menu(AppState* app_state, InputKey key) { + switch(key) { + case InputKeyUp: + if(app_state->selected_menu_item > 0) { + app_state->selected_menu_item--; + } + break; + case InputKeyDown: + if(app_state->selected_menu_item < MENU_ITEMS_COUNT - 1) { + app_state->selected_menu_item++; + } + break; + case InputKeyOk: + if(app_state->selected_menu_item == 1) { + // new game + init_game(&app_state->game); + save_game(&app_state->game); + } + app_state->screen = AppScreenGame; + break; + default: + break; + } + return true; +} + +// returns `true` if the event loop should keep going +bool handle_key(AppState* app_state, InputKey key) { + GameState* game_state = &app_state->game; + + switch(app_state->screen) { + case AppScreenGame: + return handle_key_game(game_state, key); + break; + case AppScreenMenu: + return handle_key_menu(app_state, key); + break; + } + return true; +} + +int32_t game_reversi_app() { + AppState app_state; + app_state.screen = AppScreenGame; + if(!load_game(&app_state.game)) { + init_game(&app_state.game); + } + + app_state.mutex = furi_mutex_alloc(FuriMutexTypeNormal); + if(!app_state.mutex) { + return 255; + } + InputEvent input; + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); + + ViewPort* view_port = view_port_alloc(); + view_port_draw_callback_set(view_port, draw_callback, &app_state); + view_port_input_callback_set(view_port, input_callback, event_queue); + + Gui* gui = furi_record_open(RECORD_GUI); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + bool is_finished = false; + + while(!is_finished) { + // check if it's computer's turn + if(!app_state.game.is_game_over && + (app_state.game.current_player != app_state.game.human_color)) { + computer_move(&app_state.game); + } + FuriStatus event_status = furi_message_queue_get(event_queue, &input, FuriWaitForever); + if(event_status == FuriStatusOk) { + // handle only press event, ignore repeat/release events + + if(input.type == InputTypeLong && input.key == InputKeyOk && + app_state.screen == AppScreenGame) { + furi_mutex_acquire(app_state.mutex, FuriWaitForever); + app_state.selected_menu_item = 0; + app_state.screen = AppScreenMenu; + view_port_update(view_port); + furi_mutex_release(app_state.mutex); + continue; + } + if(input.type != InputTypePress) continue; + + furi_mutex_acquire(app_state.mutex, FuriWaitForever); + is_finished = !handle_key(&app_state, input.key); + view_port_update(view_port); + furi_mutex_release(app_state.mutex); + } + } + + gui_remove_view_port(gui, view_port); + furi_record_close(RECORD_GUI); + + view_port_free(view_port); + + furi_message_queue_free(event_queue); + + furi_mutex_free(app_state.mutex); + + return 0; +} diff --git a/applications/external/reversi/game_reversi.png b/applications/external/reversi/game_reversi.png new file mode 100644 index 0000000000..3a321471b9 Binary files /dev/null and b/applications/external/reversi/game_reversi.png differ diff --git a/applications/external/reversi/reversi.c b/applications/external/reversi/reversi.c new file mode 100644 index 0000000000..46c1929cf2 --- /dev/null +++ b/applications/external/reversi/reversi.c @@ -0,0 +1,168 @@ +// Game "Reversi" for Flipper Zero +// Copyright 2023 Dmitry Matyukhin + +#include "reversi.h" + +// Psst! Most of this file was written with Copilot + +// Check if the move is legal by checking if it results in any opponent pieces being captured +bool is_legal_move(int8_t board[BOARD_SIZE][BOARD_SIZE], int row, int col, int player) { + if(board[row][col] != 0) return false; + int opponent = -player; + for(int i = -1; i <= 1; i++) { + for(int j = -1; j <= 1; j++) { + if(i == 0 && j == 0) continue; + int r = row + i, c = col + j; + if(r >= 0 && r < BOARD_SIZE && c >= 0 && c < BOARD_SIZE && board[r][c] == opponent) { + int k = 2; + while(true) { + r += i; + c += j; + if(r < 0 || r >= BOARD_SIZE || c < 0 || c >= BOARD_SIZE) break; + if(board[r][c] == player) return true; + if(board[r][c] == 0) break; + k++; + } + } + } + } + return false; +} + +// Check if the game is over by checking if there are no more moves left for +// either player +bool is_game_over(int8_t board[BOARD_SIZE][BOARD_SIZE]) { + for(int i = 0; i < BOARD_SIZE; i++) { + for(int j = 0; j < BOARD_SIZE; j++) { + if(is_legal_move(board, i, j, BLACK) || is_legal_move(board, i, j, WHITE)) { + return false; + } + } + } + return true; +} + +bool has_legal_moves(int8_t board[BOARD_SIZE][BOARD_SIZE], int8_t player_color) { + for(int i = 0; i < BOARD_SIZE; i++) { + for(int j = 0; j < BOARD_SIZE; j++) { + if(is_legal_move(board, i, j, player_color)) { + return true; + } + } + } + return false; +} + +// Calculate the heuristic value of the current board. This function can +// be replaced with a more complex evaluation function that takes into +// account factors such as mobility, piece square tables, etc. +int heuristic(int8_t board[BOARD_SIZE][BOARD_SIZE]) { + int white = 0, black = 0; + for(int i = 0; i < BOARD_SIZE; i++) { + for(int j = 0; j < BOARD_SIZE; j++) { + if(board[i][j] == 1) white++; + if(board[i][j] == -1) black++; + } + } + return white - black; +} + +// Make a move on the board and capture any opponent pieces +void make_move(GameState* state, int x, int y, int player) { + state->board[x][y] = player; + int opponent = -player; + for(int i = -1; i <= 1; i++) { + for(int j = -1; j <= 1; j++) { + if(i == 0 && j == 0) continue; + int r = x + i, c = y + j; + if(r >= 0 && r < BOARD_SIZE && c >= 0 && c < BOARD_SIZE && + state->board[r][c] == opponent) { + int k = 2; + while(true) { + r += i; + c += j; + if(r < 0 || r >= BOARD_SIZE || c < 0 || c >= BOARD_SIZE) break; + if(state->board[r][c] == player) { + r -= i; + c -= j; + while(r != x || c != y) { + state->board[r][c] = player; + r -= i; + c -= j; + } + break; + } + if(state->board[r][c] == 0) break; + k++; + } + } + } + } + state->is_game_over = is_game_over(state->board); +} + +void init_game(GameState* state) { + for(int i = 0; i < BOARD_SIZE; i++) { + for(int j = 0; j < BOARD_SIZE; j++) { + state->board[i][j] = 0; + } + } + + // Place the initial pieces + int mid = BOARD_SIZE / 2; + state->board[mid - 1][mid - 1] = WHITE; + state->board[mid][mid] = WHITE; + state->board[mid - 1][mid] = BLACK; + state->board[mid][mid - 1] = BLACK; + + state->cursor_x = mid - 1; + state->cursor_y = mid + 1; + + // Set up turn order + state->human_color = WHITE; + state->current_player = WHITE; + + state->is_game_over = false; +} + +void human_move(GameState* game_state) { + if(game_state->current_player != game_state->human_color) { + return; + } + + if(is_legal_move( + game_state->board, + game_state->cursor_x, + game_state->cursor_y, + game_state->current_player)) { + make_move( + game_state, game_state->cursor_x, game_state->cursor_y, game_state->current_player); + game_state->current_player = -game_state->current_player; + } +} + +void computer_move(GameState* game_state) { + if(game_state->current_player == game_state->human_color) { + return; + } + int best_row = -1, best_col = -1, best_score = -1000000; + for(int i = 0; i < BOARD_SIZE; i++) { + for(int j = 0; j < BOARD_SIZE; j++) { + if(!is_legal_move(game_state->board, i, j, game_state->current_player)) { + continue; + } + int score = heuristic(game_state->board); + if(score > best_score) { + best_score = score; + best_row = i; + best_col = j; + } + } + } + if(best_row != -1) { + make_move(game_state, best_row, best_col, game_state->current_player); + } + if(has_legal_moves(game_state->board, game_state->human_color)) { + game_state->current_player = -game_state->current_player; + } +} diff --git a/applications/external/reversi/reversi.h b/applications/external/reversi/reversi.h new file mode 100644 index 0000000000..ee45888384 --- /dev/null +++ b/applications/external/reversi/reversi.h @@ -0,0 +1,21 @@ +#pragma once + +#include +#include + +#define BLACK 1 +#define WHITE -1 +#define BOARD_SIZE 8 + +typedef struct { + int8_t board[BOARD_SIZE][BOARD_SIZE]; + int8_t current_player; + int8_t human_color; + uint8_t cursor_x; + uint8_t cursor_y; + uint8_t is_game_over; +} GameState; + +void init_game(GameState* state); +void computer_move(GameState* game_state); +void human_move(GameState* game_state); diff --git a/applications/external/rootoflife/LICENSE b/applications/external/rootoflife/LICENSE new file mode 100644 index 0000000000..765f2a64b4 --- /dev/null +++ b/applications/external/rootoflife/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 Kirill Korepanov + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/applications/external/rootoflife/application.fam b/applications/external/rootoflife/application.fam new file mode 100644 index 0000000000..aa3d682118 --- /dev/null +++ b/applications/external/rootoflife/application.fam @@ -0,0 +1,18 @@ +App( + appid="roots_of_life", + name="Roots of Life", + apptype=FlipperAppType.EXTERNAL, + entry_point="roots_of_life_game_app", + cdefines=["APP_ROOTS_OF_LIFE_GAME"], + requires=["gui"], + stack_size=1 * 1024, + order=30, + fap_icon="roots_of_life_10px.png", + fap_category="Games", + fap_icon_assets="images", + fap_icon_assets_symbol="roots_of_life_game", + fap_author="@Xorboo", + fap_weburl="https://github.com/Xorboo/root-of-life", + fap_version="1.0", + fap_description="A zen-puzzle game for FlipperZero, puzzle made on GlobalGameJam23 (theme: Roots)", +) diff --git a/applications/external/rootoflife/images/place_error.png b/applications/external/rootoflife/images/place_error.png new file mode 100644 index 0000000000..9032c65322 Binary files /dev/null and b/applications/external/rootoflife/images/place_error.png differ diff --git a/applications/external/rootoflife/images/place_ok.png b/applications/external/rootoflife/images/place_ok.png new file mode 100644 index 0000000000..a99ce0a3d7 Binary files /dev/null and b/applications/external/rootoflife/images/place_ok.png differ diff --git a/applications/external/rootoflife/images/root_reroll.png b/applications/external/rootoflife/images/root_reroll.png new file mode 100644 index 0000000000..996b5ff811 Binary files /dev/null and b/applications/external/rootoflife/images/root_reroll.png differ diff --git a/applications/external/rootoflife/images/score.png b/applications/external/rootoflife/images/score.png new file mode 100644 index 0000000000..73bf140d4d Binary files /dev/null and b/applications/external/rootoflife/images/score.png differ diff --git a/applications/external/rootoflife/images/tree.png b/applications/external/rootoflife/images/tree.png new file mode 100644 index 0000000000..4f424270ca Binary files /dev/null and b/applications/external/rootoflife/images/tree.png differ diff --git a/applications/external/rootoflife/roots_of_life_10px.png b/applications/external/rootoflife/roots_of_life_10px.png new file mode 100644 index 0000000000..49d72986dd Binary files /dev/null and b/applications/external/rootoflife/roots_of_life_10px.png differ diff --git a/applications/external/rootoflife/roots_of_life_game.c b/applications/external/rootoflife/roots_of_life_game.c new file mode 100644 index 0000000000..1fbee225ed --- /dev/null +++ b/applications/external/rootoflife/roots_of_life_game.c @@ -0,0 +1,750 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "roots_of_life_game_icons.h" + +#define TAG "RootsOfLife" + +// Flipper +#define FLIPPER_LCD_WIDTH 128 +#define FLIPPER_LCD_HEIGHT 64 + +// General +#define GROUND_HEIGHT 10 +#define CELL_SIZE 3 +#define FIELD_START_X 0 +#define FIELD_START_Y (GROUND_HEIGHT + 1) +#define CELLS_X (FLIPPER_LCD_WIDTH / CELL_SIZE) +#define CELLS_Y ((FLIPPER_LCD_HEIGHT - GROUND_HEIGHT) / CELL_SIZE) +#define CELLS_TOTAL (CELLS_Y * CELLS_X) +#define CELL(Y, X) (Y * CELLS_X + X) + +// Root Spawn +#define ROOT_SIZE_X 7 +#define ROOT_SIZE_Y 7 +#define ROOT(Y, X) ((Y)*ROOT_SIZE_X + (X)) + +#define SPAWN_DIRECTIONS 2 +#define GROW_STEPS 4 +#define GROW_SAME_DIRECTION_CHANCE 70 +#define RANDOM_GROW_ATTEMPTS 4 +#define RANDOM_GROW_CHANCE 50 + +// UI +#define BLINK_PERIOD 12 +#define BLINK_HIDE_FRAMES 5 +#define TREE_HEIGHT 10 +#define PICKUP_FREQUENCY 10 + +// Game +#define REROLLS_MAX 5 +#define SCORE_FACTOR 10 + +#define PICKUPS_MIN 1 +#define PICKUPS_MAX 5 +#define PICKUPS_POINTS_FACTOR 10 + +typedef enum { EventTypeTick, EventTypeKey } EventType; + +typedef enum { + R_NONE = 0, + R_UP = 0b1000, + R_DOWN = 0b0100, + R_LEFT = 0b0010, + R_RIGHT = 0b0001 +} Direction; + +typedef enum { StageStart, StageRun, StageOver } GameStage; + +typedef struct { + bool initialDraw; + + GameStage stage; + int tick; + + bool* filledCells; + char* cells; + bool* pickups; + int collectedPickups; + + bool* filledRootBase; + char* rootBase; + + int rootSizeX; + int rootSizeY; + bool* filledRoot; + char* root; + + int pX, pY; + + int rerolls; + int score; + FuriMutex* mutex; +} GameState; + +typedef struct { + EventType type; + InputEvent input; +} GameEvent; + +static Direction rand_dir() { + int r = rand() % 4; + return 1 << r; +} + +static Direction reverse_dir(Direction dir) { + switch(dir) { + case R_UP: + return R_DOWN; + case R_DOWN: + return R_UP; + case R_LEFT: + return R_RIGHT; + case R_RIGHT: + return R_LEFT; + + default: + return R_NONE; + } +} + +static int rand_range(int min, int max) { + return min + rand() % (max - min); +} +static bool rand_chance(int chance) { + return (rand() % 100) < chance; +} + +static bool has_intersection(char cellA, char cellB) { + return cellA & cellB; +} + +static int root_index(GameState* state, int y, int x) { + return y * state->rootSizeX + x; +} + +static void set_cell(GameState* state, int y, int x, char cellRoot) { + int c = CELL(y, x); + state->filledCells[c] = true; + state->cells[c] = cellRoot; +} + +static void game_state_init(GameState* state) { + state->initialDraw = false; + state->tick = 0; + + // Init field arrays + state->filledCells = (bool*)malloc(CELLS_TOTAL * sizeof(bool)); + state->cells = (char*)malloc(CELLS_TOTAL * sizeof(char)); + state->pickups = (bool*)malloc(CELLS_TOTAL * sizeof(char)); + + state->rootBase = (char*)malloc(ROOT_SIZE_X * ROOT_SIZE_Y * sizeof(char)); + state->filledRootBase = (bool*)malloc(ROOT_SIZE_X * ROOT_SIZE_Y * sizeof(bool)); + state->root = NULL; + state->filledRoot = NULL; + + for(int i = 0; i < CELLS_TOTAL; i++) { + state->filledCells[i] = false; + state->cells[i] = R_NONE; + state->pickups[i] = false; + } +} + +static void free_root(GameState* state) { + if(state->root) free(state->root); + if(state->filledRoot) free(state->filledRoot); +} + +static void game_state_free(GameState* state) { + free(state->filledCells); + free(state->cells); + free(state->pickups); + + free(state->rootBase); + free(state->filledRootBase); + + free_root(state); +} + +/*static bool has_root(GameState* state, int x, int y) { + return x >= 0 && x < ROOT_SIZE_X && y >= 0 && y < ROOT_SIZE_Y && + state->filledRootBase[ROOT(y, x)]; +}*/ + +static void generate_new_root(GameState* state) { + for(int i = 0; i < ROOT_SIZE_X * ROOT_SIZE_Y; i++) { + state->filledRootBase[i] = false; + state->rootBase[i] = R_NONE; + } + + int cX = ROOT_SIZE_X / 2; + int cY = ROOT_SIZE_Y / 2; + int c = ROOT(cY, cX); + state->filledRootBase[c] = true; + + for(int i = 0; i < SPAWN_DIRECTIONS; i++) { + int pX = cX, pY = cY; + Direction oldDir = rand_dir(); + for(int g = 0; g < GROW_STEPS; g++) { + Direction dir = rand_chance(GROW_SAME_DIRECTION_CHANCE) ? oldDir : rand_dir(); + oldDir = dir; + + int nX = pX - (dir & R_LEFT ? 1 : 0) + (dir & R_RIGHT ? 1 : 0); + int nY = pY - (dir & R_UP ? 1 : 0) + (dir & R_DOWN ? 1 : 0); + if(nX < 0 || nY < 0 || nX >= ROOT_SIZE_X || nY >= ROOT_SIZE_Y) continue; + + int n = ROOT(nY, nX); + state->filledRootBase[n] = true; + + // Connect points + int p = ROOT(pY, pX); + state->rootBase[p] |= dir; + state->rootBase[n] |= reverse_dir(dir); + + // Grow from new point + pX = nX; + pY = nY; + } + } + + for(int y = 0; y < ROOT_SIZE_Y; y++) { + for(int x = 0; x < ROOT_SIZE_X; x++) { + int c = ROOT(y, x); + if(!state->filledRootBase[c]) continue; + + /* + if(has_root(state, x - 1, y)) state->rootBase[c] |= R_LEFT; + if(has_root(state, x + 1, y)) state->rootBase[c] |= R_RIGHT; + if(has_root(state, x, y - 1)) state->rootBase[c] |= R_UP; + if(has_root(state, x, y + 1)) state->rootBase[c] |= R_DOWN; + */ + + for(int r = 0; r < RANDOM_GROW_ATTEMPTS; r++) { + if(!rand_chance(RANDOM_GROW_CHANCE)) continue; + state->rootBase[c] |= rand_dir(); + } + } + } + + // Copy root to real root + int minX = cX, maxX = cX, minY = cY, maxY = cY; + for(int y = 0; y < ROOT_SIZE_Y; y++) { + for(int x = 0; x < ROOT_SIZE_X; x++) { + int r = ROOT(y, x); + if(!state->filledRootBase[r]) continue; + + minX = MIN(minX, x); + maxX = MAX(maxX, x); + minY = MIN(minY, y); + maxY = MAX(maxY, y); + } + } + + // Clone to real root + state->rootSizeX = maxX - minX + 1; + state->rootSizeY = maxY - minY + 1; + free_root(state); + + state->root = (char*)malloc(state->rootSizeX * state->rootSizeY * sizeof(char)); + state->filledRoot = (bool*)malloc(state->rootSizeX * state->rootSizeY * sizeof(bool)); + for(int y = 0; y < state->rootSizeY; y++) { + for(int x = 0; x < state->rootSizeX; x++) { + int c = root_index(state, y, x); + int r = ROOT(y + minY, x + minX); + state->filledRoot[c] = state->filledRootBase[r]; + state->root[c] = state->rootBase[r]; + } + } +} + +static bool in_borders(int x, int y) { + return x >= 0 && y >= 0 && x < CELLS_X && y < CELLS_Y; +} +static char get_cell(GameState* state, int x, int y) { + if(!in_borders(x, y)) return R_NONE; + return state->cells[CELL(y, x)]; +} + +static bool get_filled_cell(GameState* state, int x, int y) { + if(!in_borders(x, y)) return false; + return state->filledCells[CELL(y, x)]; +} + +static bool can_place_root(GameState* state) { + bool hasConnection = false; + for(int y = 0; y < state->rootSizeY; y++) { + for(int x = 0; x < state->rootSizeX; x++) { + int r = root_index(state, y, x); + if(!state->filledRoot[r]) { + continue; + } + char root = state->root[r]; + + int rY = y + state->pY; + int rX = x + state->pX; + + // Check if colliding + if(get_filled_cell(state, rX, rY)) { + char cell = get_cell(state, rX, rY); + if(has_intersection(cell, root)) { + return false; + } + hasConnection = true; + } + + // Check neighbours + hasConnection |= (root & R_RIGHT) && (get_cell(state, rX + 1, rY) & R_LEFT); + hasConnection |= (root & R_LEFT) && (get_cell(state, rX - 1, rY) & R_RIGHT); + hasConnection |= (root & R_UP) && (get_cell(state, rX, rY - 1) & R_DOWN); + hasConnection |= (root & R_DOWN) && (get_cell(state, rX, rY + 1) & R_UP); + } + } + + return hasConnection; +} + +static bool try_place_root(GameState* state) { + if(!can_place_root(state)) return false; + + for(int y = 0; y < state->rootSizeY; y++) { + for(int x = 0; x < state->rootSizeX; x++) { + int r = root_index(state, y, x); + if(!state->filledRoot[r]) continue; + + int rY = y + state->pY; + int rX = x + state->pX; + + // Root may be out of borders in rare cases (after new cpawn changed its size), just ignore that part + if(in_borders(rX, rY)) { + int c = CELL(rY, rX); + + state->filledCells[c] = true; + state->cells[c] |= state->root[r]; + } + } + } + + return true; +} + +static void reset_level(GameState* state) { + state->stage = StageStart; + state->tick = 0; + + for(int i = 0; i < CELLS_TOTAL; i++) { + state->filledCells[i] = false; + state->cells[i] = R_NONE; + } + + generate_new_root(state); + + // Starting cells + int midX = CELLS_X / 2; + set_cell(state, 0, midX, R_UP | R_DOWN); + set_cell(state, 1, midX, R_UP | R_DOWN | R_LEFT | R_RIGHT); + set_cell(state, 1, midX - 1, R_RIGHT | R_DOWN); + set_cell(state, 1, midX + 1, R_LEFT | R_DOWN); + set_cell(state, 2, midX, R_UP); + + state->pX = midX; + state->pY = 4; + + state->rerolls = REROLLS_MAX; + state->score = 0; + + state->collectedPickups = 0; + for(int i = 0, n = rand_range(PICKUPS_MIN, PICKUPS_MAX); i < n; i++) { + int x = rand_range(0, CELLS_X); + int y = rand_range(0, CELLS_Y); + state->pickups[CELL(y, x)] = true; + } +} + +static void recalculate_score(GameState* state) { + int score = 0; + for(int i = 0; i < CELLS_TOTAL; i++) { + if(state->filledCells[i]) score++; + } + + for(int i = 0; i < CELLS_TOTAL; i++) { + if(!state->pickups[i] || !state->filledCells[i]) continue; + + state->pickups[i] = false; + state->collectedPickups++; + state->rerolls++; + } + + state->score = (score + state->collectedPickups * PICKUPS_POINTS_FACTOR) * SCORE_FACTOR; +} + +static void draw_root_cell(Canvas* canvas, char root, int y, int x, bool isHidden) { + int posX = FIELD_START_X + x * CELL_SIZE + 1, posY = FIELD_START_Y + y * CELL_SIZE + 1; + canvas_draw_dot(canvas, posX, posY); + + if(isHidden) { + canvas_set_color(canvas, ColorXOR); + } + + if(root & R_UP) canvas_draw_dot(canvas, posX, posY - 1); + if(root & R_DOWN) canvas_draw_dot(canvas, posX, posY + 1); + if(root & R_LEFT) canvas_draw_dot(canvas, posX - 1, posY); + if(root & R_RIGHT) canvas_draw_dot(canvas, posX + 1, posY); + + if(isHidden) { + canvas_set_color(canvas, ColorBlack); + } +} + +static void draw_placed_roots(Canvas* canvas, GameState* state) { + for(int y = 0; y < CELLS_Y; y++) { + for(int x = 0; x < CELLS_X; x++) { + int c = CELL(y, x); + if(!state->filledCells[c]) continue; + draw_root_cell(canvas, state->cells[c], y, x, false); + } + } +} + +static void draw_pickup(Canvas* canvas, GameState* state, int y, int x) { + int posX = FIELD_START_X + x * CELL_SIZE + 1, posY = FIELD_START_Y + y * CELL_SIZE + 1; + + int stage = state->tick / PICKUP_FREQUENCY; + + if(stage++ % 4 < 3) canvas_draw_dot(canvas, posX + 1, posY); + if(stage++ % 4 < 3) canvas_draw_dot(canvas, posX, posY + 1); + if(stage++ % 4 < 3) canvas_draw_dot(canvas, posX - 1, posY); + if(stage++ % 4 < 3) canvas_draw_dot(canvas, posX, posY - 1); +} + +static void draw_pickups(Canvas* canvas, GameState* state) { + for(int y = 0; y < CELLS_Y; y++) { + for(int x = 0; x < CELLS_X; x++) { + int c = CELL(y, x); + if(!state->pickups[c]) continue; + draw_pickup(canvas, state, y, x); + } + } +} + +static void draw_active_root(Canvas* canvas, GameState* state) { + bool isHidden = (state->tick % BLINK_PERIOD) < BLINK_HIDE_FRAMES; + + for(int y = 0; y < state->rootSizeY; y++) { + for(int x = 0; x < state->rootSizeX; x++) { + int c = root_index(state, y, x); + if(!state->filledRoot[c]) continue; + + int realX = x + state->pX; + int realY = y + state->pY; + draw_root_cell(canvas, state->root[c], realY, realX, isHidden); + } + } +} + +#if DRAW_DEBUG +static void draw_generated_root(Canvas* canvas, GameState* state) { + bool isHidden = (state->tick % BLINK_PERIOD) < BLINK_HIDE_FRAMES; + + for(int y = 0; y < ROOT_SIZE_Y; y++) { + for(int x = 0; x < ROOT_SIZE_X; x++) { + int c = ROOT(y, x); + if(!state->filledRootBase[c]) continue; + + int realX = x + 1; + int realY = y + 1; + draw_root_cell(canvas, state->rootBase[c], realY, realX, isHidden); + } + } +} +#endif + +static void draw_ground(Canvas* canvas, GameState* state) { + canvas_draw_line(canvas, 0, GROUND_HEIGHT, FLIPPER_LCD_WIDTH, GROUND_HEIGHT); + UNUSED(state); +} + +static void draw_tree(Canvas* canvas, GameState* state) { + canvas_draw_icon(canvas, FLIPPER_LCD_WIDTH / 2 - 5, GROUND_HEIGHT - TREE_HEIGHT, &I_tree); + UNUSED(state); +} + +static void draw_placement(Canvas* canvas, GameState* state) { + bool canPlace = can_place_root(state); + canvas_draw_icon(canvas, FLIPPER_LCD_WIDTH - 10, 0, canPlace ? &I_place_ok : &I_place_error); +} + +static void draw_rerolls(Canvas* canvas, GameState* state) { + UNUSED(canvas); + UNUSED(state); + + canvas_draw_icon(canvas, 0, 0, &I_root_reroll); + + // Ugh + FuriString* tmp_string = furi_string_alloc(); + furi_string_printf(tmp_string, "%d", MAX(0, state->rerolls)); + canvas_draw_str(canvas, 11, 9, furi_string_get_cstr(tmp_string)); + furi_string_free(tmp_string); +} + +static void draw_score(Canvas* canvas, GameState* state) { + UNUSED(canvas); + UNUSED(state); + + int x = FLIPPER_LCD_WIDTH / 2 + 15; + canvas_draw_icon(canvas, x, 0, &I_score); + + // Ugh + FuriString* tmp_string = furi_string_alloc(); + furi_string_printf(tmp_string, "%d", MAX(0, state->score)); + canvas_draw_str(canvas, x + 11, 9, furi_string_get_cstr(tmp_string)); + furi_string_free(tmp_string); +} + +static void draw_gui(Canvas* canvas, GameState* state) { + draw_ground(canvas, state); + draw_tree(canvas, state); + draw_placement(canvas, state); + draw_rerolls(canvas, state); + draw_score(canvas, state); +} + +static void draw_center_box(Canvas* canvas, int w2, int h2, int margin) { + int x = FLIPPER_LCD_WIDTH / 2 - w2; + int y = FLIPPER_LCD_HEIGHT / 2 - h2; + + canvas_set_color(canvas, ColorWhite); + canvas_draw_box( + canvas, x - margin - 1, y - margin - 1, (w2 + margin + 1) * 2, (h2 + margin + 1) * 2); + canvas_set_color(canvas, ColorBlack); + canvas_draw_frame(canvas, x - margin, y - margin, (w2 + margin) * 2, (h2 + margin) * 2); +} + +static void draw_start_ui(Canvas* canvas, GameState* state) { + int w2 = 40; + int margin = 3; + int h2 = 10; + draw_center_box(canvas, w2, h2, margin); + + int x = FLIPPER_LCD_WIDTH / 2 - w2; + int y = FLIPPER_LCD_HEIGHT / 2 - h2; + canvas_draw_str(canvas, x + 1, y + 9, " Grow your roots "); + canvas_draw_str(canvas, x + 1, y + 18, "Press [OK] to start"); + + UNUSED(state); +} + +static void draw_end_ui(Canvas* canvas, GameState* state) { + int w2 = 46; + int margin = 3; + int h2 = 15; + draw_center_box(canvas, w2, h2, margin); + + int x = FLIPPER_LCD_WIDTH / 2 - w2; + int y = FLIPPER_LCD_HEIGHT / 2 - h2; + + canvas_draw_str(canvas, x + 1, y + 9, " Game Over "); + + FuriString* tmp_string = furi_string_alloc(); + furi_string_printf(tmp_string, "You've got %d points", MAX(0, state->score)); + canvas_draw_str(canvas, x + 1, y + 19, furi_string_get_cstr(tmp_string)); + furi_string_free(tmp_string); + + canvas_draw_str(canvas, x + 2, y + 29, "Press [OK] to restart"); + + int h = 13, w = 54; + canvas_set_color(canvas, ColorWhite); + canvas_draw_box(canvas, 0, FLIPPER_LCD_HEIGHT - h, w + 1, h + 1); + canvas_set_color(canvas, ColorBlack); + canvas_draw_frame(canvas, 0, FLIPPER_LCD_HEIGHT - h, w, h); + canvas_draw_str(canvas, 2, FLIPPER_LCD_HEIGHT - 3, "by @Xorboo"); + UNUSED(state); +} + +static void roots_draw_callback(Canvas* const canvas, void* ctx) { + furi_assert(ctx); + GameState* state = ctx; + furi_mutex_acquire(state->mutex, FuriWaitForever); + + if(!state->initialDraw) { + state->initialDraw = true; + + canvas_set_font(canvas, FontSecondary); + reset_level(state); + } + + state->tick++; + + draw_gui(canvas, state); + draw_placed_roots(canvas, state); + draw_pickups(canvas, state); + + switch(state->stage) { + case StageStart: + draw_start_ui(canvas, state); + break; + + case StageRun: + draw_active_root(canvas, state); +#if DRAW_DEBUG + draw_generated_root(canvas, state); +#endif + break; + + case StageOver: + draw_end_ui(canvas, state); + break; + } + + furi_mutex_release(state->mutex); +} + +static void roots_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { + furi_assert(event_queue); + + GameEvent event = {.type = EventTypeKey, .input = *input_event}; + furi_message_queue_put(event_queue, &event, FuriWaitForever); +} + +static void roots_update_timer_callback(FuriMessageQueue* event_queue) { + furi_assert(event_queue); + + GameEvent event = {.type = EventTypeTick}; + furi_message_queue_put(event_queue, &event, 0); +} + +static void ProcessStartInput(GameState* state, InputKey key) { + if(key == InputKeyOk) { + state->stage = StageRun; + } +} + +static void ProcessRunInput(GameState* state, InputKey key) { + switch(key) { + case InputKeyRight: + state->pX = MIN(state->pX + 1, CELLS_X - state->rootSizeX); + break; + case InputKeyLeft: + state->pX = MAX(state->pX - 1, 0); + break; + case InputKeyUp: + state->pY = MAX(state->pY - 1, 0); + break; + case InputKeyDown: + state->pY = MIN(state->pY + 1, CELLS_Y - state->rootSizeY); + break; + case InputKeyOk: { + bool rootPlaced = try_place_root(state); + if(rootPlaced) { + recalculate_score(state); + generate_new_root(state); + } else { + state->rerolls--; + if(state->rerolls >= 0) { + generate_new_root(state); + } else { + state->stage = StageOver; + } + } + break; + } + default: + break; + } +} + +static void ProcessOverInput(GameState* state, InputKey key) { + if(key == InputKeyOk) { + state->stage = StageStart; + reset_level(state); + } +} + +int32_t roots_of_life_game_app(void* p) { + FURI_LOG_D(TAG, "Starting game..."); + + UNUSED(p); + int32_t return_code = 0; + + // Set random seed from interrR_UPts + srand(DWT->CYCCNT); + + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(GameEvent)); + + GameState* state = malloc(sizeof(GameState)); + game_state_init(state); + + state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + if(!state->mutex) { + FURI_LOG_E(TAG, "Cannot create mutex\r\n"); + return_code = 255; + goto free_and_exit; + } + + // Set system callbacks + ViewPort* view_port = view_port_alloc(); + view_port_draw_callback_set(view_port, roots_draw_callback, state); + view_port_input_callback_set(view_port, roots_input_callback, event_queue); + + FuriTimer* timer = + furi_timer_alloc(roots_update_timer_callback, FuriTimerTypePeriodic, event_queue); + furi_timer_start(timer, furi_kernel_get_tick_frequency() / 22); + + // Open GUI and register view_port + Gui* gui = furi_record_open(RECORD_GUI); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + FURI_LOG_D(TAG, "Entering game loop..."); + GameEvent event; + for(bool processing = true; processing;) { + FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); + furi_mutex_acquire(state->mutex, FuriWaitForever); + + if(event_status == FuriStatusOk) { + // Key events + if(event.type == EventTypeKey) { + //FURI_LOG_D(TAG, "Got key: %d", event.input.key); + if(event.input.type == InputTypePress || event.input.type == InputTypeLong || + event.input.type == InputTypeRepeat) { + if(event.input.key == InputKeyBack) { + processing = false; + } + + switch(state->stage) { + case StageStart: + ProcessStartInput(state, event.input.key); + break; + case StageRun: + ProcessRunInput(state, event.input.key); + break; + case StageOver: + ProcessOverInput(state, event.input.key); + break; + } + } + } + } + + view_port_update(view_port); + furi_mutex_release(state->mutex); + } + + furi_timer_free(timer); + view_port_enabled_set(view_port, false); + gui_remove_view_port(gui, view_port); + furi_record_close(RECORD_GUI); + furi_record_close(RECORD_NOTIFICATION); + view_port_free(view_port); + furi_mutex_free(state->mutex); +free_and_exit: + furi_message_queue_free(event_queue); + //FURI_LOG_D(TAG, "Quitting game..."); + game_state_free(state); + free(state); + + return return_code; +} \ No newline at end of file diff --git a/applications/external/rubiks_cube_scrambler/application.fam b/applications/external/rubiks_cube_scrambler/application.fam index 131da4de72..8dee8e952d 100644 --- a/applications/external/rubiks_cube_scrambler/application.fam +++ b/applications/external/rubiks_cube_scrambler/application.fam @@ -17,4 +17,8 @@ App( stack_size=1 * 1024, fap_category="Misc", fap_icon="cube.png", + fap_author="@RaZeSloth", + fap_weburl="https://github.com/RaZeSloth/flipperzero-rubiks-cube-scrambler", + fap_version="1.0", + fap_description="App generates random moves to scramble a Rubik's cube.", ) diff --git a/applications/external/sam/application.fam b/applications/external/sam/application.fam index dc0641b3c1..8a6d70c07d 100644 --- a/applications/external/sam/application.fam +++ b/applications/external/sam/application.fam @@ -1,5 +1,5 @@ App( - appid="SAM", + appid="sam", name="SAM AYBABTU", apptype=FlipperAppType.EXTERNAL, entry_point="sam_app", @@ -10,11 +10,10 @@ App( stack_size=4 * 1024, order=20, fap_icon="music_10px.png", - fap_category="Music", - fap_icon_assets="icons", + fap_category="Media", ) App( - appid="SAM_YES", + appid="sam_yes", name="SAM YES", apptype=FlipperAppType.EXTERNAL, entry_point="sam_app_yes", @@ -25,11 +24,10 @@ App( stack_size=4 * 1024, order=20, fap_icon="music_10px.png", - fap_category="Music", - fap_icon_assets="icons", + fap_category="Media", ) App( - appid="SAM_NO", + appid="sam_no", name="SAM NO", apptype=FlipperAppType.EXTERNAL, entry_point="sam_app_no", @@ -40,11 +38,10 @@ App( stack_size=4 * 1024, order=20, fap_icon="music_10px.png", - fap_category="Music", - fap_icon_assets="icons", + fap_category="Media", ) App( - appid="SAM_WTF", + appid="sam_wtf", name="SAM WTF", apptype=FlipperAppType.EXTERNAL, entry_point="sam_app_wtf", @@ -55,6 +52,5 @@ App( stack_size=4 * 1024, order=20, fap_icon="music_10px.png", - fap_category="Music", - fap_icon_assets="icons", + fap_category="Media", ) diff --git a/applications/external/sam/icons/music_10px.png b/applications/external/sam/icons/music_10px.png deleted file mode 100644 index d41eb0db8c..0000000000 Binary files a/applications/external/sam/icons/music_10px.png and /dev/null differ diff --git a/applications/external/scorched_tanks/LICENSE b/applications/external/scorched_tanks/LICENSE new file mode 100644 index 0000000000..f288702d2f --- /dev/null +++ b/applications/external/scorched_tanks/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/applications/external/scorched_tanks/application.fam b/applications/external/scorched_tanks/application.fam new file mode 100644 index 0000000000..5bccead848 --- /dev/null +++ b/applications/external/scorched_tanks/application.fam @@ -0,0 +1,16 @@ +App( + appid="scorched_tanks", + name="Scorched Tanks", + apptype=FlipperAppType.EXTERNAL, + entry_point="scorched_tanks_game_app", + cdefines=["APP_SCORCHED_TANKS_GAME"], + requires=["gui"], + stack_size=1 * 1024, + order=100, + fap_icon="scorchedTanks_10px.png", + fap_category="Games", + fap_author="@jasniec", + fap_weburl="https://github.com/jasniec/flipper-scorched-tanks-game", + fap_version="1.0", + fap_description="A flipper zero game inspired by scorched earth.", +) diff --git a/applications/external/scorched_tanks/scorchedTanks_10px.png b/applications/external/scorched_tanks/scorchedTanks_10px.png new file mode 100644 index 0000000000..6e1ae4c04b Binary files /dev/null and b/applications/external/scorched_tanks/scorchedTanks_10px.png differ diff --git a/applications/external/scorched_tanks/scorched_tanks_game_app.c b/applications/external/scorched_tanks/scorched_tanks_game_app.c new file mode 100644 index 0000000000..6ebcf90302 --- /dev/null +++ b/applications/external/scorched_tanks/scorched_tanks_game_app.c @@ -0,0 +1,546 @@ +#include +#include +#include +#include +#include +#include +#include + +#define SCREEN_WIDTH 128 +#define SCREEN_HEIGHT 64 +#define PLAYER_INIT_LOCATION_X 20 +#define PLAYER_INIT_AIM 45 +#define PLAYER_INIT_POWER 50 +#define ENEMY_INIT_LOCATION_X 108 +#define TANK_BARREL_LENGTH 8 +#define GRAVITY_FORCE (double)0.5 +#define MIN_GROUND_HEIGHT 35 +#define MAX_GROUND_HEIGHT 55 +#define MAX_FIRE_POWER 100 +#define MIN_FIRE_POWER 0 +#define TANK_COLLIDER_SIZE 3 +#define MAX_WIND 10 +#define MAX_PLAYER_DIFF_X 20 +#define MAX_ENEMY_DIFF_X 20 + +// That's a filthy workaround but sin(player.aimAngle) breaks it all... If you're able to fix it, please do create a PR! +double scorched_tanks_sin[91] = { + 0.000, -0.017, -0.035, -0.052, -0.070, -0.087, -0.105, -0.122, -0.139, -0.156, -0.174, -0.191, + -0.208, -0.225, -0.242, -0.259, -0.276, -0.292, -0.309, -0.326, -0.342, -0.358, -0.375, -0.391, + -0.407, -0.423, -0.438, -0.454, -0.469, -0.485, -0.500, -0.515, -0.530, -0.545, -0.559, -0.574, + -0.588, -0.602, -0.616, -0.629, -0.643, -0.656, -0.669, -0.682, -0.695, -0.707, -0.719, -0.731, + -0.743, -0.755, -0.766, -0.777, -0.788, -0.799, -0.809, -0.819, -0.829, -0.839, -0.848, -0.857, + -0.866, -0.875, -0.883, -0.891, -0.899, -0.906, -0.914, -0.921, -0.927, -0.934, -0.940, -0.946, + -0.951, -0.956, -0.961, -0.966, -0.970, -0.974, -0.978, -0.982, -0.985, -0.988, -0.990, -0.993, + -0.995, -0.996, -0.998, -0.999, -0.999, -1.000, -1.000}; +double scorched_tanks_cos[91] = { + 1.000, 1.000, 0.999, 0.999, 0.998, 0.996, 0.995, 0.993, 0.990, 0.988, 0.985, 0.982, 0.978, + 0.974, 0.970, 0.966, 0.961, 0.956, 0.951, 0.946, 0.940, 0.934, 0.927, 0.921, 0.914, 0.906, + 0.899, 0.891, 0.883, 0.875, 0.866, 0.857, 0.848, 0.839, 0.829, 0.819, 0.809, 0.799, 0.788, + 0.777, 0.766, 0.755, 0.743, 0.731, 0.719, 0.707, 0.695, 0.682, 0.669, 0.656, 0.643, 0.629, + 0.616, 0.602, 0.588, 0.574, 0.559, 0.545, 0.530, 0.515, 0.500, 0.485, 0.469, 0.454, 0.438, + 0.423, 0.407, 0.391, 0.375, 0.358, 0.342, 0.326, 0.309, 0.292, 0.276, 0.259, 0.242, 0.225, + 0.208, 0.191, 0.174, 0.156, 0.139, 0.122, 0.105, 0.087, 0.070, 0.052, 0.035, 0.017, 0.000}; +double scorched_tanks_tan[91] = { + 0.000, -0.017, -0.035, -0.052, -0.070, -0.087, -0.105, -0.123, -0.141, -0.158, -0.176, + -0.194, -0.213, -0.231, -0.249, -0.268, -0.287, -0.306, -0.325, -0.344, -0.364, -0.384, + -0.404, -0.424, -0.445, -0.466, -0.488, -0.510, -0.532, -0.554, -0.577, -0.601, -0.625, + -0.649, -0.674, -0.700, -0.727, -0.754, -0.781, -0.810, -0.839, -0.869, -0.900, -0.932, + -0.966, -1.000, -1.036, -1.072, -1.111, -1.150, -1.192, -1.235, -1.280, -1.327, -1.376, + -1.428, -1.483, -1.540, -1.600, -1.664, -1.732, -1.804, -1.881, -1.963, -2.050, -2.144, + -2.246, -2.356, -2.475, -2.605, -2.747, -2.904, -3.078, -3.271, -3.487, -3.732, -4.011, + -4.331, -4.704, -5.144, -5.671, -6.313, -7.115, -8.144, -9.513, -11.429, -14.298, -19.077, + -28.627, -57.254, -90747.269}; +uint8_t scorched_tanks_ground_modifiers[SCREEN_WIDTH] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 28, 26, 24, 22, 20, + 18, 16, 14, 12, 10, 8, 6, 4, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +typedef struct { + // +-----x + // | + // | + // y + uint8_t x; + uint8_t y; +} Point; + +typedef struct { + // +-----x + // | + // | + // y + double x; + double y; +} PointDetailed; + +typedef struct { + uint8_t locationX; + uint8_t hp; + int aimAngle; + uint8_t firePower; +} Tank; + +typedef struct { + Point ground[SCREEN_WIDTH]; + Tank player; + Tank enemy; + bool isPlayerTurn; + bool isShooting; + int windSpeed; + Point trajectory[SCREEN_WIDTH]; + uint8_t trajectoryAnimationStep; + PointDetailed bulletPosition; + PointDetailed bulletVector; + FuriMutex* mutex; +} Game; + +typedef enum { + EventTypeTick, + EventTypeKey, +} EventType; + +typedef struct { + EventType type; + InputEvent input; +} ScorchedTanksEvent; + +int scorched_tanks_random(int min, int max) { + return min + rand() % ((max + 1) - min); +} + +void scorched_tanks_generate_ground(Game* game_state) { + int lastHeight = 45; + + for(uint8_t a = 0; a < SCREEN_WIDTH; a++) { + int diffHeight = scorched_tanks_random(-2, 3); + int changeLength = scorched_tanks_random(1, 6); + + if(diffHeight == 0) { + changeLength = 1; + } + + for(int b = 0; b < changeLength; b++) { + if(a + b < SCREEN_WIDTH) { + int index = a + b; + int newPoint = lastHeight + diffHeight; + newPoint = newPoint < MIN_GROUND_HEIGHT ? MIN_GROUND_HEIGHT : newPoint; + newPoint = newPoint > MAX_GROUND_HEIGHT ? MAX_GROUND_HEIGHT : newPoint; + game_state->ground[index].x = index; + game_state->ground[index].y = newPoint - scorched_tanks_ground_modifiers[a]; + lastHeight = newPoint; + } else { + a += b; + break; + } + } + + a += changeLength - 1; + } +} + +void scorched_tanks_init_game(Game* game_state) { + game_state->player.locationX = PLAYER_INIT_LOCATION_X + + scorched_tanks_random(0, MAX_PLAYER_DIFF_X) - + MAX_PLAYER_DIFF_X / 2; + game_state->player.aimAngle = PLAYER_INIT_AIM; + game_state->player.firePower = PLAYER_INIT_POWER; + game_state->enemy.aimAngle = PLAYER_INIT_AIM; + game_state->enemy.firePower = PLAYER_INIT_POWER; + game_state->enemy.locationX = + ENEMY_INIT_LOCATION_X + scorched_tanks_random(0, MAX_ENEMY_DIFF_X) - MAX_ENEMY_DIFF_X / 2; + game_state->isPlayerTurn = true; + + game_state->windSpeed = scorched_tanks_random(0, MAX_WIND); + + for(int x = 0; x < SCREEN_WIDTH; x++) { + game_state->trajectory[x].x = 0; + game_state->trajectory[x].y = 0; + } + + scorched_tanks_generate_ground(game_state); +} + +void scorched_tanks_calculate_trajectory(Game* game_state) { + if(game_state->isShooting) { + game_state->bulletVector.x += ((double)game_state->windSpeed - MAX_WIND / 2) / 40; + game_state->bulletVector.y += GRAVITY_FORCE; + + game_state->bulletPosition.x += game_state->bulletVector.x; + game_state->bulletPosition.y += game_state->bulletVector.y; + + int totalDistanceToEnemy = 100; + + if(game_state->isPlayerTurn) { + double distanceToEnemyX = game_state->enemy.locationX - game_state->bulletPosition.x; + double distanceToEnemyY = game_state->ground[game_state->enemy.locationX].y - + TANK_COLLIDER_SIZE - game_state->bulletPosition.y; + totalDistanceToEnemy = + sqrt(distanceToEnemyX * distanceToEnemyX + distanceToEnemyY * distanceToEnemyY); + } else { + double distanceToEnemyX = game_state->player.locationX - game_state->bulletPosition.x; + double distanceToEnemyY = game_state->ground[game_state->player.locationX].y - + TANK_COLLIDER_SIZE - game_state->bulletPosition.y; + totalDistanceToEnemy = + sqrt(distanceToEnemyX * distanceToEnemyX + distanceToEnemyY * distanceToEnemyY); + } + + if(totalDistanceToEnemy <= TANK_COLLIDER_SIZE) { + game_state->isShooting = false; + scorched_tanks_init_game(game_state); + game_state->isPlayerTurn = !game_state->isPlayerTurn; + return; + } + + if(game_state->bulletPosition.x > SCREEN_WIDTH || + game_state->bulletPosition.y > + game_state->ground[(int)round(game_state->bulletPosition.x)].y) { + game_state->isShooting = false; + game_state->bulletPosition.x = 0; + game_state->bulletPosition.y = 0; + game_state->windSpeed = scorched_tanks_random(0, MAX_WIND); + game_state->isPlayerTurn = !game_state->isPlayerTurn; + return; + } + + if(game_state->bulletPosition.y > 0) { + game_state->trajectory[game_state->trajectoryAnimationStep].x = + round(game_state->bulletPosition.x); + game_state->trajectory[game_state->trajectoryAnimationStep].y = + round(game_state->bulletPosition.y); + game_state->trajectoryAnimationStep++; + } + } +} + +static void scorched_tanks_draw_tank(Canvas* const canvas, uint8_t x, uint8_t y, bool isPlayer) { + uint8_t lineIndex = 0; + + if(isPlayer) { + // Draw tank base + canvas_draw_line(canvas, x - 3, y - lineIndex, x + 3, y - lineIndex); + lineIndex++; + canvas_draw_line(canvas, x - 4, y - lineIndex, x + 4, y - lineIndex); + lineIndex++; + canvas_draw_line(canvas, x - 4, y - lineIndex, x + 4, y - lineIndex); + lineIndex++; + + // draw turret + canvas_draw_line(canvas, x - 2, y - lineIndex, x + 1, y - lineIndex); + lineIndex++; + canvas_draw_line(canvas, x - 2, y - lineIndex, x, y - lineIndex); + lineIndex++; + } else { + // Draw tank base + canvas_draw_line(canvas, x - 3, y - lineIndex, x + 3, y - lineIndex); + lineIndex++; + canvas_draw_line(canvas, x - 4, y - lineIndex, x + 4, y - lineIndex); + lineIndex++; + canvas_draw_line(canvas, x - 4, y - lineIndex, x + 4, y - lineIndex); + lineIndex++; + + // draw turret + canvas_draw_line(canvas, x - 1, y - lineIndex, x + 2, y - lineIndex); + lineIndex++; + canvas_draw_line(canvas, x, y - lineIndex, x + 2, y - lineIndex); + lineIndex++; + } +} + +static void scorched_tanks_render_callback(Canvas* const canvas, void* ctx) { + furi_assert(ctx); + const Game* game_state = ctx; + furi_mutex_acquire(game_state->mutex, FuriWaitForever); + + canvas_draw_frame(canvas, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); + + canvas_set_color(canvas, ColorBlack); + + if(game_state->isShooting) { + canvas_draw_dot(canvas, game_state->bulletPosition.x, game_state->bulletPosition.y); + } + + for(int a = 1; a < SCREEN_WIDTH; a++) { + canvas_draw_line( + canvas, + game_state->ground[a - 1].x, + game_state->ground[a - 1].y, + game_state->ground[a].x, + game_state->ground[a].y); + + if(game_state->trajectory[a].y != 0) { + canvas_draw_dot(canvas, game_state->trajectory[a].x, game_state->trajectory[a].y); + } + } + + scorched_tanks_draw_tank( + canvas, + game_state->enemy.locationX, + game_state->ground[game_state->enemy.locationX].y - TANK_COLLIDER_SIZE, + true); + + scorched_tanks_draw_tank( + canvas, + game_state->player.locationX, + game_state->ground[game_state->player.locationX].y - TANK_COLLIDER_SIZE, + false); + + int aimX1 = 0; + int aimY1 = 0; + int aimX2 = 0; + int aimY2 = 0; + + if(game_state->isPlayerTurn) { + aimX1 = game_state->player.locationX; + aimY1 = game_state->ground[game_state->player.locationX].y - TANK_COLLIDER_SIZE; + + double sinFromAngle = scorched_tanks_sin[game_state->player.aimAngle]; + double cosFromAngle = scorched_tanks_cos[game_state->player.aimAngle]; + aimX2 = aimX1 + TANK_BARREL_LENGTH * cosFromAngle; + aimY2 = aimY1 + TANK_BARREL_LENGTH * sinFromAngle; + + aimX1 += 1; + aimX2 += 1; + } else { + aimX1 = game_state->enemy.locationX; + aimY1 = game_state->ground[game_state->enemy.locationX].y - TANK_COLLIDER_SIZE; + + double sinFromAngle = scorched_tanks_sin[game_state->enemy.aimAngle]; + double cosFromAngle = scorched_tanks_cos[game_state->enemy.aimAngle]; + aimX2 = aimX1 + TANK_BARREL_LENGTH * cosFromAngle; + aimY2 = aimY1 + TANK_BARREL_LENGTH * sinFromAngle; + + aimX2 = aimX1 - (aimX2 - aimX1); + + aimX1 -= 1; + aimX2 -= 1; + } + + canvas_draw_line(canvas, aimX1, aimY1 - 3, aimX2, aimY2 - 3); + + canvas_set_font(canvas, FontSecondary); + + char buffer2[18]; + snprintf(buffer2, sizeof(buffer2), "wind: %i", game_state->windSpeed - MAX_WIND / 2); + canvas_draw_str(canvas, 55, 10, buffer2); + + if(game_state->isPlayerTurn) { + canvas_draw_str(canvas, 93, 10, "player1"); + + char buffer[12]; + snprintf(buffer, sizeof(buffer), "a: %u", game_state->player.aimAngle); + canvas_draw_str(canvas, 2, 10, buffer); + + snprintf(buffer, sizeof(buffer), "p: %u", game_state->player.firePower); + canvas_draw_str(canvas, 27, 10, buffer); + } else { + canvas_draw_str(canvas, 93, 10, "player2"); + + char buffer[12]; + snprintf(buffer, sizeof(buffer), "a: %u", game_state->enemy.aimAngle); + canvas_draw_str(canvas, 2, 10, buffer); + + snprintf(buffer, sizeof(buffer), "p: %u", game_state->enemy.firePower); + canvas_draw_str(canvas, 27, 10, buffer); + } + + furi_mutex_release(game_state->mutex); +} + +static void scorched_tanks_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { + furi_assert(event_queue); + + ScorchedTanksEvent event = {.type = EventTypeKey, .input = *input_event}; + furi_message_queue_put(event_queue, &event, FuriWaitForever); +} + +static void scorched_tanks_update_timer_callback(FuriMessageQueue* event_queue) { + furi_assert(event_queue); + + ScorchedTanksEvent event = {.type = EventTypeTick}; + furi_message_queue_put(event_queue, &event, 0); +} + +static void scorched_tanks_increase_power(Game* game_state) { + if(game_state->player.firePower < MAX_FIRE_POWER && !game_state->isShooting) { + if(game_state->isPlayerTurn && game_state->player.firePower < MAX_FIRE_POWER) { + game_state->player.firePower++; + } + + if(!game_state->isPlayerTurn && game_state->enemy.firePower < MAX_FIRE_POWER) { + game_state->enemy.firePower++; + } + } +} + +static void scorched_tanks_decrease_power(Game* game_state) { + if(game_state->player.firePower > MIN_FIRE_POWER && !game_state->isShooting) { + if(game_state->isPlayerTurn && game_state->player.firePower > MIN_FIRE_POWER) { + game_state->player.firePower--; + } + + if(!game_state->isPlayerTurn && game_state->enemy.firePower > MIN_FIRE_POWER) { + game_state->enemy.firePower--; + } + } +} + +static void scorched_tanks_aim_up(Game* game_state) { + if(!game_state->isShooting) { + if(game_state->isPlayerTurn && game_state->player.aimAngle < 90) { + game_state->player.aimAngle++; + } + + if(!game_state->isPlayerTurn && game_state->enemy.aimAngle < 90) { + game_state->enemy.aimAngle++; + } + } +} + +static void scorched_tanks_aim_down(Game* game_state) { + if(game_state->player.aimAngle > 0 && !game_state->isShooting) { + if(game_state->isPlayerTurn) { + game_state->player.aimAngle--; + } else { + game_state->enemy.aimAngle--; + } + } +} + +const NotificationSequence sequence_long_vibro = { + &message_vibro_on, + &message_delay_500, + &message_vibro_off, + NULL, +}; + +static void scorched_tanks_fire(Game* game_state) { + if(!game_state->isShooting) { + if(game_state->isPlayerTurn) { + double sinFromAngle = scorched_tanks_sin[game_state->player.aimAngle]; + double cosFromAngle = scorched_tanks_cos[game_state->player.aimAngle]; + uint8_t aimX1 = game_state->player.locationX; + uint8_t aimY1 = + game_state->ground[game_state->player.locationX].y - TANK_COLLIDER_SIZE; + int aimX2 = aimX1 + TANK_BARREL_LENGTH * cosFromAngle; + int aimY2 = aimY1 + TANK_BARREL_LENGTH * sinFromAngle; + game_state->bulletPosition.x = aimX2; + game_state->bulletPosition.y = aimY2; + game_state->bulletVector.x = scorched_tanks_cos[game_state->player.aimAngle] * + ((double)game_state->player.firePower / 10); + game_state->bulletVector.y = scorched_tanks_sin[game_state->player.aimAngle] * + ((double)game_state->player.firePower / 10); + } else { + double sinFromAngle = scorched_tanks_sin[game_state->enemy.aimAngle]; + double cosFromAngle = scorched_tanks_cos[game_state->enemy.aimAngle]; + uint8_t aimX1 = game_state->enemy.locationX; + uint8_t aimY1 = game_state->ground[game_state->enemy.locationX].y - TANK_COLLIDER_SIZE; + int aimX2 = aimX1 + TANK_BARREL_LENGTH * cosFromAngle; + int aimY2 = aimY1 + TANK_BARREL_LENGTH * sinFromAngle; + aimX2 = aimX1 - (aimX2 - aimX1); + + game_state->bulletPosition.x = aimX2; + game_state->bulletPosition.y = aimY2; + game_state->bulletVector.x = -scorched_tanks_cos[game_state->enemy.aimAngle] * + ((double)game_state->enemy.firePower / 10); + game_state->bulletVector.y = scorched_tanks_sin[game_state->enemy.aimAngle] * + ((double)game_state->enemy.firePower / 10); + } + + game_state->trajectoryAnimationStep = 0; + + for(int x = 0; x < SCREEN_WIDTH; x++) { + game_state->trajectory[x].x = 0; + game_state->trajectory[x].y = 0; + } + + game_state->isShooting = true; + + NotificationApp* notification = furi_record_open("notification"); + notification_message(notification, &sequence_long_vibro); + notification_message(notification, &sequence_blink_white_100); + furi_record_close("notification"); + } +} + +int32_t scorched_tanks_game_app(void* p) { + UNUSED(p); + srand(DWT->CYCCNT); + + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(ScorchedTanksEvent)); + + Game* game_state = malloc(sizeof(Game)); + scorched_tanks_init_game(game_state); + + game_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + if(!game_state->mutex) { + FURI_LOG_E("ScorchedTanks", "cannot create mutex\r\n"); + furi_message_queue_free(event_queue); + free(game_state); + return 255; + } + + ViewPort* view_port = view_port_alloc(); + view_port_draw_callback_set(view_port, scorched_tanks_render_callback, game_state); + view_port_input_callback_set(view_port, scorched_tanks_input_callback, event_queue); + + FuriTimer* timer = + furi_timer_alloc(scorched_tanks_update_timer_callback, FuriTimerTypePeriodic, event_queue); + furi_timer_start(timer, 2000); + + // Open GUI and register view_port + Gui* gui = furi_record_open(RECORD_GUI); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + ScorchedTanksEvent event; + for(bool processing = true; processing;) { + furi_message_queue_get(event_queue, &event, 50); + furi_mutex_acquire(game_state->mutex, FuriWaitForever); + + if(event.type == EventTypeKey) { // && game->isPlayerTurn + if(event.input.type == InputTypeRepeat || event.input.type == InputTypeShort) { + switch(event.input.key) { + case InputKeyUp: + scorched_tanks_aim_up(game_state); + break; + case InputKeyDown: + scorched_tanks_aim_down(game_state); + break; + case InputKeyRight: + scorched_tanks_increase_power(game_state); + break; + case InputKeyLeft: + scorched_tanks_decrease_power(game_state); + break; + case InputKeyOk: + scorched_tanks_fire(game_state); + break; + case InputKeyBack: + processing = false; + break; + default: + break; + } + } + } else if(event.type == EventTypeTick) { + scorched_tanks_calculate_trajectory(game_state); + } + + view_port_update(view_port); + furi_mutex_release(game_state->mutex); + } + + furi_timer_free(timer); + view_port_enabled_set(view_port, false); + gui_remove_view_port(gui, view_port); + furi_record_close(RECORD_GUI); + view_port_free(view_port); + furi_message_queue_free(event_queue); + furi_mutex_free(game_state->mutex); + free(game_state); + + return 0; +} diff --git a/applications/external/sentry_safe/application.fam b/applications/external/sentry_safe/application.fam index 5e5a6ba11d..73264cf015 100644 --- a/applications/external/sentry_safe/application.fam +++ b/applications/external/sentry_safe/application.fam @@ -8,4 +8,7 @@ App( order=40, fap_icon="safe_10px.png", fap_category="GPIO", + fap_author="@H4ckd4ddy & @xMasterX (ported to latest firmware)", + fap_version="1.0", + fap_description="App exploiting vulnerability to open any Sentry Safe and Master Lock electronic safe without any pin code via UART pins.", ) diff --git a/applications/external/sentry_safe/sentry_safe.c b/applications/external/sentry_safe/sentry_safe.c index a0667b6861..9c7445463c 100644 --- a/applications/external/sentry_safe/sentry_safe.c +++ b/applications/external/sentry_safe/sentry_safe.c @@ -85,7 +85,7 @@ int32_t sentry_safe_app(void* p) { UNUSED(p); FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(Event)); - DOLPHIN_DEED(DolphinDeedPluginStart); + dolphin_deed(DolphinDeedPluginStart); SentryState* sentry_state = malloc(sizeof(SentryState)); diff --git a/applications/external/signal_generator/application.fam b/applications/external/signal_generator/application.fam index 002651da41..a7b93b817d 100644 --- a/applications/external/signal_generator/application.fam +++ b/applications/external/signal_generator/application.fam @@ -8,5 +8,4 @@ App( order=50, fap_icon="signal_gen_10px.png", fap_category="GPIO", - fap_icon_assets="icons", ) diff --git a/applications/external/signal_generator/icons/SmallArrowDown_3x5.png b/applications/external/signal_generator/icons/SmallArrowDown_3x5.png deleted file mode 100644 index 1912e5d246..0000000000 Binary files a/applications/external/signal_generator/icons/SmallArrowDown_3x5.png and /dev/null differ diff --git a/applications/external/signal_generator/icons/SmallArrowUp_3x5.png b/applications/external/signal_generator/icons/SmallArrowUp_3x5.png deleted file mode 100644 index 9c6242078d..0000000000 Binary files a/applications/external/signal_generator/icons/SmallArrowUp_3x5.png and /dev/null differ diff --git a/applications/external/signal_generator/scenes/signal_gen_scene_pwm.c b/applications/external/signal_generator/scenes/signal_gen_scene_pwm.c index 7ac3fadda4..1cadb3a1a4 100644 --- a/applications/external/signal_generator/scenes/signal_gen_scene_pwm.c +++ b/applications/external/signal_generator/scenes/signal_gen_scene_pwm.c @@ -33,7 +33,13 @@ void signal_gen_scene_pwm_on_enter(void* context) { signal_gen_pwm_set_callback(app->pwm_view, signal_gen_pwm_callback, app); signal_gen_pwm_set_params(app->pwm_view, 0, DEFAULT_FREQ, DEFAULT_DUTY); - furi_hal_pwm_start(pwm_ch_id[0], DEFAULT_FREQ, DEFAULT_DUTY); + + if(!furi_hal_pwm_is_running(pwm_ch_id[0])) { + furi_hal_pwm_start(pwm_ch_id[0], DEFAULT_FREQ, DEFAULT_DUTY); + } else { + furi_hal_pwm_stop(pwm_ch_id[0]); + furi_hal_pwm_start(pwm_ch_id[0], DEFAULT_FREQ, DEFAULT_DUTY); + } } bool signal_gen_scene_pwm_on_event(void* context, SceneManagerEvent event) { @@ -46,8 +52,18 @@ bool signal_gen_scene_pwm_on_event(void* context, SceneManagerEvent event) { furi_hal_pwm_set_params(app->pwm_ch, app->pwm_freq, app->pwm_duty); } else if(event.event == SignalGenPwmEventChannelChange) { consumed = true; - furi_hal_pwm_stop(app->pwm_ch_prev); - furi_hal_pwm_start(app->pwm_ch, app->pwm_freq, app->pwm_duty); + // Stop previous channel PWM + if(furi_hal_pwm_is_running(app->pwm_ch_prev)) { + furi_hal_pwm_stop(app->pwm_ch_prev); + } + + // Start PWM and restart if it was starter already + if(furi_hal_pwm_is_running(app->pwm_ch)) { + furi_hal_pwm_stop(app->pwm_ch); + furi_hal_pwm_start(app->pwm_ch, app->pwm_freq, app->pwm_duty); + } else { + furi_hal_pwm_start(app->pwm_ch, app->pwm_freq, app->pwm_duty); + } } } return consumed; @@ -56,5 +72,8 @@ bool signal_gen_scene_pwm_on_event(void* context, SceneManagerEvent event) { void signal_gen_scene_pwm_on_exit(void* context) { SignalGenApp* app = context; variable_item_list_reset(app->var_item_list); - furi_hal_pwm_stop(app->pwm_ch); + + if(furi_hal_pwm_is_running(app->pwm_ch)) { + furi_hal_pwm_stop(app->pwm_ch); + } } diff --git a/applications/external/signal_generator/views/signal_gen_pwm.c b/applications/external/signal_generator/views/signal_gen_pwm.c index d625ed5a9e..84dc63f920 100644 --- a/applications/external/signal_generator/views/signal_gen_pwm.c +++ b/applications/external/signal_generator/views/signal_gen_pwm.c @@ -1,7 +1,7 @@ #include "../signal_gen_app_i.h" #include #include -#include +#include typedef enum { LineIndexChannel, diff --git a/applications/external/simonsays/LICENSE b/applications/external/simonsays/LICENSE new file mode 100644 index 0000000000..f288702d2f --- /dev/null +++ b/applications/external/simonsays/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/applications/external/simonsays/application.fam b/applications/external/simonsays/application.fam new file mode 100644 index 0000000000..d8df4793f6 --- /dev/null +++ b/applications/external/simonsays/application.fam @@ -0,0 +1,15 @@ +App( + appid="simon_says", # Must be unique + name="Simon Says", # Displayed in UI + apptype=FlipperAppType.EXTERNAL, + entry_point="simon_says_app_entry", + stack_size=2 * 1024, + fap_category="Games", + # Optional values + # fap_version=(0, 1), # (major, minor) + fap_icon="simon_says.png", # 10x10 1-bit PNG + fap_description="A Simon Says Game", + fap_author="@SimplyMinimal & @ShehabAttia96", + fap_weburl="https://github.com/SimplyMinimal/FlipperZero-SimonSays", + fap_icon_assets="images", # Image assets to compile for this application +) diff --git a/applications/external/bpmtapper/icons/DolphinCommon_56x48.png b/applications/external/simonsays/images/DolphinLeft_56x48.png similarity index 100% rename from applications/external/bpmtapper/icons/DolphinCommon_56x48.png rename to applications/external/simonsays/images/DolphinLeft_56x48.png diff --git a/applications/external/nfc_magic/assets/DolphinNice_96x59.png b/applications/external/simonsays/images/DolphinNiceLeft_96x59.png similarity index 100% rename from applications/external/nfc_magic/assets/DolphinNice_96x59.png rename to applications/external/simonsays/images/DolphinNiceLeft_96x59.png diff --git a/applications/external/simonsays/images/DolphinNiceRight_96x59.png b/applications/external/simonsays/images/DolphinNiceRight_96x59.png new file mode 100644 index 0000000000..51b91437a5 Binary files /dev/null and b/applications/external/simonsays/images/DolphinNiceRight_96x59.png differ diff --git a/applications/external/simonsays/images/DolphinRight_56x48.png b/applications/external/simonsays/images/DolphinRight_56x48.png new file mode 100644 index 0000000000..a0fcba2183 Binary files /dev/null and b/applications/external/simonsays/images/DolphinRight_56x48.png differ diff --git a/applications/external/simonsays/images/DolphinTalking_59x63.png b/applications/external/simonsays/images/DolphinTalking_59x63.png new file mode 100644 index 0000000000..46f559f65f Binary files /dev/null and b/applications/external/simonsays/images/DolphinTalking_59x63.png differ diff --git a/applications/external/simonsays/images/board.png b/applications/external/simonsays/images/board.png new file mode 100644 index 0000000000..7640db5946 Binary files /dev/null and b/applications/external/simonsays/images/board.png differ diff --git a/applications/external/simonsays/images/down.png b/applications/external/simonsays/images/down.png new file mode 100644 index 0000000000..c4451006f3 Binary files /dev/null and b/applications/external/simonsays/images/down.png differ diff --git a/applications/external/simonsays/images/left.png b/applications/external/simonsays/images/left.png new file mode 100644 index 0000000000..1eb6b58ee0 Binary files /dev/null and b/applications/external/simonsays/images/left.png differ diff --git a/applications/external/simonsays/images/right.png b/applications/external/simonsays/images/right.png new file mode 100644 index 0000000000..a5c85c2983 Binary files /dev/null and b/applications/external/simonsays/images/right.png differ diff --git a/applications/external/simonsays/images/up.png b/applications/external/simonsays/images/up.png new file mode 100644 index 0000000000..af67513274 Binary files /dev/null and b/applications/external/simonsays/images/up.png differ diff --git a/applications/external/simonsays/simon_says.c b/applications/external/simonsays/simon_says.c new file mode 100644 index 0000000000..724b1e6072 --- /dev/null +++ b/applications/external/simonsays/simon_says.c @@ -0,0 +1,661 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // Header-file for boolean data-type. +#include +#include + +/* generated by fbt from .png files in images folder */ +#include +#include + +#define TAG "Simon" // Used for logging +#define DEBUG_MSG 1 +#define SCREEN_XRES 128 +#define SCREEN_YRES 64 +#define BOARD_X 72 // Used for board placement +#define BOARD_Y 8 +#define GAME_START_LIVES 3 +#define SAVING_FILENAME APP_DATA_PATH("game_simon_says.save") + +// Define Notes +// Shamelessly stolen from Ocarina application +// https://github.com/invalidna-me/flipperzero-ocarina +#define NOTE_UP 587.33f +#define NOTE_LEFT 493.88f +#define NOTE_RIGHT 440.00f +#define NOTE_DOWN 349.23 +#define NOTE_OK 293.66f + +/* ============================ Data structures ============================= */ + +typedef enum game_state { preloading, mainMenu, inGame, gameOver, gameVictory } game_state; + +typedef enum difficulty_mode { normal, hard } difficulty_mode; + +typedef enum shape_names { up, down, left, right, number_of_shapes } Direction; + +typedef enum currently_playing { simon, player } currently_playing; + +typedef struct { + /* Game state. */ + enum game_state gameState; // This is the current game state + bool gameover; /* if true then switch to the game over state */ + bool is_wrong_direction; /* Is the last direction wrong? */ + enum currently_playing activePlayer; // This is used to track who is playing at the moment + uint32_t lives; /* Number of lives in the current game. */ + + enum difficulty_mode difficultyMode; // This is the difficulty mode for the current game + bool sound_enabled; // This is the sound enabled flag for the current game + float volume; // This is the volume for the current game + + /* Handle Score */ + int currentScore; // This is the score for the current + int highScore; /* Highscore. Shown on Game Over Screen */ + bool is_new_highscore; /* Is the last score a new highscore? */ + + /* Handle Shape Display */ + uint32_t numberOfMillisecondsBeforeShapeDisappears; // This defines the speed of the game + enum shape_names simonMoves[1000]; // Store the sequence of shapes that Simon plays + enum shape_names selectedShape; // This is used to track the shape that the player has selected + bool set_board_neutral; // This is used to track if the board should be neutral or not + int moveIndex; // This is used to track the current move in the sequence + + uint32_t last_button_press_tick; + NotificationApp* notification; + FuriMutex* mutex; +} SimonData; + +/* ============================== Sequences ============================== */ + +const NotificationSequence sequence_wrong_move = { + &message_red_255, + + &message_vibro_on, + // &message_note_g5, // Play sound but currently disabled + &message_delay_25, + // &message_note_e5, + &message_vibro_off, + &message_sound_off, + NULL, +}; + +const NotificationSequence sequence_player_submit_move = { + &message_vibro_on, + // &message_note_g5, // Play sound but currently disabled. Need On/Off menu setting + &message_delay_10, + &message_delay_1, + &message_delay_1, + &message_delay_1, + &message_delay_1, + &message_delay_1, + + // &message_note_e5, + &message_vibro_off, + &message_sound_off, + NULL, +}; + +const NotificationSequence sequence_up = { + // &message_vibro_on, + &message_note_g4, + &message_delay_100, + // &message_vibro_off, + &message_sound_off, + NULL, +}; + +const NotificationSequence sequence_down = { + // &message_vibro_on, + &message_note_c3, + &message_delay_100, + // &message_vibro_off, + &message_sound_off, + NULL, +}; + +const NotificationSequence sequence_left = { + // &message_vibro_on, + &message_note_e3, + &message_delay_100, + // &message_vibro_off, + &message_sound_off, + NULL, +}; + +const NotificationSequence sequence_right = { + // &message_vibro_on, + &message_note_g3, + &message_delay_100, + // &message_vibro_off, + &message_sound_off, + NULL, +}; + +// Indicate that it's Simon's turn +const NotificationSequence sequence_simon_is_playing = { + &message_red_255, + &message_do_not_reset, + NULL, +}; + +// Indicate that it's the Player's turn +const NotificationSequence sequence_player_is_playing = { + &message_red_0, + &message_do_not_reset, + NULL, +}; + +const NotificationSequence sequence_cleanup = { + &message_red_0, + &message_green_0, + &message_blue_0, + &message_sound_off, + &message_vibro_off, + NULL, +}; + +/* ============================ 2D drawing ================================== */ + +/* Display remaining lives in the center of the board */ +void draw_remaining_lives(Canvas* canvas, const SimonData* simon_state) { + // Convert score to string + // int length = snprintf(NULL, 0, "%lu", simon_state->lives); + // char* str_lives_remaining = malloc(length + 1); + // snprintf(str_lives_remaining, length + 1, "%lu", simon_state->lives); + + // TODO: Make it a Simon Says icon on top right + canvas_set_color(canvas, ColorBlack); + canvas_set_font(canvas, FontSecondary); + int x = SCREEN_XRES - 6; + int lives = simon_state->lives; + while(lives--) { + canvas_draw_str(canvas, x, 8, "*"); + x -= 7; + } +} + +void draw_current_score(Canvas* canvas, const SimonData* simon_data) { + /* Draw Game Score. */ + canvas_set_color(canvas, ColorXOR); + canvas_set_font(canvas, FontSecondary); + char str_score[32]; + snprintf(str_score, sizeof(str_score), "%i", simon_data->currentScore); + canvas_draw_str_aligned(canvas, SCREEN_XRES / 2 + 4, 2, AlignCenter, AlignTop, str_score); +} + +void play_sound_up(const SimonData* app) { + if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(30)) { + furi_hal_speaker_start(NOTE_UP, app->volume); + } +} + +void play_sound_down(const SimonData* app) { + if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(30)) { + furi_hal_speaker_start(NOTE_DOWN, app->volume); + } +} + +void play_sound_left(const SimonData* app) { + if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(30)) { + furi_hal_speaker_start(NOTE_LEFT, app->volume); + } +} + +void play_sound_right(const SimonData* app) { + if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(30)) { + furi_hal_speaker_start(NOTE_RIGHT, app->volume); + } +} + +void stop_sound() { + if(furi_hal_speaker_is_mine()) { + furi_hal_speaker_stop(); + furi_hal_speaker_release(); + } +} + +/* Main Render Function */ +void simon_draw_callback(Canvas* canvas, void* ctx) { + furi_assert(ctx); + const SimonData* simon_state = ctx; + furi_mutex_acquire(simon_state->mutex, FuriWaitForever); + + canvas_clear(canvas); + + // ######################### Main Menu ######################### + // Show Main Menu + if(simon_state->gameState == mainMenu) { + // Draw border frame + canvas_draw_frame(canvas, 1, 1, SCREEN_XRES - 1, SCREEN_YRES - 1); // Border + + // Draw Simon text banner + canvas_set_font(canvas, FontSecondary); + canvas_set_color(canvas, ColorBlack); + canvas_draw_str_aligned( + canvas, + SCREEN_XRES / 2, + SCREEN_YRES / 2 - 4, + AlignCenter, + AlignCenter, + "Welcome to Simon Says"); + + // Display Press OK to start below title + canvas_set_color(canvas, ColorXOR); + canvas_draw_str_aligned( + canvas, + SCREEN_XRES / 2, + SCREEN_YRES / 2 + 10, + AlignCenter, + AlignCenter, + "Press OK to start"); + } + + // ######################### in Game ######################### + //@todo Render Callback + // We're in an active game + if(simon_state->gameState == inGame) { + // Draw Current Score + draw_current_score(canvas, simon_state); + + // Draw Lives + draw_remaining_lives(canvas, simon_state); + + // Draw Simon Pose + if(simon_state->activePlayer == player) { + // Player's turn + canvas_draw_icon(canvas, 0, 4, &I_DolphinWait_61x59); + } else { + // Simon's turn + canvas_draw_icon(canvas, 0, 4, &I_DolphinTalking_59x63); + } + + if(simon_state->set_board_neutral) { + // Draw Neutral Board + canvas_draw_icon(canvas, BOARD_X, BOARD_Y, &I_board); // Draw Board + + // Stop Sound TODO: Move this to a better place + //@todo Sound + stop_sound(); + } else { + switch(simon_state->selectedShape) { + case up: + canvas_draw_icon(canvas, BOARD_X, BOARD_Y, &I_up); // Draw Up + play_sound_up(simon_state); + break; + case down: + canvas_draw_icon(canvas, BOARD_X, BOARD_Y, &I_down); // Draw Down + play_sound_down(simon_state); + break; + case left: + canvas_draw_icon(canvas, BOARD_X, BOARD_Y, &I_left); // Draw Left + play_sound_left(simon_state); + break; + case right: + canvas_draw_icon(canvas, BOARD_X, BOARD_Y, &I_right); // Draw Right + play_sound_right(simon_state); + break; + default: + if(DEBUG_MSG) + FURI_LOG_E( + TAG, "Invalid shape: %d", simon_state->simonMoves[simon_state->moveIndex]); + break; + } + } + } + + // ######################### Game Over ######################### + if(simon_state->gameState == gameOver) { + stop_sound(); //TODO: Make a game over sequence + canvas_set_color(canvas, ColorXOR); + canvas_set_font(canvas, FontPrimary); + + // TODO: if new highscore, display blinking "New High Score" + // Display High Score Text + if(simon_state->is_new_highscore) { + canvas_draw_str_aligned( + canvas, SCREEN_XRES / 2, 6, AlignCenter, AlignTop, "New High Score!"); + } else { + canvas_draw_str_aligned( + canvas, SCREEN_XRES / 2, 6, AlignCenter, AlignTop, "High Score"); + } + + // Convert highscore to string + int length = snprintf(NULL, 0, "%i", simon_state->highScore); + char* str_high_score = malloc(length + 1); + snprintf(str_high_score, length + 1, "%i", simon_state->highScore); + + // Display High Score + canvas_draw_str_aligned( + canvas, SCREEN_XRES / 2, 22, AlignCenter, AlignCenter, str_high_score); + free(str_high_score); + + // Display Game Over + canvas_draw_str_aligned( + canvas, SCREEN_XRES / 2, SCREEN_YRES / 2 + 2, AlignCenter, AlignCenter, "GAME OVER"); + + // Display Press OK to restart below title + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned( + canvas, + SCREEN_XRES / 2, + SCREEN_YRES / 2 + 15, + AlignCenter, + AlignCenter, + "Press OK to restart"); + } + + // ######################### Victory ######################### + //Player Beat Simon beyond limit! A word record holder here! + //TODO + + //release the mutex + furi_mutex_release(simon_state->mutex); +} + +/* ======================== Input Handling ============================== */ + +void simon_input_callback(InputEvent* input_event, void* ctx) { + furi_assert(ctx); + FuriMessageQueue* event_queue = ctx; + furi_message_queue_put(event_queue, input_event, FuriWaitForever); +} + +/* ======================== Simon Game Engine ======================== */ + +bool load_game(SimonData* app) { + Storage* storage = furi_record_open(RECORD_STORAGE); + storage_common_migrate(storage, EXT_PATH("apps/Games/game_simon_says.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)) { + if(storage_file_size(file) > sizeof(SimonData)) { + storage_simply_remove(storage, SAVING_FILENAME); + FURI_LOG_E( + TAG, "Error: file is larger than the data structure! The file has been deleted."); + } else { + bytes_readed = storage_file_read(file, app, sizeof(SimonData)); + } + storage_file_close(file); + storage_file_free(file); + } + + furi_record_close(RECORD_STORAGE); + return bytes_readed == sizeof(SimonData); +} + +void save_game(SimonData* app) { + Storage* storage = furi_record_open(RECORD_STORAGE); + + File* file = storage_file_alloc(storage); + if(storage_file_open(file, SAVING_FILENAME, FSAM_WRITE, FSOM_CREATE_ALWAYS)) { + storage_file_write(file, app, sizeof(SimonData)); + } + storage_file_close(file); + storage_file_free(file); + + furi_record_close(RECORD_STORAGE); +} + +int getRandomIntInRange(int lower, int upper) { + return (rand() % (upper - lower + 1)) + lower; +} + +void play_sound_sequence_correct() { + notification_message(furi_record_open(RECORD_NOTIFICATION), &sequence_success); +} + +void play_sound_wrong_move() { + //TODO: play wrong sound: Try sequence_audiovisual_alert + notification_message(furi_record_open(RECORD_NOTIFICATION), &sequence_error); +} + +/* Restart game and give player a chance to try again on same sequence */ +// @todo restartGame +void resetGame(SimonData* app) { + app->moveIndex = 0; + app->numberOfMillisecondsBeforeShapeDisappears = 500; + app->activePlayer = simon; + app->is_wrong_direction = false; + app->last_button_press_tick = 0; + app->set_board_neutral = true; + app->activePlayer = simon; +} + +/* Set gameover state */ +void game_over(SimonData* app) { + if(app->is_new_highscore) save_game(app); // Save highscore but only on change + app->gameover = true; + app->lives = GAME_START_LIVES; // Show 3 lives in game over screen to match new game start + app->gameState = gameOver; +} + +/* Called after gameover to restart the game. This function + * also calls restart_game(). */ +void restart_game_after_gameover(SimonData* app) { + app->volume = 1.0f; //TODO: make this a setting + app->gameState = inGame; + app->gameover = false; + app->currentScore = 0; + app->is_new_highscore = false; + app->lives = GAME_START_LIVES; + app->simonMoves[0] = rand() % number_of_shapes; + resetGame(app); +} + +void addNewSimonMove(int addAtIndex, SimonData* app) { + app->simonMoves[addAtIndex] = getRandomIntInRange(0, 3); +} + +void startNewRound(SimonData* app) { + addNewSimonMove(app->currentScore, app); + app->moveIndex = 0; + app->activePlayer = simon; +} + +void onPlayerAnsweredCorrect(SimonData* app) { + app->moveIndex++; +} + +void onPlayerAnsweredWrong(SimonData* app) { + if(app->lives > 0) { + app->lives--; + + // Play the wrong sound + if(app->sound_enabled) { + play_sound_wrong_move(); + } + resetGame(app); + } else { + // The player has no lives left + // Game over + game_over(app); + //TODO: Play unique game over sound + } +} + +bool isRoundComplete(SimonData* app) { + return app->moveIndex == app->currentScore; +} + +enum shape_names getCurrentSimonMove(SimonData* app) { + return app->simonMoves[app->moveIndex]; +} + +void onPlayerSelectedShapeCallback(enum shape_names shape, SimonData* app) { + if(shape == getCurrentSimonMove(app)) { + onPlayerAnsweredCorrect(app); + } else { + onPlayerAnsweredWrong(app); + } +} + +//@todo gametick +void game_tick(SimonData* simon_state) { + if(simon_state->gameState == inGame) { + if(simon_state->activePlayer == simon) { + // ############### Simon Turn ############### + notification_message(simon_state->notification, &sequence_simon_is_playing); + + //@todo Gameplay + if(simon_state->set_board_neutral) { + if(simon_state->moveIndex < simon_state->currentScore) { + simon_state->selectedShape = getCurrentSimonMove(simon_state); + simon_state->set_board_neutral = false; + simon_state->moveIndex++; + } else { + simon_state->activePlayer = player; + simon_state->set_board_neutral = true; + simon_state->moveIndex = 0; + } + } else { + simon_state->set_board_neutral = true; + } + } else { + // ############### Player Turn ############### + notification_message(simon_state->notification, &sequence_player_is_playing); + + // It's Player's Turn + if(isRoundComplete(simon_state)) { + simon_state->activePlayer = simon; + simon_state->currentScore++; + // app->numberOfMillisecondsBeforeShapeDisappears -= 50; + //TODO: Hacky way of handling highscore by subtracting 1 to account for the first move + if(simon_state->currentScore - 1 > simon_state->highScore) { + simon_state->highScore = simon_state->currentScore - 1; + simon_state->is_new_highscore = true; + } + if(simon_state->sound_enabled) { + play_sound_sequence_correct(); + } + startNewRound(simon_state); + } + } + } +} + +/* ======================== Main Entry Point ============================== */ + +int32_t simon_says_app_entry(void* p) { + UNUSED(p); + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); + + SimonData* simon_state = malloc(sizeof(SimonData)); + + simon_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + if(!simon_state->mutex) { + FURI_LOG_E(TAG, "cannot create mutex\r\n"); + free(simon_state); + return -1; + } + + // Configure view port + ViewPort* view_port = view_port_alloc(); + view_port_draw_callback_set(view_port, simon_draw_callback, simon_state); + view_port_input_callback_set(view_port, simon_input_callback, event_queue); + + // Register view port in GUI + Gui* gui = furi_record_open(RECORD_GUI); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); + simon_state->notification = notification; + + InputEvent input; + + // Show Main Menu Screen + //load_game(simon_state); + restart_game_after_gameover(simon_state); + simon_state->gameState = mainMenu; + + while(true) { + game_tick(simon_state); + + FuriStatus q_status = furi_message_queue_get( + event_queue, &input, simon_state->numberOfMillisecondsBeforeShapeDisappears); + furi_mutex_acquire(simon_state->mutex, FuriWaitForever); + + if(q_status == FuriStatusOk) { + //FURI_LOG_D(TAG, "Got input event: %d", input.key); + //break out of the loop if the back key is pressed + if(input.key == InputKeyBack && input.type == InputTypeLong) { + // Save high score before quitting + //if(simon_state->is_new_highscore) { + // save_game(simon_state); + //} + break; + } + + //@todo Set Game States + if(input.key == InputKeyOk && simon_state->gameState != inGame) { + restart_game_after_gameover(simon_state); + // Set Simon Board state + startNewRound(simon_state); + view_port_update(view_port); + } + + // Keep LED on if it is Simon's turn + if(simon_state->activePlayer == player) { + notification_message(notification, &sequence_player_is_playing); + + if(input.type == InputTypePress) { + simon_state->set_board_neutral = false; + + switch(input.key) { + case InputKeyUp: + simon_state->selectedShape = up; + onPlayerSelectedShapeCallback(up, simon_state); + break; + case InputKeyDown: + simon_state->selectedShape = down; + onPlayerSelectedShapeCallback(down, simon_state); + break; + case InputKeyLeft: + simon_state->selectedShape = left; + onPlayerSelectedShapeCallback(left, simon_state); + break; + case InputKeyRight: + simon_state->selectedShape = right; + onPlayerSelectedShapeCallback(right, simon_state); + break; + default: + simon_state->set_board_neutral = true; + break; + } + } else { + //FURI_LOG_D(TAG, "Input type is not short"); + simon_state->set_board_neutral = true; + } + } + } + + // @todo Animation Loop for debug + // if(simon_state->gameState == inGame && simon_state->activePlayer == simon) { + // simon_state->currentScore++; + // simon_state->set_board_neutral = !simon_state->set_board_neutral; + // } + + view_port_update(view_port); + furi_mutex_release(simon_state->mutex); + } + + stop_sound(); + notification_message(notification, &sequence_cleanup); + gui_remove_view_port(gui, view_port); + view_port_free(view_port); + furi_message_queue_free(event_queue); + furi_mutex_free(simon_state->mutex); + furi_record_close(RECORD_NOTIFICATION); + furi_record_close(RECORD_GUI); + free(simon_state); + + return 0; +} diff --git a/applications/external/simonsays/simon_says.png b/applications/external/simonsays/simon_says.png new file mode 100644 index 0000000000..6fe9ba3e6f Binary files /dev/null and b/applications/external/simonsays/simon_says.png differ diff --git a/applications/external/slots/application.fam b/applications/external/slots/application.fam index 27337ca53b..7821bdc919 100644 --- a/applications/external/slots/application.fam +++ b/applications/external/slots/application.fam @@ -10,4 +10,8 @@ App( order=30, fap_category="Games", fap_icon_assets="assets", + fap_author="@Daniel-dev-s", + fap_weburl="https://github.com/Daniel-dev-s/flipperzero-slots", + fap_version="1.0", + fap_description="Simple Slots simulator game", ) diff --git a/applications/external/snake_2/application.fam b/applications/external/snake_2/application.fam new file mode 100644 index 0000000000..287522983a --- /dev/null +++ b/applications/external/snake_2/application.fam @@ -0,0 +1,16 @@ +App( + appid="snake20", + name="Snake 2.0", + apptype=FlipperAppType.EXTERNAL, + entry_point="snake_20_app", + cdefines=["APP_SNAKE_20"], + requires=["gui"], + stack_size=1 * 1024, + order=30, + fap_icon="snake_10px.png", + fap_category="Games", + fap_author="@Willzvul", + fap_weburl="https://github.com/Willzvul/Snake_2.0", + fap_version="2.0", + fap_description="Advanced Snake Game (Remake of original Snake)", +) diff --git a/applications/external/snake_2/snake_10px.png b/applications/external/snake_2/snake_10px.png new file mode 100644 index 0000000000..125cfb3341 Binary files /dev/null and b/applications/external/snake_2/snake_10px.png differ diff --git a/applications/external/snake_game/snake_game.c b/applications/external/snake_2/snake_20.c similarity index 61% rename from applications/external/snake_game/snake_game.c rename to applications/external/snake_2/snake_20.c index 1a0480355c..ccd6238702 100644 --- a/applications/external/snake_game/snake_game.c +++ b/applications/external/snake_2/snake_20.c @@ -16,7 +16,7 @@ typedef struct { typedef enum { GameStateLife, - + GameStatePause, // https://melmagazine.com/en-us/story/snake-nokia-6110-oral-history-taneli-armanto // Armanto: While testing the early versions of the game, I noticed it was hard // to control the snake upon getting close to and edge but not crashing — especially @@ -27,7 +27,6 @@ typedef enum { // the player crashes, during which she can still change the directions. And if // she does, the game continues. GameStateLastChance, - GameStateGameOver, } GameState; @@ -40,16 +39,19 @@ typedef enum { DirectionLeft, } Direction; -#define MAX_SNAKE_LEN 128 * 64 / 4 +#define MAX_SNAKE_LEN 15 * 31 //128 * 64 / 4 + +#define x_back_symbol 50 +#define y_back_symbol 9 typedef struct { + FuriMutex* mutex; Point points[MAX_SNAKE_LEN]; uint16_t len; Direction currentMovement; Direction nextMovement; // if backward of currentMovement, ignore Point fruit; GameState state; - FuriMutex* mutex; } SnakeState; typedef enum { @@ -85,18 +87,22 @@ const NotificationSequence sequence_fail = { }; const NotificationSequence sequence_eat = { + + &message_vibro_on, &message_note_c7, &message_delay_50, &message_sound_off, + &message_vibro_off, NULL, }; static void snake_game_render_callback(Canvas* const canvas, void* ctx) { furi_assert(ctx); const SnakeState* snake_state = ctx; - furi_mutex_acquire(snake_state->mutex, FuriWaitForever); + // Before the function is called, the state is set with the canvas_reset(canvas) + // Frame canvas_draw_frame(canvas, 0, 0, 128, 64); @@ -105,6 +111,9 @@ static void snake_game_render_callback(Canvas* const canvas, void* ctx) { f.x = f.x * 4 + 1; f.y = f.y * 4 + 1; canvas_draw_rframe(canvas, f.x, f.y, 6, 6, 2); + canvas_draw_dot(canvas, f.x + 3, f.y - 1); + canvas_draw_dot(canvas, f.x + 4, f.y - 2); + //canvas_draw_dot(canvas,f.x+4,f.y-3); // Snake for(uint16_t i = 0; i < snake_state->len; i++) { @@ -112,24 +121,70 @@ static void snake_game_render_callback(Canvas* const canvas, void* ctx) { p.x = p.x * 4 + 2; p.y = p.y * 4 + 2; canvas_draw_box(canvas, p.x, p.y, 4, 4); + if(i == 0) { + canvas_set_color(canvas, ColorWhite); + canvas_draw_box(canvas, p.x + 1, p.y + 1, 2, 2); + canvas_set_color(canvas, ColorBlack); + } } - // Game Over banner - if(snake_state->state == GameStateGameOver) { + // Pause and GameOver banner + if(snake_state->state == GameStatePause || snake_state->state == GameStateGameOver) { // Screen is 128x64 px canvas_set_color(canvas, ColorWhite); - canvas_draw_box(canvas, 34, 20, 62, 24); + canvas_draw_box(canvas, 33, 19, 64, 26); canvas_set_color(canvas, ColorBlack); canvas_draw_frame(canvas, 34, 20, 62, 24); canvas_set_font(canvas, FontPrimary); - canvas_draw_str(canvas, 37, 31, "Game Over"); + if(snake_state->state == GameStateGameOver) { + canvas_draw_str_aligned(canvas, 65, 31, AlignCenter, AlignBottom, "Game Over"); + } + if(snake_state->state == GameStatePause) { + canvas_draw_str_aligned(canvas, 65, 31, AlignCenter, AlignBottom, "Pause"); + } canvas_set_font(canvas, FontSecondary); - char buffer[12]; + char buffer[20]; snprintf(buffer, sizeof(buffer), "Score: %u", snake_state->len - 7U); - canvas_draw_str_aligned(canvas, 64, 41, AlignCenter, AlignBottom, buffer); + canvas_draw_str_aligned(canvas, 65, 41, AlignCenter, AlignBottom, buffer); + + // Painting "back"-symbol, Help message for Exit App, ProgressBar + canvas_set_color(canvas, ColorWhite); + canvas_draw_box(canvas, 25, 2, 81, 11); + canvas_draw_box(canvas, 28, 54, 73, 9); + canvas_set_color(canvas, ColorBlack); + canvas_draw_str_aligned( + canvas, 65, 10, AlignCenter, AlignBottom, "Hold to Exit App"); + snprintf( + buffer, sizeof(buffer), "Complete: %-5.1f%%", (double)((snake_state->len - 7U) / 4.58)); + canvas_draw_str_aligned(canvas, 65, 62, AlignCenter, AlignBottom, buffer); + { + canvas_draw_dot(canvas, x_back_symbol + 0, y_back_symbol); + canvas_draw_dot(canvas, x_back_symbol + 1, y_back_symbol); + canvas_draw_dot(canvas, x_back_symbol + 2, y_back_symbol); + canvas_draw_dot(canvas, x_back_symbol + 3, y_back_symbol); + canvas_draw_dot(canvas, x_back_symbol + 4, y_back_symbol); + canvas_draw_dot(canvas, x_back_symbol + 5, y_back_symbol - 1); + canvas_draw_dot(canvas, x_back_symbol + 6, y_back_symbol - 2); + canvas_draw_dot(canvas, x_back_symbol + 6, y_back_symbol - 3); + canvas_draw_dot(canvas, x_back_symbol + 5, y_back_symbol - 4); + canvas_draw_dot(canvas, x_back_symbol + 4, y_back_symbol - 5); + canvas_draw_dot(canvas, x_back_symbol + 3, y_back_symbol - 5); + canvas_draw_dot(canvas, x_back_symbol + 2, y_back_symbol - 5); + canvas_draw_dot(canvas, x_back_symbol + 1, y_back_symbol - 5); + canvas_draw_dot(canvas, x_back_symbol + 0, y_back_symbol - 5); + canvas_draw_dot(canvas, x_back_symbol - 1, y_back_symbol - 5); + canvas_draw_dot(canvas, x_back_symbol - 2, y_back_symbol - 5); + canvas_draw_dot(canvas, x_back_symbol - 3, y_back_symbol - 5); + canvas_draw_dot(canvas, x_back_symbol - 2, y_back_symbol - 6); + canvas_draw_dot(canvas, x_back_symbol - 2, y_back_symbol - 4); + canvas_draw_dot(canvas, x_back_symbol - 1, y_back_symbol - 6); + canvas_draw_dot(canvas, x_back_symbol - 1, y_back_symbol - 4); + canvas_draw_dot(canvas, x_back_symbol - 1, y_back_symbol - 7); + canvas_draw_dot(canvas, x_back_symbol - 1, y_back_symbol - 3); + } } furi_mutex_release(snake_state->mutex); @@ -297,6 +352,7 @@ static void if(eatFruit) { snake_state->len++; if(snake_state->len >= MAX_SNAKE_LEN) { + //You win!!! snake_state->state = GameStateGameOver; notification_message_block(notification, &sequence_fail); return; @@ -308,10 +364,11 @@ static void if(eatFruit) { snake_state->fruit = snake_game_get_new_fruit(snake_state); notification_message(notification, &sequence_eat); + notification_message(notification, &sequence_blink_red_100); } } -int32_t snake_game_app(void* p) { +int32_t snake_20_app(void* p) { UNUSED(p); FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(SnakeEvent)); @@ -320,7 +377,6 @@ int32_t snake_game_app(void* p) { snake_game_init_game(snake_state); snake_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); - if(!snake_state->mutex) { FURI_LOG_E("SnakeGame", "cannot create mutex\r\n"); furi_message_queue_free(event_queue); @@ -343,6 +399,8 @@ int32_t snake_game_app(void* p) { notification_message_block(notification, &sequence_display_backlight_enforce_on); + // dolphin_deed(DolphinDeedPluginGameStart); + SnakeEvent event; for(bool processing = true; processing;) { FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); @@ -350,26 +408,83 @@ int32_t snake_game_app(void* p) { furi_mutex_acquire(snake_state->mutex, FuriWaitForever); if(event_status == FuriStatusOk) { - // press events if(event.type == EventTypeKey) { + // press events if(event.input.type == InputTypePress) { switch(event.input.key) { case InputKeyUp: - snake_state->nextMovement = DirectionUp; + if(snake_state->state != GameStatePause) { + snake_state->nextMovement = DirectionUp; + } break; case InputKeyDown: - snake_state->nextMovement = DirectionDown; + if(snake_state->state != GameStatePause) { + snake_state->nextMovement = DirectionDown; + } break; case InputKeyRight: - snake_state->nextMovement = DirectionRight; + if(snake_state->state != GameStatePause) { + snake_state->nextMovement = DirectionRight; + } break; case InputKeyLeft: - snake_state->nextMovement = DirectionLeft; + if(snake_state->state != GameStatePause) { + snake_state->nextMovement = DirectionLeft; + } break; case InputKeyOk: if(snake_state->state == GameStateGameOver) { snake_game_init_game(snake_state); } + if(snake_state->state == GameStatePause) { + furi_timer_start(timer, furi_kernel_get_tick_frequency() / 4); + snake_state->state = GameStateLife; + } + break; + case InputKeyBack: + if(snake_state->state == GameStateLife) { + furi_timer_stop(timer); + snake_state->state = GameStatePause; + break; + } + if(snake_state->state == GameStatePause) { + furi_timer_start(timer, furi_kernel_get_tick_frequency() / 4); + snake_state->state = GameStateLife; + break; + } + if(snake_state->state == GameStateGameOver) { + snake_game_init_game(snake_state); + } + default: + break; + } + } + //LongPress Events + if(event.input.type == InputTypeLong) { + switch(event.input.key) { + case InputKeyUp: + if(snake_state->state != GameStatePause) { + snake_state->nextMovement = DirectionUp; + furi_timer_start(timer, furi_kernel_get_tick_frequency() / 8); + } + break; + case InputKeyDown: + if(snake_state->state != GameStatePause) { + snake_state->nextMovement = DirectionDown; + furi_timer_start(timer, furi_kernel_get_tick_frequency() / 8); + } + break; + case InputKeyRight: + if(snake_state->state != GameStatePause) { + snake_state->nextMovement = DirectionRight; + furi_timer_start(timer, furi_kernel_get_tick_frequency() / 8); + } + break; + case InputKeyLeft: + if(snake_state->state != GameStatePause) { + snake_state->nextMovement = DirectionLeft; + furi_timer_start(timer, furi_kernel_get_tick_frequency() / 8); + } break; case InputKeyBack: processing = false; @@ -378,6 +493,12 @@ int32_t snake_game_app(void* p) { break; } } + //ReleaseKey Event + if(event.input.type == InputTypeRelease) { + if(snake_state->state != GameStatePause) { + furi_timer_start(timer, furi_kernel_get_tick_frequency() / 4); + } + } } else if(event.type == EventTypeTick) { snake_game_process_game_step(snake_state, notification); } @@ -389,8 +510,8 @@ int32_t snake_game_app(void* p) { furi_mutex_release(snake_state->mutex); } - // Return backlight to normal state - notification_message(notification, &sequence_display_backlight_enforce_auto); + // Wait for all notifications to be played and return backlight to normal state + notification_message_block(notification, &sequence_display_backlight_enforce_auto); furi_timer_free(timer); view_port_enabled_set(view_port, false); diff --git a/applications/external/snake_game/application.fam b/applications/external/snake_game/application.fam deleted file mode 100644 index 1e1ee48a6a..0000000000 --- a/applications/external/snake_game/application.fam +++ /dev/null @@ -1,11 +0,0 @@ -App( - appid="snake", - name="Snake Game", - apptype=FlipperAppType.EXTERNAL, - entry_point="snake_game_app", - requires=["gui"], - stack_size=1 * 1024, - order=210, - fap_icon="snake_10px.png", - fap_category="Games", -) diff --git a/applications/external/snake_game/snake_10px.png b/applications/external/snake_game/snake_10px.png deleted file mode 100644 index 52d9fa7e0e..0000000000 Binary files a/applications/external/snake_game/snake_10px.png and /dev/null differ diff --git a/applications/external/solitaire/application.fam b/applications/external/solitaire/application.fam index 9fb33774e2..99067e1b9f 100644 --- a/applications/external/solitaire/application.fam +++ b/applications/external/solitaire/application.fam @@ -9,4 +9,7 @@ App( fap_icon="solitaire_10px.png", fap_category="Games", fap_icon_assets="assets", + fap_author="@teeebor", + fap_version="1.0", + fap_description="Solitaire game", ) diff --git a/applications/external/solitaire/solitaire.c b/applications/external/solitaire/solitaire.c index 2fe939b73e..f83a53575a 100644 --- a/applications/external/solitaire/solitaire.c +++ b/applications/external/solitaire/solitaire.c @@ -275,6 +275,7 @@ void tick(GameState* game_state, NotificationApp* notification) { if(game_state->state == GameStatePlay) { if(game_state->top_cards[0].character == 11 && game_state->top_cards[1].character == 11 && game_state->top_cards[2].character == 11 && game_state->top_cards[3].character == 11) { + // dolphin_deed(DolphinDeedPluginGameWin); game_state->state = GameStateAnimate; return; } @@ -489,6 +490,9 @@ int32_t solitaire_app(void* p) { 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); diff --git a/applications/external/spectrum_analyzer/application.fam b/applications/external/spectrum_analyzer/application.fam index 79effb3a7f..30d0412ac3 100644 --- a/applications/external/spectrum_analyzer/application.fam +++ b/applications/external/spectrum_analyzer/application.fam @@ -8,4 +8,7 @@ App( order=12, fap_icon="spectrum_10px.png", fap_category="Sub-GHz", + fap_author="@xMasterX & @theY4Kman (original by @jolcese)", + fap_version="1.0", + fap_description="Shows received signals on spectrum, not actual analyzer, more like a demo app", ) diff --git a/applications/external/spectrum_analyzer/spectrum_analyzer.c b/applications/external/spectrum_analyzer/spectrum_analyzer.c index 6250ac0394..be3e043d97 100644 --- a/applications/external/spectrum_analyzer/spectrum_analyzer.c +++ b/applications/external/spectrum_analyzer/spectrum_analyzer.c @@ -403,7 +403,7 @@ void spectrum_analyzer_free(SpectrumAnalyzer* instance) { int32_t spectrum_analyzer_app(void* p) { UNUSED(p); - DOLPHIN_DEED(DolphinDeedPluginStart); + dolphin_deed(DolphinDeedPluginStart); SpectrumAnalyzer* spectrum_analyzer = spectrum_analyzer_alloc(); InputEvent input; diff --git a/applications/external/spi_mem_manager/images/DolphinMafia_115x62.png b/applications/external/spi_mem_manager/images/DolphinMafia_115x62.png deleted file mode 100644 index 66fdb40ff2..0000000000 Binary files a/applications/external/spi_mem_manager/images/DolphinMafia_115x62.png and /dev/null differ diff --git a/applications/external/spi_mem_manager/images/DolphinNice_96x59.png b/applications/external/spi_mem_manager/images/DolphinNice_96x59.png deleted file mode 100644 index a299d36302..0000000000 Binary files a/applications/external/spi_mem_manager/images/DolphinNice_96x59.png and /dev/null differ diff --git a/applications/external/spi_mem_manager/images/SDQuestion_35x43.png b/applications/external/spi_mem_manager/images/SDQuestion_35x43.png deleted file mode 100644 index 9b9c9a58e3..0000000000 Binary files a/applications/external/spi_mem_manager/images/SDQuestion_35x43.png and /dev/null differ diff --git a/applications/external/spi_mem_manager/spi_mem_app_i.h b/applications/external/spi_mem_manager/spi_mem_app_i.h index 285ca66d2f..ea7cad8cb4 100644 --- a/applications/external/spi_mem_manager/spi_mem_app_i.h +++ b/applications/external/spi_mem_manager/spi_mem_app_i.h @@ -19,6 +19,7 @@ #include "scenes/spi_mem_scene.h" #include "lib/spi/spi_mem_worker.h" #include "spi_mem_manager_icons.h" +#include #include "views/spi_mem_view_progress.h" #include "views/spi_mem_view_detect.h" diff --git a/applications/external/subghz_bruteforcer/application.fam b/applications/external/subghz_bruteforcer/application.fam index f756ae51a6..6a3fdef3f4 100644 --- a/applications/external/subghz_bruteforcer/application.fam +++ b/applications/external/subghz_bruteforcer/application.fam @@ -1,12 +1,11 @@ App( - appid="SubGHz_Bruteforcer", + appid="subghz_bruteforcer", name="Sub-GHz Bruteforcer", apptype=FlipperAppType.EXTERNAL, entry_point="subbrute_app", requires=["gui", "dialogs"], stack_size=2 * 1024, order=11, - fap_icon="images/subbrute_10px.png", + fap_icon="subbrute_10px.png", fap_category="Sub-GHz", - fap_icon_assets="images", ) diff --git a/applications/external/subghz_bruteforcer/helpers/subbrute_worker.c b/applications/external/subghz_bruteforcer/helpers/subbrute_worker.c index 72f9f57b9c..ef622482f9 100644 --- a/applications/external/subghz_bruteforcer/helpers/subbrute_worker.c +++ b/applications/external/subghz_bruteforcer/helpers/subbrute_worker.c @@ -28,6 +28,7 @@ SubBruteWorker* subbrute_worker_alloc() { instance->context = NULL; instance->callback = NULL; + instance->tx_timeout_ms = SUBBRUTE_TX_TIMEOUT; instance->decoder_result = NULL; instance->transmitter = NULL; instance->environment = subghz_environment_alloc(); @@ -206,6 +207,7 @@ void subbrute_worker_stop(SubBruteWorker* instance) { furi_thread_join(instance->thread); furi_hal_subghz_set_path(FuriHalSubGhzPathIsolate); + furi_hal_subghz_idle(); furi_hal_subghz_sleep(); } @@ -306,8 +308,9 @@ void subbrute_worker_set_callback( } void subbrute_worker_subghz_transmit(SubBruteWorker* instance, FlipperFormat* flipper_format) { + const uint8_t timeout = instance->tx_timeout_ms; while(instance->transmit_mode) { - furi_delay_ms(SUBBRUTE_TX_TIMEOUT); + furi_delay_ms(timeout); } instance->transmit_mode = true; if(instance->transmitter != NULL) { @@ -318,17 +321,20 @@ void subbrute_worker_subghz_transmit(SubBruteWorker* instance, FlipperFormat* fl subghz_transmitter_alloc_init(instance->environment, instance->protocol_name); subghz_transmitter_deserialize(instance->transmitter, flipper_format); furi_hal_subghz_reset(); + furi_hal_subghz_idle(); furi_hal_subghz_load_preset(instance->preset); furi_hal_subghz_set_frequency_and_path(instance->frequency); furi_hal_subghz_start_async_tx(subghz_transmitter_yield, instance->transmitter); while(!furi_hal_subghz_is_async_tx_complete()) { - furi_delay_ms(SUBBRUTE_TX_TIMEOUT); + furi_delay_ms(timeout); } furi_hal_subghz_stop_async_tx(); - furi_hal_subghz_set_path(FuriHalSubGhzPathIsolate); - furi_hal_subghz_sleep(); + //furi_hal_subghz_set_path(FuriHalSubGhzPathIsolate); + furi_hal_subghz_idle(); + //furi_hal_subghz_sleep(); + subghz_transmitter_stop(instance->transmitter); subghz_transmitter_free(instance->transmitter); instance->transmitter = NULL; @@ -420,7 +426,7 @@ int32_t subbrute_worker_thread(void* context) { instance->step++; // furi_string_free(payload); - furi_delay_ms(SUBBRUTE_TX_TIMEOUT); + furi_delay_ms(instance->tx_timeout_ms); } flipper_format_free(flipper_format); @@ -435,3 +441,19 @@ int32_t subbrute_worker_thread(void* context) { #endif return 0; } + +uint8_t subbrute_worker_get_timeout(SubBruteWorker* instance) { + return instance->tx_timeout_ms; +} + +void subbrute_worker_timeout_inc(SubBruteWorker* instance) { + if(instance->tx_timeout_ms < 255) { + instance->tx_timeout_ms++; + } +} + +void subbrute_worker_timeout_dec(SubBruteWorker* instance) { + if(instance->tx_timeout_ms > 0) { + instance->tx_timeout_ms--; + } +} \ No newline at end of file diff --git a/applications/external/subghz_bruteforcer/helpers/subbrute_worker.h b/applications/external/subghz_bruteforcer/helpers/subbrute_worker.h index 4046f997c4..f7c32dd4b1 100644 --- a/applications/external/subghz_bruteforcer/helpers/subbrute_worker.h +++ b/applications/external/subghz_bruteforcer/helpers/subbrute_worker.h @@ -39,4 +39,10 @@ bool subbrute_worker_can_manual_transmit(SubBruteWorker* instance); void subbrute_worker_set_callback( SubBruteWorker* instance, SubBruteWorkerCallback callback, - void* context); \ No newline at end of file + void* context); + +uint8_t subbrute_worker_get_timeout(SubBruteWorker* instance); + +void subbrute_worker_timeout_inc(SubBruteWorker* instance); + +void subbrute_worker_timeout_dec(SubBruteWorker* instance); diff --git a/applications/external/subghz_bruteforcer/helpers/subbrute_worker_private.h b/applications/external/subghz_bruteforcer/helpers/subbrute_worker_private.h index e38e77dc46..a660ca7316 100644 --- a/applications/external/subghz_bruteforcer/helpers/subbrute_worker_private.h +++ b/applications/external/subghz_bruteforcer/helpers/subbrute_worker_private.h @@ -21,6 +21,7 @@ struct SubBruteWorker { SubGhzEnvironment* environment; SubGhzTransmitter* transmitter; const char* protocol_name; + uint8_t tx_timeout_ms; // Initiated values SubBruteAttacks attack; // Attack state diff --git a/applications/external/subghz_bruteforcer/images/ButtonDown_7x4.png b/applications/external/subghz_bruteforcer/images/ButtonDown_7x4.png deleted file mode 100644 index 2954bb6a67..0000000000 Binary files a/applications/external/subghz_bruteforcer/images/ButtonDown_7x4.png and /dev/null differ diff --git a/applications/external/subghz_bruteforcer/images/ButtonUp_7x4.png b/applications/external/subghz_bruteforcer/images/ButtonUp_7x4.png deleted file mode 100644 index 1be79328b4..0000000000 Binary files a/applications/external/subghz_bruteforcer/images/ButtonUp_7x4.png and /dev/null differ diff --git a/applications/external/subghz_bruteforcer/images/DolphinNice_96x59.png b/applications/external/subghz_bruteforcer/images/DolphinNice_96x59.png deleted file mode 100644 index a299d36302..0000000000 Binary files a/applications/external/subghz_bruteforcer/images/DolphinNice_96x59.png and /dev/null differ diff --git a/applications/external/subghz_bruteforcer/images/Sub1ghz_14/frame_01.png b/applications/external/subghz_bruteforcer/images/Sub1ghz_14/frame_01.png deleted file mode 100644 index 52dc4ad21f..0000000000 Binary files a/applications/external/subghz_bruteforcer/images/Sub1ghz_14/frame_01.png and /dev/null differ diff --git a/applications/external/subghz_bruteforcer/images/Sub1ghz_14/frame_02.png b/applications/external/subghz_bruteforcer/images/Sub1ghz_14/frame_02.png deleted file mode 100644 index 2dff1c031d..0000000000 Binary files a/applications/external/subghz_bruteforcer/images/Sub1ghz_14/frame_02.png and /dev/null differ diff --git a/applications/external/subghz_bruteforcer/images/Sub1ghz_14/frame_03.png b/applications/external/subghz_bruteforcer/images/Sub1ghz_14/frame_03.png deleted file mode 100644 index c1e438b01c..0000000000 Binary files a/applications/external/subghz_bruteforcer/images/Sub1ghz_14/frame_03.png and /dev/null differ diff --git a/applications/external/subghz_bruteforcer/images/Sub1ghz_14/frame_04.png b/applications/external/subghz_bruteforcer/images/Sub1ghz_14/frame_04.png deleted file mode 100644 index 169fb61476..0000000000 Binary files a/applications/external/subghz_bruteforcer/images/Sub1ghz_14/frame_04.png and /dev/null differ diff --git a/applications/external/subghz_bruteforcer/images/Sub1ghz_14/frame_05.png b/applications/external/subghz_bruteforcer/images/Sub1ghz_14/frame_05.png deleted file mode 100644 index 79b2bc9725..0000000000 Binary files a/applications/external/subghz_bruteforcer/images/Sub1ghz_14/frame_05.png and /dev/null differ diff --git a/applications/external/subghz_bruteforcer/images/Sub1ghz_14/frame_06.png b/applications/external/subghz_bruteforcer/images/Sub1ghz_14/frame_06.png deleted file mode 100644 index 8fce0c44d6..0000000000 Binary files a/applications/external/subghz_bruteforcer/images/Sub1ghz_14/frame_06.png and /dev/null differ diff --git a/applications/external/subghz_bruteforcer/images/sub1_10px.png b/applications/external/subghz_bruteforcer/images/sub1_10px.png deleted file mode 100644 index 5a25fdf4ef..0000000000 Binary files a/applications/external/subghz_bruteforcer/images/sub1_10px.png and /dev/null differ diff --git a/applications/external/subghz_bruteforcer/scenes/subbrute_scene_config.h b/applications/external/subghz_bruteforcer/scenes/subbrute_scene_config.h index 3541df9aca..3c7a3bfda1 100644 --- a/applications/external/subghz_bruteforcer/scenes/subbrute_scene_config.h +++ b/applications/external/subghz_bruteforcer/scenes/subbrute_scene_config.h @@ -4,4 +4,5 @@ ADD_SCENE(subbrute, run_attack, RunAttack) ADD_SCENE(subbrute, save_name, SaveName) ADD_SCENE(subbrute, save_success, SaveSuccess) ADD_SCENE(subbrute, setup_attack, SetupAttack) +ADD_SCENE(subbrute, setup_extra, SetupExtra) ADD_SCENE(subbrute, start, Start) \ No newline at end of file diff --git a/applications/external/subghz_bruteforcer/scenes/subbrute_scene_load_file.c b/applications/external/subghz_bruteforcer/scenes/subbrute_scene_load_file.c index 8aae1bcadf..13fc859094 100644 --- a/applications/external/subghz_bruteforcer/scenes/subbrute_scene_load_file.c +++ b/applications/external/subghz_bruteforcer/scenes/subbrute_scene_load_file.c @@ -20,7 +20,7 @@ void subbrute_scene_load_file_on_enter(void* context) { // TODO: DELETE IT #ifdef SUBBRUTE_FAST_TRACK bool res = true; - furi_string_printf(load_path, "%s", "/ext/subghz/princeton.sub"); + furi_string_printf(load_path, "%s", EXT_PATH("subghz/princeton.sub")); #else bool res = dialog_file_browser_show(instance->dialogs, load_path, app_folder, &browser_options); @@ -88,4 +88,4 @@ bool subbrute_scene_load_file_on_event(void* context, SceneManagerEvent event) { UNUSED(context); UNUSED(event); return false; -} \ No newline at end of file +} diff --git a/applications/external/subghz_bruteforcer/scenes/subbrute_scene_setup_attack.c b/applications/external/subghz_bruteforcer/scenes/subbrute_scene_setup_attack.c index c2877c7cb6..5aa5e840a4 100644 --- a/applications/external/subghz_bruteforcer/scenes/subbrute_scene_setup_attack.c +++ b/applications/external/subghz_bruteforcer/scenes/subbrute_scene_setup_attack.c @@ -81,6 +81,8 @@ bool subbrute_scene_setup_attack_on_event(void* context, SceneManagerEvent event false, instance->device->extra_repeats); scene_manager_next_scene(instance->scene_manager, SubBruteSceneSaveName); + } else if(event.event == SubBruteCustomEventTypeExtraSettings) { + scene_manager_next_scene(instance->scene_manager, SubBruteSceneSetupExtra); } else if(event.event == SubBruteCustomEventTypeBackPressed) { subbrute_attack_view_init_values( view, diff --git a/applications/external/subghz_bruteforcer/scenes/subbrute_scene_setup_extra.c b/applications/external/subghz_bruteforcer/scenes/subbrute_scene_setup_extra.c new file mode 100644 index 0000000000..c798e055b6 --- /dev/null +++ b/applications/external/subghz_bruteforcer/scenes/subbrute_scene_setup_extra.c @@ -0,0 +1,66 @@ +#include "../subbrute_i.h" +#include "subbrute_scene.h" + +#define TAG "SubBruteSceneLoadFile" + +void setup_extra_widget_callback(GuiButtonType result, InputType type, void* context); + +static void setup_extra_widget_draw(void* context) { + furi_assert(context); + SubBruteState* instance = context; + + Widget* widget = instance->widget; + + widget_add_button_element( + widget, GuiButtonTypeLeft, "-TD", setup_extra_widget_callback, instance); + widget_add_button_element( + widget, GuiButtonTypeRight, "TD+", setup_extra_widget_callback, instance); + + char str[20]; + snprintf(&str[0], 20, "%d", subbrute_worker_get_timeout(instance->worker)); + + widget_add_string_element( + instance->widget, 64, 15, AlignCenter, AlignCenter, FontPrimary, "Time Delay"); + + widget_add_string_element( + instance->widget, 64, 32, AlignCenter, AlignCenter, FontBigNumbers, &str[0]); +} + +void setup_extra_widget_callback(GuiButtonType result, InputType type, void* context) { + furi_assert(context); + SubBruteState* instance = context; + + if((result == GuiButtonTypeLeft) && ((type == InputTypeShort) || (type == InputTypeRepeat))) { + widget_reset(instance->widget); + subbrute_worker_timeout_dec(instance->worker); + setup_extra_widget_draw(instance); + } else if( + (result == GuiButtonTypeRight) && + ((type == InputTypeShort) || (type == InputTypeRepeat))) { + widget_reset(instance->widget); + subbrute_worker_timeout_inc(instance->worker); + setup_extra_widget_draw(instance); + } +} + +void subbrute_scene_setup_extra_on_enter(void* context) { + furi_assert(context); + SubBruteState* instance = context; + + setup_extra_widget_draw(instance); + + view_dispatcher_switch_to_view(instance->view_dispatcher, SubBruteViewWidget); +} + +void subbrute_scene_setup_extra_on_exit(void* context) { + furi_assert(context); + SubBruteState* instance = context; + + widget_reset(instance->widget); +} + +bool subbrute_scene_setup_extra_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} \ No newline at end of file diff --git a/applications/external/subghz_bruteforcer/subbrute.c b/applications/external/subghz_bruteforcer/subbrute.c index 99334c05e4..f47c759432 100644 --- a/applications/external/subghz_bruteforcer/subbrute.c +++ b/applications/external/subghz_bruteforcer/subbrute.c @@ -94,12 +94,18 @@ SubBruteState* subbrute_alloc() { //instance->flipper_format = flipper_format_string_alloc(); //instance->environment = subghz_environment_alloc(); + // Uncomment to enable Debug pin output on PIN 17(1W) + //furi_hal_subghz_set_async_mirror_pin(&gpio_ibutton); + return instance; } void subbrute_free(SubBruteState* instance) { furi_assert(instance); + // Uncomment to enable Debug pin output on PIN 17(1W) + //furi_hal_subghz_set_async_mirror_pin(NULL); + // SubBruteWorker subbrute_worker_stop(instance->worker); subbrute_worker_free(instance->worker); @@ -174,7 +180,7 @@ void subbrute_popup_closed_callback(void* context) { int32_t subbrute_app(void* p) { UNUSED(p); - DOLPHIN_DEED(DolphinDeedPluginStart); + dolphin_deed(DolphinDeedPluginStart); SubBruteState* instance = subbrute_alloc(); view_dispatcher_attach_to_gui( instance->view_dispatcher, instance->gui, ViewDispatcherTypeFullscreen); diff --git a/applications/external/subghz_bruteforcer/images/subbrute_10px.png b/applications/external/subghz_bruteforcer/subbrute_10px.png similarity index 100% rename from applications/external/subghz_bruteforcer/images/subbrute_10px.png rename to applications/external/subghz_bruteforcer/subbrute_10px.png diff --git a/applications/external/subghz_bruteforcer/subbrute_custom_event.h b/applications/external/subghz_bruteforcer/subbrute_custom_event.h index 2864f89341..4e0c1214d0 100644 --- a/applications/external/subghz_bruteforcer/subbrute_custom_event.h +++ b/applications/external/subghz_bruteforcer/subbrute_custom_event.h @@ -12,6 +12,7 @@ typedef enum { SubBruteCustomEventTypeTransmitNotStarted, SubBruteCustomEventTypeTransmitCustom, SubBruteCustomEventTypeSaveFile, + SubBruteCustomEventTypeExtraSettings, SubBruteCustomEventTypeUpdateView, SubBruteCustomEventTypeChangeStepUp, SubBruteCustomEventTypeChangeStepDown, diff --git a/applications/external/subghz_bruteforcer/subbrute_i.h b/applications/external/subghz_bruteforcer/subbrute_i.h index c50a7ed9b2..9f1bd57d4b 100644 --- a/applications/external/subghz_bruteforcer/subbrute_i.h +++ b/applications/external/subghz_bruteforcer/subbrute_i.h @@ -16,7 +16,7 @@ #include #include -#include "SubGHz_Bruteforcer_icons.h" +#include #include @@ -29,7 +29,7 @@ #include "views/subbrute_attack_view.h" #include "views/subbrute_main_view.h" -#define SUBBRUTEFORCER_VER "Sub-GHz BruteForcer 3.5" +#define SUBBRUTEFORCER_VER "Sub-GHz BruteForcer 3.6" #ifdef FURI_DEBUG //#define SUBBRUTE_FAST_TRACK false @@ -77,4 +77,4 @@ struct SubBruteState { void subbrute_show_loading_popup(void* context, bool show); void subbrute_text_input_callback(void* context); -void subbrute_popup_closed_callback(void* context); \ No newline at end of file +void subbrute_popup_closed_callback(void* context); diff --git a/applications/external/subghz_bruteforcer/views/subbrute_attack_view.c b/applications/external/subghz_bruteforcer/views/subbrute_attack_view.c index d7770bb442..c05fb80840 100644 --- a/applications/external/subghz_bruteforcer/views/subbrute_attack_view.c +++ b/applications/external/subghz_bruteforcer/views/subbrute_attack_view.c @@ -72,9 +72,12 @@ bool subbrute_attack_view_input(InputEvent* event, void* context) { instance->is_attacking = true; instance->callback(SubBruteCustomEventTypeTransmitStarted, instance->context); update = true; - } else if(event->key == InputKeyUp) { + } else if(event->key == InputKeyUp && event->type == InputTypeShort) { instance->callback(SubBruteCustomEventTypeSaveFile, instance->context); update = true; + } else if(event->key == InputKeyUp && event->type == InputTypeLong) { + instance->callback(SubBruteCustomEventTypeExtraSettings, instance->context); + update = true; } else if(event->key == InputKeyDown) { instance->callback(SubBruteCustomEventTypeTransmitCustom, instance->context); update = true; diff --git a/applications/external/subghz_bruteforcer/views/subbrute_main_view.c b/applications/external/subghz_bruteforcer/views/subbrute_main_view.c index c21f2ea33a..925188db18 100644 --- a/applications/external/subghz_bruteforcer/views/subbrute_main_view.c +++ b/applications/external/subghz_bruteforcer/views/subbrute_main_view.c @@ -167,7 +167,7 @@ void subbrute_main_view_draw(Canvas* canvas, SubBruteMainViewModel* model) { if(model->index == position) { canvas_draw_str_aligned( canvas, - 4, + 3, 9 + (item_position * item_height) + STATUS_BAR_Y_SHIFT, AlignLeft, AlignCenter, @@ -181,9 +181,14 @@ void subbrute_main_view_draw(Canvas* canvas, SubBruteMainViewModel* model) { sizeof(buffer), "x%d", model->extra_repeats + subbrute_protocol_repeats_count(model->index)); + uint8_t temp_x_offset_repeats = 18; + if(model->extra_repeats + subbrute_protocol_repeats_count(model->index) < + 10) { + temp_x_offset_repeats = 15; + } canvas_draw_str_aligned( canvas, - screen_width - 15, + screen_width - temp_x_offset_repeats, 9 + (item_position * item_height) + STATUS_BAR_Y_SHIFT, AlignLeft, AlignCenter, @@ -232,7 +237,7 @@ bool subbrute_main_view_input(InputEvent* event, void* context) { #endif const uint8_t min_value = 0; const uint8_t correct_total = SubBruteAttackTotalCount - 1; - uint8_t max_repeats = 9 - subbrute_protocol_repeats_count(instance->index); + uint8_t max_repeats = 14 - subbrute_protocol_repeats_count(instance->index); bool updated = false; bool consumed = false; diff --git a/applications/external/subghz_playlist/application.fam b/applications/external/subghz_playlist/application.fam index 6f74f2b55a..8fe9ccbafe 100644 --- a/applications/external/subghz_playlist/application.fam +++ b/applications/external/subghz_playlist/application.fam @@ -6,7 +6,9 @@ App( requires=["storage", "gui", "dialogs", "subghz"], stack_size=2 * 1024, order=14, - fap_icon="playlist_10px.png", + fap_icon="subplaylist_10px.png", fap_category="Sub-GHz", - fap_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/images/ButtonRight_4x7.png b/applications/external/subghz_playlist/images/ButtonRight_4x7.png deleted file mode 100644 index 8e1c74c1c0..0000000000 Binary files a/applications/external/subghz_playlist/images/ButtonRight_4x7.png and /dev/null differ diff --git a/applications/external/subghz_playlist/images/sub1_10px.png b/applications/external/subghz_playlist/images/sub1_10px.png deleted file mode 100644 index 5a25fdf4ef..0000000000 Binary files a/applications/external/subghz_playlist/images/sub1_10px.png and /dev/null differ diff --git a/applications/external/subghz_playlist/playlist.c b/applications/external/subghz_playlist/playlist.c index 1373c14a26..12191dc961 100644 --- a/applications/external/subghz_playlist/playlist.c +++ b/applications/external/subghz_playlist/playlist.c @@ -6,7 +6,7 @@ #include #include -#include +#include #include #include @@ -22,8 +22,6 @@ #include "playlist_file.h" #include "canvas_helper.h" -#define PLAYLIST_FOLDER "/ext/subghz/playlist" -#define PLAYLIST_EXT ".txt" #define TAG "Playlist" #define STATE_NONE 0 @@ -695,9 +693,9 @@ void playlist_free(Playlist* app) { free(app); } -int32_t playlist_app(void* p) { +int32_t playlist_app(char* p) { UNUSED(p); - DOLPHIN_DEED(DolphinDeedPluginStart); + dolphin_deed(DolphinDeedPluginStart); // create playlist folder { @@ -725,7 +723,9 @@ int32_t playlist_app(void* p) { furi_hal_power_suppress_charge_enter(); // select playlist file - { + if(p && strlen(p)) { + furi_string_set(app->file_path, p); + } else { DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); DialogsFileBrowserOptions browser_options; dialog_file_browser_set_basic_options(&browser_options, PLAYLIST_EXT, &I_sub1_10px); diff --git a/applications/external/subghz_playlist/playlist_file.h b/applications/external/subghz_playlist/playlist_file.h index fb708edc72..7e853fb28f 100644 --- a/applications/external/subghz_playlist/playlist_file.h +++ b/applications/external/subghz_playlist/playlist_file.h @@ -4,4 +4,7 @@ #include +#define PLAYLIST_FOLDER EXT_PATH("subghz/playlist") +#define PLAYLIST_EXT ".txt" + int playlist_count_playlist_items(Storage* storage, const char* file_path); diff --git a/applications/external/subghz_playlist/playlist_10px.png b/applications/external/subghz_playlist/subplaylist_10px.png similarity index 100% rename from applications/external/subghz_playlist/playlist_10px.png rename to applications/external/subghz_playlist/subplaylist_10px.png diff --git a/applications/external/subghz_remote/application.fam b/applications/external/subghz_remote/application.fam index 7f12c31f9a..14c7f14ec7 100644 --- a/applications/external/subghz_remote/application.fam +++ b/applications/external/subghz_remote/application.fam @@ -1,5 +1,5 @@ App( - appid="SubGHz_Remote", + appid="subghz_remote", name="Sub-GHz Remote", apptype=FlipperAppType.EXTERNAL, entry_point="subghz_remote_app", @@ -13,7 +13,6 @@ App( ], stack_size=2 * 1024, order=11, - fap_libs=["assets"], - fap_icon="subghz_remote_10px.png", + fap_icon="subrem_10px.png", fap_category="Sub-GHz", ) diff --git a/applications/external/subghz_remote/subghz_remote_app.c b/applications/external/subghz_remote/subghz_remote_app.c index ed98007819..c0682c75e3 100644 --- a/applications/external/subghz_remote/subghz_remote_app.c +++ b/applications/external/subghz_remote/subghz_remote_app.c @@ -19,10 +19,11 @@ static void subghz_remote_app_tick_event_callback(void* context) { scene_manager_handle_tick_event(app->scene_manager); } -SubGhzRemoteApp* subghz_remote_app_alloc() { +SubGhzRemoteApp* subghz_remote_app_alloc(char* p) { SubGhzRemoteApp* app = malloc(sizeof(SubGhzRemoteApp)); Storage* storage = furi_record_open(RECORD_STORAGE); + storage_common_migrate(storage, EXT_PATH("unirf"), SUBREM_APP_FOLDER); storage_common_migrate(storage, EXT_PATH("subghz/unirf"), SUBREM_APP_FOLDER); if(!storage_simply_mkdir(storage, SUBREM_APP_FOLDER)) { @@ -90,13 +91,19 @@ SubGhzRemoteApp* subghz_remote_app_alloc() { subghz_txrx_set_need_save_callback(app->txrx, subrem_save_active_sub, app); + if(p && strlen(p)) { + furi_string_set(app->file_path, p); + subrem_map_file_load(app, furi_string_get_cstr(app->file_path)); + scene_manager_next_scene(app->scene_manager, SubRemSceneRemote); + } else { #ifdef SUBREM_LIGHT - scene_manager_next_scene(app->scene_manager, SubRemSceneOpenMapFile); + scene_manager_next_scene(app->scene_manager, SubRemSceneOpenMapFile); #else - scene_manager_next_scene(app->scene_manager, SubRemSceneStart); - scene_manager_set_scene_state( - app->scene_manager, SubRemSceneStart, SubmenuIndexSubRemOpenMapFile); + scene_manager_next_scene(app->scene_manager, SubRemSceneStart); + scene_manager_set_scene_state( + app->scene_manager, SubRemSceneStart, SubmenuIndexSubRemOpenMapFile); #endif + } return app; } @@ -145,12 +152,10 @@ void subghz_remote_app_free(SubGhzRemoteApp* app) { free(app); } -int32_t subghz_remote_app(void* p) { +int32_t subghz_remote_app(char* p) { UNUSED(p); - DOLPHIN_DEED(DolphinDeedPluginStart); - SubGhzRemoteApp* subghz_remote_app = subghz_remote_app_alloc(); - - furi_string_set(subghz_remote_app->file_path, SUBREM_APP_FOLDER); + dolphin_deed(DolphinDeedPluginStart); + SubGhzRemoteApp* subghz_remote_app = subghz_remote_app_alloc(p); view_dispatcher_run(subghz_remote_app->view_dispatcher); diff --git a/applications/external/subghz_remote/subghz_remote_app_i.h b/applications/external/subghz_remote/subghz_remote_app_i.h index 9291d54f26..0d25134adc 100644 --- a/applications/external/subghz_remote/subghz_remote_app_i.h +++ b/applications/external/subghz_remote/subghz_remote_app_i.h @@ -6,11 +6,7 @@ #include "helpers/txrx/subghz_txrx.h" -#ifdef APP_SUBGHZREMOTE #include -#else -#include -#endif #include "views/remote.h" @@ -56,3 +52,5 @@ bool subrem_tx_start_sub(SubGhzRemoteApp* app, SubRemSubFilePreset* sub_preset); bool subrem_tx_stop_sub(SubGhzRemoteApp* app, bool forced); void subrem_save_active_sub(void* context); + +SubRemLoadMapState subrem_map_file_load(SubGhzRemoteApp* app, const char* file_path); diff --git a/applications/external/subghz_remote_configurator/icons/subrem_10px.png b/applications/external/subghz_remote/subrem_10px.png similarity index 100% rename from applications/external/subghz_remote_configurator/icons/subrem_10px.png rename to applications/external/subghz_remote/subrem_10px.png diff --git a/applications/external/subghz_remote_configurator/application.fam b/applications/external/subghz_remote_configurator/application.fam index 5589349a0a..4604a509ba 100644 --- a/applications/external/subghz_remote_configurator/application.fam +++ b/applications/external/subghz_remote_configurator/application.fam @@ -11,6 +11,5 @@ App( order=50, fap_description="File Editor for the SubGhz Remote app", fap_category="Sub-Ghz", - fap_icon_assets="icons", - fap_icon="icons/subrem_10px.png", + fap_icon="subrem_10px.png", ) diff --git a/applications/external/subghz_remote_configurator/icons/DolphinNice_96x59.png b/applications/external/subghz_remote_configurator/icons/DolphinNice_96x59.png deleted file mode 100644 index a299d36302..0000000000 Binary files a/applications/external/subghz_remote_configurator/icons/DolphinNice_96x59.png and /dev/null differ diff --git a/applications/external/subghz_remote_configurator/icons/remote_scene/ButtonDown_7x4.png b/applications/external/subghz_remote_configurator/icons/remote_scene/ButtonDown_7x4.png deleted file mode 100644 index 2954bb6a67..0000000000 Binary files a/applications/external/subghz_remote_configurator/icons/remote_scene/ButtonDown_7x4.png and /dev/null differ diff --git a/applications/external/subghz_remote_configurator/icons/remote_scene/ButtonLeft_4x7.png b/applications/external/subghz_remote_configurator/icons/remote_scene/ButtonLeft_4x7.png deleted file mode 100644 index 0b4655d432..0000000000 Binary files a/applications/external/subghz_remote_configurator/icons/remote_scene/ButtonLeft_4x7.png and /dev/null differ diff --git a/applications/external/subghz_remote_configurator/icons/remote_scene/ButtonRight_4x7.png b/applications/external/subghz_remote_configurator/icons/remote_scene/ButtonRight_4x7.png deleted file mode 100644 index 8e1c74c1c0..0000000000 Binary files a/applications/external/subghz_remote_configurator/icons/remote_scene/ButtonRight_4x7.png and /dev/null differ diff --git a/applications/external/subghz_remote_configurator/icons/remote_scene/ButtonUp_7x4.png b/applications/external/subghz_remote_configurator/icons/remote_scene/ButtonUp_7x4.png deleted file mode 100644 index 1be79328b4..0000000000 Binary files a/applications/external/subghz_remote_configurator/icons/remote_scene/ButtonUp_7x4.png and /dev/null differ diff --git a/applications/external/subghz_remote_configurator/icons/remote_scene/Ok_btn_9x9.png b/applications/external/subghz_remote_configurator/icons/remote_scene/Ok_btn_9x9.png deleted file mode 100644 index 9a1539da20..0000000000 Binary files a/applications/external/subghz_remote_configurator/icons/remote_scene/Ok_btn_9x9.png and /dev/null differ diff --git a/applications/external/subghz_remote_configurator/icons/remote_scene/Pin_arrow_up_7x9.png b/applications/external/subghz_remote_configurator/icons/remote_scene/Pin_arrow_up_7x9.png deleted file mode 100644 index a91a6fd5e9..0000000000 Binary files a/applications/external/subghz_remote_configurator/icons/remote_scene/Pin_arrow_up_7x9.png and /dev/null differ diff --git a/applications/external/subghz_remote_configurator/icons/remote_scene/Pin_cell_13x13.png b/applications/external/subghz_remote_configurator/icons/remote_scene/Pin_cell_13x13.png deleted file mode 100644 index 1b1ff0c2fb..0000000000 Binary files a/applications/external/subghz_remote_configurator/icons/remote_scene/Pin_cell_13x13.png and /dev/null differ diff --git a/applications/external/subghz_remote_configurator/icons/remote_scene/Pin_star_7x7.png b/applications/external/subghz_remote_configurator/icons/remote_scene/Pin_star_7x7.png deleted file mode 100644 index 42fdea86e4..0000000000 Binary files a/applications/external/subghz_remote_configurator/icons/remote_scene/Pin_star_7x7.png and /dev/null differ diff --git a/applications/external/subghz_remote_configurator/icons/remote_scene/back_10px.png b/applications/external/subghz_remote_configurator/icons/remote_scene/back_10px.png deleted file mode 100644 index f9c615a99e..0000000000 Binary files a/applications/external/subghz_remote_configurator/icons/remote_scene/back_10px.png and /dev/null differ diff --git a/applications/external/subghz_remote_configurator/icons/sub1_10px.png b/applications/external/subghz_remote_configurator/icons/sub1_10px.png deleted file mode 100644 index 5a25fdf4ef..0000000000 Binary files a/applications/external/subghz_remote_configurator/icons/sub1_10px.png and /dev/null differ diff --git a/applications/external/subghz_remote_configurator/subghz_remote_app.c b/applications/external/subghz_remote_configurator/subghz_remote_app.c index 84100a2339..73408b739d 100644 --- a/applications/external/subghz_remote_configurator/subghz_remote_app.c +++ b/applications/external/subghz_remote_configurator/subghz_remote_app.c @@ -23,6 +23,7 @@ SubGhzRemoteApp* subghz_remote_app_alloc() { Storage* storage = furi_record_open(RECORD_STORAGE); storage_common_migrate(storage, EXT_PATH("unirf"), SUBREM_APP_FOLDER); + storage_common_migrate(storage, EXT_PATH("subghz/unirf"), SUBREM_APP_FOLDER); if(!storage_simply_mkdir(storage, SUBREM_APP_FOLDER)) { //FURI_LOG_E(TAG, "Could not create folder %s", SUBREM_APP_FOLDER); diff --git a/applications/external/subghz_remote_configurator/subghz_remote_app_i.h b/applications/external/subghz_remote_configurator/subghz_remote_app_i.h index 0bc1c19c2c..f096a7d46a 100644 --- a/applications/external/subghz_remote_configurator/subghz_remote_app_i.h +++ b/applications/external/subghz_remote_configurator/subghz_remote_app_i.h @@ -5,7 +5,7 @@ #include "scenes/subrem_scene.h" #include "helpers/txrx/subghz_txrx.h" -#include +#include #include "views/remote.h" #include "views/edit_menu.h" diff --git a/applications/external/subghz_remote/subghz_remote_10px.png b/applications/external/subghz_remote_configurator/subrem_10px.png similarity index 100% rename from applications/external/subghz_remote/subghz_remote_10px.png rename to applications/external/subghz_remote_configurator/subrem_10px.png diff --git a/applications/external/swd_probe/application.fam b/applications/external/swd_probe/application.fam index 065ee87690..c14ae18904 100644 --- a/applications/external/swd_probe/application.fam +++ b/applications/external/swd_probe/application.fam @@ -9,4 +9,7 @@ App( fap_icon="icons/app.png", fap_category="GPIO", fap_icon_assets="icons", + fap_author="@g3gg0 & (fixes by @xMasterX)", + fap_version="1.0", + fap_description="ARM SWD (Single Wire Debug) Probe", ) diff --git a/applications/external/swd_probe/icons/ButtonDown_7x4.png b/applications/external/swd_probe/icons/ButtonDown_7x4.png deleted file mode 100644 index 2954bb6a67..0000000000 Binary files a/applications/external/swd_probe/icons/ButtonDown_7x4.png and /dev/null differ diff --git a/applications/external/swd_probe/icons/ButtonUp_7x4.png b/applications/external/swd_probe/icons/ButtonUp_7x4.png deleted file mode 100644 index 1be79328b4..0000000000 Binary files a/applications/external/swd_probe/icons/ButtonUp_7x4.png and /dev/null differ diff --git a/applications/external/swd_probe/swd_probe_app.c b/applications/external/swd_probe/swd_probe_app.c index ae835fe610..9795f12de5 100644 --- a/applications/external/swd_probe/swd_probe_app.c +++ b/applications/external/swd_probe/swd_probe_app.c @@ -2,9 +2,12 @@ #include "swd_probe_app.h" #include "swd_probe_icons.h" +#include #include "jep106.h" #include "adi.h" +#define SWD_PATH EXT_PATH("apps_data/swd") + static void render_callback(Canvas* const canvas, void* cb_ctx); static bool swd_message_process(AppFSM* ctx); static uint8_t swd_transfer(AppFSM* const ctx, bool ap, bool write, uint8_t a23, uint32_t* data); @@ -2926,10 +2929,9 @@ static bool swd_message_process(AppFSM* ctx) { break; case ModePageScan: { - FuriString* result_path = furi_string_alloc_printf(ANY_PATH("swd_scripts")); + FuriString* result_path = furi_string_alloc_printf(SWD_PATH); FuriString* preselected = furi_string_alloc_printf( - (strlen(ctx->script_detected) > 0) ? ctx->script_detected : - ANY_PATH("swd_scripts")); + (strlen(ctx->script_detected) > 0) ? ctx->script_detected : SWD_PATH); DialogsFileBrowserOptions options; dialog_file_browser_set_basic_options(&options, "swd", &I_swd); @@ -2999,10 +3001,9 @@ static bool swd_message_process(AppFSM* ctx) { } } else if((ctx->mode_page == ModePageScan) || (ctx->mode_page == ModePageFound)) { uint32_t mode_page = ctx->mode_page; - FuriString* result_path = furi_string_alloc_printf(ANY_PATH("swd_scripts")); + FuriString* result_path = furi_string_alloc_printf(SWD_PATH); FuriString* preselected = furi_string_alloc_printf( - (strlen(ctx->script_detected) > 0) ? ctx->script_detected : - ANY_PATH("swd_scripts")); + (strlen(ctx->script_detected) > 0) ? ctx->script_detected : SWD_PATH); DialogsFileBrowserOptions options; dialog_file_browser_set_basic_options(&options, "swd", &I_swd); @@ -3101,6 +3102,7 @@ int32_t swd_probe_app_main(void* p) { app->gui = furi_record_open(RECORD_GUI); app->dialogs = furi_record_open(RECORD_DIALOGS); app->storage = furi_record_open(RECORD_STORAGE); + storage_common_migrate(app->storage, EXT_PATH("swd_scripts"), SWD_PATH); DBGS("furi_mutex_alloc"); app->swd_mutex = furi_mutex_alloc(FuriMutexTypeNormal); @@ -3128,7 +3130,9 @@ int32_t swd_probe_app_main(void* p) { notification_message(app->notification, &sequence_display_backlight_enforce_on); DBGS("swd_execute_script"); - swd_execute_script(app, ANY_PATH("swd_scripts/startup.swd")); + swd_execute_script(app, SWD_PATH "/startup.swd"); + + // dolphin_deed(DolphinDeedPluginGameStart); DBGS("processing"); for(bool processing = true; processing;) { diff --git a/applications/external/t_rex_runner/LICENSE b/applications/external/t_rex_runner/LICENSE new file mode 100644 index 0000000000..f288702d2f --- /dev/null +++ b/applications/external/t_rex_runner/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/applications/external/t_rex_runner/application.fam b/applications/external/t_rex_runner/application.fam new file mode 100644 index 0000000000..99cb0f19a2 --- /dev/null +++ b/applications/external/t_rex_runner/application.fam @@ -0,0 +1,17 @@ +App( + appid="t_rex_runner", + name="T-Rex runner", + apptype=FlipperAppType.EXTERNAL, + entry_point="trexrunner_app", + cdefines=["APP_TREXRUNNER"], + requires=["gui"], + stack_size=8 * 1024, + fap_category="Games", + fap_icon="trexrunner_icon.png", + fap_icon_assets="assets", + order=36, + fap_author="@Rrycbarm", + fap_weburl="https://github.com/Rrycbarm/t-rex-runner", + fap_version="1.0", + fap_description="Port of Chrome browser running T-rex game", +) diff --git a/applications/external/t_rex_runner/assets/Cactus.png b/applications/external/t_rex_runner/assets/Cactus.png new file mode 100644 index 0000000000..0b83293276 Binary files /dev/null and b/applications/external/t_rex_runner/assets/Cactus.png differ diff --git a/applications/external/t_rex_runner/assets/Dino.png b/applications/external/t_rex_runner/assets/Dino.png new file mode 100644 index 0000000000..9e33092d6e Binary files /dev/null and b/applications/external/t_rex_runner/assets/Dino.png differ diff --git a/applications/external/t_rex_runner/assets/DinoRun0.png b/applications/external/t_rex_runner/assets/DinoRun0.png new file mode 100644 index 0000000000..f55faebce9 Binary files /dev/null and b/applications/external/t_rex_runner/assets/DinoRun0.png differ diff --git a/applications/external/t_rex_runner/assets/DinoRun1.png b/applications/external/t_rex_runner/assets/DinoRun1.png new file mode 100644 index 0000000000..d1e738f5ec Binary files /dev/null and b/applications/external/t_rex_runner/assets/DinoRun1.png differ diff --git a/applications/external/t_rex_runner/assets/HorizonLine0.png b/applications/external/t_rex_runner/assets/HorizonLine0.png new file mode 100644 index 0000000000..ee802cd281 Binary files /dev/null and b/applications/external/t_rex_runner/assets/HorizonLine0.png differ diff --git a/applications/external/t_rex_runner/trexrunner.c b/applications/external/t_rex_runner/trexrunner.c new file mode 100644 index 0000000000..d7db91fad2 --- /dev/null +++ b/applications/external/t_rex_runner/trexrunner.c @@ -0,0 +1,271 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "t_rex_runner_icons.h" + +#define DINO_START_X 10 +#define DINO_START_Y 34 // 64 - 22 - BACKGROUND_H / 2 - 2 + +#define FPS 20 + +#define DINO_RUNNING_MS_PER_FRAME 500 + +#define GRAVITY 60 +#define JUMP_SPEED 30 + +#define CACTUS_W 10 +#define CACTUS_H 10 +#define START_x_speed 25 + +#define BACKGROUND_W 128 +#define BACKGROUND_H 12 + +typedef enum { + EventTypeTick, + EventTypeKey, +} EventType; + +typedef struct { + EventType type; + InputEvent input; +} PluginEvent; + +typedef struct { + FuriTimer* timer; + uint32_t last_tick; + const Icon* dino_icon; + int dino_frame_ms; + FuriMutex* mutex; + + // Dino info + float y_position; + float y_speed; + int y_acceleration; + float x_speed; + + // Cactus info + int cactus_position; + int has_cactus; + + // Horizontal line + int background_position; + + int lost; + + int score; +} GameState; + +static void timer_callback(void* ctx) { + GameState* game_state = ctx; + furi_mutex_acquire(game_state->mutex, FuriWaitForever); + + if(game_state == NULL) { + return; + } + + uint32_t ticks_elapsed = furi_get_tick() - game_state->last_tick; + game_state->last_tick = furi_get_tick(); + int delta_time_ms = ticks_elapsed * 1000 / furi_kernel_get_tick_frequency(); + + // dino update + game_state->dino_frame_ms += delta_time_ms; + // TODO: switch by dino state + if(game_state->dino_frame_ms >= DINO_RUNNING_MS_PER_FRAME) { + if(game_state->dino_icon == &I_DinoRun0) { + game_state->dino_icon = &I_DinoRun1; + } else { + game_state->dino_icon = &I_DinoRun0; + } + game_state->dino_frame_ms = 0; + } + + // Compute dino dynamics + game_state->y_acceleration = game_state->y_acceleration - GRAVITY * delta_time_ms / 1000; + game_state->y_speed = game_state->y_speed + game_state->y_acceleration * delta_time_ms / 1000; + game_state->y_position = game_state->y_position - game_state->y_speed * delta_time_ms / 1000; + + // Touch ground + if(game_state->y_position >= DINO_START_Y) { + game_state->y_acceleration = 0; + game_state->y_speed = 0; + game_state->y_position = DINO_START_Y; + } + + // Update Cactus state + if(game_state->has_cactus) { + game_state->cactus_position = + game_state->cactus_position - game_state->x_speed * delta_time_ms / 1000; + if(game_state->cactus_position <= 0) { + game_state->has_cactus = 0; + game_state->score = game_state->score + 1; + } + } + // Create cactus (not random) + else { + game_state->has_cactus = 1; + game_state->cactus_position = 120; + } + + // Move horizontal line + if(game_state->background_position <= -BACKGROUND_W) + game_state->background_position += BACKGROUND_W; + game_state->background_position = + game_state->background_position - game_state->x_speed * delta_time_ms / 1000; + + // Lose condition + if((game_state->y_position + 22 >= (64 - CACTUS_H)) && + ((DINO_START_X + 20) >= game_state->cactus_position) && + (DINO_START_X <= (game_state->cactus_position + CACTUS_W))) + game_state->lost = 1; + + furi_mutex_release(game_state->mutex); +} + +static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { + furi_assert(event_queue); + + PluginEvent event = {.type = EventTypeKey, .input = *input_event}; + furi_message_queue_put(event_queue, &event, FuriWaitForever); +} + +static void render_callback(Canvas* const canvas, void* ctx) { + const GameState* game_state = ctx; + furi_mutex_acquire(game_state->mutex, FuriWaitForever); + + if(game_state == NULL) { + return; + } + + char score_string[12]; + if(!game_state->lost) { + // Show Ground + canvas_draw_icon( + canvas, game_state->background_position, 64 - BACKGROUND_H, &I_HorizonLine0); + canvas_draw_icon( + canvas, + game_state->background_position + BACKGROUND_W, + 64 - BACKGROUND_H, + &I_HorizonLine0); + + // Show DINO + canvas_draw_icon(canvas, DINO_START_X, game_state->y_position, game_state->dino_icon); + + // Show cactus + if(game_state->has_cactus) + //canvas_draw_triangle(canvas, game_state->cactus_position, 64 - BACKGROUND_H + CACTUS_W, CACTUS_W, CACTUS_H, CanvasDirectionBottomToTop); + canvas_draw_icon( + canvas, + game_state->cactus_position, + 64 - BACKGROUND_H / 2 - CACTUS_H - 2, + &I_Cactus); + + // Show score + if(game_state->score == 0) canvas_set_font(canvas, FontSecondary); + snprintf(score_string, 12, "Score: %d", game_state->score); + canvas_draw_str_aligned(canvas, 85, 5, AlignLeft, AlignTop, score_string); + + } else { + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 64, 32, AlignCenter, AlignBottom, "You lost :c"); + } + + furi_mutex_release(game_state->mutex); +} + +static void game_state_init(GameState* const game_state) { + game_state->last_tick = furi_get_tick(); + game_state->dino_frame_ms = 0; + game_state->dino_icon = &I_Dino; + game_state->y_acceleration = game_state->y_speed = 0; + game_state->y_position = DINO_START_Y; + game_state->has_cactus = 0; + game_state->background_position = 0; + game_state->lost = 0; + game_state->x_speed = START_x_speed; + game_state->score = 0; + game_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); +} + +int32_t trexrunner_app() { + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent)); + + GameState* game_state = malloc(sizeof(GameState)); + game_state_init(game_state); + + if(!game_state->mutex) { + FURI_LOG_E("T-rex runner", "cannot create mutex\r\n"); + free(game_state); + return 255; + } + // BEGIN IMPLEMENTATION + + // Set system callbacks + ViewPort* view_port = view_port_alloc(); + view_port_draw_callback_set(view_port, render_callback, game_state); + view_port_input_callback_set(view_port, input_callback, event_queue); + game_state->timer = furi_timer_alloc(timer_callback, FuriTimerTypePeriodic, game_state); + + furi_timer_start(game_state->timer, (uint32_t)furi_kernel_get_tick_frequency() / FPS); + + // Open GUI and register view_port + Gui* gui = furi_record_open("gui"); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + PluginEvent event; + for(bool processing = true; processing && !game_state->lost;) { + FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); + if(event_status == FuriStatusOk) { + // press events + if(event.type == EventTypeKey) { + if(event.input.type == InputTypeShort) { + switch(event.input.key) { + case InputKeyUp: + break; + case InputKeyDown: + break; + case InputKeyLeft: + break; + case InputKeyRight: + break; + case InputKeyOk: + if(game_state->y_position == DINO_START_Y) + game_state->y_speed = JUMP_SPEED; + break; + case InputKeyMAX: + break; + case InputKeyBack: + // Exit the app + processing = false; + break; + } + } + } + } else { + // event timeout + ; + } + if(game_state->lost) { + furi_message_queue_get( + event_queue, &event, 1500); //Sleep to show the "you lost" message + } + view_port_update(view_port); + furi_mutex_release(game_state->mutex); + } + + view_port_enabled_set(view_port, false); + gui_remove_view_port(gui, view_port); + furi_record_close("gui"); + view_port_free(view_port); + furi_message_queue_free(event_queue); + furi_mutex_free(game_state->mutex); + furi_timer_free(game_state->timer); + free(game_state); + + return 0; +} diff --git a/applications/external/t_rex_runner/trexrunner_icon.png b/applications/external/t_rex_runner/trexrunner_icon.png new file mode 100644 index 0000000000..0b83293276 Binary files /dev/null and b/applications/external/t_rex_runner/trexrunner_icon.png differ diff --git a/applications/external/t_rex_runner/uncut_assets/HorizonLine0.png b/applications/external/t_rex_runner/uncut_assets/HorizonLine0.png new file mode 100644 index 0000000000..b326cbb7f4 Binary files /dev/null and b/applications/external/t_rex_runner/uncut_assets/HorizonLine0.png differ diff --git a/applications/external/t_rex_runner/uncut_assets/HorizonLine1.png b/applications/external/t_rex_runner/uncut_assets/HorizonLine1.png new file mode 100644 index 0000000000..f63a0a74ee Binary files /dev/null and b/applications/external/t_rex_runner/uncut_assets/HorizonLine1.png differ diff --git a/applications/external/tama_p1/application.fam b/applications/external/tama_p1/application.fam index 93ee53aa53..e51281ff71 100644 --- a/applications/external/tama_p1/application.fam +++ b/applications/external/tama_p1/application.fam @@ -1,5 +1,5 @@ App( - appid="TAMA_P1", + appid="tama_p1", name="Tamagotchi", apptype=FlipperAppType.EXTERNAL, entry_point="tama_p1_app", diff --git a/applications/external/tama_p1/tama.h b/applications/external/tama_p1/tama.h index e8eecc945d..7cbcbcfb11 100644 --- a/applications/external/tama_p1/tama.h +++ b/applications/external/tama_p1/tama.h @@ -4,14 +4,14 @@ #include "tamalib/tamalib.h" #define TAG "TamaP1" -#define TAMA_ROM_PATH EXT_PATH("tama_p1/rom.bin") +#define TAMA_ROM_PATH APP_DATA_PATH("rom.bin") #define TAMA_SCREEN_SCALE_FACTOR 2 #define TAMA_LCD_ICON_SIZE 14 #define TAMA_LCD_ICON_MARGIN 1 #define STATE_FILE_MAGIC "TLST" #define STATE_FILE_VERSION 2 -#define TAMA_SAVE_PATH EXT_PATH("tama_p1/save.bin") +#define TAMA_SAVE_PATH APP_DATA_PATH("save.bin") typedef struct { FuriThread* thread; diff --git a/applications/external/tama_p1/tama_p1.c b/applications/external/tama_p1/tama_p1.c index 1d7939a019..f098aa1577 100644 --- a/applications/external/tama_p1/tama_p1.c +++ b/applications/external/tama_p1/tama_p1.c @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -719,6 +720,7 @@ static void tama_p1_init(TamaApp* const ctx) { // Load ROM Storage* storage = furi_record_open(RECORD_STORAGE); + storage_common_migrate(storage, EXT_PATH("tama_p1"), STORAGE_APP_DATA_PATH_PREFIX); FileInfo fi; if(storage_common_stat(storage, TAMA_ROM_PATH, &fi) == FSE_OK) { File* rom_file = storage_file_alloc(storage); @@ -750,6 +752,9 @@ static void tama_p1_init(TamaApp* const ctx) { if(ctx->rom != NULL) { // Init TIM2 // 64KHz + + furi_hal_bus_enable(FuriHalBusTIM2); + LL_TIM_InitTypeDef tim_init = { .Prescaler = 999, .CounterMode = LL_TIM_COUNTERMODE_UP, @@ -782,6 +787,7 @@ static void tama_p1_deinit(TamaApp* const ctx) { if(ctx->rom != NULL) { tamalib_release(); furi_thread_free(ctx->thread); + furi_hal_bus_disable(FuriHalBusTIM2); free(ctx->rom); } } diff --git a/applications/external/tanksgame/application.fam b/applications/external/tanksgame/application.fam index 7489882972..f4fe497230 100644 --- a/applications/external/tanksgame/application.fam +++ b/applications/external/tanksgame/application.fam @@ -1,5 +1,5 @@ App( - appid="Tanks", + appid="tanks", name="Tanks", apptype=FlipperAppType.EXTERNAL, entry_point="tanks_game_app", diff --git a/applications/external/tanksgame/tanks_game.c b/applications/external/tanksgame/tanks_game.c index e8fb988fa4..52ce6b36e5 100644 --- a/applications/external/tanksgame/tanks_game.c +++ b/applications/external/tanksgame/tanks_game.c @@ -10,7 +10,7 @@ #include #include #include -#include +#include #include "constants.h" diff --git a/applications/external/tetris_game/application.fam b/applications/external/tetris_game/application.fam index dab2e9fe6a..eb6ab5d5e2 100644 --- a/applications/external/tetris_game/application.fam +++ b/applications/external/tetris_game/application.fam @@ -8,4 +8,7 @@ App( order=240, fap_icon="tetris_10px.png", fap_category="Games", + fap_author="@xMasterX & @jeffplang", + fap_version="1.0", + fap_description="Tetris Game", ) diff --git a/applications/external/tetris_game/tetris_game.c b/applications/external/tetris_game/tetris_game.c index 653718fd44..bd16cddfcd 100644 --- a/applications/external/tetris_game/tetris_game.c +++ b/applications/external/tetris_game/tetris_game.c @@ -387,6 +387,9 @@ int32_t tetris_game_app() { Piece* newPiece = malloc(sizeof(Piece)); uint8_t downRepeatCounter = 0; + // Call dolphin deed on game start + // dolphin_deed(DolphinDeedPluginGameStart); + for(bool processing = true; processing;) { // This 10U implicitly sets the game loop speed. downRepeatCounter relies on this value FuriStatus event_status = furi_message_queue_get(event_queue, &event, 10U); diff --git a/applications/external/text2sam/application.fam b/applications/external/text2sam/application.fam new file mode 100644 index 0000000000..d7d0e42eb5 --- /dev/null +++ b/applications/external/text2sam/application.fam @@ -0,0 +1,21 @@ +App( + appid="text2sam", + name="Text to SAM", + apptype=FlipperAppType.EXTERNAL, + entry_point="sam_app", + cdefines=["APP_SAM"], + # requires=["gui",], + requires=[ + "gui", + "dialogs", + ], + stack_size=4 * 1024, + # stack_size=2 * 1024, + fap_icon="icon.png", + fap_category="Media", + order=20, + fap_author="@Round-Pi & (Fixes by @Willy-JL)", + fap_weburl="https://github.com/Round-Pi/flipperzero-text2sam", + fap_version="1.0", + fap_description="Enter text and hear it spoken by SAM (Software Automatic Mouth)", +) diff --git a/applications/external/text2sam/icon.png b/applications/external/text2sam/icon.png new file mode 100644 index 0000000000..d13fa0c074 Binary files /dev/null and b/applications/external/text2sam/icon.png differ diff --git a/applications/external/text2sam/sam_app.cpp b/applications/external/text2sam/sam_app.cpp new file mode 100644 index 0000000000..3c7e42a74d --- /dev/null +++ b/applications/external/text2sam/sam_app.cpp @@ -0,0 +1,145 @@ +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include "stm32_sam.h" + +#define TAG "SAM" +#define SAM_SAVE_PATH APP_DATA_PATH("message.txt") +#define TEXT_BUFFER_SIZE 256 +STM32SAM voice; + +typedef enum { + EventTypeTick, + EventTypeKey, +} EventType; + +typedef struct { + EventType type; + InputEvent input; +} PluginEvent; + +typedef struct { + ViewDispatcher* view_dispatcher; + TextInput* text_input; + char input[TEXT_BUFFER_SIZE]; +} AppState; + +AppState* app_state; + +static void say_something(char* something) { + if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(1000)) { + voice.begin(); + voice.say(something); + furi_hal_speaker_release(); + } +} + +static void text_input_callback(void* ctx) { + AppState* app_state = (AppState*)ctx; + //FURI_LOG_D(TAG, "Input text: %s", app_state->input); + + // underscore_to_space(app_state->input); + for(int i = 0; app_state->input[i] != '\0'; i++) { + if(app_state->input[i] == '_') { + app_state->input[i] = ' '; + } + } + + say_something(app_state->input); +} + +static bool back_event_callback(void* ctx) { + const AppState* app_state = (AppState*)ctx; + view_dispatcher_stop(app_state->view_dispatcher); + return true; +} + +static void sam_state_init(AppState* const app_state) { + app_state->view_dispatcher = view_dispatcher_alloc(); + app_state->text_input = text_input_alloc(); +} + +static void sam_state_free(AppState* const app_state) { + text_input_free(app_state->text_input); + view_dispatcher_remove_view(app_state->view_dispatcher, 0); + view_dispatcher_free(app_state->view_dispatcher); + free(app_state); +} + +static void save_message(FuriString* save_string) { + Storage* storage = (Storage*)furi_record_open(RECORD_STORAGE); + File* file = storage_file_alloc(storage); + if(storage_file_open(file, SAM_SAVE_PATH, FSAM_WRITE, FSOM_CREATE_ALWAYS)) { + storage_file_write(file, save_string, TEXT_BUFFER_SIZE); + } + storage_file_close(file); + storage_file_free(file); + furi_record_close(RECORD_STORAGE); +} + +static bool load_messages() { + Storage* storage = (Storage*)furi_record_open(RECORD_STORAGE); + storage_common_migrate(storage, EXT_PATH("sam.txt"), SAM_SAVE_PATH); + File* file = storage_file_alloc(storage); + uint16_t bytes_read = 0; + if(storage_file_open(file, SAM_SAVE_PATH, FSAM_READ, FSOM_OPEN_EXISTING)) { + bytes_read = storage_file_read(file, app_state->input, TEXT_BUFFER_SIZE); + } + storage_file_close(file); + storage_file_free(file); + furi_record_close(RECORD_STORAGE); + return bytes_read == TEXT_BUFFER_SIZE; +} + +extern "C" int32_t sam_app(void* p) { + UNUSED(p); + app_state = (AppState*)malloc(sizeof(AppState)); + + FURI_LOG_D(TAG, "Running sam_state_init"); + sam_state_init(app_state); + + FURI_LOG_D(TAG, "Assigning text input callback"); + + load_messages(); + text_input_set_result_callback( + app_state->text_input, + text_input_callback, + app_state, + app_state->input, + TEXT_BUFFER_SIZE, + false); //clear default text + text_input_set_header_text(app_state->text_input, "Input"); + + Gui* gui = (Gui*)furi_record_open(RECORD_GUI); + + view_dispatcher_enable_queue(app_state->view_dispatcher); + + FURI_LOG_D(TAG, "Adding text input view to dispatcher"); + view_dispatcher_add_view( + app_state->view_dispatcher, 0, text_input_get_view(app_state->text_input)); + + FURI_LOG_D(TAG, "Attaching view dispatcher to GUI"); + view_dispatcher_attach_to_gui(app_state->view_dispatcher, gui, ViewDispatcherTypeFullscreen); + FURI_LOG_D(TAG, "starting view dispatcher"); + view_dispatcher_set_navigation_event_callback(app_state->view_dispatcher, back_event_callback); + view_dispatcher_set_event_callback_context(app_state->view_dispatcher, app_state); + view_dispatcher_switch_to_view(app_state->view_dispatcher, 0); + view_dispatcher_run(app_state->view_dispatcher); + + save_message((FuriString*)app_state->input); + + furi_record_close(RECORD_GUI); + sam_state_free(app_state); + + return 0; +} diff --git a/applications/external/text2sam/stm32_sam.cpp b/applications/external/text2sam/stm32_sam.cpp new file mode 100644 index 0000000000..16f6fcaab2 --- /dev/null +++ b/applications/external/text2sam/stm32_sam.cpp @@ -0,0 +1,5704 @@ + +#include "stm32_sam.h" +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// All +// +//////////////////////////////////////////////////////////////////////////////////////////// + +char input[256 + 1] = {0}; //tab39445 +//standard sam sound + +unsigned char wait1 = 7; +unsigned char wait2 = 6; + +unsigned char A, X, Y; +unsigned char mem44; +unsigned char mem47; +unsigned char mem49; +unsigned char mem39; +unsigned char mem50; +unsigned char mem51; +unsigned char mem53; +unsigned char mem56; +unsigned char mem59 = 0; + +unsigned char phonemeIndexOutput[60]; //tab47296 +unsigned char stressOutput[60]; //tab47365 +unsigned char phonemeLengthOutput[60]; //tab47416 + +// contains the soundbuffer position +int bufferpos; + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// Sam Tabs +// +//////////////////////////////////////////////////////////////////////////////////////////// + +//tab40672 +const unsigned char stressInputTable[] = {'*', '1', '2', '3', '4', '5', '6', '7', '8'}; + +//tab40682 +const unsigned char signInputTable1[] = { + ' ', '.', '?', ',', '-', 'I', 'I', 'E', 'A', 'A', 'A', 'A', 'U', 'A', 'I', 'E', 'U', + 'O', 'R', 'L', 'W', 'Y', 'W', 'R', 'L', 'W', 'Y', 'M', 'N', 'N', 'D', 'Q', 'S', 'S', + 'F', 'T', '/', '/', 'Z', 'Z', 'V', 'D', 'C', '*', 'J', '*', '*', '*', 'E', 'A', 'O', + 'A', 'O', 'U', 'B', '*', '*', 'D', '*', '*', 'G', '*', '*', 'G', '*', '*', 'P', '*', + '*', 'T', '*', '*', 'K', '*', '*', 'K', '*', '*', 'U', 'U', 'U'}; + +//tab40763 +const unsigned char signInputTable2[] = { + '*', '*', '*', '*', '*', 'Y', 'H', 'H', 'E', 'A', 'H', 'O', 'H', 'X', 'X', 'R', 'X', + 'H', 'X', 'X', 'X', 'X', 'H', '*', '*', '*', '*', '*', '*', 'X', 'X', '*', '*', 'H', + '*', 'H', 'H', 'X', '*', 'H', '*', 'H', 'H', '*', '*', '*', '*', '*', 'Y', 'Y', 'Y', + 'W', 'W', 'W', '*', '*', '*', '*', '*', '*', '*', '*', '*', 'X', '*', '*', '*', '*', + '*', '*', '*', '*', '*', '*', '*', 'X', '*', '*', 'L', 'M', 'N'}; + +//loc_9F8C +const unsigned char flags[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0x84, 0x84, 0xA4, + 0xA4, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x44, 0x44, 0x44, 0x44, 0x44, 0x4C, + 0x4C, 0x4C, 0x48, 0x4C, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x44, 0x44, 0x44, 0x44, + 0x48, 0x40, 0x4C, 0x44, 0x00, 0x00, 0xB4, 0xB4, 0xB4, 0x94, 0x94, 0x94, 0x4E, 0x4E, + 0x4E, 0x4E, 0x4E, 0x4E, 0x4E, 0x4E, 0x4E, 0x4E, 0x4E, 0x4E, 0x4B, 0x4B, 0x4B, 0x4B, + 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x80, 0xC1, 0xC1 + +}; + +//??? flags overlap flags2 +//loc_9FDA +const unsigned char flags2[] = { + 0x80, 0xC1, 0xC1, 0xC1, 0xC1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x10, 0x08, 0x0C, 0x08, 0x04, 0x40, + 0x24, 0x20, 0x20, 0x24, 0x00, 0x00, 0x24, 0x20, 0x20, 0x24, 0x20, 0x20, 0x00, 0x20, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +//tab45616??? +const unsigned char phonemeStressedLengthTable[] = { + 0x00, 0x12, 0x12, 0x12, 8, 0xB, 9, 0xB, 0xE, 0xF, 0xB, 0x10, 0xC, 6, 6, 0xE, + 0xC, 0xE, 0xC, 0xB, 8, 8, 0xB, 0xA, 9, 8, 8, 8, 8, 8, 3, 5, + 2, 2, 2, 2, 2, 2, 6, 6, 8, 6, 6, 2, 9, 4, 2, 1, + 0xE, 0xF, 0xF, 0xF, 0xE, 0xE, 8, 2, 2, 7, 2, 1, 7, 2, 2, 7, + 2, 2, 8, 2, 2, 6, 2, 2, 7, 2, 4, 7, 1, 4, 5, 5}; + +//tab45536??? +const unsigned char phonemeLengthTable[] = { + 0, 0x12, 0x12, 0x12, 8, 8, 8, 8, 8, 0xB, 6, 0xC, 0xA, 5, 5, 0xB, 0xA, 0xA, 0xA, 9, + 8, 7, 9, 7, 6, 8, 6, 7, 7, 7, 2, 5, 2, 2, 2, 2, 2, 2, 6, 6, + 7, 6, 6, 2, 8, 3, 1, 0x1E, 0xD, 0xC, 0xC, 0xC, 0xE, 9, 6, 1, 2, 5, 1, 1, + 6, 1, 2, 6, 1, 2, 8, 2, 2, 4, 2, 2, 6, 1, 4, 6, 1, 4, 0xC7, 0xFF}; + +/* + + Ind | phoneme | flags | + -----|---------|----------| + 0 | * | 00000000 | + 1 | .* | 00000000 | + 2 | ?* | 00000000 | + 3 | ,* | 00000000 | + 4 | -* | 00000000 | + + VOWELS + 5 | IY | 10100100 | + 6 | IH | 10100100 | + 7 | EH | 10100100 | + 8 | AE | 10100100 | + 9 | AA | 10100100 | + 10 | AH | 10100100 | + 11 | AO | 10000100 | + 17 | OH | 10000100 | + 12 | UH | 10000100 | + 16 | UX | 10000100 | + 15 | ER | 10000100 | + 13 | AX | 10100100 | + 14 | IX | 10100100 | + + DIPHTONGS + 48 | EY | 10110100 | + 49 | AY | 10110100 | + 50 | OY | 10110100 | + 51 | AW | 10010100 | + 52 | OW | 10010100 | + 53 | UW | 10010100 | + + + 21 | YX | 10000100 | + 20 | WX | 10000100 | + 18 | RX | 10000100 | + 19 | LX | 10000100 | + 37 | /X | 01000000 | + 30 | DX | 01001000 | + + + 22 | WH | 01000100 | + + + VOICED CONSONANTS + 23 | R* | 01000100 | + 24 | L* | 01000100 | + 25 | W* | 01000100 | + 26 | Y* | 01000100 | + 27 | M* | 01001100 | + 28 | N* | 01001100 | + 29 | NX | 01001100 | + 54 | B* | 01001110 | + 57 | D* | 01001110 | + 60 | G* | 01001110 | + 44 | J* | 01001100 | + 38 | Z* | 01000100 | + 39 | ZH | 01000100 | + 40 | V* | 01000100 | + 41 | DH | 01000100 | + + unvoiced CONSONANTS + 32 | S* | 01000000 | + 33 | SH | 01000000 | + 34 | F* | 01000000 | + 35 | TH | 01000000 | + 66 | P* | 01001011 | + 69 | T* | 01001011 | + 72 | K* | 01001011 | + 42 | CH | 01001000 | + 36 | /H | 01000000 | + + 43 | ** | 01000000 | + 45 | ** | 01000100 | + 46 | ** | 00000000 | + 47 | ** | 00000000 | + + + 55 | ** | 01001110 | + 56 | ** | 01001110 | + 58 | ** | 01001110 | + 59 | ** | 01001110 | + 61 | ** | 01001110 | + 62 | ** | 01001110 | + 63 | GX | 01001110 | + 64 | ** | 01001110 | + 65 | ** | 01001110 | + 67 | ** | 01001011 | + 68 | ** | 01001011 | + 70 | ** | 01001011 | + 71 | ** | 01001011 | + 73 | ** | 01001011 | + 74 | ** | 01001011 | + 75 | KX | 01001011 | + 76 | ** | 01001011 | + 77 | ** | 01001011 | + + + SPECIAL + 78 | UL | 10000000 | + 79 | UM | 11000001 | + 80 | UN | 11000001 | + 31 | Q* | 01001100 | + +*/ + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// RenderTabs +// +//////////////////////////////////////////////////////////////////////////////////////////// + +const unsigned char tab48426[5] = {0x18, 0x1A, 0x17, 0x17, 0x17}; + +const unsigned char tab47492[] = {0, 0, 0xE0, 0xE6, 0xEC, 0xF3, 0xF9, 0, 6, 0xC, 6}; + +const unsigned char amplitudeRescale[] = { + 0, + 1, + 2, + 2, + 2, + 3, + 3, + 4, + 4, + 5, + 6, + 8, + 9, + 0xB, + 0xD, + 0xF, + 0 //17 elements? +}; + +// Used to decide which phoneme's blend lengths. The candidate with the lower score is selected. +// tab45856 +const unsigned char blendRank[] = {0, 0x1F, 0x1F, 0x1F, 0x1F, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 5, 5, 2, 0xA, 2, 8, + 5, 5, 0xB, 0xA, 9, 8, 8, 0xA0, 8, 8, + 0x17, 0x1F, 0x12, 0x12, 0x12, 0x12, 0x1E, 0x1E, 0x14, 0x14, + 0x14, 0x14, 0x17, 0x17, 0x1A, 0x1A, 0x1D, 0x1D, 2, 2, + 2, 2, 2, 2, 0x1A, 0x1D, 0x1B, 0x1A, 0x1D, 0x1B, + 0x1A, 0x1D, 0x1B, 0x1A, 0x1D, 0x1B, 0x17, 0x1D, 0x17, 0x17, + 0x1D, 0x17, 0x17, 0x1D, 0x17, 0x17, 0x1D, 0x17, 0x17, 0x17}; + +// Number of frames at the end of a phoneme devoted to interpolating to next phoneme's final value +//tab45696 +const unsigned char outBlendLength[] = {0, 2, 2, 2, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 3, 2, 4, 4, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 2, 2, 2, 1, 0, 1, 0, 1, 0, 5, + 5, 5, 5, 5, 4, 4, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, + 0, 1, 2, 0, 2, 2, 0, 1, 3, 0, 2, 3, 0, 2, 0xA0, 0xA0}; + +// Number of frames at beginning of a phoneme devoted to interpolating to phoneme's final value +// tab45776 +const unsigned char inBlendLength[] = {0, 2, 2, 2, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 3, 3, 4, 4, 3, 3, 3, 3, 3, 1, 2, 3, 2, 1, + 3, 3, 3, 3, 1, 1, 3, 3, 3, 2, 2, 3, 2, 3, 0, 0, + 5, 5, 5, 5, 4, 4, 2, 0, 2, 2, 0, 3, 2, 0, 4, 2, + 0, 3, 2, 0, 2, 2, 0, 2, 3, 0, 3, 3, 0, 3, 0xB0, 0xA0}; + +// Looks like it's used as bit flags +// High bits masked by 248 (11111000) +// +// 32: S* 241 11110001 +// 33: SH 226 11100010 +// 34: F* 211 11010011 +// 35: TH 187 10111011 +// 36: /H 124 01111100 +// 37: /X 149 10010101 +// 38: Z* 1 00000001 +// 39: ZH 2 00000010 +// 40: V* 3 00000011 +// 41: DH 3 00000011 +// 43: ** 114 01110010 +// 45: ** 2 00000010 +// 67: ** 27 00011011 +// 70: ** 25 00011001 +// tab45936 +const unsigned char sampledConsonantFlags[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xF1, 0xE2, 0xD3, 0xBB, 0x7C, 0x95, 1, 2, + 3, 3, 0, 0x72, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0x1B, 0, 0, 0x19, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +//tab45056 +unsigned char freq1data[] = { + 0x00, 0x13, 0x13, 0x13, 0x13, 0xA, 0xE, 0x12, 0x18, 0x1A, 0x16, 0x14, 0x10, 0x14, 0xE, 0x12, + 0xE, 0x12, 0x12, 0x10, 0xC, 0xE, 0xA, 0x12, 0xE, 0xA, 8, 6, 6, 6, 6, 0x11, + 6, 6, 6, 6, 0xE, 0x10, 9, 0xA, 8, 0xA, 6, 6, 6, 5, 6, 0, + 0x12, 0x1A, 0x14, 0x1A, 0x12, 0xC, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 0xA, 0xA, 6, 6, 6, 0x2C, 0x13}; + +//tab451356 +unsigned char freq2data[] = {0x00, 0x43, 0x43, 0x43, 0x43, 0x54, 0x48, 0x42, 0x3E, 0x28, + 0x2C, 0x1E, 0x24, 0x2C, 0x48, 0x30, 0x24, 0x1E, 0x32, 0x24, + 0x1C, 0x44, 0x18, 0x32, 0x1E, 0x18, 0x52, 0x2E, 0x36, 0x56, + 0x36, 0x43, 0x49, 0x4F, 0x1A, 0x42, 0x49, 0x25, 0x33, 0x42, + 0x28, 0x2F, 0x4F, 0x4F, 0x42, 0x4F, 0x6E, 0x00, 0x48, 0x26, + 0x1E, 0x2A, 0x1E, 0x22, 0x1A, 0x1A, 0x1A, 0x42, 0x42, 0x42, + 0x6E, 0x6E, 0x6E, 0x54, 0x54, 0x54, 0x1A, 0x1A, 0x1A, 0x42, + 0x42, 0x42, 0x6D, 0x56, 0x6D, 0x54, 0x54, 0x54, 0x7F, 0x7F}; +//tab45216 +unsigned char freq3data[] = {0x00, 0x5B, 0x5B, 0x5B, 0x5B, 0x6E, 0x5D, 0x5B, 0x58, 0x59, + 0x57, 0x58, 0x52, 0x59, 0x5D, 0x3E, 0x52, 0x58, 0x3E, 0x6E, + 0x50, 0x5D, 0x5A, 0x3C, 0x6E, 0x5A, 0x6E, 0x51, 0x79, 0x65, + 0x79, 0x5B, 0x63, 0x6A, 0x51, 0x79, 0x5D, 0x52, 0x5D, 0x67, + 0x4C, 0x5D, 0x65, 0x65, 0x79, 0x65, 0x79, 0x00, 0x5A, 0x58, + 0x58, 0x58, 0x58, 0x52, 0x51, 0x51, 0x51, 0x79, 0x79, 0x79, + 0x70, 0x6E, 0x6E, 0x5E, 0x5E, 0x5E, 0x51, 0x51, 0x51, 0x79, + 0x79, 0x79, 0x65, 0x65, 0x70, 0x5E, 0x5E, 0x5E, 0x08, 0x01}; + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// Reciter +// +//////////////////////////////////////////////////////////////////////////////////////////// + +unsigned char inputtemp[256]; // secure copy of input tab36096 + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// Render +// +//////////////////////////////////////////////////////////////////////////////////////////// + +//timetable for more accurate c64 simulation +int timetable[5][5] = { + {162, 167, 167, 127, 128}, + {226, 60, 60, 0, 0}, + {225, 60, 59, 0, 0}, + {200, 0, 0, 54, 55}, + {199, 0, 0, 54, 54}}; + +unsigned oldtimetableindex; + +const unsigned char ampl1data[] = {0, 0, 0, 0, 0, 0xD, 0xD, 0xE, 0xF, 0xF, 0xF, 0xF, + 0xF, 0xC, 0xD, 0xC, 0xF, 0xF, 0xD, 0xD, 0xD, 0xE, 0xD, 0xC, + 0xD, 0xD, 0xD, 0xC, 9, 9, 0, 0, 0, 0, 0, 0, + 0, 0, 0xB, 0xB, 0xB, 0xB, 0, 0, 1, 0xB, 0, 2, + 0xE, 0xF, 0xF, 0xF, 0xF, 0xD, 2, 4, 0, 2, 4, 0, + 1, 4, 0, 1, 4, 0, 0, 0, 0, 0, 0, 0, + 0, 0xC, 0, 0, 0, 0, 0xF, 0xF}; + +const unsigned char ampl2data[] = { + 0, 0, 0, 0, 0, 0xA, 0xB, 0xD, 0xE, 0xD, 0xC, 0xC, 0xB, 9, 0xB, 0xB, 0xC, 0xC, 0xC, 8, + 8, 0xC, 8, 0xA, 8, 8, 0xA, 3, 9, 6, 0, 0, 0, 0, 0, 0, 0, 0, 3, 5, + 3, 4, 0, 0, 0, 5, 0xA, 2, 0xE, 0xD, 0xC, 0xD, 0xC, 8, 0, 1, 0, 0, 1, 0, + 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0xA, 0, 0, 0xA, 0, 0, 0}; + +const unsigned char ampl3data[] = {0, 0, 0, 0, 0, 8, 7, 8, 8, 1, 1, 0, 1, 0, 7, 5, + 1, 0, 6, 1, 0, 7, 0, 5, 1, 0, 8, 0, 0, 3, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0xE, 1, + 9, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 5, 0, 0x13, 0x10}; + +//tab42240 +const signed char sinus[256] = { + 0, 3, 6, 9, 12, 16, 19, 22, 25, 28, 31, 34, 37, 40, 43, 46, + 49, 51, 54, 57, 60, 63, 65, 68, 71, 73, 76, 78, 81, 83, 85, 88, + 90, 92, 94, 96, 98, 100, 102, 104, 106, 107, 109, 111, 112, 113, 115, 116, + 117, 118, 120, 121, 122, 122, 123, 124, 125, 125, 126, 126, 126, 127, 127, 127, + 127, 127, 127, 127, 126, 126, 126, 125, 125, 124, 123, 122, 122, 121, 120, 118, + 117, 116, 115, 113, 112, 111, 109, 107, 106, 104, 102, 100, 98, 96, 94, 92, + 90, 88, 85, 83, 81, 78, 76, 73, 71, 68, 65, 63, 60, 57, 54, 51, + 49, 46, 43, 40, 37, 34, 31, 28, 25, 22, 19, 16, 12, 9, 6, 3, + 0, -3, -6, -9, -12, -16, -19, -22, -25, -28, -31, -34, -37, -40, -43, -46, + -49, -51, -54, -57, -60, -63, -65, -68, -71, -73, -76, -78, -81, -83, -85, -88, + -90, -92, -94, -96, -98, -100, -102, -104, -106, -107, -109, -111, -112, -113, -115, -116, + -117, -118, -120, -121, -122, -122, -123, -124, -125, -125, -126, -126, -126, -127, -127, -127, + -127, -127, -127, -127, -126, -126, -126, -125, -125, -124, -123, -122, -122, -121, -120, -118, + -117, -116, -115, -113, -112, -111, -109, -107, -106, -104, -102, -100, -98, -96, -94, -92, + -90, -88, -85, -83, -81, -78, -76, -73, -71, -68, -65, -63, -60, -57, -54, -51, + -49, -46, -43, -40, -37, -34, -31, -28, -25, -22, -19, -16, -12, -9, -6, -3}; + +//tab42496 +const unsigned char rectangle[] = { + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, + 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, + 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, + 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, + 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, + 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, + 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, + 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, + 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, + 0x70}; + +//random data ? +const unsigned char sampleTable[0x500] = { + //00 + + 0x38, + 0x84, + 0x6B, + 0x19, + 0xC6, + 0x63, + 0x18, + 0x86, + 0x73, + 0x98, + 0xC6, + 0xB1, + 0x1C, + 0xCA, + 0x31, + 0x8C, + 0xC7, + 0x31, + 0x88, + 0xC2, + 0x30, + 0x98, + 0x46, + 0x31, + 0x18, + 0xC6, + 0x35, + 0xC, + 0xCA, + 0x31, + 0xC, + 0xC6 + //20 + , + 0x21, + 0x10, + 0x24, + 0x69, + 0x12, + 0xC2, + 0x31, + 0x14, + 0xC4, + 0x71, + 8, + 0x4A, + 0x22, + 0x49, + 0xAB, + 0x6A, + 0xA8, + 0xAC, + 0x49, + 0x51, + 0x32, + 0xD5, + 0x52, + 0x88, + 0x93, + 0x6C, + 0x94, + 0x22, + 0x15, + 0x54, + 0xD2, + 0x25 + //40 + , + 0x96, + 0xD4, + 0x50, + 0xA5, + 0x46, + 0x21, + 8, + 0x85, + 0x6B, + 0x18, + 0xC4, + 0x63, + 0x10, + 0xCE, + 0x6B, + 0x18, + 0x8C, + 0x71, + 0x19, + 0x8C, + 0x63, + 0x35, + 0xC, + 0xC6, + 0x33, + 0x99, + 0xCC, + 0x6C, + 0xB5, + 0x4E, + 0xA2, + 0x99 + //60 + , + 0x46, + 0x21, + 0x28, + 0x82, + 0x95, + 0x2E, + 0xE3, + 0x30, + 0x9C, + 0xC5, + 0x30, + 0x9C, + 0xA2, + 0xB1, + 0x9C, + 0x67, + 0x31, + 0x88, + 0x66, + 0x59, + 0x2C, + 0x53, + 0x18, + 0x84, + 0x67, + 0x50, + 0xCA, + 0xE3, + 0xA, + 0xAC, + 0xAB, + 0x30 + //80 + , + 0xAC, + 0x62, + 0x30, + 0x8C, + 0x63, + 0x10, + 0x94, + 0x62, + 0xB1, + 0x8C, + 0x82, + 0x28, + 0x96, + 0x33, + 0x98, + 0xD6, + 0xB5, + 0x4C, + 0x62, + 0x29, + 0xA5, + 0x4A, + 0xB5, + 0x9C, + 0xC6, + 0x31, + 0x14, + 0xD6, + 0x38, + 0x9C, + 0x4B, + 0xB4 + //A0 + , + 0x86, + 0x65, + 0x18, + 0xAE, + 0x67, + 0x1C, + 0xA6, + 0x63, + 0x19, + 0x96, + 0x23, + 0x19, + 0x84, + 0x13, + 8, + 0xA6, + 0x52, + 0xAC, + 0xCA, + 0x22, + 0x89, + 0x6E, + 0xAB, + 0x19, + 0x8C, + 0x62, + 0x34, + 0xC4, + 0x62, + 0x19, + 0x86, + 0x63 + //C0 + , + 0x18, + 0xC4, + 0x23, + 0x58, + 0xD6, + 0xA3, + 0x50, + 0x42, + 0x54, + 0x4A, + 0xAD, + 0x4A, + 0x25, + 0x11, + 0x6B, + 0x64, + 0x89, + 0x4A, + 0x63, + 0x39, + 0x8A, + 0x23, + 0x31, + 0x2A, + 0xEA, + 0xA2, + 0xA9, + 0x44, + 0xC5, + 0x12, + 0xCD, + 0x42 + //E0 + , + 0x34, + 0x8C, + 0x62, + 0x18, + 0x8C, + 0x63, + 0x11, + 0x48, + 0x66, + 0x31, + 0x9D, + 0x44, + 0x33, + 0x1D, + 0x46, + 0x31, + 0x9C, + 0xC6, + 0xB1, + 0xC, + 0xCD, + 0x32, + 0x88, + 0xC4, + 0x73, + 0x18, + 0x86, + 0x73, + 8, + 0xD6, + 0x63, + 0x58 + //100 + , + 7, + 0x81, + 0xE0, + 0xF0, + 0x3C, + 7, + 0x87, + 0x90, + 0x3C, + 0x7C, + 0xF, + 0xC7, + 0xC0, + 0xC0, + 0xF0, + 0x7C, + 0x1E, + 7, + 0x80, + 0x80, + 0, + 0x1C, + 0x78, + 0x70, + 0xF1, + 0xC7, + 0x1F, + 0xC0, + 0xC, + 0xFE, + 0x1C, + 0x1F + //120 + , + 0x1F, + 0xE, + 0xA, + 0x7A, + 0xC0, + 0x71, + 0xF2, + 0x83, + 0x8F, + 3, + 0xF, + 0xF, + 0xC, + 0, + 0x79, + 0xF8, + 0x61, + 0xE0, + 0x43, + 0xF, + 0x83, + 0xE7, + 0x18, + 0xF9, + 0xC1, + 0x13, + 0xDA, + 0xE9, + 0x63, + 0x8F, + 0xF, + 0x83 + //140 + , + 0x83, + 0x87, + 0xC3, + 0x1F, + 0x3C, + 0x70, + 0xF0, + 0xE1, + 0xE1, + 0xE3, + 0x87, + 0xB8, + 0x71, + 0xE, + 0x20, + 0xE3, + 0x8D, + 0x48, + 0x78, + 0x1C, + 0x93, + 0x87, + 0x30, + 0xE1, + 0xC1, + 0xC1, + 0xE4, + 0x78, + 0x21, + 0x83, + 0x83, + 0xC3 + //160 + , + 0x87, + 6, + 0x39, + 0xE5, + 0xC3, + 0x87, + 7, + 0xE, + 0x1C, + 0x1C, + 0x70, + 0xF4, + 0x71, + 0x9C, + 0x60, + 0x36, + 0x32, + 0xC3, + 0x1E, + 0x3C, + 0xF3, + 0x8F, + 0xE, + 0x3C, + 0x70, + 0xE3, + 0xC7, + 0x8F, + 0xF, + 0xF, + 0xE, + 0x3C + //180 + , + 0x78, + 0xF0, + 0xE3, + 0x87, + 6, + 0xF0, + 0xE3, + 7, + 0xC1, + 0x99, + 0x87, + 0xF, + 0x18, + 0x78, + 0x70, + 0x70, + 0xFC, + 0xF3, + 0x10, + 0xB1, + 0x8C, + 0x8C, + 0x31, + 0x7C, + 0x70, + 0xE1, + 0x86, + 0x3C, + 0x64, + 0x6C, + 0xB0, + 0xE1 + //1A0 + , + 0xE3, + 0xF, + 0x23, + 0x8F, + 0xF, + 0x1E, + 0x3E, + 0x38, + 0x3C, + 0x38, + 0x7B, + 0x8F, + 7, + 0xE, + 0x3C, + 0xF4, + 0x17, + 0x1E, + 0x3C, + 0x78, + 0xF2, + 0x9E, + 0x72, + 0x49, + 0xE3, + 0x25, + 0x36, + 0x38, + 0x58, + 0x39, + 0xE2, + 0xDE + //1C0 + , + 0x3C, + 0x78, + 0x78, + 0xE1, + 0xC7, + 0x61, + 0xE1, + 0xE1, + 0xB0, + 0xF0, + 0xF0, + 0xC3, + 0xC7, + 0xE, + 0x38, + 0xC0, + 0xF0, + 0xCE, + 0x73, + 0x73, + 0x18, + 0x34, + 0xB0, + 0xE1, + 0xC7, + 0x8E, + 0x1C, + 0x3C, + 0xF8, + 0x38, + 0xF0, + 0xE1 + //1E0 + , + 0xC1, + 0x8B, + 0x86, + 0x8F, + 0x1C, + 0x78, + 0x70, + 0xF0, + 0x78, + 0xAC, + 0xB1, + 0x8F, + 0x39, + 0x31, + 0xDB, + 0x38, + 0x61, + 0xC3, + 0xE, + 0xE, + 0x38, + 0x78, + 0x73, + 0x17, + 0x1E, + 0x39, + 0x1E, + 0x38, + 0x64, + 0xE1, + 0xF1, + 0xC1 + //200 + , + 0x4E, + 0xF, + 0x40, + 0xA2, + 2, + 0xC5, + 0x8F, + 0x81, + 0xA1, + 0xFC, + 0x12, + 8, + 0x64, + 0xE0, + 0x3C, + 0x22, + 0xE0, + 0x45, + 7, + 0x8E, + 0xC, + 0x32, + 0x90, + 0xF0, + 0x1F, + 0x20, + 0x49, + 0xE0, + 0xF8, + 0xC, + 0x60, + 0xF0 + //220 + , + 0x17, + 0x1A, + 0x41, + 0xAA, + 0xA4, + 0xD0, + 0x8D, + 0x12, + 0x82, + 0x1E, + 0x1E, + 3, + 0xF8, + 0x3E, + 3, + 0xC, + 0x73, + 0x80, + 0x70, + 0x44, + 0x26, + 3, + 0x24, + 0xE1, + 0x3E, + 4, + 0x4E, + 4, + 0x1C, + 0xC1, + 9, + 0xCC + //240 + , + 0x9E, + 0x90, + 0x21, + 7, + 0x90, + 0x43, + 0x64, + 0xC0, + 0xF, + 0xC6, + 0x90, + 0x9C, + 0xC1, + 0x5B, + 3, + 0xE2, + 0x1D, + 0x81, + 0xE0, + 0x5E, + 0x1D, + 3, + 0x84, + 0xB8, + 0x2C, + 0xF, + 0x80, + 0xB1, + 0x83, + 0xE0, + 0x30, + 0x41 + //260 + , + 0x1E, + 0x43, + 0x89, + 0x83, + 0x50, + 0xFC, + 0x24, + 0x2E, + 0x13, + 0x83, + 0xF1, + 0x7C, + 0x4C, + 0x2C, + 0xC9, + 0xD, + 0x83, + 0xB0, + 0xB5, + 0x82, + 0xE4, + 0xE8, + 6, + 0x9C, + 7, + 0xA0, + 0x99, + 0x1D, + 7, + 0x3E, + 0x82, + 0x8F + //280 + , + 0x70, + 0x30, + 0x74, + 0x40, + 0xCA, + 0x10, + 0xE4, + 0xE8, + 0xF, + 0x92, + 0x14, + 0x3F, + 6, + 0xF8, + 0x84, + 0x88, + 0x43, + 0x81, + 0xA, + 0x34, + 0x39, + 0x41, + 0xC6, + 0xE3, + 0x1C, + 0x47, + 3, + 0xB0, + 0xB8, + 0x13, + 0xA, + 0xC2 + //2A0 + , + 0x64, + 0xF8, + 0x18, + 0xF9, + 0x60, + 0xB3, + 0xC0, + 0x65, + 0x20, + 0x60, + 0xA6, + 0x8C, + 0xC3, + 0x81, + 0x20, + 0x30, + 0x26, + 0x1E, + 0x1C, + 0x38, + 0xD3, + 1, + 0xB0, + 0x26, + 0x40, + 0xF4, + 0xB, + 0xC3, + 0x42, + 0x1F, + 0x85, + 0x32 + //2C0 + , + 0x26, + 0x60, + 0x40, + 0xC9, + 0xCB, + 1, + 0xEC, + 0x11, + 0x28, + 0x40, + 0xFA, + 4, + 0x34, + 0xE0, + 0x70, + 0x4C, + 0x8C, + 0x1D, + 7, + 0x69, + 3, + 0x16, + 0xC8, + 4, + 0x23, + 0xE8, + 0xC6, + 0x9A, + 0xB, + 0x1A, + 3, + 0xE0 + //2E0 + , + 0x76, + 6, + 5, + 0xCF, + 0x1E, + 0xBC, + 0x58, + 0x31, + 0x71, + 0x66, + 0, + 0xF8, + 0x3F, + 4, + 0xFC, + 0xC, + 0x74, + 0x27, + 0x8A, + 0x80, + 0x71, + 0xC2, + 0x3A, + 0x26, + 6, + 0xC0, + 0x1F, + 5, + 0xF, + 0x98, + 0x40, + 0xAE + //300 + , + 1, + 0x7F, + 0xC0, + 7, + 0xFF, + 0, + 0xE, + 0xFE, + 0, + 3, + 0xDF, + 0x80, + 3, + 0xEF, + 0x80, + 0x1B, + 0xF1, + 0xC2, + 0, + 0xE7, + 0xE0, + 0x18, + 0xFC, + 0xE0, + 0x21, + 0xFC, + 0x80, + 0x3C, + 0xFC, + 0x40, + 0xE, + 0x7E + //320 + , + 0, + 0x3F, + 0x3E, + 0, + 0xF, + 0xFE, + 0, + 0x1F, + 0xFF, + 0, + 0x3E, + 0xF0, + 7, + 0xFC, + 0, + 0x7E, + 0x10, + 0x3F, + 0xFF, + 0, + 0x3F, + 0x38, + 0xE, + 0x7C, + 1, + 0x87, + 0xC, + 0xFC, + 0xC7, + 0, + 0x3E, + 4 + //340 + , + 0xF, + 0x3E, + 0x1F, + 0xF, + 0xF, + 0x1F, + 0xF, + 2, + 0x83, + 0x87, + 0xCF, + 3, + 0x87, + 0xF, + 0x3F, + 0xC0, + 7, + 0x9E, + 0x60, + 0x3F, + 0xC0, + 3, + 0xFE, + 0, + 0x3F, + 0xE0, + 0x77, + 0xE1, + 0xC0, + 0xFE, + 0xE0, + 0xC3 + //360 + , + 0xE0, + 1, + 0xDF, + 0xF8, + 3, + 7, + 0, + 0x7E, + 0x70, + 0, + 0x7C, + 0x38, + 0x18, + 0xFE, + 0xC, + 0x1E, + 0x78, + 0x1C, + 0x7C, + 0x3E, + 0xE, + 0x1F, + 0x1E, + 0x1E, + 0x3E, + 0, + 0x7F, + 0x83, + 7, + 0xDB, + 0x87, + 0x83 + //380 + , + 7, + 0xC7, + 7, + 0x10, + 0x71, + 0xFF, + 0, + 0x3F, + 0xE2, + 1, + 0xE0, + 0xC1, + 0xC3, + 0xE1, + 0, + 0x7F, + 0xC0, + 5, + 0xF0, + 0x20, + 0xF8, + 0xF0, + 0x70, + 0xFE, + 0x78, + 0x79, + 0xF8, + 2, + 0x3F, + 0xC, + 0x8F, + 3 + //3a0 + , + 0xF, + 0x9F, + 0xE0, + 0xC1, + 0xC7, + 0x87, + 3, + 0xC3, + 0xC3, + 0xB0, + 0xE1, + 0xE1, + 0xC1, + 0xE3, + 0xE0, + 0x71, + 0xF0, + 0, + 0xFC, + 0x70, + 0x7C, + 0xC, + 0x3E, + 0x38, + 0xE, + 0x1C, + 0x70, + 0xC3, + 0xC7, + 3, + 0x81, + 0xC1 + //3c0 + , + 0xC7, + 0xE7, + 0, + 0xF, + 0xC7, + 0x87, + 0x19, + 9, + 0xEF, + 0xC4, + 0x33, + 0xE0, + 0xC1, + 0xFC, + 0xF8, + 0x70, + 0xF0, + 0x78, + 0xF8, + 0xF0, + 0x61, + 0xC7, + 0, + 0x1F, + 0xF8, + 1, + 0x7C, + 0xF8, + 0xF0, + 0x78, + 0x70, + 0x3C + //3e0 + , + 0x7C, + 0xCE, + 0xE, + 0x21, + 0x83, + 0xCF, + 8, + 7, + 0x8F, + 8, + 0xC1, + 0x87, + 0x8F, + 0x80, + 0xC7, + 0xE3, + 0, + 7, + 0xF8, + 0xE0, + 0xEF, + 0, + 0x39, + 0xF7, + 0x80, + 0xE, + 0xF8, + 0xE1, + 0xE3, + 0xF8, + 0x21, + 0x9F + //400 + , + 0xC0, + 0xFF, + 3, + 0xF8, + 7, + 0xC0, + 0x1F, + 0xF8, + 0xC4, + 4, + 0xFC, + 0xC4, + 0xC1, + 0xBC, + 0x87, + 0xF0, + 0xF, + 0xC0, + 0x7F, + 5, + 0xE0, + 0x25, + 0xEC, + 0xC0, + 0x3E, + 0x84, + 0x47, + 0xF0, + 0x8E, + 3, + 0xF8, + 3 + //420 + , + 0xFB, + 0xC0, + 0x19, + 0xF8, + 7, + 0x9C, + 0xC, + 0x17, + 0xF8, + 7, + 0xE0, + 0x1F, + 0xA1, + 0xFC, + 0xF, + 0xFC, + 1, + 0xF0, + 0x3F, + 0, + 0xFE, + 3, + 0xF0, + 0x1F, + 0, + 0xFD, + 0, + 0xFF, + 0x88, + 0xD, + 0xF9, + 1 + //440 + , + 0xFF, + 0, + 0x70, + 7, + 0xC0, + 0x3E, + 0x42, + 0xF3, + 0xD, + 0xC4, + 0x7F, + 0x80, + 0xFC, + 7, + 0xF0, + 0x5E, + 0xC0, + 0x3F, + 0, + 0x78, + 0x3F, + 0x81, + 0xFF, + 1, + 0xF8, + 1, + 0xC3, + 0xE8, + 0xC, + 0xE4, + 0x64, + 0x8F + ////460 + , + 0xE4, + 0xF, + 0xF0, + 7, + 0xF0, + 0xC2, + 0x1F, + 0, + 0x7F, + 0xC0, + 0x6F, + 0x80, + 0x7E, + 3, + 0xF8, + 7, + 0xF0, + 0x3F, + 0xC0, + 0x78, + 0xF, + 0x82, + 7, + 0xFE, + 0x22, + 0x77, + 0x70, + 2, + 0x76, + 3, + 0xFE, + 0 + //480 + , + 0xFE, + 0x67, + 0, + 0x7C, + 0xC7, + 0xF1, + 0x8E, + 0xC6, + 0x3B, + 0xE0, + 0x3F, + 0x84, + 0xF3, + 0x19, + 0xD8, + 3, + 0x99, + 0xFC, + 9, + 0xB8, + 0xF, + 0xF8, + 0, + 0x9D, + 0x24, + 0x61, + 0xF9, + 0xD, + 0, + 0xFD, + 3, + 0xF0 + //4a0 + , + 0x1F, + 0x90, + 0x3F, + 1, + 0xF8, + 0x1F, + 0xD0, + 0xF, + 0xF8, + 0x37, + 1, + 0xF8, + 7, + 0xF0, + 0xF, + 0xC0, + 0x3F, + 0, + 0xFE, + 3, + 0xF8, + 0xF, + 0xC0, + 0x3F, + 0, + 0xFA, + 3, + 0xF0, + 0xF, + 0x80, + 0xFF, + 1 + //4c0 + , + 0xB8, + 7, + 0xF0, + 1, + 0xFC, + 1, + 0xBC, + 0x80, + 0x13, + 0x1E, + 0, + 0x7F, + 0xE1, + 0x40, + 0x7F, + 0xA0, + 0x7F, + 0xB0, + 0, + 0x3F, + 0xC0, + 0x1F, + 0xC0, + 0x38, + 0xF, + 0xF0, + 0x1F, + 0x80, + 0xFF, + 1, + 0xFC, + 3 + //4e0 + , + 0xF1, + 0x7E, + 1, + 0xFE, + 1, + 0xF0, + 0xFF, + 0, + 0x7F, + 0xC0, + 0x1D, + 7, + 0xF0, + 0xF, + 0xC0, + 0x7E, + 6, + 0xE0, + 7, + 0xE0, + 0xF, + 0xF8, + 6, + 0xC1, + 0xFE, + 1, + 0xFC, + 3, + 0xE0, + 0xF, + 0, + 0xFC}; + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// Render +// +//////////////////////////////////////////////////////////////////////////////////////////// + +unsigned char pitches[256]; // tab43008 + +unsigned char frequency1[256]; +unsigned char frequency2[256]; +unsigned char frequency3[256]; + +unsigned char amplitude1[256]; +unsigned char amplitude2[256]; +unsigned char amplitude3[256]; + +unsigned char sampledConsonantFlag[256]; // tab44800 + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// Sam +// +//////////////////////////////////////////////////////////////////////////////////////////// + +unsigned char stress[256]; //numbers from 0 to 8 +unsigned char phonemeLength[256]; //tab40160 +unsigned char phonemeindex[256]; + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// ReciterTabs +// +//////////////////////////////////////////////////////////////////////////////////////////// + +//some flags +const unsigned char tab36376[] = { + 0, 0, 0, 0, 0, 0, 0, 0, // 0-7 + 0, 0, 0, 0, 0, 0, 0, 0, // 8-15 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 2, 2, 2, 2, 2, 2, 130, // ' ', '!' + 0, 0, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 2, 2, 2, 2, 2, 2, 2, 192, 168, 176, 172, 192, 160, 184, // '@', 'A' + 160, 192, 188, 160, 172, 168, 172, 192, 160, 160, 172, 180, 164, 192, 168, 168, + 176, 192, 188, 0, 0, 0, 2, 0, // 'X', 'Y', 'Z', '[', + 32, 32, 155, 32, 192, 185, 32, 205, 163, 76, 138, 142}; + +const unsigned char rules[] = { + ']', 'A' | 0x80, ' ', '(', 'A', '.', ')', '=', + 'E', 'H', '4', 'Y', '.', ' ' | 0x80, '(', 'A', + ')', ' ', '=', 'A', 'H' | 0x80, ' ', '(', 'A', + 'R', 'E', ')', ' ', '=', 'A', 'A', 'R' | 0x80, + ' ', '(', 'A', 'R', ')', 'O', '=', 'A', + 'X', 'R' | 0x80, '(', 'A', 'R', ')', '#', '=', + 'E', 'H', '4', 'R' | 0x80, ' ', '^', '(', 'A', + 'S', ')', '#', '=', 'E', 'Y', '4', 'S' | 0x80, + '(', 'A', ')', 'W', 'A', '=', 'A', 'X' | 0x80, + '(', 'A', 'W', ')', '=', 'A', 'O', '5' | 0x80, + ' ', ':', '(', 'A', 'N', 'Y', ')', '=', + 'E', 'H', '4', 'N', 'I', 'Y' | 0x80, '(', 'A', + ')', '^', '+', '#', '=', 'E', 'Y', '5' | 0x80, + '#', ':', '(', 'A', 'L', 'L', 'Y', ')', + '=', 'U', 'L', 'I', 'Y' | 0x80, ' ', '(', 'A', + 'L', ')', '#', '=', 'U', 'L' | 0x80, '(', 'A', + 'G', 'A', 'I', 'N', ')', '=', 'A', 'X', + 'G', 'E', 'H', '4', 'N' | 0x80, '#', ':', '(', + 'A', 'G', ')', 'E', '=', 'I', 'H', 'J' | 0x80, + '(', 'A', ')', '^', '%', '=', 'E', 'Y' | 0x80, + '(', 'A', ')', '^', '+', ':', '#', '=', + 'A', 'E' | 0x80, ' ', ':', '(', 'A', ')', '^', + '+', ' ', '=', 'E', 'Y', '4' | 0x80, ' ', '(', + 'A', 'R', 'R', ')', '=', 'A', 'X', 'R' | 0x80, + '(', 'A', 'R', 'R', ')', '=', 'A', 'E', + '4', 'R' | 0x80, ' ', '^', '(', 'A', 'R', ')', + ' ', '=', 'A', 'A', '5', 'R' | 0x80, '(', 'A', + 'R', ')', '=', 'A', 'A', '5', 'R' | 0x80, '(', + 'A', 'I', 'R', ')', '=', 'E', 'H', '4', + 'R' | 0x80, '(', 'A', 'I', ')', '=', 'E', 'Y', + '4' | 0x80, '(', 'A', 'Y', ')', '=', 'E', 'Y', + '5' | 0x80, '(', 'A', 'U', ')', '=', 'A', 'O', + '4' | 0x80, '#', ':', '(', 'A', 'L', ')', ' ', + '=', 'U', 'L' | 0x80, '#', ':', '(', 'A', 'L', + 'S', ')', ' ', '=', 'U', 'L', 'Z' | 0x80, '(', + 'A', 'L', 'K', ')', '=', 'A', 'O', '4', + 'K' | 0x80, '(', 'A', 'L', ')', '^', '=', 'A', + 'O', 'L' | 0x80, ' ', ':', '(', 'A', 'B', 'L', + 'E', ')', '=', 'E', 'Y', '4', 'B', 'U', + 'L' | 0x80, '(', 'A', 'B', 'L', 'E', ')', '=', + 'A', 'X', 'B', 'U', 'L' | 0x80, '(', 'A', ')', + 'V', 'O', '=', 'E', 'Y', '4' | 0x80, '(', 'A', + 'N', 'G', ')', '+', '=', 'E', 'Y', '4', + 'N', 'J' | 0x80, '(', 'A', 'T', 'A', 'R', 'I', + ')', '=', 'A', 'H', 'T', 'A', 'A', '4', + 'R', 'I', 'Y' | 0x80, '(', 'A', ')', 'T', 'O', + 'M', '=', 'A', 'E' | 0x80, '(', 'A', ')', 'T', + 'T', 'I', '=', 'A', 'E' | 0x80, ' ', '(', 'A', + 'T', ')', ' ', '=', 'A', 'E', 'T' | 0x80, ' ', + '(', 'A', ')', 'T', '=', 'A', 'H' | 0x80, '(', + 'A', ')', '=', 'A', 'E' | 0x80, + + ']', 'B' | 0x80, ' ', '(', 'B', ')', ' ', '=', + 'B', 'I', 'Y', '4' | 0x80, ' ', '(', 'B', 'E', + ')', '^', '#', '=', 'B', 'I', 'H' | 0x80, '(', + 'B', 'E', 'I', 'N', 'G', ')', '=', 'B', + 'I', 'Y', '4', 'I', 'H', 'N', 'X' | 0x80, ' ', + '(', 'B', 'O', 'T', 'H', ')', ' ', '=', + 'B', 'O', 'W', '4', 'T', 'H' | 0x80, ' ', '(', + 'B', 'U', 'S', ')', '#', '=', 'B', 'I', + 'H', '4', 'Z' | 0x80, '(', 'B', 'R', 'E', 'A', + 'K', ')', '=', 'B', 'R', 'E', 'Y', '5', + 'K' | 0x80, '(', 'B', 'U', 'I', 'L', ')', '=', + 'B', 'I', 'H', '4', 'L' | 0x80, '(', 'B', ')', + '=', 'B' | 0x80, + + ']', 'C' | 0x80, ' ', '(', 'C', ')', ' ', '=', + 'S', 'I', 'Y', '4' | 0x80, ' ', '(', 'C', 'H', + ')', '^', '=', 'K' | 0x80, '^', 'E', '(', 'C', + 'H', ')', '=', 'K' | 0x80, '(', 'C', 'H', 'A', + ')', 'R', '#', '=', 'K', 'E', 'H', '5' | 0x80, + '(', 'C', 'H', ')', '=', 'C', 'H' | 0x80, ' ', + 'S', '(', 'C', 'I', ')', '#', '=', 'S', + 'A', 'Y', '4' | 0x80, '(', 'C', 'I', ')', 'A', + '=', 'S', 'H' | 0x80, '(', 'C', 'I', ')', 'O', + '=', 'S', 'H' | 0x80, '(', 'C', 'I', ')', 'E', + 'N', '=', 'S', 'H' | 0x80, '(', 'C', 'I', 'T', + 'Y', ')', '=', 'S', 'I', 'H', 'T', 'I', + 'Y' | 0x80, '(', 'C', ')', '+', '=', 'S' | 0x80, '(', + 'C', 'K', ')', '=', 'K' | 0x80, '(', 'C', 'O', + 'M', 'M', 'O', 'D', 'O', 'R', 'E', ')', + '=', 'K', 'A', 'A', '4', 'M', 'A', 'H', + 'D', 'O', 'H', 'R' | 0x80, '(', 'C', 'O', 'M', + ')', '=', 'K', 'A', 'H', 'M' | 0x80, '(', 'C', + 'U', 'I', 'T', ')', '=', 'K', 'I', 'H', + 'T' | 0x80, '(', 'C', 'R', 'E', 'A', ')', '=', + 'K', 'R', 'I', 'Y', 'E', 'Y' | 0x80, '(', 'C', + ')', '=', 'K' | 0x80, + + ']', 'D' | 0x80, ' ', '(', 'D', ')', ' ', '=', + 'D', 'I', 'Y', '4' | 0x80, ' ', '(', 'D', 'R', + '.', ')', ' ', '=', 'D', 'A', 'A', '4', + 'K', 'T', 'E', 'R' | 0x80, '#', ':', '(', 'D', + 'E', 'D', ')', ' ', '=', 'D', 'I', 'H', + 'D' | 0x80, '.', 'E', '(', 'D', ')', ' ', '=', + 'D' | 0x80, '#', ':', '^', 'E', '(', 'D', ')', + ' ', '=', 'T' | 0x80, ' ', '(', 'D', 'E', ')', + '^', '#', '=', 'D', 'I', 'H' | 0x80, ' ', '(', + 'D', 'O', ')', ' ', '=', 'D', 'U', 'W' | 0x80, + ' ', '(', 'D', 'O', 'E', 'S', ')', '=', + 'D', 'A', 'H', 'Z' | 0x80, '(', 'D', 'O', 'N', + 'E', ')', ' ', '=', 'D', 'A', 'H', '5', + 'N' | 0x80, '(', 'D', 'O', 'I', 'N', 'G', ')', + '=', 'D', 'U', 'W', '4', 'I', 'H', 'N', + 'X' | 0x80, ' ', '(', 'D', 'O', 'W', ')', '=', + 'D', 'A', 'W' | 0x80, '#', '(', 'D', 'U', ')', + 'A', '=', 'J', 'U', 'W' | 0x80, '#', '(', 'D', + 'U', ')', '^', '#', '=', 'J', 'A', 'X' | 0x80, + '(', 'D', ')', '=', 'D' | 0x80, + + ']', 'E' | 0x80, ' ', '(', 'E', ')', ' ', '=', + 'I', 'Y', 'I', 'Y', '4' | 0x80, '#', ':', '(', + 'E', ')', ' ', '=' | 0x80, '\'', ':', '^', '(', + 'E', ')', ' ', '=' | 0x80, ' ', ':', '(', 'E', + ')', ' ', '=', 'I', 'Y' | 0x80, '#', '(', 'E', + 'D', ')', ' ', '=', 'D' | 0x80, '#', ':', '(', + 'E', ')', 'D', ' ', '=' | 0x80, '(', 'E', 'V', + ')', 'E', 'R', '=', 'E', 'H', '4', 'V' | 0x80, + '(', 'E', ')', '^', '%', '=', 'I', 'Y', + '4' | 0x80, '(', 'E', 'R', 'I', ')', '#', '=', + 'I', 'Y', '4', 'R', 'I', 'Y' | 0x80, '(', 'E', + 'R', 'I', ')', '=', 'E', 'H', '4', 'R', + 'I', 'H' | 0x80, '#', ':', '(', 'E', 'R', ')', + '#', '=', 'E', 'R' | 0x80, '(', 'E', 'R', 'R', + 'O', 'R', ')', '=', 'E', 'H', '4', 'R', + 'O', 'H', 'R' | 0x80, '(', 'E', 'R', 'A', 'S', + 'E', ')', '=', 'I', 'H', 'R', 'E', 'Y', + '5', 'S' | 0x80, '(', 'E', 'R', ')', '#', '=', + 'E', 'H', 'R' | 0x80, '(', 'E', 'R', ')', '=', + 'E', 'R' | 0x80, ' ', '(', 'E', 'V', 'E', 'N', + ')', '=', 'I', 'Y', 'V', 'E', 'H', 'N' | 0x80, + '#', ':', '(', 'E', ')', 'W', '=' | 0x80, '@', + '(', 'E', 'W', ')', '=', 'U', 'W' | 0x80, '(', + 'E', 'W', ')', '=', 'Y', 'U', 'W' | 0x80, '(', + 'E', ')', 'O', '=', 'I', 'Y' | 0x80, '#', ':', + '&', '(', 'E', 'S', ')', ' ', '=', 'I', + 'H', 'Z' | 0x80, '#', ':', '(', 'E', ')', 'S', + ' ', '=' | 0x80, '#', ':', '(', 'E', 'L', 'Y', + ')', ' ', '=', 'L', 'I', 'Y' | 0x80, '#', ':', + '(', 'E', 'M', 'E', 'N', 'T', ')', '=', + 'M', 'E', 'H', 'N', 'T' | 0x80, '(', 'E', 'F', + 'U', 'L', ')', '=', 'F', 'U', 'H', 'L' | 0x80, + '(', 'E', 'E', ')', '=', 'I', 'Y', '4' | 0x80, + '(', 'E', 'A', 'R', 'N', ')', '=', 'E', + 'R', '5', 'N' | 0x80, ' ', '(', 'E', 'A', 'R', + ')', '^', '=', 'E', 'R', '5' | 0x80, '(', 'E', + 'A', 'D', ')', '=', 'E', 'H', 'D' | 0x80, '#', + ':', '(', 'E', 'A', ')', ' ', '=', 'I', + 'Y', 'A', 'X' | 0x80, '(', 'E', 'A', ')', 'S', + 'U', '=', 'E', 'H', '5' | 0x80, '(', 'E', 'A', + ')', '=', 'I', 'Y', '5' | 0x80, '(', 'E', 'I', + 'G', 'H', ')', '=', 'E', 'Y', '4' | 0x80, '(', + 'E', 'I', ')', '=', 'I', 'Y', '4' | 0x80, ' ', + '(', 'E', 'Y', 'E', ')', '=', 'A', 'Y', + '4' | 0x80, '(', 'E', 'Y', ')', '=', 'I', 'Y' | 0x80, + '(', 'E', 'U', ')', '=', 'Y', 'U', 'W', + '5' | 0x80, '(', 'E', 'Q', 'U', 'A', 'L', ')', + '=', 'I', 'Y', '4', 'K', 'W', 'U', 'L' | 0x80, + '(', 'E', ')', '=', 'E', 'H' | 0x80, + + ']', 'F' | 0x80, ' ', '(', 'F', ')', ' ', '=', + 'E', 'H', '4', 'F' | 0x80, '(', 'F', 'U', 'L', + ')', '=', 'F', 'U', 'H', 'L' | 0x80, '(', 'F', + 'R', 'I', 'E', 'N', 'D', ')', '=', 'F', + 'R', 'E', 'H', '5', 'N', 'D' | 0x80, '(', 'F', + 'A', 'T', 'H', 'E', 'R', ')', '=', 'F', + 'A', 'A', '4', 'D', 'H', 'E', 'R' | 0x80, '(', + 'F', ')', 'F', '=' | 0x80, '(', 'F', ')', '=', + 'F' | 0x80, + + ']', 'G' | 0x80, ' ', '(', 'G', ')', ' ', '=', + 'J', 'I', 'Y', '4' | 0x80, '(', 'G', 'I', 'V', + ')', '=', 'G', 'I', 'H', '5', 'V' | 0x80, ' ', + '(', 'G', ')', 'I', '^', '=', 'G' | 0x80, '(', + 'G', 'E', ')', 'T', '=', 'G', 'E', 'H', + '5' | 0x80, 'S', 'U', '(', 'G', 'G', 'E', 'S', + ')', '=', 'G', 'J', 'E', 'H', '4', 'S' | 0x80, + '(', 'G', 'G', ')', '=', 'G' | 0x80, ' ', 'B', + '#', '(', 'G', ')', '=', 'G' | 0x80, '(', 'G', + ')', '+', '=', 'J' | 0x80, '(', 'G', 'R', 'E', + 'A', 'T', ')', '=', 'G', 'R', 'E', 'Y', + '4', 'T' | 0x80, '(', 'G', 'O', 'N', ')', 'E', + '=', 'G', 'A', 'O', '5', 'N' | 0x80, '#', '(', + 'G', 'H', ')', '=' | 0x80, ' ', '(', 'G', 'N', + ')', '=', 'N' | 0x80, '(', 'G', ')', '=', 'G' | 0x80, + + ']', 'H' | 0x80, ' ', '(', 'H', ')', ' ', '=', + 'E', 'Y', '4', 'C', 'H' | 0x80, ' ', '(', 'H', + 'A', 'V', ')', '=', '/', 'H', 'A', 'E', + '6', 'V' | 0x80, ' ', '(', 'H', 'E', 'R', 'E', + ')', '=', '/', 'H', 'I', 'Y', 'R' | 0x80, ' ', + '(', 'H', 'O', 'U', 'R', ')', '=', 'A', + 'W', '5', 'E', 'R' | 0x80, '(', 'H', 'O', 'W', + ')', '=', '/', 'H', 'A', 'W' | 0x80, '(', 'H', + ')', '#', '=', '/', 'H' | 0x80, '(', 'H', ')', + '=' | 0x80, + + ']', 'I' | 0x80, ' ', '(', 'I', 'N', ')', '=', + 'I', 'H', 'N' | 0x80, ' ', '(', 'I', ')', ' ', + '=', 'A', 'Y', '4' | 0x80, '(', 'I', ')', ' ', + '=', 'A', 'Y' | 0x80, '(', 'I', 'N', ')', 'D', + '=', 'A', 'Y', '5', 'N' | 0x80, 'S', 'E', 'M', + '(', 'I', ')', '=', 'I', 'Y' | 0x80, ' ', 'A', + 'N', 'T', '(', 'I', ')', '=', 'A', 'Y' | 0x80, + '(', 'I', 'E', 'R', ')', '=', 'I', 'Y', + 'E', 'R' | 0x80, '#', ':', 'R', '(', 'I', 'E', + 'D', ')', ' ', '=', 'I', 'Y', 'D' | 0x80, '(', + 'I', 'E', 'D', ')', ' ', '=', 'A', 'Y', + '5', 'D' | 0x80, '(', 'I', 'E', 'N', ')', '=', + 'I', 'Y', 'E', 'H', 'N' | 0x80, '(', 'I', 'E', + ')', 'T', '=', 'A', 'Y', '4', 'E', 'H' | 0x80, + '(', 'I', '\'', ')', '=', 'A', 'Y', '5' | 0x80, + ' ', ':', '(', 'I', ')', '^', '%', '=', + 'A', 'Y', '5' | 0x80, ' ', ':', '(', 'I', 'E', + ')', ' ', '=', 'A', 'Y', '4' | 0x80, '(', 'I', + ')', '%', '=', 'I', 'Y' | 0x80, '(', 'I', 'E', + ')', '=', 'I', 'Y', '4' | 0x80, ' ', '(', 'I', + 'D', 'E', 'A', ')', '=', 'A', 'Y', 'D', + 'I', 'Y', '5', 'A', 'H' | 0x80, '(', 'I', ')', + '^', '+', ':', '#', '=', 'I', 'H' | 0x80, '(', + 'I', 'R', ')', '#', '=', 'A', 'Y', 'R' | 0x80, + '(', 'I', 'Z', ')', '%', '=', 'A', 'Y', + 'Z' | 0x80, '(', 'I', 'S', ')', '%', '=', 'A', + 'Y', 'Z' | 0x80, 'I', '^', '(', 'I', ')', '^', + '#', '=', 'I', 'H' | 0x80, '+', '^', '(', 'I', + ')', '^', '+', '=', 'A', 'Y' | 0x80, '#', ':', + '^', '(', 'I', ')', '^', '+', '=', 'I', + 'H' | 0x80, '(', 'I', ')', '^', '+', '=', 'A', + 'Y' | 0x80, '(', 'I', 'R', ')', '=', 'E', 'R' | 0x80, + '(', 'I', 'G', 'H', ')', '=', 'A', 'Y', + '4' | 0x80, '(', 'I', 'L', 'D', ')', '=', 'A', + 'Y', '5', 'L', 'D' | 0x80, ' ', '(', 'I', 'G', + 'N', ')', '=', 'I', 'H', 'G', 'N' | 0x80, '(', + 'I', 'G', 'N', ')', ' ', '=', 'A', 'Y', + '4', 'N' | 0x80, '(', 'I', 'G', 'N', ')', '^', + '=', 'A', 'Y', '4', 'N' | 0x80, '(', 'I', 'G', + 'N', ')', '%', '=', 'A', 'Y', '4', 'N' | 0x80, + '(', 'I', 'C', 'R', 'O', ')', '=', 'A', + 'Y', '4', 'K', 'R', 'O', 'H' | 0x80, '(', 'I', + 'Q', 'U', 'E', ')', '=', 'I', 'Y', '4', + 'K' | 0x80, '(', 'I', ')', '=', 'I', 'H' | 0x80, + + ']', 'J' | 0x80, ' ', '(', 'J', ')', ' ', '=', + 'J', 'E', 'Y', '4' | 0x80, '(', 'J', ')', '=', + 'J' | 0x80, + + ']', 'K' | 0x80, ' ', '(', 'K', ')', ' ', '=', + 'K', 'E', 'Y', '4' | 0x80, ' ', '(', 'K', ')', + 'N', '=' | 0x80, '(', 'K', ')', '=', 'K' | 0x80, + + ']', 'L' | 0x80, ' ', '(', 'L', ')', ' ', '=', + 'E', 'H', '4', 'L' | 0x80, '(', 'L', 'O', ')', + 'C', '#', '=', 'L', 'O', 'W' | 0x80, 'L', '(', + 'L', ')', '=' | 0x80, '#', ':', '^', '(', 'L', + ')', '%', '=', 'U', 'L' | 0x80, '(', 'L', 'E', + 'A', 'D', ')', '=', 'L', 'I', 'Y', 'D' | 0x80, + ' ', '(', 'L', 'A', 'U', 'G', 'H', ')', + '=', 'L', 'A', 'E', '4', 'F' | 0x80, '(', 'L', + ')', '=', 'L' | 0x80, + + ']', 'M' | 0x80, ' ', '(', 'M', ')', ' ', '=', + 'E', 'H', '4', 'M' | 0x80, ' ', '(', 'M', 'R', + '.', ')', ' ', '=', 'M', 'I', 'H', '4', + 'S', 'T', 'E', 'R' | 0x80, ' ', '(', 'M', 'S', + '.', ')', '=', 'M', 'I', 'H', '5', 'Z' | 0x80, + ' ', '(', 'M', 'R', 'S', '.', ')', ' ', + '=', 'M', 'I', 'H', '4', 'S', 'I', 'X', + 'Z' | 0x80, '(', 'M', 'O', 'V', ')', '=', 'M', + 'U', 'W', '4', 'V' | 0x80, '(', 'M', 'A', 'C', + 'H', 'I', 'N', ')', '=', 'M', 'A', 'H', + 'S', 'H', 'I', 'Y', '5', 'N' | 0x80, 'M', '(', + 'M', ')', '=' | 0x80, '(', 'M', ')', '=', 'M' | 0x80, + + ']', 'N' | 0x80, ' ', '(', 'N', ')', ' ', '=', + 'E', 'H', '4', 'N' | 0x80, 'E', '(', 'N', 'G', + ')', '+', '=', 'N', 'J' | 0x80, '(', 'N', 'G', + ')', 'R', '=', 'N', 'X', 'G' | 0x80, '(', 'N', + 'G', ')', '#', '=', 'N', 'X', 'G' | 0x80, '(', + 'N', 'G', 'L', ')', '%', '=', 'N', 'X', + 'G', 'U', 'L' | 0x80, '(', 'N', 'G', ')', '=', + 'N', 'X' | 0x80, '(', 'N', 'K', ')', '=', 'N', + 'X', 'K' | 0x80, ' ', '(', 'N', 'O', 'W', ')', + ' ', '=', 'N', 'A', 'W', '4' | 0x80, 'N', '(', + 'N', ')', '=' | 0x80, '(', 'N', 'O', 'N', ')', + 'E', '=', 'N', 'A', 'H', '4', 'N' | 0x80, '(', + 'N', ')', '=', 'N' | 0x80, + + ']', 'O' | 0x80, ' ', '(', 'O', ')', ' ', '=', + 'O', 'H', '4', 'W' | 0x80, '(', 'O', 'F', ')', + ' ', '=', 'A', 'H', 'V' | 0x80, ' ', '(', 'O', + 'H', ')', ' ', '=', 'O', 'W', '5' | 0x80, '(', + 'O', 'R', 'O', 'U', 'G', 'H', ')', '=', + 'E', 'R', '4', 'O', 'W' | 0x80, '#', ':', '(', + 'O', 'R', ')', ' ', '=', 'E', 'R' | 0x80, '#', + ':', '(', 'O', 'R', 'S', ')', ' ', '=', + 'E', 'R', 'Z' | 0x80, '(', 'O', 'R', ')', '=', + 'A', 'O', 'R' | 0x80, ' ', '(', 'O', 'N', 'E', + ')', '=', 'W', 'A', 'H', 'N' | 0x80, '#', '(', + 'O', 'N', 'E', ')', ' ', '=', 'W', 'A', + 'H', 'N' | 0x80, '(', 'O', 'W', ')', '=', 'O', + 'W' | 0x80, ' ', '(', 'O', 'V', 'E', 'R', ')', + '=', 'O', 'W', '5', 'V', 'E', 'R' | 0x80, 'P', + 'R', '(', 'O', ')', 'V', '=', 'U', 'W', + '4' | 0x80, '(', 'O', 'V', ')', '=', 'A', 'H', + '4', 'V' | 0x80, '(', 'O', ')', '^', '%', '=', + 'O', 'W', '5' | 0x80, '(', 'O', ')', '^', 'E', + 'N', '=', 'O', 'W' | 0x80, '(', 'O', ')', '^', + 'I', '#', '=', 'O', 'W', '5' | 0x80, '(', 'O', + 'L', ')', 'D', '=', 'O', 'W', '4', 'L' | 0x80, + '(', 'O', 'U', 'G', 'H', 'T', ')', '=', + 'A', 'O', '5', 'T' | 0x80, '(', 'O', 'U', 'G', + 'H', ')', '=', 'A', 'H', '5', 'F' | 0x80, ' ', + '(', 'O', 'U', ')', '=', 'A', 'W' | 0x80, 'H', + '(', 'O', 'U', ')', 'S', '#', '=', 'A', + 'W', '4' | 0x80, '(', 'O', 'U', 'S', ')', '=', + 'A', 'X', 'S' | 0x80, '(', 'O', 'U', 'R', ')', + '=', 'O', 'H', 'R' | 0x80, '(', 'O', 'U', 'L', + 'D', ')', '=', 'U', 'H', '5', 'D' | 0x80, '(', + 'O', 'U', ')', '^', 'L', '=', 'A', 'H', + '5' | 0x80, '(', 'O', 'U', 'P', ')', '=', 'U', + 'W', '5', 'P' | 0x80, '(', 'O', 'U', ')', '=', + 'A', 'W' | 0x80, '(', 'O', 'Y', ')', '=', 'O', + 'Y' | 0x80, '(', 'O', 'I', 'N', 'G', ')', '=', + 'O', 'W', '4', 'I', 'H', 'N', 'X' | 0x80, '(', + 'O', 'I', ')', '=', 'O', 'Y', '5' | 0x80, '(', + 'O', 'O', 'R', ')', '=', 'O', 'H', '5', + 'R' | 0x80, '(', 'O', 'O', 'K', ')', '=', 'U', + 'H', '5', 'K' | 0x80, 'F', '(', 'O', 'O', 'D', + ')', '=', 'U', 'W', '5', 'D' | 0x80, 'L', '(', + 'O', 'O', 'D', ')', '=', 'A', 'H', '5', + 'D' | 0x80, 'M', '(', 'O', 'O', 'D', ')', '=', + 'U', 'W', '5', 'D' | 0x80, '(', 'O', 'O', 'D', + ')', '=', 'U', 'H', '5', 'D' | 0x80, 'F', '(', + 'O', 'O', 'T', ')', '=', 'U', 'H', '5', + 'T' | 0x80, '(', 'O', 'O', ')', '=', 'U', 'W', + '5' | 0x80, '(', 'O', '\'', ')', '=', 'O', 'H' | 0x80, + '(', 'O', ')', 'E', '=', 'O', 'W' | 0x80, '(', + 'O', ')', ' ', '=', 'O', 'W' | 0x80, '(', 'O', + 'A', ')', '=', 'O', 'W', '4' | 0x80, ' ', '(', + 'O', 'N', 'L', 'Y', ')', '=', 'O', 'W', + '4', 'N', 'L', 'I', 'Y' | 0x80, ' ', '(', 'O', + 'N', 'C', 'E', ')', '=', 'W', 'A', 'H', + '4', 'N', 'S' | 0x80, '(', 'O', 'N', '\'', 'T', + ')', '=', 'O', 'W', '4', 'N', 'T' | 0x80, 'C', + '(', 'O', ')', 'N', '=', 'A', 'A' | 0x80, '(', + 'O', ')', 'N', 'G', '=', 'A', 'O' | 0x80, ' ', + ':', '^', '(', 'O', ')', 'N', '=', 'A', + 'H' | 0x80, 'I', '(', 'O', 'N', ')', '=', 'U', + 'N' | 0x80, '#', ':', '(', 'O', 'N', ')', '=', + 'U', 'N' | 0x80, '#', '^', '(', 'O', 'N', ')', + '=', 'U', 'N' | 0x80, '(', 'O', ')', 'S', 'T', + '=', 'O', 'W' | 0x80, '(', 'O', 'F', ')', '^', + '=', 'A', 'O', '4', 'F' | 0x80, '(', 'O', 'T', + 'H', 'E', 'R', ')', '=', 'A', 'H', '5', + 'D', 'H', 'E', 'R' | 0x80, 'R', '(', 'O', ')', + 'B', '=', 'R', 'A', 'A' | 0x80, '^', 'R', '(', + 'O', ')', ':', '#', '=', 'O', 'W', '5' | 0x80, + '(', 'O', 'S', 'S', ')', ' ', '=', 'A', + 'O', '5', 'S' | 0x80, '#', ':', '^', '(', 'O', + 'M', ')', '=', 'A', 'H', 'M' | 0x80, '(', 'O', + ')', '=', 'A', 'A' | 0x80, + + ']', 'P' | 0x80, ' ', '(', 'P', ')', ' ', '=', + 'P', 'I', 'Y', '4' | 0x80, '(', 'P', 'H', ')', + '=', 'F' | 0x80, '(', 'P', 'E', 'O', 'P', 'L', + ')', '=', 'P', 'I', 'Y', '5', 'P', 'U', + 'L' | 0x80, '(', 'P', 'O', 'W', ')', '=', 'P', + 'A', 'W', '4' | 0x80, '(', 'P', 'U', 'T', ')', + ' ', '=', 'P', 'U', 'H', 'T' | 0x80, '(', 'P', + ')', 'P', '=' | 0x80, '(', 'P', ')', 'S', '=' | 0x80, + '(', 'P', ')', 'N', '=' | 0x80, '(', 'P', 'R', + 'O', 'F', '.', ')', '=', 'P', 'R', 'O', + 'H', 'F', 'E', 'H', '4', 'S', 'E', 'R' | 0x80, + '(', 'P', ')', '=', 'P' | 0x80, + + ']', 'Q' | 0x80, ' ', '(', 'Q', ')', ' ', '=', + 'K', 'Y', 'U', 'W', '4' | 0x80, '(', 'Q', 'U', + 'A', 'R', ')', '=', 'K', 'W', 'O', 'H', + '5', 'R' | 0x80, '(', 'Q', 'U', ')', '=', 'K', + 'W' | 0x80, '(', 'Q', ')', '=', 'K' | 0x80, ']', 'R' | 0x80, + ' ', '(', 'R', ')', ' ', '=', 'A', 'A', + '5', 'R' | 0x80, ' ', '(', 'R', 'E', ')', '^', + '#', '=', 'R', 'I', 'Y' | 0x80, '(', 'R', ')', + 'R', '=' | 0x80, '(', 'R', ')', '=', 'R' | 0x80, + + ']', 'S' | 0x80, ' ', '(', 'S', ')', ' ', '=', + 'E', 'H', '4', 'S' | 0x80, '(', 'S', 'H', ')', + '=', 'S', 'H' | 0x80, '#', '(', 'S', 'I', 'O', + 'N', ')', '=', 'Z', 'H', 'U', 'N' | 0x80, '(', + 'S', 'O', 'M', 'E', ')', '=', 'S', 'A', + 'H', 'M' | 0x80, '#', '(', 'S', 'U', 'R', ')', + '#', '=', 'Z', 'H', 'E', 'R' | 0x80, '(', 'S', + 'U', 'R', ')', '#', '=', 'S', 'H', 'E', + 'R' | 0x80, '#', '(', 'S', 'U', ')', '#', '=', + 'Z', 'H', 'U', 'W' | 0x80, '#', '(', 'S', 'S', + 'U', ')', '#', '=', 'S', 'H', 'U', 'W' | 0x80, + '#', '(', 'S', 'E', 'D', ')', '=', 'Z', + 'D' | 0x80, '#', '(', 'S', ')', '#', '=', 'Z' | 0x80, + '(', 'S', 'A', 'I', 'D', ')', '=', 'S', + 'E', 'H', 'D' | 0x80, '^', '(', 'S', 'I', 'O', + 'N', ')', '=', 'S', 'H', 'U', 'N' | 0x80, '(', + 'S', ')', 'S', '=' | 0x80, '.', '(', 'S', ')', + ' ', '=', 'Z' | 0x80, '#', ':', '.', 'E', '(', + 'S', ')', ' ', '=', 'Z' | 0x80, '#', ':', '^', + '#', '(', 'S', ')', ' ', '=', 'S' | 0x80, 'U', + '(', 'S', ')', ' ', '=', 'S' | 0x80, ' ', ':', + '#', '(', 'S', ')', ' ', '=', 'Z' | 0x80, '#', + '#', '(', 'S', ')', ' ', '=', 'Z' | 0x80, ' ', + '(', 'S', 'C', 'H', ')', '=', 'S', 'K' | 0x80, + '(', 'S', ')', 'C', '+', '=' | 0x80, '#', '(', + 'S', 'M', ')', '=', 'Z', 'U', 'M' | 0x80, '#', + '(', 'S', 'N', ')', '\'', '=', 'Z', 'U', + 'M' | 0x80, '(', 'S', 'T', 'L', 'E', ')', '=', + 'S', 'U', 'L' | 0x80, '(', 'S', ')', '=', 'S' | 0x80, + + ']', 'T' | 0x80, ' ', '(', 'T', ')', ' ', '=', + 'T', 'I', 'Y', '4' | 0x80, ' ', '(', 'T', 'H', + 'E', ')', ' ', '#', '=', 'D', 'H', 'I', + 'Y' | 0x80, ' ', '(', 'T', 'H', 'E', ')', ' ', + '=', 'D', 'H', 'A', 'X' | 0x80, '(', 'T', 'O', + ')', ' ', '=', 'T', 'U', 'X' | 0x80, ' ', '(', + 'T', 'H', 'A', 'T', ')', '=', 'D', 'H', + 'A', 'E', 'T' | 0x80, ' ', '(', 'T', 'H', 'I', + 'S', ')', ' ', '=', 'D', 'H', 'I', 'H', + 'S' | 0x80, ' ', '(', 'T', 'H', 'E', 'Y', ')', + '=', 'D', 'H', 'E', 'Y' | 0x80, ' ', '(', 'T', + 'H', 'E', 'R', 'E', ')', '=', 'D', 'H', + 'E', 'H', 'R' | 0x80, '(', 'T', 'H', 'E', 'R', + ')', '=', 'D', 'H', 'E', 'R' | 0x80, '(', 'T', + 'H', 'E', 'I', 'R', ')', '=', 'D', 'H', + 'E', 'H', 'R' | 0x80, ' ', '(', 'T', 'H', 'A', + 'N', ')', ' ', '=', 'D', 'H', 'A', 'E', + 'N' | 0x80, ' ', '(', 'T', 'H', 'E', 'M', ')', + ' ', '=', 'D', 'H', 'A', 'E', 'N' | 0x80, '(', + 'T', 'H', 'E', 'S', 'E', ')', ' ', '=', + 'D', 'H', 'I', 'Y', 'Z' | 0x80, ' ', '(', 'T', + 'H', 'E', 'N', ')', '=', 'D', 'H', 'E', + 'H', 'N' | 0x80, '(', 'T', 'H', 'R', 'O', 'U', + 'G', 'H', ')', '=', 'T', 'H', 'R', 'U', + 'W', '4' | 0x80, '(', 'T', 'H', 'O', 'S', 'E', + ')', '=', 'D', 'H', 'O', 'H', 'Z' | 0x80, '(', + 'T', 'H', 'O', 'U', 'G', 'H', ')', ' ', + '=', 'D', 'H', 'O', 'W' | 0x80, '(', 'T', 'O', + 'D', 'A', 'Y', ')', '=', 'T', 'U', 'X', + 'D', 'E', 'Y' | 0x80, '(', 'T', 'O', 'M', 'O', + ')', 'R', 'R', 'O', 'W', '=', 'T', 'U', + 'M', 'A', 'A', '5' | 0x80, '(', 'T', 'O', ')', + 'T', 'A', 'L', '=', 'T', 'O', 'W', '5' | 0x80, + ' ', '(', 'T', 'H', 'U', 'S', ')', '=', + 'D', 'H', 'A', 'H', '4', 'S' | 0x80, '(', 'T', + 'H', ')', '=', 'T', 'H' | 0x80, '#', ':', '(', + 'T', 'E', 'D', ')', '=', 'T', 'I', 'X', + 'D' | 0x80, 'S', '(', 'T', 'I', ')', '#', 'N', + '=', 'C', 'H' | 0x80, '(', 'T', 'I', ')', 'O', + '=', 'S', 'H' | 0x80, '(', 'T', 'I', ')', 'A', + '=', 'S', 'H' | 0x80, '(', 'T', 'I', 'E', 'N', + ')', '=', 'S', 'H', 'U', 'N' | 0x80, '(', 'T', + 'U', 'R', ')', '#', '=', 'C', 'H', 'E', + 'R' | 0x80, '(', 'T', 'U', ')', 'A', '=', 'C', + 'H', 'U', 'W' | 0x80, ' ', '(', 'T', 'W', 'O', + ')', '=', 'T', 'U', 'W' | 0x80, '&', '(', 'T', + ')', 'E', 'N', ' ', '=' | 0x80, '(', 'T', ')', + '=', 'T' | 0x80, + + ']', 'U' | 0x80, ' ', '(', 'U', ')', ' ', '=', + 'Y', 'U', 'W', '4' | 0x80, ' ', '(', 'U', 'N', + ')', 'I', '=', 'Y', 'U', 'W', 'N' | 0x80, ' ', + '(', 'U', 'N', ')', '=', 'A', 'H', 'N' | 0x80, + ' ', '(', 'U', 'P', 'O', 'N', ')', '=', + 'A', 'X', 'P', 'A', 'O', 'N' | 0x80, '@', '(', + 'U', 'R', ')', '#', '=', 'U', 'H', '4', + 'R' | 0x80, '(', 'U', 'R', ')', '#', '=', 'Y', + 'U', 'H', '4', 'R' | 0x80, '(', 'U', 'R', ')', + '=', 'E', 'R' | 0x80, '(', 'U', ')', '^', ' ', + '=', 'A', 'H' | 0x80, '(', 'U', ')', '^', '^', + '=', 'A', 'H', '5' | 0x80, '(', 'U', 'Y', ')', + '=', 'A', 'Y', '5' | 0x80, ' ', 'G', '(', 'U', + ')', '#', '=' | 0x80, 'G', '(', 'U', ')', '%', + '=' | 0x80, 'G', '(', 'U', ')', '#', '=', 'W' | 0x80, + '#', 'N', '(', 'U', ')', '=', 'Y', 'U', + 'W' | 0x80, '@', '(', 'U', ')', '=', 'U', 'W' | 0x80, + '(', 'U', ')', '=', 'Y', 'U', 'W' | 0x80, + + ']', 'V' | 0x80, ' ', '(', 'V', ')', ' ', '=', + 'V', 'I', 'Y', '4' | 0x80, '(', 'V', 'I', 'E', + 'W', ')', '=', 'V', 'Y', 'U', 'W', '5' | 0x80, + '(', 'V', ')', '=', 'V' | 0x80, + + ']', 'W' | 0x80, ' ', '(', 'W', ')', ' ', '=', + 'D', 'A', 'H', '4', 'B', 'U', 'L', 'Y', + 'U', 'W' | 0x80, ' ', '(', 'W', 'E', 'R', 'E', + ')', '=', 'W', 'E', 'R' | 0x80, '(', 'W', 'A', + ')', 'S', 'H', '=', 'W', 'A', 'A' | 0x80, '(', + 'W', 'A', ')', 'S', 'T', '=', 'W', 'E', + 'Y' | 0x80, '(', 'W', 'A', ')', 'S', '=', 'W', + 'A', 'H' | 0x80, '(', 'W', 'A', ')', 'T', '=', + 'W', 'A', 'A' | 0x80, '(', 'W', 'H', 'E', 'R', + 'E', ')', '=', 'W', 'H', 'E', 'H', 'R' | 0x80, + '(', 'W', 'H', 'A', 'T', ')', '=', 'W', + 'H', 'A', 'H', 'T' | 0x80, '(', 'W', 'H', 'O', + 'L', ')', '=', '/', 'H', 'O', 'W', 'L' | 0x80, + '(', 'W', 'H', 'O', ')', '=', '/', 'H', + 'U', 'W' | 0x80, '(', 'W', 'H', ')', '=', 'W', + 'H' | 0x80, '(', 'W', 'A', 'R', ')', '#', '=', + 'W', 'E', 'H', 'R' | 0x80, '(', 'W', 'A', 'R', + ')', '=', 'W', 'A', 'O', 'R' | 0x80, '(', 'W', + 'O', 'R', ')', '^', '=', 'W', 'E', 'R' | 0x80, + '(', 'W', 'R', ')', '=', 'R' | 0x80, '(', 'W', + 'O', 'M', ')', 'A', '=', 'W', 'U', 'H', + 'M' | 0x80, '(', 'W', 'O', 'M', ')', 'E', '=', + 'W', 'I', 'H', 'M' | 0x80, '(', 'W', 'E', 'A', + ')', 'R', '=', 'W', 'E', 'H' | 0x80, '(', 'W', + 'A', 'N', 'T', ')', '=', 'W', 'A', 'A', + '5', 'N', 'T' | 0x80, 'A', 'N', 'S', '(', 'W', + 'E', 'R', ')', '=', 'E', 'R' | 0x80, '(', 'W', + ')', '=', 'W' | 0x80, + + ']', 'X' | 0x80, ' ', '(', 'X', ')', ' ', '=', + 'E', 'H', '4', 'K', 'R' | 0x80, ' ', '(', 'X', + ')', '=', 'Z' | 0x80, '(', 'X', ')', '=', 'K', + 'S' | 0x80, + + ']', 'Y' | 0x80, ' ', '(', 'Y', ')', ' ', '=', + 'W', 'A', 'Y', '4' | 0x80, '(', 'Y', 'O', 'U', + 'N', 'G', ')', '=', 'Y', 'A', 'H', 'N', + 'X' | 0x80, ' ', '(', 'Y', 'O', 'U', 'R', ')', + '=', 'Y', 'O', 'H', 'R' | 0x80, ' ', '(', 'Y', + 'O', 'U', ')', '=', 'Y', 'U', 'W' | 0x80, ' ', + '(', 'Y', 'E', 'S', ')', '=', 'Y', 'E', + 'H', 'S' | 0x80, ' ', '(', 'Y', ')', '=', 'Y' | 0x80, + 'F', '(', 'Y', ')', '=', 'A', 'Y' | 0x80, 'P', + 'S', '(', 'Y', 'C', 'H', ')', '=', 'A', + 'Y', 'K' | 0x80, '#', ':', '^', '(', 'Y', ')', + '=', 'I', 'Y' | 0x80, '#', ':', '^', '(', 'Y', + ')', 'I', '=', 'I', 'Y' | 0x80, ' ', ':', '(', + 'Y', ')', ' ', '=', 'A', 'Y' | 0x80, ' ', ':', + '(', 'Y', ')', '#', '=', 'A', 'Y' | 0x80, ' ', + ':', '(', 'Y', ')', '^', '+', ':', '#', + '=', 'I', 'H' | 0x80, ' ', ':', '(', 'Y', ')', + '^', '#', '=', 'A', 'Y' | 0x80, '(', 'Y', ')', + '=', 'I', 'H' | 0x80, + + ']', 'Z' | 0x80, ' ', '(', 'Z', ')', ' ', '=', + 'Z', 'I', 'Y', '4' | 0x80, '(', 'Z', ')', '=', + 'Z' | 0x80, 'j' | 0x80}; + +const unsigned char rules2[] = { + '(', 'A', ')', '=' | 0x80, '(', '!', ')', '=', + '.' | 0x80, '(', '"', ')', ' ', '=', '-', 'A', + 'H', '5', 'N', 'K', 'W', 'O', 'W', 'T', + '-' | 0x80, '(', '"', ')', '=', 'K', 'W', 'O', + 'W', '4', 'T', '-' | 0x80, '(', '#', ')', '=', + ' ', 'N', 'A', 'H', '4', 'M', 'B', 'E', + 'R' | 0x80, '(', '$', ')', '=', ' ', 'D', 'A', + 'A', '4', 'L', 'E', 'R' | 0x80, '(', '%', ')', + '=', ' ', 'P', 'E', 'R', 'S', 'E', 'H', + '4', 'N', 'T' | 0x80, '(', '&', ')', '=', ' ', + 'A', 'E', 'N', 'D' | 0x80, '(', '\'', ')', '=' | 0x80, + '(', '*', ')', '=', ' ', 'A', 'E', '4', + 'S', 'T', 'E', 'R', 'I', 'H', 'S', 'K' | 0x80, + '(', '+', ')', '=', ' ', 'P', 'L', 'A', + 'H', '4', 'S' | 0x80, '(', ',', ')', '=', ',' | 0x80, + ' ', '(', '-', ')', ' ', '=', '-' | 0x80, '(', + '-', ')', '=' | 0x80, '(', '.', ')', '=', ' ', + 'P', 'O', 'Y', 'N', 'T' | 0x80, '(', '/', ')', + '=', ' ', 'S', 'L', 'A', 'E', '4', 'S', + 'H' | 0x80, '(', '0', ')', '=', ' ', 'Z', 'I', + 'Y', '4', 'R', 'O', 'W' | 0x80, ' ', '(', '1', + 'S', 'T', ')', '=', 'F', 'E', 'R', '4', + 'S', 'T' | 0x80, ' ', '(', '1', '0', 'T', 'H', + ')', '=', 'T', 'E', 'H', '4', 'N', 'T', + 'H' | 0x80, '(', '1', ')', '=', ' ', 'W', 'A', + 'H', '4', 'N' | 0x80, ' ', '(', '2', 'N', 'D', + ')', '=', 'S', 'E', 'H', '4', 'K', 'U', + 'N', 'D' | 0x80, '(', '2', ')', '=', ' ', 'T', + 'U', 'W', '4' | 0x80, ' ', '(', '3', 'R', 'D', + ')', '=', 'T', 'H', 'E', 'R', '4', 'D' | 0x80, + '(', '3', ')', '=', ' ', 'T', 'H', 'R', + 'I', 'Y', '4' | 0x80, '(', '4', ')', '=', ' ', + 'F', 'O', 'H', '4', 'R' | 0x80, ' ', '(', '5', + 'T', 'H', ')', '=', 'F', 'I', 'H', '4', + 'F', 'T', 'H' | 0x80, '(', '5', ')', '=', ' ', + 'F', 'A', 'Y', '4', 'V' | 0x80, ' ', '(', '6', + '4', ')', ' ', '=', 'S', 'I', 'H', '4', + 'K', 'S', 'T', 'I', 'Y', ' ', 'F', 'O', + 'H', 'R' | 0x80, '(', '6', ')', '=', ' ', 'S', + 'I', 'H', '4', 'K', 'S' | 0x80, '(', '7', ')', + '=', ' ', 'S', 'E', 'H', '4', 'V', 'U', + 'N' | 0x80, ' ', '(', '8', 'T', 'H', ')', '=', + 'E', 'Y', '4', 'T', 'H' | 0x80, '(', '8', ')', + '=', ' ', 'E', 'Y', '4', 'T' | 0x80, '(', '9', + ')', '=', ' ', 'N', 'A', 'Y', '4', 'N' | 0x80, + '(', ':', ')', '=', '.' | 0x80, '(', ';', ')', + '=', '.' | 0x80, '(', '<', ')', '=', ' ', 'L', + 'E', 'H', '4', 'S', ' ', 'D', 'H', 'A', + 'E', 'N' | 0x80, '(', '=', ')', '=', ' ', 'I', + 'Y', '4', 'K', 'W', 'U', 'L', 'Z' | 0x80, '(', + '>', ')', '=', ' ', 'G', 'R', 'E', 'Y', + '4', 'T', 'E', 'R', ' ', 'D', 'H', 'A', + 'E', 'N' | 0x80, '(', '?', ')', '=', '?' | 0x80, '(', + '@', ')', '=', ' ', 'A', 'E', '6', 'T' | 0x80, + '(', '^', ')', '=', ' ', 'K', 'A', 'E', + '4', 'R', 'I', 'X', 'T' | 0x80, ']', 'A' | 0x80}; + +//26 items. From 'A' to 'Z' +// positions for mem62 and mem63 for each character +const unsigned char tab37489[] = {0, 149, 247, 162, 57, 197, 6, 126, 199, 38, 55, 78, 145, + 241, 85, 161, 254, 36, 69, 45, 167, 54, 83, 46, 71, 218}; + +const unsigned char tab37515[] = {125, 126, 126, 127, 128, 129, 130, 130, 130, 132, 132, 132, 132, + 132, 133, 135, 135, 136, 136, 137, 138, 139, 139, 140, 140, 140}; + +void STM32SAM::Output8BitAry(int index, unsigned char ary[5]) { + int k; + + uint32_t bufferposOld = bufferpos; + + bufferpos += timetable[oldtimetableindex][index]; + oldtimetableindex = index; + + int sample_uS = bufferpos - bufferposOld; + + uint32_t f = 0; + + // write a little bit in advance + for(k = 0; k < 5; k++) { + // buffer[bufferpos / 50 + k] = ary[k]; + + // f = micros() + sample_uS / (_STM32SAM_SPEED + 1); + // while(micros() < f) { + // }; + f = sample_uS / (_STM32SAM_SPEED + 1); + furi_delay_us(f); + SetAUDIO(ary[k]); + // delayMicroseconds(sample_uS / 5 ); + } + + // SetAUDIO(ary[0]); +} + +void STM32SAM::Output8Bit(int index, unsigned char A) { + unsigned char ary[5] = {A, A, A, A, A}; + Output8BitAry(index, ary); +} + +//written by me because of different table positions. +// mem[47] = ... +// 168=pitches +// 169=frequency1 +// 170=frequency2 +// 171=frequency3 +// 172=amplitude1 +// 173=amplitude2 +// 174=amplitude3 +unsigned char STM32SAM::Read(unsigned char p, unsigned char Y) { + switch(p) { + case 168: + return pitches[Y]; + case 169: + return frequency1[Y]; + case 170: + return frequency2[Y]; + case 171: + return frequency3[Y]; + case 172: + return amplitude1[Y]; + case 173: + return amplitude2[Y]; + case 174: + return amplitude3[Y]; + } + // Serial1.println("Error reading to tables"); + return 0; +} + +void STM32SAM::Write(unsigned char p, unsigned char Y, unsigned char value) { + switch(p) { + case 168: + pitches[Y] = value; + return; + case 169: + frequency1[Y] = value; + return; + case 170: + frequency2[Y] = value; + return; + case 171: + frequency3[Y] = value; + return; + case 172: + amplitude1[Y] = value; + return; + case 173: + amplitude2[Y] = value; + return; + case 174: + amplitude3[Y] = value; + return; + } + //Serial1.println("Error writing to tables\n"); +} + +// ------------------------------------------------------------------------- +//Code48227 +// Render a sampled sound from the sampleTable. +// +// Phoneme Sample Start Sample End +// 32: S* 15 255 +// 33: SH 257 511 +// 34: F* 559 767 +// 35: TH 583 767 +// 36: /H 903 1023 +// 37: /X 1135 1279 +// 38: Z* 84 119 +// 39: ZH 340 375 +// 40: V* 596 639 +// 41: DH 596 631 +// +// 42: CH +// 43: ** 399 511 +// +// 44: J* +// 45: ** 257 276 +// 46: ** +// +// 66: P* +// 67: ** 743 767 +// 68: ** +// +// 69: T* +// 70: ** 231 255 +// 71: ** +// +// The SampledPhonemesTable[] holds flags indicating if a phoneme is +// voiced or not. If the upper 5 bits are zero, the sample is voiced. +// +// Samples in the sampleTable are compressed, with bits being converted to +// bytes from high bit to low, as follows: +// +// unvoiced 0 bit -> X +// unvoiced 1 bit -> 5 +// +// voiced 0 bit -> 6 +// voiced 1 bit -> 24 +// +// Where X is a value from the table: +// +// { 0x18, 0x1A, 0x17, 0x17, 0x17 }; +// +// The index into this table is determined by masking off the lower +// 3 bits from the SampledPhonemesTable: +// +// index = (SampledPhonemesTable[i] & 7) - 1; +// +// For voices samples, samples are interleaved between voiced output. + +// Code48227() +void STM32SAM::RenderSample(unsigned char* mem66) { + int tempA; + // current phoneme's index + mem49 = Y; + + // mask low three bits and subtract 1 get value to + // convert 0 bits on unvoiced samples. + A = mem39 & 7; + X = A - 1; + + // store the result + mem56 = X; + + // determine which offset to use from table { 0x18, 0x1A, 0x17, 0x17, 0x17 } + // T, S, Z 0 0x18 + // CH, J, SH, ZH 1 0x1A + // P, F*, V, TH, DH 2 0x17 + // /H 3 0x17 + // /X 4 0x17 + + // get value from the table + mem53 = tab48426[X]; + mem47 = X; //46016+mem[56]*256 + + // voiced sample? + A = mem39 & 248; + if(A == 0) { + // voiced phoneme: Z*, ZH, V*, DH + Y = mem49; + A = pitches[mem49] >> 4; + + // jump to voiced portion + goto pos48315; + } + + Y = A ^ 255; +pos48274: + + // step through the 8 bits in the sample + mem56 = 8; + + // get the next sample from the table + // mem47*256 = offset to start of samples + A = sampleTable[mem47 * 256 + Y]; +pos48280: + + // left shift to get the high bit + tempA = A; + A = A << 1; + //48281: BCC 48290 + + // bit not set? + if((tempA & 128) == 0) { + // convert the bit to value from table + X = mem53; + //mem[54296] = X; + // output the byte + Output8Bit(1, (X & 0x0f) * 16); + // if X != 0, exit loop + if(X != 0) goto pos48296; + } + + // output a 5 for the on bit + Output8Bit(2, 5 * 16); + + //48295: NOP +pos48296: + + X = 0; + + // decrement counter + mem56--; + + // if not done, jump to top of loop + if(mem56 != 0) goto pos48280; + + // increment position + Y++; + if(Y != 0) goto pos48274; + + // restore values and return + mem44 = 1; + Y = mem49; + return; + + unsigned char phase1; + +pos48315: + // handle voiced samples here + + // number of samples? + phase1 = A ^ 255; + + Y = *mem66; + do { + //pos48321: + + // shift through all 8 bits + mem56 = 8; + //A = Read(mem47, Y); + + // fetch value from table + A = sampleTable[mem47 * 256 + Y]; + + // loop 8 times + //pos48327: + do { + //48327: ASL A + //48328: BCC 48337 + + // left shift and check high bit + tempA = A; + A = A << 1; + if((tempA & 128) != 0) { + // if bit set, output 26 + X = 26; + Output8Bit(3, (X & 0xf) * 16); + } else { + //timetable 4 + // bit is not set, output a 6 + X = 6; + Output8Bit(4, (X & 0xf) * 16); + } + + mem56--; + } while(mem56 != 0); + + // move ahead in the table + Y++; + + // continue until counter done + phase1++; + + } while(phase1 != 0); + // if (phase1 != 0) goto pos48321; + + // restore values and return + A = 1; + mem44 = 1; + *mem66 = Y; + Y = mem49; + return; +} + +// RENDER THE PHONEMES IN THE LIST +// +// The phoneme list is converted into sound through the steps: +// +// 1. Copy each phoneme number of times into the frames list, +// where each frame represents 10 milliseconds of sound. +// +// 2. Determine the transitions lengths between phonemes, and linearly +// interpolate the values across the frames. +// +// 3. Offset the pitches by the fundamental frequency. +// +// 4. Render the each frame. + +//void Code47574() +void STM32SAM::Render() { + unsigned char phase1 = 0; //mem43 + unsigned char phase2 = 0; + unsigned char phase3 = 0; + unsigned char mem66 = 0; + unsigned char mem38 = 0; + unsigned char mem40 = 0; + unsigned char speedcounter = 0; //mem45 + unsigned char mem48 = 0; + int i; + if(phonemeIndexOutput[0] == 255) return; //exit if no data + + A = 0; + X = 0; + mem44 = 0; + + // CREATE FRAMES + // + // The length parameter in the list corresponds to the number of frames + // to expand the phoneme to. Each frame represents 10 milliseconds of time. + // So a phoneme with a length of 7 = 7 frames = 70 milliseconds duration. + // + // The parameters are copied from the phoneme to the frame verbatim. + + // pos47587: + do { + // get the index + Y = mem44; + // get the phoneme at the index + A = phonemeIndexOutput[mem44]; + mem56 = A; + + // if terminal phoneme, exit the loop + if(A == 255) break; + + // period phoneme *. + if(A == 1) { + // add rising inflection + A = 1; + mem48 = 1; + //goto pos48376; + AddInflection(mem48, phase1); + } + /* + if (A == 2) goto pos48372; + */ + + // question mark phoneme? + if(A == 2) { + // create falling inflection + mem48 = 255; + AddInflection(mem48, phase1); + } + // pos47615: + + // get the stress amount (more stress = higher pitch) + phase1 = tab47492[stressOutput[Y] + 1]; + + // get number of frames to write + phase2 = phonemeLengthOutput[Y]; + Y = mem56; + + // copy from the source to the frames list + do { + frequency1[X] = freq1data[Y]; // F1 frequency + frequency2[X] = freq2data[Y]; // F2 frequency + frequency3[X] = freq3data[Y]; // F3 frequency + amplitude1[X] = ampl1data[Y]; // F1 amplitude + amplitude2[X] = ampl2data[Y]; // F2 amplitude + amplitude3[X] = ampl3data[Y]; // F3 amplitude + sampledConsonantFlag[X] = + sampledConsonantFlags[Y]; // phoneme data for sampled consonants + pitches[X] = pitch + phase1; // pitch + X++; + phase2--; + } while(phase2 != 0); + mem44++; + } while(mem44 != 0); + // ------------------- + //pos47694: + + // CREATE TRANSITIONS + // + // Linear transitions are now created to smoothly connect the + // end of one sustained portion of a phoneme to the following + // phoneme. + // + // To do this, three tables are used: + // + // Table Purpose + // ========= ================================================== + // blendRank Determines which phoneme's blend values are used. + // + // blendOut The number of frames at the end of the phoneme that + // will be used to transition to the following phoneme. + // + // blendIn The number of frames of the following phoneme that + // will be used to transition into that phoneme. + // + // In creating a transition between two phonemes, the phoneme + // with the HIGHEST rank is used. Phonemes are ranked on how much + // their identity is based on their transitions. For example, + // vowels are and diphthongs are identified by their sustained portion, + // rather than the transitions, so they are given low values. In contrast, + // stop consonants (P, B, T, K) and glides (Y, L) are almost entirely + // defined by their transitions, and are given high rank values. + // + // Here are the rankings used by SAM: + // + // Rank Type Phonemes + // 2 All vowels IY, IH, etc. + // 5 Diphthong endings YX, WX, ER + // 8 Terminal liquid consonants LX, WX, YX, N, NX + // 9 Liquid consonants L, RX, W + // 10 Glide R, OH + // 11 Glide WH + // 18 Voiceless fricatives S, SH, F, TH + // 20 Voiced fricatives Z, ZH, V, DH + // 23 Plosives, stop consonants P, T, K, KX, DX, CH + // 26 Stop consonants J, GX, B, D, G + // 27-29 Stop consonants (internal) ** + // 30 Unvoiced consonants /H, /X and Q* + // 160 Nasal M + // + // To determine how many frames to use, the two phonemes are + // compared using the blendRank[] table. The phoneme with the + // higher rank is selected. In case of a tie, a blend of each is used: + // + // if blendRank[phoneme1] == blendRank[phomneme2] + // // use lengths from each phoneme + // outBlendFrames = outBlend[phoneme1] + // inBlendFrames = outBlend[phoneme2] + // else if blendRank[phoneme1] > blendRank[phoneme2] + // // use lengths from first phoneme + // outBlendFrames = outBlendLength[phoneme1] + // inBlendFrames = inBlendLength[phoneme1] + // else + // // use lengths from the second phoneme + // // note that in and out are SWAPPED! + // outBlendFrames = inBlendLength[phoneme2] + // inBlendFrames = outBlendLength[phoneme2] + // + // Blend lengths can't be less than zero. + // + // Transitions are assumed to be symetrical, so if the transition + // values for the second phoneme are used, the inBlendLength and + // outBlendLength values are SWAPPED. + // + // For most of the parameters, SAM interpolates over the range of the last + // outBlendFrames-1 and the first inBlendFrames. + // + // The exception to this is the Pitch[] parameter, which is interpolates the + // pitch from the CENTER of the current phoneme to the CENTER of the next + // phoneme. + // + // Here are two examples. First, For example, consider the word "SUN" (S AH N) + // + // Phoneme Duration BlendWeight OutBlendFrames InBlendFrames + // S 2 18 1 3 + // AH 8 2 4 4 + // N 7 8 1 2 + // + // The formant transitions for the output frames are calculated as follows: + // + // flags ampl1 freq1 ampl2 freq2 ampl3 freq3 pitch + // ------------------------------------------------ + // S + // 241 0 6 0 73 0 99 61 Use S (weight 18) for transition instead of AH (weight 2) + // 241 0 6 0 73 0 99 61 <-- (OutBlendFrames-1) = (1-1) = 0 frames + // AH + // 0 2 10 2 66 0 96 59 * <-- InBlendFrames = 3 frames + // 0 4 14 3 59 0 93 57 * + // 0 8 18 5 52 0 90 55 * + // 0 15 22 9 44 1 87 53 + // 0 15 22 9 44 1 87 53 + // 0 15 22 9 44 1 87 53 Use N (weight 8) for transition instead of AH (weight 2). + // 0 15 22 9 44 1 87 53 Since N is second phoneme, reverse the IN and OUT values. + // 0 11 17 8 47 1 98 56 * <-- (InBlendFrames-1) = (2-1) = 1 frames + // N + // 0 8 12 6 50 1 109 58 * <-- OutBlendFrames = 1 + // 0 5 6 5 54 0 121 61 + // 0 5 6 5 54 0 121 61 + // 0 5 6 5 54 0 121 61 + // 0 5 6 5 54 0 121 61 + // 0 5 6 5 54 0 121 61 + // 0 5 6 5 54 0 121 61 + // + // Now, consider the reverse "NUS" (N AH S): + // + // flags ampl1 freq1 ampl2 freq2 ampl3 freq3 pitch + // ------------------------------------------------ + // N + // 0 5 6 5 54 0 121 61 + // 0 5 6 5 54 0 121 61 + // 0 5 6 5 54 0 121 61 + // 0 5 6 5 54 0 121 61 + // 0 5 6 5 54 0 121 61 + // 0 5 6 5 54 0 121 61 Use N (weight 8) for transition instead of AH (weight 2) + // 0 5 6 5 54 0 121 61 <-- (OutBlendFrames-1) = (1-1) = 0 frames + // AH + // 0 8 11 6 51 0 110 59 * <-- InBlendFrames = 2 + // 0 11 16 8 48 0 99 56 * + // 0 15 22 9 44 1 87 53 Use S (weight 18) for transition instead of AH (weight 2) + // 0 15 22 9 44 1 87 53 Since S is second phoneme, reverse the IN and OUT values. + // 0 9 18 5 51 1 90 55 * <-- (InBlendFrames-1) = (3-1) = 2 + // 0 4 14 3 58 1 93 57 * + // S + // 241 2 10 2 65 1 96 59 * <-- OutBlendFrames = 1 + // 241 0 6 0 73 0 99 61 + + A = 0; + mem44 = 0; + mem49 = 0; // mem49 starts at as 0 + X = 0; + while(1) //while No. 1 + { + // get the current and following phoneme + Y = phonemeIndexOutput[X]; + A = phonemeIndexOutput[X + 1]; + X++; + + // exit loop at end token + if(A == 255) break; //goto pos47970; + + // get the ranking of each phoneme + X = A; + mem56 = blendRank[A]; + A = blendRank[Y]; + + // compare the rank - lower rank value is stronger + if(A == mem56) { + // same rank, so use out blend lengths from each phoneme + phase1 = outBlendLength[Y]; + phase2 = outBlendLength[X]; + } else if(A < mem56) { + // first phoneme is stronger, so us it's blend lengths + phase1 = inBlendLength[X]; + phase2 = outBlendLength[X]; + } else { + // second phoneme is stronger, so use it's blend lengths + // note the out/in are swapped + phase1 = outBlendLength[Y]; + phase2 = inBlendLength[Y]; + } + + Y = mem44; + A = mem49 + phonemeLengthOutput[mem44]; // A is mem49 + length + mem49 = A; // mem49 now holds length + position + A = A + phase2; //Maybe Problem because of carry flag + + //47776: ADC 42 + speedcounter = A; + mem47 = 168; + phase3 = mem49 - phase1; // what is mem49 + A = phase1 + phase2; // total transition? + mem38 = A; + + X = A; + X -= 2; + if((X & 128) == 0) + do //while No. 2 + { + //pos47810: + + // mem47 is used to index the tables: + // 168 pitches[] + // 169 frequency1 + // 170 frequency2 + // 171 frequency3 + // 172 amplitude1 + // 173 amplitude2 + // 174 amplitude3 + + mem40 = mem38; + + if(mem47 == 168) // pitch + { + // unlike the other values, the pitches[] interpolates from + // the middle of the current phoneme to the middle of the + // next phoneme + + unsigned char mem36, mem37; + // half the width of the current phoneme + mem36 = phonemeLengthOutput[mem44] >> 1; + // half the width of the next phoneme + mem37 = phonemeLengthOutput[mem44 + 1] >> 1; + // sum the values + mem40 = mem36 + mem37; // length of both halves + mem37 += mem49; // center of next phoneme + mem36 = mem49 - mem36; // center index of current phoneme + A = Read( + mem47, mem37); // value at center of next phoneme - end interpolation value + //A = mem[address]; + + Y = mem36; // start index of interpolation + mem53 = A - Read(mem47, mem36); // value to center of current phoneme + } else { + // value to interpolate to + A = Read(mem47, speedcounter); + // position to start interpolation from + Y = phase3; + // value to interpolate from + mem53 = A - Read(mem47, phase3); + } + + //Code47503(mem40); + // ML : Code47503 is division with remainder, and mem50 gets the sign + + // calculate change per frame + signed char m53 = (signed char)mem53; + mem50 = mem53 & 128; + unsigned char m53abs = abs(m53); + mem51 = m53abs % mem40; //abs((char)m53) % mem40; + mem53 = (unsigned char)((signed char)(m53) / mem40); + + // interpolation range + X = mem40; // number of frames to interpolate over + Y = phase3; // starting frame + + // linearly interpolate values + + mem56 = 0; + //47907: CLC + //pos47908: + while(1) //while No. 3 + { + A = Read(mem47, Y) + mem53; //carry alway cleared + + mem48 = A; + Y++; + X--; + if(X == 0) break; + + mem56 += mem51; + if(mem56 >= mem40) //??? + { + mem56 -= mem40; //carry? is set + //if ((mem56 & 128)==0) + if((mem50 & 128) == 0) { + //47935: BIT 50 + //47937: BMI 47943 + if(mem48 != 0) mem48++; + } else + mem48--; + } + //pos47945: + Write(mem47, Y, mem48); + } //while No. 3 + + //pos47952: + mem47++; + //if (mem47 != 175) goto pos47810; + } while(mem47 != 175); //while No. 2 + //pos47963: + mem44++; + X = mem44; + } //while No. 1 + + //goto pos47701; + //pos47970: + + // add the length of this phoneme + mem48 = mem49 + phonemeLengthOutput[mem44]; + + // ASSIGN PITCH CONTOUR + // + // This subtracts the F1 frequency from the pitch to create a + // pitch contour. Without this, the output would be at a single + // pitch level (monotone). + + // don't adjust pitch if in sing mode + if(!singmode) { + // iterate through the buffer + for(i = 0; i < 256; i++) { + // subtract half the frequency of the formant 1. + // this adds variety to the voice + pitches[i] -= (frequency1[i] >> 1); + } + } + + phase1 = 0; + phase2 = 0; + phase3 = 0; + mem49 = 0; + speedcounter = 72; //sam standard speed + + // RESCALE AMPLITUDE + // + // Rescale volume from a linear scale to decibels. + // + + //amplitude rescaling + for(i = 255; i >= 0; i--) { + amplitude1[i] = amplitudeRescale[amplitude1[i]]; + amplitude2[i] = amplitudeRescale[amplitude2[i]]; + amplitude3[i] = amplitudeRescale[amplitude3[i]]; + } + + Y = 0; + A = pitches[0]; + mem44 = A; + X = A; + mem38 = A - (A >> 2); // 3/4*A ??? + + // PROCESS THE FRAMES + // + // In traditional vocal synthesis, the glottal pulse drives filters, which + // are attenuated to the frequencies of the formants. + // + // SAM generates these formants directly with sin and rectangular waves. + // To simulate them being driven by the glottal pulse, the waveforms are + // reset at the beginning of each glottal pulse. + + //finally the loop for sound output + //pos48078: + while(1) { + // get the sampled information on the phoneme + A = sampledConsonantFlag[Y]; + mem39 = A; + + // unvoiced sampled phoneme? + A = A & 248; + if(A != 0) { + // render the sample for the phoneme + RenderSample(&mem66); + + // skip ahead two in the phoneme buffer + Y += 2; + mem48 -= 2; + } else { + // simulate the glottal pulse and formants + unsigned char ary[5]; + unsigned int p1 = + phase1 * 256; // Fixed point integers because we need to divide later on + unsigned int p2 = phase2 * 256; + unsigned int p3 = phase3 * 256; + int k; + for(k = 0; k < 5; k++) { + signed char sp1 = (signed char)sinus[0xff & (p1 >> 8)]; + signed char sp2 = (signed char)sinus[0xff & (p2 >> 8)]; + signed char rp3 = (signed char)rectangle[0xff & (p3 >> 8)]; + signed int sin1 = sp1 * ((unsigned char)amplitude1[Y] & 0x0f); + signed int sin2 = sp2 * ((unsigned char)amplitude2[Y] & 0x0f); + signed int rect = rp3 * ((unsigned char)amplitude3[Y] & 0x0f); + signed int mux = sin1 + sin2 + rect; + mux /= 32; + mux += 128; // Go from signed to unsigned amplitude + ary[k] = mux; + p1 += frequency1[Y] * 256 / 4; // Compromise, this becomes a shift and works well + p2 += frequency2[Y] * 256 / 4; + p3 += frequency3[Y] * 256 / 4; + } + // output the accumulated value + Output8BitAry(0, ary); + speedcounter--; + if(speedcounter != 0) goto pos48155; + Y++; //go to next amplitude + + // decrement the frame count + mem48--; + } + + // if the frame count is zero, exit the loop + if(mem48 == 0) return; + speedcounter = speed; + pos48155: + + // decrement the remaining length of the glottal pulse + mem44--; + + // finished with a glottal pulse? + if(mem44 == 0) { + pos48159: + // fetch the next glottal pulse length + A = pitches[Y]; + mem44 = A; + A = A - (A >> 2); + mem38 = A; + + // reset the formant wave generators to keep them in + // sync with the glottal pulse + phase1 = 0; + phase2 = 0; + phase3 = 0; + continue; + } + + // decrement the count + mem38--; + + // is the count non-zero and the sampled flag is zero? + if((mem38 != 0) || (mem39 == 0)) { + // reset the phase of the formants to match the pulse + phase1 += frequency1[Y]; + phase2 += frequency2[Y]; + phase3 += frequency3[Y]; + continue; + } + + // voiced sampled phonemes interleave the sample with the + // glottal pulse. The sample flag is non-zero, so render + // the sample for the phoneme. + RenderSample(&mem66); + goto pos48159; + } //while + + // The following code is never reached. It's left over from when + // the voiced sample code was part of this loop, instead of part + // of RenderSample(); + + //pos48315: + int tempA; + phase1 = A ^ 255; + Y = mem66; + do { + //pos48321: + + mem56 = 8; + A = Read(mem47, Y); + + //pos48327: + do { + //48327: ASL A + //48328: BCC 48337 + tempA = A; + A = A << 1; + if((tempA & 128) != 0) { + X = 26; + // mem[54296] = X; + bufferpos += 150; + // + // + // buffer[bufferpos / 50] = (X & 15) * 16; + // + // + + } else { + //mem[54296] = 6; + X = 6; + bufferpos += 150; + // + // buffer[bufferpos / 50] = (X & 15) * 16; + // + // + } + + for(X = wait2; X > 0; X--) + ; //wait + mem56--; + } while(mem56 != 0); + + Y++; + phase1++; + + } while(phase1 != 0); + // if (phase1 != 0) goto pos48321; + A = 1; + mem44 = 1; + mem66 = Y; + Y = mem49; + return; +} + +// Create a rising or falling inflection 30 frames prior to +// index X. A rising inflection is used for questions, and +// a falling inflection is used for statements. + +void STM32SAM::AddInflection(unsigned char mem48, unsigned char phase1) { + //pos48372: + // mem48 = 255; + //pos48376: + + // store the location of the punctuation + mem49 = X; + A = X; + int Atemp = A; + + // backup 30 frames + A = A - 30; + // if index is before buffer, point to start of buffer + if(Atemp <= 30) A = 0; + X = A; + + // FIXME: Explain this fix better, it's not obvious + // ML : A =, fixes a problem with invalid pitch with '.' + while((A = pitches[X]) == 127) X++; + +pos48398: + //48398: CLC + //48399: ADC 48 + + // add the inflection direction + A += mem48; + phase1 = A; + + // set the inflection + pitches[X] = A; +pos48406: + + // increment the position + X++; + + // exit if the punctuation has been reached + if(X == mem49) return; //goto pos47615; + if(pitches[X] == 255) goto pos48406; + A = phase1; + goto pos48398; +} + +/* + SAM's voice can be altered by changing the frequencies of the + mouth formant (F1) and the throat formant (F2). Only the voiced + phonemes (5-29 and 48-53) are altered. +*/ +void STM32SAM::SetMouthThroat() { + unsigned char initialFrequency; + unsigned char newFrequency = 0; + //unsigned char mouth; //mem38880 + //unsigned char throat; //mem38881 + + // mouth formants (F1) 5..29 + unsigned char mouthFormants5_29[30] = {0, 0, 0, 0, 0, 10, 14, 19, 24, 27, + 23, 21, 16, 20, 14, 18, 14, 18, 18, 16, + 13, 15, 11, 18, 14, 11, 9, 6, 6, 6}; + + // throat formants (F2) 5..29 + unsigned char throatFormants5_29[30] = {255, 255, 255, 255, 255, 84, 73, 67, 63, 40, + 44, 31, 37, 45, 73, 49, 36, 30, 51, 37, + 29, 69, 24, 50, 30, 24, 83, 46, 54, 86}; + + // there must be no zeros in this 2 tables + // formant 1 frequencies (mouth) 48..53 + unsigned char mouthFormants48_53[6] = {19, 27, 21, 27, 18, 13}; + + // formant 2 frequencies (throat) 48..53 + unsigned char throatFormants48_53[6] = {72, 39, 31, 43, 30, 34}; + + unsigned char pos = 5; //mem39216 + //pos38942: + // recalculate formant frequencies 5..29 for the mouth (F1) and throat (F2) + while(pos != 30) { + // recalculate mouth frequency + initialFrequency = mouthFormants5_29[pos]; + if(initialFrequency != 0) newFrequency = trans(mouth, initialFrequency); + freq1data[pos] = newFrequency; + + // recalculate throat frequency + initialFrequency = throatFormants5_29[pos]; + if(initialFrequency != 0) newFrequency = trans(throat, initialFrequency); + freq2data[pos] = newFrequency; + pos++; + } + + //pos39059: + // recalculate formant frequencies 48..53 + pos = 48; + Y = 0; + while(pos != 54) { + // recalculate F1 (mouth formant) + initialFrequency = mouthFormants48_53[Y]; + newFrequency = trans(mouth, initialFrequency); + freq1data[pos] = newFrequency; + + // recalculate F2 (throat formant) + initialFrequency = throatFormants48_53[Y]; + newFrequency = trans(throat, initialFrequency); + freq2data[pos] = newFrequency; + Y++; + pos++; + } +} + +//return = (mem39212*mem39213) >> 1 +unsigned char STM32SAM::trans(unsigned char mem39212, unsigned char mem39213) { + //pos39008: + unsigned char carry; + int temp; + unsigned char mem39214, mem39215; + A = 0; + mem39215 = 0; + mem39214 = 0; + X = 8; + do { + carry = mem39212 & 1; + mem39212 = mem39212 >> 1; + if(carry != 0) { + /* + 39018: LSR 39212 + 39021: BCC 39033 + */ + carry = 0; + A = mem39215; + temp = (int)A + (int)mem39213; + A = A + mem39213; + if(temp > 255) carry = 1; + mem39215 = A; + } + temp = mem39215 & 1; + mem39215 = (mem39215 >> 1) | (carry ? 128 : 0); + carry = temp; + //39033: ROR 39215 + X--; + } while(X != 0); + temp = mem39214 & 128; + mem39214 = (mem39214 << 1) | (carry ? 1 : 0); + carry = temp; + temp = mem39215 & 128; + mem39215 = (mem39215 << 1) | (carry ? 1 : 0); + carry = temp; + + return mem39215; +} + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// Sam +// +//////////////////////////////////////////////////////////////////////////////////////////// + +//char input[]={"/HAALAOAO MAYN NAAMAEAE IHSTT SAEBAASTTIHAAN \x9b\x9b\0"}; +//unsigned char input[]={"/HAALAOAO \x9b\0"}; +//unsigned char input[]={"AA \x9b\0"}; +//unsigned char input[] = {"GUH5DEHN TAEG\x9b\0"}; + +//unsigned char input[]={"AY5 AEM EY TAO4LXKIHNX KAX4MPYUX4TAH. GOW4 AH/HEH3D PAHNK.MEYK MAY8 DEY.\x9b\0"}; +//unsigned char input[]={"/HEH3LOW2, /HAW AH YUX2 TUXDEY. AY /HOH3P YUX AH FIYLIHNX OW4 KEY.\x9b\0"}; +//unsigned char input[]={"/HEY2, DHIHS IH3Z GREY2T. /HAH /HAH /HAH.AYL BIY5 BAEK.\x9b\0"}; +//unsigned char input[]={"/HAH /HAH /HAH \x9b\0"}; +//unsigned char input[]={"/HAH /HAH /HAH.\x9b\0"}; +//unsigned char input[]={".TUW BIY5Y3,, OHR NAA3T - TUW BIY5IYIY., DHAE4T IHZ DHAH KWEH4SCHAHN.\x9b\0"}; +//unsigned char input[]={"/HEY2, DHIHS \x9b\0"}; + +//unsigned char input[]={" IYIHEHAEAAAHAOOHUHUXERAXIX \x9b\0"}; +//unsigned char input[]={" RLWWYMNNXBDGJZZHVDH \x9b\0"}; +//unsigned char input[]={" SSHFTHPTKCH/H \x9b\0"}; + +//unsigned char input[]={" EYAYOYAWOWUW ULUMUNQ YXWXRXLX/XDX\x9b\0"}; + +void STM32SAM::SetInput(char* _input) { + int i, l; + l = strlen(_input); + if(l > 254) l = 254; + for(i = 0; i < l; i++) { + input[i] = _input[i]; + } + input[l] = 0; +} + +// 168=pitches +// 169=frequency1 +// 170=frequency2 +// 171=frequency3 +// 172=amplitude1 +// 173=amplitude2 +// 174=amplitude3 + +void STM32SAM::Init() { + bufferpos = 0; + int i; + SetMouthThroat(); + + bufferpos = 0; + // TODO, check for free the memory, 10 seconds of output should be more than enough + //buffer = malloc(22050*10); + + // buffer = (char*) calloc(1, sizeof(char)); + + /* + freq2data = &mem[45136]; + freq1data = &mem[45056]; + freq3data = &mem[45216]; + */ + //pitches = &mem[43008]; + /* + frequency1 = &mem[43264]; + frequency2 = &mem[43520]; + frequency3 = &mem[43776]; + */ + /* + amplitude1 = &mem[44032]; + amplitude2 = &mem[44288]; + amplitude3 = &mem[44544]; + */ + //phoneme = &mem[39904]; + /* + ampl1data = &mem[45296]; + ampl2data = &mem[45376]; + ampl3data = &mem[45456]; + */ + + for(i = 0; i < 256; i++) { + stress[i] = 0; + phonemeLength[i] = 0; + } + + for(i = 0; i < 60; i++) { + phonemeIndexOutput[i] = 0; + stressOutput[i] = 0; + phonemeLengthOutput[i] = 0; + } + phonemeindex[255] = + 255; //to prevent buffer overflow // ML : changed from 32 to 255 to stop freezing with long inputs +} + +//int Code39771() +int STM32SAM::SAMMain() { + Init(); + phonemeindex[255] = 32; //to prevent buffer overflow + + if(!Parser1()) { + return 0; + } + + Parser2(); + CopyStress(); + SetPhonemeLength(); + AdjustLengths(); + Code41240(); + do { + A = phonemeindex[X]; + if(A > 80) { + phonemeindex[X] = 255; + break; // error: delete all behind it + } + X++; + } while(X != 0); + + //pos39848: + InsertBreath(); + + //mem[40158] = 255; + + PrepareOutput(); + + return 1; +} + +//void Code48547() +void STM32SAM::PrepareOutput() { + A = 0; + X = 0; + Y = 0; + + //pos48551: + while(1) { + A = phonemeindex[X]; + if(A == 255) { + A = 255; + phonemeIndexOutput[Y] = 255; + Render(); + return; + } + if(A == 254) { + X++; + int temp = X; + //mem[48546] = X; + phonemeIndexOutput[Y] = 255; + Render(); + //X = mem[48546]; + X = temp; + Y = 0; + continue; + } + + if(A == 0) { + X++; + continue; + } + + phonemeIndexOutput[Y] = A; + phonemeLengthOutput[Y] = phonemeLength[X]; + stressOutput[Y] = stress[X]; + X++; + Y++; + } +} + +//void Code41014() +void STM32SAM::Insert( + unsigned char position /*var57*/, + unsigned char mem60, + unsigned char mem59, + unsigned char mem58) { + int i; + for(i = 253; i >= position; i--) // ML : always keep last safe-guarding 255 + { + phonemeindex[i + 1] = phonemeindex[i]; + phonemeLength[i + 1] = phonemeLength[i]; + stress[i + 1] = stress[i]; + } + + phonemeindex[position] = mem60; + phonemeLength[position] = mem59; + stress[position] = mem58; + return; +} + +//void Code48431() +void STM32SAM::InsertBreath() { + unsigned char mem54; + unsigned char mem55; + unsigned char index; //variable Y + mem54 = 255; + X++; + mem55 = 0; + unsigned char mem66 = 0; + while(1) { + //pos48440: + X = mem66; + index = phonemeindex[X]; + if(index == 255) return; + mem55 += phonemeLength[X]; + + if(mem55 < 232) { + if(index != 254) // ML : Prevents an index out of bounds problem + { + A = flags2[index] & 1; + if(A != 0) { + X++; + mem55 = 0; + Insert(X, 254, mem59, 0); + mem66++; + mem66++; + continue; + } + } + if(index == 0) mem54 = X; + mem66++; + continue; + } + X = mem54; + phonemeindex[X] = 31; // 'Q*' glottal stop + phonemeLength[X] = 4; + stress[X] = 0; + X++; + mem55 = 0; + Insert(X, 254, mem59, 0); + X++; + mem66 = X; + } +} + +// Iterates through the phoneme buffer, copying the stress value from +// the following phoneme under the following circumstance: + +// 1. The current phoneme is voiced, excluding plosives and fricatives +// 2. The following phoneme is voiced, excluding plosives and fricatives, and +// 3. The following phoneme is stressed +// +// In those cases, the stress value+1 from the following phoneme is copied. +// +// For example, the word LOITER is represented as LOY5TER, with as stress +// of 5 on the diphtong OY. This routine will copy the stress value of 6 (5+1) +// to the L that precedes it. + +//void Code41883() +void STM32SAM::CopyStress() { + // loop thought all the phonemes to be output + unsigned char pos = 0; //mem66 + while(1) { + // get the phomene + Y = phonemeindex[pos]; + + // exit at end of buffer + if(Y == 255) return; + + // if CONSONANT_FLAG set, skip - only vowels get stress + if((flags[Y] & 64) == 0) { + pos++; + continue; + } + // get the next phoneme + Y = phonemeindex[pos + 1]; + if(Y == 255) //prevent buffer overflow + { + pos++; + continue; + } else + // if the following phoneme is a vowel, skip + if((flags[Y] & 128) == 0) { + pos++; + continue; + } + + // get the stress value at the next position + Y = stress[pos + 1]; + + // if next phoneme is not stressed, skip + if(Y == 0) { + pos++; + continue; + } + + // if next phoneme is not a VOWEL OR ER, skip + if((Y & 128) != 0) { + pos++; + continue; + } + + // copy stress from prior phoneme to this one + stress[pos] = Y + 1; + + // advance pointer + pos++; + } +} + +// The input[] buffer contains a string of phonemes and stress markers along +// the lines of: +// +// DHAX KAET IHZ AH5GLIY. <0x9B> +// +// The byte 0x9B marks the end of the buffer. Some phonemes are 2 bytes +// long, such as "DH" and "AX". Others are 1 byte long, such as "T" and "Z". +// There are also stress markers, such as "5" and ".". +// +// The first character of the phonemes are stored in the table signInputTable1[]. +// The second character of the phonemes are stored in the table signInputTable2[]. +// The stress characters are arranged in low to high stress order in stressInputTable[]. +// +// The following process is used to parse the input[] buffer: +// +// Repeat until the <0x9B> character is reached: +// +// First, a search is made for a 2 character match for phonemes that do not +// end with the '*' (wildcard) character. On a match, the index of the phoneme +// is added to phonemeIndex[] and the buffer position is advanced 2 bytes. +// +// If this fails, a search is made for a 1 character match against all +// phoneme names ending with a '*' (wildcard). If this succeeds, the +// phoneme is added to phonemeIndex[] and the buffer position is advanced +// 1 byte. +// +// If this fails, search for a 1 character match in the stressInputTable[]. +// If this succeeds, the stress value is placed in the last stress[] table +// at the same index of the last added phoneme, and the buffer position is +// advanced by 1 byte. +// +// If this fails, return a 0. +// +// On success: +// +// 1. phonemeIndex[] will contain the index of all the phonemes. +// 2. The last index in phonemeIndex[] will be 255. +// 3. stress[] will contain the stress value for each phoneme + +// input[] holds the string of phonemes, each two bytes wide +// signInputTable1[] holds the first character of each phoneme +// signInputTable2[] holds te second character of each phoneme +// phonemeIndex[] holds the indexes of the phonemes after parsing input[] +// +// The parser scans through the input[], finding the names of the phonemes +// by searching signInputTable1[] and signInputTable2[]. On a match, it +// copies the index of the phoneme into the phonemeIndexTable[]. +// +// The character <0x9B> marks the end of text in input[]. When it is reached, +// the index 255 is placed at the end of the phonemeIndexTable[], and the +// function returns with a 1 indicating success. +int STM32SAM::Parser1() { + int i; + unsigned char sign1; + unsigned char sign2; + unsigned char position = 0; + X = 0; + A = 0; + Y = 0; + + // CLEAR THE STRESS TABLE + for(i = 0; i < 256; i++) stress[i] = 0; + + // THIS CODE MATCHES THE PHONEME LETTERS TO THE TABLE + // pos41078: + while(1) { + // GET THE FIRST CHARACTER FROM THE PHONEME BUFFER + sign1 = input[X]; + // TEST FOR 155 (�) END OF LINE MARKER + if(sign1 == 155) { + // MARK ENDPOINT AND RETURN + phonemeindex[position] = 255; //mark endpoint + // REACHED END OF PHONEMES, SO EXIT + return 1; //all ok + } + + // GET THE NEXT CHARACTER FROM THE BUFFER + X++; + sign2 = input[X]; + + // NOW sign1 = FIRST CHARACTER OF PHONEME, AND sign2 = SECOND CHARACTER OF PHONEME + + // TRY TO MATCH PHONEMES ON TWO TWO-CHARACTER NAME + // IGNORE PHONEMES IN TABLE ENDING WITH WILDCARDS + + // SET INDEX TO 0 + Y = 0; + pos41095: + + // GET FIRST CHARACTER AT POSITION Y IN signInputTable + // --> should change name to PhonemeNameTable1 + A = signInputTable1[Y]; + + // FIRST CHARACTER MATCHES? + if(A == sign1) { + // GET THE CHARACTER FROM THE PhonemeSecondLetterTable + A = signInputTable2[Y]; + // NOT A SPECIAL AND MATCHES SECOND CHARACTER? + if((A != '*') && (A == sign2)) { + // STORE THE INDEX OF THE PHONEME INTO THE phomeneIndexTable + phonemeindex[position] = Y; + + // ADVANCE THE POINTER TO THE phonemeIndexTable + position++; + // ADVANCE THE POINTER TO THE phonemeInputBuffer + X++; + + // CONTINUE PARSING + continue; + } + } + + // NO MATCH, TRY TO MATCH ON FIRST CHARACTER TO WILDCARD NAMES (ENDING WITH '*') + + // ADVANCE TO THE NEXT POSITION + Y++; + // IF NOT END OF TABLE, CONTINUE + if(Y != 81) goto pos41095; + + // REACHED END OF TABLE WITHOUT AN EXACT (2 CHARACTER) MATCH. + // THIS TIME, SEARCH FOR A 1 CHARACTER MATCH AGAINST THE WILDCARDS + + // RESET THE INDEX TO POINT TO THE START OF THE PHONEME NAME TABLE + Y = 0; + pos41134: + // DOES THE PHONEME IN THE TABLE END WITH '*'? + if(signInputTable2[Y] == '*') { + // DOES THE FIRST CHARACTER MATCH THE FIRST LETTER OF THE PHONEME + if(signInputTable1[Y] == sign1) { + // SAVE THE POSITION AND MOVE AHEAD + phonemeindex[position] = Y; + + // ADVANCE THE POINTER + position++; + + // CONTINUE THROUGH THE LOOP + continue; + } + } + Y++; + if(Y != 81) goto pos41134; //81 is size of PHONEME NAME table + + // FAILED TO MATCH WITH A WILDCARD. ASSUME THIS IS A STRESS + // CHARACTER. SEARCH THROUGH THE STRESS TABLE + + // SET INDEX TO POSITION 8 (END OF STRESS TABLE) + Y = 8; + + // WALK BACK THROUGH TABLE LOOKING FOR A MATCH + while((sign1 != stressInputTable[Y]) && (Y > 0)) { + // DECREMENT INDEX + Y--; + } + + // REACHED THE END OF THE SEARCH WITHOUT BREAKING OUT OF LOOP? + if(Y == 0) { + //mem[39444] = X; + //41181: JSR 42043 //Error + // FAILED TO MATCH ANYTHING, RETURN 0 ON FAILURE + return 0; + } + // SET THE STRESS FOR THE PRIOR PHONEME + stress[position - 1] = Y; + } //while +} + +//change phonemelength depedendent on stress +//void Code41203() +void STM32SAM::SetPhonemeLength() { + unsigned char A; + int position = 0; + while(phonemeindex[position] != 255) { + A = stress[position]; + //41218: BMI 41229 + if((A == 0) || ((A & 128) != 0)) { + phonemeLength[position] = phonemeLengthTable[phonemeindex[position]]; + } else { + phonemeLength[position] = phonemeStressedLengthTable[phonemeindex[position]]; + } + position++; + } +} + +void STM32SAM::Code41240() { + unsigned char pos = 0; + + while(phonemeindex[pos] != 255) { + unsigned char index; //register AC + X = pos; + index = phonemeindex[pos]; + if((flags[index] & 2) == 0) { + pos++; + continue; + } else if((flags[index] & 1) == 0) { + Insert(pos + 1, index + 1, phonemeLengthTable[index + 1], stress[pos]); + Insert(pos + 2, index + 2, phonemeLengthTable[index + 2], stress[pos]); + pos += 3; + continue; + } + + do { + X++; + A = phonemeindex[X]; + } while(A == 0); + + if(A != 255) { + if((flags[A] & 8) != 0) { + pos++; + continue; + } + if((A == 36) || (A == 37)) { + pos++; // '/H' '/X' + continue; + } + } + + Insert(pos + 1, index + 1, phonemeLengthTable[index + 1], stress[pos]); + Insert(pos + 2, index + 2, phonemeLengthTable[index + 2], stress[pos]); + pos += 3; + }; +} + +// Rewrites the phonemes using the following rules: +// +// -> WX +// -> YX +// UL -> AX L +// UM -> AX M +// -> Q +// T R -> CH R +// D R -> J R +// R -> RX +// L -> LX +// G S -> G Z +// K -> KX +// G -> GX +// S P -> S B +// S T -> S D +// S K -> S G +// S KX -> S GX +// UW -> UX +// CH -> CH CH' (CH requires two phonemes to represent it) +// J -> J J' (J requires two phonemes to represent it) +// T -> DX +// D -> DX + +//void Code41397() +void STM32SAM::Parser2() { + unsigned char pos = 0; //mem66; + unsigned char mem58 = 0; + + // Loop through phonemes + while(1) { + // SET X TO THE CURRENT POSITION + X = pos; + // GET THE PHONEME AT THE CURRENT POSITION + A = phonemeindex[pos]; + + // Is phoneme pause? + if(A == 0) { + // Move ahead to the + pos++; + continue; + } + + // If end of phonemes flag reached, exit routine + if(A == 255) return; + + // Copy the current phoneme index to Y + Y = A; + + // RULE: + // -> WX + // -> YX + // Example: OIL, COW + + // Check for DIPHTONG + if((flags[A] & 16) == 0) goto pos41457; + + // Not a diphthong. Get the stress + mem58 = stress[pos]; + + // End in IY sound? + A = flags[Y] & 32; + + // If ends with IY, use YX, else use WX + if(A == 0) + A = 20; + else + A = 21; // 'WX' = 20 'YX' = 21 + //pos41443: + // Insert at WX or YX following, copying the stress + + Insert(pos + 1, A, mem59, mem58); + X = pos; + // Jump to ??? + goto pos41749; + + pos41457: + + // RULE: + // UL -> AX L + // Example: MEDDLE + + // Get phoneme + A = phonemeindex[X]; + // Skip this rule if phoneme is not UL + if(A != 78) goto pos41487; // 'UL' + A = 24; // 'L' //change 'UL' to 'AX L' + + pos41466: + // Get current phoneme stress + mem58 = stress[X]; + + // Change UL to AX + phonemeindex[X] = 13; // 'AX' + // Perform insert. Note code below may jump up here with different values + Insert(X + 1, A, mem59, mem58); + pos++; + // Move to next phoneme + continue; + + pos41487: + + // RULE: + // UM -> AX M + // Example: ASTRONOMY + + // Skip rule if phoneme != UM + if(A != 79) goto pos41495; // 'UM' + // Jump up to branch - replaces current phoneme with AX and continues + A = 27; // 'M' //change 'UM' to 'AX M' + + goto pos41466; + pos41495: + + // RULE: + // UN -> AX N + // Example: FUNCTION + + // Skip rule if phoneme != UN + if(A != 80) goto pos41503; // 'UN' + + // Jump up to branch - replaces current phoneme with AX and continues + A = 28; // 'N' //change UN to 'AX N' + + goto pos41466; + pos41503: + + // RULE: + // -> Q + // EXAMPLE: AWAY EIGHT + + Y = A; + // VOWEL set? + A = flags[A] & 128; + + // Skip if not a vowel + if(A != 0) { + // Get the stress + A = stress[X]; + + // If stressed... + if(A != 0) { + // Get the following phoneme + X++; + A = phonemeindex[X]; + // If following phoneme is a pause + + if(A == 0) { + // Get the phoneme following pause + X++; + Y = phonemeindex[X]; + + // Check for end of buffer flag + if(Y == 255) //buffer overflow + // ??? Not sure about these flags + A = 65 & 128; + else + // And VOWEL flag to current phoneme's flags + A = flags[Y] & 128; + + // If following phonemes is not a pause + if(A != 0) { + // If the following phoneme is not stressed + A = stress[X]; + if(A != 0) { + // 31 = 'Q' + Insert(X, 31, mem59, 0); + pos++; + continue; + } + } + } + } + } + + // RULES FOR PHONEMES BEFORE R + // T R -> CH R + // Example: TRACK + + // Get current position and phoneme + X = pos; + A = phonemeindex[pos]; + if(A != 23) goto pos41611; // 'R' + + // Look at prior phoneme + X--; + A = phonemeindex[pos - 1]; + //pos41567: + if(A == 69) // 'T' + { + phonemeindex[pos - 1] = 42; + goto pos41779; + } + + // RULES FOR PHONEMES BEFORE R + // D R -> J R + // Example: DRY + + // Prior phonemes D? + if(A == 57) // 'D' + { + // Change D to J + phonemeindex[pos - 1] = 44; + + goto pos41788; + } + + // RULES FOR PHONEMES BEFORE R + // R -> RX + // Example: ART + + // If vowel flag is set change R to RX + A = flags[A] & 128; + + if(A != 0) phonemeindex[pos] = 18; // 'RX' + + // continue to next phoneme + pos++; + continue; + + pos41611: + + // RULE: + // L -> LX + // Example: ALL + + // Is phoneme L? + if(A == 24) // 'L' + { + // If prior phoneme does not have VOWEL flag set, move to next phoneme + if((flags[phonemeindex[pos - 1]] & 128) == 0) { + pos++; + continue; + } + // Prior phoneme has VOWEL flag set, so change L to LX and move to next phoneme + + phonemeindex[X] = 19; // 'LX' + pos++; + continue; + } + + // RULE: + // G S -> G Z + // + // Can't get to fire - + // 1. The G -> GX rule intervenes + // 2. Reciter already replaces GS -> GZ + + // Is current phoneme S? + if(A == 32) // 'S' + { + // If prior phoneme is not G, move to next phoneme + if(phonemeindex[pos - 1] != 60) { + pos++; + continue; + } + // Replace S with Z and move on + + phonemeindex[pos] = 38; // 'Z' + pos++; + continue; + } + + // RULE: + // K -> KX + // Example: COW + + // Is current phoneme K? + if(A == 72) // 'K' + { + // Get next phoneme + Y = phonemeindex[pos + 1]; + // If at end, replace current phoneme with KX + if(Y == 255) + phonemeindex[pos] = 75; // ML : prevents an index out of bounds problem + else { + // VOWELS AND DIPHTONGS ENDING WITH IY SOUND flag set? + A = flags[Y] & 32; + + // Replace with KX + if(A == 0) phonemeindex[pos] = 75; // 'KX' + } + } else + + // RULE: + // G -> GX + // Example: GO + + // Is character a G? + if(A == 60) // 'G' + { + // Get the following character + unsigned char index = phonemeindex[pos + 1]; + + // At end of buffer? + if(index == 255) //prevent buffer overflow + { + pos++; + continue; + } else + // If diphtong ending with YX, move continue processing next phoneme + if((flags[index] & 32) != 0) { + pos++; + continue; + } + // replace G with GX and continue processing next phoneme + + phonemeindex[pos] = 63; // 'GX' + pos++; + continue; + } + + // RULE: + // S P -> S B + // S T -> S D + // S K -> S G + // S KX -> S GX + // Examples: SPY, STY, SKY, SCOWL + + Y = phonemeindex[pos]; + //pos41719: + // Replace with softer version? + A = flags[Y] & 1; + if(A == 0) goto pos41749; + A = phonemeindex[pos - 1]; + if(A != 32) // 'S' + { + A = Y; + goto pos41812; + } + // Replace with softer version + + phonemeindex[pos] = Y - 12; + pos++; + continue; + + pos41749: + + // RULE: + // UW -> UX + // + // Example: NEW, DEW, SUE, ZOO, THOO, TOO + + // UW -> UX + + A = phonemeindex[X]; + if(A == 53) // 'UW' + { + // ALVEOLAR flag set? + Y = phonemeindex[X - 1]; + A = flags2[Y] & 4; + // If not set, continue processing next phoneme + if(A == 0) { + pos++; + continue; + } + + phonemeindex[X] = 16; + pos++; + continue; + } + pos41779: + + // RULE: + // CH -> CH CH' (CH requires two phonemes to represent it) + // Example: CHEW + + if(A == 42) // 'CH' + { + // pos41783: + + Insert(X + 1, A + 1, mem59, stress[X]); + pos++; + continue; + } + + pos41788: + + // RULE: + // J -> J J' (J requires two phonemes to represent it) + // Example: JAY + + if(A == 44) // 'J' + { + Insert(X + 1, A + 1, mem59, stress[X]); + pos++; + continue; + } + + // Jump here to continue + pos41812: + + // RULE: Soften T following vowel + // NOTE: This rule fails for cases such as "ODD" + // T -> DX + // D -> DX + // Example: PARTY, TARDY + + // Past this point, only process if phoneme is T or D + + if(A != 69) // 'T' + if(A != 57) { + pos++; // 'D' + continue; + } + //pos41825: + + // If prior phoneme is not a vowel, continue processing phonemes + if((flags[phonemeindex[X - 1]] & 128) == 0) { + pos++; + continue; + } + + // Get next phoneme + X++; + A = phonemeindex[X]; + //pos41841 + // Is the next phoneme a pause? + if(A != 0) { + // If next phoneme is not a pause, continue processing phonemes + if((flags[A] & 128) == 0) { + pos++; + continue; + } + // If next phoneme is stressed, continue processing phonemes + // FIXME: How does a pause get stressed? + if(stress[X] != 0) { + pos++; + continue; + } + //pos41856: + // Set phonemes to DX + + phonemeindex[pos] = 30; // 'DX' + } else { + A = phonemeindex[X + 1]; + if(A == 255) //prevent buffer overflow + A = 65 & 128; + else + // Is next phoneme a vowel or ER? + A = flags[A] & 128; + + if(A != 0) phonemeindex[pos] = 30; // 'DX' + } + + pos++; + + } // while +} // parser 2 + +// Applies various rules that adjust the lengths of phonemes +// +// Lengthen or between and by 1.5 +// - decrease length by 1 +// - decrease vowel by 1/8th +// - increase vowel by 1/2 + 1 +// - set nasal = 5, consonant = 6 +// {optional silence} - shorten both to 1/2 + 1 +// - decrease by 2 + +//void Code48619() +void STM32SAM::AdjustLengths() { + // LENGTHEN VOWELS PRECEDING PUNCTUATION + // + // Search for punctuation. If found, back up to the first vowel, then + // process all phonemes between there and up to (but not including) the punctuation. + // If any phoneme is found that is a either a fricative or voiced, the duration is + // increased by (length * 1.5) + 1 + + // loop index + X = 0; + unsigned char index; + + // iterate through the phoneme list + unsigned char loopIndex = 0; + while(1) { + // get a phoneme + index = phonemeindex[X]; + + // exit loop if end on buffer token + if(index == 255) break; + + // not punctuation? + if((flags2[index] & 1) == 0) { + // skip + X++; + continue; + } + + // hold index + loopIndex = X; + + // Loop backwards from this point + pos48644: + + // back up one phoneme + X--; + + // stop once the beginning is reached + if(X == 0) break; + + // get the preceding phoneme + index = phonemeindex[X]; + + if(index != 255) //inserted to prevent access overrun + if((flags[index] & 128) == 0) goto pos48644; // if not a vowel, continue looping + + //pos48657: + do { + // test for vowel + index = phonemeindex[X]; + + if(index != 255) //inserted to prevent access overrun + // test for fricative/unvoiced or not voiced + if(((flags2[index] & 32) == 0) || ((flags[index] & 4) != 0)) //nochmal �berpr�fen + { + //A = flags[Y] & 4; + //if(A == 0) goto pos48688; + + // get the phoneme length + A = phonemeLength[X]; + + // change phoneme length to (length * 1.5) + 1 + A = (A >> 1) + A + 1; + + phonemeLength[X] = A; + } + // keep moving forward + X++; + } while(X != loopIndex); + // if (X != loopIndex) goto pos48657; + X++; + } // while + + // Similar to the above routine, but shorten vowels under some circumstances + + // Loop throught all phonemes + loopIndex = 0; + //pos48697 + + while(1) { + // get a phoneme + X = loopIndex; + index = phonemeindex[X]; + + // exit routine at end token + if(index == 255) return; + + // vowel? + A = flags[index] & 128; + if(A != 0) { + // get next phoneme + X++; + index = phonemeindex[X]; + + // get flags + if(index == 255) + mem56 = 65; // use if end marker + else + mem56 = flags[index]; + + // not a consonant + if((flags[index] & 64) == 0) { + // RX or LX? + if((index == 18) || (index == 19)) // 'RX' & 'LX' + { + // get the next phoneme + X++; + index = phonemeindex[X]; + + // next phoneme a consonant? + if((flags[index] & 64) != 0) { + // RULE: RX | LX + + // decrease length of vowel by 1 frame + phonemeLength[loopIndex]--; + } + // move ahead + loopIndex++; + continue; + } + // move ahead + loopIndex++; + continue; + } + + // Got here if not + + // not voiced + if((mem56 & 4) == 0) { + // Unvoiced + // *, .*, ?*, ,*, -*, DX, S*, SH, F*, TH, /H, /X, CH, P*, T*, K*, KX + + // not an unvoiced plosive? + if((mem56 & 1) == 0) { + // move ahead + loopIndex++; + continue; + } + + // P*, T*, K*, KX + + // RULE: + // + + // move back + X--; + + // decrease length by 1/8th + mem56 = phonemeLength[X] >> 3; + phonemeLength[X] -= mem56; + + // move ahead + loopIndex++; + continue; + } + + // RULE: + // + + // decrease length + A = phonemeLength[X - 1]; + phonemeLength[X - 1] = (A >> 2) + A + 1; // 5/4*A + 1 + + // move ahead + loopIndex++; + continue; + } + + // WH, R*, L*, W*, Y*, M*, N*, NX, Q*, Z*, ZH, V*, DH, J*, B*, D*, G*, GX + + //pos48821: + + // RULE: + // Set punctuation length to 6 + // Set stop consonant length to 5 + + // nasal? + if((flags2[index] & 8) != 0) { + // M*, N*, NX, + + // get the next phoneme + X++; + index = phonemeindex[X]; + + // end of buffer? + if(index == 255) + A = 65 & 2; //prevent buffer overflow + else + A = flags[index] & 2; // check for stop consonant + + // is next phoneme a stop consonant? + if(A != 0) + + // B*, D*, G*, GX, P*, T*, K*, KX + + { + // set stop consonant length to 6 + phonemeLength[X] = 6; + + // set nasal length to 5 + phonemeLength[X - 1] = 5; + } + // move to next phoneme + loopIndex++; + continue; + } + + // WH, R*, L*, W*, Y*, Q*, Z*, ZH, V*, DH, J*, B*, D*, G*, GX + + // RULE: {optional silence} + // Shorten both to (length/2 + 1) + + // (voiced) stop consonant? + if((flags[index] & 2) != 0) { + // B*, D*, G*, GX + + // move past silence + do { + // move ahead + X++; + index = phonemeindex[X]; + } while(index == 0); + + // check for end of buffer + if(index == 255) //buffer overflow + { + // ignore, overflow code + if((65 & 2) == 0) { + loopIndex++; + continue; + } + } else if((flags[index] & 2) == 0) { + // if another stop consonant, move ahead + loopIndex++; + continue; + } + + // RULE: {optional silence} + + // X gets overwritten, so hold prior X value for debug statement + // int debugX = X; + // shorten the prior phoneme length to (length/2 + 1) + phonemeLength[X] = (phonemeLength[X] >> 1) + 1; + X = loopIndex; + + // also shorten this phoneme length to (length/2 +1) + phonemeLength[loopIndex] = (phonemeLength[loopIndex] >> 1) + 1; + + // move ahead + loopIndex++; + continue; + } + + // WH, R*, L*, W*, Y*, Q*, Z*, ZH, V*, DH, J*, **, + + // RULE: + // Decrease by 2 + + // liquic consonant? + if((flags2[index] & 16) != 0) { + // R*, L*, W*, Y* + + // get the prior phoneme + index = phonemeindex[X - 1]; + + // prior phoneme a stop consonant> + if((flags[index] & 2) != 0) { + // Rule: + + // decrease the phoneme length by 2 frames (20 ms) + phonemeLength[X] -= 2; + } + } + + // move to next phoneme + loopIndex++; + continue; + } + // goto pos48701; +} + +// ------------------------------------------------------------------------- +// ML : Code47503 is division with remainder, and mem50 gets the sign +void STM32SAM::Code47503(unsigned char mem52) { + Y = 0; + if((mem53 & 128) != 0) { + mem53 = -mem53; + Y = 128; + } + mem50 = Y; + A = 0; + for(X = 8; X > 0; X--) { + int temp = mem53; + mem53 = mem53 << 1; + A = A << 1; + if(temp >= 128) A++; + if(A >= mem52) { + A = A - mem52; + mem53++; + } + } + + mem51 = A; + if((mem50 & 128) != 0) mem53 = -mem53; +} + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// Reciter +// +//////////////////////////////////////////////////////////////////////////////////////////// + +void STM32SAM::Code37055(unsigned char mem59) { + X = mem59; + X--; + A = inputtemp[X]; + Y = A; + A = tab36376[Y]; + return; +} + +void STM32SAM::Code37066(unsigned char mem58) { + X = mem58; + X++; + A = inputtemp[X]; + Y = A; + A = tab36376[Y]; +} + +unsigned char STM32SAM::GetRuleByte(unsigned short mem62, unsigned char Y) { + unsigned int address = mem62; + + if(mem62 >= 37541) { + address -= 37541; + return rules2[address + Y]; + } + address -= 32000; + return rules[address + Y]; +} + +int STM32SAM::TextToPhonemes(unsigned char* input) // Code36484 +{ + //unsigned char *tab39445 = &mem[39445]; //input and output + //unsigned char mem29; + unsigned char mem56; //output position for phonemes + unsigned char mem57; + unsigned char mem58; + unsigned char mem59; + unsigned char mem60; + unsigned char mem61; + unsigned short mem62; // memory position of current rule + + unsigned char mem64; // position of '=' or current character + unsigned char mem65; // position of ')' + unsigned char mem66; // position of '(' + unsigned char mem36653; + + inputtemp[0] = 32; + + // secure copy of input + // because input will be overwritten by phonemes + X = 1; + Y = 0; + do { + //pos36499: + A = input[Y] & 127; + if(A >= 112) + A = A & 95; + else if(A >= 96) + A = A & 79; + + inputtemp[X] = A; + X++; + Y++; + } while(Y != 255); + + X = 255; + inputtemp[X] = 27; + mem61 = 255; + +pos36550: + A = 255; + mem56 = 255; + +pos36554: + while(1) { + mem61++; + X = mem61; + A = inputtemp[X]; + mem64 = A; + if(A == '[') { + mem56++; + X = mem56; + A = 155; + input[X] = 155; + //goto pos36542; + // Code39771(); //Code39777(); + return 1; + } + + //pos36579: + if(A != '.') break; + X++; + Y = inputtemp[X]; + A = tab36376[Y] & 1; + if(A != 0) break; + mem56++; + X = mem56; + A = '.'; + input[X] = '.'; + } //while + + //pos36607: + A = mem64; + Y = A; + A = tab36376[A]; + mem57 = A; + if((A & 2) != 0) { + mem62 = 37541; + goto pos36700; + } + + //pos36630: + A = mem57; + if(A != 0) goto pos36677; + A = 32; + inputtemp[X] = ' '; + mem56++; + X = mem56; + if(X > 120) goto pos36654; + input[X] = A; + goto pos36554; + + // ----- + + //36653 is unknown. Contains position + +pos36654: + input[X] = 155; + A = mem61; + mem36653 = A; + // mem29 = A; // not used + // Code36538(); das ist eigentlich + return 1; + //Code39771(); + //go on if there is more input ??? + mem61 = mem36653; + goto pos36550; + +pos36677: + A = mem57 & 128; + if(A == 0) { + //36683: BRK + return 0; + } + + // go to the right rules for this character. + X = mem64 - 'A'; + mem62 = tab37489[X] | (tab37515[X] << 8); + + // ------------------------------------- + // go to next rule + // ------------------------------------- + +pos36700: + + // find next rule + Y = 0; + do { + mem62 += 1; + A = GetRuleByte(mem62, Y); + } while((A & 128) == 0); + Y++; + + //pos36720: + // find '(' + while(1) { + A = GetRuleByte(mem62, Y); + if(A == '(') break; + Y++; + } + mem66 = Y; + + //pos36732: + // find ')' + do { + Y++; + A = GetRuleByte(mem62, Y); + } while(A != ')'); + mem65 = Y; + + //pos36741: + // find '=' + do { + Y++; + A = GetRuleByte(mem62, Y); + A = A & 127; + } while(A != '='); + mem64 = Y; + + X = mem61; + mem60 = X; + + // compare the string within the bracket + Y = mem66; + Y++; + //pos36759: + while(1) { + mem57 = inputtemp[X]; + A = GetRuleByte(mem62, Y); + if(A != mem57) goto pos36700; + Y++; + if(Y == mem65) break; + X++; + mem60 = X; + } + + // the string in the bracket is correct + + //pos36787: + A = mem61; + mem59 = mem61; + +pos36791: + while(1) { + mem66--; + Y = mem66; + A = GetRuleByte(mem62, Y); + mem57 = A; + //36800: BPL 36805 + if((A & 128) != 0) goto pos37180; + X = A & 127; + A = tab36376[X] & 128; + if(A == 0) break; + X = mem59 - 1; + A = inputtemp[X]; + if(A != mem57) goto pos36700; + mem59 = X; + } + + //pos36833: + A = mem57; + if(A == ' ') goto pos36895; + if(A == '#') goto pos36910; + if(A == '.') goto pos36920; + if(A == '&') goto pos36935; + if(A == '@') goto pos36967; + if(A == '^') goto pos37004; + if(A == '+') goto pos37019; + if(A == ':') goto pos37040; + // Code42041(); //Error + //36894: BRK + return 0; + + // -------------- + +pos36895: + Code37055(mem59); + A = A & 128; + if(A != 0) goto pos36700; +pos36905: + mem59 = X; + goto pos36791; + + // -------------- + +pos36910: + Code37055(mem59); + A = A & 64; + if(A != 0) goto pos36905; + goto pos36700; + + // -------------- + +pos36920: + Code37055(mem59); + A = A & 8; + if(A == 0) goto pos36700; +pos36930: + mem59 = X; + goto pos36791; + + // -------------- + +pos36935: + Code37055(mem59); + A = A & 16; + if(A != 0) goto pos36930; + A = inputtemp[X]; + if(A != 72) goto pos36700; + X--; + A = inputtemp[X]; + if((A == 67) || (A == 83)) goto pos36930; + goto pos36700; + + // -------------- + +pos36967: + Code37055(mem59); + A = A & 4; + if(A != 0) goto pos36930; + A = inputtemp[X]; + if(A != 72) goto pos36700; + if((A != 84) && (A != 67) && (A != 83)) goto pos36700; + mem59 = X; + goto pos36791; + + // -------------- + +pos37004: + Code37055(mem59); + A = A & 32; + if(A == 0) goto pos36700; + +pos37014: + mem59 = X; + goto pos36791; + + // -------------- + +pos37019: + X = mem59; + X--; + A = inputtemp[X]; + if((A == 'E') || (A == 'I') || (A == 'Y')) goto pos37014; + goto pos36700; + // -------------- + +pos37040: + Code37055(mem59); + A = A & 32; + if(A == 0) goto pos36791; + mem59 = X; + goto pos37040; + + //--------------------------------------- + +pos37077: + X = mem58 + 1; + A = inputtemp[X]; + if(A != 'E') goto pos37157; + X++; + Y = inputtemp[X]; + X--; + A = tab36376[Y] & 128; + if(A == 0) goto pos37108; + X++; + A = inputtemp[X]; + if(A != 'R') goto pos37113; +pos37108: + mem58 = X; + goto pos37184; +pos37113: + if((A == 83) || (A == 68)) goto pos37108; // 'S' 'D' + if(A != 76) goto pos37135; // 'L' + X++; + A = inputtemp[X]; + if(A != 89) goto pos36700; + goto pos37108; + +pos37135: + if(A != 70) goto pos36700; + X++; + A = inputtemp[X]; + if(A != 85) goto pos36700; + X++; + A = inputtemp[X]; + if(A == 76) goto pos37108; + goto pos36700; + +pos37157: + if(A != 73) goto pos36700; + X++; + A = inputtemp[X]; + if(A != 78) goto pos36700; + X++; + A = inputtemp[X]; + if(A == 71) goto pos37108; + //pos37177: + goto pos36700; + + // ----------------------------------------- + +pos37180: + + A = mem60; + mem58 = A; + +pos37184: + Y = mem65 + 1; + + //37187: CPY 64 + // if(? != 0) goto pos37194; + if(Y == mem64) goto pos37455; + mem65 = Y; + //37196: LDA (62),y + A = GetRuleByte(mem62, Y); + mem57 = A; + X = A; + A = tab36376[X] & 128; + if(A == 0) goto pos37226; + X = mem58 + 1; + A = inputtemp[X]; + if(A != mem57) goto pos36700; + mem58 = X; + goto pos37184; +pos37226: + A = mem57; + if(A == 32) goto pos37295; // ' ' + if(A == 35) goto pos37310; // '#' + if(A == 46) goto pos37320; // '.' + if(A == 38) goto pos37335; // '&' + if(A == 64) goto pos37367; // '' + if(A == 94) goto pos37404; // '' + if(A == 43) goto pos37419; // '+' + if(A == 58) goto pos37440; // ':' + if(A == 37) goto pos37077; // '%' + //pos37291: + // Code42041(); //Error + //37294: BRK + return 0; + + // -------------- +pos37295: + Code37066(mem58); + A = A & 128; + if(A != 0) goto pos36700; +pos37305: + mem58 = X; + goto pos37184; + + // -------------- + +pos37310: + Code37066(mem58); + A = A & 64; + if(A != 0) goto pos37305; + goto pos36700; + + // -------------- + +pos37320: + Code37066(mem58); + A = A & 8; + if(A == 0) goto pos36700; + +pos37330: + mem58 = X; + goto pos37184; + + // -------------- + +pos37335: + Code37066(mem58); + A = A & 16; + if(A != 0) goto pos37330; + A = inputtemp[X]; + if(A != 72) goto pos36700; + X++; + A = inputtemp[X]; + if((A == 67) || (A == 83)) goto pos37330; + goto pos36700; + + // -------------- + +pos37367: + Code37066(mem58); + A = A & 4; + if(A != 0) goto pos37330; + A = inputtemp[X]; + if(A != 72) goto pos36700; + if((A != 84) && (A != 67) && (A != 83)) goto pos36700; + mem58 = X; + goto pos37184; + + // -------------- + +pos37404: + Code37066(mem58); + A = A & 32; + if(A == 0) goto pos36700; +pos37414: + mem58 = X; + goto pos37184; + + // -------------- + +pos37419: + X = mem58; + X++; + A = inputtemp[X]; + if((A == 69) || (A == 73) || (A == 89)) goto pos37414; + goto pos36700; + + // ---------------------- + +pos37440: + + Code37066(mem58); + A = A & 32; + if(A == 0) goto pos37184; + mem58 = X; + goto pos37440; +pos37455: + Y = mem64; + mem61 = mem60; + +pos37461: + //37461: LDA (62),y + A = GetRuleByte(mem62, Y); + mem57 = A; + A = A & 127; + if(A != '=') { + mem56++; + X = mem56; + input[X] = A; + } + + //37478: BIT 57 + //37480: BPL 37485 //not negative flag + if((mem57 & 128) == 0) goto pos37485; //??? + goto pos36554; +pos37485: + Y++; + goto pos37461; +} + +// Constructor + +STM32SAM::STM32SAM(uint32_t STM32SAM_SPEED /* = 5 */) { + STM32SAM_SPEED = STM32SAM_SPEED & 0x1f; // limit it from 0 to 31 + + _STM32SAM_SPEED = STM32SAM_SPEED; + + // set default voice + + speed = 72; + pitch = 64; + mouth = 128; + throat = 128; + + phonetic = 0; + singmode = 0; + + wait1 = 7; + wait2 = 6; + + mem59 = 0; + + oldtimetableindex = 0; +} + +STM32SAM::STM32SAM() { + _STM32SAM_SPEED = 7; + + // set default voice + + speed = 72; + pitch = 64; + mouth = 128; + throat = 128; + + phonetic = 0; + singmode = 0; + + wait1 = 7; + wait2 = 6; + + mem59 = 0; + + oldtimetableindex = 0; +} + +/* + STM32SAM::~STM32SAM() { + { + // TODO: end(); + } +*/ + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// STM32SAM sam (variable string, phonetic, sing, pitch, speed, mouth, throat) +// STM32SAM say (sing off, phonetic off) (const string) +// STM32SAM say (sing off, phonetic off) (variable string) +// STM32SAM sing (sing on, phonetic off) (const string) +// STM32SAM sing (sing on, phonetic off) (variable string) +// STM32SAM sayPhonetic (sing off, phonetic on) (const string) +// STM32SAM sayPhonetic (sing off, phonetic on) (variable string) +// STM32SAM singPhonetic (sing on, phonetic on) (const string) +// STM32SAM singPhonetic (sing on, phonetic on) (variable string) +// STM32SAM voice (pitch, speed, mouth, throat) +// STM32SAM setPitch (pitch) +// STM32SAM setSpeed (speed) +// STM32SAM setMouth (mouth) +// STM32SAM setThroat (throat) +// +// +//////////////////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// STM32SAM sam (const string, phonetic, sing, pitch, speed, mouth, throat) +// +//////////////////////////////////////////////////////////////////////////////////////////// + +char to_upper_case(char c) { + if(c >= 'a' && c <= 'z') { + return c - 'a' + 'A'; + } + return c; +} + +void STM32SAM::sam( + const char* argv, + unsigned char _phonetic, + unsigned char _singmode, + unsigned char _pitch, + unsigned char _speed, + unsigned char _mouth, + unsigned char _throat) { + phonetic = _phonetic; + singmode = _singmode; + pitch = _pitch; + speed = _speed; + mouth = _mouth; + throat = _throat; + + int i; + + for(i = 0; i < 256; i++) { + input[i] = argv[i]; + } + + for(i = 0; input[i] != 0; i++) { + if(i != 0) { + input[i] = to_upper_case((int)argv[i]); + } + } + + if(!phonetic) { + strncat(input, "[", 256); + if(!TextToPhonemes((unsigned char*)input)) { + // PrintUsage(); + return; + } + + } else { + strncat(input, "\x9b", 256); + } + + SetInput(input); + + if(!SAMMain()) { + return; + } +} + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// STM32SAM sam (variable string, phonetic, sing, pitch, speed, mouth, throat) +// +//////////////////////////////////////////////////////////////////////////////////////////// + +void STM32SAM::sam( + char* argv, + unsigned char _phonetic, + unsigned char _singmode, + unsigned char _pitch, + unsigned char _speed, + unsigned char _mouth, + unsigned char _throat) { + phonetic = _phonetic; + singmode = _singmode; + pitch = _pitch; + speed = _speed; + mouth = _mouth; + throat = _throat; + + int i; + + for(i = 0; i < 256; i++) { + input[i] = argv[i]; + } + + for(i = 0; input[i] != 0; i++) { + if(i != 0) { + input[i] = to_upper_case((int)argv[i]); + } + } + + if(!phonetic) { + strncat(input, "[", 256); + if(!TextToPhonemes((unsigned char*)input)) { + // PrintUsage(); + return; + } + + } else { + strncat(input, "\x9b", 256); + } + + SetInput(input); + + if(!SAMMain()) { + return; + } +} + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// STM32SAM say(sing off, phonetic off) (const string) +// +//////////////////////////////////////////////////////////////////////////////////////////// + +void STM32SAM::say(const char* argv) { + int i; + + phonetic = 0; + singmode = 0; + + char const_input[256]; + + for(i = 0; i < 256; i++) { + const_input[i] = argv[i]; + } + + sam(const_input, phonetic, singmode, pitch, speed, mouth, throat); +} + +void STM32SAM::say(char* argv) { + int i; + + phonetic = 0; + singmode = 0; + + char const_input[256]; + + for(i = 0; i < 256; i++) { + const_input[i] = argv[i]; + } + + sam(const_input, phonetic, singmode, pitch, speed, mouth, throat); +} + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// STM32SAM sing (sing on, phonetic off) +// +//////////////////////////////////////////////////////////////////////////////////////////// + +void STM32SAM::sing(const char* argv) { + int i; + + phonetic = 0; + singmode = 1; + + char const_input[256]; + + for(i = 0; i < 256; i++) { + const_input[i] = argv[i]; + } + + sam(const_input, phonetic, singmode, pitch, speed, mouth, throat); +} + +void STM32SAM::sing(char* argv) { + int i; + + phonetic = 0; + singmode = 1; + + char const_input[256]; + + for(i = 0; i < 256; i++) { + const_input[i] = argv[i]; + } + + sam(const_input, phonetic, singmode, pitch, speed, mouth, throat); +} + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// STM32SAM sayPhonetic (sing off, phonetic on) +// +//////////////////////////////////////////////////////////////////////////////////////////// + +void STM32SAM::sayPhonetic(const char* argv) { + int i; + + phonetic = 1; + singmode = 0; + + char const_input[256]; + + for(i = 0; i < 256; i++) { + const_input[i] = argv[i]; + } + + sam(const_input, phonetic, singmode, pitch, speed, mouth, throat); +} + +void STM32SAM::sayPhonetic(char* argv) { + int i; + + phonetic = 1; + singmode = 0; + + char const_input[256]; + + for(i = 0; i < 256; i++) { + const_input[i] = argv[i]; + } + + sam(const_input, phonetic, singmode, pitch, speed, mouth, throat); +} + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// STM32SAM singPhonetic (sing on, phonetic on) +// +//////////////////////////////////////////////////////////////////////////////////////////// + +void STM32SAM::singPhonetic(const char* argv) { + int i; + + phonetic = 1; + singmode = 1; + + char const_input[256]; + + for(i = 0; i < 256; i++) { + const_input[i] = argv[i]; + } + + sam(const_input, phonetic, singmode, pitch, speed, mouth, throat); +} + +void STM32SAM::singPhonetic(char* argv) { + int i; + + phonetic = 1; + singmode = 0; + + char const_input[256]; + + for(i = 0; i < 256; i++) { + const_input[i] = argv[i]; + } + + sam(const_input, phonetic, singmode, pitch, speed, mouth, throat); +} + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// STM32SAM voice (pitch, speed, mouth, throat) +// +//////////////////////////////////////////////////////////////////////////////////////////// + +void STM32SAM::setVoice( + unsigned char _pitch /* = 64 */, + unsigned char _speed /* = 72 */, + unsigned char _mouth /* = 128 */, + unsigned char _throat /* = 128 */) { + pitch = _pitch; + speed = _speed; + mouth = _mouth; + throat = _throat; +} + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// STM32SAM setPitch (pitch) +// +//////////////////////////////////////////////////////////////////////////////////////////// + +void STM32SAM::setPitch(unsigned char _pitch /* = 64 */) { + pitch = _pitch; +} +//////////////////////////////////////////////////////////////////////////////////////////// +// +// STM32SAM setSpeed (speed) +// +//////////////////////////////////////////////////////////////////////////////////////////// + +void STM32SAM::setSpeed(unsigned char _speed /* = 72 */) { + speed = _speed; +} +//////////////////////////////////////////////////////////////////////////////////////////// +// +// STM32SAM setMouth (mouth) +// +//////////////////////////////////////////////////////////////////////////////////////////// + +void STM32SAM::setMouth(unsigned char _mouth /* = 128 */) { + mouth = _mouth; +} + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// STM32SAM setThroat (throat) +// +//////////////////////////////////////////////////////////////////////////////////////////// + +void STM32SAM::setThroat(unsigned char _throat /* = 128 */) { + throat = _throat; +} +//////////////////////////////////////////////////////////////////////////////////////////// +// +// Hardware +// +//////////////////////////////////////////////////////////////////////////////////////////// +// Hardware specifics, for easier porting to other microcontrollers + +// +// Set PA8 pin as PWM, at 256 timer ticks overflow (8bit resolution) + +#include +#include + +#define FURI_HAL_SPEAKER_TIMER TIM16 +#define FURI_HAL_SPEAKER_CHANNEL LL_TIM_CHANNEL_CH1 + +void STM32SAM::begin(void) { +#ifdef USE_ROGER_CORE + + pinMode(PA8, PWM); // audio output pin + + Timer1.setPeriod( + 4); // Can't set at 256 ticks, only in uS. First nearest uS is 4 (Roger core is only for bluepill, that means 72*4=288 ticks, or 128*4=512 ticks when overclocked. It's ok, just overall volume will be lower, because maximum volume will be 256/288 or 256/512) + +#endif + +#ifdef USE_STM32duino_CORE + pinMode(PA8, OUTPUT); + + PWM->pause(); + PWM->setMode(1, TIMER_OUTPUT_COMPARE_PWM1, PA8); // TIM1 CH1 (PA8) + PWM->setPrescaleFactor(1); + PWM->setOverflow(256, TICK_FORMAT); // 256 ticks overflow, no matter the CPU (timer) speed + PWM->resume(); + +#endif + + LL_TIM_InitTypeDef TIM_InitStruct; + memset(&TIM_InitStruct, 0, sizeof(LL_TIM_InitTypeDef)); + TIM_InitStruct.Prescaler = 4; + TIM_InitStruct.Autoreload = 255; + LL_TIM_Init(FURI_HAL_SPEAKER_TIMER, &TIM_InitStruct); + + LL_TIM_OC_InitTypeDef TIM_OC_InitStruct; + memset(&TIM_OC_InitStruct, 0, sizeof(LL_TIM_OC_InitTypeDef)); + TIM_OC_InitStruct.OCMode = LL_TIM_OCMODE_PWM1; + TIM_OC_InitStruct.OCState = LL_TIM_OCSTATE_ENABLE; + TIM_OC_InitStruct.CompareValue = 127; + LL_TIM_OC_Init(FURI_HAL_SPEAKER_TIMER, FURI_HAL_SPEAKER_CHANNEL, &TIM_OC_InitStruct); + + LL_TIM_EnableAllOutputs(FURI_HAL_SPEAKER_TIMER); + LL_TIM_EnableCounter(FURI_HAL_SPEAKER_TIMER); +} // begin + +inline void STM32SAM::SetAUDIO(unsigned char main_volume) { +#ifdef USE_ROGER_CORE + Timer1.setCompare(TIMER_CH1, main_volume); +#endif + +#ifdef USE_STM32duino_CORE + PWM->setCaptureCompare(1, main_volume, TICK_COMPARE_FORMAT); +#endif + + // if(main_volume > 64) { + // LL_TIM_OC_SetCompareCH1(FURI_HAL_SPEAKER_TIMER, 127); + // } else { + // LL_TIM_OC_SetCompareCH1(FURI_HAL_SPEAKER_TIMER, main_volume); + // } + + float data = main_volume; + data /= 255.0f; + data -= 0.5f; + data *= 4.0f; + data = tanhf(data); + + data += 0.5f; + data *= 255.0f; + + if(data < 0) { + data = 0; + } else if(data > 255) { + data = 255; + } + + LL_TIM_OC_SetCompareCH1(FURI_HAL_SPEAKER_TIMER, data); +} \ No newline at end of file diff --git a/applications/external/text2sam/stm32_sam.h b/applications/external/text2sam/stm32_sam.h new file mode 100644 index 0000000000..910227ac36 --- /dev/null +++ b/applications/external/text2sam/stm32_sam.h @@ -0,0 +1,96 @@ +#include + +#ifndef __STM32SAM__ +#define __STM32SAM__ + +// SAM Text-To-Speech (TTS), ported from https://github.com/s-macke/SAM + +class STM32SAM { +public: + STM32SAM(uint32_t STM32SAM_SPEED); + STM32SAM(); + + void begin(void); + + void + sam(const char* argv, + unsigned char phonetic, + unsigned char singmode, + unsigned char pitch, + unsigned char speed, + unsigned char mouth, + unsigned char throat); + void + sam(char* argv, + unsigned char phonetic, + unsigned char singmode, + unsigned char pitch, + unsigned char speed, + unsigned char mouth, + unsigned char throat); + + void say(const char* argv); + void say(char* argv); + void sing(const char* argv); + void sing(char* argv); + void sayPhonetic(const char* argv); + void sayPhonetic(char* argv); + void singPhonetic(const char* argv); + void singPhonetic(char* argv); + void setVoice( + unsigned char _pitch = 64, + unsigned char _speed = 72, + unsigned char _mouth = 128, + unsigned char _throat = 128); + void setPitch(unsigned char _pitch = 64); + void setSpeed(unsigned char _speed = 72); + void setMouth(unsigned char _mouth = 128); + void setThroat(unsigned char _throat = 128); + +private: + void SetAUDIO(unsigned char main_volume); + + void Output8BitAry(int index, unsigned char ary[5]); + void Output8Bit(int index, unsigned char A); + unsigned char Read(unsigned char p, unsigned char Y); + void Write(unsigned char p, unsigned char Y, unsigned char value); + void RenderSample(unsigned char* mem66); + void Render(); + void AddInflection(unsigned char mem48, unsigned char phase1); + void SetMouthThroat(); + unsigned char trans(unsigned char mem39212, unsigned char mem39213); + void SetInput(char* _input); + void Init(); + int SAMMain(); + void PrepareOutput(); + void Insert( + unsigned char position /*var57*/, + unsigned char mem60, + unsigned char mem59, + unsigned char mem58); + void InsertBreath(); + void CopyStress(); + int Parser1(); + void SetPhonemeLength(); + void Code41240(); + void Parser2(); + void AdjustLengths(); + void Code47503(unsigned char mem52); + void Code37055(unsigned char mem59); + void Code37066(unsigned char mem58); + unsigned char GetRuleByte(unsigned short mem62, unsigned char Y); + int TextToPhonemes(unsigned char* input); // Code36484 + + uint32_t _STM32SAM_SPEED; + + unsigned char speed; + unsigned char pitch; + unsigned char mouth; + unsigned char throat; + + unsigned char phonetic; + unsigned char singmode; + +}; // STM32SAM class + +#endif \ No newline at end of file diff --git a/applications/external/text_viewer/application.fam b/applications/external/text_viewer/application.fam index 518626f41c..e36b7a8700 100644 --- a/applications/external/text_viewer/application.fam +++ b/applications/external/text_viewer/application.fam @@ -12,4 +12,7 @@ App( fap_icon="icons/text_10px.png", fap_category="Misc", fap_icon_assets="icons", + fap_author="@kowalski7cc & @kyhwana", + fap_version="1.0", + fap_description="Text viewer application", ) diff --git a/applications/external/text_viewer/text_viewer.c b/applications/external/text_viewer/text_viewer.c index b5ccb6ef36..d1ac686cef 100644 --- a/applications/external/text_viewer/text_viewer.c +++ b/applications/external/text_viewer/text_viewer.c @@ -13,7 +13,7 @@ #define TAG "TextViewer" -#define TEXT_VIEWER_APP_PATH_FOLDER ANY_PATH("") +#define TEXT_VIEWER_APP_PATH_FOLDER STORAGE_EXT_PATH_PREFIX #define TEXT_VIEWER_APP_EXTENSION "*" #define TEXT_VIEWER_BYTES_PER_LINE 20u @@ -282,4 +282,4 @@ int32_t text_viewer_app(void* p) { text_viewer_free(text_viewer); return 0; -} \ No newline at end of file +} diff --git a/applications/external/tictactoe_game/application.fam b/applications/external/tictactoe_game/application.fam index 2f19647574..3def1fa854 100644 --- a/applications/external/tictactoe_game/application.fam +++ b/applications/external/tictactoe_game/application.fam @@ -8,4 +8,7 @@ App( order=250, fap_icon="tictactoe_10px.png", fap_category="Games", + fap_author="@xMasterX & @gotnull", + fap_version="1.0", + fap_description="Tic Tac Toe game, for 2 players, play on one device", ) diff --git a/applications/external/tictactoe_game/tictactoe_game.c b/applications/external/tictactoe_game/tictactoe_game.c index b6c0747d60..5113fcf847 100644 --- a/applications/external/tictactoe_game/tictactoe_game.c +++ b/applications/external/tictactoe_game/tictactoe_game.c @@ -331,6 +331,9 @@ int32_t tictactoe_game_app(void* p) { Gui* gui = furi_record_open(RECORD_GUI); gui_add_view_port(gui, view_port, GuiLayerFullscreen); + // Call dolphin deed on game start + // dolphin_deed(DolphinDeedPluginGameStart); + GameEvent event; for(bool processing = true; processing;) { FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); diff --git a/applications/external/timelapse/application.fam b/applications/external/timelapse/application.fam index ba5babc7e7..a6dc6ad33f 100644 --- a/applications/external/timelapse/application.fam +++ b/applications/external/timelapse/application.fam @@ -1,5 +1,5 @@ App( - appid="GPIO_Timelapse", + appid="timelapse", name="[GPIO] Timelapse", apptype=FlipperAppType.EXTERNAL, entry_point="zeitraffer_app", @@ -10,7 +10,8 @@ App( fap_icon_assets="icons", fap_icon="zeitraffer.png", fap_category="GPIO", - fap_description="Simple intervalometer app", + fap_version="1.0", + fap_description="Simple intervalometer app, works via GPIO pins.", fap_author="Aurelius Rosenbaum", fap_weburl="https://github.com/theageoflove/flipperzero-zeitraffer", ) diff --git a/applications/external/timelapse/icons/ButtonDown_7x4.png b/applications/external/timelapse/icons/ButtonDown_7x4.png deleted file mode 100644 index 2954bb6a67..0000000000 Binary files a/applications/external/timelapse/icons/ButtonDown_7x4.png and /dev/null differ diff --git a/applications/external/timelapse/icons/ButtonLeft_4x7.png b/applications/external/timelapse/icons/ButtonLeft_4x7.png deleted file mode 100644 index 0b4655d432..0000000000 Binary files a/applications/external/timelapse/icons/ButtonLeft_4x7.png and /dev/null differ diff --git a/applications/external/timelapse/icons/ButtonRight_4x7.png b/applications/external/timelapse/icons/ButtonRight_4x7.png deleted file mode 100644 index 8e1c74c1c0..0000000000 Binary files a/applications/external/timelapse/icons/ButtonRight_4x7.png and /dev/null differ diff --git a/applications/external/timelapse/icons/ButtonUp_7x4.png b/applications/external/timelapse/icons/ButtonUp_7x4.png deleted file mode 100644 index 1be79328b4..0000000000 Binary files a/applications/external/timelapse/icons/ButtonUp_7x4.png and /dev/null differ diff --git a/applications/external/timelapse/icons/Pin_star_7x7.png b/applications/external/timelapse/icons/Pin_star_7x7.png deleted file mode 100644 index 42fdea86e4..0000000000 Binary files a/applications/external/timelapse/icons/Pin_star_7x7.png and /dev/null differ diff --git a/applications/external/timelapse/icons/loading_10px.png b/applications/external/timelapse/icons/loading_10px.png deleted file mode 100644 index 4f626b3d58..0000000000 Binary files a/applications/external/timelapse/icons/loading_10px.png and /dev/null differ diff --git a/applications/external/timelapse/zeitraffer.c b/applications/external/timelapse/zeitraffer.c index 7a300fdcf0..4ffdba5f2d 100644 --- a/applications/external/timelapse/zeitraffer.c +++ b/applications/external/timelapse/zeitraffer.c @@ -5,10 +5,10 @@ #include #include #include "gpio_item.h" -#include "GPIO_Timelapse_icons.h" +#include "timelapse_icons.h" +#include -#define CONFIG_FILE_DIRECTORY_PATH "/ext/apps_data/timelapse" -#define CONFIG_FILE_PATH CONFIG_FILE_DIRECTORY_PATH "/timelapse.conf" +#define CONFIG_FILE_PATH APP_DATA_PATH("timelapse.conf") // Часть кода покрадена из https://github.com/zmactep/flipperzero-hello-world @@ -153,10 +153,6 @@ int32_t zeitraffer_app(void* p) { FlipperFormat* load = flipper_format_file_alloc(storage); do { - if(!storage_simply_mkdir(storage, CONFIG_FILE_DIRECTORY_PATH)) { - notification_message(notifications, &sequence_error); - break; - } if(!flipper_format_file_open_existing(load, CONFIG_FILE_PATH)) { notification_message(notifications, &sequence_error); break; diff --git a/applications/external/totp/features_config.h b/applications/external/totp/features_config.h index 15e81b28a2..59a1e0ecc5 100644 --- a/applications/external/totp/features_config.h +++ b/applications/external/totp/features_config.h @@ -1,3 +1,8 @@ +// Application automatic lock timeout if user IDLE. (ticks) +#ifndef TOTP_AUTO_LOCK_IDLE_TIMEOUT_SEC +#define TOTP_AUTO_LOCK_IDLE_TIMEOUT_SEC (60) +#endif + // Include Bluetooth token input automation #define TOTP_BADBT_TYPE_ENABLED diff --git a/applications/external/totp/images/DolphinCommon_56x48.png b/applications/external/totp/images/DolphinCommon_56x48.png deleted file mode 100644 index 089aaed835..0000000000 Binary files a/applications/external/totp/images/DolphinCommon_56x48.png and /dev/null differ diff --git a/applications/external/totp/services/crypto/crypto.c b/applications/external/totp/services/crypto/crypto.c index 03d9c9d518..8c20fe7859 100644 --- a/applications/external/totp/services/crypto/crypto.c +++ b/applications/external/totp/services/crypto/crypto.c @@ -6,10 +6,11 @@ #include "memset_s.h" #define CRYPTO_KEY_SLOT (2) -#define CRYPTO_VERIFY_KEY "FFF_Crypto_pass" #define CRYPTO_VERIFY_KEY_LENGTH (16) #define CRYPTO_ALIGNMENT_FACTOR (16) +static const char* CRYPTO_VERIFY_KEY = "FFF_Crypto_pass"; + uint8_t* totp_crypto_encrypt( const uint8_t* plain_data, const size_t plain_data_length, @@ -89,8 +90,11 @@ CryptoSeedIVResult } else { max_i = uid_size; } - +#if TOTP_TARGET_FIRMWARE == TOTP_FIRMWARE_UL_XFW + const uint8_t* uid = furi_hal_version_uid_default(); +#else const uint8_t* uid = furi_hal_version_uid(); +#endif for(uint8_t i = 0; i < max_i; i++) { plugin_state->iv[i] = plugin_state->iv[i] ^ uid[i]; } @@ -104,7 +108,7 @@ CryptoSeedIVResult plugin_state->crypto_verify_data_length = CRYPTO_VERIFY_KEY_LENGTH; plugin_state->crypto_verify_data = totp_crypto_encrypt( - (uint8_t*)CRYPTO_VERIFY_KEY, + (const uint8_t*)CRYPTO_VERIFY_KEY, CRYPTO_VERIFY_KEY_LENGTH, &plugin_state->iv[0], &plugin_state->crypto_verify_data_length); @@ -119,7 +123,7 @@ CryptoSeedIVResult bool totp_crypto_verify_key(const PluginState* plugin_state) { size_t decrypted_key_length; - const uint8_t* decrypted_key = totp_crypto_decrypt( + uint8_t* decrypted_key = totp_crypto_decrypt( plugin_state->crypto_verify_data, plugin_state->crypto_verify_data_length, &plugin_state->iv[0], @@ -130,5 +134,7 @@ bool totp_crypto_verify_key(const PluginState* plugin_state) { if(decrypted_key[i] != CRYPTO_VERIFY_KEY[i]) key_valid = false; } + free(decrypted_key); + return key_valid; -} \ No newline at end of file +} diff --git a/applications/external/totp/services/idle_timeout/idle_timeout.c b/applications/external/totp/services/idle_timeout/idle_timeout.c new file mode 100644 index 0000000000..9df1b5487e --- /dev/null +++ b/applications/external/totp/services/idle_timeout/idle_timeout.c @@ -0,0 +1,66 @@ +#include "idle_timeout.h" +#include +#include + +#define IDLE_TIMER_CHECK_PERIODICITY_SEC (1) +#define SEC_TO_TICKS(sec) ((sec)*1000) + +struct IdleTimeoutContext { + FuriTimer* timer; + bool activity_reported; + void* on_idle_callback_context; + IDLE_TIMEOUT_CALLBACK on_idle_callback; + uint16_t timeout_sec; + uint16_t idle_period_sec; + bool idle_handled; +}; + +static void idle_timer_callback(void* context) { + IdleTimeoutContext* instance = context; + if(instance->activity_reported) { + instance->idle_period_sec = 0; + instance->idle_handled = false; + instance->activity_reported = false; + } else if(!instance->idle_handled) { + if(instance->idle_period_sec >= instance->timeout_sec) { + instance->idle_handled = + instance->on_idle_callback(instance->on_idle_callback_context); + } else { + instance->idle_period_sec += IDLE_TIMER_CHECK_PERIODICITY_SEC; + } + } +} + +IdleTimeoutContext* idle_timeout_alloc( + uint16_t timeout_sec, + IDLE_TIMEOUT_CALLBACK on_idle_callback, + void* on_idle_callback_context) { + IdleTimeoutContext* instance = malloc(sizeof(IdleTimeoutContext)); + if(instance == NULL) return NULL; + + instance->timer = furi_timer_alloc(&idle_timer_callback, FuriTimerTypePeriodic, instance); + if(instance->timer == NULL) return NULL; + + instance->timeout_sec = timeout_sec; + instance->on_idle_callback = on_idle_callback; + instance->on_idle_callback_context = on_idle_callback_context; + return instance; +} + +void idle_timeout_start(IdleTimeoutContext* context) { + furi_timer_start(context->timer, SEC_TO_TICKS(IDLE_TIMER_CHECK_PERIODICITY_SEC)); +} + +void idle_timeout_stop(IdleTimeoutContext* context) { + furi_timer_stop(context->timer); +} + +void idle_timeout_report_activity(IdleTimeoutContext* context) { + context->activity_reported = true; +} + +void idle_timeout_free(IdleTimeoutContext* context) { + furi_timer_stop(context->timer); + furi_timer_free(context->timer); + free(context); +} diff --git a/applications/external/totp/services/idle_timeout/idle_timeout.h b/applications/external/totp/services/idle_timeout/idle_timeout.h new file mode 100644 index 0000000000..12825e4543 --- /dev/null +++ b/applications/external/totp/services/idle_timeout/idle_timeout.h @@ -0,0 +1,44 @@ +#pragma once + +#include +#include + +typedef struct IdleTimeoutContext IdleTimeoutContext; + +typedef bool (*IDLE_TIMEOUT_CALLBACK)(void* context); + +/** + * @brief Initializes a new instance of IDLE timeout + * @param timeout_sec IDLE timeout in seconds + * @param on_idle_callback callback function to trigger when IDLE timeout happened + * @param on_idle_callback_context callback function context + * @return IDLE timeout context + */ +IdleTimeoutContext* idle_timeout_alloc( + uint16_t timeout_sec, + IDLE_TIMEOUT_CALLBACK on_idle_callback, + void* on_idle_callback_context); + +/** + * @brief Starts IDLE timeout + * @param context IDLE timeout context + */ +void idle_timeout_start(IdleTimeoutContext* context); + +/** + * @brief Stops IDLE timeout + * @param context IDLE timeout context + */ +void idle_timeout_stop(IdleTimeoutContext* context); + +/** + * @brief Reports activity to IDLE timeout + * @param context IDLE timeout context + */ +void idle_timeout_report_activity(IdleTimeoutContext* context); + +/** + * @brief Disposes IDLE timeout and releases all the resources + * @param context IDLE timeout context + */ +void idle_timeout_free(IdleTimeoutContext* context); diff --git a/applications/external/totp/totp_app.c b/applications/external/totp/totp_app.c index 74217d6087..c87fdd4966 100644 --- a/applications/external/totp/totp_app.c +++ b/applications/external/totp/totp_app.c @@ -17,8 +17,6 @@ #include "services/crypto/crypto.h" #include "cli/cli.h" -#define IDLE_TIMEOUT (60000) - static void render_callback(Canvas* const canvas, void* ctx) { furi_assert(ctx); PluginState* plugin_state = ctx; @@ -105,6 +103,17 @@ static bool totp_activate_initial_scene(PluginState* const plugin_state) { return true; } +static bool on_user_idle(void* context) { + PluginState* plugin_state = context; + if(plugin_state->current_scene != TotpSceneAuthentication && + plugin_state->current_scene != TotpSceneStandby) { + totp_scene_director_activate_scene(plugin_state, TotpSceneAuthentication); + return true; + } + + return false; +} + static bool totp_plugin_state_init(PluginState* const plugin_state) { plugin_state->selected_font = 0; plugin_state->gui = furi_record_open(RECORD_GUI); @@ -127,10 +136,23 @@ static bool totp_plugin_state_init(PluginState* const plugin_state) { } #endif + if(plugin_state->pin_set) { + plugin_state->idle_timeout_context = + idle_timeout_alloc(TOTP_AUTO_LOCK_IDLE_TIMEOUT_SEC, &on_user_idle, plugin_state); + idle_timeout_start(plugin_state->idle_timeout_context); + } else { + plugin_state->idle_timeout_context = NULL; + } + return true; } static void totp_plugin_state_free(PluginState* plugin_state) { + if(plugin_state->idle_timeout_context != NULL) { + idle_timeout_stop(plugin_state->idle_timeout_context); + idle_timeout_free(plugin_state->idle_timeout_context); + } + furi_record_close(RECORD_GUI); furi_record_close(RECORD_NOTIFICATION); furi_record_close(RECORD_DIALOGS); @@ -171,6 +193,9 @@ int32_t totp_app() { return 253; } + // Affecting dolphin level + // dolphin_deed(DolphinDeedPluginStart); + // Set system callbacks ViewPort* view_port = view_port_alloc(); view_port_draw_callback_set(view_port, render_callback, plugin_state); @@ -181,14 +206,13 @@ int32_t totp_app() { PluginEvent event; bool processing = true; - uint32_t last_user_interaction_time = furi_get_tick(); while(processing) { - FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); + FuriStatus event_status = furi_message_queue_get(event_queue, &event, FuriWaitForever); if(furi_mutex_acquire(plugin_state->mutex, FuriWaitForever) == FuriStatusOk) { if(event_status == FuriStatusOk) { - if(event.type == EventTypeKey) { - last_user_interaction_time = furi_get_tick(); + if(event.type == EventTypeKey && plugin_state->idle_timeout_context != NULL) { + idle_timeout_report_activity(plugin_state->idle_timeout_context); } if(event.type == EventForceCloseApp) { @@ -196,11 +220,6 @@ int32_t totp_app() { } else { processing = totp_scene_director_handle_event(&event, plugin_state); } - } else if( - plugin_state->pin_set && plugin_state->current_scene != TotpSceneAuthentication && - plugin_state->current_scene != TotpSceneStandby && - furi_get_tick() - last_user_interaction_time > IDLE_TIMEOUT) { - totp_scene_director_activate_scene(plugin_state, TotpSceneAuthentication); } view_port_update(view_port); diff --git a/applications/external/totp/types/plugin_state.h b/applications/external/totp/types/plugin_state.h index 97d126330b..234894d581 100644 --- a/applications/external/totp/types/plugin_state.h +++ b/applications/external/totp/types/plugin_state.h @@ -6,6 +6,7 @@ #include "../features_config.h" #include "../ui/totp_scenes_enum.h" #include "../services/config/config_file_context.h" +#include "../services/idle_timeout/idle_timeout.h" #include "notification_method.h" #include "automation_method.h" #ifdef TOTP_BADBT_TYPE_ENABLED @@ -48,6 +49,9 @@ typedef struct { */ float timezone_offset; + /** + * @brief Config file context + */ ConfigFileContext* config_file_context; /** @@ -101,4 +105,9 @@ typedef struct { */ TotpBtTypeCodeWorkerContext* bt_type_code_worker_context; #endif + + /** + * @brief IDLE timeout context + */ + IdleTimeoutContext* idle_timeout_context; } PluginState; 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 bf76b7c82d..0f2e0cf868 100644 --- a/applications/external/totp/ui/scenes/app_settings/totp_app_settings.c +++ b/applications/external/totp/ui/scenes/app_settings/totp_app_settings.c @@ -1,6 +1,7 @@ #include "totp_app_settings.h" #include #include +#include #include "../../ui_controls.h" #include "../../common_dialogs.h" #include "../../scene_director.h" diff --git a/applications/external/totp/ui/scenes/authenticate/totp_scene_authenticate.c b/applications/external/totp/ui/scenes/authenticate/totp_scene_authenticate.c index 218e5e3975..23a919ed7d 100644 --- a/applications/external/totp/ui/scenes/authenticate/totp_scene_authenticate.c +++ b/applications/external/totp/ui/scenes/authenticate/totp_scene_authenticate.c @@ -1,6 +1,7 @@ #include "totp_scene_authenticate.h" #include #include +#include #include "../../../types/common.h" #include "../../constants.h" #include "../../../services/config/config.h" diff --git a/applications/external/totp/ui/scenes/generate_token/totp_scene_generate_token.c b/applications/external/totp/ui/scenes/generate_token/totp_scene_generate_token.c index da99623fd5..ab1b730d36 100644 --- a/applications/external/totp/ui/scenes/generate_token/totp_scene_generate_token.c +++ b/applications/external/totp/ui/scenes/generate_token/totp_scene_generate_token.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include "totp_scene_generate_token.h" #include "../../../types/token_info.h" diff --git a/applications/external/totp/ui/scenes/standby/standby.c b/applications/external/totp/ui/scenes/standby/standby.c index 5cd6bae6a7..8524cf2a85 100644 --- a/applications/external/totp/ui/scenes/standby/standby.c +++ b/applications/external/totp/ui/scenes/standby/standby.c @@ -1,5 +1,6 @@ #include "standby.h" #include +#include #include "../../constants.h" void totp_scene_standby_render(Canvas* const canvas) { @@ -9,4 +10,4 @@ void totp_scene_standby_render(Canvas* const canvas) { canvas_draw_str_aligned(canvas, 5, 10, AlignLeft, AlignTop, "CLI command"); canvas_draw_str_aligned(canvas, 5, 24, AlignLeft, AlignTop, "is running now"); -} \ No newline at end of file +} diff --git a/applications/external/totp/ui/ui_controls.c b/applications/external/totp/ui/ui_controls.c index d5e86aa581..c6ad3e1fe5 100644 --- a/applications/external/totp/ui/ui_controls.c +++ b/applications/external/totp/ui/ui_controls.c @@ -1,5 +1,6 @@ #include "ui_controls.h" #include +#include #include "constants.h" #define TEXT_BOX_HEIGHT (13) diff --git a/applications/external/totp/workers/type_code_common.c b/applications/external/totp/workers/type_code_common.c index bc42fadaaf..82a5a028e4 100644 --- a/applications/external/totp/workers/type_code_common.c +++ b/applications/external/totp/workers/type_code_common.c @@ -52,10 +52,10 @@ void totp_type_code_worker_execute_automation( while(i < code_buffer_size && (cb_char = code_buffer[i]) != 0) { uint8_t char_index = CONVERT_CHAR_TO_DIGIT(cb_char); if(char_index > 9) { - char_index = cb_char - 0x41 + 10; + char_index = cb_char - 'A' + 10; } - if(char_index > 35) break; + if(char_index >= sizeof(hid_number_keys)) break; uint16_t hid_kb_key = hid_number_keys[char_index]; if(char_index > 9) { diff --git a/applications/external/tuning_fork/application.fam b/applications/external/tuning_fork/application.fam index 5979ddb2ab..350eeb5737 100644 --- a/applications/external/tuning_fork/application.fam +++ b/applications/external/tuning_fork/application.fam @@ -8,7 +8,11 @@ App( "gui", ], fap_icon="tuning_fork_icon.png", - fap_category="Music", + fap_category="Media", stack_size=2 * 1024, order=20, + fap_author="@besya & (Fixes by @Willy-JL)", + fap_weburl="https://github.com/besya/flipperzero-tuning-fork", + fap_version="1.0", + fap_description="Tuning fork for tuning musical instruments", ) diff --git a/applications/external/tuning_fork/tuning_fork.c b/applications/external/tuning_fork/tuning_fork.c index 6912d17807..5547fb670f 100644 --- a/applications/external/tuning_fork/tuning_fork.c +++ b/applications/external/tuning_fork/tuning_fork.c @@ -138,8 +138,7 @@ static void render_callback(Canvas* const canvas, void* ctx) { TuningForkState* tuning_fork_state = ctx; furi_mutex_acquire(tuning_fork_state->mutex, FuriWaitForever); - string_t tempStr; - string_init(tempStr); + FuriString* tempStr = furi_string_alloc(); canvas_draw_frame(canvas, 0, 0, 128, 64); @@ -148,23 +147,24 @@ static void render_callback(Canvas* const canvas, void* ctx) { if(tuning_fork_state->page == Tunings) { char tuningLabel[20]; current_tuning_label(tuning_fork_state, tuningLabel); - string_printf(tempStr, "< %s >", tuningLabel); + furi_string_printf(tempStr, "< %s >", tuningLabel); canvas_draw_str_aligned( - canvas, 64, 28, AlignCenter, AlignCenter, string_get_cstr(tempStr)); - string_reset(tempStr); + canvas, 64, 28, AlignCenter, AlignCenter, furi_string_get_cstr(tempStr)); + furi_string_reset(tempStr); } else { char tuningLabel[20]; current_tuning_label(tuning_fork_state, tuningLabel); - string_printf(tempStr, "%s", tuningLabel); - canvas_draw_str_aligned(canvas, 64, 8, AlignCenter, AlignCenter, string_get_cstr(tempStr)); - string_reset(tempStr); + furi_string_printf(tempStr, "%s", tuningLabel); + canvas_draw_str_aligned( + canvas, 64, 8, AlignCenter, AlignCenter, furi_string_get_cstr(tempStr)); + furi_string_reset(tempStr); char tuningNoteLabel[20]; current_tuning_note_label(tuning_fork_state, tuningNoteLabel); - string_printf(tempStr, "< %s >", tuningNoteLabel); + furi_string_printf(tempStr, "< %s >", tuningNoteLabel); canvas_draw_str_aligned( - canvas, 64, 24, AlignCenter, AlignCenter, string_get_cstr(tempStr)); - string_reset(tempStr); + canvas, 64, 24, AlignCenter, AlignCenter, furi_string_get_cstr(tempStr)); + furi_string_reset(tempStr); } canvas_set_font(canvas, FontSecondary); @@ -184,7 +184,7 @@ static void render_callback(Canvas* const canvas, void* ctx) { elements_progress_bar(canvas, 8, 36, 112, tuning_fork_state->volume); } - string_clear(tempStr); + furi_string_free(tempStr); furi_mutex_release(tuning_fork_state->mutex); } diff --git a/applications/external/uart_terminal/application.fam b/applications/external/uart_terminal/application.fam index 530baf2fc1..6d7e63d6d2 100644 --- a/applications/external/uart_terminal/application.fam +++ b/applications/external/uart_terminal/application.fam @@ -1,6 +1,6 @@ App( appid="uart_terminal", - name="[GPIO] UART Terminal", + name="[UART] Terminal", apptype=FlipperAppType.EXTERNAL, entry_point="uart_terminal_app", requires=["gui"], @@ -8,5 +8,7 @@ App( 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.", ) diff --git a/applications/external/uart_terminal/assets/KeyBackspaceSelected_16x9.png b/applications/external/uart_terminal/assets/KeyBackspaceSelected_16x9.png deleted file mode 100644 index 7cc0759a8c..0000000000 Binary files a/applications/external/uart_terminal/assets/KeyBackspaceSelected_16x9.png and /dev/null differ diff --git a/applications/external/uart_terminal/assets/KeyBackspace_16x9.png b/applications/external/uart_terminal/assets/KeyBackspace_16x9.png deleted file mode 100644 index 9946232d95..0000000000 Binary files a/applications/external/uart_terminal/assets/KeyBackspace_16x9.png and /dev/null differ diff --git a/applications/external/uart_terminal/assets/KeySaveSelected_24x11.png b/applications/external/uart_terminal/assets/KeySaveSelected_24x11.png deleted file mode 100644 index eeb3569d3a..0000000000 Binary files a/applications/external/uart_terminal/assets/KeySaveSelected_24x11.png and /dev/null differ diff --git a/applications/external/uart_terminal/assets/KeySave_24x11.png b/applications/external/uart_terminal/assets/KeySave_24x11.png deleted file mode 100644 index e7dba987a0..0000000000 Binary files a/applications/external/uart_terminal/assets/KeySave_24x11.png and /dev/null differ diff --git a/applications/external/uart_terminal/assets/WarningDolphin_45x42.png b/applications/external/uart_terminal/assets/WarningDolphin_45x42.png deleted file mode 100644 index d766ffbb44..0000000000 Binary files a/applications/external/uart_terminal/assets/WarningDolphin_45x42.png and /dev/null differ diff --git a/applications/external/uart_terminal/uart_text_input.c b/applications/external/uart_terminal/uart_text_input.c index 7d400b81ef..62884550d8 100644 --- a/applications/external/uart_terminal/uart_text_input.c +++ b/applications/external/uart_terminal/uart_text_input.c @@ -1,6 +1,6 @@ #include "uart_text_input.h" #include -#include "uart_terminal_icons.h" +#include #include "uart_terminal_app_i.h" #include diff --git a/applications/external/unitemp/Sensors.c b/applications/external/unitemp/Sensors.c index 666438bfae..33dd3fa88a 100644 --- a/applications/external/unitemp/Sensors.c +++ b/applications/external/unitemp/Sensors.c @@ -78,7 +78,8 @@ const Interface SPI = { static const SensorType* sensorTypes[] = {&DHT11, &DHT12_SW, &DHT20, &DHT21, &DHT22, &Dallas, &AM2320_SW, &AM2320_I2C, &HTU21x, &AHT10, &SHT30, &GXHT30, &LM75, &HDC1080, &BMP180, - &BMP280, &BME280, &BME680, &MAX31855, &MAX6675}; + &BMP280, &BME280, &BME680, &MAX31855, &MAX6675, + &SCD30, &SCD40}; const SensorType* unitemp_sensors_getTypeFromInt(uint8_t index) { if(index > SENSOR_TYPES_COUNT) return NULL; @@ -623,11 +624,16 @@ UnitempStatus unitemp_sensor_updateData(Sensor* sensor) { UNITEMP_DEBUG("Sensor %s update status %d", sensor->name, sensor->status); } - if(app->settings.temp_unit == UT_TEMP_FAHRENHEIT && sensor->status == UT_SENSORSTATUS_OK) { - uintemp_celsiumToFarengate(sensor); - } - if(sensor->status == UT_SENSORSTATUS_OK) { + if(app->settings.heat_index && + ((sensor->type->datatype & (UT_TEMPERATURE | UT_HUMIDITY)) == + (UT_TEMPERATURE | UT_HUMIDITY))) { + unitemp_calculate_heat_index(sensor); + } + if(app->settings.temp_unit == UT_TEMP_FAHRENHEIT) { + uintemp_celsiumToFarengate(sensor); + } + sensor->temp += sensor->temp_offset / 10.f; if(app->settings.pressure_unit == UT_PRESSURE_MM_HG) { unitemp_pascalToMmHg(sensor); diff --git a/applications/external/unitemp/Sensors.h b/applications/external/unitemp/Sensors.h index d2b7c07af8..339a4deff1 100644 --- a/applications/external/unitemp/Sensors.h +++ b/applications/external/unitemp/Sensors.h @@ -24,6 +24,7 @@ #define UT_TEMPERATURE 0b00000001 #define UT_HUMIDITY 0b00000010 #define UT_PRESSURE 0b00000100 +#define UT_CO2 0b00001000 //Статусы опроса датчика typedef enum { @@ -31,6 +32,7 @@ typedef enum { UT_DATA_TYPE_TEMP_HUM = UT_TEMPERATURE | UT_HUMIDITY, UT_DATA_TYPE_TEMP_PRESS = UT_TEMPERATURE | UT_PRESSURE, UT_DATA_TYPE_TEMP_HUM_PRESS = UT_TEMPERATURE | UT_HUMIDITY | UT_PRESSURE, + UT_DATA_TYPE_TEMP_HUM_CO2 = UT_TEMPERATURE | UT_HUMIDITY | UT_CO2, } SensorDataType; //Типы возвращаемых данных @@ -117,10 +119,13 @@ typedef struct Sensor { char* name; //Температура float temp; + float heat_index; //Относительная влажность float hum; //Атмосферное давление float pressure; + // Концентрация CO2 + float co2; //Тип датчика const SensorType* type; //Статус последнего опроса датчика @@ -329,4 +334,6 @@ const GPIO* #include "./sensors/HDC1080.h" #include "./sensors/MAX31855.h" #include "./sensors/MAX6675.h" +#include "./sensors/SCD30.h" +#include "./sensors/SCD40.h" #endif diff --git a/applications/external/unitemp/application.fam b/applications/external/unitemp/application.fam index b0e0aa6ee9..3fbdd51853 100644 --- a/applications/external/unitemp/application.fam +++ b/applications/external/unitemp/application.fam @@ -9,10 +9,9 @@ App( stack_size=2 * 1024, order=100, fap_description="Universal temperature sensors reader", - fap_author="Quenon", - fap_weburl="https://github.com/quen0n/Unitemp-Flipper-Zero-Plugin", + fap_author="@quen0n & (fixes by @xMasterX)", + fap_weburl="https://github.com/quen0n/unitemp-flipperzero", fap_category="GPIO", fap_icon="icon.png", fap_icon_assets="assets", - fap_libs=["assets"], ) diff --git a/applications/external/unitemp/assets/co2_11x14.png b/applications/external/unitemp/assets/co2_11x14.png new file mode 100644 index 0000000000..2a2b5e068d Binary files /dev/null and b/applications/external/unitemp/assets/co2_11x14.png differ diff --git a/applications/external/unitemp/assets/heat_index_11x14.png b/applications/external/unitemp/assets/heat_index_11x14.png new file mode 100644 index 0000000000..f2f0d4ce5f Binary files /dev/null and b/applications/external/unitemp/assets/heat_index_11x14.png differ diff --git a/applications/external/unitemp/interfaces/endianness.h b/applications/external/unitemp/interfaces/endianness.h new file mode 100644 index 0000000000..c4a3f4b870 --- /dev/null +++ b/applications/external/unitemp/interfaces/endianness.h @@ -0,0 +1,60 @@ +// +// Created by Avilov Vasily on 10.06.2023. +// + +#ifndef FLIPPERZERO_FIRMWARE_ENDIANNESS_H +#define FLIPPERZERO_FIRMWARE_ENDIANNESS_H + +inline static void store16(uint8_t* b, uint16_t i) { + memcpy(b, &i, 2); +} + +inline static void store32(uint8_t* b, uint32_t i) { + memcpy(b, &i, 4); +} + +inline static uint16_t load16(uint8_t* b) { + uint16_t x; + memcpy(&x, b, 2); + return x; +} + +inline static uint32_t load32(uint8_t* b) { + uint32_t x; + memcpy(&x, b, 4); + return x; +} + +#if BYTE_ORDER == BIG_ENDIAN +#define htobe16(x) (x) +#define htobe32(x) (x) +#define htole16(x) __builtin_bswap16(x) +#define htole32(x) __builtin_bswap32(x) +#define be16toh(x) (x) +#define be32toh(x) (x) +#define le16toh(x) __builtin_bswap16(x) +#define le32toh(x) __builtin_bswap32(x) +#elif BYTE_ORDER == LITTLE_ENDIAN +#define htobe16(x) __builtin_bswap16(x) +#define htobe32(x) __builtin_bswap32(x) +#define htole16(x) (x) +#define htole32(x) (x) +#define be16toh(x) __builtin_bswap16(x) +#define be32toh(x) __builtin_bswap32(x) +#define le16toh(x) (x) +#define le32toh(x) (x) +#else +#error "What kind of system is this?" +#endif + +#define load16_le(b) (le16toh(load16(b))) +#define load32_le(b) (le32toh(load32(b))) +#define store16_le(b, i) (store16(b, htole16(i))) +#define store32_le(b, i) (store32(b, htole32(i))) + +#define load16_be(b) (be16toh(load16(b))) +#define load32_be(b) (be32toh(load32(b))) +#define store16_be(b, i) (store16(b, htobe16(i))) +#define store32_be(b, i) (store32(b, htobe32(i))) + +#endif //FLIPPERZERO_FIRMWARE_ENDIANNESS_H diff --git a/applications/external/unitemp/sensors/SCD30.c b/applications/external/unitemp/sensors/SCD30.c new file mode 100644 index 0000000000..d7a10149c5 --- /dev/null +++ b/applications/external/unitemp/sensors/SCD30.c @@ -0,0 +1,387 @@ +/* + Unitemp - Universal temperature reader + Copyright (C) 2022-2023 Victor Nikitchuk (https://github.com/quen0n) + Contributed by divinebird (https://github.com/divinebird) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +// Some information may be seen on https://github.com/sparkfun/SparkFun_SCD30_Arduino_Library + +#include "SCD30.h" +#include "../interfaces/I2CSensor.h" +#include "../interfaces/endianness.h" +//#include <3rdparty/everest/include/everest/kremlin/c_endianness.h> + +typedef union { + uint16_t array16[2]; + uint8_t array8[4]; + float value; +} ByteToFl; + +bool unitemp_SCD30_alloc(Sensor* sensor, char* args); +bool unitemp_SCD30_init(Sensor* sensor); +bool unitemp_SCD30_deinit(Sensor* sensor); +UnitempStatus unitemp_SCD30_update(Sensor* sensor); +bool unitemp_SCD30_free(Sensor* sensor); + +const SensorType SCD30 = { + .typename = "SCD30", + .interface = &I2C, + .datatype = UT_DATA_TYPE_TEMP_HUM_CO2, + .pollingInterval = 2000, + .allocator = unitemp_SCD30_alloc, + .mem_releaser = unitemp_SCD30_free, + .initializer = unitemp_SCD30_init, + .deinitializer = unitemp_SCD30_deinit, + .updater = unitemp_SCD30_update}; + +#define SCD30_ID 0x61 + +#define COMMAND_CONTINUOUS_MEASUREMENT 0x0010 +#define COMMAND_SET_MEASUREMENT_INTERVAL 0x4600 +#define COMMAND_GET_DATA_READY 0x0202 +#define COMMAND_READ_MEASUREMENT 0x0300 +#define COMMAND_AUTOMATIC_SELF_CALIBRATION 0x5306 +#define COMMAND_SET_FORCED_RECALIBRATION_FACTOR 0x5204 +#define COMMAND_SET_TEMPERATURE_OFFSET 0x5403 +#define COMMAND_SET_ALTITUDE_COMPENSATION 0x5102 +#define COMMAND_RESET 0xD304 // Soft reset +#define COMMAND_STOP_MEAS 0x0104 +#define COMMAND_READ_FW_VER 0xD100 + +static bool dataAvailable(Sensor* sensor) __attribute__((unused)); +static bool readMeasurement(Sensor* sensor) __attribute__((unused)); +static void reset(Sensor* sensor) __attribute__((unused)); + +static bool setAutoSelfCalibration(Sensor* sensor, bool enable) __attribute__((unused)); +static bool getAutoSelfCalibration(Sensor* sensor) __attribute__((unused)); + +static bool getFirmwareVersion(Sensor* sensor, uint16_t* val) __attribute__((unused)); + +static bool setForcedRecalibrationFactor(Sensor* sensor, uint16_t concentration) + __attribute__((unused)); +static uint16_t getAltitudeCompensation(Sensor* sensor) __attribute__((unused)); +static bool setAltitudeCompensation(Sensor* sensor, uint16_t altitude) __attribute__((unused)); +static bool setAmbientPressure(Sensor* sensor, uint16_t pressure_mbar) __attribute__((unused)); + +static float getTemperatureOffset(Sensor* sensor) __attribute__((unused)); +static bool setTemperatureOffset(Sensor* sensor, float tempOffset) __attribute__((unused)); + +static bool beginMeasuringWithSettings(Sensor* sensor, uint16_t pressureOffset) + __attribute__((unused)); +static bool beginMeasuring(Sensor* sensor) __attribute__((unused)); +static bool stopMeasurement(Sensor* sensor) __attribute__((unused)); + +static bool setMeasurementInterval(Sensor* sensor, uint16_t interval) __attribute__((unused)); +static uint16_t getMeasurementInterval(Sensor* sensor) __attribute__((unused)); + +bool unitemp_SCD30_alloc(Sensor* sensor, char* args) { + UNUSED(args); + I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance; + + i2c_sensor->minI2CAdr = SCD30_ID << 1; + i2c_sensor->maxI2CAdr = SCD30_ID << 1; + return true; +} + +bool unitemp_SCD30_free(Sensor* sensor) { + //Нечего высвобождать, так как ничего не было выделено + UNUSED(sensor); + return true; +} + +bool unitemp_SCD30_init(Sensor* sensor) { + if(beginMeasuring(sensor) == true) { // Start continuous measurements + setMeasurementInterval(sensor, SCD30.pollingInterval / 1000); + setAutoSelfCalibration(sensor, true); + setAmbientPressure(sensor, 0); + } else + return false; + + return true; +} + +bool unitemp_SCD30_deinit(Sensor* sensor) { + return stopMeasurement(sensor); +} + +UnitempStatus unitemp_SCD30_update(Sensor* sensor) { + readMeasurement(sensor); + return UT_SENSORSTATUS_OK; +} + +static uint8_t computeCRC8(uint8_t* message, uint8_t len) { + uint8_t crc = 0xFF; // Init with 0xFF + for(uint8_t x = 0; x < len; x++) { + crc ^= message[x]; // XOR-in the next input byte + for(uint8_t i = 0; i < 8; i++) { + if((crc & 0x80) != 0) + crc = (uint8_t)((crc << 1) ^ 0x31); + else + crc <<= 1; + } + } + return crc; // No output reflection +} + +// Sends a command along with arguments and CRC +static bool sendCommandWithCRC(Sensor* sensor, uint16_t command, uint16_t arguments) { + static const uint8_t cmdSize = 5; + + uint8_t bytes[cmdSize]; + uint8_t* pointer = bytes; + store16_be(pointer, command); + pointer += 2; + uint8_t* argPos = pointer; + store16_be(pointer, arguments); + pointer += 2; + *pointer = computeCRC8(argPos, pointer - argPos); + + I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance; + return unitemp_i2c_writeArray(i2c_sensor, cmdSize, bytes); +} + +// Sends just a command, no arguments, no CRC +static bool sendCommand(Sensor* sensor, uint16_t command) { + static const uint8_t cmdSize = 2; + + uint8_t bytes[cmdSize]; + store16_be(bytes, command); + + I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance; + return unitemp_i2c_writeArray(i2c_sensor, cmdSize, bytes); +} + +static uint16_t readRegister(Sensor* sensor, uint16_t registerAddress) { + static const uint8_t regSize = 2; + + if(!sendCommand(sensor, registerAddress)) return 0; // Sensor did not ACK + + furi_delay_ms(3); + + uint8_t bytes[regSize]; + I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance; + if(!unitemp_i2c_readArray(i2c_sensor, regSize, bytes)) return 0; + + return load16_be(bytes); +} + +static bool loadWord(uint8_t* buff, uint16_t* val) { + uint16_t tmp = load16_be(buff); + uint8_t expectedCRC = computeCRC8(buff, 2); + if(buff[2] != expectedCRC) return false; + *val = tmp; + return true; +} + +static bool getSettingValue(Sensor* sensor, uint16_t registerAddress, uint16_t* val) { + static const uint8_t respSize = 3; + + if(!sendCommand(sensor, registerAddress)) return false; // Sensor did not ACK + + furi_delay_ms(3); + + uint8_t bytes[respSize]; + I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance; + if(!unitemp_i2c_readArray(i2c_sensor, respSize, bytes)) return false; + + return loadWord(bytes, val); +} + +static bool loadFloat(uint8_t* buff, float* val) { + // ByteToFl tmp; + size_t cntr = 0; + uint8_t floatBuff[4]; + for(size_t i = 0; i < 2; i++) { + floatBuff[cntr++] = buff[0]; + floatBuff[cntr++] = buff[1]; + uint8_t expectedCRC = computeCRC8(buff, 2); + if(buff[2] != expectedCRC) return false; + buff += 3; + } + uint32_t tmpVal = load32_be(floatBuff); + memcpy(val, &tmpVal, sizeof(float)); + return true; +} + +// Get 18 bytes from SCD30 +// Updates global variables with floats +// Returns true if success +static bool readMeasurement(Sensor* sensor) { + // Verify we have data from the sensor + if(!dataAvailable(sensor)) { + return false; + } + + if(!sendCommand(sensor, COMMAND_READ_MEASUREMENT)) { + FURI_LOG_E(APP_NAME, "Sensor did not ACK"); + return false; // Sensor did not ACK + } + + float tempCO2 = 0; + float tempHumidity = 0; + float tempTemperature = 0; + + furi_delay_ms(3); + + static const uint8_t respSize = 18; + uint8_t buff[respSize]; + uint8_t* bytes = buff; + I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance; + if(!unitemp_i2c_readArray(i2c_sensor, respSize, bytes)) { + FURI_LOG_E(APP_NAME, "Error while read measures"); + return false; + } + + bool error = false; + if(loadFloat(bytes, &tempCO2)) { + sensor->co2 = tempCO2; + } else { + FURI_LOG_E(APP_NAME, "Error while parsing CO2"); + error = true; + } + + bytes += 6; + if(loadFloat(bytes, &tempTemperature)) { + sensor->temp = tempTemperature; + } else { + FURI_LOG_E(APP_NAME, "Error while parsing temp"); + error = true; + } + + bytes += 6; + if(loadFloat(bytes, &tempHumidity)) { + sensor->hum = tempHumidity; + } else { + FURI_LOG_E(APP_NAME, "Error while parsing humidity"); + error = true; + } + + return !error; +} + +static void reset(Sensor* sensor) { + sendCommand(sensor, COMMAND_RESET); +} + +static bool setAutoSelfCalibration(Sensor* sensor, bool enable) { + return sendCommandWithCRC( + sensor, COMMAND_AUTOMATIC_SELF_CALIBRATION, enable); // Activate continuous ASC +} + +// Get the current ASC setting +static bool getAutoSelfCalibration(Sensor* sensor) { + return 1 == readRegister(sensor, COMMAND_AUTOMATIC_SELF_CALIBRATION); +} + +static bool getFirmwareVersion(Sensor* sensor, uint16_t* val) { + return getSettingValue(sensor, COMMAND_READ_FW_VER, val); +} + +// Set the forced recalibration factor. See 1.3.7. +// The reference CO2 concentration has to be within the range 400 ppm ≤ cref(CO2) ≤ 2000 ppm. +static bool setForcedRecalibrationFactor(Sensor* sensor, uint16_t concentration) { + if(concentration < 400 || concentration > 2000) { + return false; // Error check. + } + return sendCommandWithCRC(sensor, COMMAND_SET_FORCED_RECALIBRATION_FACTOR, concentration); +} + +// Get the temperature offset. See 1.3.8. +static float getTemperatureOffset(Sensor* sensor) { + union { + int16_t signed16; + uint16_t unsigned16; + } signedUnsigned; // Avoid any ambiguity casting int16_t to uint16_t + signedUnsigned.unsigned16 = readRegister(sensor, COMMAND_SET_TEMPERATURE_OFFSET); + + return ((float)signedUnsigned.signed16) / 100.0; +} + +static bool setTemperatureOffset(Sensor* sensor, float tempOffset) { + // Temp offset is only positive. See: https://github.com/sparkfun/SparkFun_SCD30_Arduino_Library/issues/27#issuecomment-971986826 + //"The SCD30 offset temperature is obtained by subtracting the reference temperature from the SCD30 output temperature" + // https://www.sensirion.com/fileadmin/user_upload/customers/sensirion/Dokumente/9.5_CO2/Sensirion_CO2_Sensors_SCD30_Low_Power_Mode.pdf + + if(tempOffset < 0.0) return false; + + uint16_t value = tempOffset * 100; + + return sendCommandWithCRC(sensor, COMMAND_SET_TEMPERATURE_OFFSET, value); +} + +// Get the altitude compenstation. See 1.3.9. +static uint16_t getAltitudeCompensation(Sensor* sensor) { + return readRegister(sensor, COMMAND_SET_ALTITUDE_COMPENSATION); +} + +// Set the altitude compenstation. See 1.3.9. +static bool setAltitudeCompensation(Sensor* sensor, uint16_t altitude) { + return sendCommandWithCRC(sensor, COMMAND_SET_ALTITUDE_COMPENSATION, altitude); +} + +// Set the pressure compenstation. This is passed during measurement startup. +// mbar can be 700 to 1200 +static bool setAmbientPressure(Sensor* sensor, uint16_t pressure_mbar) { + if(pressure_mbar != 0 || pressure_mbar < 700 || pressure_mbar > 1200) { + return false; + } + return sendCommandWithCRC(sensor, COMMAND_CONTINUOUS_MEASUREMENT, pressure_mbar); +} + +// Begins continuous measurements +// Continuous measurement status is saved in non-volatile memory. When the sensor +// is powered down while continuous measurement mode is active SCD30 will measure +// continuously after repowering without sending the measurement command. +// Returns true if successful +static bool beginMeasuringWithSettings(Sensor* sensor, uint16_t pressureOffset) { + return sendCommandWithCRC(sensor, COMMAND_CONTINUOUS_MEASUREMENT, pressureOffset); +} + +// Overload - no pressureOffset +static bool beginMeasuring(Sensor* sensor) { + return beginMeasuringWithSettings(sensor, 0); +} + +// Stop continuous measurement +static bool stopMeasurement(Sensor* sensor) { + return sendCommand(sensor, COMMAND_STOP_MEAS); +} + +// Sets interval between measurements +// 2 seconds to 1800 seconds (30 minutes) +static bool setMeasurementInterval(Sensor* sensor, uint16_t interval) { + if(interval < 2 || interval > 1800) return false; + if(!sendCommandWithCRC(sensor, COMMAND_SET_MEASUREMENT_INTERVAL, interval)) return false; + uint16_t verInterval = readRegister(sensor, COMMAND_SET_MEASUREMENT_INTERVAL); + if(verInterval != interval) { + FURI_LOG_E(APP_NAME, "Measure interval wrong! Val: %02x", verInterval); + return false; + } + return true; +} + +// Gets interval between measurements +// 2 seconds to 1800 seconds (30 minutes) +static uint16_t getMeasurementInterval(Sensor* sensor) { + uint16_t interval = 0; + getSettingValue(sensor, COMMAND_SET_MEASUREMENT_INTERVAL, &interval); + return interval; +} + +// Returns true when data is available +static bool dataAvailable(Sensor* sensor) { + return 1 == readRegister(sensor, COMMAND_GET_DATA_READY); +} \ No newline at end of file diff --git a/applications/external/unitemp/sensors/SCD30.h b/applications/external/unitemp/sensors/SCD30.h new file mode 100644 index 0000000000..1ebfbb4119 --- /dev/null +++ b/applications/external/unitemp/sensors/SCD30.h @@ -0,0 +1,59 @@ +/* + Unitemp - Universal temperature reader + Copyright (C) 2022-2023 Victor Nikitchuk (https://github.com/quen0n) + Contributed by divinebird (https://github.com/divinebird) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +#ifndef UNITEMP_SCD30 +#define UNITEMP_SCD30 + +#include "../unitemp.h" +#include "../Sensors.h" + +extern const SensorType SCD30; +/** + * @brief Выделение памяти и установка начальных значений датчика SCD30 + * @param sensor Указатель на создаваемый датчик + * @return Истина при успехе + */ +bool unitemp_SCD30_alloc(Sensor* sensor, char* args); + +/** + * @brief Инициализации датчика SCD30 + * @param sensor Указатель на датчик + * @return Истина если инициализация упспешная + */ +bool unitemp_SCD30_init(Sensor* sensor); + +/** + * @brief Деинициализация датчика + * @param sensor Указатель на датчик + */ +bool unitemp_SCD30_deinit(Sensor* sensor); + +/** + * @brief Обновление значений из датчика + * @param sensor Указатель на датчик + * @return Статус опроса датчика + */ +UnitempStatus unitemp_SCD30_update(Sensor* sensor); + +/** + * @brief Высвободить память датчика + * @param sensor Указатель на датчик + */ +bool unitemp_SCD30_free(Sensor* sensor); + +#endif \ No newline at end of file diff --git a/applications/external/unitemp/sensors/SCD40.c b/applications/external/unitemp/sensors/SCD40.c new file mode 100644 index 0000000000..c88943a005 --- /dev/null +++ b/applications/external/unitemp/sensors/SCD40.c @@ -0,0 +1,291 @@ +/* + Unitemp - Universal temperature reader + Copyright (C) 2022-2023 Victor Nikitchuk (https://github.com/quen0n) + Contributed by divinebird (https://github.com/divinebird) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +// Some information may be seen on https://github.com/sparkfun/SparkFun_SCD30_Arduino_Library + +#include "SCD30.h" +#include "../interfaces/I2CSensor.h" +#include "../interfaces/endianness.h" +//#include <3rdparty/everest/include/everest/kremlin/c_endianness.h> + +bool unitemp_SCD40_alloc(Sensor* sensor, char* args); +bool unitemp_SCD40_init(Sensor* sensor); +bool unitemp_SCD40_deinit(Sensor* sensor); +UnitempStatus unitemp_SCD40_update(Sensor* sensor); +bool unitemp_SCD40_free(Sensor* sensor); + +const SensorType SCD40 = { + .typename = "SCD40", + .interface = &I2C, + .datatype = UT_DATA_TYPE_TEMP_HUM_CO2, + .pollingInterval = 5000, + .allocator = unitemp_SCD40_alloc, + .mem_releaser = unitemp_SCD40_free, + .initializer = unitemp_SCD40_init, + .deinitializer = unitemp_SCD40_deinit, + .updater = unitemp_SCD40_update}; + +#define SCD40_ID 0x62 + +#define COMMAND_START_PERIODIC_MEASUREMENT 0X21B1 +#define COMMAND_READ_MEASUREMENT 0XEC05 +#define COMMAND_STOP_PERIODIC_MEASUREMENT 0X3F86 + +#define COMMAND_PERSIST_SETTINGS 0X3615 +#define COMMAND_GET_SERIAL_NUMBER 0X3682 +#define COMMAND_PERFORM_SELF_TEST 0X3639 +#define COMMAND_PERFORM_FACTORY_RESET 0X3632 +#define COMMAND_REINIT 0X3646 + +#define COMMAND_SET_TEMPERATURE_OFFSET 0X241D +#define COMMAND_GET_TEMPERATURE_OFFSET 0X2318 +#define COMMAND_SET_SENSOR_ALTITUDE 0X2427 +#define COMMAND_GET_SENSOR_ALTITUDE 0X2322 +#define COMMAND_SET_AMBIENT_PRESSURE 0XE000 +#define COMMAND_PERFORM_FORCED_RECALIBRATION 0X362F +#define COMMAND_SET_AUTOMATIC_SELF_CALIBRATION_ENABLED 0X2416 +#define COMMAND_GET_AUTOMATIC_SELF_CALIBRATION_ENABLED 0X2313 + +static bool readMeasurement(Sensor* sensor) __attribute__((unused)); +static void reset(Sensor* sensor) __attribute__((unused)); + +static bool setAutoSelfCalibration(Sensor* sensor, bool enable) __attribute__((unused)); +static bool getAutoSelfCalibration(Sensor* sensor) __attribute__((unused)); + +static bool getFirmwareVersion(Sensor* sensor, uint16_t* val) __attribute__((unused)); + +static float getTemperatureOffset(Sensor* sensor) __attribute__((unused)); +static bool setTemperatureOffset(Sensor* sensor, float tempOffset) __attribute__((unused)); + +static bool beginMeasuring(Sensor* sensor) __attribute__((unused)); +static bool stopMeasurement(Sensor* sensor) __attribute__((unused)); + +bool unitemp_SCD40_alloc(Sensor* sensor, char* args) { + UNUSED(args); + I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance; + + i2c_sensor->minI2CAdr = SCD40_ID << 1; + i2c_sensor->maxI2CAdr = SCD40_ID << 1; + return true; +} + +bool unitemp_SCD40_free(Sensor* sensor) { + //Нечего высвобождать, так как ничего не было выделено + UNUSED(sensor); + return true; +} + +bool unitemp_SCD40_init(Sensor* sensor) { + return beginMeasuring(sensor); +} + +bool unitemp_SCD40_deinit(Sensor* sensor) { + return stopMeasurement(sensor); +} + +UnitempStatus unitemp_SCD40_update(Sensor* sensor) { + readMeasurement(sensor); + return UT_SENSORSTATUS_OK; +} + +#define CRC8_POLYNOMIAL 0x31 +#define CRC8_INIT 0xFF + +static uint8_t computeCRC8(uint8_t* message, uint8_t len) { + uint8_t crc = CRC8_INIT; // Init with 0xFF + for(uint8_t x = 0; x < len; x++) { + crc ^= message[x]; // XOR-in the next input byte + for(uint8_t i = 0; i < 8; i++) { + if((crc & 0x80) != 0) + crc = (uint8_t)((crc << 1) ^ CRC8_POLYNOMIAL); + else + crc <<= 1; + } + } + return crc; // No output reflection +} + +// Sends a command along with arguments and CRC +static bool sendCommandWithCRC(Sensor* sensor, uint16_t command, uint16_t arguments) { + static const uint8_t cmdSize = 5; + + uint8_t bytes[cmdSize]; + uint8_t* pointer = bytes; + store16_be(pointer, command); + pointer += 2; + uint8_t* argPos = pointer; + store16_be(pointer, arguments); + pointer += 2; + *pointer = computeCRC8(argPos, pointer - argPos); + + I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance; + return unitemp_i2c_writeArray(i2c_sensor, cmdSize, bytes); +} + +// Sends just a command, no arguments, no CRC +static bool sendCommand(Sensor* sensor, uint16_t command) { + static const uint8_t cmdSize = 2; + + uint8_t bytes[cmdSize]; + store16_be(bytes, command); + + I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance; + return unitemp_i2c_writeArray(i2c_sensor, cmdSize, bytes); +} + +static uint16_t readRegister(Sensor* sensor, uint16_t registerAddress) { + static const uint8_t regSize = 2; + + if(!sendCommand(sensor, registerAddress)) return 0; // Sensor did not ACK + + furi_delay_ms(3); + + uint8_t bytes[regSize]; + I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance; + if(!unitemp_i2c_readArray(i2c_sensor, regSize, bytes)) return 0; + + return load16_be(bytes); +} + +static bool loadWord(uint8_t* buff, uint16_t* val) { + uint16_t tmp = load16_be(buff); + uint8_t expectedCRC = computeCRC8(buff, 2); + if(buff[2] != expectedCRC) return false; + *val = tmp; + return true; +} + +static bool getSettingValue(Sensor* sensor, uint16_t registerAddress, uint16_t* val) { + static const uint8_t respSize = 3; + + if(!sendCommand(sensor, registerAddress)) return false; // Sensor did not ACK + + furi_delay_ms(3); + + uint8_t bytes[respSize]; + I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance; + if(!unitemp_i2c_readArray(i2c_sensor, respSize, bytes)) return false; + + return loadWord(bytes, val); +} + +// Get 18 bytes from SCD30 +// Updates global variables with floats +// Returns true if success +static bool readMeasurement(Sensor* sensor) { + if(!sendCommand(sensor, COMMAND_READ_MEASUREMENT)) { + FURI_LOG_E(APP_NAME, "Sensor did not ACK"); + return false; // Sensor did not ACK + } + + furi_delay_ms(3); + + static const uint8_t respSize = 9; + uint8_t buff[respSize]; + uint8_t* bytes = buff; + I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance; + if(!unitemp_i2c_readArray(i2c_sensor, respSize, bytes)) { + FURI_LOG_E(APP_NAME, "Error while read measures"); + return false; + } + + uint16_t tmpValue; + + bool error = false; + if(loadWord(bytes, &tmpValue)) { + sensor->co2 = tmpValue; + } else { + FURI_LOG_E(APP_NAME, "Error while parsing CO2"); + error = true; + } + + bytes += 3; + if(loadWord(bytes, &tmpValue)) { + sensor->temp = (float)tmpValue * 175.0f / 65535.0f - 45.0f; + } else { + FURI_LOG_E(APP_NAME, "Error while parsing temp"); + error = true; + } + + bytes += 3; + if(loadWord(bytes, &tmpValue)) { + sensor->hum = (float)tmpValue * 100.0f / 65535.0f; + } else { + FURI_LOG_E(APP_NAME, "Error while parsing humidity"); + error = true; + } + + return !error; +} + +static void reset(Sensor* sensor) { + sendCommand(sensor, COMMAND_REINIT); +} + +static bool setAutoSelfCalibration(Sensor* sensor, bool enable) { + return sendCommandWithCRC( + sensor, COMMAND_SET_AUTOMATIC_SELF_CALIBRATION_ENABLED, enable); // Activate continuous ASC +} + +// Get the current ASC setting +static bool getAutoSelfCalibration(Sensor* sensor) { + return 1 == readRegister(sensor, COMMAND_GET_AUTOMATIC_SELF_CALIBRATION_ENABLED); +} + +// Unfinished +static bool getFirmwareVersion(Sensor* sensor, uint16_t* val) { + if(!sendCommand(sensor, COMMAND_READ_MEASUREMENT)) { + FURI_LOG_E(APP_NAME, "Sensor did not ACK"); + return false; // Sensor did not ACK + } + + static const uint8_t respSize = 9; + uint8_t buff[respSize]; + uint8_t* bytes = buff; + I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance; + if(!unitemp_i2c_readArray(i2c_sensor, respSize, bytes)) { + FURI_LOG_E(APP_NAME, "Error while read measures"); + return false; + } + + *val = 0; + + return true; +} + +static bool beginMeasuring(Sensor* sensor) { + return sendCommand(sensor, COMMAND_START_PERIODIC_MEASUREMENT); +} + +// Stop continuous measurement +static bool stopMeasurement(Sensor* sensor) { + return sendCommand(sensor, COMMAND_READ_MEASUREMENT); +} + +static float getTemperatureOffset(Sensor* sensor) { + uint16_t curOffset; + if(!getSettingValue(sensor, COMMAND_GET_TEMPERATURE_OFFSET, &curOffset)) return 0.0; + return (float)curOffset * 175.0f / 65536.0f; +} + +static bool setTemperatureOffset(Sensor* sensor, float tempOffset) { + uint16_t newOffset = tempOffset * 65536.0 / 175.0 + 0.5f; + return sendCommandWithCRC( + sensor, COMMAND_SET_TEMPERATURE_OFFSET, newOffset); // Activate continuous ASC +} diff --git a/applications/external/unitemp/sensors/SCD40.h b/applications/external/unitemp/sensors/SCD40.h new file mode 100644 index 0000000000..5cf7a4324d --- /dev/null +++ b/applications/external/unitemp/sensors/SCD40.h @@ -0,0 +1,59 @@ +/* + Unitemp - Universal temperature reader + Copyright (C) 2022-2023 Victor Nikitchuk (https://github.com/quen0n) + Contributed by divinebird (https://github.com/divinebird) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +#ifndef UNITEMP_SCD40 +#define UNITEMP_SCD40 + +#include "../unitemp.h" +#include "../Sensors.h" + +extern const SensorType SCD40; +/** + * @brief Выделение памяти и установка начальных значений датчика SCD40 + * @param sensor Указатель на создаваемый датчик + * @return Истина при успехе + */ +bool unitemp_SCD40_alloc(Sensor* sensor, char* args); + +/** + * @brief Инициализации датчика SCD40 + * @param sensor Указатель на датчик + * @return Истина если инициализация упспешная + */ +bool unitemp_SCD40_init(Sensor* sensor); + +/** + * @brief Деинициализация датчика + * @param sensor Указатель на датчик + */ +bool unitemp_SCD40_deinit(Sensor* sensor); + +/** + * @brief Обновление значений из датчика + * @param sensor Указатель на датчик + * @return Статус опроса датчика + */ +UnitempStatus unitemp_SCD40_update(Sensor* sensor); + +/** + * @brief Высвободить память датчика + * @param sensor Указатель на датчик + */ +bool unitemp_SCD40_free(Sensor* sensor); + +#endif \ No newline at end of file diff --git a/applications/external/unitemp/unitemp.c b/applications/external/unitemp/unitemp.c index fbf9eca55d..9f56d8eef6 100644 --- a/applications/external/unitemp/unitemp.c +++ b/applications/external/unitemp/unitemp.c @@ -28,8 +28,31 @@ Unitemp* app; void uintemp_celsiumToFarengate(Sensor* sensor) { sensor->temp = sensor->temp * (9.0 / 5.0) + 32; + sensor->heat_index = sensor->heat_index * (9.0 / 5.0) + 32; } +static float heat_index_consts[9] = { + -42.379f, + 2.04901523f, + 10.14333127f, + -0.22475541f, + -0.00683783f, + -0.05481717f, + 0.00122874f, + 0.00085282f, + -0.00000199f}; +void unitemp_calculate_heat_index(Sensor* sensor) { + // temp should be in Celsius, heat index will be in Celsius + float temp = sensor->temp * (9.0 / 5.0) + 32.0f; + float hum = sensor->hum; + sensor->heat_index = + (heat_index_consts[0] + heat_index_consts[1] * temp + heat_index_consts[2] * hum + + heat_index_consts[3] * temp * hum + heat_index_consts[4] * temp * temp + + heat_index_consts[5] * hum * hum + heat_index_consts[6] * temp * temp * hum + + heat_index_consts[7] * temp * hum * hum + heat_index_consts[8] * temp * temp * hum * hum - + 32.0f) * + (5.0 / 9.0); +} void unitemp_pascalToMmHg(Sensor* sensor) { sensor->pressure = sensor->pressure * 0.007500638; } @@ -71,6 +94,7 @@ bool unitemp_saveSettings(void) { app->file_stream, "INFINITY_BACKLIGHT %d\n", app->settings.infinityBacklight); stream_write_format(app->file_stream, "TEMP_UNIT %d\n", app->settings.temp_unit); stream_write_format(app->file_stream, "PRESSURE_UNIT %d\n", app->settings.pressure_unit); + stream_write_format(app->file_stream, "HEAT_INDEX %d\n", app->settings.heat_index); //Закрытие потока и освобождение памяти file_stream_close(app->file_stream); @@ -166,6 +190,11 @@ bool unitemp_loadSettings(void) { int p = 0; sscanf(((char*)(file_buf + line_end)), "\nPRESSURE_UNIT %d", &p); app->settings.pressure_unit = p; + } else if(!strcmp(buff, "HEAT_INDEX")) { + //Чтение значения параметра + int p = 0; + sscanf(((char*)(file_buf + line_end)), "\nHEAT_INDEX %d", &p); + app->settings.heat_index = p; } else { FURI_LOG_W(APP_NAME, "Unknown settings parameter: %s", buff); } @@ -195,6 +224,7 @@ static bool unitemp_alloc(void) { //Открытие хранилища (?) app->storage = furi_record_open(RECORD_STORAGE); + storage_common_migrate(app->storage, EXT_PATH("unitemp"), APP_PATH_FOLDER); //Уведомления app->notifications = furi_record_open(RECORD_NOTIFICATION); @@ -203,6 +233,7 @@ static bool unitemp_alloc(void) { app->settings.infinityBacklight = true; //Подсветка горит всегда app->settings.temp_unit = UT_TEMP_CELSIUS; //Единица измерения температуры - градусы Цельсия app->settings.pressure_unit = UT_PRESSURE_MM_HG; //Единица измерения давления - мм рт. ст. + app->settings.heat_index = false; app->gui = furi_record_open(RECORD_GUI); //Диспетчер окон diff --git a/applications/external/unitemp/unitemp.h b/applications/external/unitemp/unitemp.h index 4d184b2c37..3201631269 100644 --- a/applications/external/unitemp/unitemp.h +++ b/applications/external/unitemp/unitemp.h @@ -40,9 +40,9 @@ //Имя приложения #define APP_NAME "Unitemp" //Версия приложения -#define UNITEMP_APP_VER "1.2" +#define UNITEMP_APP_VER "1.4" //Путь хранения файлов плагина -#define APP_PATH_FOLDER "/ext/unitemp" +#define APP_PATH_FOLDER EXT_PATH("apps_data/unitemp") //Имя файла с настройками #define APP_FILENAME_SETTINGS "settings.cfg" //Имя файла с датчиками @@ -80,6 +80,8 @@ typedef struct { tempMeasureUnit temp_unit; //Единица измерения давления pressureMeasureUnit pressure_unit; + // Do calculate and show heat index + bool heat_index; //Последнее состояние OTG bool lastOTGState; } UnitempSettings; @@ -111,6 +113,13 @@ typedef struct { /* Объявление прототипов функций */ +/** + * @brief Calculates the heat index in Celsius from the temperature and humidity and stores it in the sensor heat_index field + * + * @param sensor The sensor struct, with temperature in Celcius and humidity in percent + */ +void unitemp_calculate_heat_index(Sensor* sensor); + /** * @brief Перевод значения температуры датчика из Цельсия в Фаренгейты * diff --git a/applications/external/unitemp/views/General_view.c b/applications/external/unitemp/views/General_view.c index 2c8d389bfa..0a8e8ad178 100644 --- a/applications/external/unitemp/views/General_view.c +++ b/applications/external/unitemp/views/General_view.c @@ -113,6 +113,33 @@ static void _draw_humidity(Canvas* canvas, Sensor* sensor, const uint8_t pos[2]) canvas_draw_str(canvas, pos[0] + 27 + int_len / 2 + 4, pos[1] + 10 + 7, "%"); } +static void _draw_heat_index(Canvas* canvas, Sensor* sensor, const uint8_t pos[2]) { + canvas_draw_rframe(canvas, pos[0], pos[1], 54, 20, 3); + canvas_draw_rframe(canvas, pos[0], pos[1], 54, 19, 3); + + canvas_draw_icon(canvas, pos[0] + 3, pos[1] + 3, &I_heat_index_11x14); + + int16_t heat_index_int = sensor->heat_index; + int8_t heat_index_dec = abs((int16_t)(sensor->heat_index * 10) % 10); + + snprintf(app->buff, BUFF_SIZE, "%d", heat_index_int); + canvas_set_font(canvas, FontBigNumbers); + canvas_draw_str_aligned( + canvas, + pos[0] + 27 + ((sensor->heat_index <= -10 || sensor->heat_index > 99) ? 5 : 0), + pos[1] + 10, + AlignCenter, + AlignCenter, + app->buff); + + if(heat_index_int <= 99) { + uint8_t int_len = canvas_string_width(canvas, app->buff); + snprintf(app->buff, BUFF_SIZE, ".%d", heat_index_dec); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, pos[0] + 27 + int_len / 2 + 2, pos[1] + 10 + 7, app->buff); + } +} + static void _draw_pressure(Canvas* canvas, Sensor* sensor) { const uint8_t x = 29, y = 39; //Рисование рамки @@ -162,6 +189,35 @@ static void _draw_pressure(Canvas* canvas, Sensor* sensor) { } } +static void _draw_co2(Canvas* canvas, Sensor* sensor, Color color) { + const uint8_t x = 29, y = 39; + //Рисование рамки + canvas_draw_rframe(canvas, x, y, 75, 20, 3); + if(color == ColorBlack) { + canvas_draw_rbox(canvas, x, y, 75, 19, 3); + canvas_invert_color(canvas); + } else { + canvas_draw_rframe(canvas, x, y, 75, 19, 3); + } + + //Рисование иконки + canvas_draw_icon(canvas, x + 3, y + 3, &I_co2_11x14); + + int16_t concentration_int = sensor->co2; + // int8_t concentration_dec = (int16_t)(sensor->co2 * 10) % 10; + + //Целая часть + if(concentration_int > 9999) { + snprintf(app->buff, BUFF_SIZE, "MAX "); + canvas_set_font(canvas, FontPrimary); + } else { + snprintf(app->buff, BUFF_SIZE, "%d", concentration_int); + canvas_set_font(canvas, FontBigNumbers); + } + + canvas_draw_str_aligned(canvas, x + 70, y + 10, AlignRight, AlignCenter, app->buff); +} + static void _draw_singleSensor(Canvas* canvas, Sensor* sensor, const uint8_t pos[2], Color color) { canvas_set_font(canvas, FontPrimary); @@ -291,12 +347,23 @@ static void _draw_carousel_values(Canvas* canvas) { ColorWhite); break; case UT_DATA_TYPE_TEMP_HUM: - _draw_temperature( - canvas, - unitemp_sensor_getActive(generalview_sensor_index), - temp_positions[1][0], - temp_positions[1][1], - ColorWhite); + if(!app->settings.heat_index) { + _draw_temperature( + canvas, + unitemp_sensor_getActive(generalview_sensor_index), + temp_positions[1][0], + temp_positions[1][1], + ColorWhite); + } else { + _draw_temperature( + canvas, + unitemp_sensor_getActive(generalview_sensor_index), + temp_positions[2][0], + temp_positions[2][1], + ColorWhite); + _draw_heat_index( + canvas, unitemp_sensor_getActive(generalview_sensor_index), hum_positions[1]); + } _draw_humidity( canvas, unitemp_sensor_getActive(generalview_sensor_index), hum_positions[0]); break; @@ -320,6 +387,17 @@ static void _draw_carousel_values(Canvas* canvas) { canvas, unitemp_sensor_getActive(generalview_sensor_index), hum_positions[1]); _draw_pressure(canvas, unitemp_sensor_getActive(generalview_sensor_index)); break; + case UT_DATA_TYPE_TEMP_HUM_CO2: + _draw_temperature( + canvas, + unitemp_sensor_getActive(generalview_sensor_index), + temp_positions[2][0], + temp_positions[2][1], + ColorWhite); + _draw_humidity( + canvas, unitemp_sensor_getActive(generalview_sensor_index), hum_positions[1]); + _draw_co2(canvas, unitemp_sensor_getActive(generalview_sensor_index), ColorWhite); + break; } } @@ -406,8 +484,8 @@ static void _draw_carousel_info(Canvas* canvas) { ->currentI2CAdr >> 1); canvas_draw_str(canvas, 57, 35, app->buff); - canvas_draw_str(canvas, 54, 46, "15 (C0)"); - canvas_draw_str(canvas, 54, 58, "16 (C1)"); + canvas_draw_str(canvas, 54, 46, "15 (C1)"); + canvas_draw_str(canvas, 54, 58, "16 (C0)"); } } static void _draw_view_sensorsCarousel(Canvas* canvas) { diff --git a/applications/external/unitemp/views/Settings_view.c b/applications/external/unitemp/views/Settings_view.c index e61c6cad6a..3d1eca9069 100644 --- a/applications/external/unitemp/views/Settings_view.c +++ b/applications/external/unitemp/views/Settings_view.c @@ -26,6 +26,7 @@ static VariableItemList* variable_item_list; static const char states[2][9] = {"Auto", "Infinity"}; static const char temp_units[UT_TEMP_COUNT][3] = {"*C", "*F"}; static const char pressure_units[UT_PRESSURE_COUNT][6] = {"mm Hg", "in Hg", "kPa", "hPA"}; +static const char heat_index_bool[2][4] = {"OFF", "ON"}; //Элемент списка - бесконечная подсветка VariableItem* infinity_backlight_item; @@ -33,6 +34,8 @@ VariableItem* infinity_backlight_item; VariableItem* temperature_unit_item; //Единица измерения давления VariableItem* pressure_unit_item; + +VariableItem* heat_index_item; #define VIEW_ID UnitempViewSettings /** @@ -57,6 +60,7 @@ static uint32_t _exit_callback(void* context) { (bool)variable_item_get_current_value_index(infinity_backlight_item); app->settings.temp_unit = variable_item_get_current_value_index(temperature_unit_item); app->settings.pressure_unit = variable_item_get_current_value_index(pressure_unit_item); + app->settings.heat_index = variable_item_get_current_value_index(heat_index_item); unitemp_saveSettings(); unitemp_loadSettings(); @@ -90,6 +94,11 @@ static void _setting_change_callback(VariableItem* item) { pressure_unit_item, pressure_units[variable_item_get_current_value_index(pressure_unit_item)]); } + if(item == heat_index_item) { + variable_item_set_current_value_text( + heat_index_item, + heat_index_bool[variable_item_get_current_value_index(heat_index_item)]); + } } /** @@ -106,6 +115,8 @@ void unitemp_Settings_alloc(void) { variable_item_list_add(variable_item_list, "Temp. unit", 2, _setting_change_callback, app); pressure_unit_item = variable_item_list_add( variable_item_list, "Press. unit", UT_PRESSURE_COUNT, _setting_change_callback, app); + heat_index_item = variable_item_list_add( + variable_item_list, "Calc. heat index", 2, _setting_change_callback, app); //Добавление колбека на нажатие средней кнопки variable_item_list_set_enter_callback(variable_item_list, _enter_callback, app); @@ -139,6 +150,10 @@ void unitemp_Settings_switch(void) { pressure_unit_item, pressure_units[variable_item_get_current_value_index(pressure_unit_item)]); + variable_item_set_current_value_index(heat_index_item, (uint8_t)app->settings.heat_index); + variable_item_set_current_value_text( + heat_index_item, heat_index_bool[variable_item_get_current_value_index(heat_index_item)]); + view_dispatcher_switch_to_view(app->view_dispatcher, VIEW_ID); } diff --git a/applications/external/videopoker/application.fam b/applications/external/videopoker/application.fam index e40953fb4e..1298816482 100644 --- a/applications/external/videopoker/application.fam +++ b/applications/external/videopoker/application.fam @@ -1,5 +1,5 @@ App( - appid="VideoPoker", + appid="videopoker", name="Video Poker", apptype=FlipperAppType.EXTERNAL, entry_point="video_poker_app", @@ -9,4 +9,8 @@ App( order=270, fap_icon="pokerIcon.png", fap_category="Games", + fap_author="@PixlEmly", + fap_weburl="https://github.com/PixlEmly", + fap_version="1.0", + fap_description="Video poker is a casino game based on five-card draw poker", ) diff --git a/applications/external/wav_player/application.fam b/applications/external/wav_player/application.fam index 571e8b2638..cc462d0efd 100644 --- a/applications/external/wav_player/application.fam +++ b/applications/external/wav_player/application.fam @@ -6,6 +6,8 @@ App( stack_size=4 * 1024, order=46, fap_icon="wav_10px.png", - fap_category="Music", - fap_icon_assets="images", + fap_category="Media", + 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/images/music_10px.png b/applications/external/wav_player/images/music_10px.png deleted file mode 100644 index d41eb0db8c..0000000000 Binary files a/applications/external/wav_player/images/music_10px.png and /dev/null differ diff --git a/applications/external/wav_player/wav_player.c b/applications/external/wav_player/wav_player.c index 3a48f41e84..14fd8a1e11 100644 --- a/applications/external/wav_player/wav_player.c +++ b/applications/external/wav_player/wav_player.c @@ -12,11 +12,11 @@ #include "wav_player_view.h" #include -#include +#include #define TAG "WavPlayer" -#define WAVPLAYER_FOLDER "/ext/wav_player" +#define WAVPLAYER_FOLDER EXT_PATH("wav_player") static bool open_wav_stream(Stream* stream) { DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); diff --git a/applications/external/weather_station/images/Fishing_123x52.png b/applications/external/weather_station/images/Fishing_123x52.png deleted file mode 100644 index 1e365de8f4..0000000000 Binary files a/applications/external/weather_station/images/Fishing_123x52.png and /dev/null differ diff --git a/applications/external/weather_station/images/Lock_7x8.png b/applications/external/weather_station/images/Lock_7x8.png deleted file mode 100644 index f7c9ca2c70..0000000000 Binary files a/applications/external/weather_station/images/Lock_7x8.png and /dev/null differ diff --git a/applications/external/weather_station/images/Pin_back_arrow_10x8.png b/applications/external/weather_station/images/Pin_back_arrow_10x8.png deleted file mode 100644 index 3bafabd144..0000000000 Binary files a/applications/external/weather_station/images/Pin_back_arrow_10x8.png and /dev/null differ diff --git a/applications/external/weather_station/images/Quest_7x8.png b/applications/external/weather_station/images/Quest_7x8.png deleted file mode 100644 index 6825247fbe..0000000000 Binary files a/applications/external/weather_station/images/Quest_7x8.png and /dev/null differ diff --git a/applications/external/weather_station/images/Scanning_123x52.png b/applications/external/weather_station/images/Scanning_123x52.png deleted file mode 100644 index ec785948d0..0000000000 Binary files a/applications/external/weather_station/images/Scanning_123x52.png and /dev/null differ diff --git a/applications/external/weather_station/images/Unlock_7x8.png b/applications/external/weather_station/images/Unlock_7x8.png deleted file mode 100644 index 9d82b4daf3..0000000000 Binary files a/applications/external/weather_station/images/Unlock_7x8.png and /dev/null differ diff --git a/applications/external/weather_station/images/WarningDolphin_45x42.png b/applications/external/weather_station/images/WarningDolphin_45x42.png deleted file mode 100644 index d766ffbb44..0000000000 Binary files a/applications/external/weather_station/images/WarningDolphin_45x42.png and /dev/null differ diff --git a/applications/external/weather_station/protocols/oregon3.c b/applications/external/weather_station/protocols/oregon3.c new file mode 100644 index 0000000000..a211c5ad32 --- /dev/null +++ b/applications/external/weather_station/protocols/oregon3.c @@ -0,0 +1,365 @@ +#include "oregon3.h" + +#include +#include +#include +#include +#include "ws_generic.h" + +#include +#include + +#define TAG "WSProtocolOregon3" + +static const SubGhzBlockConst ws_oregon3_const = { + .te_long = 1100, + .te_short = 500, + .te_delta = 300, + .min_count_bit_for_found = 32, +}; + +#define OREGON3_PREAMBLE_BITS 28 +#define OREGON3_PREAMBLE_MASK 0b1111111111111111111111111111 +// 24 ones + 0101 (inverted A) +#define OREGON3_PREAMBLE 0b1111111111111111111111110101 + +// Fixed part contains: +// - Sensor type: 16 bits +// - Channel: 4 bits +// - ID (changes when batteries are changed): 8 bits +// - Battery status: 4 bits +#define OREGON3_FIXED_PART_BITS (16 + 4 + 8 + 4) +#define OREGON3_SENSOR_ID(d) (((d) >> 16) & 0xFFFF) +#define OREGON3_CHECKSUM_BITS 8 + +// bit indicating the low battery +#define OREGON3_FLAG_BAT_LOW 0x4 + +/// Documentation for Oregon Scientific protocols can be found here: +/// https://www.osengr.org/Articles/OS-RF-Protocols-IV.pdf +// Sensors ID +#define ID_THGR221 0xf824 + +struct WSProtocolDecoderOregon3 { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + WSBlockGeneric generic; + ManchesterState manchester_state; + bool prev_bit; + + uint8_t var_bits; + uint64_t var_data; +}; + +typedef struct WSProtocolDecoderOregon3 WSProtocolDecoderOregon3; + +typedef enum { + Oregon3DecoderStepReset = 0, + Oregon3DecoderStepFoundPreamble, + Oregon3DecoderStepVarData, +} Oregon3DecoderStep; + +void* ws_protocol_decoder_oregon3_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + WSProtocolDecoderOregon3* instance = malloc(sizeof(WSProtocolDecoderOregon3)); + instance->base.protocol = &ws_protocol_oregon3; + instance->generic.protocol_name = instance->base.protocol->name; + instance->generic.humidity = WS_NO_HUMIDITY; + instance->generic.temp = WS_NO_TEMPERATURE; + instance->generic.btn = WS_NO_BTN; + instance->generic.channel = WS_NO_CHANNEL; + instance->generic.battery_low = WS_NO_BATT; + instance->generic.id = WS_NO_ID; + instance->prev_bit = false; + return instance; +} + +void ws_protocol_decoder_oregon3_free(void* context) { + furi_assert(context); + WSProtocolDecoderOregon3* instance = context; + free(instance); +} + +void ws_protocol_decoder_oregon3_reset(void* context) { + furi_assert(context); + WSProtocolDecoderOregon3* instance = context; + instance->decoder.parser_step = Oregon3DecoderStepReset; + instance->decoder.decode_data = 0UL; + instance->decoder.decode_count_bit = 0; + manchester_advance( + instance->manchester_state, ManchesterEventReset, &instance->manchester_state, NULL); + instance->prev_bit = false; + instance->var_data = 0; + instance->var_bits = 0; +} + +static ManchesterEvent level_and_duration_to_event(bool level, uint32_t duration) { + bool is_long = false; + + if(DURATION_DIFF(duration, ws_oregon3_const.te_long) < ws_oregon3_const.te_delta) { + is_long = true; + } else if(DURATION_DIFF(duration, ws_oregon3_const.te_short) < ws_oregon3_const.te_delta) { + is_long = false; + } else { + return ManchesterEventReset; + } + + if(level) + return is_long ? ManchesterEventLongHigh : ManchesterEventShortHigh; + else + return is_long ? ManchesterEventLongLow : ManchesterEventShortLow; +} + +// From sensor id code return amount of bits in variable section +// https://temofeev.ru/info/articles/o-dekodirovanii-protokola-pogodnykh-datchikov-oregon-scientific +static uint8_t oregon3_sensor_id_var_bits(uint16_t sensor_id) { + switch(sensor_id) { + case ID_THGR221: + default: + // nibbles: temp + hum + '0' + return (4 + 2 + 1) * 4; + } +} + +static void ws_oregon3_decode_const_data(WSBlockGeneric* ws_block) { + ws_block->id = OREGON3_SENSOR_ID(ws_block->data); + ws_block->channel = (ws_block->data >> 12) & 0xF; + ws_block->battery_low = (ws_block->data & OREGON3_FLAG_BAT_LOW) ? 1 : 0; +} + +static uint16_t ws_oregon3_bcd_decode_short(uint32_t data) { + return (data & 0xF) * 10 + ((data >> 4) & 0xF); +} + +static float ws_oregon3_decode_temp(uint32_t data) { + int32_t temp_val; + temp_val = ws_oregon3_bcd_decode_short(data >> 4); + temp_val *= 10; + temp_val += (data >> 12) & 0xF; + if(data & 0xF) temp_val = -temp_val; + return (float)temp_val / 10.0; +} + +static void ws_oregon3_decode_var_data(WSBlockGeneric* ws_b, uint16_t sensor_id, uint32_t data) { + switch(sensor_id) { + case ID_THGR221: + default: + ws_b->humidity = ws_oregon3_bcd_decode_short(data >> 4); + ws_b->temp = ws_oregon3_decode_temp(data >> 12); + break; + } +} + +void ws_protocol_decoder_oregon3_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + WSProtocolDecoderOregon3* instance = context; + // Oregon v3.0 protocol is inverted + ManchesterEvent event = level_and_duration_to_event(!level, duration); + + // low-level bit sequence decoding + if(event == ManchesterEventReset) { + instance->decoder.parser_step = Oregon3DecoderStepReset; + instance->prev_bit = false; + instance->decoder.decode_data = 0UL; + instance->decoder.decode_count_bit = 0; + } + if(manchester_advance( + instance->manchester_state, event, &instance->manchester_state, &instance->prev_bit)) { + subghz_protocol_blocks_add_bit(&instance->decoder, instance->prev_bit); + } + + switch(instance->decoder.parser_step) { + case Oregon3DecoderStepReset: + // waiting for fixed oregon3 preamble + if(instance->decoder.decode_count_bit >= OREGON3_PREAMBLE_BITS && + ((instance->decoder.decode_data & OREGON3_PREAMBLE_MASK) == OREGON3_PREAMBLE)) { + instance->decoder.parser_step = Oregon3DecoderStepFoundPreamble; + instance->decoder.decode_count_bit = 0; + instance->decoder.decode_data = 0UL; + } + break; + case Oregon3DecoderStepFoundPreamble: + // waiting for fixed oregon3 data + if(instance->decoder.decode_count_bit == OREGON3_FIXED_PART_BITS) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + instance->decoder.decode_data = 0UL; + instance->decoder.decode_count_bit = 0; + + // reverse nibbles in decoded data as oregon v3.0 is LSB first + instance->generic.data = (instance->generic.data & 0x55555555) << 1 | + (instance->generic.data & 0xAAAAAAAA) >> 1; + instance->generic.data = (instance->generic.data & 0x33333333) << 2 | + (instance->generic.data & 0xCCCCCCCC) >> 2; + + ws_oregon3_decode_const_data(&instance->generic); + instance->var_bits = + oregon3_sensor_id_var_bits(OREGON3_SENSOR_ID(instance->generic.data)); + + if(!instance->var_bits) { + // sensor is not supported, stop decoding, but showing the decoded fixed part + instance->decoder.parser_step = Oregon3DecoderStepReset; + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } else { + instance->decoder.parser_step = Oregon3DecoderStepVarData; + } + } + break; + case Oregon3DecoderStepVarData: + // waiting for variable (sensor-specific data) + if(instance->decoder.decode_count_bit == instance->var_bits + OREGON3_CHECKSUM_BITS) { + instance->var_data = instance->decoder.decode_data & 0xFFFFFFFFFFFFFFFF; + + // reverse nibbles in var data + instance->var_data = (instance->var_data & 0x5555555555555555) << 1 | + (instance->var_data & 0xAAAAAAAAAAAAAAAA) >> 1; + instance->var_data = (instance->var_data & 0x3333333333333333) << 2 | + (instance->var_data & 0xCCCCCCCCCCCCCCCC) >> 2; + + ws_oregon3_decode_var_data( + &instance->generic, + OREGON3_SENSOR_ID(instance->generic.data), + instance->var_data >> OREGON3_CHECKSUM_BITS); + + instance->decoder.parser_step = Oregon3DecoderStepReset; + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + break; + } +} + +uint8_t ws_protocol_decoder_oregon3_get_hash_data(void* context) { + furi_assert(context); + WSProtocolDecoderOregon3* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +SubGhzProtocolStatus ws_protocol_decoder_oregon3_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + WSProtocolDecoderOregon3* instance = context; + SubGhzProtocolStatus ret = SubGhzProtocolStatusError; + ret = ws_block_generic_serialize(&instance->generic, flipper_format, preset); + if(ret != SubGhzProtocolStatusOk) return ret; + uint32_t temp = instance->var_bits; + if(!flipper_format_write_uint32(flipper_format, "VarBits", &temp, 1)) { + FURI_LOG_E(TAG, "Error adding VarBits"); + return SubGhzProtocolStatusErrorParserOthers; + } + if(!flipper_format_write_hex( + flipper_format, + "VarData", + (const uint8_t*)&instance->var_data, + sizeof(instance->var_data))) { + FURI_LOG_E(TAG, "Error adding VarData"); + return SubGhzProtocolStatusErrorParserOthers; + } + return ret; +} + +SubGhzProtocolStatus + ws_protocol_decoder_oregon3_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + WSProtocolDecoderOregon3* instance = context; + uint32_t temp_data; + SubGhzProtocolStatus ret = SubGhzProtocolStatusError; + do { + ret = ws_block_generic_deserialize(&instance->generic, flipper_format); + if(ret != SubGhzProtocolStatusOk) { + break; + } + if(!flipper_format_read_uint32(flipper_format, "VarBits", &temp_data, 1)) { + FURI_LOG_E(TAG, "Missing VarLen"); + ret = SubGhzProtocolStatusErrorParserOthers; + break; + } + instance->var_bits = (uint8_t)temp_data; + if(!flipper_format_read_hex( + flipper_format, + "VarData", + (uint8_t*)&instance->var_data, + sizeof(instance->var_data))) { //-V1051 + FURI_LOG_E(TAG, "Missing VarData"); + ret = SubGhzProtocolStatusErrorParserOthers; + break; + } + if(instance->generic.data_count_bit != ws_oregon3_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key: %d", instance->generic.data_count_bit); + ret = SubGhzProtocolStatusErrorValueBitCount; + break; + } + } while(false); + return ret; +} + +static void oregon3_append_check_sum(uint32_t fix_data, uint64_t var_data, FuriString* output) { + uint8_t sum = fix_data & 0xF; + uint8_t ref_sum = var_data & 0xFF; + var_data >>= 4; + + for(uint8_t i = 1; i < 8; i++) { + fix_data >>= 4; + var_data >>= 4; + sum += (fix_data & 0xF) + (var_data & 0xF); + } + + // swap calculated sum nibbles + sum = (((sum >> 4) & 0xF) | (sum << 4)) & 0xFF; + if(sum == ref_sum) + furi_string_cat_printf(output, "Sum ok: 0x%hhX", ref_sum); + else + furi_string_cat_printf(output, "Sum err: 0x%hhX vs 0x%hhX", ref_sum, sum); +} + +void ws_protocol_decoder_oregon3_get_string(void* context, FuriString* output) { + furi_assert(context); + WSProtocolDecoderOregon3* instance = context; + furi_string_cat_printf( + output, + "%s\r\n" + "ID: 0x%04lX, ch: %d, bat: %d, rc: 0x%02lX\r\n", + instance->generic.protocol_name, + instance->generic.id, + instance->generic.channel, + instance->generic.battery_low, + (uint32_t)(instance->generic.data >> 4) & 0xFF); + + if(instance->var_bits > 0) { + furi_string_cat_printf( + output, + "Temp:%d.%d C Hum:%d%%", + (int16_t)instance->generic.temp, + abs( + ((int16_t)(instance->generic.temp * 10) - + (((int16_t)instance->generic.temp) * 10))), + instance->generic.humidity); + oregon3_append_check_sum((uint32_t)instance->generic.data, instance->var_data, output); + } +} + +const SubGhzProtocolDecoder ws_protocol_oregon3_decoder = { + .alloc = ws_protocol_decoder_oregon3_alloc, + .free = ws_protocol_decoder_oregon3_free, + + .feed = ws_protocol_decoder_oregon3_feed, + .reset = ws_protocol_decoder_oregon3_reset, + + .get_hash_data = ws_protocol_decoder_oregon3_get_hash_data, + .serialize = ws_protocol_decoder_oregon3_serialize, + .deserialize = ws_protocol_decoder_oregon3_deserialize, + .get_string = ws_protocol_decoder_oregon3_get_string, +}; + +const SubGhzProtocol ws_protocol_oregon3 = { + .name = WS_PROTOCOL_OREGON3_NAME, + .type = SubGhzProtocolWeatherStation, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, + + .decoder = &ws_protocol_oregon3_decoder, +}; diff --git a/applications/external/weather_station/protocols/oregon3.h b/applications/external/weather_station/protocols/oregon3.h new file mode 100644 index 0000000000..ec51ddb000 --- /dev/null +++ b/applications/external/weather_station/protocols/oregon3.h @@ -0,0 +1,6 @@ +#pragma once + +#include + +#define WS_PROTOCOL_OREGON3_NAME "Oregon3" +extern const SubGhzProtocol ws_protocol_oregon3; diff --git a/applications/external/weather_station/protocols/protocol_items.c b/applications/external/weather_station/protocols/protocol_items.c index cd4bae76dc..93dc25488d 100644 --- a/applications/external/weather_station/protocols/protocol_items.c +++ b/applications/external/weather_station/protocols/protocol_items.c @@ -11,6 +11,7 @@ const SubGhzProtocol* weather_station_protocol_registry_items[] = { &ws_protocol_lacrosse_tx, &ws_protocol_lacrosse_tx141thbv2, &ws_protocol_oregon2, + &ws_protocol_oregon3, &ws_protocol_acurite_592txr, &ws_protocol_ambient_weather, &ws_protocol_auriol_th, @@ -21,4 +22,4 @@ const SubGhzProtocol* weather_station_protocol_registry_items[] = { const SubGhzProtocolRegistry weather_station_protocol_registry = { .items = weather_station_protocol_registry_items, - .size = COUNT_OF(weather_station_protocol_registry_items)}; \ No newline at end of file + .size = COUNT_OF(weather_station_protocol_registry_items)}; diff --git a/applications/external/weather_station/protocols/protocol_items.h b/applications/external/weather_station/protocols/protocol_items.h index 0398c11f25..712eb07f22 100644 --- a/applications/external/weather_station/protocols/protocol_items.h +++ b/applications/external/weather_station/protocols/protocol_items.h @@ -11,6 +11,7 @@ #include "lacrosse_tx.h" #include "lacrosse_tx141thbv2.h" #include "oregon2.h" +#include "oregon3.h" #include "acurite_592txr.h" #include "ambient_weather.h" #include "auriol_hg0601a.h" diff --git a/applications/external/weather_station/views/weather_station_receiver.c b/applications/external/weather_station/views/weather_station_receiver.c index e994e7830e..22f5343f1c 100644 --- a/applications/external/weather_station/views/weather_station_receiver.c +++ b/applications/external/weather_station/views/weather_station_receiver.c @@ -1,6 +1,7 @@ #include "weather_station_receiver.h" #include "../weather_station_app_i.h" #include +#include #include #include diff --git a/applications/external/weather_station/views/weather_station_receiver_info.c b/applications/external/weather_station/views/weather_station_receiver_info.c index b3b3f21939..c58993a98f 100644 --- a/applications/external/weather_station/views/weather_station_receiver_info.c +++ b/applications/external/weather_station/views/weather_station_receiver_info.c @@ -1,6 +1,7 @@ #include "weather_station_receiver.h" #include "../weather_station_app_i.h" #include "weather_station_icons.h" +#include #include "../protocols/ws_generic.h" #include #include diff --git a/applications/external/wifi_deauther/application.fam b/applications/external/wifi_deauther/application.fam index 07ceb543b7..17c6978b9c 100644 --- a/applications/external/wifi_deauther/application.fam +++ b/applications/external/wifi_deauther/application.fam @@ -1,5 +1,5 @@ App( - appid="ESP8266_Wifi_Deauther_v2", + appid="esp8266_wifi_deauther_v2", name="[ESP8266] Deauther v2", apptype=FlipperAppType.EXTERNAL, entry_point="wifi_deauther_app", @@ -9,4 +9,8 @@ App( order=30, fap_icon="wifi_10px.png", fap_category="WiFi", + fap_author="@Timmotools & @xMasterX", + fap_weburl="https://github.com/Timmotools/flipperzero_esp8266_deautherv2", + fap_version="1.0", + fap_description="Works with ESP8266 Deauther v2 by @SpacehuhnTech (github)", ) diff --git a/applications/external/wifi_deauther/wifi_deauther_app.c b/applications/external/wifi_deauther/wifi_deauther_app.c index 2c9ea17fc4..2e05a1ce0b 100644 --- a/applications/external/wifi_deauther/wifi_deauther_app.c +++ b/applications/external/wifi_deauther/wifi_deauther_app.c @@ -25,7 +25,7 @@ static void wifi_deauther_app_tick_event_callback(void* context) { WifideautherApp* wifi_deauther_app_alloc() { WifideautherApp* app = malloc(sizeof(WifideautherApp)); - DOLPHIN_DEED(DolphinDeedPluginStart); + dolphin_deed(DolphinDeedPluginStart); app->gui = furi_record_open(RECORD_GUI); diff --git a/applications/external/wifi_marauder_companion/assets/DolphinCommon_56x48.png b/applications/external/wifi_marauder_companion/assets/DolphinCommon_56x48.png deleted file mode 100644 index 089aaed835..0000000000 Binary files a/applications/external/wifi_marauder_companion/assets/DolphinCommon_56x48.png and /dev/null differ diff --git a/applications/external/wifi_marauder_companion/assets/KeyBackspaceSelected_16x9.png b/applications/external/wifi_marauder_companion/assets/KeyBackspaceSelected_16x9.png deleted file mode 100644 index 7cc0759a8c..0000000000 Binary files a/applications/external/wifi_marauder_companion/assets/KeyBackspaceSelected_16x9.png and /dev/null differ diff --git a/applications/external/wifi_marauder_companion/assets/KeyBackspace_16x9.png b/applications/external/wifi_marauder_companion/assets/KeyBackspace_16x9.png deleted file mode 100644 index 9946232d95..0000000000 Binary files a/applications/external/wifi_marauder_companion/assets/KeyBackspace_16x9.png and /dev/null differ diff --git a/applications/external/wifi_marauder_companion/assets/KeyKeyboardSelected_10x11.png b/applications/external/wifi_marauder_companion/assets/KeyKeyboardSelected_10x11.png deleted file mode 100644 index 231880386a..0000000000 Binary files a/applications/external/wifi_marauder_companion/assets/KeyKeyboardSelected_10x11.png and /dev/null differ diff --git a/applications/external/wifi_marauder_companion/assets/KeyKeyboard_10x11.png b/applications/external/wifi_marauder_companion/assets/KeyKeyboard_10x11.png deleted file mode 100644 index 1f4c03478f..0000000000 Binary files a/applications/external/wifi_marauder_companion/assets/KeyKeyboard_10x11.png and /dev/null differ diff --git a/applications/external/wifi_marauder_companion/assets/KeySaveSelected_24x11.png b/applications/external/wifi_marauder_companion/assets/KeySaveSelected_24x11.png deleted file mode 100644 index eeb3569d3a..0000000000 Binary files a/applications/external/wifi_marauder_companion/assets/KeySaveSelected_24x11.png and /dev/null differ diff --git a/applications/external/wifi_marauder_companion/assets/KeySave_24x11.png b/applications/external/wifi_marauder_companion/assets/KeySave_24x11.png deleted file mode 100644 index e7dba987a0..0000000000 Binary files a/applications/external/wifi_marauder_companion/assets/KeySave_24x11.png and /dev/null differ diff --git a/applications/external/wifi_marauder_companion/assets/WarningDolphin_45x42.png b/applications/external/wifi_marauder_companion/assets/WarningDolphin_45x42.png deleted file mode 100644 index d766ffbb44..0000000000 Binary files a/applications/external/wifi_marauder_companion/assets/WarningDolphin_45x42.png and /dev/null differ diff --git a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_select.c b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_select.c index bc07468589..ffdc2ded05 100644 --- a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_select.c +++ b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_select.c @@ -1,5 +1,7 @@ #include "../wifi_marauder_app_i.h" +#define MAX_NAME_LEN 254 + static void wifi_marauder_scene_script_select_callback(void* context, uint32_t index) { WifiMarauderApp* app = context; @@ -35,10 +37,10 @@ void wifi_marauder_scene_script_select_on_enter(void* context) { File* dir_scripts = storage_file_alloc(app->storage); if(storage_dir_open(dir_scripts, MARAUDER_APP_FOLDER_SCRIPTS)) { FileInfo file_info; - char file_path[255]; + char file_path[MAX_NAME_LEN]; app->script_list_count = 0; // Goes through the files in the folder counting the ones that end with the json extension - while(storage_dir_read(dir_scripts, &file_info, file_path, 255)) { + while(storage_dir_read(dir_scripts, &file_info, file_path, MAX_NAME_LEN)) { app->script_list_count++; } if(app->script_list_count > 0) { @@ -48,7 +50,7 @@ void wifi_marauder_scene_script_select_on_enter(void* context) { storage_dir_open(dir_scripts, MARAUDER_APP_FOLDER_SCRIPTS); // Read the files again from the beginning, adding the scripts in the list int script_index = 0; - while(storage_dir_read(dir_scripts, &file_info, file_path, 255)) { + while(storage_dir_read(dir_scripts, &file_info, file_path, MAX_NAME_LEN)) { app->script_list[script_index] = furi_string_alloc(); path_extract_filename_no_ext(file_path, app->script_list[script_index]); submenu_add_item( diff --git a/applications/external/wifi_marauder_companion/wifi_marauder_app.c b/applications/external/wifi_marauder_companion/wifi_marauder_app.c index 821987a8ac..c7f03061af 100644 --- a/applications/external/wifi_marauder_companion/wifi_marauder_app.c +++ b/applications/external/wifi_marauder_companion/wifi_marauder_app.c @@ -24,7 +24,7 @@ static void wifi_marauder_app_tick_event_callback(void* context) { WifiMarauderApp* wifi_marauder_app_alloc() { WifiMarauderApp* app = malloc(sizeof(WifiMarauderApp)); - DOLPHIN_DEED(DolphinDeedPluginStart); + dolphin_deed(DolphinDeedPluginStart); app->gui = furi_record_open(RECORD_GUI); app->dialogs = furi_record_open(RECORD_DIALOGS); diff --git a/applications/external/wifi_marauder_companion/wifi_marauder_app_i.h b/applications/external/wifi_marauder_companion/wifi_marauder_app_i.h index d6a0d37c70..9e24d39642 100644 --- a/applications/external/wifi_marauder_companion/wifi_marauder_app_i.h +++ b/applications/external/wifi_marauder_companion/wifi_marauder_app_i.h @@ -22,6 +22,7 @@ #include "wifi_marauder_text_input.h" #include +#include #include #include #include diff --git a/applications/external/wifi_marauder_companion/wifi_marauder_uart.c b/applications/external/wifi_marauder_companion/wifi_marauder_uart.c index 5ce6480f2d..080280b5ff 100644 --- a/applications/external/wifi_marauder_companion/wifi_marauder_uart.c +++ b/applications/external/wifi_marauder_companion/wifi_marauder_uart.c @@ -106,6 +106,9 @@ void wifi_marauder_uart_free(WifiMarauderUart* uart) { furi_thread_free(uart->rx_thread); furi_hal_uart_set_irq_cb(uart->channel, NULL, NULL); + if(uart->channel == FuriHalUartIdLPUART1) { + furi_hal_uart_deinit(uart->channel); + } furi_hal_console_enable(); free(uart); diff --git a/applications/external/wifi_scanner/application.fam b/applications/external/wifi_scanner/application.fam index 0f33384b9b..b8a441a2e6 100644 --- a/applications/external/wifi_scanner/application.fam +++ b/applications/external/wifi_scanner/application.fam @@ -1,6 +1,6 @@ App( appid="wifi_scanner", - name="[WiFi] Scanner", + name="[ESP] WiFi Scanner", apptype=FlipperAppType.EXTERNAL, entry_point="wifi_scanner_app", requires=["gui"], @@ -8,4 +8,7 @@ App( order=70, fap_icon="wifi_10px.png", fap_category="WiFi", + fap_author="@SequoiaSan & @xMasterX", + fap_version="1.0", + fap_description="WiFi scanner module interface, based on ESP8266", ) diff --git a/applications/external/wiiec/_image_tool/README b/applications/external/wiiec/_image_tool/README deleted file mode 100644 index 979605a08f..0000000000 --- a/applications/external/wiiec/_image_tool/README +++ /dev/null @@ -1,30 +0,0 @@ -1. Prepare the image - a. Open your *black and white* image in GIMP - b. File -> Export As - filename: EXAMPLE.c - Type : "C source code" - [Export] - prefixed name: gimp_image - Comment : - [x] Use GLib types - [ ] <> - Opacity : 100% - [Export] - -2. Prepare conversion tool [stored in (eg.) /path/] - a. cp _convert*.* /path/ - b. cp EXAMPLE.c /path/ - -3. Run the conversion tool - a. cd /path/ - b. ./_convert.sh EXAMPLE.c - -4. All being well, you will see an ascii version of your image. - If not, then you're gonna have to submit a bug report - -5. You should now have a directory called img_/ - In that directory should be - img_EXAMPLE.c - The data for your new image - img_*.c - The data for other images - images.h - A header for ALL images that have been created in this directory - images.c - A sample FlipperZero show() function [not optimised] diff --git a/applications/external/wiiec/_image_tool/_convert.sh b/applications/external/wiiec/_image_tool/_convert.sh deleted file mode 100755 index aaa7977b58..0000000000 --- a/applications/external/wiiec/_image_tool/_convert.sh +++ /dev/null @@ -1,79 +0,0 @@ -#!/bin/bash - -[ -z $1 ] && { - echo "Specify an image" - echo "gimp -> export -> c source file -> [x] gunit names" - exit 2 -} - -echo $* - -for N in $* ; do - - [ ! -f $N ] && { - echo "!! File missing $N" - continue - } - - # filename (sans extension) - FN=$(basename -- "$N") - EXT="${FN##*.}" - NAME="${FN%.*}" - - OUTDIR=img_/ - mkdir -p ${OUTDIR} - - HDR=${OUTDIR}/images.h - SRC=${OUTDIR}/images.c - - OUT=${OUTDIR}/img_${NAME}.c - - echo -e "\n¦${N}¦ == ¦${NAME}¦ -> ¦${OUT}¦" - - TESTX=test_${NAME} - TESTC=test_${NAME}.c - - # compile name - CONV=${NAME}_ - - # clean up gimp output - sed -e "s/gimp_image/img/g" \ - -e 's/guint8/unsigned char/g' \ - -e 's/width/w/g' \ - -e 's/height/h/g' \ - -e 's/bytes_per_pixel/bpp/g' \ - -e 's/pixel_data/b/g' \ - -e 's/guint/unsigned int/g' \ - $N \ - | grep -v ^/ \ - | grep -v ^$ \ - > ${CONV}.c - - # append conversion code - cat _convert.c >> ${CONV}.c - - # compile & run converter - rm -f ${CONV} - gcc ${CONV}.c -DIMGTEST -o ${CONV} - ./${CONV} ${NAME} ${OUT} - rm -f ${CONV} ${CONV}.c - - # (create &) update header - [[ ! -f ${HDR} ]] && cp _convert_images.h ${HDR} - sed -i "/ img_${NAME};/d" ${HDR} - sed -i "s#//\[TAG\]#//\[TAG\]\nextern const image_t img_${NAME};#" ${HDR} - - # sample FZ code - [[ ! -f images.c ]] && cp _convert_images.c ${SRC} - - # test - ROOT=${PWD} - pushd ${OUTDIR} >/dev/null - sed "s/zzz/${NAME}/" ${ROOT}/_convert_test.c > ${TESTC} - rm -f ${TESTX} - gcc ${TESTC} ${OUT##*/} -DIMGTEST -o ${TESTX} - ./${TESTX} - rm -f ${TESTX} ${TESTC} - popd >/dev/null - -done diff --git a/applications/external/wiiec/_image_tool/_convert_images.c b/applications/external/wiiec/_image_tool/_convert_images.c deleted file mode 100644 index e8ab899f75..0000000000 --- a/applications/external/wiiec/_image_tool/_convert_images.c +++ /dev/null @@ -1,137 +0,0 @@ -#include // GUI (screen/keyboard) API - -#include "images.h" - -//----------------------------------------------------------------------------- ---------------------------------------- -static Canvas* _canvas; -static uint8_t _tlx; -static uint8_t _tly; - -static uint8_t _x; -static uint8_t _y; - -static const image_t* _img; - -static bool _blk; -static Color _set; -static Color _clr; - -//+============================================================================ -static void _showByteSet(const uint8_t b) { - for(uint8_t m = 0x80; m; m >>= 1) { - if(b & m) // plot only SET bits - canvas_draw_dot(_canvas, (_tlx + _x), (_tly + _y)); - if(((++_x) == _img->w) && !(_x = 0) && ((++_y) == _img->h)) break; - } -} - -//+============================================================================ -static void _showByteClr(const uint8_t b) { - for(uint8_t m = 0x80; m; m >>= 1) { - if(!(b & m)) // plot only CLR bits - canvas_draw_dot(_canvas, (_tlx + _x), (_tly + _y)); - if(((++_x) == _img->w) && !(_x = 0) && ((++_y) == _img->h)) break; - } -} - -//+============================================================================ -static void _showByteAll(const uint8_t b) { - for(uint8_t m = 0x80; m; m >>= 1) { - if((!!(b & m)) ^ _blk) { // Change colour only when required - canvas_set_color(_canvas, ((b & m) ? _set : _clr)); - _blk = !_blk; - } - canvas_draw_dot(_canvas, (_tlx + _x), (_tly + _y)); - if(((++_x) == _img->w) && !(_x = 0) && ((++_y) == _img->h)) break; - } -} - -//+============================================================================ -// available modes are SHOW_SET_BLK - plot image pixels that are SET in BLACK -// SHOW_XOR - same as SET_BLACK -// SHOW_SET_WHT - plot image pixels that are SET in WHITE -// SHOW_CLR_BLK - plot image pixels that are CLEAR in BLACK -// SHOW_CLR_WHT - plot image pixels that are CLEAR in WHITE -// SHOW_ALL - plot all images pixels as they are -// SHOW_ALL_INV - plot all images pixels inverted -// -void show( - Canvas* const canvas, - const uint8_t tlx, - const uint8_t tly, - const image_t* img, - const showMode_t mode) { - void (*fnShow)(const uint8_t) = NULL; - - const uint8_t* bp = img->data; - - // code size optimisation - switch(mode & SHOW_INV_) { - case SHOW_NRM_: - _set = ColorBlack; - _clr = ColorWhite; - break; - - case SHOW_INV_: - _set = ColorWhite; - _clr = ColorBlack; - break; - - case SHOW_BLK_: - canvas_set_color(canvas, ColorBlack); - break; - - case SHOW_WHT_: - canvas_set_color(canvas, ColorWhite); - break; - } - switch(mode & SHOW_INV_) { - case SHOW_NRM_: - case SHOW_INV_: - fnShow = _showByteAll; - canvas_set_color(canvas, ColorWhite); - _blk = 0; - break; - - case SHOW_BLK_: - case SHOW_WHT_: - switch(mode & SHOW_ALL_) { - case SHOW_SET_: - fnShow = _showByteSet; - break; - case SHOW_CLR_: - fnShow = _showByteClr; - break; - } - break; - } - furi_check(fnShow); - - // I want nested functions! - _canvas = canvas; - _img = img; - _tlx = tlx; - _tly = tly; - _x = 0; - _y = 0; - - // Compressed - if(img->c) { - for(unsigned int i = 0; i < img->len; i++, bp++) { - // Compressed data? {tag, length, value} - if(*bp == img->tag) { - for(uint16_t c = 0; c < bp[1]; c++) fnShow(bp[2]); - bp += 3 - 1; - i += 3 - 1; - - // Uncompressed byte - } else { - fnShow(*bp); - } - } - - // Not compressed - } else { - for(unsigned int i = 0; i < img->len; i++, bp++) fnShow(*bp); - } -} diff --git a/applications/external/wiiec/_image_tool/_convert_images.h b/applications/external/wiiec/_image_tool/_convert_images.h deleted file mode 100644 index 1743cb4094..0000000000 --- a/applications/external/wiiec/_image_tool/_convert_images.h +++ /dev/null @@ -1,53 +0,0 @@ -#ifndef IMAGES_H_ -#define IMAGES_H_ - -#include -#include - -//----------------------------------------------------------------------------- ---------------------------------------- -typedef enum showMode { - // {INV:--:WHT:BLK::--:--:CLR:SET} - SHOW_SET_ = 0x01, - SHOW_CLR_ = 0x02, - SHOW_ALL_ = SHOW_SET_ | SHOW_CLR_, - - SHOW_BLK_ = 0x10, - SHOW_WHT_ = 0x20, - SHOW_NRM_ = 0x00, - SHOW_INV_ = SHOW_BLK_ | SHOW_WHT_, - - SHOW_SET_BLK = SHOW_SET_ | SHOW_BLK_, - SHOW_SET_WHT = SHOW_SET_ | SHOW_WHT_, - - SHOW_CLR_BLK = SHOW_CLR_ | SHOW_BLK_, - SHOW_CLR_WHT = SHOW_CLR_ | SHOW_WHT_, - - SHOW_ALL = SHOW_ALL_ | SHOW_NRM_, - SHOW_ALL_INV = SHOW_ALL_ | SHOW_INV_, -} showMode_t; - -//----------------------------------------------------------------------------- ---------------------------------------- -typedef struct image { - uint8_t w; // width - uint8_t h; // height - bool c; // compressed? - uint16_t len; // image data length - uint8_t tag; // rle tag - uint8_t data[]; // image data -} image_t; - -//----------------------------------------------------------------------------- ---------------------------------------- -//[TAG] - -//----------------------------------------------------------------------------- ---------------------------------------- -#ifndef IMGTEST -#include -void show( - Canvas* const canvas, - const uint8_t tlx, - const uint8_t tly, - const image_t* img, - const showMode_t mode); -#endif - -#endif //IMAGES_H_ diff --git a/applications/external/wiiec/application.fam b/applications/external/wiiec/application.fam index 0f0ee08e0f..e84bba6208 100644 --- a/applications/external/wiiec/application.fam +++ b/applications/external/wiiec/application.fam @@ -3,7 +3,7 @@ App( # --- App Info appid="wii_ec_anal", - name="[GPIO] Wii EC Analyser", + name="[WII] EC Analyser", # --- Entry point apptype=FlipperAppType.EXTERNAL, entry_point="wii_ec_anal", @@ -25,4 +25,8 @@ App( # fap_version=(1,0), fap_icon="WiiEC.png", fap_category="GPIO", + fap_author="@csBlueChip", + fap_weburl="https://github.com/csBlueChip/FlipperZero_WiiEC", + fap_version="1.0", + fap_description="Application to test Wii Extension Controllers.", ) diff --git a/applications/external/wiiec/info.sh b/applications/external/wiiec/info.sh deleted file mode 100755 index e009eb1187..0000000000 --- a/applications/external/wiiec/info.sh +++ /dev/null @@ -1,11 +0,0 @@ -echo "MARKED AS TODO" -echo "==============" -grep //! *.c *.h - -echo -e "\nSUPPORTED CONTROLLERS" -echo "=====================" -grep '\[PID_.*{ {' wii_ec.c | head -n -3 | sed 's/\s*\(.*\)/\1/' - -echo -e "\nLOGGING" -echo "=======" -grep LOG_LEVEL *.h | grep -v '#if ' diff --git a/applications/external/yatzee/application.fam b/applications/external/yatzee/application.fam index 1c59d634b9..5885082f14 100644 --- a/applications/external/yatzee/application.fam +++ b/applications/external/yatzee/application.fam @@ -6,7 +6,11 @@ App( requires=["gui"], stack_size=4 * 1024, order=99, - fap_icon="images/yatzee_icon_10px.png", + fap_icon="yatzee_icon_10px.png", fap_category="Games", fap_icon_assets="images", + fap_author="@emfleak", + fap_weburl="https://github.com/emfleak/flipperzero-yatzee", + fap_version="1.0", + fap_description="Yahtzee game", ) diff --git a/applications/external/yatzee/images/yatzee_icon_10px.png b/applications/external/yatzee/yatzee_icon_10px.png similarity index 100% rename from applications/external/yatzee/images/yatzee_icon_10px.png rename to applications/external/yatzee/yatzee_icon_10px.png diff --git a/applications/external/zombiez/application.fam b/applications/external/zombiez/application.fam index 69f5a9379c..ca61208997 100644 --- a/applications/external/zombiez/application.fam +++ b/applications/external/zombiez/application.fam @@ -8,4 +8,7 @@ App( order=280, fap_icon="zombie_10px.png", fap_category="Games", + fap_author="@DevMilanIan & @xMasterX, (original By @Dooskington)", + fap_version="1.0", + fap_description="Defend your walls from the zombies", ) diff --git a/applications/external/zombiez/zombiez.c b/applications/external/zombiez/zombiez.c index 590edf97d4..291e76966d 100644 --- a/applications/external/zombiez/zombiez.c +++ b/applications/external/zombiez/zombiez.c @@ -231,7 +231,7 @@ static void tick(PluginState* const plugin_state) { free(z); plugin_state->zombies[i] = NULL; plugin_state->score++; - //if(plugin_state->score % 15 == 0) DOLPHIN_DEED(getRandomDeed()); + //if(plugin_state->score % 15 == 0) dolphin_deed(getRandomDeed()); //} } else if(z->position.x <= WALL_X && z->position.x > 0) { // zombie got to the wall plugin_state->zombies_count -= 1; @@ -313,6 +313,9 @@ int32_t zombiez_game_app(void* p) { Gui* gui = furi_record_open(RECORD_GUI); gui_add_view_port(gui, view_port, GuiLayerFullscreen); + // Call dolphin deed on game start + // dolphin_deed(DolphinDeedPluginGameStart); + PluginEvent event; bool isRunning = true; while(isRunning) { diff --git a/applications/main/application.fam b/applications/main/application.fam index 0066709475..f3409d5c9b 100644 --- a/applications/main/application.fam +++ b/applications/main/application.fam @@ -3,7 +3,6 @@ App( name="Basic applications for main menu", apptype=FlipperAppType.METAPACKAGE, provides=[ - "fap_loader", "subghz", "lfrfid", "nfc", diff --git a/applications/main/archive/archive.c b/applications/main/archive/archive.c index 588087fd21..2f77eb47cb 100644 --- a/applications/main/archive/archive.c +++ b/applications/main/archive/archive.c @@ -22,6 +22,7 @@ static ArchiveApp* archive_alloc() { ArchiveApp* archive = malloc(sizeof(ArchiveApp)); archive->fav_move_str = furi_string_alloc(); + archive->file_extension = furi_string_alloc(); archive->scene_manager = scene_manager_alloc(&archive_scene_handlers, archive); archive->view_dispatcher = view_dispatcher_alloc(); @@ -50,6 +51,11 @@ static ArchiveApp* archive_alloc() { view_dispatcher, ArchiveViewStack, view_stack_get_view(archive->view_stack)); archive->browser = browser_alloc(); + with_view_model( + archive->browser->view, + ArchiveBrowserViewModel * model, + { model->archive = archive; }, + true); view_dispatcher_add_view( archive->view_dispatcher, ArchiveViewBrowser, archive_browser_get_view(archive->browser)); @@ -63,6 +69,14 @@ void archive_free(ArchiveApp* archive) { furi_assert(archive); ViewDispatcher* view_dispatcher = archive->view_dispatcher; + scene_manager_set_scene_state(archive->scene_manager, ArchiveAppSceneInfo, false); + scene_manager_set_scene_state(archive->scene_manager, ArchiveAppSceneSearch, false); + if(archive->thread) { + furi_thread_join(archive->thread); + furi_thread_free(archive->thread); + archive->thread = NULL; + } + // Loading loading_free(archive->loading); @@ -82,6 +96,7 @@ void archive_free(ArchiveApp* archive) { browser_free(archive->browser); furi_string_free(archive->fav_move_str); + furi_string_free(archive->file_extension); furi_record_close(RECORD_DIALOGS); archive->dialogs = NULL; diff --git a/applications/main/archive/archive_i.h b/applications/main/archive/archive_i.h index b961458277..8d60d63a23 100644 --- a/applications/main/archive/archive_i.h +++ b/applications/main/archive/archive_i.h @@ -8,9 +8,11 @@ #include #include #include +#include #include #include #include +#include #include #include "views/archive_browser_view.h" @@ -38,7 +40,10 @@ struct ArchiveApp { FuriString* fav_move_str; char text_store[MAX_NAME_LEN]; - char file_extension[MAX_EXT_LEN + 1]; + FuriString* file_extension; + + WidgetElement* element; + FuriThread* thread; }; -void archive_show_loading_popup(ArchiveApp* context, bool show); \ No newline at end of file +void archive_show_loading_popup(ArchiveApp* context, bool show); diff --git a/applications/main/archive/helpers/archive_apps.c b/applications/main/archive/helpers/archive_apps.c index c8ad67625f..267525ca3e 100644 --- a/applications/main/archive/helpers/archive_apps.c +++ b/applications/main/archive/helpers/archive_apps.c @@ -1,9 +1,11 @@ #include "archive_files.h" #include "archive_apps.h" #include "archive_browser.h" +#include static const char* known_apps[] = { [ArchiveAppTypeU2f] = "u2f", + [ArchiveAppTypeSearch] = "search", }; ArchiveAppTypeEnum archive_get_app_type(const char* path) { @@ -26,20 +28,23 @@ bool archive_app_is_available(void* context, const char* path) { furi_assert(path); ArchiveAppTypeEnum app = archive_get_app_type(path); + bool res = false; - if(app == ArchiveAppTypeU2f) { - bool file_exists = false; - Storage* storage = furi_record_open(RECORD_STORAGE); - - if(storage_file_exists(storage, ANY_PATH("u2f/key.u2f"))) { - file_exists = storage_file_exists(storage, ANY_PATH("u2f/cnt.u2f")); - } - - furi_record_close(RECORD_STORAGE); - return file_exists; - } else { - return false; + Storage* storage = furi_record_open(RECORD_STORAGE); + switch(app) { + case ArchiveAppTypeU2f: + res = storage_file_exists(storage, U2F_KEY_FILE) && + storage_file_exists(storage, U2F_CNT_FILE); + break; + case ArchiveAppTypeSearch: + res = true; + break; + default: + break; } + furi_record_close(RECORD_STORAGE); + + return res; } bool archive_app_read_dir(void* context, const char* path) { @@ -47,14 +52,17 @@ bool archive_app_read_dir(void* context, const char* path) { furi_assert(path); ArchiveBrowserView* browser = context; - archive_file_array_rm_all(browser); - ArchiveAppTypeEnum app = archive_get_app_type(path); - if(app == ArchiveAppTypeU2f) { + switch(app) { + case ArchiveAppTypeU2f: + archive_file_array_rm_all(browser); archive_add_app_item(browser, "/app:u2f/U2F Token"); return true; - } else { + case ArchiveAppTypeSearch: + return true; + default: + archive_file_array_rm_all(browser); return false; } } @@ -67,16 +75,21 @@ void archive_app_delete_file(void* context, const char* path) { ArchiveAppTypeEnum app = archive_get_app_type(path); bool res = false; - if(app == ArchiveAppTypeU2f) { - Storage* fs_api = furi_record_open(RECORD_STORAGE); - res = (storage_common_remove(fs_api, ANY_PATH("u2f/key.u2f")) == FSE_OK); - res |= (storage_common_remove(fs_api, ANY_PATH("u2f/cnt.u2f")) == FSE_OK); - furi_record_close(RECORD_STORAGE); - + Storage* fs_api = furi_record_open(RECORD_STORAGE); + switch(app) { + case ArchiveAppTypeU2f: + res = (storage_common_remove(fs_api, U2F_KEY_FILE) == FSE_OK); + res |= (storage_common_remove(fs_api, U2F_CNT_FILE) == FSE_OK); if(archive_is_favorite("/app:u2f/U2F Token")) { archive_favorites_delete("/app:u2f/U2F Token"); } + break; + case ArchiveAppTypeSearch: + break; + default: + break; } + furi_record_close(RECORD_STORAGE); if(res) { archive_file_array_rm_selected(browser); diff --git a/applications/main/archive/helpers/archive_apps.h b/applications/main/archive/helpers/archive_apps.h index 8bc904587b..2e3535e5d4 100644 --- a/applications/main/archive/helpers/archive_apps.h +++ b/applications/main/archive/helpers/archive_apps.h @@ -2,12 +2,14 @@ typedef enum { ArchiveAppTypeU2f, + ArchiveAppTypeSearch, ArchiveAppTypeUnknown, ArchiveAppsTotal, } ArchiveAppTypeEnum; static const ArchiveFileTypeEnum app_file_types[] = { [ArchiveAppTypeU2f] = ArchiveFileTypeU2f, + [ArchiveAppTypeSearch] = ArchiveFileTypeSearch, [ArchiveAppTypeUnknown] = ArchiveFileTypeUnknown, }; diff --git a/applications/main/archive/helpers/archive_browser.c b/applications/main/archive/helpers/archive_browser.c index 15c9d97f42..c0325ff16e 100644 --- a/applications/main/archive/helpers/archive_browser.c +++ b/applications/main/archive/helpers/archive_browser.c @@ -6,7 +6,7 @@ #include #include #include -#include +#include #include #include @@ -28,7 +28,7 @@ static void { files_array_reset(model->files); model->item_cnt = item_cnt; - model->item_idx = (file_idx > 0) ? file_idx : 0; + model->item_idx = file_idx; load_offset = CLAMP(model->item_idx - FILE_LIST_BUF_LEN / 2, (int32_t)model->item_cnt, 0); model->array_offset = 0; @@ -75,11 +75,31 @@ static void archive_list_item_cb( ArchiveBrowserViewModel * model, { if(model->item_cnt <= BROWSER_SORT_THRESHOLD) { + FuriString* selected = NULL; + if(model->item_idx >= 0) { + selected = furi_string_alloc_set( + files_array_get(model->files, model->item_idx)->path); + } + files_array_sort(model->files); + + if(selected != NULL) { + for(uint32_t i = 0; i < model->item_cnt; i++) { + if(!furi_string_cmp(files_array_get(model->files, i)->path, selected)) { + model->item_idx = i; + break; + } + } + } + + if(model->item_idx < 0) { + model->item_idx = 0; + } } model->list_loading = false; }, true); + archive_update_offset(browser); } } @@ -385,7 +405,7 @@ void archive_add_app_item(ArchiveBrowserView* browser, const char* name) { static bool archive_get_fap_meta(FuriString* file_path, FuriString* fap_name, uint8_t** icon_ptr) { Storage* storage = furi_record_open(RECORD_STORAGE); bool success = false; - if(fap_loader_load_name_and_icon(file_path, storage, icon_ptr, fap_name)) { + if(flipper_application_load_name_and_icon(file_path, storage, icon_ptr, fap_name)) { success = true; } furi_record_close(RECORD_STORAGE); @@ -449,7 +469,8 @@ void archive_favorites_move_mode(ArchiveBrowserView* browser, bool active) { } static bool archive_is_dir_exists(FuriString* path) { - if(furi_string_equal(path, STORAGE_ANY_PATH_PREFIX)) { + if(furi_string_equal(path, STORAGE_INT_PATH_PREFIX) || + furi_string_equal(path, STORAGE_EXT_PATH_PREFIX)) { return true; } bool state = false; @@ -468,19 +489,28 @@ void archive_switch_tab(ArchiveBrowserView* browser, InputKey key) { furi_assert(browser); ArchiveTabEnum tab = archive_get_tab(browser); + if(tab == ArchiveTabSearch) { + ArchiveApp* archive; + with_view_model( + browser->view, ArchiveBrowserViewModel * model, { archive = model->archive; }, false); + scene_manager_set_scene_state(archive->scene_manager, ArchiveAppSceneSearch, false); + if(archive->thread) { + furi_thread_join(archive->thread); + furi_thread_free(archive->thread); + archive->thread = NULL; + } + } + browser->last_tab_switch_dir = key; - if(key == InputKeyLeft) { - tab = ((tab - 1) + ArchiveTabTotal) % ArchiveTabTotal; - } else { - tab = (tab + 1) % ArchiveTabTotal; - } - if(tab == ArchiveTabInternal && !furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + for(int i = 0; i < 2; i++) { if(key == InputKeyLeft) { tab = ((tab - 1) + ArchiveTabTotal) % ArchiveTabTotal; } else { tab = (tab + 1) % ArchiveTabTotal; } + if(tab == ArchiveTabInternal && !XTREME_SETTINGS()->show_internal_tab) continue; + break; } browser->is_root = true; @@ -488,32 +518,40 @@ void archive_switch_tab(ArchiveBrowserView* browser, InputKey key) { furi_string_set(browser->path, archive_get_default_path(tab)); bool tab_empty = true; + bool is_app_tab = furi_string_start_with_str(browser->path, "/app:"); if(tab == ArchiveTabFavorites) { - if(archive_favorites_count(browser) > 0) { + if(archive_favorites_count() > 0) { tab_empty = false; } - } else if(furi_string_start_with_str(browser->path, "/app:")) { + } else if(is_app_tab) { char* app_name = strchr(furi_string_get_cstr(browser->path), ':'); if(app_name != NULL) { if(archive_app_is_available(browser, furi_string_get_cstr(browser->path))) { tab_empty = false; + if(tab == ArchiveTabSearch) { + archive_file_array_rm_all(browser); + archive_add_app_item(browser, "/app:search/Search for files"); + archive_set_item_count(browser, 1); + } } } } else { tab = archive_get_tab(browser); if(archive_is_dir_exists(browser->path)) { - bool skip_assets = (strcmp(archive_get_tab_ext(tab), "*") == 0) ? false : true; + bool is_browser = !strcmp(archive_get_tab_ext(tab), "*"); + bool skip_assets = !is_browser; // Hide dot files everywhere except Browser if in debug mode - bool hide_dot_files = (strcmp(archive_get_tab_ext(tab), "*") == 0) ? - !furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug) : - true; + bool hide_dot_files = !is_browser ? true : + tab == ArchiveTabInternal ? + false : + !XTREME_SETTINGS()->show_hidden_files; archive_file_browser_set_path( browser, browser->path, archive_get_tab_ext(tab), skip_assets, hide_dot_files); tab_empty = false; // Empty check will be performed later } } - if((tab_empty) && (tab != ArchiveTabBrowser)) { + if(tab_empty && tab != ArchiveTabBrowser && tab != ArchiveTabInternal) { archive_switch_tab(browser, key); } else { with_view_model( @@ -522,6 +560,7 @@ void archive_switch_tab(ArchiveBrowserView* browser, InputKey key) { { model->item_idx = 0; model->array_offset = 0; + model->is_app_tab = is_app_tab; }, false); archive_get_items(browser, furi_string_get_cstr(browser->path)); @@ -533,14 +572,32 @@ void archive_enter_dir(ArchiveBrowserView* browser, FuriString* path) { furi_assert(browser); furi_assert(path); - int32_t idx_temp = 0; - - with_view_model( - browser->view, ArchiveBrowserViewModel * model, { idx_temp = model->item_idx; }, false); - furi_string_set(browser->path, path); - file_browser_worker_folder_enter(browser->worker, path, idx_temp); + const char* switch_ext = NULL; + switch(archive_get_tab(browser)) { + case ArchiveTabSubGhz: + if(furi_string_cmp_str(browser->path, EXT_PATH("subghz/playlist")) == 0) { + switch_ext = known_ext[ArchiveFileTypeSubghzPlaylist]; + } else if(furi_string_cmp_str(browser->path, EXT_PATH("subghz/remote")) == 0) { + switch_ext = known_ext[ArchiveFileTypeSubghzRemote]; + } + break; + case ArchiveTabInfrared: + if(furi_string_cmp_str(browser->path, EXT_PATH("infrared/remote")) == 0) { + switch_ext = known_ext[ArchiveFileTypeInfraredRemote]; + } + break; + default: + break; + } + + if(switch_ext != NULL && + strcmp(switch_ext, file_browser_worker_get_filter_ext(browser->worker)) != 0) { + file_browser_worker_set_filter_ext(browser->worker, browser->path, switch_ext); + } else { + file_browser_worker_folder_enter(browser->worker, path, 0); + } } void archive_leave_dir(ArchiveBrowserView* browser) { @@ -549,15 +606,38 @@ void archive_leave_dir(ArchiveBrowserView* browser) { size_t dirname_start = furi_string_search_rchar(browser->path, '/'); furi_string_left(browser->path, dirname_start); - file_browser_worker_folder_exit(browser->worker); + const char* switch_ext = NULL; + switch(archive_get_tab(browser)) { + case ArchiveTabSubGhz: + if(furi_string_cmp_str(browser->path, EXT_PATH("subghz")) == 0) { + switch_ext = known_ext[ArchiveFileTypeSubGhz]; + } + break; + case ArchiveTabInfrared: + if(furi_string_cmp_str(browser->path, EXT_PATH("infrared")) == 0) { + switch_ext = known_ext[ArchiveFileTypeInfrared]; + } + break; + default: + break; + } + + if(switch_ext != NULL && + strcmp(switch_ext, file_browser_worker_get_filter_ext(browser->worker)) != 0) { + file_browser_worker_set_filter_ext(browser->worker, browser->path, switch_ext); + } else { + file_browser_worker_folder_exit(browser->worker); + } } void archive_refresh_dir(ArchiveBrowserView* browser) { furi_assert(browser); - int32_t idx_temp = 0; - - with_view_model( - browser->view, ArchiveBrowserViewModel * model, { idx_temp = model->item_idx; }, false); - file_browser_worker_folder_refresh(browser->worker, idx_temp); + ArchiveFile_t* current = archive_get_current_file(browser); + FuriString* str = furi_string_alloc(); + if(current != NULL) { + path_extract_basename(furi_string_get_cstr(current->path), str); + } + file_browser_worker_folder_refresh(browser->worker, furi_string_get_cstr(str)); + furi_string_free(str); } diff --git a/applications/main/archive/helpers/archive_browser.h b/applications/main/archive/helpers/archive_browser.h index ec3ef00cea..8074d2532b 100644 --- a/applications/main/archive/helpers/archive_browser.h +++ b/applications/main/archive/helpers/archive_browser.h @@ -9,16 +9,17 @@ static const char* tab_default_paths[] = { [ArchiveTabFavorites] = "/app:favorites", - [ArchiveTabIButton] = ANY_PATH("ibutton"), - [ArchiveTabNFC] = ANY_PATH("nfc"), - [ArchiveTabSubGhz] = ANY_PATH("subghz"), - [ArchiveTabLFRFID] = ANY_PATH("lfrfid"), - [ArchiveTabInfrared] = ANY_PATH("infrared"), - [ArchiveTabBadKb] = ANY_PATH("badkb"), + [ArchiveTabIButton] = EXT_PATH("ibutton"), + [ArchiveTabNFC] = EXT_PATH("nfc"), + [ArchiveTabSubGhz] = EXT_PATH("subghz"), + [ArchiveTabLFRFID] = EXT_PATH("lfrfid"), + [ArchiveTabInfrared] = EXT_PATH("infrared"), + [ArchiveTabBadKb] = EXT_PATH("badkb"), [ArchiveTabU2f] = "/app:u2f", - [ArchiveTabApplications] = ANY_PATH("apps"), + [ArchiveTabApplications] = EXT_PATH("apps"), + [ArchiveTabSearch] = "/app:search", [ArchiveTabInternal] = STORAGE_INT_PATH_PREFIX, - [ArchiveTabBrowser] = STORAGE_ANY_PATH_PREFIX, + [ArchiveTabBrowser] = STORAGE_EXT_PATH_PREFIX, }; static const char* known_ext[] = { @@ -27,9 +28,13 @@ static const char* known_ext[] = { [ArchiveFileTypeSubGhz] = ".sub", [ArchiveFileTypeLFRFID] = ".rfid", [ArchiveFileTypeInfrared] = ".ir", + [ArchiveFileTypeSubghzPlaylist] = ".txt", + [ArchiveFileTypeSubghzRemote] = ".txt", + [ArchiveFileTypeInfraredRemote] = ".txt", [ArchiveFileTypeBadKb] = ".txt", [ArchiveFileTypeU2f] = "?", [ArchiveFileTypeApplication] = ".fap", + [ArchiveFileTypeSearch] = "*", [ArchiveFileTypeUpdateManifest] = ".fuf", [ArchiveFileTypeFolder] = "?", [ArchiveFileTypeUnknown] = "*", @@ -45,6 +50,7 @@ static const ArchiveFileTypeEnum known_type[] = { [ArchiveTabBadKb] = ArchiveFileTypeBadKb, [ArchiveTabU2f] = ArchiveFileTypeU2f, [ArchiveTabApplications] = ArchiveFileTypeApplication, + [ArchiveTabSearch] = ArchiveFileTypeSearch, [ArchiveTabInternal] = ArchiveFileTypeUnknown, [ArchiveTabBrowser] = ArchiveFileTypeUnknown, }; @@ -62,7 +68,7 @@ static inline const char* archive_get_default_path(ArchiveTabEnum tab) { } inline bool archive_is_known_app(ArchiveFileTypeEnum type) { - return (type != ArchiveFileTypeUnknown); + return (type < ArchiveFileTypeUnknown); } bool archive_is_item_in_array(ArchiveBrowserViewModel* model, uint32_t idx); diff --git a/applications/main/archive/helpers/archive_favorites.c b/applications/main/archive/helpers/archive_favorites.c index c7b058235e..7efeeb4d19 100644 --- a/applications/main/archive/helpers/archive_favorites.c +++ b/applications/main/archive/helpers/archive_favorites.c @@ -46,9 +46,7 @@ static bool archive_favorites_read_line(File* file, FuriString* str_result) { return result; } -uint16_t archive_favorites_count(void* context) { - furi_assert(context); - +uint16_t archive_favorites_count() { Storage* fs_api = furi_record_open(RECORD_STORAGE); File* file = storage_file_alloc(fs_api); @@ -59,10 +57,7 @@ uint16_t archive_favorites_count(void* context) { uint16_t lines = 0; if(result) { - while(1) { - if(!archive_favorites_read_line(file, buffer)) { - break; - } + while(archive_favorites_read_line(file, buffer)) { if(!furi_string_size(buffer)) { continue; // Skip empty lines } @@ -87,10 +82,7 @@ static bool archive_favourites_rescan() { bool result = storage_file_open(file, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING); if(result) { - while(1) { - if(!archive_favorites_read_line(file, buffer)) { - break; - } + while(archive_favorites_read_line(file, buffer)) { if(!furi_string_size(buffer)) { continue; } @@ -112,7 +104,9 @@ static bool archive_favourites_rescan() { furi_string_free(buffer); storage_file_close(file); - storage_common_move(storage, ARCHIVE_FAV_TEMP_PATH, ARCHIVE_FAV_PATH); + if(storage_common_move(storage, ARCHIVE_FAV_TEMP_PATH, ARCHIVE_FAV_PATH) == FSE_NOT_EXIST) { + storage_common_remove(storage, ARCHIVE_FAV_PATH); + } storage_file_free(file); furi_record_close(RECORD_STORAGE); @@ -139,10 +133,7 @@ bool archive_favorites_read(void* context) { bool result = storage_file_open(file, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING); if(result) { - while(1) { - if(!archive_favorites_read_line(file, buffer)) { - break; - } + while(archive_favorites_read_line(file, buffer)) { if(!furi_string_size(buffer)) { continue; } @@ -197,10 +188,7 @@ bool archive_favorites_delete(const char* format, ...) { bool result = storage_file_open(file, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING); if(result) { - while(1) { - if(!archive_favorites_read_line(file, buffer)) { - break; - } + while(archive_favorites_read_line(file, buffer)) { if(!furi_string_size(buffer)) { continue; } @@ -215,7 +203,9 @@ bool archive_favorites_delete(const char* format, ...) { furi_string_free(filename); storage_file_close(file); - storage_common_move(fs_api, ARCHIVE_FAV_TEMP_PATH, ARCHIVE_FAV_PATH); + if(storage_common_move(fs_api, ARCHIVE_FAV_TEMP_PATH, ARCHIVE_FAV_PATH) == FSE_NOT_EXIST) { + storage_common_remove(fs_api, ARCHIVE_FAV_PATH); + } storage_file_free(file); furi_record_close(RECORD_STORAGE); @@ -239,10 +229,7 @@ bool archive_is_favorite(const char* format, ...) { bool result = storage_file_open(file, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING); if(result) { - while(1) { - if(!archive_favorites_read_line(file, buffer)) { - break; - } + while(archive_favorites_read_line(file, buffer)) { if(!furi_string_size(buffer)) { continue; } @@ -279,10 +266,7 @@ bool archive_favorites_rename(const char* src, const char* dst) { bool result = storage_file_open(file, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING); if(result) { - while(1) { - if(!archive_favorites_read_line(file, buffer)) { - break; - } + while(archive_favorites_read_line(file, buffer)) { if(!furi_string_size(buffer)) { continue; } @@ -298,7 +282,9 @@ bool archive_favorites_rename(const char* src, const char* dst) { furi_string_free(path); storage_file_close(file); - storage_common_move(fs_api, ARCHIVE_FAV_TEMP_PATH, ARCHIVE_FAV_PATH); + if(storage_common_move(fs_api, ARCHIVE_FAV_TEMP_PATH, ARCHIVE_FAV_PATH) == FSE_NOT_EXIST) { + storage_common_remove(fs_api, ARCHIVE_FAV_PATH); + } storage_file_free(file); furi_record_close(RECORD_STORAGE); @@ -324,7 +310,9 @@ void archive_favorites_save(void* context) { archive_file_append(ARCHIVE_FAV_TEMP_PATH, "%s\n", furi_string_get_cstr(item->path)); } - storage_common_move(fs_api, ARCHIVE_FAV_TEMP_PATH, ARCHIVE_FAV_PATH); + if(storage_common_move(fs_api, ARCHIVE_FAV_TEMP_PATH, ARCHIVE_FAV_PATH) == FSE_NOT_EXIST) { + storage_common_remove(fs_api, ARCHIVE_FAV_PATH); + } storage_file_free(file); furi_record_close(RECORD_STORAGE); diff --git a/applications/main/archive/helpers/archive_favorites.h b/applications/main/archive/helpers/archive_favorites.h index e45af92e74..dfeb18e763 100644 --- a/applications/main/archive/helpers/archive_favorites.h +++ b/applications/main/archive/helpers/archive_favorites.h @@ -6,7 +6,7 @@ #define ARCHIVE_FAV_PATH CFG_PATH("favorites.txt") #define ARCHIVE_FAV_TEMP_PATH CFG_PATH("favorites.tmp") -uint16_t archive_favorites_count(void* context); +uint16_t archive_favorites_count(); bool archive_favorites_read(void* context); bool archive_favorites_delete(const char* format, ...) _ATTRIBUTE((__format__(__printf__, 1, 2))); bool archive_is_favorite(const char* format, ...) _ATTRIBUTE((__format__(__printf__, 1, 2))); diff --git a/applications/main/archive/helpers/archive_files.c b/applications/main/archive/helpers/archive_files.c index 11a30d956e..66d5478866 100644 --- a/applications/main/archive/helpers/archive_files.c +++ b/applications/main/archive/helpers/archive_files.c @@ -1,6 +1,9 @@ #include "archive_files.h" #include "archive_apps.h" #include "archive_browser.h" +#include +#include +#include #define TAG "Archive" @@ -15,12 +18,27 @@ void archive_set_file_type(ArchiveFile_t* file, const char* path, bool is_folder } else { for(size_t i = 0; i < COUNT_OF(known_ext); i++) { if((known_ext[i][0] == '?') || (known_ext[i][0] == '*')) continue; - if(furi_string_search(file->path, known_ext[i], 0) != FURI_STRING_FAILURE) { - if(i == ArchiveFileTypeBadKb) { - if(furi_string_search(file->path, archive_get_default_path(ArchiveTabBadKb)) == - 0) { + if(furi_string_end_with_str(file->path, known_ext[i])) { + // Check for .txt containing folder + if(strcmp(known_ext[i], ".txt") == 0) { + const char* txt_path = NULL; + switch(i) { + case ArchiveFileTypeSubghzPlaylist: + txt_path = PLAYLIST_FOLDER; + break; + case ArchiveFileTypeSubghzRemote: + txt_path = SUBREM_APP_FOLDER; + break; + case ArchiveFileTypeInfraredRemote: + txt_path = IR_REMOTE_PATH; + break; + case ArchiveFileTypeBadKb: + txt_path = archive_get_default_path(ArchiveTabBadKb); + break; + } + if(txt_path != NULL && furi_string_start_with_str(file->path, txt_path)) { file->type = i; - return; // *.txt file is a BadKB script only if it is in BadKB folder + return; } } else { file->type = i; @@ -32,11 +50,6 @@ void archive_set_file_type(ArchiveFile_t* file, const char* path, bool is_folder if(is_folder) { file->type = ArchiveFileTypeFolder; } else { - char tmp_extension[MAX_EXT_LEN]; - path_extract_extension(file->path, tmp_extension, MAX_EXT_LEN); - if((strcmp(tmp_extension, ".txt") == 0) || (strcmp(tmp_extension, ".md") == 0)) { - file->is_text_file = true; - } file->type = ArchiveFileTypeUnknown; } } @@ -118,66 +131,65 @@ void archive_delete_file(void* context, const char* format, ...) { FS_Error archive_copy_rename_file_or_dir( void* context, const char* src_path, - const char* dst_path, + FuriString* dst_path, bool copy, bool find_name) { furi_assert(context); + const char* dst_cstr = furi_string_get_cstr(dst_path); - FURI_LOG_I(TAG, "%s from %s to %s", copy ? "Copy" : "Move", src_path, dst_path); + FURI_LOG_I(TAG, "%s from %s to %s", copy ? "Copy" : "Move", src_path, dst_cstr); - ArchiveBrowserView* browser = context; + UNUSED(context); Storage* fs_api = furi_record_open(RECORD_STORAGE); - FuriString* dst_str = furi_string_alloc_set(dst_path); - dst_path = furi_string_get_cstr(dst_str); FileInfo fileinfo; storage_common_stat(fs_api, src_path, &fileinfo); FS_Error error = FSE_OK; - if(!path_contains_only_ascii(dst_path)) { + if(!path_contains_only_ascii(dst_cstr)) { error = FSE_INVALID_NAME; - } else if(!copy && !strcmp(src_path, dst_path)) { + } else if(!copy && !strcmp(src_path, dst_cstr)) { error = FSE_EXIST; } else { - if(find_name && storage_common_exists(fs_api, dst_path)) { + if(find_name && storage_common_exists(fs_api, dst_cstr)) { FuriString* dir_path = furi_string_alloc(); FuriString* filename = furi_string_alloc(); - char extension[MAX_EXT_LEN] = {0}; + FuriString* file_ext = furi_string_alloc(); - path_extract_filename(dst_str, filename, true); - path_extract_dirname(furi_string_get_cstr(dst_str), dir_path); - path_extract_extension(dst_str, extension, MAX_EXT_LEN); + path_extract_dirname(dst_cstr, dir_path); + path_extract_filename(dst_path, filename, true); + path_extract_ext_str(dst_path, file_ext); storage_get_next_filename( fs_api, furi_string_get_cstr(dir_path), furi_string_get_cstr(filename), - extension, - dst_str, + furi_string_get_cstr(file_ext), + dst_path, 255); - furi_string_cat_printf(dir_path, "/%s%s", furi_string_get_cstr(dst_str), extension); - furi_string_set(dst_str, dir_path); + furi_string_cat_printf(dir_path, "/%s%s", dst_cstr, furi_string_get_cstr(file_ext)); + furi_string_set(dst_path, dir_path); furi_string_free(dir_path); furi_string_free(filename); + furi_string_free(file_ext); } if(copy) { - error = storage_common_copy(fs_api, src_path, dst_path); + error = storage_common_copy(fs_api, src_path, dst_cstr); } else { - error = storage_common_rename(fs_api, src_path, dst_path); + error = storage_common_rename(fs_api, src_path, dst_cstr); } } furi_record_close(RECORD_STORAGE); if(!copy && archive_is_favorite("%s", src_path)) { - archive_favorites_rename(src_path, dst_path); + archive_favorites_rename(src_path, dst_cstr); } if(error == FSE_OK) { - FURI_LOG_I(TAG, "%s from %s to %s is DONE", copy ? "Copy" : "Move", src_path, dst_path); - archive_refresh_dir(browser); + FURI_LOG_I(TAG, "%s from %s to %s is DONE", copy ? "Copy" : "Move", src_path, dst_cstr); } else { FURI_LOG_E( TAG, @@ -187,7 +199,5 @@ FS_Error archive_copy_rename_file_or_dir( error); } - furi_string_free(dst_str); - return error; } diff --git a/applications/main/archive/helpers/archive_files.h b/applications/main/archive/helpers/archive_files.h index d54b99994e..84a35ec306 100644 --- a/applications/main/archive/helpers/archive_files.h +++ b/applications/main/archive/helpers/archive_files.h @@ -15,9 +15,13 @@ typedef enum { ArchiveFileTypeSubGhz, ArchiveFileTypeLFRFID, ArchiveFileTypeInfrared, + ArchiveFileTypeSubghzPlaylist, + ArchiveFileTypeSubghzRemote, + ArchiveFileTypeInfraredRemote, ArchiveFileTypeBadKb, ArchiveFileTypeU2f, ArchiveFileTypeApplication, + ArchiveFileTypeSearch, ArchiveFileTypeUpdateManifest, ArchiveFileTypeFolder, ArchiveFileTypeUnknown, @@ -31,7 +35,6 @@ typedef struct { FuriString* custom_name; bool fav; bool is_app; - bool is_text_file; } ArchiveFile_t; static void ArchiveFile_t_init(ArchiveFile_t* obj) { @@ -41,7 +44,6 @@ static void ArchiveFile_t_init(ArchiveFile_t* obj) { obj->custom_name = furi_string_alloc(); obj->fav = false; obj->is_app = false; - obj->is_text_file = false; } static void ArchiveFile_t_init_set(ArchiveFile_t* obj, const ArchiveFile_t* src) { @@ -56,7 +58,6 @@ static void ArchiveFile_t_init_set(ArchiveFile_t* obj, const ArchiveFile_t* src) obj->custom_name = furi_string_alloc_set(src->custom_name); obj->fav = src->fav; obj->is_app = src->is_app; - obj->is_text_file = src->is_text_file; } static void ArchiveFile_t_set(ArchiveFile_t* obj, const ArchiveFile_t* src) { @@ -71,7 +72,6 @@ static void ArchiveFile_t_set(ArchiveFile_t* obj, const ArchiveFile_t* src) { furi_string_set(obj->custom_name, src->custom_name); obj->fav = src->fav; obj->is_app = src->is_app; - obj->is_text_file = src->is_text_file; } static void ArchiveFile_t_clear(ArchiveFile_t* obj) { @@ -120,6 +120,6 @@ void archive_delete_file(void* context, const char* format, ...) FS_Error archive_copy_rename_file_or_dir( void* context, const char* src_path, - const char* dst_path, + FuriString* dst_path, bool copy, bool find_name); diff --git a/applications/main/archive/helpers/archive_menu.h b/applications/main/archive/helpers/archive_menu.h index 4a85b851d3..683c6e6fcf 100644 --- a/applications/main/archive/helpers/archive_menu.h +++ b/applications/main/archive/helpers/archive_menu.h @@ -43,8 +43,8 @@ ARRAY_DEF( #pragma GCC diagnostic ignored "-Wunused-function" // Using in applications/archive/views/archive_browser_view.c static void - archive_menu_add_item(ArchiveContextMenuItem_t* obj, FuriString* text, uint32_t event) { + archive_menu_add_item(ArchiveContextMenuItem_t* obj, const char* text, uint32_t event) { obj->text = furi_string_alloc_set(text); obj->event = event; } -#pragma GCC diagnostic pop \ No newline at end of file +#pragma GCC diagnostic pop diff --git a/applications/main/archive/scenes/archive_scene_browser.c b/applications/main/archive/scenes/archive_scene_browser.c index bf9079be4b..285b1ebf92 100644 --- a/applications/main/archive/scenes/archive_scene_browser.c +++ b/applications/main/archive/scenes/archive_scene_browser.c @@ -11,18 +11,34 @@ #define SCENE_STATE_DEFAULT (0) #define SCENE_STATE_NEED_REFRESH (1) -static const char* flipper_app_name[] = { - [ArchiveFileTypeIButton] = "iButton", - [ArchiveFileTypeNFC] = "NFC", - [ArchiveFileTypeSubGhz] = "SubGHz", - [ArchiveFileTypeLFRFID] = "RFID", - [ArchiveFileTypeInfrared] = "Infrared", - [ArchiveFileTypeBadKb] = "Bad KB", - [ArchiveFileTypeU2f] = "U2F", - [ArchiveFileTypeApplication] = "Apps", - [ArchiveFileTypeUpdateManifest] = "UpdaterApp", - [ArchiveFileTypeFolder] = "Archive", -}; +const char* archive_get_flipper_app_name(ArchiveFileTypeEnum file_type) { + switch(file_type) { + case ArchiveFileTypeIButton: + return "iButton"; + case ArchiveFileTypeNFC: + return "NFC"; + case ArchiveFileTypeSubGhz: + return "SubGHz"; + case ArchiveFileTypeLFRFID: + return "RFID"; + case ArchiveFileTypeInfrared: + return "Infrared"; + case ArchiveFileTypeSubghzPlaylist: + return EXT_PATH("apps/Sub-Ghz/subghz_playlist.fap"); + case ArchiveFileTypeSubghzRemote: + return EXT_PATH("apps/Sub-Ghz/subghz_remote.fap"); + case ArchiveFileTypeInfraredRemote: + return EXT_PATH("apps/Tools/ir_remote.fap"); + case ArchiveFileTypeBadKb: + return "Bad KB"; + case ArchiveFileTypeU2f: + return "U2F"; + case ArchiveFileTypeUpdateManifest: + return "UpdaterApp"; + default: + return NULL; + } +} static void archive_loader_callback(const void* message, void* context) { furi_assert(message); @@ -41,26 +57,38 @@ static void UNUSED(browser); Loader* loader = furi_record_open(RECORD_LOADER); - LoaderStatus status; - if(selected->is_app) { - char* param = strrchr(furi_string_get_cstr(selected->path), '/'); - if(param != NULL) { - param++; + const char* app_name = archive_get_flipper_app_name(selected->type); + + if(selected->type == ArchiveFileTypeSearch) { + while(archive_get_tab(browser) != ArchiveTabSearch) { + archive_switch_tab(browser, TAB_LEFT); } - status = loader_start(loader, flipper_app_name[selected->type], param); - } else { - const char* str = furi_string_get_cstr(selected->path); - if(favorites) { - char arg[strlen(str) + 4]; - snprintf(arg, sizeof(arg), "fav%s", str); - status = loader_start(loader, flipper_app_name[selected->type], arg); + ArchiveApp* archive; + with_view_model( + browser->view, ArchiveBrowserViewModel * model, { archive = model->archive; }, false); + view_dispatcher_send_custom_event(archive->view_dispatcher, ArchiveBrowserEventSearch); + } else if(app_name) { + if(selected->is_app) { + char* param = strrchr(furi_string_get_cstr(selected->path), '/'); + if(param != NULL) { + param++; + } + loader_start_with_gui_error(loader, app_name, param); } else { - status = loader_start(loader, flipper_app_name[selected->type], str); + const char* str = furi_string_get_cstr(selected->path); + if(favorites && + (selected->type == ArchiveFileTypeIButton || + selected->type == ArchiveFileTypeLFRFID || selected->type == ArchiveFileTypeNFC || + selected->type == ArchiveFileTypeSubGhz)) { + char arg[strlen(str) + 4]; + snprintf(arg, sizeof(arg), "fav%s", str); + loader_start_with_gui_error(loader, app_name, arg); + } else { + loader_start_with_gui_error(loader, app_name, str); + } } - } - - if(status != LoaderStatusOk) { - FURI_LOG_E(TAG, "loader_start failed: %d", status); + } else { + loader_start_with_gui_error(loader, furi_string_get_cstr(selected->path), NULL); } furi_record_close(RECORD_LOADER); @@ -77,6 +105,9 @@ void archive_scene_browser_on_enter(void* context) { browser->is_root = true; archive_browser_set_callback(browser, archive_scene_browser_callback, archive); + if(archive_get_tab(browser) == ArchiveTabFavorites && archive_favorites_count() < 1) { + archive_switch_tab(browser, TAB_LEFT); + } archive_update_focus(browser, archive->text_store); view_dispatcher_switch_to_view(archive->view_dispatcher, ArchiveViewBrowser); @@ -111,9 +142,7 @@ bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) { consumed = true; break; case ArchiveBrowserEventManageMenuOpen: - if(!favorites) { - archive_show_file_menu(browser, true, true); - } + archive_show_file_menu(browser, true, true); consumed = true; break; case ArchiveBrowserEventFileMenuClose: @@ -130,7 +159,7 @@ bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) { archive_show_file_menu(browser, false, false); consumed = true; break; - case ArchiveBrowserEventFileMenuPin: { + case ArchiveBrowserEventFileMenuFavorite: { const char* name = archive_get_name(browser); if(favorites) { archive_favorites_delete("%s", name); @@ -159,38 +188,6 @@ bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(archive->scene_manager, ArchiveAppSceneShow); consumed = true; break; - case ArchiveBrowserEventFileMenuCut: - archive_show_file_menu(browser, false, false); - if(!favorites) { - with_view_model( - browser->view, - ArchiveBrowserViewModel * model, - { - if(model->clipboard == NULL) { - model->clipboard = strdup(furi_string_get_cstr(selected->path)); - model->clipboard_copy = false; - } - }, - false); - } - consumed = true; - break; - case ArchiveBrowserEventFileMenuCopy: - archive_show_file_menu(browser, false, false); - if(!favorites) { - with_view_model( - browser->view, - ArchiveBrowserViewModel * model, - { - if(model->clipboard == NULL) { - model->clipboard = strdup(furi_string_get_cstr(selected->path)); - model->clipboard_copy = true; - } - }, - false); - } - consumed = true; - break; case ArchiveBrowserEventFileMenuPaste: archive_show_file_menu(browser, false, false); if(!favorites) { @@ -221,14 +218,8 @@ bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) { view_dispatcher_switch_to_view(archive->view_dispatcher, ArchiveViewStack); archive_show_loading_popup(archive, true); FS_Error error = archive_copy_rename_file_or_dir( - archive->browser, - furi_string_get_cstr(path_src), - furi_string_get_cstr(path_dst), - copy, - true); + archive->browser, furi_string_get_cstr(path_src), path_dst, copy, true); archive_show_loading_popup(archive, false); - furi_string_free(path_src); - furi_string_free(path_dst); if(error != FSE_OK) { FuriString* dialog_msg; dialog_msg = furi_string_alloc(); @@ -240,12 +231,51 @@ bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) { dialog_message_show_storage_error( archive->dialogs, furi_string_get_cstr(dialog_msg)); furi_string_free(dialog_msg); + } else { + ArchiveFile_t* current = archive_get_current_file(archive->browser); + if(current != NULL) furi_string_set(current->path, path_dst); + view_dispatcher_send_custom_event( + archive->view_dispatcher, ArchiveBrowserEventListRefresh); } + furi_string_free(path_src); + furi_string_free(path_dst); view_dispatcher_switch_to_view(archive->view_dispatcher, ArchiveViewBrowser); } } consumed = true; break; + case ArchiveBrowserEventFileMenuCut: + archive_show_file_menu(browser, false, false); + if(!favorites) { + with_view_model( + browser->view, + ArchiveBrowserViewModel * model, + { + if(model->clipboard == NULL) { + model->clipboard = strdup(furi_string_get_cstr(selected->path)); + model->clipboard_copy = false; + } + }, + false); + } + consumed = true; + break; + case ArchiveBrowserEventFileMenuCopy: + archive_show_file_menu(browser, false, false); + if(!favorites) { + with_view_model( + browser->view, + ArchiveBrowserViewModel * model, + { + if(model->clipboard == NULL) { + model->clipboard = strdup(furi_string_get_cstr(selected->path)); + model->clipboard_copy = true; + } + }, + false); + } + consumed = true; + break; case ArchiveBrowserEventFileMenuNewDir: archive_show_file_menu(browser, false, false); if(!favorites) { @@ -257,30 +287,35 @@ bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) { break; case ArchiveBrowserEventFileMenuRename: archive_show_file_menu(browser, false, false); - if(favorites) { - browser->callback(ArchiveBrowserEventEnterFavMove, browser->context); - //} else if((archive_is_known_app(selected->type)) && (selected->is_app == false)) { - } else { - // Added ability to rename files and folders - scene_manager_set_scene_state( - archive->scene_manager, ArchiveAppSceneBrowser, SCENE_STATE_NEED_REFRESH); - scene_manager_next_scene(archive->scene_manager, ArchiveAppSceneRename); - } + scene_manager_set_scene_state( + archive->scene_manager, ArchiveAppSceneBrowser, SCENE_STATE_NEED_REFRESH); + scene_manager_next_scene(archive->scene_manager, ArchiveAppSceneRename); consumed = true; break; case ArchiveBrowserEventFileMenuDelete: archive_show_file_menu(browser, false, false); - if(!favorites) { - scene_manager_set_scene_state( - archive->scene_manager, ArchiveAppSceneBrowser, SCENE_STATE_NEED_REFRESH); - scene_manager_next_scene(archive->scene_manager, ArchiveAppSceneDelete); - } + scene_manager_set_scene_state( + archive->scene_manager, ArchiveAppSceneBrowser, SCENE_STATE_NEED_REFRESH); + scene_manager_next_scene(archive->scene_manager, ArchiveAppSceneDelete); consumed = true; break; case ArchiveBrowserEventEnterDir: archive_enter_dir(browser, selected->path); consumed = true; break; + case ArchiveBrowserEventSearch: { + bool open = + !scene_manager_get_scene_state(archive->scene_manager, ArchiveAppSceneSearch); + scene_manager_set_scene_state(archive->scene_manager, ArchiveAppSceneSearch, false); + if(archive->thread) { + furi_thread_join(archive->thread); + furi_thread_free(archive->thread); + archive->thread = NULL; + } + if(open) scene_manager_next_scene(archive->scene_manager, ArchiveAppSceneSearch); + consumed = true; + break; + } case ArchiveBrowserEventFavMoveUp: archive_file_array_swap(browser, 1); consumed = true; @@ -290,6 +325,7 @@ bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) { consumed = true; break; case ArchiveBrowserEventEnterFavMove: + archive_show_file_menu(browser, false, false); furi_string_set(archive->fav_move_str, selected->path); archive_favorites_move_mode(archive->browser, true); consumed = true; diff --git a/applications/main/archive/scenes/archive_scene_config.h b/applications/main/archive/scenes/archive_scene_config.h index 63631c65cd..00ff4cf385 100644 --- a/applications/main/archive/scenes/archive_scene_config.h +++ b/applications/main/archive/scenes/archive_scene_config.h @@ -2,5 +2,6 @@ ADD_SCENE(archive, browser, Browser) ADD_SCENE(archive, new_dir, NewDir) ADD_SCENE(archive, rename, Rename) ADD_SCENE(archive, delete, Delete) +ADD_SCENE(archive, search, Search) ADD_SCENE(archive, info, Info) ADD_SCENE(archive, show, Show) diff --git a/applications/main/archive/scenes/archive_scene_delete.c b/applications/main/archive/scenes/archive_scene_delete.c index 4a84e1c382..7dac0a9d3d 100644 --- a/applications/main/archive/scenes/archive_scene_delete.c +++ b/applications/main/archive/scenes/archive_scene_delete.c @@ -3,7 +3,6 @@ #include "../helpers/archive_browser.h" #define SCENE_DELETE_CUSTOM_EVENT (0UL) -#define MAX_TEXT_INPUT_LEN 22 void archive_scene_delete_widget_callback(GuiButtonType result, InputType type, void* context) { furi_assert(context); diff --git a/applications/main/archive/scenes/archive_scene_info.c b/applications/main/archive/scenes/archive_scene_info.c index 07b5aeadb5..14725eaed6 100644 --- a/applications/main/archive/scenes/archive_scene_info.c +++ b/applications/main/archive/scenes/archive_scene_info.c @@ -3,6 +3,8 @@ #define TAG "Archive" +const char* units[] = {"Bytes", "KiB", "MiB", "GiB", "TiB"}; + void archive_scene_info_widget_callback(GuiButtonType result, InputType type, void* context) { furi_assert(context); ArchiveApp* app = (ArchiveApp*)context; @@ -11,6 +13,54 @@ void archive_scene_info_widget_callback(GuiButtonType result, InputType type, vo } } +uint32_t archive_scene_info_dirwalk(void* context) { + furi_assert(context); + ArchiveApp* instance = context; + + char buf[128]; + FileInfo fileinfo; + uint64_t total = 0; + DirWalk* dir_walk = dir_walk_alloc(furi_record_open(RECORD_STORAGE)); + ArchiveFile_t* current = archive_get_current_file(instance->browser); + if(dir_walk_open(dir_walk, furi_string_get_cstr(current->path))) { + while(scene_manager_get_scene_state(instance->scene_manager, ArchiveAppSceneInfo)) { + DirWalkResult result = dir_walk_read(dir_walk, NULL, &fileinfo); + if(result == DirWalkError) { + snprintf(buf, sizeof(buf), "Size: \e#Error\e#"); + widget_element_text_box_set_text(instance->element, buf); + break; + } + bool is_last = result == DirWalkLast; + if(!file_info_is_dir(&fileinfo) || is_last) { + if(!is_last) total += fileinfo.size; + double show = total; + size_t unit; + for(unit = 0; unit < COUNT_OF(units); unit++) { + if(show < 1024) break; + show /= 1024; + } + snprintf( + buf, + sizeof(buf), + unit ? "Size: %s\e#%.2f\e# %s" : "Size: %s\e#%.0f\e# %s", + is_last ? "" : "... ", + show, + units[unit]); + widget_element_text_box_set_text(instance->element, buf); + } + if(is_last) break; + } + } else { + snprintf(buf, sizeof(buf), "Size: \e#Error\e#"); + widget_element_text_box_set_text(instance->element, buf); + } + dir_walk_free(dir_walk); + furi_record_close(RECORD_STORAGE); + + view_dispatcher_switch_to_view(instance->view_dispatcher, ArchiveViewWidget); + return 0; +} + void archive_scene_info_on_enter(void* context) { furi_assert(context); ArchiveApp* instance = context; @@ -18,54 +68,30 @@ void archive_scene_info_on_enter(void* context) { widget_add_button_element( instance->widget, GuiButtonTypeLeft, "Back", archive_scene_info_widget_callback, instance); - FuriString* filename; - FuriString* dirname; - FuriString* str_size; - filename = furi_string_alloc(); - dirname = furi_string_alloc(); - str_size = furi_string_alloc(); + FuriString* filename = furi_string_alloc(); + FuriString* dirname = furi_string_alloc(); ArchiveFile_t* current = archive_get_current_file(instance->browser); - char file_info_message[128]; - Storage* fs_api = furi_record_open(RECORD_STORAGE); + char buf[128]; // Filename path_extract_filename(current->path, filename, false); - snprintf( - file_info_message, sizeof(file_info_message), "\e#%s\e#", furi_string_get_cstr(filename)); + snprintf(buf, sizeof(buf), "\e#%s\e#", furi_string_get_cstr(filename)); widget_add_text_box_element( - instance->widget, 0, 0, 128, 25, AlignLeft, AlignCenter, file_info_message, false); + instance->widget, 0, 0, 128, 24, AlignLeft, AlignCenter, buf, false); // Directory path path_extract_dirname(furi_string_get_cstr(current->path), dirname); - if(strcmp(furi_string_get_cstr(dirname), "/any") == 0) { - furi_string_replace(dirname, STORAGE_ANY_PATH_PREFIX, "/"); - } else { - furi_string_replace(dirname, STORAGE_ANY_PATH_PREFIX, ""); - } - - // File size - FileInfo fileinfo; - storage_common_stat(fs_api, furi_string_get_cstr(current->path), &fileinfo); - if(fileinfo.size <= 1024) { - furi_string_printf(str_size, "%lld", fileinfo.size); - snprintf( - file_info_message, - sizeof(file_info_message), - "Size: \e#%s\e# bytes\n%s", - furi_string_get_cstr(str_size), - furi_string_get_cstr(dirname)); - } else { - furi_string_printf(str_size, "%lld", fileinfo.size / 1024); - snprintf( - file_info_message, - sizeof(file_info_message), - "Size: \e#%s\e# Kb.\n%s", - furi_string_get_cstr(str_size), - furi_string_get_cstr(dirname)); - } widget_add_text_box_element( - instance->widget, 0, 25, 128, 25, AlignLeft, AlignCenter, file_info_message, true); + instance->widget, + 0, + 42, + 128, + 12, + AlignLeft, + AlignCenter, + furi_string_get_cstr(dirname), + false); // This one to return and cursor select this file path_extract_filename_no_ext(furi_string_get_cstr(current->path), filename); @@ -73,9 +99,44 @@ void archive_scene_info_on_enter(void* context) { furi_string_free(filename); furi_string_free(dirname); - furi_string_free(str_size); + + // File size + FileInfo fileinfo; + bool is_dir = false; + if(storage_common_stat( + furi_record_open(RECORD_STORAGE), furi_string_get_cstr(current->path), &fileinfo) != + FSE_OK) { + snprintf(buf, sizeof(buf), "Size: \e#Error\e#"); + } else if(file_info_is_dir(&fileinfo)) { + is_dir = true; + snprintf(buf, sizeof(buf), "Size: ... \e#0\e# %s", units[0]); + + } else { + double show = fileinfo.size; + size_t unit; + for(unit = 0; unit < COUNT_OF(units); unit++) { + if(show < 1024) break; + show /= 1024; + } + snprintf( + buf, + sizeof(buf), + unit ? "Size: \e#%.2f\e# %s" : "Size: \e#%.0f\e# %s", + show, + units[unit]); + } + instance->element = widget_add_text_box_element( + instance->widget, 0, 24, 128, 24, AlignLeft, AlignCenter, buf, true); + furi_record_close(RECORD_STORAGE); view_dispatcher_switch_to_view(instance->view_dispatcher, ArchiveViewWidget); + + if(is_dir) { + scene_manager_set_scene_state(instance->scene_manager, ArchiveAppSceneInfo, true); + instance->thread = furi_thread_alloc_ex( + "ArchiveInfoDirWalk", 1024, (FuriThreadCallback)archive_scene_info_dirwalk, instance); + furi_thread_start(instance->thread); + } } bool archive_scene_info_on_event(void* context, SceneManagerEvent event) { @@ -93,5 +154,11 @@ void archive_scene_info_on_exit(void* context) { furi_assert(context); ArchiveApp* app = (ArchiveApp*)context; + scene_manager_set_scene_state(app->scene_manager, ArchiveAppSceneInfo, false); + if(app->thread) { + furi_thread_join(app->thread); + furi_thread_free(app->thread); + app->thread = NULL; + } widget_reset(app->widget); } diff --git a/applications/main/archive/scenes/archive_scene_new_dir.c b/applications/main/archive/scenes/archive_scene_new_dir.c index 4d64d0ee9e..ba8226d5b5 100644 --- a/applications/main/archive/scenes/archive_scene_new_dir.c +++ b/applications/main/archive/scenes/archive_scene_new_dir.c @@ -9,7 +9,6 @@ #define TAG "Archive" #define SCENE_NEW_DIR_CUSTOM_EVENT (0UL) -#define MAX_TEXT_INPUT_LEN 22 void archive_scene_new_dir_text_input_callback(void* context) { ArchiveApp* archive = (ArchiveApp*)context; @@ -29,7 +28,7 @@ void archive_scene_new_dir_on_enter(void* context) { archive_scene_new_dir_text_input_callback, context, archive->text_store, - MAX_TEXT_INPUT_LEN, + MAX_NAME_LEN, false); view_dispatcher_switch_to_view(archive->view_dispatcher, ArchiveViewTextInput); @@ -56,11 +55,8 @@ bool archive_scene_new_dir_on_event(void* context, SceneManagerEvent event) { error = storage_common_mkdir(fs_api, furi_string_get_cstr(path_dst)); furi_record_close(RECORD_STORAGE); } - archive_refresh_dir(archive->browser); archive_show_loading_popup(archive, false); - furi_string_free(path_dst); - if(error != FSE_OK) { FuriString* dialog_msg; dialog_msg = furi_string_alloc(); @@ -69,7 +65,12 @@ bool archive_scene_new_dir_on_event(void* context, SceneManagerEvent event) { dialog_message_show_storage_error( archive->dialogs, furi_string_get_cstr(dialog_msg)); furi_string_free(dialog_msg); + } else { + ArchiveFile_t* current = archive_get_current_file(archive->browser); + if(current != NULL) furi_string_set(current->path, path_dst); } + + furi_string_free(path_dst); scene_manager_previous_scene(archive->scene_manager); consumed = true; } diff --git a/applications/main/archive/scenes/archive_scene_rename.c b/applications/main/archive/scenes/archive_scene_rename.c index 850cde3499..bf70c80c20 100644 --- a/applications/main/archive/scenes/archive_scene_rename.c +++ b/applications/main/archive/scenes/archive_scene_rename.c @@ -9,7 +9,6 @@ #define TAG "Archive" #define SCENE_RENAME_CUSTOM_EVENT (0UL) -#define MAX_TEXT_INPUT_LEN 22 void archive_scene_rename_text_input_callback(void* context) { ArchiveApp* archive = (ArchiveApp*)context; @@ -25,25 +24,19 @@ void archive_scene_rename_on_enter(void* context) { FuriString* path_name = furi_string_alloc(); FuriString* path_folder = furi_string_alloc(); - if(current->type == ArchiveFileTypeFolder) { - // Set file ext to empty since we need to see folder name here - strcpy(archive->file_extension, ""); - // Extract folder name and copy into text_store + ArchiveTabEnum tab = archive_get_tab(archive->browser); + bool hide_ext = tab != ArchiveTabBrowser && tab != ArchiveTabInternal; + + if(current->type == ArchiveFileTypeFolder || !hide_ext) { + furi_string_reset(archive->file_extension); path_extract_basename(furi_string_get_cstr(current->path), path_name); - strlcpy(archive->text_store, furi_string_get_cstr(path_name), MAX_NAME_LEN); - text_input_set_header_text(text_input, "Rename directory:"); - } else /*if(current->type != ArchiveFileTypeUnknown) */ { - // Extract file name and copy into text_store + } else { + path_extract_ext_str(current->path, archive->file_extension); path_extract_filename(current->path, path_name, true); - strlcpy(archive->text_store, furi_string_get_cstr(path_name), MAX_NAME_LEN); - // Extract file extension for validator and rename func - path_extract_extension(current->path, archive->file_extension, MAX_EXT_LEN); - text_input_set_header_text(text_input, "Rename file:"); - } /*else { - path_extract_filename(current->path, path_name, false); - strlcpy(archive->text_store, furi_string_get_cstr(path_name), MAX_NAME_LEN); - text_input_set_header_text(text_input, "Rename unknown file:"); - }*/ + } + strlcpy(archive->text_store, furi_string_get_cstr(path_name), MAX_NAME_LEN); + text_input_set_header_text( + text_input, current->type == ArchiveFileTypeFolder ? "Rename directory:" : "Rename file:"); // Get current folder (for file) or previous folder (for folder) for validator path_extract_dirname(furi_string_get_cstr(current->path), path_folder); @@ -53,12 +46,14 @@ void archive_scene_rename_on_enter(void* context) { archive_scene_rename_text_input_callback, context, archive->text_store, - MAX_TEXT_INPUT_LEN, + MAX_NAME_LEN, false); // Init validator to show message to user that name already exist ValidatorIsFile* validator_is_file = validator_is_file_alloc_init( - furi_string_get_cstr(path_folder), archive->file_extension, archive->text_store); + furi_string_get_cstr(path_folder), + furi_string_get_cstr(archive->file_extension), + archive->text_store); text_input_set_validator(text_input, validator_is_file_callback, validator_is_file); furi_string_free(path_name); @@ -74,36 +69,25 @@ bool archive_scene_rename_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == SCENE_RENAME_CUSTOM_EVENT) { const char* path_src = archive_get_name(archive->browser); - ArchiveFile_t* file = archive_get_current_file(archive->browser); FuriString* path_dst; path_dst = furi_string_alloc(); - if(file->type == ArchiveFileTypeFolder) { - // Rename folder/dir - path_extract_dirname(path_src, path_dst); - furi_string_cat_printf(path_dst, "/%s", archive->text_store); - } else if(file->type != ArchiveFileTypeUnknown) { - // Rename known type - path_extract_dirname(path_src, path_dst); - furi_string_cat_printf( - path_dst, "/%s%s", archive->text_store, known_ext[file->type]); - } else { - // Rename unknown type - path_extract_dirname(path_src, path_dst); - furi_string_cat_printf( - path_dst, "/%s%s", archive->text_store, archive->file_extension); - } + path_extract_dirname(path_src, path_dst); + furi_string_cat_printf( + path_dst, + "/%s%s", + archive->text_store, + furi_string_get_cstr(archive->file_extension)); + // Long time process if this is directory view_dispatcher_switch_to_view(archive->view_dispatcher, ArchiveViewStack); archive_show_loading_popup(archive, true); FS_Error error = archive_copy_rename_file_or_dir( - archive->browser, path_src, furi_string_get_cstr(path_dst), false, false); + archive->browser, path_src, path_dst, false, false); archive_show_loading_popup(archive, false); - furi_string_free(path_dst); - if(error != FSE_OK) { FuriString* dialog_msg; dialog_msg = furi_string_alloc(); @@ -112,7 +96,12 @@ bool archive_scene_rename_on_event(void* context, SceneManagerEvent event) { dialog_message_show_storage_error( archive->dialogs, furi_string_get_cstr(dialog_msg)); furi_string_free(dialog_msg); + } else { + ArchiveFile_t* current = archive_get_current_file(archive->browser); + if(current != NULL) furi_string_set(current->path, path_dst); } + + furi_string_free(path_dst); scene_manager_previous_scene(archive->scene_manager); consumed = true; } diff --git a/applications/main/archive/scenes/archive_scene_search.c b/applications/main/archive/scenes/archive_scene_search.c new file mode 100644 index 0000000000..458708baeb --- /dev/null +++ b/applications/main/archive/scenes/archive_scene_search.c @@ -0,0 +1,121 @@ +#include "../archive_i.h" +#include "../helpers/archive_favorites.h" +#include "../helpers/archive_files.h" +#include "../helpers/archive_browser.h" +#include "archive/views/archive_browser_view.h" +#include "toolbox/path.h" +#include +#include + +#define TAG "Archive" + +#define SCENE_SEARCH_CUSTOM_EVENT (0UL) + +void archive_scene_search_text_input_callback(void* context) { + ArchiveApp* archive = (ArchiveApp*)context; + view_dispatcher_send_custom_event(archive->view_dispatcher, SCENE_SEARCH_CUSTOM_EVENT); +} + +void archive_scene_search_on_enter(void* context) { + ArchiveApp* archive = context; + + TextInput* text_input = archive->text_input; + strlcpy(archive->text_store, "", MAX_NAME_LEN); + text_input_set_header_text(text_input, "Search for files:"); + + text_input_set_result_callback( + text_input, + archive_scene_search_text_input_callback, + context, + archive->text_store, + MAX_NAME_LEN, + false); + + view_dispatcher_switch_to_view(archive->view_dispatcher, ArchiveViewTextInput); +} + +uint32_t archive_scene_search_dirwalk(void* context) { + furi_assert(context); + ArchiveApp* archive = context; + + uint32_t count = 1; + DirWalk* dir_walk = dir_walk_alloc(furi_record_open(RECORD_STORAGE)); + const char* ignore[] = { + XTREME_ASSETS_PATH, + BASE_ANIMATION_DIR, + }; + dir_walk_set_recurse_filter(dir_walk, ignore, COUNT_OF(ignore)); + FuriString* path = furi_string_alloc(); + FuriString* name = furi_string_alloc(); + FileInfo fileinfo; + + if(dir_walk_open(dir_walk, STORAGE_EXT_PATH_PREFIX)) { + while(scene_manager_get_scene_state(archive->scene_manager, ArchiveAppSceneSearch)) { + DirWalkResult result = dir_walk_read(dir_walk, path, &fileinfo); + if(result == DirWalkError) { + archive_add_app_item(archive->browser, "/app:search/Error while searching!"); + archive_set_item_count(archive->browser, ++count); + break; + } + if(result == DirWalkLast) { + if(count == 1) { + archive_add_app_item(archive->browser, "/app:search/No results found!"); + archive_set_item_count(archive->browser, ++count); + } + break; + } + if(!file_info_is_dir(&fileinfo)) { + furi_string_set( + name, furi_string_get_cstr(path) + furi_string_search_rchar(path, '/') + 1); + if(furi_string_search_str(name, archive->text_store) != FURI_STRING_FAILURE) { + archive_add_file_item(archive->browser, false, furi_string_get_cstr(path)); + archive_set_item_count(archive->browser, ++count); + } + } + } + } else { + archive_add_app_item(archive->browser, "/app:search/Error while searching!"); + archive_set_item_count(archive->browser, ++count); + } + furi_string_set( + archive_get_file_at(archive->browser, 0)->path, "/app:search/Search for files"); + scene_manager_set_scene_state(archive->scene_manager, ArchiveAppSceneSearch, false); + + furi_string_free(name); + furi_string_free(path); + dir_walk_free(dir_walk); + furi_record_close(RECORD_STORAGE); + return 0; +} + +bool archive_scene_search_on_event(void* context, SceneManagerEvent event) { + ArchiveApp* archive = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SCENE_SEARCH_CUSTOM_EVENT) { + archive_file_array_rm_all(archive->browser); + archive_add_app_item(archive->browser, "/app:search/Cancel search"); + archive_set_item_count(archive->browser, 1); + + // Thread here is fine because only the info pane uses it too, + // but only for directories, which are ignored for search + scene_manager_set_scene_state(archive->scene_manager, ArchiveAppSceneSearch, true); + archive->thread = furi_thread_alloc_ex( + "ArchiveSearchDirWalk", + 1024, + (FuriThreadCallback)archive_scene_search_dirwalk, + archive); + furi_thread_start(archive->thread); + + scene_manager_previous_scene(archive->scene_manager); + consumed = true; + } + } + return consumed; +} + +void archive_scene_search_on_exit(void* context) { + ArchiveApp* archive = context; + text_input_reset(archive->text_input); +} diff --git a/applications/main/archive/views/archive_browser_view.c b/applications/main/archive/views/archive_browser_view.c index d4a52ba9e2..5aba055783 100644 --- a/applications/main/archive/views/archive_browser_view.c +++ b/applications/main/archive/views/archive_browser_view.c @@ -19,6 +19,7 @@ static const char* ArchiveTabNames[] = { [ArchiveTabBadKb] = "Bad KB", [ArchiveTabU2f] = "U2F", [ArchiveTabApplications] = "Apps", + [ArchiveTabSearch] = "Search", [ArchiveTabInternal] = "Internal", [ArchiveTabBrowser] = "Browser", }; @@ -29,9 +30,13 @@ static const Icon* ArchiveItemIcons[] = { [ArchiveFileTypeSubGhz] = &I_sub1_10px, [ArchiveFileTypeLFRFID] = &I_125_10px, [ArchiveFileTypeInfrared] = &I_ir_10px, + [ArchiveFileTypeSubghzPlaylist] = &I_subplaylist_10px, + [ArchiveFileTypeSubghzRemote] = &I_subrem_10px, + [ArchiveFileTypeInfraredRemote] = &I_ir_scope_10px, [ArchiveFileTypeBadKb] = &I_badkb_10px, [ArchiveFileTypeU2f] = &I_u2f_10px, [ArchiveFileTypeApplication] = &I_Apps_10px, + [ArchiveFileTypeSearch] = &I_search_10px, [ArchiveFileTypeUpdateManifest] = &I_update_10px, [ArchiveFileTypeFolder] = &I_dir_10px, [ArchiveFileTypeUnknown] = &I_unknown_10px, @@ -57,174 +62,93 @@ static void render_item_menu(Canvas* canvas, ArchiveBrowserViewModel* model) { NULL; bool favorites = model->tab_idx == ArchiveTabFavorites; - if(model->menu_manage && !favorites) { - FuriString* item_cut = furi_string_alloc_set("Cut"); - FuriString* item_copy = furi_string_alloc_set("Copy"); - FuriString* item_paste = furi_string_alloc_set("Paste"); - FuriString* item_new_dir = furi_string_alloc_set("New Dir"); - FuriString* item_rename = furi_string_alloc_set("Rename"); - FuriString* item_delete = furi_string_alloc_set("Delete"); - if(model->clipboard != NULL) { - archive_menu_add_item( - menu_array_push_raw(model->context_menu), - item_paste, - ArchiveBrowserEventFileMenuPaste); - } else if(selected) { - archive_menu_add_item( - menu_array_push_raw(model->context_menu), - item_cut, - ArchiveBrowserEventFileMenuCut); + if(model->menu_manage) { + if(!model->is_app_tab && !favorites) { + if(model->clipboard != NULL) { + archive_menu_add_item( + menu_array_push_raw(model->context_menu), + "Paste", + ArchiveBrowserEventFileMenuPaste); + } else if(selected) { + archive_menu_add_item( + menu_array_push_raw(model->context_menu), + "Cut", + ArchiveBrowserEventFileMenuCut); + archive_menu_add_item( + menu_array_push_raw(model->context_menu), + "Copy", + ArchiveBrowserEventFileMenuCopy); + } archive_menu_add_item( menu_array_push_raw(model->context_menu), - item_copy, - ArchiveBrowserEventFileMenuCopy); + "New Dir", + ArchiveBrowserEventFileMenuNewDir); } - archive_menu_add_item( - menu_array_push_raw(model->context_menu), - item_new_dir, - ArchiveBrowserEventFileMenuNewDir); if(selected) { if(!selected->is_app) { archive_menu_add_item( menu_array_push_raw(model->context_menu), - item_rename, + "Rename", ArchiveBrowserEventFileMenuRename); } archive_menu_add_item( menu_array_push_raw(model->context_menu), - item_delete, + "Delete", ArchiveBrowserEventFileMenuDelete); } - furi_string_free(item_cut); - furi_string_free(item_copy); - furi_string_free(item_paste); - furi_string_free(item_new_dir); - furi_string_free(item_rename); - furi_string_free(item_delete); - } else if(!model->menu_manage && selected) { - FuriString* item_run = furi_string_alloc_set("Run In App"); - FuriString* item_pin = furi_string_alloc_set("Pin"); - FuriString* item_info = furi_string_alloc_set("Info"); - FuriString* item_show = furi_string_alloc_set("Show"); - if(selected->fav || favorites) { - furi_string_set(item_pin, "Unpin"); - } - - if(favorites) { - //FURI_LOG_D(TAG, "ArchiveTabFavorites"); + } else if(selected) { + if(archive_is_known_app(selected->type)) { + if(selected->type != ArchiveFileTypeFolder) { + archive_menu_add_item( + menu_array_push_raw(model->context_menu), + "Run In App", + ArchiveBrowserEventFileMenuRun); + } archive_menu_add_item( menu_array_push_raw(model->context_menu), - item_run, - ArchiveBrowserEventFileMenuRun); + (selected->fav || favorites) ? "Unfavorite" : "Favorite", + ArchiveBrowserEventFileMenuFavorite); + } + if(!selected->is_app) { archive_menu_add_item( menu_array_push_raw(model->context_menu), - item_pin, - ArchiveBrowserEventFileMenuPin); - if(selected->type <= ArchiveFileTypeBadKb) { + "Info", + ArchiveBrowserEventFileMenuInfo); + if(selected->type != ArchiveFileTypeFolder) { archive_menu_add_item( menu_array_push_raw(model->context_menu), - item_show, + "Show", ArchiveBrowserEventFileMenuShow); } - furi_string_set(item_info, "Move"); + } + if(favorites) { archive_menu_add_item( menu_array_push_raw(model->context_menu), - item_info, - ArchiveBrowserEventFileMenuRename); - } else { - if(selected->type == ArchiveFileTypeFolder) { - //FURI_LOG_D(TAG, "Directory type"); - archive_menu_add_item( - menu_array_push_raw(model->context_menu), - item_pin, - ArchiveBrowserEventFileMenuPin); - } else if(!archive_is_known_app(selected->type)) { - //FURI_LOG_D(TAG, "Unknown type"); - archive_menu_add_item( - menu_array_push_raw(model->context_menu), - item_info, - ArchiveBrowserEventFileMenuInfo); - if(selected->is_text_file) { - archive_menu_add_item( - menu_array_push_raw(model->context_menu), - item_show, - ArchiveBrowserEventFileMenuShow); - } - } else if(selected->is_app) { - //FURI_LOG_D(TAG, "3 types"); - archive_menu_add_item( - menu_array_push_raw(model->context_menu), - item_run, - ArchiveBrowserEventFileMenuRun); - archive_menu_add_item( - menu_array_push_raw(model->context_menu), - item_info, - ArchiveBrowserEventFileMenuInfo); - if(selected->type <= ArchiveFileTypeBadKb) { - archive_menu_add_item( - menu_array_push_raw(model->context_menu), - item_show, - ArchiveBrowserEventFileMenuShow); - } - archive_menu_add_item( - menu_array_push_raw(model->context_menu), - item_pin, - ArchiveBrowserEventFileMenuPin); - } else { - //FURI_LOG_D(TAG, "All menu"); - archive_menu_add_item( - menu_array_push_raw(model->context_menu), - item_run, - ArchiveBrowserEventFileMenuRun); - archive_menu_add_item( - menu_array_push_raw(model->context_menu), - item_pin, - ArchiveBrowserEventFileMenuPin); - archive_menu_add_item( - menu_array_push_raw(model->context_menu), - item_info, - ArchiveBrowserEventFileMenuInfo); - if(selected->type <= ArchiveFileTypeBadKb) { - archive_menu_add_item( - menu_array_push_raw(model->context_menu), - item_show, - ArchiveBrowserEventFileMenuShow); - } - } + "Move", + ArchiveBrowserEventEnterFavMove); } - - furi_string_free(item_run); - furi_string_free(item_pin); - furi_string_free(item_info); - furi_string_free(item_show); } - } /*else { - FURI_LOG_D(TAG, "menu_array_size already set: %d", menu_array_size(model->context_menu)); - }*/ + } size_t size_menu = menu_array_size(model->context_menu); const uint8_t menu_height = 48; const uint8_t line_height = 10; + const uint8_t calc_height = menu_height - ((MENU_ITEMS - size_menu - 1) * line_height); + const uint8_t off = (MENU_ITEMS - size_menu) * (line_height / 2); canvas_set_color(canvas, ColorWhite); - uint8_t calc_height = menu_height - ((MENU_ITEMS - size_menu - 1) * line_height); - canvas_draw_box(canvas, 71, 1, 57, calc_height + 4); + canvas_draw_box(canvas, 72, off + 2, 56, calc_height + 4); canvas_set_color(canvas, ColorBlack); - elements_slightly_rounded_frame(canvas, 70, 2, 58, calc_height + 4); - - /*FURI_LOG_D( - TAG, - "size_menu: %d, calc_height: %d, menu_idx: %d", - size_menu, - calc_height, - model->menu_idx);*/ - canvas_draw_str(canvas, 74, 11, model->menu_manage ? "Manage:" : "Actions:"); + elements_slightly_rounded_frame(canvas, 71, off + 2, 57, calc_height + 4); + + canvas_draw_str(canvas, 74, off + 11, model->menu_manage ? "Manage:" : "Actions:"); for(size_t i = 0; i < size_menu; i++) { ArchiveContextMenuItem_t* current = menu_array_get(model->context_menu, i); canvas_draw_str( - canvas, 82, 11 + (i + 1) * line_height, furi_string_get_cstr(current->text)); + canvas, 82, off + 11 + (i + 1) * line_height, furi_string_get_cstr(current->text)); } - canvas_draw_icon(canvas, 74, 4 + (model->menu_idx + 1) * line_height, &I_ButtonRight_4x7); + canvas_draw_icon( + canvas, 74, off + 4 + (model->menu_idx + 1) * line_height, &I_ButtonRight_4x7); } static void archive_draw_frame(Canvas* canvas, uint16_t idx, bool scrollbar, bool moving) { @@ -276,16 +200,18 @@ static void draw_list(Canvas* canvas, ArchiveBrowserViewModel* model) { ArchiveFile_t* file = files_array_get( model->files, CLAMP(idx - model->array_offset, (int32_t)(array_size - 1), 0)); file_type = file->type; + bool ext = model->tab_idx == ArchiveTabBrowser || + model->tab_idx == ArchiveTabInternal || model->tab_idx == ArchiveTabSearch; if(file_type == ArchiveFileTypeApplication) { if(file->custom_icon_data) { custom_icon_data = file->custom_icon_data; furi_string_set(str_buf, file->custom_name); } else { file_type = ArchiveFileTypeUnknown; - path_extract_filename(file->path, str_buf, archive_is_known_app(file->type)); + path_extract_filename(file->path, str_buf, !ext); } } else { - path_extract_filename(file->path, str_buf, archive_is_known_app(file->type)); + path_extract_filename(file->path, str_buf, !ext); } } else { furi_string_set(str_buf, "---"); @@ -339,6 +265,10 @@ static void archive_render_status_bar(Canvas* canvas, ArchiveBrowserViewModel* m furi_assert(model); const char* tab_name = ArchiveTabNames[model->tab_idx]; + if(model->tab_idx == ArchiveTabSearch && + scene_manager_get_scene_state(model->archive->scene_manager, ArchiveAppSceneSearch)) { + tab_name = "Searching"; + } bool clip = model->clipboard != NULL; canvas_draw_icon(canvas, 0, 0, &I_Background_128x11); @@ -432,6 +362,7 @@ static bool archive_view_input(InputEvent* event, void* context) { ArchiveBrowserView* browser = context; bool in_menu; + int32_t cur_item_idx; bool move_fav_mode; bool is_loading; with_view_model( @@ -439,6 +370,7 @@ static bool archive_view_input(InputEvent* event, void* context) { ArchiveBrowserViewModel * model, { in_menu = model->menu; + cur_item_idx = model->item_idx; move_fav_mode = model->move_fav; is_loading = model->folder_loading || model->list_loading; }, @@ -499,9 +431,31 @@ static bool archive_view_input(InputEvent* event, void* context) { browser->view, ArchiveBrowserViewModel * model, { + int32_t scroll_speed = 1; + if(model->button_held_for_ticks > 5) { + if(model->button_held_for_ticks % 2) { + scroll_speed = 0; + } else { + scroll_speed = model->button_held_for_ticks > 9 ? 4 : 2; + } + } else if(model->button_held_for_ticks < 0) { + scroll_speed = 0; + } + if(event->key == InputKeyUp) { + if(model->item_idx < scroll_speed) { + scroll_speed = model->item_idx; + if(scroll_speed == 0) { + if(model->button_held_for_ticks > 0) { + model->button_held_for_ticks = -1; + } else { + scroll_speed = 1; + } + } + } + model->item_idx = - ((model->item_idx - 1) + model->item_cnt) % model->item_cnt; + ((model->item_idx - scroll_speed) + model->item_cnt) % model->item_cnt; if(is_file_list_load_required(model)) { model->list_loading = true; browser->callback(ArchiveBrowserEventLoadPrevItems, browser->context); @@ -510,8 +464,25 @@ static bool archive_view_input(InputEvent* event, void* context) { browser->callback(ArchiveBrowserEventFavMoveUp, browser->context); } model->scroll_counter = 0; + + if(model->button_held_for_ticks < -1) { + model->button_held_for_ticks = 0; + } + model->button_held_for_ticks += 1; } else if(event->key == InputKeyDown) { - model->item_idx = (model->item_idx + 1) % model->item_cnt; + int32_t count = model->item_cnt; + if(model->item_idx >= (count - scroll_speed)) { + scroll_speed = model->item_cnt - model->item_idx - 1; + if(scroll_speed == 0) { + if(model->button_held_for_ticks > 0) { + model->button_held_for_ticks = -1; + } else { + scroll_speed = 1; + } + } + } + + model->item_idx = (model->item_idx + scroll_speed) % model->item_cnt; if(is_file_list_load_required(model)) { model->list_loading = true; browser->callback(ArchiveBrowserEventLoadNextItems, browser->context); @@ -520,17 +491,31 @@ static bool archive_view_input(InputEvent* event, void* context) { browser->callback(ArchiveBrowserEventFavMoveDown, browser->context); } model->scroll_counter = 0; + + if(model->button_held_for_ticks < -1) { + model->button_held_for_ticks = 0; + } + model->button_held_for_ticks += 1; } }, false); archive_update_offset(browser); } - if(event->key == InputKeyOk) { - ArchiveFile_t* selected = archive_get_current_file(browser); - + ArchiveFile_t* selected = archive_get_current_file(browser); + bool favorites = archive_get_tab(browser) == ArchiveTabFavorites; + if(selected && selected->type == ArchiveFileTypeSearch) { + if((cur_item_idx == 0 || favorites) && event->key == InputKeyOk) { + if(event->type == InputTypeShort) { + browser->callback( + favorites ? ArchiveBrowserEventFileMenuRun : ArchiveBrowserEventSearch, + browser->context); + } else if(event->type == InputTypeLong) { + browser->callback(ArchiveBrowserEventFileMenuOpen, browser->context); + } + } + } else if(event->key == InputKeyOk) { if(selected) { - bool favorites = archive_get_tab(browser) == ArchiveTabFavorites; bool folder = selected->type == ArchiveFileTypeFolder; if(event->type == InputTypeShort) { @@ -558,6 +543,14 @@ static bool archive_view_input(InputEvent* event, void* context) { } } + if(event->type == InputTypeRelease) { + with_view_model( + browser->view, + ArchiveBrowserViewModel * model, + { model->button_held_for_ticks = 0; }, + true); + } + return true; } diff --git a/applications/main/archive/views/archive_browser_view.h b/applications/main/archive/views/archive_browser_view.h index 1b4c0f5259..f87d59a2b1 100644 --- a/applications/main/archive/views/archive_browser_view.h +++ b/applications/main/archive/views/archive_browser_view.h @@ -15,8 +15,7 @@ #include "gui/modules/file_browser_worker.h" #define MAX_LEN_PX 110 -#define MAX_NAME_LEN 255 -#define MAX_EXT_LEN 6 +#define MAX_NAME_LEN 254 #define FRAME_HEIGHT 12 #define MENU_ITEMS 5u #define MOVE_OFFSET 5u @@ -31,6 +30,7 @@ typedef enum { ArchiveTabBadKb, ArchiveTabU2f, ArchiveTabApplications, + ArchiveTabSearch, ArchiveTabInternal, ArchiveTabBrowser, ArchiveTabTotal, @@ -41,19 +41,21 @@ typedef enum { ArchiveBrowserEventFileMenuOpen, ArchiveBrowserEventManageMenuOpen, ArchiveBrowserEventFileMenuRun, - ArchiveBrowserEventFileMenuPin, + ArchiveBrowserEventFileMenuFavorite, + ArchiveBrowserEventFileMenuInfo, + ArchiveBrowserEventFileMenuShow, + ArchiveBrowserEventFileMenuPaste, ArchiveBrowserEventFileMenuCut, ArchiveBrowserEventFileMenuCopy, - ArchiveBrowserEventFileMenuPaste, ArchiveBrowserEventFileMenuNewDir, ArchiveBrowserEventFileMenuRename, ArchiveBrowserEventFileMenuDelete, - ArchiveBrowserEventFileMenuInfo, - ArchiveBrowserEventFileMenuShow, ArchiveBrowserEventFileMenuClose, ArchiveBrowserEventEnterDir, + ArchiveBrowserEventSearch, + ArchiveBrowserEventFavMoveUp, ArchiveBrowserEventFavMoveDown, ArchiveBrowserEventEnterFavMove, @@ -91,6 +93,7 @@ struct ArchiveBrowserView { }; typedef struct { + ArchiveApp* archive; ArchiveTabEnum tab_idx; files_array_t files; @@ -101,6 +104,7 @@ typedef struct { bool clipboard_copy; menu_array_t context_menu; + bool is_app_tab; bool move_fav; bool list_loading; bool folder_loading; @@ -110,6 +114,8 @@ typedef struct { int32_t array_offset; int32_t list_offset; size_t scroll_counter; + + int32_t button_held_for_ticks; } ArchiveBrowserViewModel; void archive_browser_set_callback( diff --git a/applications/main/bad_kb/application.fam b/applications/main/bad_kb/application.fam index 09531da81a..165634967f 100644 --- a/applications/main/bad_kb/application.fam +++ b/applications/main/bad_kb/application.fam @@ -1,7 +1,7 @@ App( appid="bad_kb", name="Bad KB", - apptype=FlipperAppType.APP, + apptype=FlipperAppType.FAPP, entry_point="bad_kb_app", cdefines=["APP_BAD_KB"], requires=[ @@ -11,5 +11,4 @@ App( stack_size=2 * 1024, icon="A_BadKb_14", order=70, - fap_libs=["assets"], ) diff --git a/applications/main/bad_kb/bad_kb_app.c b/applications/main/bad_kb/bad_kb_app.c index c2d7e1fe3e..e62ad4d3a7 100644 --- a/applications/main/bad_kb/bad_kb_app.c +++ b/applications/main/bad_kb/bad_kb_app.c @@ -5,7 +5,6 @@ #include #include #include -#include #include #include @@ -30,11 +29,13 @@ static void bad_kb_app_tick_event_callback(void* context) { static void bad_kb_load_settings(BadKbApp* app) { furi_string_reset(app->keyboard_layout); - strcpy(app->config.bt_name, ""); - memcpy( - app->config.bt_mac, - furi_hal_bt_get_profile_mac_addr(FuriHalBtProfileHidKeyboard), - BAD_KB_MAC_ADDRESS_LEN); + BadKbConfig* cfg = &app->config; + strcpy(cfg->bt_name, ""); + memcpy(cfg->bt_mac, BAD_KB_EMPTY_MAC, BAD_KB_MAC_LEN); + strcpy(cfg->usb_cfg.manuf, ""); + strcpy(cfg->usb_cfg.product, ""); + cfg->usb_cfg.vid = 0; + cfg->usb_cfg.pid = 0; Storage* storage = furi_record_open(RECORD_STORAGE); FlipperFormat* file = flipper_format_file_alloc(storage); @@ -44,16 +45,29 @@ static void bad_kb_load_settings(BadKbApp* app) { furi_string_reset(app->keyboard_layout); } if(flipper_format_read_string(file, "Bt_Name", tmp_str) && !furi_string_empty(tmp_str)) { - strcpy(app->config.bt_name, furi_string_get_cstr(tmp_str)); + strlcpy(cfg->bt_name, furi_string_get_cstr(tmp_str), BAD_KB_NAME_LEN); } else { - strcpy(app->config.bt_name, ""); + strcpy(cfg->bt_name, ""); } - if(!flipper_format_read_hex( - file, "Bt_Mac", (uint8_t*)&app->config.bt_mac, BAD_KB_MAC_ADDRESS_LEN)) { - memcpy( - app->config.bt_mac, - furi_hal_bt_get_profile_mac_addr(FuriHalBtProfileHidKeyboard), - BAD_KB_MAC_ADDRESS_LEN); + if(!flipper_format_read_hex(file, "Bt_Mac", (uint8_t*)&cfg->bt_mac, BAD_KB_MAC_LEN)) { + memcpy(cfg->bt_mac, BAD_KB_EMPTY_MAC, BAD_KB_MAC_LEN); + } + if(flipper_format_read_string(file, "Usb_Manuf", tmp_str) && !furi_string_empty(tmp_str)) { + strlcpy(cfg->usb_cfg.manuf, furi_string_get_cstr(tmp_str), BAD_KB_USB_LEN); + } else { + strcpy(cfg->usb_cfg.manuf, ""); + } + if(flipper_format_read_string(file, "Usb_Product", tmp_str) && + !furi_string_empty(tmp_str)) { + strlcpy(cfg->usb_cfg.product, furi_string_get_cstr(tmp_str), BAD_KB_USB_LEN); + } else { + strcpy(cfg->usb_cfg.product, ""); + } + if(!flipper_format_read_uint32(file, "Usb_Vid", &cfg->usb_cfg.vid, 1)) { + cfg->usb_cfg.vid = 0; + } + if(!flipper_format_read_uint32(file, "Usb_Pid", &cfg->usb_cfg.pid, 1)) { + cfg->usb_cfg.pid = 0; } furi_string_free(tmp_str); flipper_format_file_close(file); @@ -77,122 +91,23 @@ static void bad_kb_load_settings(BadKbApp* app) { } static void bad_kb_save_settings(BadKbApp* app) { + BadKbConfig* cfg = &app->config; Storage* storage = furi_record_open(RECORD_STORAGE); FlipperFormat* file = flipper_format_file_alloc(storage); if(flipper_format_file_open_always(file, BAD_KB_SETTINGS_PATH)) { flipper_format_write_string(file, "Keyboard_Layout", app->keyboard_layout); - flipper_format_write_string_cstr(file, "Bt_Name", app->config.bt_name); - flipper_format_write_hex( - file, "Bt_Mac", (uint8_t*)&app->config.bt_mac, BAD_KB_MAC_ADDRESS_LEN); + flipper_format_write_string_cstr(file, "Bt_Name", cfg->bt_name); + flipper_format_write_hex(file, "Bt_Mac", (uint8_t*)&cfg->bt_mac, BAD_KB_MAC_LEN); + flipper_format_write_string_cstr(file, "Usb_Manuf", cfg->usb_cfg.manuf); + flipper_format_write_string_cstr(file, "Usb_Product", cfg->usb_cfg.product); + flipper_format_write_uint32(file, "Usb_Vid", &cfg->usb_cfg.vid, 1); + flipper_format_write_uint32(file, "Usb_Pid", &cfg->usb_cfg.pid, 1); flipper_format_file_close(file); } flipper_format_free(file); furi_record_close(RECORD_STORAGE); } -void bad_kb_reload_worker(BadKbApp* app) { - bad_kb_script_close(app->bad_kb_script); - app->bad_kb_script = bad_kb_script_open(app->file_path, app->is_bt ? app->bt : NULL, app); - bad_kb_script_set_keyboard_layout(app->bad_kb_script, app->keyboard_layout); -} - -int32_t bad_kb_config_switch_mode(BadKbApp* app) { - if(!app->is_bt) furi_hal_bt_stop_advertising(); - XTREME_SETTINGS()->bad_bt = app->is_bt; - XTREME_SETTINGS_SAVE(); - bad_kb_reload_worker(app); - if(app->is_bt) furi_hal_bt_start_advertising(); - bad_kb_config_refresh_menu(app); - return 0; -} - -void bad_kb_config_refresh_menu(BadKbApp* app) { - scene_manager_next_scene(app->scene_manager, BadKbSceneConfig); - scene_manager_previous_scene(app->scene_manager); -} - -void bad_kb_config_switch_remember_mode(BadKbApp* app) { - if(app->bt_remember) { - furi_hal_bt_set_profile_pairing_method( - FuriHalBtProfileHidKeyboard, GapPairingPinCodeVerifyYesNo); - bt_set_profile_mac_address(app->bt, (uint8_t*)&BAD_KB_BOUND_MAC_ADDRESS); - bt_enable_peer_key_update(app->bt); - } else { - furi_hal_bt_set_profile_pairing_method(FuriHalBtProfileHidKeyboard, GapPairingNone); - bt_set_profile_mac_address(app->bt, app->config.bt_mac); - bt_disable_peer_key_update(app->bt); - } - bad_kb_reload_worker(app); -} - -int32_t bad_kb_connection_init(BadKbApp* app) { - app->prev_config.usb_mode = furi_hal_usb_get_config(); - furi_hal_usb_set_config(NULL, NULL); - - strcpy( - app->prev_config.bt_name, furi_hal_bt_get_profile_adv_name(FuriHalBtProfileHidKeyboard)); - memcpy( - app->prev_config.bt_mac, - furi_hal_bt_get_profile_mac_addr(FuriHalBtProfileHidKeyboard), - BAD_KB_MAC_ADDRESS_LEN); - app->prev_config.bt_mode = furi_hal_bt_get_profile_pairing_method(FuriHalBtProfileHidKeyboard); - - bt_timeout = bt_hid_delays[LevelRssi39_0]; - bt_disconnect(app->bt); - bt_keys_storage_set_storage_path(app->bt, BAD_KB_KEYS_PATH); - if(strcmp(app->config.bt_name, "") != 0) { - furi_hal_bt_set_profile_adv_name(FuriHalBtProfileHidKeyboard, app->config.bt_name); - } - if(app->bt_remember) { - furi_hal_bt_set_profile_mac_addr( - FuriHalBtProfileHidKeyboard, (uint8_t*)&BAD_KB_BOUND_MAC_ADDRESS); - furi_hal_bt_set_profile_pairing_method( - FuriHalBtProfileHidKeyboard, GapPairingPinCodeVerifyYesNo); - } else { - if(memcmp( - app->config.bt_mac, (uint8_t*)&BAD_KB_EMPTY_MAC_ADDRESS, BAD_KB_MAC_ADDRESS_LEN) != - 0) { - furi_hal_bt_set_profile_mac_addr(FuriHalBtProfileHidKeyboard, app->config.bt_mac); - } - furi_hal_bt_set_profile_pairing_method(FuriHalBtProfileHidKeyboard, GapPairingNone); - } - bt_set_profile(app->bt, BtProfileHidKeyboard); - if(strcmp(app->config.bt_name, "") == 0) { - strcpy(app->config.bt_name, furi_hal_bt_get_profile_adv_name(FuriHalBtProfileHidKeyboard)); - } - if(memcmp(app->config.bt_mac, (uint8_t*)&BAD_KB_EMPTY_MAC_ADDRESS, BAD_KB_MAC_ADDRESS_LEN) == - 0) { - memcpy( - app->config.bt_mac, - furi_hal_bt_get_profile_mac_addr(FuriHalBtProfileHidKeyboard), - BAD_KB_MAC_ADDRESS_LEN); - } - if(app->is_bt) { - furi_hal_bt_start_advertising(); - if(app->bt_remember) { - bt_enable_peer_key_update(app->bt); - } else { - bt_disable_peer_key_update(app->bt); - } - } else { - furi_hal_bt_stop_advertising(); - } - - return 0; -} - -void bad_kb_connection_deinit(BadKbApp* app) { - furi_hal_usb_set_config(app->prev_config.usb_mode, NULL); - - bt_disconnect(app->bt); - bt_keys_storage_set_default_path(app->bt); - furi_hal_bt_set_profile_adv_name(FuriHalBtProfileHidKeyboard, app->prev_config.bt_name); - furi_hal_bt_set_profile_mac_addr(FuriHalBtProfileHidKeyboard, app->prev_config.bt_mac); - furi_hal_bt_set_profile_pairing_method(FuriHalBtProfileHidKeyboard, app->prev_config.bt_mode); - bt_set_profile(app->bt, BtProfileSerial); - bt_enable_peer_key_update(app->bt); -} - BadKbApp* bad_kb_app_alloc(char* arg) { BadKbApp* app = malloc(sizeof(BadKbApp)); @@ -200,7 +115,6 @@ BadKbApp* bad_kb_app_alloc(char* arg) { app->file_path = furi_string_alloc(); app->keyboard_layout = furi_string_alloc(); - process_favorite_launch(&arg); if(arg && strlen(arg)) { furi_string_set(app->file_path, arg); } @@ -223,7 +137,7 @@ BadKbApp* bad_kb_app_alloc(char* arg) { view_dispatcher_set_event_callback_context(app->view_dispatcher, app); view_dispatcher_set_tick_event_callback( - app->view_dispatcher, bad_kb_app_tick_event_callback, 500); + app->view_dispatcher, bad_kb_app_tick_event_callback, 250); view_dispatcher_set_custom_event_callback( app->view_dispatcher, bad_kb_app_custom_event_callback); view_dispatcher_set_navigation_event_callback( @@ -234,15 +148,29 @@ BadKbApp* bad_kb_app_alloc(char* arg) { app->bt->suppress_pin_screen = true; app->is_bt = XTREME_SETTINGS()->bad_bt; app->bt_remember = XTREME_SETTINGS()->bad_bt_remember; + bad_kb_config_adjust(&app->config); + + // Save prev config + app->prev_usb_mode = furi_hal_usb_get_config(); + FuriHalBtProfile kbd = FuriHalBtProfileHidKeyboard; + app->prev_bt_mode = furi_hal_bt_get_profile_pairing_method(kbd); + memcpy(app->prev_bt_mac, furi_hal_bt_get_profile_mac_addr(kbd), BAD_KB_MAC_LEN); + strlcpy(app->prev_bt_name, furi_hal_bt_get_profile_adv_name(kbd), BAD_KB_NAME_LEN); + + // Adjust BT remember MAC to be serial MAC +2 + memcpy(BAD_KB_BOUND_MAC, furi_hal_version_get_ble_mac(), BAD_KB_MAC_LEN); + BAD_KB_BOUND_MAC[2] += 2; // Custom Widget app->widget = widget_alloc(); view_dispatcher_add_view( - app->view_dispatcher, BadKbAppViewError, widget_get_view(app->widget)); + app->view_dispatcher, BadKbAppViewWidget, widget_get_view(app->widget)); app->var_item_list = variable_item_list_alloc(); view_dispatcher_add_view( - app->view_dispatcher, BadKbAppViewConfig, variable_item_list_get_view(app->var_item_list)); + app->view_dispatcher, + BadKbAppViewVarItemList, + variable_item_list_get_view(app->var_item_list)); app->bad_kb_view = bad_kb_alloc(); view_dispatcher_add_view( @@ -250,31 +178,25 @@ BadKbApp* bad_kb_app_alloc(char* arg) { app->text_input = text_input_alloc(); view_dispatcher_add_view( - app->view_dispatcher, BadKbAppViewConfigName, text_input_get_view(app->text_input)); + app->view_dispatcher, BadKbAppViewTextInput, text_input_get_view(app->text_input)); app->byte_input = byte_input_alloc(); view_dispatcher_add_view( - app->view_dispatcher, BadKbAppViewConfigMac, byte_input_get_view(app->byte_input)); + app->view_dispatcher, BadKbAppViewByteInput, byte_input_get_view(app->byte_input)); view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); - if(furi_hal_usb_is_locked()) { - app->error = BadKbAppErrorCloseRpc; - app->conn_init_thread = NULL; - scene_manager_next_scene(app->scene_manager, BadKbSceneError); + app->conn_mode = BadKbConnModeNone; + app->conn_init_thread = + furi_thread_alloc_ex("BadKbConnInit", 1024, (FuriThreadCallback)bad_kb_conn_apply, app); + furi_thread_start(app->conn_init_thread); + if(!furi_string_empty(app->file_path)) { + app->bad_kb_script = bad_kb_script_open(app->file_path, app->is_bt ? app->bt : NULL, app); + bad_kb_script_set_keyboard_layout(app->bad_kb_script, app->keyboard_layout); + scene_manager_next_scene(app->scene_manager, BadKbSceneWork); } else { - app->conn_init_thread = furi_thread_alloc_ex( - "BadKbConnInit", 1024, (FuriThreadCallback)bad_kb_connection_init, app); - furi_thread_start(app->conn_init_thread); - if(!furi_string_empty(app->file_path)) { - app->bad_kb_script = - bad_kb_script_open(app->file_path, app->is_bt ? app->bt : NULL, app); - bad_kb_script_set_keyboard_layout(app->bad_kb_script, app->keyboard_layout); - scene_manager_next_scene(app->scene_manager, BadKbSceneWork); - } else { - furi_string_set(app->file_path, BAD_KB_APP_BASE_FOLDER); - scene_manager_next_scene(app->scene_manager, BadKbSceneFileSelect); - } + furi_string_set(app->file_path, BAD_KB_APP_BASE_FOLDER); + scene_manager_next_scene(app->scene_manager, BadKbSceneFileSelect); } return app; @@ -293,32 +215,34 @@ void bad_kb_app_free(BadKbApp* app) { bad_kb_free(app->bad_kb_view); // Custom Widget - view_dispatcher_remove_view(app->view_dispatcher, BadKbAppViewError); + view_dispatcher_remove_view(app->view_dispatcher, BadKbAppViewWidget); widget_free(app->widget); // Variable item list - view_dispatcher_remove_view(app->view_dispatcher, BadKbAppViewConfig); + view_dispatcher_remove_view(app->view_dispatcher, BadKbAppViewVarItemList); variable_item_list_free(app->var_item_list); // Text Input - view_dispatcher_remove_view(app->view_dispatcher, BadKbAppViewConfigName); + view_dispatcher_remove_view(app->view_dispatcher, BadKbAppViewTextInput); text_input_free(app->text_input); // Byte Input - view_dispatcher_remove_view(app->view_dispatcher, BadKbAppViewConfigMac); + view_dispatcher_remove_view(app->view_dispatcher, BadKbAppViewByteInput); byte_input_free(app->byte_input); // View dispatcher view_dispatcher_free(app->view_dispatcher); scene_manager_free(app->scene_manager); - // Restore bt config + // Restore connection config app->bt->suppress_pin_screen = false; if(app->conn_init_thread) { furi_thread_join(app->conn_init_thread); furi_thread_free(app->conn_init_thread); - bad_kb_connection_deinit(app); + app->conn_init_thread = NULL; } + bad_kb_conn_reset(app); + if(app->hid_cfg) free(app->hid_cfg); // Close records furi_record_close(RECORD_GUI); diff --git a/applications/main/bad_kb/bad_kb_app.h b/applications/main/bad_kb/bad_kb_app.h index 20bc87ce44..e2786d301d 100644 --- a/applications/main/bad_kb/bad_kb_app.h +++ b/applications/main/bad_kb/bad_kb_app.h @@ -1,7 +1,5 @@ #pragma once -#include "bad_kb_app.h" -#include "bad_kb_paths.h" #include "scenes/bad_kb_scene.h" #include "helpers/ducky_script.h" @@ -15,21 +13,15 @@ #define BAD_KB_APP_LAYOUT_EXTENSION ".kl" typedef enum BadKbCustomEvent { - BadKbAppCustomEventTextEditResult, + BadKbAppCustomEventTextInputDone, BadKbAppCustomEventByteInputDone, BadKbCustomEventErrorBack } BadKbCustomEvent; typedef enum { - BadKbAppViewError, + BadKbAppViewWidget, BadKbAppViewWork, - BadKbAppViewConfig, - BadKbAppViewConfigMac, - BadKbAppViewConfigName + BadKbAppViewVarItemList, + BadKbAppViewByteInput, + BadKbAppViewTextInput } BadKbAppView; - -void bad_kb_config_switch_remember_mode(BadKbApp* app); - -int32_t bad_kb_connection_init(BadKbApp* app); - -void bad_kb_connection_deinit(BadKbApp* app); diff --git a/applications/main/bad_kb/helpers/ducky_script.c b/applications/main/bad_kb/helpers/ducky_script.c index 57742a4643..8f45b5e1f3 100644 --- a/applications/main/bad_kb/helpers/ducky_script.c +++ b/applications/main/bad_kb/helpers/ducky_script.c @@ -11,11 +11,13 @@ #include "ducky_script_i.h" #include #include +#include +#include "../scenes/bad_kb_scene.h" -const uint8_t BAD_KB_BOUND_MAC_ADDRESS[BAD_KB_MAC_ADDRESS_LEN] = - {0x41, 0x4a, 0xef, 0xb6, 0xa9, 0xd4}; -const uint8_t BAD_KB_EMPTY_MAC_ADDRESS[BAD_KB_MAC_ADDRESS_LEN] = - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; +const uint8_t BAD_KB_EMPTY_MAC[BAD_KB_MAC_LEN] = FURI_HAL_BT_EMPTY_MAC_ADDR; + +// Adjusts to serial MAC +2 in app init +uint8_t BAD_KB_BOUND_MAC[BAD_KB_MAC_LEN] = FURI_HAL_BT_EMPTY_MAC_ADDR; #define TAG "BadKB" #define WORKER_TAG TAG "Worker" @@ -23,15 +25,13 @@ const uint8_t BAD_KB_EMPTY_MAC_ADDRESS[BAD_KB_MAC_ADDRESS_LEN] = #define BADKB_ASCII_TO_KEY(script, x) \ (((uint8_t)x < 128) ? (script->layout[(uint8_t)x]) : HID_KEYBOARD_NONE) -/** - * Delays for waiting between HID key press and key release -*/ +// Delays for waiting between HID key press and key release const uint8_t bt_hid_delays[LevelRssiNum] = { - 30, // LevelRssi122_100 - 25, // LevelRssi99_80 - 20, // LevelRssi79_60 - 17, // LevelRssi59_40 - 14, // LevelRssi39_0 + 45, // LevelRssi122_100 + 38, // LevelRssi99_80 + 30, // LevelRssi79_60 + 26, // LevelRssi59_40 + 21, // LevelRssi39_0 }; uint8_t bt_timeout = 0; @@ -309,51 +309,52 @@ static int32_t ducky_parse_line(BadKbScript* bad_kb, FuriString* line) { } static bool ducky_set_usb_id(BadKbScript* bad_kb, const char* line) { - if(sscanf(line, "%lX:%lX", &bad_kb->hid_cfg.vid, &bad_kb->hid_cfg.pid) == 2) { - bad_kb->hid_cfg.manuf[0] = '\0'; - bad_kb->hid_cfg.product[0] = '\0'; + FuriHalUsbHidConfig* cfg = &bad_kb->app->id_config.usb_cfg; + + if(sscanf(line, "%lX:%lX", &cfg->vid, &cfg->pid) == 2) { + cfg->manuf[0] = '\0'; + cfg->product[0] = '\0'; uint8_t id_len = ducky_get_command_len(line); if(!ducky_is_line_end(line[id_len + 1])) { - sscanf( - &line[id_len + 1], - "%31[^\r\n:]:%31[^\r\n]", - bad_kb->hid_cfg.manuf, - bad_kb->hid_cfg.product); + sscanf(&line[id_len + 1], "%31[^\r\n:]:%31[^\r\n]", cfg->manuf, cfg->product); } FURI_LOG_D( WORKER_TAG, - "set id: %04lX:%04lX mfr:%s product:%s", - bad_kb->hid_cfg.vid, - bad_kb->hid_cfg.pid, - bad_kb->hid_cfg.manuf, - bad_kb->hid_cfg.product); + "set usb id: %04lX:%04lX mfr:%s product:%s", + cfg->vid, + cfg->pid, + cfg->manuf, + cfg->product); return true; } return false; } static bool ducky_set_bt_id(BadKbScript* bad_kb, const char* line) { + BadKbConfig* cfg = &bad_kb->app->id_config; + size_t line_len = strlen(line); - size_t mac_len = BAD_KB_MAC_ADDRESS_LEN * 3; + size_t mac_len = BAD_KB_MAC_LEN * 3; // 2 text chars + separator per byte if(line_len < mac_len + 1) return false; // MAC + at least 1 char for name - uint8_t mac[BAD_KB_MAC_ADDRESS_LEN]; - for(size_t i = 0; i < BAD_KB_MAC_ADDRESS_LEN; i++) { + for(size_t i = 0; i < BAD_KB_MAC_LEN; i++) { char a = line[i * 3]; char b = line[i * 3 + 1]; if((a < 'A' && a > 'F') || (a < '0' && a > '9') || (b < 'A' && b > 'F') || - (b < '0' && b > '9') || !hex_char_to_uint8(a, b, &mac[i])) { + (b < '0' && b > '9') || !hex_char_to_uint8(a, b, &cfg->bt_mac[i])) { return false; } } + furi_hal_bt_reverse_mac_addr(cfg->bt_mac); - furi_hal_bt_set_profile_adv_name(FuriHalBtProfileHidKeyboard, line + mac_len); - bt_set_profile_mac_address(bad_kb->bt, mac); + strlcpy(cfg->bt_name, line + mac_len, BAD_KB_NAME_LEN); + FURI_LOG_D(WORKER_TAG, "set bt id: %s", line); return true; } -static bool ducky_script_preload(BadKbScript* bad_kb, File* script_file) { +static void ducky_script_preload(BadKbScript* bad_kb, File* script_file) { + BadKbApp* app = bad_kb->app; uint8_t ret = 0; uint32_t line_len = 0; @@ -380,81 +381,23 @@ static bool ducky_script_preload(BadKbScript* bad_kb, File* script_file) { } } while(ret > 0); - const char* line_tmp = furi_string_get_cstr(bad_kb->line); - if(bad_kb->app->switch_mode_thread) { - furi_thread_join(bad_kb->app->switch_mode_thread); - furi_thread_free(bad_kb->app->switch_mode_thread); - bad_kb->app->switch_mode_thread = NULL; - } // Looking for ID or BT_ID command at first line - bad_kb->set_usb_id = false; - bad_kb->set_bt_id = false; - bad_kb->has_usb_id = strncmp(line_tmp, ducky_cmd_id, strlen(ducky_cmd_id)) == 0; - bad_kb->has_bt_id = strncmp(line_tmp, ducky_cmd_bt_id, strlen(ducky_cmd_bt_id)) == 0; - if(bad_kb->has_usb_id) { - if(bad_kb->bt) { - bad_kb->app->is_bt = false; - bad_kb->app->switch_mode_thread = furi_thread_alloc_ex( - "BadKbSwitchMode", - 1024, - (FuriThreadCallback)bad_kb_config_switch_mode, - bad_kb->app); - furi_thread_start(bad_kb->app->switch_mode_thread); - return false; - } - bad_kb->set_usb_id = ducky_set_usb_id(bad_kb, &line_tmp[strlen(ducky_cmd_id) + 1]); - } else if(bad_kb->has_bt_id) { - if(!bad_kb->bt) { - bad_kb->app->is_bt = true; - bad_kb->app->switch_mode_thread = furi_thread_alloc_ex( - "BadKbSwitchMode", - 1024, - (FuriThreadCallback)bad_kb_config_switch_mode, - bad_kb->app); - furi_thread_start(bad_kb->app->switch_mode_thread); - return false; - } - if(!bad_kb->app->bt_remember) { - bad_kb->set_bt_id = ducky_set_bt_id(bad_kb, &line_tmp[strlen(ducky_cmd_bt_id) + 1]); - } - } - bad_kb_config_refresh_menu(bad_kb->app); - - if(bad_kb->bt) { - if(!bad_kb->set_bt_id) { - const char* bt_name = bad_kb->app->config.bt_name; - const uint8_t* bt_mac = bad_kb->app->bt_remember ? - (uint8_t*)&BAD_KB_BOUND_MAC_ADDRESS : - bad_kb->app->config.bt_mac; - bool reset_name = strncmp( - bt_name, - furi_hal_bt_get_profile_adv_name(FuriHalBtProfileHidKeyboard), - BAD_KB_ADV_NAME_MAX_LEN); - bool reset_mac = memcmp( - bt_mac, - furi_hal_bt_get_profile_mac_addr(FuriHalBtProfileHidKeyboard), - BAD_KB_MAC_ADDRESS_LEN); - if(reset_name && reset_mac) { - furi_hal_bt_set_profile_adv_name(FuriHalBtProfileHidKeyboard, bt_name); - } else if(reset_name) { - bt_set_profile_adv_name(bad_kb->bt, bt_name); - } - if(reset_mac) { - bt_set_profile_mac_address(bad_kb->bt, bt_mac); - } - } - } else { - if(bad_kb->set_usb_id) { - furi_check(furi_hal_usb_set_config(&usb_hid, &bad_kb->hid_cfg)); - } else { - furi_check(furi_hal_usb_set_config(&usb_hid, NULL)); - } + const char* line_tmp = furi_string_get_cstr(bad_kb->line); + app->set_usb_id = false; + app->set_bt_id = false; + app->has_usb_id = strncmp(line_tmp, ducky_cmd_id, strlen(ducky_cmd_id)) == 0; + app->has_bt_id = strncmp(line_tmp, ducky_cmd_bt_id, strlen(ducky_cmd_bt_id)) == 0; + + if(app->has_usb_id) { + app->is_bt = false; + app->set_usb_id = ducky_set_usb_id(bad_kb, &line_tmp[strlen(ducky_cmd_id) + 1]); + } else if(app->has_bt_id) { + app->is_bt = true; + app->set_bt_id = ducky_set_bt_id(bad_kb, &line_tmp[strlen(ducky_cmd_bt_id) + 1]); } storage_file_seek(script_file, 0, true); furi_string_reset(bad_kb->line); - - return true; } static int32_t ducky_script_execute_next(BadKbScript* bad_kb, File* script_file) { @@ -566,6 +509,189 @@ static uint32_t bad_kb_flags_get(uint32_t flags_mask, uint32_t timeout) { return flags; } +int32_t bad_kb_conn_apply(BadKbApp* app) { + if(app->is_bt) { + // Shorthands so this bs is readable + BadKbConfig* cfg = app->set_bt_id ? &app->id_config : &app->config; + FuriHalBtProfile kbd = FuriHalBtProfileHidKeyboard; + + // Setup new config + bt_timeout = bt_hid_delays[LevelRssi39_0]; + bt_disconnect(app->bt); + furi_delay_ms(200); + bt_keys_storage_set_storage_path(app->bt, BAD_KB_KEYS_PATH); + furi_hal_bt_set_profile_adv_name(kbd, cfg->bt_name); + if(app->bt_remember) { + furi_hal_bt_set_profile_mac_addr(kbd, BAD_KB_BOUND_MAC); + furi_hal_bt_set_profile_pairing_method(kbd, GapPairingPinCodeVerifyYesNo); + } else { + furi_hal_bt_set_profile_mac_addr(kbd, cfg->bt_mac); + furi_hal_bt_set_profile_pairing_method(kbd, GapPairingNone); + } + + // Set profile, restart BT, adjust defaults + furi_check(bt_set_profile(app->bt, BtProfileHidKeyboard)); + + // Advertise even if BT is off in settings + furi_hal_bt_start_advertising(); + + // Toggle key callback after since BT restart resets it + if(app->bt_remember) { + bt_enable_peer_key_update(app->bt); + } else { + bt_disable_peer_key_update(app->bt); + } + + app->conn_mode = BadKbConnModeBt; + + } else { + // Unlock RPC connections + furi_hal_usb_unlock(); + + // Context will apply with set_config only if pointer address is different, so we use a copy + FuriHalUsbHidConfig* hid_cfg = malloc(sizeof(FuriHalUsbHidConfig)); + memcpy( + hid_cfg, + app->set_usb_id ? &app->id_config.usb_cfg : &app->config.usb_cfg, + sizeof(FuriHalUsbHidConfig)); + furi_check(furi_hal_usb_set_config(&usb_hid, hid_cfg)); + if(app->hid_cfg) free(app->hid_cfg); + app->hid_cfg = hid_cfg; + + app->conn_mode = BadKbConnModeUsb; + } + + return 0; +} + +void bad_kb_conn_reset(BadKbApp* app) { + if(app->conn_mode == BadKbConnModeBt) { + // TODO: maybe also restore BT profile? + bt_disconnect(app->bt); + furi_delay_ms(200); + bt_keys_storage_set_default_path(app->bt); + FuriHalBtProfile kbd = FuriHalBtProfileHidKeyboard; + furi_hal_bt_set_profile_mac_addr(kbd, app->prev_bt_mac); + furi_hal_bt_set_profile_adv_name(kbd, app->prev_bt_name); + furi_hal_bt_set_profile_pairing_method(kbd, app->prev_bt_mode); + furi_check(bt_set_profile(app->bt, BtProfileSerial)); + bt_enable_peer_key_update(app->bt); + + } else if(app->conn_mode == BadKbConnModeUsb) { + // TODO: maybe also restore USB context? + furi_check(furi_hal_usb_set_config(app->prev_usb_mode, NULL)); + } + + app->conn_mode = BadKbConnModeNone; +} + +void bad_kb_config_adjust(BadKbConfig* cfg) { + // Avoid empty name + if(strcmp(cfg->bt_name, "") == 0) { + snprintf(cfg->bt_name, BAD_KB_NAME_LEN, "Control %s", furi_hal_version_get_name_ptr()); + } + + // MAC is adjusted by furi_hal_bt, adjust here too so it matches after applying + const uint8_t* normal_mac = furi_hal_version_get_ble_mac(); + uint8_t empty_mac[BAD_KB_MAC_LEN] = FURI_HAL_BT_EMPTY_MAC_ADDR; + uint8_t default_mac[BAD_KB_MAC_LEN] = FURI_HAL_BT_DEFAULT_MAC_ADDR; + if(memcmp(cfg->bt_mac, empty_mac, BAD_KB_MAC_LEN) == 0 || + memcmp(cfg->bt_mac, normal_mac, BAD_KB_MAC_LEN) == 0 || + memcmp(cfg->bt_mac, default_mac, BAD_KB_MAC_LEN) == 0) { + memcpy(cfg->bt_mac, normal_mac, BAD_KB_MAC_LEN); + cfg->bt_mac[2]++; + } + + // Use defaults if vid or pid are unset + if(cfg->usb_cfg.vid == 0) cfg->usb_cfg.vid = HID_VID_DEFAULT; + if(cfg->usb_cfg.pid == 0) cfg->usb_cfg.pid = HID_PID_DEFAULT; +} + +void bad_kb_config_refresh(BadKbApp* app) { + bt_set_status_changed_callback(app->bt, NULL, NULL); + furi_hal_hid_set_state_callback(NULL, NULL); + if(app->bad_kb_script) { + furi_thread_flags_set(furi_thread_get_id(app->bad_kb_script->thread), WorkerEvtDisconnect); + } + if(app->conn_init_thread) { + furi_thread_join(app->conn_init_thread); + } + + bool apply = false; + if(app->is_bt) { + BadKbConfig* cfg = app->set_bt_id ? &app->id_config : &app->config; + bad_kb_config_adjust(cfg); + + if(app->conn_mode != BadKbConnModeBt) { + apply = true; + bad_kb_conn_reset(app); + } else { + apply = apply || strncmp( + cfg->bt_name, + furi_hal_bt_get_profile_adv_name(FuriHalBtProfileHidKeyboard), + BAD_KB_NAME_LEN); + apply = apply || memcmp( + app->bt_remember ? BAD_KB_BOUND_MAC : cfg->bt_mac, + furi_hal_bt_get_profile_mac_addr(FuriHalBtProfileHidKeyboard), + BAD_KB_MAC_LEN); + } + } else { + BadKbConfig* cfg = app->set_usb_id ? &app->id_config : &app->config; + bad_kb_config_adjust(cfg); + + if(app->conn_mode != BadKbConnModeUsb) { + apply = true; + bad_kb_conn_reset(app); + } else { + void* ctx; + if(furi_hal_usb_get_config() == &usb_hid && + (ctx = furi_hal_usb_get_config_context()) != NULL) { + FuriHalUsbHidConfig* cur = ctx; + apply = apply || cfg->usb_cfg.vid != cur->vid; + apply = apply || cfg->usb_cfg.pid != cur->pid; + apply = apply || strncmp(cfg->usb_cfg.manuf, cur->manuf, sizeof(cur->manuf)); + apply = apply || strncmp(cfg->usb_cfg.product, cur->product, sizeof(cur->product)); + } else { + apply = true; + } + } + } + + if(apply) { + bad_kb_conn_apply(app); + } + + if(app->bad_kb_script) { + BadKbScript* script = app->bad_kb_script; + script->st.is_bt = app->is_bt; + script->bt = app->is_bt ? app->bt : NULL; + bool connected; + if(app->is_bt) { + bt_set_status_changed_callback(app->bt, bad_kb_bt_hid_state_callback, script); + connected = furi_hal_bt_is_connected(); + } else { + furi_hal_hid_set_state_callback(bad_kb_usb_hid_state_callback, script); + connected = furi_hal_hid_is_connected(); + } + if(connected) { + furi_thread_flags_set(furi_thread_get_id(script->thread), WorkerEvtConnect); + } + } + + // Reload config page + scene_manager_next_scene(app->scene_manager, BadKbSceneConfig); + scene_manager_previous_scene(app->scene_manager); + + // Update settings + XtremeSettings* xtreme_settings = XTREME_SETTINGS(); + if(xtreme_settings->bad_bt != app->is_bt || + xtreme_settings->bad_bt_remember != app->bt_remember) { + xtreme_settings->bad_bt = app->is_bt; + xtreme_settings->bad_bt_remember = app->bt_remember; + XTREME_SETTINGS_SAVE(); + } +} + static int32_t bad_kb_worker(void* context) { BadKbScript* bad_kb = context; @@ -579,33 +705,18 @@ static int32_t bad_kb_worker(void* context) { bad_kb->line_prev = furi_string_alloc(); bad_kb->string_print = furi_string_alloc(); - if(bad_kb->bt) { - bt_set_status_changed_callback(bad_kb->bt, bad_kb_bt_hid_state_callback, bad_kb); - } else { - furi_hal_hid_set_state_callback(bad_kb_usb_hid_state_callback, bad_kb); - } - while(1) { if(worker_state == BadKbStateInit) { // State: initialization + FURI_LOG_D(WORKER_TAG, "init start"); if(storage_file_open( script_file, furi_string_get_cstr(bad_kb->file_path), FSAM_READ, FSOM_OPEN_EXISTING)) { - if((ducky_script_preload(bad_kb, script_file)) && (bad_kb->st.line_nb > 0)) { - if(bad_kb->bt) { - if(furi_hal_bt_is_connected()) { - worker_state = BadKbStateIdle; // Ready to run - } else { - worker_state = BadKbStateNotConnected; // Not connected - } - } else { - if(furi_hal_hid_is_connected()) { - worker_state = BadKbStateIdle; // Ready to run - } else { - worker_state = BadKbStateNotConnected; // Not connected - } - } + ducky_script_preload(bad_kb, script_file); + if(bad_kb->st.line_nb > 0) { + bad_kb_config_refresh(bad_kb->app); + worker_state = BadKbStateNotConnected; // Refresh will set connected flag } else { worker_state = BadKbStateScriptError; // Script preload error } @@ -614,10 +725,14 @@ static int32_t bad_kb_worker(void* context) { worker_state = BadKbStateFileError; // File open error } bad_kb->st.state = worker_state; + FURI_LOG_D(WORKER_TAG, "init done"); } else if(worker_state == BadKbStateNotConnected) { // State: Not connected + FURI_LOG_D(WORKER_TAG, "not connected wait"); uint32_t flags = bad_kb_flags_get( - WorkerEvtEnd | WorkerEvtConnect | WorkerEvtStartStop, FuriWaitForever); + WorkerEvtEnd | WorkerEvtConnect | WorkerEvtDisconnect | WorkerEvtStartStop, + FuriWaitForever); + FURI_LOG_D(WORKER_TAG, "not connected flags: %lu", flags); if(flags & WorkerEvtEnd) { break; @@ -629,13 +744,16 @@ static int32_t bad_kb_worker(void* context) { bad_kb->st.state = worker_state; } else if(worker_state == BadKbStateIdle) { // State: ready to start + FURI_LOG_D(WORKER_TAG, "idle wait"); uint32_t flags = bad_kb_flags_get( - WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtDisconnect, FuriWaitForever); + WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtConnect | WorkerEvtDisconnect, + FuriWaitForever); + FURI_LOG_D(WORKER_TAG, "idle flags: %lu", flags); if(flags & WorkerEvtEnd) { break; } else if(flags & WorkerEvtStartStop) { // Start executing script - DOLPHIN_DEED(DolphinDeedBadKbPlayScript); + dolphin_deed(DolphinDeedBadKbPlayScript); delay_val = 0; bad_kb->buf_len = 0; bad_kb->st.line_cur = 0; @@ -653,13 +771,16 @@ static int32_t bad_kb_worker(void* context) { bad_kb->st.state = worker_state; } else if(worker_state == BadKbStateWillRun) { // State: start on connection + FURI_LOG_D(WORKER_TAG, "will run wait"); uint32_t flags = bad_kb_flags_get( - WorkerEvtEnd | WorkerEvtConnect | WorkerEvtStartStop, FuriWaitForever); + WorkerEvtEnd | WorkerEvtConnect | WorkerEvtDisconnect | WorkerEvtStartStop, + FuriWaitForever); + FURI_LOG_D(WORKER_TAG, "will run flags: %lu", flags); if(flags & WorkerEvtEnd) { break; } else if(flags & WorkerEvtConnect) { // Start executing script - DOLPHIN_DEED(DolphinDeedBadKbPlayScript); + dolphin_deed(DolphinDeedBadKbPlayScript); delay_val = 0; bad_kb->buf_len = 0; bad_kb->st.line_cur = 0; @@ -672,7 +793,7 @@ static int32_t bad_kb_worker(void* context) { flags = furi_thread_flags_wait( WorkerEvtEnd | WorkerEvtDisconnect | WorkerEvtStartStop, FuriFlagWaitAny | FuriFlagNoClear, - 1500); + bad_kb->bt ? 3000 : 1500); if(flags == (unsigned)FuriFlagErrorTimeout) { // If nothing happened - start script execution worker_state = BadKbStateRunning; @@ -690,11 +811,14 @@ static int32_t bad_kb_worker(void* context) { bad_kb->st.state = worker_state; } else if(worker_state == BadKbStateRunning) { // State: running + FURI_LOG_D(WORKER_TAG, "running"); uint16_t delay_cur = (delay_val > 1000) ? (1000) : (delay_val); uint32_t flags = furi_thread_flags_wait( - WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtPauseResume | WorkerEvtDisconnect, + WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtPauseResume | WorkerEvtConnect | + WorkerEvtDisconnect, FuriFlagWaitAny, delay_cur); + FURI_LOG_D(WORKER_TAG, "running flags: %lu", flags); delay_val -= delay_cur; if(!(flags & FuriFlagError)) { @@ -763,9 +887,12 @@ static int32_t bad_kb_worker(void* context) { furi_check((flags & FuriFlagError) == 0); } } else if(worker_state == BadKbStateWaitForBtn) { // State: Wait for button Press + FURI_LOG_D(WORKER_TAG, "button wait"); uint32_t flags = bad_kb_flags_get( - WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtPauseResume | WorkerEvtDisconnect, + WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtPauseResume | WorkerEvtConnect | + WorkerEvtDisconnect, FuriWaitForever); + FURI_LOG_D(WORKER_TAG, "button flags: %lu", flags); if(!(flags & FuriFlagError)) { if(flags & WorkerEvtEnd) { break; @@ -774,26 +901,41 @@ static int32_t bad_kb_worker(void* context) { worker_state = BadKbStateRunning; } else if(flags & WorkerEvtDisconnect) { worker_state = BadKbStateNotConnected; // Disconnected - furi_hal_hid_kb_release_all(); + if(bad_kb->bt) { + furi_hal_bt_hid_kb_release_all(); + } else { + furi_hal_hid_kb_release_all(); + } } bad_kb->st.state = worker_state; continue; } } else if(worker_state == BadKbStatePaused) { // State: Paused + FURI_LOG_D(WORKER_TAG, "paused wait"); uint32_t flags = bad_kb_flags_get( - WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtPauseResume | WorkerEvtDisconnect, + WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtPauseResume | WorkerEvtConnect | + WorkerEvtDisconnect, FuriWaitForever); + FURI_LOG_D(WORKER_TAG, "paused flags: %lu", flags); if(!(flags & FuriFlagError)) { if(flags & WorkerEvtEnd) { break; } else if(flags & WorkerEvtStartStop) { worker_state = BadKbStateIdle; // Stop executing script bad_kb->st.state = worker_state; - furi_hal_hid_kb_release_all(); + if(bad_kb->bt) { + furi_hal_bt_hid_kb_release_all(); + } else { + furi_hal_hid_kb_release_all(); + } } else if(flags & WorkerEvtDisconnect) { worker_state = BadKbStateNotConnected; // Disconnected bad_kb->st.state = worker_state; - furi_hal_hid_kb_release_all(); + if(bad_kb->bt) { + furi_hal_bt_hid_kb_release_all(); + } else { + furi_hal_hid_kb_release_all(); + } } else if(flags & WorkerEvtPauseResume) { if(pause_state == BadKbStateRunning) { if(delay_val > 0) { @@ -812,9 +954,12 @@ static int32_t bad_kb_worker(void* context) { continue; } } else if(worker_state == BadKbStateStringDelay) { // State: print string with delays + FURI_LOG_D(WORKER_TAG, "delay wait"); uint32_t flags = bad_kb_flags_get( - WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtPauseResume | WorkerEvtDisconnect, + WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtPauseResume | WorkerEvtConnect | + WorkerEvtDisconnect, bad_kb->stringdelay); + FURI_LOG_D(WORKER_TAG, "delay flags: %lu", flags); if(!(flags & FuriFlagError)) { if(flags & WorkerEvtEnd) { @@ -853,8 +998,10 @@ static int32_t bad_kb_worker(void* context) { } else if( (worker_state == BadKbStateFileError) || (worker_state == BadKbStateScriptError)) { // State: error + FURI_LOG_D(WORKER_TAG, "error wait"); uint32_t flags = bad_kb_flags_get(WorkerEvtEnd, FuriWaitForever); // Waiting for exit command + FURI_LOG_D(WORKER_TAG, "error flags: %lu", flags); if(flags & WorkerEvtEnd) { break; @@ -865,11 +1012,8 @@ static int32_t bad_kb_worker(void* context) { } } - if(bad_kb->bt) { - bt_set_status_changed_callback(bad_kb->bt, NULL, NULL); - } else { - furi_hal_hid_set_state_callback(NULL, NULL); - } + bt_set_status_changed_callback(bad_kb->app->bt, NULL, NULL); + furi_hal_hid_set_state_callback(NULL, NULL); storage_file_close(script_file); storage_file_free(script_file); diff --git a/applications/main/bad_kb/helpers/ducky_script.h b/applications/main/bad_kb/helpers/ducky_script.h index ecdf8fc02e..3586179030 100644 --- a/applications/main/bad_kb/helpers/ducky_script.h +++ b/applications/main/bad_kb/helpers/ducky_script.h @@ -14,6 +14,7 @@ extern "C" { #include #include #include "../views/bad_kb_view.h" +#include "../bad_kb_paths.h" #define FILE_BUFFER_LEN 16 @@ -60,7 +61,6 @@ struct BadKbState { typedef struct BadKbApp BadKbApp; typedef struct { - FuriHalUsbHidConfig hid_cfg; FuriThread* thread; BadKbState st; @@ -83,11 +83,6 @@ typedef struct { FuriString* string_print; size_t string_print_pos; - bool set_usb_id; - bool set_bt_id; - bool has_usb_id; - bool has_bt_id; - Bt* bt; BadKbApp* app; } BadKbScript; @@ -108,25 +103,29 @@ void bad_kb_script_pause_resume(BadKbScript* bad_kb); BadKbState* bad_kb_script_get_state(BadKbScript* bad_kb); -#define BAD_KB_ADV_NAME_MAX_LEN FURI_HAL_BT_ADV_NAME_LENGTH -#define BAD_KB_MAC_ADDRESS_LEN GAP_MAC_ADDR_SIZE +#define BAD_KB_NAME_LEN FURI_HAL_BT_ADV_NAME_LENGTH +#define BAD_KB_MAC_LEN GAP_MAC_ADDR_SIZE +#define BAD_KB_USB_LEN HID_MANUF_PRODUCT_NAME_LEN -// this is the MAC address used when we do not forget paired device (BOUND STATE) -extern const uint8_t BAD_KB_BOUND_MAC_ADDRESS[BAD_KB_MAC_ADDRESS_LEN]; -extern const uint8_t BAD_KB_EMPTY_MAC_ADDRESS[BAD_KB_MAC_ADDRESS_LEN]; +extern const uint8_t BAD_KB_EMPTY_MAC[BAD_KB_MAC_LEN]; +extern uint8_t BAD_KB_BOUND_MAC[BAD_KB_MAC_LEN]; // For remember mode typedef enum { BadKbAppErrorNoFiles, - BadKbAppErrorCloseRpc, } BadKbAppError; typedef struct { - char bt_name[BAD_KB_ADV_NAME_MAX_LEN + 1]; - uint8_t bt_mac[BAD_KB_MAC_ADDRESS_LEN]; - FuriHalUsbInterface* usb_mode; - GapPairing bt_mode; + char bt_name[BAD_KB_NAME_LEN]; + uint8_t bt_mac[BAD_KB_MAC_LEN]; + FuriHalUsbHidConfig usb_cfg; } BadKbConfig; +typedef enum { + BadKbConnModeNone, + BadKbConnModeUsb, + BadKbConnModeBt, +} BadKbConnMode; + struct BadKbApp { Gui* gui; ViewDispatcher* view_dispatcher; @@ -137,6 +136,10 @@ struct BadKbApp { VariableItemList* var_item_list; TextInput* text_input; ByteInput* byte_input; + char usb_name_buf[BAD_KB_USB_LEN]; + uint16_t usb_vidpid_buf[2]; + char bt_name_buf[BAD_KB_NAME_LEN]; + uint8_t bt_mac_buf[BAD_KB_MAC_LEN]; BadKbAppError error; FuriString* file_path; @@ -147,15 +150,31 @@ struct BadKbApp { Bt* bt; bool is_bt; bool bt_remember; - BadKbConfig config; - BadKbConfig prev_config; + BadKbConfig config; // User options + BadKbConfig id_config; // ID and BT_ID values + + bool set_usb_id; + bool set_bt_id; + bool has_usb_id; + bool has_bt_id; + + GapPairing prev_bt_mode; + char prev_bt_name[BAD_KB_NAME_LEN]; + uint8_t prev_bt_mac[BAD_KB_MAC_LEN]; + FuriHalUsbInterface* prev_usb_mode; + + FuriHalUsbHidConfig* hid_cfg; + BadKbConnMode conn_mode; FuriThread* conn_init_thread; - FuriThread* switch_mode_thread; }; -int32_t bad_kb_config_switch_mode(BadKbApp* app); +int32_t bad_kb_conn_apply(BadKbApp* app); + +void bad_kb_conn_reset(BadKbApp* app); + +void bad_kb_config_refresh(BadKbApp* app); -void bad_kb_config_refresh_menu(BadKbApp* app); +void bad_kb_config_adjust(BadKbConfig* cfg); #ifdef __cplusplus } diff --git a/applications/main/bad_kb/helpers/ducky_script_commands.c b/applications/main/bad_kb/helpers/ducky_script_commands.c index 42ed1b9e8f..5bae8fc24e 100644 --- a/applications/main/bad_kb/helpers/ducky_script_commands.c +++ b/applications/main/bad_kb/helpers/ducky_script_commands.c @@ -86,8 +86,7 @@ static int32_t ducky_fnc_sysrq(BadKbScript* bad_kb, const char* line, int32_t pa furi_hal_bt_hid_kb_press(KEY_MOD_LEFT_ALT | HID_KEYBOARD_PRINT_SCREEN); furi_hal_bt_hid_kb_press(key); furi_delay_ms(bt_timeout); - furi_hal_bt_hid_kb_release(key); - furi_hal_bt_hid_kb_release(KEY_MOD_LEFT_ALT | HID_KEYBOARD_PRINT_SCREEN); + furi_hal_bt_hid_kb_release_all(); } else { furi_hal_hid_kb_press(KEY_MOD_LEFT_ALT | HID_KEYBOARD_PRINT_SCREEN); furi_hal_hid_kb_press(key); diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config.c b/applications/main/bad_kb/scenes/bad_kb_scene_config.c index e029d052e7..be285a4672 100644 --- a/applications/main/bad_kb/scenes/bad_kb_scene_config.c +++ b/applications/main/bad_kb/scenes/bad_kb_scene_config.c @@ -7,10 +7,20 @@ enum VarItemListIndex { VarItemListIndexKeyboardLayout, VarItemListIndexConnection, - VarItemListIndexBtRemember, +}; + +enum VarItemListIndexBt { + VarItemListIndexBtRemember = VarItemListIndexConnection + 1, VarItemListIndexBtDeviceName, VarItemListIndexBtMacAddress, - VarItemListIndexRandomizeBtMac, + VarItemListIndexBtRandomizeMac, +}; + +enum VarItemListIndexUsb { + VarItemListIndexUsbManufacturer = VarItemListIndexConnection + 1, + VarItemListIndexUsbProductName, + VarItemListIndexUsbVidPid, + VarItemListIndexUsbRandomizeVidPid, }; void bad_kb_scene_config_connection_callback(VariableItem* item) { @@ -23,8 +33,6 @@ void bad_kb_scene_config_connection_callback(VariableItem* item) { void bad_kb_scene_config_bt_remember_callback(VariableItem* item) { BadKbApp* bad_kb = variable_item_get_context(item); bad_kb->bt_remember = variable_item_get_current_value_index(item); - XTREME_SETTINGS()->bad_bt_remember = bad_kb->bt_remember; - XTREME_SETTINGS_SAVE(); variable_item_set_current_value_text(item, bad_kb->bt_remember ? "ON" : "OFF"); view_dispatcher_send_custom_event(bad_kb->view_dispatcher, VarItemListIndexBtRemember); } @@ -45,9 +53,9 @@ void bad_kb_scene_config_on_enter(void* context) { var_item_list, "Connection", 2, bad_kb_scene_config_connection_callback, bad_kb); variable_item_set_current_value_index(item, bad_kb->is_bt); variable_item_set_current_value_text(item, bad_kb->is_bt ? "BT" : "USB"); - if(bad_kb->bad_kb_script->has_usb_id) { + if(bad_kb->has_usb_id) { variable_item_set_locked(item, true, "Script has\nID cmd!\nLocked to\nUSB Mode!"); - } else if(bad_kb->bad_kb_script->has_bt_id) { + } else if(bad_kb->has_bt_id) { variable_item_set_locked(item, true, "Script has\nBT_ID cmd!\nLocked to\nBT Mode!"); } @@ -58,23 +66,43 @@ void bad_kb_scene_config_on_enter(void* context) { variable_item_set_current_value_text(item, bad_kb->bt_remember ? "ON" : "OFF"); item = variable_item_list_add(var_item_list, "BT Device Name", 0, NULL, bad_kb); - if(bad_kb->bad_kb_script->set_bt_id) { + if(bad_kb->set_bt_id) { variable_item_set_locked(item, true, "Script has\nBT_ID cmd!\nLocked to\nset Name!"); } item = variable_item_list_add(var_item_list, "BT MAC Address", 0, NULL, bad_kb); if(bad_kb->bt_remember) { variable_item_set_locked(item, true, "Remember\nmust be Off!"); - } else if(bad_kb->bad_kb_script->set_bt_id) { + } else if(bad_kb->set_bt_id) { variable_item_set_locked(item, true, "Script has\nBT_ID cmd!\nLocked to\nset MAC!"); } item = variable_item_list_add(var_item_list, "Randomize BT MAC", 0, NULL, bad_kb); if(bad_kb->bt_remember) { variable_item_set_locked(item, true, "Remember\nmust be Off!"); - } else if(bad_kb->bad_kb_script->set_bt_id) { + } else if(bad_kb->set_bt_id) { variable_item_set_locked(item, true, "Script has\nBT_ID cmd!\nLocked to\nset MAC!"); } + } else { + item = variable_item_list_add(var_item_list, "USB Manufacturer", 0, NULL, bad_kb); + if(bad_kb->set_usb_id) { + variable_item_set_locked(item, true, "Script has\nID cmd!\nLocked to\nset Mname!"); + } + + item = variable_item_list_add(var_item_list, "USB Product Name", 0, NULL, bad_kb); + if(bad_kb->set_usb_id) { + variable_item_set_locked(item, true, "Script has\nID cmd!\nLocked to\nset Pname!"); + } + + item = variable_item_list_add(var_item_list, "USB VID and PID", 0, NULL, bad_kb); + if(bad_kb->set_usb_id) { + variable_item_set_locked(item, true, "Script has\nID cmd!\nLocked to\nset IDs!"); + } + + item = variable_item_list_add(var_item_list, "Randomize USB VID:PID", 0, NULL, bad_kb); + if(bad_kb->set_usb_id) { + variable_item_set_locked(item, true, "Script has\nID cmd!\nLocked to\nset IDs!"); + } } variable_item_list_set_enter_callback( @@ -83,7 +111,7 @@ void bad_kb_scene_config_on_enter(void* context) { variable_item_list_set_selected_item( var_item_list, scene_manager_get_scene_state(bad_kb->scene_manager, BadKbSceneConfig)); - view_dispatcher_switch_to_view(bad_kb->view_dispatcher, BadKbAppViewConfig); + view_dispatcher_switch_to_view(bad_kb->view_dispatcher, BadKbAppViewVarItemList); } bool bad_kb_scene_config_on_event(void* context, SceneManagerEvent event) { @@ -98,26 +126,55 @@ bool bad_kb_scene_config_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigLayout); break; case VarItemListIndexConnection: - bad_kb_config_switch_mode(bad_kb); - break; - case VarItemListIndexBtRemember: - bad_kb_config_switch_remember_mode(bad_kb); - scene_manager_previous_scene(bad_kb->scene_manager); - scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfig); - break; - case VarItemListIndexBtDeviceName: - scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigName); - break; - case VarItemListIndexBtMacAddress: - scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigMac); - break; - case VarItemListIndexRandomizeBtMac: - furi_hal_random_fill_buf(bad_kb->config.bt_mac, BAD_KB_MAC_ADDRESS_LEN); - bt_set_profile_mac_address(bad_kb->bt, bad_kb->config.bt_mac); + bad_kb_config_refresh(bad_kb); break; default: break; } + if(bad_kb->is_bt) { + switch(event.event) { + case VarItemListIndexBtRemember: + bad_kb_config_refresh(bad_kb); + break; + case VarItemListIndexBtDeviceName: + scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigBtName); + break; + case VarItemListIndexBtMacAddress: + scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigBtMac); + break; + case VarItemListIndexBtRandomizeMac: + furi_hal_random_fill_buf(bad_kb->config.bt_mac, BAD_KB_MAC_LEN); + bad_kb_config_refresh(bad_kb); + break; + default: + break; + } + } else { + switch(event.event) { + case VarItemListIndexUsbManufacturer: + scene_manager_set_scene_state( + bad_kb->scene_manager, BadKbSceneConfigUsbName, true); + scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigUsbName); + break; + case VarItemListIndexUsbProductName: + scene_manager_set_scene_state( + bad_kb->scene_manager, BadKbSceneConfigUsbName, false); + scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigUsbName); + break; + case VarItemListIndexUsbVidPid: + scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigUsbVidPid); + break; + case VarItemListIndexUsbRandomizeVidPid: + furi_hal_random_fill_buf( + (void*)bad_kb->usb_vidpid_buf, sizeof(bad_kb->usb_vidpid_buf)); + bad_kb->config.usb_cfg.vid = bad_kb->usb_vidpid_buf[0]; + bad_kb->config.usb_cfg.pid = bad_kb->usb_vidpid_buf[1]; + bad_kb_config_refresh(bad_kb); + break; + default: + break; + } + } } return consumed; diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config.h b/applications/main/bad_kb/scenes/bad_kb_scene_config.h index ac445c673f..034a898a44 100644 --- a/applications/main/bad_kb/scenes/bad_kb_scene_config.h +++ b/applications/main/bad_kb/scenes/bad_kb_scene_config.h @@ -3,5 +3,7 @@ ADD_SCENE(bad_kb, work, Work) ADD_SCENE(bad_kb, error, Error) ADD_SCENE(bad_kb, config, Config) ADD_SCENE(bad_kb, config_layout, ConfigLayout) -ADD_SCENE(bad_kb, config_name, ConfigName) -ADD_SCENE(bad_kb, config_mac, ConfigMac) +ADD_SCENE(bad_kb, config_bt_name, ConfigBtName) +ADD_SCENE(bad_kb, config_bt_mac, ConfigBtMac) +ADD_SCENE(bad_kb, config_usb_name, ConfigUsbName) +ADD_SCENE(bad_kb, config_usb_vidpid, ConfigUsbVidPid) diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config_bt_mac.c b/applications/main/bad_kb/scenes/bad_kb_scene_config_bt_mac.c new file mode 100644 index 0000000000..e60d686228 --- /dev/null +++ b/applications/main/bad_kb/scenes/bad_kb_scene_config_bt_mac.c @@ -0,0 +1,50 @@ +#include "../bad_kb_app.h" + +void bad_kb_scene_config_bt_mac_byte_input_callback(void* context) { + BadKbApp* bad_kb = context; + + view_dispatcher_send_custom_event(bad_kb->view_dispatcher, BadKbAppCustomEventByteInputDone); +} + +void bad_kb_scene_config_bt_mac_on_enter(void* context) { + BadKbApp* bad_kb = context; + ByteInput* byte_input = bad_kb->byte_input; + + memmove(bad_kb->bt_mac_buf, bad_kb->config.bt_mac, BAD_KB_MAC_LEN); + furi_hal_bt_reverse_mac_addr(bad_kb->bt_mac_buf); + byte_input_set_header_text(byte_input, "Set BT MAC address"); + + byte_input_set_result_callback( + byte_input, + bad_kb_scene_config_bt_mac_byte_input_callback, + NULL, + bad_kb, + bad_kb->bt_mac_buf, + BAD_KB_MAC_LEN); + + view_dispatcher_switch_to_view(bad_kb->view_dispatcher, BadKbAppViewByteInput); +} + +bool bad_kb_scene_config_bt_mac_on_event(void* context, SceneManagerEvent event) { + BadKbApp* bad_kb = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + if(event.event == BadKbAppCustomEventByteInputDone) { + furi_hal_bt_reverse_mac_addr(bad_kb->bt_mac_buf); + memmove(bad_kb->config.bt_mac, bad_kb->bt_mac_buf, BAD_KB_MAC_LEN); + bad_kb_config_refresh(bad_kb); + } + scene_manager_previous_scene(bad_kb->scene_manager); + } + return consumed; +} + +void bad_kb_scene_config_bt_mac_on_exit(void* context) { + BadKbApp* bad_kb = context; + ByteInput* byte_input = bad_kb->byte_input; + + byte_input_set_result_callback(byte_input, NULL, NULL, NULL, NULL, 0); + byte_input_set_header_text(byte_input, ""); +} diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config_name.c b/applications/main/bad_kb/scenes/bad_kb_scene_config_bt_name.c similarity index 52% rename from applications/main/bad_kb/scenes/bad_kb_scene_config_name.c rename to applications/main/bad_kb/scenes/bad_kb_scene_config_bt_name.c index 40bfc4be80..3ce2c99b4a 100644 --- a/applications/main/bad_kb/scenes/bad_kb_scene_config_name.c +++ b/applications/main/bad_kb/scenes/bad_kb_scene_config_bt_name.c @@ -1,43 +1,45 @@ #include "../bad_kb_app.h" -static void bad_kb_scene_config_name_text_input_callback(void* context) { +static void bad_kb_scene_config_bt_name_text_input_callback(void* context) { BadKbApp* bad_kb = context; - view_dispatcher_send_custom_event(bad_kb->view_dispatcher, BadKbAppCustomEventTextEditResult); + view_dispatcher_send_custom_event(bad_kb->view_dispatcher, BadKbAppCustomEventTextInputDone); } -void bad_kb_scene_config_name_on_enter(void* context) { +void bad_kb_scene_config_bt_name_on_enter(void* context) { BadKbApp* bad_kb = context; TextInput* text_input = bad_kb->text_input; + strlcpy(bad_kb->bt_name_buf, bad_kb->config.bt_name, BAD_KB_NAME_LEN); text_input_set_header_text(text_input, "Set BT device name"); text_input_set_result_callback( text_input, - bad_kb_scene_config_name_text_input_callback, + bad_kb_scene_config_bt_name_text_input_callback, bad_kb, - bad_kb->config.bt_name, - BAD_KB_ADV_NAME_MAX_LEN, + bad_kb->bt_name_buf, + BAD_KB_NAME_LEN, true); - view_dispatcher_switch_to_view(bad_kb->view_dispatcher, BadKbAppViewConfigName); + view_dispatcher_switch_to_view(bad_kb->view_dispatcher, BadKbAppViewTextInput); } -bool bad_kb_scene_config_name_on_event(void* context, SceneManagerEvent event) { +bool bad_kb_scene_config_bt_name_on_event(void* context, SceneManagerEvent event) { BadKbApp* bad_kb = context; bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { consumed = true; - if(event.event == BadKbAppCustomEventTextEditResult) { - bt_set_profile_adv_name(bad_kb->bt, bad_kb->config.bt_name); + if(event.event == BadKbAppCustomEventTextInputDone) { + strlcpy(bad_kb->config.bt_name, bad_kb->bt_name_buf, BAD_KB_NAME_LEN); + bad_kb_config_refresh(bad_kb); } scene_manager_previous_scene(bad_kb->scene_manager); } return consumed; } -void bad_kb_scene_config_name_on_exit(void* context) { +void bad_kb_scene_config_bt_name_on_exit(void* context) { BadKbApp* bad_kb = context; TextInput* text_input = bad_kb->text_input; diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config_layout.c b/applications/main/bad_kb/scenes/bad_kb_scene_config_layout.c index 17477b2c27..31d2b49ecf 100644 --- a/applications/main/bad_kb/scenes/bad_kb_scene_config_layout.c +++ b/applications/main/bad_kb/scenes/bad_kb_scene_config_layout.c @@ -40,11 +40,9 @@ void bad_kb_scene_config_layout_on_enter(void* context) { bool bad_kb_scene_config_layout_on_event(void* context, SceneManagerEvent event) { UNUSED(context); UNUSED(event); - // BadKbApp* bad_kb = context; return false; } void bad_kb_scene_config_layout_on_exit(void* context) { UNUSED(context); - // BadKbApp* bad_kb = context; } diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config_mac.c b/applications/main/bad_kb/scenes/bad_kb_scene_config_mac.c deleted file mode 100644 index 2b30c90acd..0000000000 --- a/applications/main/bad_kb/scenes/bad_kb_scene_config_mac.c +++ /dev/null @@ -1,47 +0,0 @@ -#include "../bad_kb_app.h" - -#define TAG "BadKbConfigMac" - -void bad_kb_scene_config_mac_byte_input_callback(void* context) { - BadKbApp* bad_kb = context; - - view_dispatcher_send_custom_event(bad_kb->view_dispatcher, BadKbAppCustomEventByteInputDone); -} - -void bad_kb_scene_config_mac_on_enter(void* context) { - BadKbApp* bad_kb = context; - - // Setup view - ByteInput* byte_input = bad_kb->byte_input; - byte_input_set_header_text(byte_input, "Set BT MAC address"); - byte_input_set_result_callback( - byte_input, - bad_kb_scene_config_mac_byte_input_callback, - NULL, - bad_kb, - bad_kb->config.bt_mac, - GAP_MAC_ADDR_SIZE); - view_dispatcher_switch_to_view(bad_kb->view_dispatcher, BadKbAppViewConfigMac); -} - -bool bad_kb_scene_config_mac_on_event(void* context, SceneManagerEvent event) { - BadKbApp* bad_kb = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == BadKbAppCustomEventByteInputDone) { - bt_set_profile_mac_address(bad_kb->bt, bad_kb->config.bt_mac); - scene_manager_previous_scene(bad_kb->scene_manager); - consumed = true; - } - } - return consumed; -} - -void bad_kb_scene_config_mac_on_exit(void* context) { - BadKbApp* bad_kb = context; - - // Clear view - byte_input_set_result_callback(bad_kb->byte_input, NULL, NULL, NULL, NULL, 0); - byte_input_set_header_text(bad_kb->byte_input, ""); -} diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config_usb_name.c b/applications/main/bad_kb/scenes/bad_kb_scene_config_usb_name.c new file mode 100644 index 0000000000..381c2cdf00 --- /dev/null +++ b/applications/main/bad_kb/scenes/bad_kb_scene_config_usb_name.c @@ -0,0 +1,56 @@ +#include "../bad_kb_app.h" + +static void bad_kb_scene_config_usb_name_text_input_callback(void* context) { + BadKbApp* bad_kb = context; + + view_dispatcher_send_custom_event(bad_kb->view_dispatcher, BadKbAppCustomEventTextInputDone); +} + +void bad_kb_scene_config_usb_name_on_enter(void* context) { + BadKbApp* bad_kb = context; + TextInput* text_input = bad_kb->text_input; + + if(scene_manager_get_scene_state(bad_kb->scene_manager, BadKbSceneConfigUsbName)) { + strlcpy(bad_kb->usb_name_buf, bad_kb->config.usb_cfg.manuf, BAD_KB_USB_LEN); + text_input_set_header_text(text_input, "Set USB manufacturer name"); + } else { + strlcpy(bad_kb->usb_name_buf, bad_kb->config.usb_cfg.product, BAD_KB_USB_LEN); + text_input_set_header_text(text_input, "Set USB product name"); + } + + text_input_set_result_callback( + text_input, + bad_kb_scene_config_usb_name_text_input_callback, + bad_kb, + bad_kb->usb_name_buf, + BAD_KB_USB_LEN, + true); + + view_dispatcher_switch_to_view(bad_kb->view_dispatcher, BadKbAppViewTextInput); +} + +bool bad_kb_scene_config_usb_name_on_event(void* context, SceneManagerEvent event) { + BadKbApp* bad_kb = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + if(event.event == BadKbAppCustomEventTextInputDone) { + if(scene_manager_get_scene_state(bad_kb->scene_manager, BadKbSceneConfigUsbName)) { + strlcpy(bad_kb->config.usb_cfg.manuf, bad_kb->usb_name_buf, BAD_KB_USB_LEN); + } else { + strlcpy(bad_kb->config.usb_cfg.product, bad_kb->usb_name_buf, BAD_KB_USB_LEN); + } + bad_kb_config_refresh(bad_kb); + } + scene_manager_previous_scene(bad_kb->scene_manager); + } + return consumed; +} + +void bad_kb_scene_config_usb_name_on_exit(void* context) { + BadKbApp* bad_kb = context; + TextInput* text_input = bad_kb->text_input; + + text_input_reset(text_input); +} diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config_usb_vidpid.c b/applications/main/bad_kb/scenes/bad_kb_scene_config_usb_vidpid.c new file mode 100644 index 0000000000..ef1a4700fc --- /dev/null +++ b/applications/main/bad_kb/scenes/bad_kb_scene_config_usb_vidpid.c @@ -0,0 +1,50 @@ +#include "../bad_kb_app.h" + +void bad_kb_scene_config_usb_vidpid_byte_input_callback(void* context) { + BadKbApp* bad_kb = context; + + view_dispatcher_send_custom_event(bad_kb->view_dispatcher, BadKbAppCustomEventByteInputDone); +} + +void bad_kb_scene_config_usb_vidpid_on_enter(void* context) { + BadKbApp* bad_kb = context; + ByteInput* byte_input = bad_kb->byte_input; + + bad_kb->usb_vidpid_buf[0] = __REVSH(bad_kb->config.usb_cfg.vid); + bad_kb->usb_vidpid_buf[1] = __REVSH(bad_kb->config.usb_cfg.pid); + byte_input_set_header_text(byte_input, "Set USB VID:PID"); + + byte_input_set_result_callback( + byte_input, + bad_kb_scene_config_usb_vidpid_byte_input_callback, + NULL, + bad_kb, + (void*)bad_kb->usb_vidpid_buf, + sizeof(bad_kb->usb_vidpid_buf)); + + view_dispatcher_switch_to_view(bad_kb->view_dispatcher, BadKbAppViewByteInput); +} + +bool bad_kb_scene_config_usb_vidpid_on_event(void* context, SceneManagerEvent event) { + BadKbApp* bad_kb = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + if(event.event == BadKbAppCustomEventByteInputDone) { + bad_kb->config.usb_cfg.vid = __REVSH(bad_kb->usb_vidpid_buf[0]); + bad_kb->config.usb_cfg.pid = __REVSH(bad_kb->usb_vidpid_buf[1]); + bad_kb_config_refresh(bad_kb); + } + scene_manager_previous_scene(bad_kb->scene_manager); + } + return consumed; +} + +void bad_kb_scene_config_usb_vidpid_on_exit(void* context) { + BadKbApp* bad_kb = context; + ByteInput* byte_input = bad_kb->byte_input; + + byte_input_set_result_callback(byte_input, NULL, NULL, NULL, NULL, 0); + byte_input_set_header_text(byte_input, ""); +} diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_error.c b/applications/main/bad_kb/scenes/bad_kb_scene_error.c index 53393016cb..e27329b9aa 100644 --- a/applications/main/bad_kb/scenes/bad_kb_scene_error.c +++ b/applications/main/bad_kb/scenes/bad_kb_scene_error.c @@ -1,5 +1,4 @@ #include "../bad_kb_app.h" -#include static void bad_kb_scene_error_event_callback(GuiButtonType result, InputType type, void* context) { @@ -26,34 +25,9 @@ void bad_kb_scene_error_on_enter(void* context) { "No SD card or\napp data found.\nThis app will not\nwork without\nrequired files."); widget_add_button_element( app->widget, GuiButtonTypeLeft, "Back", bad_kb_scene_error_event_callback, app); - } else if(app->error == BadKbAppErrorCloseRpc) { - widget_add_icon_element(app->widget, 78, 0, &I_ActiveConnection_50x64); - if(XTREME_SETTINGS()->is_nsfw) { - widget_add_string_multiline_element( - app->widget, 3, 2, AlignLeft, AlignTop, FontPrimary, "I am not\na whore!"); - widget_add_string_multiline_element( - app->widget, - 3, - 30, - AlignLeft, - AlignTop, - FontSecondary, - "Pull out from\nPC or phone to\nuse me like this."); - } else { - widget_add_string_multiline_element( - app->widget, 3, 2, AlignLeft, AlignTop, FontPrimary, "Connection\nis active!"); - widget_add_string_multiline_element( - app->widget, - 3, - 30, - AlignLeft, - AlignTop, - FontSecondary, - "Disconnect from\nPC or phone to\nuse this function."); - } } - view_dispatcher_switch_to_view(app->view_dispatcher, BadKbAppViewError); + view_dispatcher_switch_to_view(app->view_dispatcher, BadKbAppViewWidget); } bool bad_kb_scene_error_on_event(void* context, SceneManagerEvent event) { diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_file_select.c b/applications/main/bad_kb/scenes/bad_kb_scene_file_select.c index 3a8c0748d3..635b4f3187 100644 --- a/applications/main/bad_kb/scenes/bad_kb_scene_file_select.c +++ b/applications/main/bad_kb/scenes/bad_kb_scene_file_select.c @@ -41,11 +41,9 @@ void bad_kb_scene_file_select_on_enter(void* context) { bool bad_kb_scene_file_select_on_event(void* context, SceneManagerEvent event) { UNUSED(context); UNUSED(event); - // BadKbApp* bad_kb = context; return false; } void bad_kb_scene_file_select_on_exit(void* context) { UNUSED(context); - // BadKbApp* bad_kb = context; } diff --git a/applications/main/bad_kb/views/bad_kb_view.c b/applications/main/bad_kb/views/bad_kb_view.c index af7dd27096..8b2ba222a1 100644 --- a/applications/main/bad_kb/views/bad_kb_view.c +++ b/applications/main/bad_kb/views/bad_kb_view.c @@ -18,9 +18,12 @@ typedef struct { static void bad_kb_draw_callback(Canvas* canvas, void* _model) { BadKbModel* model = _model; + BadKbWorkerState state = model->state.state; - FuriString* disp_str; - disp_str = furi_string_alloc_set(model->state.is_bt ? "(BT) " : "(USB) "); + FuriString* disp_str = furi_string_alloc_set( + state == BadKbStateInit ? "( . . . )" : + model->state.is_bt ? "(BT) " : + "(USB) "); furi_string_cat_str(disp_str, model->file_name); elements_string_fit_width(canvas, disp_str, 128 - 2); canvas_set_font(canvas, FontSecondary); @@ -42,7 +45,6 @@ static void bad_kb_draw_callback(Canvas* canvas, void* _model) { canvas_draw_icon(canvas, 22, 24, &I_UsbTree_48x22); - BadKbWorkerState state = model->state.state; if((state == BadKbStateIdle) || (state == BadKbStateDone) || (state == BadKbStateNotConnected)) { if(XTREME_SETTINGS()->is_nsfw) { diff --git a/applications/main/fap_loader/application.fam b/applications/main/fap_loader/application.fam deleted file mode 100644 index 827f3d8ec6..0000000000 --- a/applications/main/fap_loader/application.fam +++ /dev/null @@ -1,18 +0,0 @@ -App( - appid="fap_loader", - name="Apps", - apptype=FlipperAppType.APP, - entry_point="fap_loader_app", - cdefines=["APP_FAP_LOADER"], - requires=[ - "gui", - "storage", - "loader", - ], - stack_size=int(1.5 * 1024), - icon="A_Plugins_14", - order=9, - sdk_headers=[ - "fap_loader_app.h", - ], -) diff --git a/applications/main/fap_loader/fap_loader_app.c b/applications/main/fap_loader/fap_loader_app.c deleted file mode 100644 index 7f1383ae57..0000000000 --- a/applications/main/fap_loader/fap_loader_app.c +++ /dev/null @@ -1,274 +0,0 @@ -#include "fap_loader_app.h" - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define TAG "FapLoader" - -struct FapLoader { - FlipperApplication* app; - Storage* storage; - DialogsApp* dialogs; - Gui* gui; - FuriString* fap_path; - ViewDispatcher* view_dispatcher; - Loading* loading; -}; - -bool fap_loader_load_name_and_icon( - FuriString* path, - Storage* storage, - uint8_t** icon_ptr, - FuriString* item_name) { - StorageData* storage_data; - if(storage_get_data(storage, path, &storage_data) == FSE_OK && - storage_path_already_open(path, storage_data)) { - size_t offset = furi_string_search_rchar(path, '/'); - if(offset != FURI_STRING_FAILURE) { - furi_string_set_n(item_name, path, offset + 1, furi_string_size(path) - offset - 1); - } else { - furi_string_set(item_name, path); - } - return false; - } - - FlipperApplication* app = flipper_application_alloc(storage, firmware_api_interface); - - FlipperApplicationPreloadStatus preload_res = - flipper_application_preload_manifest(app, furi_string_get_cstr(path)); - - bool load_success = false; - - if(preload_res == FlipperApplicationPreloadStatusSuccess || - preload_res == FlipperApplicationPreloadStatusApiMismatch) { - const FlipperApplicationManifest* manifest = flipper_application_get_manifest(app); - if(manifest->has_icon && icon_ptr != NULL && *icon_ptr != NULL) { - memcpy(*icon_ptr, manifest->icon, FAP_MANIFEST_MAX_ICON_SIZE); - } - furi_string_set(item_name, manifest->name); - load_success = true; - } else { - FURI_LOG_E(TAG, "FAP Loader failed to preload %s", furi_string_get_cstr(path)); - size_t offset = furi_string_search_rchar(path, '/'); - if(offset != FURI_STRING_FAILURE) { - furi_string_set_n(item_name, path, offset + 1, furi_string_size(path) - offset - 1); - } else { - furi_string_set(item_name, path); - } - load_success = false; - } - - flipper_application_free(app); - return load_success; -} - -static bool fap_loader_item_callback( - FuriString* path, - void* context, - uint8_t** icon_ptr, - FuriString* item_name) { - FapLoader* fap_loader = context; - furi_assert(fap_loader); - return fap_loader_load_name_and_icon(path, fap_loader->storage, icon_ptr, item_name); -} - -static bool fap_loader_run_selected_app(FapLoader* loader, bool ignore_mismatch) { - furi_assert(loader); - - FuriString* error_message; - - error_message = furi_string_alloc_set("unknown error"); - - bool file_selected = false; - bool show_error = true; - bool retry = false; - do { - file_selected = true; - loader->app = flipper_application_alloc(loader->storage, firmware_api_interface); - size_t start = furi_get_tick(); - - FURI_LOG_I(TAG, "FAP Loader is loading %s", furi_string_get_cstr(loader->fap_path)); - - FlipperApplicationPreloadStatus preload_res = - flipper_application_preload(loader->app, furi_string_get_cstr(loader->fap_path)); - if(preload_res != FlipperApplicationPreloadStatusSuccess) { - if(preload_res == FlipperApplicationPreloadStatusApiMismatch) { - if(!ignore_mismatch) { - DialogMessage* message = dialog_message_alloc(); - dialog_message_set_header( - message, "API Mismatch", 64, 0, AlignCenter, AlignTop); - dialog_message_set_buttons(message, "Cancel", NULL, "Continue"); - dialog_message_set_text( - message, - "This app might not\nwork correctly\nContinue anyways?", - 64, - 32, - AlignCenter, - AlignCenter); - if(dialog_message_show(loader->dialogs, message) == DialogMessageButtonRight) { - retry = true; - } - dialog_message_free(message); - show_error = false; - break; - } - } else { - const char* err_msg = flipper_application_preload_status_to_string(preload_res); - furi_string_printf(error_message, "Preload failed: %s", err_msg); - FURI_LOG_E( - TAG, - "FAP Loader failed to preload %s: %s", - furi_string_get_cstr(loader->fap_path), - err_msg); - break; - } - } - - FURI_LOG_I(TAG, "FAP Loader is mapping"); - FlipperApplicationLoadStatus load_status = flipper_application_map_to_memory(loader->app); - if(load_status != FlipperApplicationLoadStatusSuccess) { - const char* err_msg = flipper_application_load_status_to_string(load_status); - furi_string_printf(error_message, "Load failed: %s", err_msg); - FURI_LOG_E( - TAG, - "FAP Loader failed to map to memory %s: %s", - furi_string_get_cstr(loader->fap_path), - err_msg); - break; - } - - FURI_LOG_I(TAG, "Loaded in %ums", (size_t)(furi_get_tick() - start)); - FURI_LOG_I(TAG, "FAP Loader is starting app"); - - FuriThread* thread = flipper_application_spawn(loader->app, NULL); - - /* This flag is set by the debugger - to break on app start */ - if(furi_hal_debug_is_gdb_session_active()) { - FURI_LOG_W(TAG, "Triggering BP for debugger"); - /* After hitting this, you can set breakpoints in your .fap's code - * Note that you have to toggle breakpoints that were set before */ - __asm volatile("bkpt 0"); - } - - FuriString* app_name = furi_string_alloc(); - path_extract_filename_no_ext(furi_string_get_cstr(loader->fap_path), app_name); - furi_thread_set_appid(thread, furi_string_get_cstr(app_name)); - furi_string_free(app_name); - - furi_thread_start(thread); - furi_thread_join(thread); - - show_error = false; - int ret = furi_thread_get_return_code(thread); - - FURI_LOG_I(TAG, "FAP app returned: %i", ret); - } while(0); - - if(show_error) { - DialogMessage* message = dialog_message_alloc(); - dialog_message_set_header(message, "Error", 64, 0, AlignCenter, AlignTop); - dialog_message_set_buttons(message, NULL, NULL, NULL); - - FuriString* buffer; - buffer = furi_string_alloc(); - furi_string_printf(buffer, "%s", furi_string_get_cstr(error_message)); - furi_string_replace(buffer, ":", "\n"); - dialog_message_set_text( - message, furi_string_get_cstr(buffer), 64, 32, AlignCenter, AlignCenter); - - dialog_message_show(loader->dialogs, message); - dialog_message_free(message); - furi_string_free(buffer); - } - - furi_string_free(error_message); - - if(file_selected) { - flipper_application_free(loader->app); - } - - return retry; -} - -static bool fap_loader_select_app(FapLoader* loader) { - const DialogsFileBrowserOptions browser_options = { - .extension = ".fap", - .skip_assets = true, - .icon = &I_unknown_10px, - .hide_ext = true, - .hide_dot_files = true, - .item_loader_callback = fap_loader_item_callback, - .item_loader_context = loader, - .base_path = EXT_PATH("apps"), - }; - - return dialog_file_browser_show( - loader->dialogs, loader->fap_path, loader->fap_path, &browser_options); -} - -static FapLoader* fap_loader_alloc(const char* path) { - FapLoader* loader = malloc(sizeof(FapLoader)); //-V799 - loader->fap_path = furi_string_alloc_set(path); - loader->storage = furi_record_open(RECORD_STORAGE); - loader->dialogs = furi_record_open(RECORD_DIALOGS); - loader->gui = furi_record_open(RECORD_GUI); - loader->view_dispatcher = view_dispatcher_alloc(); - loader->loading = loading_alloc(); - view_dispatcher_attach_to_gui( - loader->view_dispatcher, loader->gui, ViewDispatcherTypeFullscreen); - view_dispatcher_add_view(loader->view_dispatcher, 0, loading_get_view(loader->loading)); - return loader; -} //-V773 - -static void fap_loader_free(FapLoader* loader) { - view_dispatcher_remove_view(loader->view_dispatcher, 0); - loading_free(loader->loading); - view_dispatcher_free(loader->view_dispatcher); - furi_string_free(loader->fap_path); - furi_record_close(RECORD_GUI); - furi_record_close(RECORD_DIALOGS); - furi_record_close(RECORD_STORAGE); - free(loader); -} - -int32_t fap_loader_app(char* p) { - size_t start = furi_get_tick(); - XTREME_ASSETS_FREE(); - FURI_LOG_I("Assets", "Freed in %ums", (size_t)(furi_get_tick() - start)); - FapLoader* loader; - process_favorite_launch(&p); - if(p) { - loader = fap_loader_alloc((const char*)p); - view_dispatcher_switch_to_view(loader->view_dispatcher, 0); - if(fap_loader_run_selected_app(loader, false)) { - fap_loader_run_selected_app(loader, true); - } - } else { - loader = fap_loader_alloc(EXT_PATH("apps")); - while(fap_loader_select_app(loader)) { - view_dispatcher_switch_to_view(loader->view_dispatcher, 0); - if(fap_loader_run_selected_app(loader, false)) { - fap_loader_run_selected_app(loader, true); - } - } - } - - fap_loader_free(loader); - - start = furi_get_tick(); - XTREME_ASSETS_LOAD(); - FURI_LOG_I("Assets", "Loaded in %ums", (size_t)(furi_get_tick() - start)); - return 0; -} diff --git a/applications/main/fap_loader/fap_loader_app.h b/applications/main/fap_loader/fap_loader_app.h deleted file mode 100644 index 9ed725efe5..0000000000 --- a/applications/main/fap_loader/fap_loader_app.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct FapLoader FapLoader; - -/** - * @brief Load name and icon from FAP file. - * - * @param path Path to FAP file. - * @param storage Storage instance. - * @param icon_ptr Icon pointer. - * @param item_name Application name. - * @return true if icon and name were loaded successfully. - */ -bool fap_loader_load_name_and_icon( - FuriString* path, - Storage* storage, - uint8_t** icon_ptr, - FuriString* item_name); - -#ifdef __cplusplus -} -#endif \ No newline at end of file diff --git a/applications/main/gpio/application.fam b/applications/main/gpio/application.fam index de77c7eddc..0e99eb45a1 100644 --- a/applications/main/gpio/application.fam +++ b/applications/main/gpio/application.fam @@ -1,13 +1,11 @@ App( appid="gpio", name="GPIO", - apptype=FlipperAppType.EXTMAINAPP, - preload=True, + apptype=FlipperAppType.FAPP, entry_point="gpio_app", cdefines=["APP_GPIO"], requires=["gui"], stack_size=1 * 1024, icon="A_GPIO_14", order=50, - fap_libs=["assets"], ) diff --git a/applications/main/gpio/scenes/gpio_scene_start.c b/applications/main/gpio/scenes/gpio_scene_start.c index 2d8f7adbcc..28ced135a5 100644 --- a/applications/main/gpio/scenes/gpio_scene_start.c +++ b/applications/main/gpio/scenes/gpio_scene_start.c @@ -107,7 +107,7 @@ bool gpio_scene_start_on_event(void* context, SceneManagerEvent event) { } else if(event.event == GpioStartEventUsbUart) { scene_manager_set_scene_state(app->scene_manager, GpioSceneStart, GpioItemUsbUart); if(!furi_hal_usb_is_locked()) { - DOLPHIN_DEED(DolphinDeedGpioUartBridge); + dolphin_deed(DolphinDeedGpioUartBridge); scene_manager_next_scene(app->scene_manager, GpioSceneUsbUart); } else { scene_manager_next_scene(app->scene_manager, GpioSceneUsbUartCloseRpc); diff --git a/applications/main/ibutton/application.fam b/applications/main/ibutton/application.fam index 511b145d5c..f37f98a5c0 100644 --- a/applications/main/ibutton/application.fam +++ b/applications/main/ibutton/application.fam @@ -1,8 +1,7 @@ App( appid="ibutton", name="iButton", - apptype=FlipperAppType.EXTMAINAPP, - preload=True, + apptype=FlipperAppType.FAPP, targets=["f7"], entry_point="ibutton_app", cdefines=["APP_IBUTTON"], @@ -14,7 +13,6 @@ App( icon="A_iButton_14", stack_size=2 * 1024, order=60, - fap_libs=["assets"], ) App( diff --git a/applications/main/ibutton/ibutton.c b/applications/main/ibutton/ibutton.c index 3fea2d2f67..78c433f324 100644 --- a/applications/main/ibutton/ibutton.c +++ b/applications/main/ibutton/ibutton.c @@ -284,14 +284,14 @@ int32_t ibutton_app(char* arg) { view_dispatcher_attach_to_gui( ibutton->view_dispatcher, ibutton->gui, ViewDispatcherTypeDesktop); scene_manager_next_scene(ibutton->scene_manager, iButtonSceneRpc); - DOLPHIN_DEED(DolphinDeedIbuttonEmulate); + dolphin_deed(DolphinDeedIbuttonEmulate); } else { view_dispatcher_attach_to_gui( ibutton->view_dispatcher, ibutton->gui, ViewDispatcherTypeFullscreen); if(key_loaded) { //-V547 scene_manager_next_scene(ibutton->scene_manager, iButtonSceneEmulate); - DOLPHIN_DEED(DolphinDeedIbuttonEmulate); + dolphin_deed(DolphinDeedIbuttonEmulate); } else { scene_manager_next_scene(ibutton->scene_manager, iButtonSceneStart); } diff --git a/applications/main/ibutton/scenes/ibutton_scene_read.c b/applications/main/ibutton/scenes/ibutton_scene_read.c index a840fb7b7e..f360c3ac43 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_read.c +++ b/applications/main/ibutton/scenes/ibutton_scene_read.c @@ -38,7 +38,7 @@ bool ibutton_scene_read_on_event(void* context, SceneManagerEvent event) { ibutton_notification_message(ibutton, iButtonNotificationMessageSuccess); scene_manager_next_scene(scene_manager, iButtonSceneReadSuccess); - DOLPHIN_DEED(DolphinDeedIbuttonReadSuccess); + dolphin_deed(DolphinDeedIbuttonReadSuccess); } else { scene_manager_next_scene(scene_manager, iButtonSceneReadError); diff --git a/applications/main/ibutton/scenes/ibutton_scene_read_key_menu.c b/applications/main/ibutton/scenes/ibutton_scene_read_key_menu.c index 716f72c7d3..1555f2cc20 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_read_key_menu.c +++ b/applications/main/ibutton/scenes/ibutton_scene_read_key_menu.c @@ -75,7 +75,7 @@ bool ibutton_scene_read_key_menu_on_event(void* context, SceneManagerEvent event scene_manager_next_scene(scene_manager, iButtonSceneSaveName); } else if(event.event == SubmenuIndexEmulate) { scene_manager_next_scene(scene_manager, iButtonSceneEmulate); - DOLPHIN_DEED(DolphinDeedIbuttonEmulate); + dolphin_deed(DolphinDeedIbuttonEmulate); } else if(event.event == SubmenuIndexViewData) { scene_manager_next_scene(scene_manager, iButtonSceneViewData); } else if(event.event == SubmenuIndexWriteBlank) { diff --git a/applications/main/ibutton/scenes/ibutton_scene_save_name.c b/applications/main/ibutton/scenes/ibutton_scene_save_name.c index 4ad0315e54..7bd49df833 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_save_name.c +++ b/applications/main/ibutton/scenes/ibutton_scene_save_name.c @@ -58,9 +58,9 @@ bool ibutton_scene_save_name_on_event(void* context, SceneManagerEvent event) { // Nothing, do not count editing as saving } else if(scene_manager_has_previous_scene( ibutton->scene_manager, iButtonSceneAddType)) { - DOLPHIN_DEED(DolphinDeedIbuttonAdd); + dolphin_deed(DolphinDeedIbuttonAdd); } else { - DOLPHIN_DEED(DolphinDeedIbuttonSave); + dolphin_deed(DolphinDeedIbuttonSave); } } else { diff --git a/applications/main/ibutton/scenes/ibutton_scene_saved_key_menu.c b/applications/main/ibutton/scenes/ibutton_scene_saved_key_menu.c index 80fca28b5e..fc0cf42e2f 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_saved_key_menu.c +++ b/applications/main/ibutton/scenes/ibutton_scene_saved_key_menu.c @@ -48,7 +48,7 @@ bool ibutton_scene_saved_key_menu_on_event(void* context, SceneManagerEvent even consumed = true; if(event.event == SubmenuIndexEmulate) { scene_manager_next_scene(scene_manager, iButtonSceneEmulate); - DOLPHIN_DEED(DolphinDeedIbuttonEmulate); + dolphin_deed(DolphinDeedIbuttonEmulate); } else if(event.event == SubmenuIndexWriteBlank) { ibutton->write_mode = iButtonWriteModeBlank; scene_manager_next_scene(scene_manager, iButtonSceneWrite); diff --git a/applications/main/ibutton/scenes/ibutton_scene_start.c b/applications/main/ibutton/scenes/ibutton_scene_start.c index 37bf96f39f..63a4cf869d 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_start.c +++ b/applications/main/ibutton/scenes/ibutton_scene_start.c @@ -33,7 +33,7 @@ bool ibutton_scene_start_on_event(void* context, SceneManagerEvent event) { consumed = true; if(event.event == SubmenuIndexRead) { scene_manager_next_scene(ibutton->scene_manager, iButtonSceneRead); - DOLPHIN_DEED(DolphinDeedIbuttonRead); + dolphin_deed(DolphinDeedIbuttonRead); } else if(event.event == SubmenuIndexSaved) { scene_manager_next_scene(ibutton->scene_manager, iButtonSceneSelectKey); } else if(event.event == SubmenuIndexAdd) { diff --git a/applications/main/infrared/application.fam b/applications/main/infrared/application.fam index e5483e9ffa..5c65cd0795 100644 --- a/applications/main/infrared/application.fam +++ b/applications/main/infrared/application.fam @@ -1,7 +1,7 @@ App( appid="infrared", name="Infrared", - apptype=FlipperAppType.APP, + apptype=FlipperAppType.FAPP, entry_point="infrared_app", targets=["f7"], cdefines=["APP_INFRARED"], @@ -13,7 +13,6 @@ App( icon="A_Infrared_14", stack_size=3 * 1024, order=40, - fap_libs=["assets"], ) App( diff --git a/applications/main/infrared/infrared.c b/applications/main/infrared/infrared.c index 74a84ace4b..d4bfde99f3 100644 --- a/applications/main/infrared/infrared.c +++ b/applications/main/infrared/infrared.c @@ -2,7 +2,6 @@ #include #include -#include #define INFRARED_TX_MIN_INTERVAL_MS 50U @@ -334,7 +333,7 @@ void infrared_tx_start_signal(Infrared* infrared, InfraredSignal* signal) { infrared_worker_set_decoded_signal(infrared->worker, message); } - DOLPHIN_DEED(DolphinDeedIrSend); + dolphin_deed(DolphinDeedIrSend); infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStartSend); infrared_worker_tx_set_get_signal_callback( @@ -450,7 +449,6 @@ int32_t infrared_app(char* p) { bool is_remote_loaded = false; bool is_rpc_mode = false; - process_favorite_launch(&p); if(p && strlen(p)) { uint32_t rpc_ctx = 0; if(sscanf(p, "RPC %lX", &rpc_ctx) == 1) { diff --git a/applications/main/infrared/scenes/common/infrared_scene_universal_common.c b/applications/main/infrared/scenes/common/infrared_scene_universal_common.c index d55d8d0a6d..96f28cc489 100644 --- a/applications/main/infrared/scenes/common/infrared_scene_universal_common.c +++ b/applications/main/infrared/scenes/common/infrared_scene_universal_common.c @@ -70,7 +70,7 @@ bool infrared_scene_universal_common_on_event(void* context, SceneManagerEvent e uint32_t record_count; if(infrared_brute_force_start( brute_force, infrared_custom_event_get_value(event.event), &record_count)) { - DOLPHIN_DEED(DolphinDeedIrSend); + dolphin_deed(DolphinDeedIrSend); infrared_scene_universal_common_show_popup(infrared, record_count); } else { scene_manager_next_scene(scene_manager, InfraredSceneErrorDatabases); diff --git a/applications/main/infrared/scenes/infrared_scene_learn.c b/applications/main/infrared/scenes/infrared_scene_learn.c index 48699a71fc..46646c6d69 100644 --- a/applications/main/infrared/scenes/infrared_scene_learn.c +++ b/applications/main/infrared/scenes/infrared_scene_learn.c @@ -28,7 +28,7 @@ bool infrared_scene_learn_on_event(void* context, SceneManagerEvent event) { if(event.event == InfraredCustomEventTypeSignalReceived) { infrared_play_notification_message(infrared, InfraredNotificationMessageSuccess); scene_manager_next_scene(infrared->scene_manager, InfraredSceneLearnSuccess); - DOLPHIN_DEED(DolphinDeedIrLearnSuccess); + dolphin_deed(DolphinDeedIrLearnSuccess); consumed = true; } } diff --git a/applications/main/infrared/scenes/infrared_scene_learn_enter_name.c b/applications/main/infrared/scenes/infrared_scene_learn_enter_name.c index a8772a985c..104a4cb7b6 100644 --- a/applications/main/infrared/scenes/infrared_scene_learn_enter_name.c +++ b/applications/main/infrared/scenes/infrared_scene_learn_enter_name.c @@ -50,7 +50,7 @@ bool infrared_scene_learn_enter_name_on_event(void* context, SceneManagerEvent e if(success) { scene_manager_next_scene(scene_manager, InfraredSceneLearnDone); - DOLPHIN_DEED(DolphinDeedIrSave); + dolphin_deed(DolphinDeedIrSave); } else { dialog_message_show_storage_error(infrared->dialogs, "Failed to save file"); const uint32_t possible_scenes[] = {InfraredSceneRemoteList, InfraredSceneStart}; diff --git a/applications/main/lfrfid/application.fam b/applications/main/lfrfid/application.fam index e84ed2a31a..aa887cdd73 100644 --- a/applications/main/lfrfid/application.fam +++ b/applications/main/lfrfid/application.fam @@ -1,7 +1,7 @@ App( appid="lfrfid", name="RFID", - apptype=FlipperAppType.APP, + apptype=FlipperAppType.FAPP, targets=["f7"], entry_point="lfrfid_app", cdefines=["APP_LF_RFID"], @@ -15,7 +15,6 @@ App( icon="A_125khz_14", stack_size=2 * 1024, order=20, - fap_libs=["assets"], ) App( diff --git a/applications/main/lfrfid/lfrfid.c b/applications/main/lfrfid/lfrfid.c index d4b580ab51..fe88cd91d4 100644 --- a/applications/main/lfrfid/lfrfid.c +++ b/applications/main/lfrfid/lfrfid.c @@ -184,14 +184,14 @@ int32_t lfrfid_app(char* args) { view_dispatcher_attach_to_gui( app->view_dispatcher, app->gui, ViewDispatcherTypeDesktop); scene_manager_next_scene(app->scene_manager, LfRfidSceneRpc); - DOLPHIN_DEED(DolphinDeedRfidEmulate); + dolphin_deed(DolphinDeedRfidEmulate); } else { furi_string_set(app->file_path, args); if(lfrfid_load_key_data(app, app->file_path, true)) { view_dispatcher_attach_to_gui( app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); scene_manager_next_scene(app->scene_manager, LfRfidSceneEmulate); - DOLPHIN_DEED(DolphinDeedRfidEmulate); + dolphin_deed(DolphinDeedRfidEmulate); } else { // TODO: exit properly lfrfid_free(app); diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c b/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c index 8a71b023a9..c93b8b7f37 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c @@ -74,12 +74,12 @@ bool lfrfid_scene_extra_actions_on_event(void* context, SceneManagerEvent event) if(event.event == SubmenuIndexASK) { app->read_type = LFRFIDWorkerReadTypeASKOnly; scene_manager_next_scene(app->scene_manager, LfRfidSceneRead); - DOLPHIN_DEED(DolphinDeedRfidRead); + dolphin_deed(DolphinDeedRfidRead); consumed = true; } else if(event.event == SubmenuIndexPSK) { app->read_type = LFRFIDWorkerReadTypePSKOnly; scene_manager_next_scene(app->scene_manager, LfRfidSceneRead); - DOLPHIN_DEED(DolphinDeedRfidRead); + dolphin_deed(DolphinDeedRfidRead); consumed = true; } else if(event.event == SubmenuIndexClearT5577) { scene_manager_next_scene(app->scene_manager, LfRfidSceneClearT5577Confirm); diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_read.c b/applications/main/lfrfid/scenes/lfrfid_scene_read.c index 5f19597282..d04ce41d4a 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_read.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_read.c @@ -81,7 +81,7 @@ bool lfrfid_scene_read_on_event(void* context, SceneManagerEvent event) { notification_message(app->notifications, &sequence_success); furi_string_reset(app->file_name); scene_manager_next_scene(app->scene_manager, LfRfidSceneReadSuccess); - DOLPHIN_DEED(DolphinDeedRfidReadSuccess); + dolphin_deed(DolphinDeedRfidReadSuccess); consumed = true; } else if(event.event == LfRfidEventReadStartPSK) { if(app->read_type == LFRFIDWorkerReadTypeAuto) { diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_read_key_menu.c b/applications/main/lfrfid/scenes/lfrfid_scene_read_key_menu.c index 081c479123..36f0d6d93a 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_read_key_menu.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_read_key_menu.c @@ -44,7 +44,7 @@ bool lfrfid_scene_read_key_menu_on_event(void* context, SceneManagerEvent event) consumed = true; } else if(event.event == SubmenuIndexEmulate) { scene_manager_next_scene(app->scene_manager, LfRfidSceneEmulate); - DOLPHIN_DEED(DolphinDeedRfidEmulate); + dolphin_deed(DolphinDeedRfidEmulate); consumed = true; } scene_manager_set_scene_state(app->scene_manager, LfRfidSceneReadKeyMenu, event.event); diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_save_name.c b/applications/main/lfrfid/scenes/lfrfid_scene_save_name.c index 87e110f185..771f2f6038 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_save_name.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_save_name.c @@ -59,9 +59,9 @@ bool lfrfid_scene_save_name_on_event(void* context, SceneManagerEvent event) { if(scene_manager_has_previous_scene(scene_manager, LfRfidSceneSavedKeyMenu)) { // Nothing, do not count editing as saving } else if(scene_manager_has_previous_scene(scene_manager, LfRfidSceneSaveType)) { - DOLPHIN_DEED(DolphinDeedRfidAdd); + dolphin_deed(DolphinDeedRfidAdd); } else { - DOLPHIN_DEED(DolphinDeedRfidSave); + dolphin_deed(DolphinDeedRfidSave); } } else { scene_manager_search_and_switch_to_previous_scene( diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_saved_key_menu.c b/applications/main/lfrfid/scenes/lfrfid_scene_saved_key_menu.c index d3c3d389a8..206074e9b0 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_saved_key_menu.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_saved_key_menu.c @@ -43,7 +43,7 @@ bool lfrfid_scene_saved_key_menu_on_event(void* context, SceneManagerEvent event if(event.type == SceneManagerEventTypeCustom) { if(event.event == SubmenuIndexEmulate) { scene_manager_next_scene(app->scene_manager, LfRfidSceneEmulate); - DOLPHIN_DEED(DolphinDeedRfidEmulate); + dolphin_deed(DolphinDeedRfidEmulate); consumed = true; } else if(event.event == SubmenuIndexWrite) { scene_manager_next_scene(app->scene_manager, LfRfidSceneWrite); diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_start.c b/applications/main/lfrfid/scenes/lfrfid_scene_start.c index 2d83ba53b3..8a01fc707b 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_start.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_start.c @@ -49,7 +49,7 @@ bool lfrfid_scene_start_on_event(void* context, SceneManagerEvent event) { if(event.event == SubmenuIndexRead) { scene_manager_set_scene_state(app->scene_manager, LfRfidSceneStart, SubmenuIndexRead); scene_manager_next_scene(app->scene_manager, LfRfidSceneRead); - DOLPHIN_DEED(DolphinDeedRfidRead); + dolphin_deed(DolphinDeedRfidRead); consumed = true; } else if(event.event == SubmenuIndexSaved) { // Like in the other apps, explicitly save the scene state diff --git a/applications/main/nfc/nfc.c b/applications/main/nfc/nfc.c index e87f070120..1b83ffad7a 100644 --- a/applications/main/nfc/nfc.c +++ b/applications/main/nfc/nfc.c @@ -287,19 +287,19 @@ int32_t nfc_app(char* p) { if(nfc_device_load(nfc->dev, p, true)) { if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightEmulate); - DOLPHIN_DEED(DolphinDeedNfcEmulate); + dolphin_deed(DolphinDeedNfcEmulate); } else if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicEmulate); - DOLPHIN_DEED(DolphinDeedNfcEmulate); + dolphin_deed(DolphinDeedNfcEmulate); } else if(nfc->dev->format == NfcDeviceSaveFormatNfcV) { scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVEmulate); - DOLPHIN_DEED(DolphinDeedNfcEmulate); + dolphin_deed(DolphinDeedNfcEmulate); } else if(nfc->dev->format == NfcDeviceSaveFormatBankCard) { scene_manager_next_scene(nfc->scene_manager, NfcSceneDeviceInfo); - DOLPHIN_DEED(DolphinDeedNfcEmulate); + dolphin_deed(DolphinDeedNfcEmulate); } else { scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid); - DOLPHIN_DEED(DolphinDeedNfcEmulate); + dolphin_deed(DolphinDeedNfcEmulate); } } else { // Exit app diff --git a/applications/main/nfc/nfc_cli.c b/applications/main/nfc/nfc_cli.c index 577b9adf1e..aa3f74c361 100644 --- a/applications/main/nfc/nfc_cli.c +++ b/applications/main/nfc/nfc_cli.c @@ -160,6 +160,10 @@ static void nfc_cli_apdu(Cli* cli, FuriString* args) { break; } resp_size = (tx_rx.rx_bits / 8) * 2; + if(!resp_size) { + printf("No response\r\n"); + break; + } resp_buffer = malloc(resp_size); uint8_to_hex_chars(tx_rx.rx_data, resp_buffer, resp_size); resp_buffer[resp_size] = 0; diff --git a/applications/main/nfc/scenes/nfc_scene_felica_menu.c b/applications/main/nfc/scenes/nfc_scene_felica_menu.c index b989047d60..b8e653e208 100644 --- a/applications/main/nfc/scenes/nfc_scene_felica_menu.c +++ b/applications/main/nfc/scenes/nfc_scene_felica_menu.c @@ -4,7 +4,9 @@ enum SubmenuIndex { /* SubmenuIndexUnlock, + */ SubmenuIndexSave, + /* SubmenuIndexEmulate, */ SubmenuIndexInfo, @@ -24,8 +26,10 @@ void nfc_scene_felica_menu_on_enter(void* context) { /* submenu_add_item( submenu, "Unlock", SubmenuIndexUnlock, nfc_scene_felica_menu_submenu_callback, nfc); + */ submenu_add_item( submenu, "Save", SubmenuIndexSave, nfc_scene_felica_menu_submenu_callback, nfc); + /* submenu_add_item( submenu, "Emulate", SubmenuIndexEmulate, nfc_scene_felica_menu_submenu_callback, nfc); */ @@ -43,27 +47,26 @@ bool nfc_scene_felica_menu_on_event(void* context, SceneManagerEvent event) { bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { - /* if(event.event == SubmenuIndexSave) { nfc->dev->format = NfcDeviceSaveFormatFelica; // Clear device name nfc_device_set_name(nfc->dev, ""); scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); consumed = true; + /* } else if(event.event == SubmenuIndexEmulate) { scene_manager_next_scene(nfc->scene_manager, NfcSceneFelicaEmulate); if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) { - DOLPHIN_DEED(DolphinDeedNfcAddEmulate); + dolphin_deed(DolphinDeedNfcAddEmulate); } else { - DOLPHIN_DEED(DolphinDeedNfcEmulate); + dolphin_deed(DolphinDeedNfcEmulate); } consumed = true; } else if(event.event == SubmenuIndexUnlock) { scene_manager_next_scene(nfc->scene_manager, NfcSceneFelicaUnlockMenu); consumed = true; - } else */ - if(event.event == SubmenuIndexInfo) { + } else if(event.event == SubmenuIndexInfo) { scene_manager_next_scene(nfc->scene_manager, NfcSceneFelicaInfoSelect); consumed = true; } diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c index cb2f3a82d9..5bd24d7eac 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c @@ -111,7 +111,7 @@ bool nfc_scene_mf_classic_dict_attack_on_event(void* context, SceneManagerEvent } else { notification_message(nfc->notifications, &sequence_success); scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicReadSuccess); - DOLPHIN_DEED(DolphinDeedNfcReadSuccess); + dolphin_deed(DolphinDeedNfcReadSuccess); consumed = true; } } else if(event.event == NfcWorkerEventAborted) { @@ -123,7 +123,7 @@ bool nfc_scene_mf_classic_dict_attack_on_event(void* context, SceneManagerEvent notification_message(nfc->notifications, &sequence_success); scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicReadSuccess); // Counting failed attempts too - DOLPHIN_DEED(DolphinDeedNfcReadSuccess); + dolphin_deed(DolphinDeedNfcReadSuccess); consumed = true; } } else if(event.event == NfcWorkerEventCardDetected) { diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_add.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_add.c index b122aa225b..3a999f0311 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_add.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_add.c @@ -37,7 +37,7 @@ bool nfc_scene_mf_classic_keys_add_on_event(void* context, SceneManagerEvent eve nfc->scene_manager, NfcSceneMfClassicKeysWarnDuplicate); } else if(mf_classic_dict_add_key(dict, nfc->byte_input_store)) { scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveSuccess); - DOLPHIN_DEED(DolphinDeedNfcMfcAdd); + dolphin_deed(DolphinDeedNfcMfcAdd); } else { scene_manager_next_scene(nfc->scene_manager, NfcSceneDictNotFound); } diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_menu.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_menu.c index 67b2a85309..9c41636764 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_menu.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_menu.c @@ -54,14 +54,14 @@ bool nfc_scene_mf_classic_menu_on_event(void* context, SceneManagerEvent event) } else if(event.event == SubmenuIndexEmulate) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicEmulate); if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) { - DOLPHIN_DEED(DolphinDeedNfcAddEmulate); + dolphin_deed(DolphinDeedNfcAddEmulate); } else { - DOLPHIN_DEED(DolphinDeedNfcEmulate); + dolphin_deed(DolphinDeedNfcEmulate); } consumed = true; } else if(event.event == SubmenuIndexDetectReader) { scene_manager_next_scene(nfc->scene_manager, NfcSceneDetectReader); - DOLPHIN_DEED(DolphinDeedNfcDetectReader); + dolphin_deed(DolphinDeedNfcDetectReader); consumed = true; } else if(event.event == SubmenuIndexInfo) { scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcDataInfo); diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_update.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_update.c index aacf77f773..ffef1b7b9f 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_update.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_update.c @@ -34,7 +34,7 @@ static void nfc_scene_mf_classic_update_setup_view(Nfc* nfc) { void nfc_scene_mf_classic_update_on_enter(void* context) { Nfc* nfc = context; - DOLPHIN_DEED(DolphinDeedNfcEmulate); + dolphin_deed(DolphinDeedNfcEmulate); scene_manager_set_scene_state( nfc->scene_manager, NfcSceneMfClassicUpdate, NfcSceneMfClassicUpdateStateCardSearch); diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_update_success.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_update_success.c index fef8fd5e93..fb1868459d 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_update_success.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_update_success.c @@ -8,7 +8,7 @@ void nfc_scene_mf_classic_update_success_popup_callback(void* context) { void nfc_scene_mf_classic_update_success_on_enter(void* context) { Nfc* nfc = context; - DOLPHIN_DEED(DolphinDeedNfcSave); + dolphin_deed(DolphinDeedNfcSave); notification_message(nfc->notifications, &sequence_success); diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_write.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_write.c index 3543cbc588..20ebfcc70a 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_write.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_write.c @@ -34,7 +34,7 @@ static void nfc_scene_mf_classic_write_setup_view(Nfc* nfc) { void nfc_scene_mf_classic_write_on_enter(void* context) { Nfc* nfc = context; - DOLPHIN_DEED(DolphinDeedNfcEmulate); + dolphin_deed(DolphinDeedNfcEmulate); scene_manager_set_scene_state( nfc->scene_manager, NfcSceneMfClassicWrite, NfcSceneMfClassicWriteStateCardSearch); diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_write_success.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_write_success.c index 2f2a3beb11..00030d4fe8 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_write_success.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_write_success.c @@ -8,7 +8,7 @@ void nfc_scene_mf_classic_write_success_popup_callback(void* context) { void nfc_scene_mf_classic_write_success_on_enter(void* context) { Nfc* nfc = context; - DOLPHIN_DEED(DolphinDeedNfcSave); + dolphin_deed(DolphinDeedNfcSave); notification_message(nfc->notifications, &sequence_success); diff --git a/applications/main/nfc/scenes/nfc_scene_mf_desfire_menu.c b/applications/main/nfc/scenes/nfc_scene_mf_desfire_menu.c index bee63d775b..9cebefedfa 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_desfire_menu.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_desfire_menu.c @@ -50,9 +50,9 @@ bool nfc_scene_mf_desfire_menu_on_event(void* context, SceneManagerEvent event) } else if(event.event == SubmenuIndexEmulateUid) { scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid); if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) { - DOLPHIN_DEED(DolphinDeedNfcAddEmulate); + dolphin_deed(DolphinDeedNfcAddEmulate); } else { - DOLPHIN_DEED(DolphinDeedNfcEmulate); + dolphin_deed(DolphinDeedNfcEmulate); } consumed = true; } else if(event.event == SubmenuIndexInfo) { diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_menu.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_menu.c index e7a494d273..b3bd780f4b 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_menu.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_menu.c @@ -60,9 +60,9 @@ bool nfc_scene_mf_ultralight_menu_on_event(void* context, SceneManagerEvent even } else if(event.event == SubmenuIndexEmulate) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightEmulate); if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) { - DOLPHIN_DEED(DolphinDeedNfcAddEmulate); + dolphin_deed(DolphinDeedNfcAddEmulate); } else { - DOLPHIN_DEED(DolphinDeedNfcEmulate); + dolphin_deed(DolphinDeedNfcEmulate); } consumed = true; } else if(event.event == SubmenuIndexUnlock) { diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_warn.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_warn.c index 16efae9dea..af2eca0ce5 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_warn.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_warn.c @@ -61,7 +61,7 @@ bool nfc_scene_mf_ultralight_unlock_warn_on_event(void* context, SceneManagerEve if(event.type == SceneManagerEventTypeCustom) { if(event.event == DialogExResultRight) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightReadAuth); - DOLPHIN_DEED(DolphinDeedNfcRead); + dolphin_deed(DolphinDeedNfcRead); consumed = true; } else if(event.event == DialogExResultLeft) { if(auth_method == MfUltralightAuthMethodAuto) { @@ -79,7 +79,7 @@ bool nfc_scene_mf_ultralight_unlock_warn_on_event(void* context, SceneManagerEve if(event.type == SceneManagerEventTypeCustom) { if(event.event == DialogExResultCenter) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightReadAuth); - DOLPHIN_DEED(DolphinDeedNfcRead); + dolphin_deed(DolphinDeedNfcRead); consumed = true; } } diff --git a/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c b/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c index 1512f228d1..4c2bfc6788 100644 --- a/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c +++ b/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c @@ -8,6 +8,83 @@ void nfc_scene_nfc_data_info_widget_callback(GuiButtonType result, InputType typ } } +void nfc_scene_slix_build_string( + FuriString* temp_str, + NfcVData* nfcv_data, + SlixTypeFeatures features, + const char* type) { + furi_string_cat_printf(temp_str, "Type: %s\n", type); + furi_string_cat_printf(temp_str, "Keys:\n"); + if(features & SlixFeatureRead) { + furi_string_cat_printf( + temp_str, + " Read %08llX%s\n", + nfc_util_bytes2num(nfcv_data->sub_data.slix.key_read, 4), + (nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsHasKeyRead) ? "" : " (unset)"); + } + if(features & SlixFeatureWrite) { + furi_string_cat_printf( + temp_str, + " Write %08llX%s\n", + nfc_util_bytes2num(nfcv_data->sub_data.slix.key_write, 4), + (nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsHasKeyWrite) ? "" : " (unset)"); + } + if(features & SlixFeaturePrivacy) { + furi_string_cat_printf( + temp_str, + " Privacy %08llX%s\n", + nfc_util_bytes2num(nfcv_data->sub_data.slix.key_privacy, 4), + (nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsHasKeyPrivacy) ? "" : " (unset)"); + furi_string_cat_printf( + temp_str, + " Privacy mode %s\n", + (nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsPrivacy) ? "ENABLED" : "DISABLED"); + } + if(features & SlixFeatureDestroy) { + furi_string_cat_printf( + temp_str, + " Destroy %08llX%s\n", + nfc_util_bytes2num(nfcv_data->sub_data.slix.key_destroy, 4), + (nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsHasKeyDestroy) ? "" : " (unset)"); + } + if(features & SlixFeatureEas) { + furi_string_cat_printf( + temp_str, + " EAS %08llX%s\n", + nfc_util_bytes2num(nfcv_data->sub_data.slix.key_eas, 4), + (nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsHasKeyEas) ? "" : " (unset)"); + } + if(features & SlixFeatureSignature) { + furi_string_cat_printf( + temp_str, + "Signature %08llX...\n", + nfc_util_bytes2num(nfcv_data->sub_data.slix.signature, 4)); + } + furi_string_cat_printf( + temp_str, + "DSFID: %02X %s\n", + nfcv_data->dsfid, + (nfcv_data->security_status[0] & NfcVLockBitDsfid) ? "(locked)" : ""); + furi_string_cat_printf( + temp_str, + "AFI: %02X %s\n", + nfcv_data->afi, + (nfcv_data->security_status[0] & NfcVLockBitAfi) ? "(locked)" : ""); + furi_string_cat_printf( + temp_str, + "EAS: %s\n", + (nfcv_data->security_status[0] & NfcVLockBitEas) ? "locked" : "not locked"); + + if(features & SlixFeatureProtection) { + furi_string_cat_printf( + temp_str, + "PPL: %s\n", + (nfcv_data->security_status[0] & NfcVLockBitPpl) ? "locked" : "not locked"); + furi_string_cat_printf(temp_str, "Prot.ptr %02X\n", nfcv_data->sub_data.slix.pp_pointer); + furi_string_cat_printf(temp_str, "Prot.con %02X\n", nfcv_data->sub_data.slix.pp_condition); + } +} + void nfc_scene_nfc_data_info_on_enter(void* context) { Nfc* nfc = context; Widget* widget = nfc->widget; @@ -80,95 +157,25 @@ void nfc_scene_nfc_data_info_on_enter(void* context) { } furi_string_cat_printf(temp_str, "\n"); - furi_string_cat_printf( - temp_str, - "DSFID: %02X %s\n", - nfcv_data->dsfid, - (nfcv_data->security_status[0] & NfcVLockBitDsfid) ? "(locked)" : ""); - furi_string_cat_printf( - temp_str, - "AFI: %02X %s\n", - nfcv_data->afi, - (nfcv_data->security_status[0] & NfcVLockBitAfi) ? "(locked)" : ""); - furi_string_cat_printf(temp_str, "IC Ref: %02X\n", nfcv_data->ic_ref); - furi_string_cat_printf(temp_str, "Blocks: %02X\n", nfcv_data->block_num); - furi_string_cat_printf(temp_str, "Blocksize: %02X\n", nfcv_data->block_size); + furi_string_cat_printf(temp_str, "IC Ref: %d\n", nfcv_data->ic_ref); + furi_string_cat_printf(temp_str, "Blocks: %d\n", nfcv_data->block_num); + furi_string_cat_printf(temp_str, "Blocksize: %d\n", nfcv_data->block_size); switch(dev_data->nfcv_data.sub_type) { case NfcVTypePlain: furi_string_cat_printf(temp_str, "Type: Plain\n"); break; case NfcVTypeSlix: - furi_string_cat_printf(temp_str, "Type: SLIX\n"); - furi_string_cat_printf(temp_str, "Keys:\n"); - furi_string_cat_printf( - temp_str, - " EAS %08llX\n", - nfc_util_bytes2num(nfcv_data->sub_data.slix.key_eas, 4)); + nfc_scene_slix_build_string(temp_str, nfcv_data, SlixFeatureSlix, "SLIX"); break; case NfcVTypeSlixS: - furi_string_cat_printf(temp_str, "Type: SLIX-S\n"); - furi_string_cat_printf(temp_str, "Keys:\n"); - furi_string_cat_printf( - temp_str, - " Read %08llX\n", - nfc_util_bytes2num(nfcv_data->sub_data.slix.key_read, 4)); - furi_string_cat_printf( - temp_str, - " Write %08llX\n", - nfc_util_bytes2num(nfcv_data->sub_data.slix.key_write, 4)); - furi_string_cat_printf( - temp_str, - " Privacy %08llX\n", - nfc_util_bytes2num(nfcv_data->sub_data.slix.key_privacy, 4)); - furi_string_cat_printf( - temp_str, - " Destroy %08llX\n", - nfc_util_bytes2num(nfcv_data->sub_data.slix.key_destroy, 4)); - furi_string_cat_printf( - temp_str, - " EAS %08llX\n", - nfc_util_bytes2num(nfcv_data->sub_data.slix.key_eas, 4)); + nfc_scene_slix_build_string(temp_str, nfcv_data, SlixFeatureSlixS, "SLIX-S"); break; case NfcVTypeSlixL: - furi_string_cat_printf(temp_str, "Type: SLIX-L\n"); - furi_string_cat_printf(temp_str, "Keys:\n"); - furi_string_cat_printf( - temp_str, - " Privacy %08llX\n", - nfc_util_bytes2num(nfcv_data->sub_data.slix.key_privacy, 4)); - furi_string_cat_printf( - temp_str, - " Destroy %08llX\n", - nfc_util_bytes2num(nfcv_data->sub_data.slix.key_destroy, 4)); - furi_string_cat_printf( - temp_str, - " EAS %08llX\n", - nfc_util_bytes2num(nfcv_data->sub_data.slix.key_eas, 4)); + nfc_scene_slix_build_string(temp_str, nfcv_data, SlixFeatureSlixL, "SLIX-L"); break; case NfcVTypeSlix2: - furi_string_cat_printf(temp_str, "Type: SLIX2\n"); - furi_string_cat_printf(temp_str, "Keys:\n"); - furi_string_cat_printf( - temp_str, - " Read %08llX\n", - nfc_util_bytes2num(nfcv_data->sub_data.slix.key_read, 4)); - furi_string_cat_printf( - temp_str, - " Write %08llX\n", - nfc_util_bytes2num(nfcv_data->sub_data.slix.key_write, 4)); - furi_string_cat_printf( - temp_str, - " Privacy %08llX\n", - nfc_util_bytes2num(nfcv_data->sub_data.slix.key_privacy, 4)); - furi_string_cat_printf( - temp_str, - " Destroy %08llX\n", - nfc_util_bytes2num(nfcv_data->sub_data.slix.key_destroy, 4)); - furi_string_cat_printf( - temp_str, - " EAS %08llX\n", - nfc_util_bytes2num(nfcv_data->sub_data.slix.key_eas, 4)); + nfc_scene_slix_build_string(temp_str, nfcv_data, SlixFeatureSlix2, "SLIX2"); break; default: furi_string_cat_printf(temp_str, "\e#ISO15693 (unknown)\n"); diff --git a/applications/main/nfc/scenes/nfc_scene_nfca_menu.c b/applications/main/nfc/scenes/nfc_scene_nfca_menu.c index 30f63945c6..9779470a38 100644 --- a/applications/main/nfc/scenes/nfc_scene_nfca_menu.c +++ b/applications/main/nfc/scenes/nfc_scene_nfca_menu.c @@ -43,9 +43,9 @@ bool nfc_scene_nfca_menu_on_event(void* context, SceneManagerEvent event) { } else if(event.event == SubmenuIndexEmulateUid) { scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid); if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) { - DOLPHIN_DEED(DolphinDeedNfcAddEmulate); + dolphin_deed(DolphinDeedNfcAddEmulate); } else { - DOLPHIN_DEED(DolphinDeedNfcEmulate); + dolphin_deed(DolphinDeedNfcEmulate); } consumed = true; } else if(event.event == SubmenuIndexInfo) { diff --git a/applications/main/nfc/scenes/nfc_scene_nfcf_menu.c b/applications/main/nfc/scenes/nfc_scene_nfcf_menu.c index cae7055b1b..69815dab15 100644 --- a/applications/main/nfc/scenes/nfc_scene_nfcf_menu.c +++ b/applications/main/nfc/scenes/nfc_scene_nfcf_menu.c @@ -49,9 +49,9 @@ bool nfc_scene_nfcf_menu_on_event(void* context, SceneManagerEvent event) { } else if(event.event == SubmenuIndexEmulate) { scene_manager_next_scene(nfc->scene_manager, NfcSceneFelicaEmulate); if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) { - DOLPHIN_DEED(DolphinDeedNfcAddEmulate); + dolphin_deed(DolphinDeedNfcAddEmulate); } else { - DOLPHIN_DEED(DolphinDeedNfcEmulate); + dolphin_deed(DolphinDeedNfcEmulate); } consumed = true; } else if(event.event == SubmenuIndexUnlock) { diff --git a/applications/main/nfc/scenes/nfc_scene_nfcv_emulate.c b/applications/main/nfc/scenes/nfc_scene_nfcv_emulate.c index 3dd7c460b5..d812988bdf 100644 --- a/applications/main/nfc/scenes/nfc_scene_nfcv_emulate.c +++ b/applications/main/nfc/scenes/nfc_scene_nfcv_emulate.c @@ -50,7 +50,7 @@ static void nfc_scene_nfcv_emulate_widget_config(Nfc* nfc, bool data_received) { widget_add_icon_element(widget, 0, 3, &I_NFC_dolphin_emulation_47x61); widget_add_string_multiline_element( widget, 87, 13, AlignCenter, AlignTop, FontPrimary, "Emulating\nNFC V"); - if(strcmp(nfc->dev->dev_name, "")) { + if(strcmp(nfc->dev->dev_name, "") != 0) { furi_string_printf(info_str, "%s", nfc->dev->dev_name); } else { for(uint8_t i = 0; i < data->uid_len; i++) { diff --git a/applications/main/nfc/scenes/nfc_scene_nfcv_key_input.c b/applications/main/nfc/scenes/nfc_scene_nfcv_key_input.c index cc53c4dcb4..13d903c4b7 100644 --- a/applications/main/nfc/scenes/nfc_scene_nfcv_key_input.c +++ b/applications/main/nfc/scenes/nfc_scene_nfcv_key_input.c @@ -32,7 +32,7 @@ bool nfc_scene_nfcv_key_input_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == NfcCustomEventByteInputDone) { scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVUnlock); - DOLPHIN_DEED(DolphinDeedNfcRead); + dolphin_deed(DolphinDeedNfcRead); consumed = true; } } diff --git a/applications/main/nfc/scenes/nfc_scene_nfcv_menu.c b/applications/main/nfc/scenes/nfc_scene_nfcv_menu.c index 7c6780b7c7..60eb354e85 100644 --- a/applications/main/nfc/scenes/nfc_scene_nfcv_menu.c +++ b/applications/main/nfc/scenes/nfc_scene_nfcv_menu.c @@ -42,9 +42,9 @@ bool nfc_scene_nfcv_menu_on_event(void* context, SceneManagerEvent event) { } else if(event.event == SubmenuIndexEmulate) { scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVEmulate); if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) { - DOLPHIN_DEED(DolphinDeedNfcAddEmulate); + dolphin_deed(DolphinDeedNfcAddEmulate); } else { - DOLPHIN_DEED(DolphinDeedNfcEmulate); + dolphin_deed(DolphinDeedNfcEmulate); } consumed = true; } else if(event.event == SubmenuIndexInfo) { diff --git a/applications/main/nfc/scenes/nfc_scene_nfcv_read_success.c b/applications/main/nfc/scenes/nfc_scene_nfcv_read_success.c index bdf7692ccb..04e60611d0 100644 --- a/applications/main/nfc/scenes/nfc_scene_nfcv_read_success.c +++ b/applications/main/nfc/scenes/nfc_scene_nfcv_read_success.c @@ -16,7 +16,6 @@ void nfc_scene_nfcv_read_success_on_enter(void* context) { Nfc* nfc = context; NfcDeviceData* dev_data = &nfc->dev->dev_data; FuriHalNfcDevData* nfc_data = &nfc->dev->dev_data.nfc_data; - NfcVData* nfcv_data = &nfc->dev->dev_data.nfcv_data; // Setup view Widget* widget = nfc->widget; widget_add_button_element( @@ -46,13 +45,12 @@ void nfc_scene_nfcv_read_success_on_enter(void* context) { furi_string_cat_printf(temp_str, "\e#ISO15693 (unknown)\n"); break; } - furi_string_cat_printf(temp_str, "UID:"); + furi_string_cat_printf(temp_str, "UID:\n"); for(size_t i = 0; i < nfc_data->uid_len; i++) { furi_string_cat_printf(temp_str, " %02X", nfc_data->uid[i]); } furi_string_cat_printf(temp_str, "\n"); - furi_string_cat_printf(temp_str, "Blocks: %02X\n", nfcv_data->block_num); - furi_string_cat_printf(temp_str, "Blocksize: %02X\n", nfcv_data->block_size); + furi_string_cat_printf(temp_str, "(see More->Info for details)\n"); widget_add_text_scroll_element(widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str)); furi_string_free(temp_str); diff --git a/applications/main/nfc/scenes/nfc_scene_nfcv_unlock.c b/applications/main/nfc/scenes/nfc_scene_nfcv_unlock.c index 26de304de5..38d7ad563d 100644 --- a/applications/main/nfc/scenes/nfc_scene_nfcv_unlock.c +++ b/applications/main/nfc/scenes/nfc_scene_nfcv_unlock.c @@ -76,7 +76,7 @@ void nfc_scene_nfcv_unlock_set_state(Nfc* nfc, NfcSceneNfcVUnlockState state) { popup_set_timeout(popup, 1500); view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); - DOLPHIN_DEED(DolphinDeedNfcReadSuccess); + dolphin_deed(DolphinDeedNfcReadSuccess); } else if(state == NfcSceneNfcVUnlockStateAlreadyUnlocked) { popup_reset(popup); diff --git a/applications/main/nfc/scenes/nfc_scene_nfcv_unlock_menu.c b/applications/main/nfc/scenes/nfc_scene_nfcv_unlock_menu.c index 9c4c81fbda..2f73672567 100644 --- a/applications/main/nfc/scenes/nfc_scene_nfcv_unlock_menu.c +++ b/applications/main/nfc/scenes/nfc_scene_nfcv_unlock_menu.c @@ -45,7 +45,7 @@ bool nfc_scene_nfcv_unlock_menu_on_event(void* context, SceneManagerEvent event) } else if(event.event == SubmenuIndexNfcVUnlockMenuTonieBox) { nfc->dev->dev_data.nfcv_data.auth_method = NfcVAuthMethodTonieBox; scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVUnlock); - DOLPHIN_DEED(DolphinDeedNfcRead); + dolphin_deed(DolphinDeedNfcRead); consumed = true; } scene_manager_set_scene_state(nfc->scene_manager, NfcSceneNfcVUnlockMenu, event.event); diff --git a/applications/main/nfc/scenes/nfc_scene_read.c b/applications/main/nfc/scenes/nfc_scene_read.c index 6655c06bcd..45b0f4a7eb 100644 --- a/applications/main/nfc/scenes/nfc_scene_read.c +++ b/applications/main/nfc/scenes/nfc_scene_read.c @@ -60,39 +60,39 @@ bool nfc_scene_read_on_event(void* context, SceneManagerEvent event) { (event.event == NfcWorkerEventReadUidNfcV)) { notification_message(nfc->notifications, &sequence_success); scene_manager_next_scene(nfc->scene_manager, NfcSceneReadCardSuccess); - DOLPHIN_DEED(DolphinDeedNfcReadSuccess); + dolphin_deed(DolphinDeedNfcReadSuccess); consumed = true; } else if(event.event == NfcWorkerEventReadUidNfcA) { notification_message(nfc->notifications, &sequence_success); scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcaReadSuccess); - DOLPHIN_DEED(DolphinDeedNfcReadSuccess); + dolphin_deed(DolphinDeedNfcReadSuccess); consumed = true; } else if(event.event == NfcWorkerEventReadNfcV) { notification_message(nfc->notifications, &sequence_success); scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVReadSuccess); - DOLPHIN_DEED(DolphinDeedNfcReadSuccess); + dolphin_deed(DolphinDeedNfcReadSuccess); consumed = true; } else if(event.event == NfcWorkerEventReadMfUltralight) { notification_message(nfc->notifications, &sequence_success); // Set unlock password input to 0xFFFFFFFF only on fresh read memset(nfc->byte_input_store, 0xFF, sizeof(nfc->byte_input_store)); scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightReadSuccess); - DOLPHIN_DEED(DolphinDeedNfcReadSuccess); + dolphin_deed(DolphinDeedNfcReadSuccess); consumed = true; } else if(event.event == NfcWorkerEventReadMfClassicDone) { notification_message(nfc->notifications, &sequence_success); scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicReadSuccess); - DOLPHIN_DEED(DolphinDeedNfcReadSuccess); + dolphin_deed(DolphinDeedNfcReadSuccess); consumed = true; } else if(event.event == NfcWorkerEventReadMfDesfire) { notification_message(nfc->notifications, &sequence_success); scene_manager_next_scene(nfc->scene_manager, NfcSceneMfDesfireReadSuccess); - DOLPHIN_DEED(DolphinDeedNfcReadSuccess); + dolphin_deed(DolphinDeedNfcReadSuccess); consumed = true; } else if(event.event == NfcWorkerEventReadBankCard) { notification_message(nfc->notifications, &sequence_success); scene_manager_next_scene(nfc->scene_manager, NfcSceneEmvReadSuccess); - DOLPHIN_DEED(DolphinDeedNfcReadSuccess); + dolphin_deed(DolphinDeedNfcReadSuccess); consumed = true; } else if(event.event == NfcWorkerEventReadMfClassicDictAttackRequired) { if(mf_classic_dict_check_presence(MfClassicDictTypeSystem)) { @@ -104,12 +104,12 @@ bool nfc_scene_read_on_event(void* context, SceneManagerEvent event) { } else if(event.event == NfcWorkerEventReadUidNfcF) { notification_message(nfc->notifications, &sequence_success); scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcfReadSuccess); - DOLPHIN_DEED(DolphinDeedNfcReadSuccess); + dolphin_deed(DolphinDeedNfcReadSuccess); consumed = true; } else if(event.event == NfcWorkerEventReadFelica) { notification_message(nfc->notifications, &sequence_success); scene_manager_next_scene(nfc->scene_manager, NfcSceneFelicaReadSuccess); - DOLPHIN_DEED(DolphinDeedNfcReadSuccess); + dolphin_deed(DolphinDeedNfcReadSuccess); consumed = true; } else if(event.event == NfcWorkerEventCardDetected) { nfc_scene_read_set_state(nfc, NfcSceneReadStateReading); diff --git a/applications/main/nfc/scenes/nfc_scene_restore_original_confirm.c b/applications/main/nfc/scenes/nfc_scene_restore_original_confirm.c index 730dd41e85..16b0953f80 100644 --- a/applications/main/nfc/scenes/nfc_scene_restore_original_confirm.c +++ b/applications/main/nfc/scenes/nfc_scene_restore_original_confirm.c @@ -11,7 +11,7 @@ void nfc_scene_restore_original_confirm_on_enter(void* context) { DialogEx* dialog_ex = nfc->dialog_ex; dialog_ex_set_header(dialog_ex, "Restore Card Data?", 64, 0, AlignCenter, AlignTop); - dialog_ex_set_icon(dialog_ex, 5, 15, &I_Restoring_38x32); + dialog_ex_set_icon(dialog_ex, 5, 11, &I_ArrowC_1_36x36); dialog_ex_set_text( dialog_ex, "It will be returned\nto its original state.", 47, 21, AlignLeft, AlignTop); dialog_ex_set_left_button_text(dialog_ex, "Cancel"); diff --git a/applications/main/nfc/scenes/nfc_scene_save_name.c b/applications/main/nfc/scenes/nfc_scene_save_name.c index 0072742267..a7b97aac06 100644 --- a/applications/main/nfc/scenes/nfc_scene_save_name.c +++ b/applications/main/nfc/scenes/nfc_scene_save_name.c @@ -67,9 +67,9 @@ bool nfc_scene_save_name_on_event(void* context, SceneManagerEvent event) { if(!scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSavedMenu)) { // Nothing, do not count editing as saving } else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) { - DOLPHIN_DEED(DolphinDeedNfcAddSave); + dolphin_deed(DolphinDeedNfcAddSave); } else { - DOLPHIN_DEED(DolphinDeedNfcSave); + dolphin_deed(DolphinDeedNfcSave); } consumed = true; } else { diff --git a/applications/main/nfc/scenes/nfc_scene_saved_menu.c b/applications/main/nfc/scenes/nfc_scene_saved_menu.c index 4371db48ba..9b0c633d39 100644 --- a/applications/main/nfc/scenes/nfc_scene_saved_menu.c +++ b/applications/main/nfc/scenes/nfc_scene_saved_menu.c @@ -125,11 +125,11 @@ bool nfc_scene_saved_menu_on_event(void* context, SceneManagerEvent event) { } else { scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid); } - DOLPHIN_DEED(DolphinDeedNfcEmulate); + dolphin_deed(DolphinDeedNfcEmulate); consumed = true; } else if(event.event == SubmenuIndexDetectReader) { scene_manager_next_scene(nfc->scene_manager, NfcSceneDetectReader); - DOLPHIN_DEED(DolphinDeedNfcDetectReader); + dolphin_deed(DolphinDeedNfcDetectReader); consumed = true; } else if(event.event == SubmenuIndexWrite) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicWrite); diff --git a/applications/main/nfc/scenes/nfc_scene_start.c b/applications/main/nfc/scenes/nfc_scene_start.c index 48ddf55c51..48c6d1d752 100644 --- a/applications/main/nfc/scenes/nfc_scene_start.c +++ b/applications/main/nfc/scenes/nfc_scene_start.c @@ -55,7 +55,7 @@ bool nfc_scene_start_on_event(void* context, SceneManagerEvent event) { scene_manager_set_scene_state(nfc->scene_manager, NfcSceneStart, SubmenuIndexRead); nfc->dev->dev_data.read_mode = NfcReadModeAuto; scene_manager_next_scene(nfc->scene_manager, NfcSceneRead); - DOLPHIN_DEED(DolphinDeedNfcRead); + dolphin_deed(DolphinDeedNfcRead); consumed = true; } else if(event.event == SubmenuIndexDetectReader) { scene_manager_set_scene_state( @@ -64,7 +64,7 @@ bool nfc_scene_start_on_event(void* context, SceneManagerEvent event) { if(sd_exist) { nfc_device_data_clear(&nfc->dev->dev_data); scene_manager_next_scene(nfc->scene_manager, NfcSceneDetectReader); - DOLPHIN_DEED(DolphinDeedNfcDetectReader); + dolphin_deed(DolphinDeedNfcDetectReader); } else { scene_manager_next_scene(nfc->scene_manager, NfcSceneDictNotFound); } diff --git a/applications/main/subghz/scenes/subghz_scene_radio_settings.c b/applications/main/subghz/scenes/subghz_scene_radio_settings.c index 77723fa94b..80f05ad172 100644 --- a/applications/main/subghz/scenes/subghz_scene_radio_settings.c +++ b/applications/main/subghz/scenes/subghz_scene_radio_settings.c @@ -111,7 +111,6 @@ static void subghz_scene_receiver_config_set_timestamp_file_names(VariableItem* variable_item_set_current_value_text(item, timestamp_names_text[index]); - furi_hal_subghz_set_timestamp_file_names((index == 1)); subghz->last_settings->timestamp_file_names = (index == 1); subghz_last_settings_save(subghz->last_settings); } @@ -148,7 +147,7 @@ void subghz_scene_radio_settings_on_enter(void* context) { TIMESTAMP_NAMES_COUNT, subghz_scene_receiver_config_set_timestamp_file_names, subghz); - value_index = furi_hal_subghz_get_timestamp_file_names(); + value_index = subghz->last_settings->timestamp_file_names; variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_text(item, timestamp_names_text[value_index]); diff --git a/applications/main/subghz/scenes/subghz_scene_read_raw.c b/applications/main/subghz/scenes/subghz_scene_read_raw.c index 92fcfe125c..7394ace90f 100644 --- a/applications/main/subghz/scenes/subghz_scene_read_raw.c +++ b/applications/main/subghz/scenes/subghz_scene_read_raw.c @@ -226,7 +226,7 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { } else { if(scene_manager_has_previous_scene(subghz->scene_manager, SubGhzSceneSaved) || !scene_manager_has_previous_scene(subghz->scene_manager, SubGhzSceneStart)) { - DOLPHIN_DEED(DolphinDeedSubGhzSend); + dolphin_deed(DolphinDeedSubGhzSend); } // set callback end tx subghz_txrx_set_raw_file_encoder_worker_callback_end( @@ -288,7 +288,7 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { } else { SubGhzRadioPreset preset = subghz_txrx_get_preset(subghz->txrx); if(subghz_protocol_raw_save_to_file_init(decoder_raw, RAW_FILE_NAME, &preset)) { - DOLPHIN_DEED(DolphinDeedSubGhzRawRec); + dolphin_deed(DolphinDeedSubGhzRawRec); subghz_txrx_rx_start(subghz->txrx); subghz->state_notifications = SubGhzNotificationStateRx; subghz_rx_key_state_set(subghz, SubGhzRxKeyStateAddKey); diff --git a/applications/main/subghz/scenes/subghz_scene_receiver.c b/applications/main/subghz/scenes/subghz_scene_receiver.c index 26e92e9833..343c803cde 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver.c @@ -92,32 +92,41 @@ static void subghz_scene_add_to_history_callback( void* context) { furi_assert(context); SubGhz* subghz = context; - SubGhzHistory* history = subghz->history; - FuriString* item_name = furi_string_alloc(); - FuriString* item_time = furi_string_alloc(); - uint16_t idx = subghz_history_get_item(history); - SubGhzRadioPreset preset = subghz_txrx_get_preset(subghz->txrx); - if(subghz_history_add_to_history(history, decoder_base, &preset)) { - furi_string_reset(item_name); - furi_string_reset(item_time); + // The check can be moved to /lib/subghz/receiver.c, but may result in false positives + if((decoder_base->protocol->flag & subghz->ignore_filter) == 0) { + SubGhzHistory* history = subghz->history; + FuriString* item_name = furi_string_alloc(); + FuriString* item_time = furi_string_alloc(); + uint16_t idx = subghz_history_get_item(history); - subghz->state_notifications = SubGhzNotificationStateRxDone; + SubGhzRadioPreset preset = subghz_txrx_get_preset(subghz->txrx); + if(subghz_history_add_to_history(history, decoder_base, &preset)) { + furi_string_reset(item_name); + furi_string_reset(item_time); - subghz_history_get_text_item_menu(history, item_name, idx); - subghz_history_get_time_item_menu(history, item_time, idx); - subghz_view_receiver_add_item_to_menu( - subghz->subghz_receiver, - furi_string_get_cstr(item_name), - furi_string_get_cstr(item_time), - subghz_history_get_type_protocol(history, idx)); + subghz->state_notifications = SubGhzNotificationStateRxDone; + + subghz_history_get_text_item_menu(history, item_name, idx); + subghz_history_get_time_item_menu(history, item_time, idx); + subghz_view_receiver_add_item_to_menu( + subghz->subghz_receiver, + furi_string_get_cstr(item_name), + furi_string_get_cstr(item_time), + subghz_history_get_type_protocol(history, idx)); - subghz_scene_receiver_update_statusbar(subghz); + subghz_scene_receiver_update_statusbar(subghz); + if(subghz_history_get_text_space_left(subghz->history, NULL)) { + notification_message(subghz->notifications, &sequence_error); + } + } + subghz_receiver_reset(receiver); + furi_string_free(item_name); + furi_string_free(item_time); + subghz_rx_key_state_set(subghz, SubGhzRxKeyStateAddKey); + } else { + FURI_LOG_I(TAG, "%s protocol ignored", decoder_base->protocol->name); } - subghz_receiver_reset(receiver); - furi_string_free(item_name); - furi_string_free(item_time); - subghz_rx_key_state_set(subghz, SubGhzRxKeyStateAddKey); } void subghz_scene_receiver_on_enter(void* context) { @@ -131,6 +140,7 @@ void subghz_scene_receiver_on_enter(void* context) { subghz_txrx_set_preset(subghz->txrx, "AM650", subghz->last_settings->frequency, NULL, 0); subghz_history_reset(history); subghz_rx_key_state_set(subghz, SubGhzRxKeyStateStart); + subghz->idx_menu_chosen = 0; } subghz_view_receiver_set_lock(subghz->subghz_receiver, subghz_is_locked(subghz)); @@ -157,33 +167,9 @@ void subghz_scene_receiver_on_enter(void* context) { subghz->subghz_receiver, subghz_scene_receiver_callback, subghz); subghz_txrx_set_rx_calback(subghz->txrx, subghz_scene_add_to_history_callback, subghz); - // TODO: Replace with proper solution based on protocol flags, remove kostily and velosipedy from here - // Needs to be done after subghz refactoring merge!!! - if(subghz->ignore_starline == true) { - if(subghz_txrx_load_decoder_by_name_protocol(subghz->txrx, "Star Line")) { - subghz_protocol_decoder_base_set_decoder_callback( - subghz_txrx_get_decoder(subghz->txrx), NULL, NULL); - } - } - if(subghz->ignore_auto_alarms == true) { - if(subghz_txrx_load_decoder_by_name_protocol(subghz->txrx, "KIA Seed")) { - subghz_protocol_decoder_base_set_decoder_callback( - subghz_txrx_get_decoder(subghz->txrx), NULL, NULL); - } - - if(subghz_txrx_load_decoder_by_name_protocol(subghz->txrx, "Scher-Khan")) { - subghz_protocol_decoder_base_set_decoder_callback( - subghz_txrx_get_decoder(subghz->txrx), NULL, NULL); - } - } - if(subghz->ignore_magellan == true) { - if(subghz_txrx_load_decoder_by_name_protocol(subghz->txrx, "Magellan")) { - subghz_protocol_decoder_base_set_decoder_callback( - subghz_txrx_get_decoder(subghz->txrx), NULL, NULL); - } + if(!subghz_history_get_text_space_left(subghz->history, NULL)) { + subghz->state_notifications = SubGhzNotificationStateRx; } - - subghz->state_notifications = SubGhzNotificationStateRx; subghz_txrx_rx_start(subghz->txrx); subghz_view_receiver_set_idx_menu(subghz->subghz_receiver, subghz->idx_menu_chosen); @@ -198,13 +184,15 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) { SubGhz* subghz = context; bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { + // Save cursor position before going to any other dialog + subghz->idx_menu_chosen = subghz_view_receiver_get_idx_menu(subghz->subghz_receiver); + switch(event.event) { case SubGhzCustomEventViewReceiverBack: // Stop CC1101 Rx subghz->state_notifications = SubGhzNotificationStateIDLE; subghz_txrx_stop(subghz->txrx); subghz_txrx_hopper_set_state(subghz->txrx, SubGhzHopperStateOFF); - subghz->idx_menu_chosen = 0; subghz_txrx_set_rx_calback(subghz->txrx, NULL, subghz); if(subghz_rx_key_state_get(subghz) == SubGhzRxKeyStateAddKey) { @@ -221,31 +209,28 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) { break; case SubGhzCustomEventViewReceiverOK: // Show file info, scene: receiver_info - subghz->idx_menu_chosen = subghz_view_receiver_get_idx_menu(subghz->subghz_receiver); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiverInfo); - DOLPHIN_DEED(DolphinDeedSubGhzReceiverInfo); + dolphin_deed(DolphinDeedSubGhzReceiverInfo); consumed = true; break; case SubGhzCustomEventViewReceiverDeleteItem: subghz->state_notifications = SubGhzNotificationStateRx; - subghz->idx_menu_chosen = subghz_view_receiver_get_idx_menu(subghz->subghz_receiver); subghz_view_receiver_disable_draw_callback(subghz->subghz_receiver); subghz_history_delete_item(subghz->history, subghz->idx_menu_chosen); subghz_view_receiver_delete_element_callback(subghz->subghz_receiver); - subghz_view_receiver_enable_draw_callback(subghz->subghz_receiver); subghz_scene_receiver_update_statusbar(subghz); + subghz->idx_menu_chosen = subghz_view_receiver_get_idx_menu(subghz->subghz_receiver); if(subghz_history_get_last_index(subghz->history) == 0) { subghz_rx_key_state_set(subghz, SubGhzRxKeyStateStart); } consumed = true; break; case SubGhzCustomEventViewReceiverConfig: - subghz->state_notifications = SubGhzNotificationStateIDLE; - subghz->idx_menu_chosen = subghz_view_receiver_get_idx_menu(subghz->subghz_receiver); + // Actually signals are received but SubGhzNotificationStateRx is not working inside Config Scene scene_manager_set_scene_state( subghz->scene_manager, SubGhzViewIdReceiver, SubGhzCustomEventManagerSet); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiverConfig); diff --git a/applications/main/subghz/scenes/subghz_scene_receiver_config.c b/applications/main/subghz/scenes/subghz_scene_receiver_config.c index 29d55c03a3..62ee538712 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver_config.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver_config.c @@ -71,20 +71,8 @@ const uint32_t bin_raw_value[BIN_RAW_COUNT] = { SubGhzProtocolFlag_Decodable, SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_BinRAW, }; -#define STARLINE_COUNT 2 -const char* const starline_text[STARLINE_COUNT] = { - "OFF", - "ON", -}; - -#define AUTO_ALARMS_COUNT 2 -const char* const auto_alarms_text[AUTO_ALARMS_COUNT] = { - "OFF", - "ON", -}; - -#define MAGELLAN_COUNT 2 -const char* const magellan_text[MAGELLAN_COUNT] = { +#define PROTOCOL_IGNORE_COUNT 2 +const char* const protocol_ignore_text[PROTOCOL_IGNORE_COUNT] = { "OFF", "ON", }; @@ -257,28 +245,35 @@ static void subghz_scene_receiver_config_set_raw_threshold_rssi(VariableItem* it subghz_threshold_rssi_set(subghz->threshold_rssi, raw_threshold_rssi_value[index]); } -static void subghz_scene_receiver_config_set_starline(VariableItem* item) { +static inline void + subghz_scene_receiver_config_set_ignore_filter(VariableItem* item, SubGhzProtocolFlag filter) { SubGhz* subghz = variable_item_get_context(item); uint8_t index = variable_item_get_current_value_index(item); - variable_item_set_current_value_text(item, starline_text[index]); - subghz->ignore_starline = (index == 1); + variable_item_set_current_value_text(item, protocol_ignore_text[index]); + + if(index == 0) { + CLEAR_BIT(subghz->ignore_filter, filter); + } else { + SET_BIT(subghz->ignore_filter, filter); + } +} +static inline bool subghz_scene_receiver_config_ignore_filter_get_index( + SubGhzProtocolFlag filter, + SubGhzProtocolFlag flag) { + return READ_BIT(filter, flag) > 0; +} + +static void subghz_scene_receiver_config_set_starline(VariableItem* item) { + subghz_scene_receiver_config_set_ignore_filter(item, SubGhzProtocolFlag_StarLine); } static void subghz_scene_receiver_config_set_auto_alarms(VariableItem* item) { - SubGhz* subghz = variable_item_get_context(item); - uint8_t index = variable_item_get_current_value_index(item); - - variable_item_set_current_value_text(item, auto_alarms_text[index]); - subghz->ignore_auto_alarms = (index == 1); + subghz_scene_receiver_config_set_ignore_filter(item, SubGhzProtocolFlag_AutoAlarms); } static void subghz_scene_receiver_config_set_magellan(VariableItem* item) { - SubGhz* subghz = variable_item_get_context(item); - uint8_t index = variable_item_get_current_value_index(item); - - variable_item_set_current_value_text(item, magellan_text[index]); - subghz->ignore_magellan = (index == 1); + subghz_scene_receiver_config_set_ignore_filter(item, SubGhzProtocolFlag_Magelan); } static void subghz_scene_receiver_config_var_list_enter_callback(void* context, uint32_t index) { @@ -362,35 +357,38 @@ void subghz_scene_receiver_config_on_enter(void* context) { item = variable_item_list_add( subghz->variable_item_list, "Ignore Starline:", - STARLINE_COUNT, + PROTOCOL_IGNORE_COUNT, subghz_scene_receiver_config_set_starline, subghz); - value_index = subghz->ignore_starline; + value_index = subghz_scene_receiver_config_ignore_filter_get_index( + subghz->ignore_filter, SubGhzProtocolFlag_StarLine); variable_item_set_current_value_index(item, value_index); - variable_item_set_current_value_text(item, starline_text[value_index]); + variable_item_set_current_value_text(item, protocol_ignore_text[value_index]); item = variable_item_list_add( subghz->variable_item_list, "Ignore Cars:", - AUTO_ALARMS_COUNT, + PROTOCOL_IGNORE_COUNT, subghz_scene_receiver_config_set_auto_alarms, subghz); - value_index = subghz->ignore_auto_alarms; + value_index = subghz_scene_receiver_config_ignore_filter_get_index( + subghz->ignore_filter, SubGhzProtocolFlag_AutoAlarms); variable_item_set_current_value_index(item, value_index); - variable_item_set_current_value_text(item, auto_alarms_text[value_index]); + variable_item_set_current_value_text(item, protocol_ignore_text[value_index]); item = variable_item_list_add( subghz->variable_item_list, "Ignore Magellan:", - MAGELLAN_COUNT, + PROTOCOL_IGNORE_COUNT, subghz_scene_receiver_config_set_magellan, subghz); - value_index = subghz->ignore_magellan; + value_index = subghz_scene_receiver_config_ignore_filter_get_index( + subghz->ignore_filter, SubGhzProtocolFlag_Magelan); variable_item_set_current_value_index(item, value_index); - variable_item_set_current_value_text(item, magellan_text[value_index]); + variable_item_set_current_value_text(item, protocol_ignore_text[value_index]); } // Enable speaker, will send all incoming noises and signals to speaker so you can listen how your remote sounds like :) diff --git a/applications/main/subghz/scenes/subghz_scene_receiver_info.c b/applications/main/subghz/scenes/subghz_scene_receiver_info.c index e3ff485d22..fc1c1a8e93 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver_info.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver_info.c @@ -109,6 +109,10 @@ void subghz_scene_receiver_info_on_enter(void* context) { subghz_custom_btns_reset(); subghz_scene_receiver_info_draw_widget(subghz); + + if(!subghz_history_get_text_space_left(subghz->history, NULL)) { + subghz->state_notifications = SubGhzNotificationStateRx; + } } bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event) { @@ -142,7 +146,9 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event) subghz_txrx_rx_start(subghz->txrx); subghz_txrx_hopper_unpause(subghz->txrx); - subghz->state_notifications = SubGhzNotificationStateRx; + if(!subghz_history_get_text_space_left(subghz->history, NULL)) { + subghz->state_notifications = SubGhzNotificationStateRx; + } } return true; } else if(event.event == SubGhzCustomEventSceneReceiverInfoSave) { diff --git a/applications/main/subghz/scenes/subghz_scene_save_name.c b/applications/main/subghz/scenes/subghz_scene_save_name.c index 74a861efb5..845b40f414 100644 --- a/applications/main/subghz/scenes/subghz_scene_save_name.c +++ b/applications/main/subghz/scenes/subghz_scene_save_name.c @@ -6,8 +6,6 @@ #include #include -#define MAX_TEXT_INPUT_LEN 23 - void subghz_scene_save_name_text_input_callback(void* context) { furi_assert(context); SubGhz* subghz = context; @@ -56,7 +54,7 @@ void subghz_scene_save_name_on_enter(void* context) { if(!subghz_path_is_file(subghz->file_path)) { char file_name_buf[SUBGHZ_MAX_LEN_NAME] = {0}; - if(furi_hal_subghz_get_timestamp_file_names()) { + if(subghz->last_settings->timestamp_file_names) { SubGhzProtocolDecoderBase* decoder_result = subghz_txrx_get_decoder(subghz->txrx); if(decoder_result != 0x0) { if(decoder_result != NULL) { @@ -108,7 +106,7 @@ void subghz_scene_save_name_on_enter(void* context) { subghz_scene_save_name_text_input_callback, subghz, subghz->file_name_tmp, - MAX_TEXT_INPUT_LEN, + SUBGHZ_MAX_LEN_NAME, dev_name_empty); ValidatorIsFile* validator_is_file = validator_is_file_alloc_init( @@ -180,9 +178,9 @@ bool subghz_scene_save_name_on_event(void* context, SceneManagerEvent event) { // Ditto, for RAW signals } else if(scene_manager_has_previous_scene( subghz->scene_manager, SubGhzSceneSetType)) { - DOLPHIN_DEED(DolphinDeedSubGhzAddManually); + dolphin_deed(DolphinDeedSubGhzAddManually); } else { - DOLPHIN_DEED(DolphinDeedSubGhzSave); + dolphin_deed(DolphinDeedSubGhzSave); } return true; } else { @@ -205,4 +203,4 @@ void subghz_scene_save_name_on_exit(void* context) { // Clear view text_input_reset(subghz->text_input); -} \ No newline at end of file +} diff --git a/applications/main/subghz/scenes/subghz_scene_start.c b/applications/main/subghz/scenes/subghz_scene_start.c index 78b99c6910..0bcfe719f7 100644 --- a/applications/main/subghz/scenes/subghz_scene_start.c +++ b/applications/main/subghz/scenes/subghz_scene_start.c @@ -114,7 +114,7 @@ bool subghz_scene_start_on_event(void* context, SceneManagerEvent event) { scene_manager_set_scene_state( subghz->scene_manager, SubGhzSceneStart, SubmenuIndexFrequencyAnalyzer); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneFrequencyAnalyzer); - DOLPHIN_DEED(DolphinDeedSubGhzFrequencyAnalyzer); + dolphin_deed(DolphinDeedSubGhzFrequencyAnalyzer); return true; } else if(event.event == SubmenuIndexTest) { scene_manager_set_scene_state( diff --git a/applications/main/subghz/scenes/subghz_scene_transmitter.c b/applications/main/subghz/scenes/subghz_scene_transmitter.c index 010c81e1a7..c7c95f457c 100644 --- a/applications/main/subghz/scenes/subghz_scene_transmitter.c +++ b/applications/main/subghz/scenes/subghz_scene_transmitter.c @@ -91,7 +91,7 @@ bool subghz_scene_transmitter_on_event(void* context, SceneManagerEvent event) { if(subghz_tx_start(subghz, subghz_txrx_get_fff_data(subghz->txrx))) { subghz->state_notifications = SubGhzNotificationStateTx; subghz_scene_transmitter_update_data_show(subghz); - DOLPHIN_DEED(DolphinDeedSubGhzSend); + dolphin_deed(DolphinDeedSubGhzSend); } return true; } else if(event.event == SubGhzCustomEventViewTransmitterSendStop) { diff --git a/applications/main/subghz/subghz.c b/applications/main/subghz/subghz.c index 39cb128a74..f03ad39b23 100644 --- a/applications/main/subghz/subghz.c +++ b/applications/main/subghz/subghz.c @@ -53,6 +53,42 @@ static void subghz_rpc_command_callback(RpcAppSystemEvent event, void* context) } } +static void subghz_load_custom_presets(SubGhzSetting* setting) { + furi_assert(setting); + + const char* presets[][2] = { + {"FM95", + "02 0D 0B 06 08 32 07 04 14 00 13 02 12 04 11 83 10 67 15 24 18 18 19 16 1D 91 1C 00 1B 07 20 FB 22 10 21 56 00 00 C0 00 00 00 00 00 00 00"}, + + // #2-FSK 200khz BW / 135kHz Filter/ 15.86Khz Deviation + Ramping + {"FM15k", + "02 0D 03 47 08 32 0B 06 15 32 14 00 13 00 12 00 11 32 10 A7 18 18 19 1D 1D 92 1C 00 1B 04 20 FB 22 17 21 B6 00 00 00 12 0E 34 60 C5 C1 C0"}, + + // Pagers + {"Pagers", + "02 0D 07 04 08 32 0B 06 10 64 11 93 12 0C 13 02 14 00 15 15 18 18 19 16 1B 07 1C 00 1D 91 20 FB 21 56 22 10 00 00 C0 00 00 00 00 00 00 00"}, + + // # HND - FM preset + {"HND_1", + "02 0D 0B 06 08 32 07 04 14 00 13 02 12 04 11 36 10 69 15 32 18 18 19 16 1D 91 1C 00 1B 07 20 FB 22 10 21 56 00 00 C0 00 00 00 00 00 00 00"}, + }; + + FlipperFormat* fff_temp = flipper_format_string_alloc(); + + for(uint8_t i = 0; i < COUNT_OF(presets); i++) { + flipper_format_insert_or_update_string_cstr(fff_temp, "Custom_preset_data", presets[i][1]); + + flipper_format_rewind(fff_temp); + subghz_setting_load_custom_preset(setting, presets[i][0], fff_temp); + } + + flipper_format_free(fff_temp); + +#ifdef FURI_DEBUG + subghz_setting_customs_presets_to_log(setting); +#endif +} + SubGhz* subghz_alloc(bool alloc_for_tx_only) { SubGhz* subghz = malloc(sizeof(SubGhz)); @@ -179,62 +215,7 @@ SubGhz* subghz_alloc(bool alloc_for_tx_only) { SubGhzSetting* setting = subghz_txrx_get_setting(subghz->txrx); - // Custom Presets load without using config file - if(!alloc_for_tx_only) { - FlipperFormat* temp_fm_preset = flipper_format_string_alloc(); - flipper_format_write_string_cstr( - temp_fm_preset, - (const char*)"Custom_preset_data", - (const char*)"02 0D 0B 06 08 32 07 04 14 00 13 02 12 04 11 83 10 67 15 24 18 18 19 16 1D 91 1C 00 1B 07 20 FB 22 10 21 56 00 00 C0 00 00 00 00 00 00 00"); - flipper_format_rewind(temp_fm_preset); - subghz_setting_load_custom_preset(setting, (const char*)"FM95", temp_fm_preset); - - flipper_format_free(temp_fm_preset); - - // #2-FSK 200khz BW / 135kHz Filter/ 15.86Khz Deviation + Ramping - FlipperFormat* temp_fm_preset2 = flipper_format_string_alloc(); - flipper_format_write_string_cstr( - temp_fm_preset2, - (const char*)"Custom_preset_data", - (const char*)"02 0D 03 47 08 32 0B 06 15 32 14 00 13 00 12 00 11 32 10 A7 18 18 19 1D 1D 92 1C 00 1B 04 20 FB 22 17 21 B6 00 00 00 12 0E 34 60 C5 C1 C0"); - flipper_format_rewind(temp_fm_preset2); - subghz_setting_load_custom_preset(setting, (const char*)"FM15k", temp_fm_preset2); - - flipper_format_free(temp_fm_preset2); - - // Pagers - FlipperFormat* temp_fm_preset3 = flipper_format_string_alloc(); - flipper_format_write_string_cstr( - temp_fm_preset3, - (const char*)"Custom_preset_data", - (const char*)"02 0D 07 04 08 32 0B 06 10 64 11 93 12 0C 13 02 14 00 15 15 18 18 19 16 1B 07 1C 00 1D 91 20 FB 21 56 22 10 00 00 C0 00 00 00 00 00 00 00"); - flipper_format_rewind(temp_fm_preset3); - subghz_setting_load_custom_preset(setting, (const char*)"Pagers", temp_fm_preset3); - - flipper_format_free(temp_fm_preset3); - - // # HND - FM presets - FlipperFormat* temp_fm_preset4 = flipper_format_string_alloc(); - flipper_format_write_string_cstr( - temp_fm_preset4, - (const char*)"Custom_preset_data", - (const char*)"02 0D 0B 06 08 32 07 04 14 00 13 02 12 04 11 36 10 69 15 32 18 18 19 16 1D 91 1C 00 1B 07 20 FB 22 10 21 56 00 00 C0 00 00 00 00 00 00 00"); - flipper_format_rewind(temp_fm_preset4); - subghz_setting_load_custom_preset(setting, (const char*)"HND_1", temp_fm_preset4); - - flipper_format_free(temp_fm_preset4); - - FlipperFormat* temp_fm_preset5 = flipper_format_string_alloc(); - flipper_format_write_string_cstr( - temp_fm_preset5, - (const char*)"Custom_preset_data", - (const char*)"02 0D 0B 06 08 32 07 04 14 00 13 02 12 07 11 36 10 E9 15 32 18 18 19 16 1D 92 1C 40 1B 03 20 FB 22 10 21 56 00 00 C0 00 00 00 00 00 00 00"); - flipper_format_rewind(temp_fm_preset5); - subghz_setting_load_custom_preset(setting, (const char*)"HND_2", temp_fm_preset5); - - flipper_format_free(temp_fm_preset5); - } - // custom presets loading - end + subghz_load_custom_presets(setting); // Load last used values for Read, Read RAW, etc. or default subghz->last_settings = subghz_last_settings_alloc(); @@ -263,6 +244,7 @@ SubGhz* subghz_alloc(bool alloc_for_tx_only) { subghz->secure_data = malloc(sizeof(SecureData)); subghz->filter = SubGhzProtocolFlag_Decodable; + subghz->ignore_filter = 0x0; subghz_txrx_receiver_set_filter(subghz->txrx, subghz->filter); subghz_txrx_set_need_save_callback(subghz->txrx, subghz_save_to_file, subghz); diff --git a/applications/main/subghz/subghz_cli.c b/applications/main/subghz/subghz_cli.c index 83f21214f2..e0e2e48659 100644 --- a/applications/main/subghz/subghz_cli.c +++ b/applications/main/subghz/subghz_cli.c @@ -780,6 +780,15 @@ static void subghz_cli_command_chat(Cli* cli, FuriString* args, void* context) { static void subghz_cli_command(Cli* cli, FuriString* args, void* context) { FuriString* cmd = furi_string_alloc(); + // Enable power for External CC1101 if it is connected + furi_hal_subghz_enable_ext_power(); + // Auto switch to internal radio if external radio is not available + furi_delay_ms(15); + if(!furi_hal_subghz_check_radio()) { + furi_hal_subghz_select_radio_type(SubGhzRadioInternal); + furi_hal_subghz_init_radio_type(SubGhzRadioInternal); + } + do { if(!args_read_string_and_trim(args, cmd)) { subghz_cli_command_print_usage(); @@ -836,6 +845,11 @@ static void subghz_cli_command(Cli* cli, FuriString* args, void* context) { subghz_cli_command_print_usage(); } while(false); + // Disable power for External CC1101 if it was enabled and module is connected + furi_hal_subghz_disable_ext_power(); + // Reinit SPI handles for internal radio / nfc + furi_hal_subghz_init_radio_type(SubGhzRadioInternal); + furi_string_free(cmd); } diff --git a/applications/main/subghz/subghz_i.h b/applications/main/subghz/subghz_i.h index 9d91223320..4b5de724ef 100644 --- a/applications/main/subghz/subghz_i.h +++ b/applications/main/subghz/subghz_i.h @@ -89,13 +89,10 @@ struct SubGhz { SubGhzLastSettings* last_settings; SubGhzProtocolFlag filter; + SubGhzProtocolFlag ignore_filter; FuriString* error_str; SubGhzLock lock; - bool ignore_starline; - bool ignore_auto_alarms; - bool ignore_magellan; - SecureData* secure_data; SubGhzFileEncoderWorker* decode_raw_file_worker_encoder; diff --git a/applications/main/subghz/subghz_last_settings.c b/applications/main/subghz/subghz_last_settings.c index 7ee2554b0d..614e0baa63 100644 --- a/applications/main/subghz/subghz_last_settings.c +++ b/applications/main/subghz/subghz_last_settings.c @@ -119,9 +119,6 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count instance->timestamp_file_names = temp_timestamp_file_names; - // Set globally - furi_hal_subghz_set_timestamp_file_names(instance->timestamp_file_names); - if(instance->external_module_power_5v_disable) { furi_hal_subghz_set_external_power_disable(true); furi_hal_subghz_disable_ext_power(); diff --git a/applications/main/subghz/views/receiver.c b/applications/main/subghz/views/receiver.c index c327d55b3f..eb7a5a154d 100644 --- a/applications/main/subghz/views/receiver.c +++ b/applications/main/subghz/views/receiver.c @@ -14,8 +14,7 @@ #define SUBGHZ_RAW_THRESHOLD_MIN -90.0f -#define SCROLL_INTERVAL (606) -#define SCROLL_DELAY (2) +#define FLIP_TIMEOUT (500) typedef struct { FuriString* item_str; @@ -55,7 +54,7 @@ struct SubGhzViewReceiver { View* view; SubGhzViewReceiverCallback callback; void* context; - FuriTimer* scroll_timer; + FuriTimer* flip_time_timer; }; typedef struct { @@ -70,7 +69,7 @@ typedef struct { SubGhzViewReceiverBarShow bar_show; SubGhzViewReceiverMode mode; uint8_t u_rssi; - size_t scroll_counter; + bool show_time; bool nodraw; } SubGhzViewReceiverModel; @@ -148,6 +147,24 @@ static void subghz_view_receiver_update_offset(SubGhzViewReceiver* subghz_receiv true); } +static void subghz_view_receiver_show_time_moment(void* context) { + furi_assert(context); + SubGhzViewReceiver* subghz_receiver = context; + with_view_model( + subghz_receiver->view, SubGhzViewReceiverModel * model, { model->show_time = true; }, true); + furi_timer_start(subghz_receiver->flip_time_timer, FLIP_TIMEOUT); +} + +static void subghz_view_receiver_flip_string_callback(void* context) { + furi_assert(context); + SubGhzViewReceiver* subghz_receiver = context; + with_view_model( + subghz_receiver->view, + SubGhzViewReceiverModel * model, + { model->show_time = false; }, + true); +} + void subghz_view_receiver_add_item_to_menu( SubGhzViewReceiver* subghz_receiver, const char* name, @@ -166,6 +183,7 @@ void subghz_view_receiver_add_item_to_menu( if((model->idx == model->history_item - 1)) { model->history_item++; model->idx++; + subghz_view_receiver_show_time_moment(subghz_receiver); } else { model->history_item++; } @@ -255,30 +273,18 @@ void subghz_view_receiver_draw(Canvas* canvas, SubGhzViewReceiverModel* model) { break; } furi_string_set(str_buff, item_menu->item_str); - size_t scroll_counter = model->scroll_counter; if(model->idx == idx) { subghz_view_receiver_draw_frame(canvas, i, scrollbar); - if(scroll_counter < SCROLL_DELAY) { + if(model->show_time) { // Show time of signal one moment furi_string_set(str_buff, item_menu->time); - scroll_counter = 0; - } else { - scroll_counter -= SCROLL_DELAY; } } else { canvas_set_color(canvas, ColorBlack); - scroll_counter = 0; } + elements_string_fit_width(canvas, str_buff, scrollbar ? MAX_LEN_PX - 7 : MAX_LEN_PX); canvas_draw_icon(canvas, 4, 2 + i * FRAME_HEIGHT, ReceiverItemIcons[item_menu->type]); - elements_scrollable_text_line( - canvas, - 15, - 9 + i * FRAME_HEIGHT, - (scrollbar ? MAX_LEN_PX - 6 : MAX_LEN_PX), - str_buff, - scroll_counter, - (model->idx != idx), - false); + canvas_draw_str(canvas, 15, 9 + i * FRAME_HEIGHT, furi_string_get_cstr(str_buff)); furi_string_reset(str_buff); } if(scrollbar) { @@ -385,13 +391,6 @@ void subghz_view_receiver_draw(Canvas* canvas, SubGhzViewReceiverModel* model) { } } -static void subghz_view_receiver_scroll_timer_callback(void* context) { - furi_assert(context); - SubGhzViewReceiver* subghz_receiver = context; - with_view_model( - subghz_receiver->view, SubGhzViewReceiverModel * model, { model->scroll_counter++; }, true); -} - static void subghz_view_receiver_timer_callback(void* context) { furi_assert(context); SubGhzViewReceiver* subghz_receiver = context; @@ -451,7 +450,7 @@ bool subghz_view_receiver_input(InputEvent* event, void* context) { SubGhzViewReceiverModel * model, { if(model->idx != 0) model->idx--; - model->scroll_counter = 0; + subghz_view_receiver_show_time_moment(context); }, true); } else if( @@ -461,9 +460,10 @@ bool subghz_view_receiver_input(InputEvent* event, void* context) { subghz_receiver->view, SubGhzViewReceiverModel * model, { - if((model->history_item != 0) && (model->idx != model->history_item - 1)) + if((model->history_item != 0) && (model->idx != model->history_item - 1)) { model->idx++; - model->scroll_counter = 0; + subghz_view_receiver_show_time_moment(context); + } }, true); } else if(event->key == InputKeyLeft && event->type == InputTypeShort) { @@ -500,13 +500,7 @@ bool subghz_view_receiver_input(InputEvent* event, void* context) { void subghz_view_receiver_enter(void* context) { furi_assert(context); - SubGhzViewReceiver* subghz_receiver = context; - with_view_model( - subghz_receiver->view, - SubGhzViewReceiverModel * model, - { model->scroll_counter = 0; }, - true); - furi_timer_start(subghz_receiver->scroll_timer, SCROLL_INTERVAL); + subghz_view_receiver_show_time_moment(context); } void subghz_view_receiver_exit(void* context) { @@ -534,7 +528,7 @@ void subghz_view_receiver_exit(void* context) { }, false); furi_timer_stop(subghz_receiver->timer); - furi_timer_stop(subghz_receiver->scroll_timer); + furi_timer_stop(subghz_receiver->flip_time_timer); } SubGhzViewReceiver* subghz_view_receiver_alloc() { @@ -553,8 +547,8 @@ SubGhzViewReceiver* subghz_view_receiver_alloc() { view_set_enter_callback(subghz_receiver->view, subghz_view_receiver_enter); view_set_exit_callback(subghz_receiver->view, subghz_view_receiver_exit); - subghz_receiver->scroll_timer = furi_timer_alloc( - subghz_view_receiver_scroll_timer_callback, FuriTimerTypePeriodic, subghz_receiver); + subghz_receiver->flip_time_timer = furi_timer_alloc( + subghz_view_receiver_flip_string_callback, FuriTimerTypeOnce, subghz_receiver); with_view_model( subghz_receiver->view, @@ -578,7 +572,7 @@ SubGhzViewReceiver* subghz_view_receiver_alloc() { void subghz_view_receiver_free(SubGhzViewReceiver* subghz_receiver) { furi_assert(subghz_receiver); - furi_timer_free(subghz_receiver->scroll_timer); + furi_timer_free(subghz_receiver->flip_time_timer); with_view_model( subghz_receiver->view, diff --git a/applications/main/subghz/views/subghz_frequency_analyzer.c b/applications/main/subghz/views/subghz_frequency_analyzer.c index 23c05e4c5f..30545d4b70 100644 --- a/applications/main/subghz/views/subghz_frequency_analyzer.c +++ b/applications/main/subghz/views/subghz_frequency_analyzer.c @@ -24,9 +24,10 @@ static const uint32_t subghz_frequency_list[] = { 300000000, 302757000, 303875000, 304250000, 307000000, 307500000, 307800000, 309000000, 310000000, 312000000, 312100000, 313000000, 313850000, 314000000, 314350000, 314980000, 315000000, 318000000, 330000000, 345000000, 348000000, 350000000, 387000000, 390000000, - 418000000, 433075000, 433220000, 433420000, 433657070, 433889000, 433920000, 434075000, - 434176948, 434390000, 434420000, 434775000, 438900000, 440175000, 464000000, 779000000, - 868350000, 868400000, 868800000, 868950000, 906400000, 915000000, 925000000, 928000000}; + 418000000, 430000000, 431000000, 431500000, 433075000, 433220000, 433420000, 433657070, + 433889000, 433920000, 434075000, 434176948, 434390000, 434420000, 434775000, 438900000, + 440175000, 464000000, 779000000, 868350000, 868400000, 868800000, 868950000, 906400000, + 915000000, 925000000, 928000000}; typedef enum { SubGhzFrequencyAnalyzerStatusIDLE, diff --git a/applications/main/u2f/application.fam b/applications/main/u2f/application.fam index 82010ffb43..6b32e02253 100644 --- a/applications/main/u2f/application.fam +++ b/applications/main/u2f/application.fam @@ -11,5 +11,4 @@ App( stack_size=2 * 1024, icon="A_U2F_14", order=80, - fap_libs=["assets"], ) diff --git a/applications/main/u2f/scenes/u2f_scene_error.c b/applications/main/u2f/scenes/u2f_scene_error.c index 33839a61a1..36e490f1c2 100644 --- a/applications/main/u2f/scenes/u2f_scene_error.c +++ b/applications/main/u2f/scenes/u2f_scene_error.c @@ -25,31 +25,6 @@ void u2f_scene_error_on_enter(void* context) { "No SD card or\napp data found.\nThis app will not\nwork without\nrequired files."); widget_add_button_element( app->widget, GuiButtonTypeLeft, "Back", u2f_scene_error_event_callback, app); - } else if(app->error == U2fAppErrorCloseRpc) { - widget_add_icon_element(app->widget, 78, 0, &I_ActiveConnection_50x64); - if(XTREME_SETTINGS()->is_nsfw) { - widget_add_string_multiline_element( - app->widget, 3, 2, AlignLeft, AlignTop, FontPrimary, "I am not\na whore!"); - widget_add_string_multiline_element( - app->widget, - 3, - 30, - AlignLeft, - AlignTop, - FontSecondary, - "Pull out from\nPC or phone to\nuse me like this."); - } else { - widget_add_string_multiline_element( - app->widget, 3, 2, AlignLeft, AlignTop, FontPrimary, "Connection\nis active!"); - widget_add_string_multiline_element( - app->widget, - 3, - 30, - AlignLeft, - AlignTop, - FontSecondary, - "Disconnect from\nPC or phone to\nuse this function."); - } } view_dispatcher_switch_to_view(app->view_dispatcher, U2fAppViewError); diff --git a/applications/main/u2f/scenes/u2f_scene_main.c b/applications/main/u2f/scenes/u2f_scene_main.c index 251bc4d991..992236e7a8 100644 --- a/applications/main/u2f/scenes/u2f_scene_main.c +++ b/applications/main/u2f/scenes/u2f_scene_main.c @@ -68,7 +68,7 @@ bool u2f_scene_main_on_event(void* context, SceneManagerEvent event) { notification_message(app->notifications, &sequence_blink_magenta_10); } else if(event.event == U2fCustomEventAuthSuccess) { notification_message_block(app->notifications, &sequence_set_green_255); - DOLPHIN_DEED(DolphinDeedU2fAuthorized); + dolphin_deed(DolphinDeedU2fAuthorized); furi_timer_start(app->timer, U2F_SUCCESS_TIMEOUT); app->event_cur = U2fCustomEventNone; u2f_view_set_state(app->u2f_view, U2fMsgSuccess); diff --git a/applications/main/u2f/u2f_app.c b/applications/main/u2f/u2f_app.c index db94a398c3..1c8db40d09 100644 --- a/applications/main/u2f/u2f_app.c +++ b/applications/main/u2f/u2f_app.c @@ -49,16 +49,12 @@ U2fApp* u2f_app_alloc() { view_dispatcher_add_view( app->view_dispatcher, U2fAppViewMain, u2f_view_get_view(app->u2f_view)); - if(furi_hal_usb_is_locked()) { - app->error = U2fAppErrorCloseRpc; - scene_manager_next_scene(app->scene_manager, U2fSceneError); + furi_hal_usb_unlock(); + if(u2f_data_check(true)) { + scene_manager_next_scene(app->scene_manager, U2fSceneMain); } else { - if(u2f_data_check(true)) { - scene_manager_next_scene(app->scene_manager, U2fSceneMain); - } else { - app->error = U2fAppErrorNoFiles; - scene_manager_next_scene(app->scene_manager, U2fSceneError); - } + app->error = U2fAppErrorNoFiles; + scene_manager_next_scene(app->scene_manager, U2fSceneError); } return app; diff --git a/applications/main/u2f/u2f_app_i.h b/applications/main/u2f/u2f_app_i.h index 2896684c37..7a06caf052 100644 --- a/applications/main/u2f/u2f_app_i.h +++ b/applications/main/u2f/u2f_app_i.h @@ -18,7 +18,6 @@ typedef enum { U2fAppErrorNoFiles, - U2fAppErrorCloseRpc, } U2fAppError; typedef enum { diff --git a/applications/main/xtreme_app/application.fam b/applications/main/xtreme_app/application.fam index ca9cc62dc6..854e721993 100644 --- a/applications/main/xtreme_app/application.fam +++ b/applications/main/xtreme_app/application.fam @@ -1,7 +1,7 @@ App( appid="xtreme_app", name="Xtreme", - apptype=FlipperAppType.EXTMAINAPP, + apptype=FlipperAppType.FAPP, entry_point="xtreme_app", cdefines=["APP_XTREME"], requires=[ @@ -11,5 +11,4 @@ App( stack_size=2 * 1024, icon="A_Xtreme_14", order=90, - fap_libs=["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 dfe115522e..77ba439911 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_config.h +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_config.h @@ -5,11 +5,13 @@ ADD_SCENE(xtreme_app, interface_mainmenu, InterfaceMainmenu) ADD_SCENE(xtreme_app, interface_mainmenu_add, InterfaceMainmenuAdd) ADD_SCENE(xtreme_app, interface_lockscreen, InterfaceLockscreen) ADD_SCENE(xtreme_app, interface_statusbar, InterfaceStatusbar) -ADD_SCENE(xtreme_app, interface_common, InterfaceCommon) +ADD_SCENE(xtreme_app, interface_filebrowser, InterfaceFilebrowser) ADD_SCENE(xtreme_app, protocols, Protocols) -ADD_SCENE(xtreme_app, protocols_frequencies, ProtocolsFrequencies) -ADD_SCENE(xtreme_app, protocols_frequencies_static, ProtocolsFrequenciesStatic) -ADD_SCENE(xtreme_app, protocols_frequencies_hopper, ProtocolsFrequenciesHopper) -ADD_SCENE(xtreme_app, protocols_frequencies_add, ProtocolsFrequenciesAdd) +ADD_SCENE(xtreme_app, protocols_freqs, ProtocolsFreqs) +ADD_SCENE(xtreme_app, protocols_freqs_static, ProtocolsFreqsStatic) +ADD_SCENE(xtreme_app, protocols_freqs_hopper, ProtocolsFreqsHopper) +ADD_SCENE(xtreme_app, protocols_freqs_add, ProtocolsFreqsAdd) ADD_SCENE(xtreme_app, misc, Misc) +ADD_SCENE(xtreme_app, misc_screen, MiscScreen) +ADD_SCENE(xtreme_app, misc_dolphin, MiscDolphin) 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 a033e47464..e6b62d0346 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_interface.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_interface.c @@ -5,7 +5,7 @@ enum VarItemListIndex { VarItemListIndexMainmenu, VarItemListIndexLockscreen, VarItemListIndexStatusbar, - VarItemListIndexCommon, + VarItemListIndexFileBrowser, }; void xtreme_app_scene_interface_var_item_list_callback(void* context, uint32_t index) { @@ -16,12 +16,22 @@ void xtreme_app_scene_interface_var_item_list_callback(void* context, uint32_t i void xtreme_app_scene_interface_on_enter(void* context) { XtremeApp* app = context; VariableItemList* var_item_list = app->var_item_list; + VariableItem* item; - variable_item_list_add(var_item_list, "Graphics", 0, NULL, app); - variable_item_list_add(var_item_list, "Mainmenu", 0, NULL, app); - variable_item_list_add(var_item_list, "Lockscreen", 0, NULL, app); - variable_item_list_add(var_item_list, "Statusbar", 0, NULL, app); - variable_item_list_add(var_item_list, "Common", 0, NULL, app); + item = variable_item_list_add(var_item_list, "Graphics", 0, NULL, app); + variable_item_set_current_value_text(item, ">"); + + item = variable_item_list_add(var_item_list, "Mainmenu", 0, NULL, app); + variable_item_set_current_value_text(item, ">"); + + item = variable_item_list_add(var_item_list, "Lockscreen", 0, NULL, app); + variable_item_set_current_value_text(item, ">"); + + item = variable_item_list_add(var_item_list, "Statusbar", 0, NULL, app); + variable_item_set_current_value_text(item, ">"); + + item = variable_item_list_add(var_item_list, "File Browser", 0, NULL, app); + variable_item_set_current_value_text(item, ">"); variable_item_list_set_enter_callback( var_item_list, xtreme_app_scene_interface_var_item_list_callback, app); @@ -52,8 +62,8 @@ bool xtreme_app_scene_interface_on_event(void* context, SceneManagerEvent event) case VarItemListIndexStatusbar: scene_manager_next_scene(app->scene_manager, XtremeAppSceneInterfaceStatusbar); break; - case VarItemListIndexCommon: - scene_manager_next_scene(app->scene_manager, XtremeAppSceneInterfaceCommon); + case VarItemListIndexFileBrowser: + scene_manager_next_scene(app->scene_manager, XtremeAppSceneInterfaceFilebrowser); break; default: break; diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_common.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_filebrowser.c similarity index 60% rename from applications/main/xtreme_app/scenes/xtreme_app_scene_interface_common.c rename to applications/main/xtreme_app/scenes/xtreme_app_scene_interface_filebrowser.c index 54b0241d97..ed19516262 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_common.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_filebrowser.c @@ -2,16 +2,17 @@ enum VarItemListIndex { VarItemListIndexSortDirsFirst, - VarItemListIndexDarkMode, - VarItemListIndexLeftHanded, + VarItemListIndexShowHiddenFiles, + VarItemListIndexShowInternalTab, + VarItemListIndexFavoriteTimeout, }; -void xtreme_app_scene_interface_common_var_item_list_callback(void* context, uint32_t index) { +void xtreme_app_scene_interface_filebrowser_var_item_list_callback(void* context, uint32_t index) { XtremeApp* app = context; view_dispatcher_send_custom_event(app->view_dispatcher, index); } -static void xtreme_app_scene_interface_common_sort_dirs_first_changed(VariableItem* item) { +static void xtreme_app_scene_interface_filebrowser_sort_dirs_first_changed(VariableItem* item) { XtremeApp* app = variable_item_get_context(item); bool value = variable_item_get_current_value_index(item); variable_item_set_current_value_text(item, value ? "ON" : "OFF"); @@ -19,25 +20,23 @@ static void xtreme_app_scene_interface_common_sort_dirs_first_changed(VariableIt app->save_settings = true; } -static void xtreme_app_scene_interface_common_dark_mode_changed(VariableItem* item) { +static void xtreme_app_scene_interface_filebrowser_show_hidden_files_changed(VariableItem* item) { XtremeApp* app = variable_item_get_context(item); bool value = variable_item_get_current_value_index(item); variable_item_set_current_value_text(item, value ? "ON" : "OFF"); - XTREME_SETTINGS()->dark_mode = value; + XTREME_SETTINGS()->show_hidden_files = value; app->save_settings = true; } -static void xtreme_app_scene_interface_common_left_handed_changed(VariableItem* item) { +static void xtreme_app_scene_interface_filebrowser_show_internal_tab_changed(VariableItem* item) { + XtremeApp* app = variable_item_get_context(item); bool value = variable_item_get_current_value_index(item); variable_item_set_current_value_text(item, value ? "ON" : "OFF"); - if(value) { - furi_hal_rtc_set_flag(FuriHalRtcFlagHandOrient); - } else { - furi_hal_rtc_reset_flag(FuriHalRtcFlagHandOrient); - } + XTREME_SETTINGS()->show_internal_tab = value; + app->save_settings = true; } -static void xtreme_app_scene_interface_common_favorite_timeout_changed(VariableItem* item) { +static void xtreme_app_scene_interface_filebrowser_favorite_timeout_changed(VariableItem* item) { XtremeApp* app = variable_item_get_context(item); uint32_t value = variable_item_get_current_value_index(item); char text[6]; @@ -47,7 +46,7 @@ static void xtreme_app_scene_interface_common_favorite_timeout_changed(VariableI app->save_settings = true; } -void xtreme_app_scene_interface_common_on_enter(void* context) { +void xtreme_app_scene_interface_filebrowser_on_enter(void* context) { XtremeApp* app = context; XtremeSettings* xtreme_settings = XTREME_SETTINGS(); VariableItemList* var_item_list = app->var_item_list; @@ -57,31 +56,34 @@ void xtreme_app_scene_interface_common_on_enter(void* context) { var_item_list, "Sort Dirs First", 2, - xtreme_app_scene_interface_common_sort_dirs_first_changed, + xtreme_app_scene_interface_filebrowser_sort_dirs_first_changed, app); variable_item_set_current_value_index(item, xtreme_settings->sort_dirs_first); variable_item_set_current_value_text(item, xtreme_settings->sort_dirs_first ? "ON" : "OFF"); item = variable_item_list_add( - var_item_list, "Dark Mode", 2, xtreme_app_scene_interface_common_dark_mode_changed, app); - variable_item_set_current_value_index(item, xtreme_settings->dark_mode); - variable_item_set_current_value_text(item, xtreme_settings->dark_mode ? "ON" : "OFF"); + var_item_list, + "Show Hidden Files", + 2, + xtreme_app_scene_interface_filebrowser_show_hidden_files_changed, + app); + variable_item_set_current_value_index(item, xtreme_settings->show_hidden_files); + variable_item_set_current_value_text(item, xtreme_settings->show_hidden_files ? "ON" : "OFF"); item = variable_item_list_add( var_item_list, - "Left Handed", + "Show Internal Tab", 2, - xtreme_app_scene_interface_common_left_handed_changed, + xtreme_app_scene_interface_filebrowser_show_internal_tab_changed, app); - bool value = furi_hal_rtc_is_flag_set(FuriHalRtcFlagHandOrient); - variable_item_set_current_value_index(item, value); - variable_item_set_current_value_text(item, value ? "ON" : "OFF"); + variable_item_set_current_value_index(item, xtreme_settings->show_internal_tab); + variable_item_set_current_value_text(item, xtreme_settings->show_internal_tab ? "ON" : "OFF"); item = variable_item_list_add( var_item_list, "Favorite Timeout", 61, - xtreme_app_scene_interface_common_favorite_timeout_changed, + xtreme_app_scene_interface_filebrowser_favorite_timeout_changed, app); variable_item_set_current_value_index(item, xtreme_settings->favorite_timeout); char text[4]; @@ -89,22 +91,22 @@ void xtreme_app_scene_interface_common_on_enter(void* context) { variable_item_set_current_value_text(item, xtreme_settings->favorite_timeout ? text : "OFF"); variable_item_list_set_enter_callback( - var_item_list, xtreme_app_scene_interface_common_var_item_list_callback, app); + var_item_list, xtreme_app_scene_interface_filebrowser_var_item_list_callback, app); variable_item_list_set_selected_item( var_item_list, - scene_manager_get_scene_state(app->scene_manager, XtremeAppSceneInterfaceCommon)); + scene_manager_get_scene_state(app->scene_manager, XtremeAppSceneInterfaceFilebrowser)); view_dispatcher_switch_to_view(app->view_dispatcher, XtremeAppViewVarItemList); } -bool xtreme_app_scene_interface_common_on_event(void* context, SceneManagerEvent event) { +bool xtreme_app_scene_interface_filebrowser_on_event(void* context, SceneManagerEvent event) { XtremeApp* app = context; bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { scene_manager_set_scene_state( - app->scene_manager, XtremeAppSceneInterfaceCommon, event.event); + app->scene_manager, XtremeAppSceneInterfaceFilebrowser, event.event); consumed = true; switch(event.event) { default: @@ -115,7 +117,7 @@ bool xtreme_app_scene_interface_common_on_event(void* context, SceneManagerEvent return consumed; } -void xtreme_app_scene_interface_common_on_exit(void* context) { +void xtreme_app_scene_interface_filebrowser_on_exit(void* context) { XtremeApp* app = context; variable_item_list_reset(app->var_item_list); } 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 7b0d54b39e..5b016815f9 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_graphics.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_graphics.c @@ -5,6 +5,7 @@ enum VarItemListIndex { VarItemListIndexAnimSpeed, VarItemListIndexCycleAnims, VarItemListIndexUnlockAnims, + VarItemListIndexFallbackAnim, }; void xtreme_app_scene_interface_graphics_var_item_list_callback(void* context, uint32_t index) { diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_lockscreen.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_lockscreen.c index 5ae21cb3ff..21a570bfb6 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_lockscreen.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_lockscreen.c @@ -1,8 +1,14 @@ #include "../xtreme_app.h" enum VarItemListIndex { - VarItemListIndexShowClock, + VarItemListIndexLockOnBoot, + VarItemListIndexFormatOn10BadPins, + VarItemListIndexPinUnlockFromApp, + VarItemListIndexShowTime, + VarItemListIndexShowSeconds, VarItemListIndexShowDate, + VarItemListIndexShowStatusbar, + VarItemListIndexUnlockPrompt, }; void xtreme_app_scene_interface_lockscreen_var_item_list_callback(void* context, uint32_t index) { @@ -26,7 +32,15 @@ static void xtreme_app_scene_interface_lockscreen_bad_pins_format_changed(Variab app->save_settings = true; } -static void xtreme_app_scene_interface_lockscreen_show_time_changed(VariableItem* item) { +static void xtreme_app_scene_interface_lockscreen_pin_unlock_from_app_changed(VariableItem* item) { + XtremeApp* app = variable_item_get_context(item); + bool value = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, value ? "ON" : "OFF"); + XTREME_SETTINGS()->pin_unlock_from_app = value; + app->save_settings = true; +} + +static void xtreme_app_scene_interface_lockscreen_lockscreen_time_changed(VariableItem* item) { XtremeApp* app = variable_item_get_context(item); bool value = variable_item_get_current_value_index(item); variable_item_set_current_value_text(item, value ? "ON" : "OFF"); @@ -34,7 +48,7 @@ static void xtreme_app_scene_interface_lockscreen_show_time_changed(VariableItem app->save_settings = true; } -static void xtreme_app_scene_interface_lockscreen_show_seconds_changed(VariableItem* item) { +static void xtreme_app_scene_interface_lockscreen_lockscreen_seconds_changed(VariableItem* item) { XtremeApp* app = variable_item_get_context(item); bool value = variable_item_get_current_value_index(item); variable_item_set_current_value_text(item, value ? "ON" : "OFF"); @@ -42,7 +56,7 @@ static void xtreme_app_scene_interface_lockscreen_show_seconds_changed(VariableI app->save_settings = true; } -static void xtreme_app_scene_interface_lockscreen_show_date_changed(VariableItem* item) { +static void xtreme_app_scene_interface_lockscreen_lockscreen_date_changed(VariableItem* item) { XtremeApp* app = variable_item_get_context(item); bool value = variable_item_get_current_value_index(item); variable_item_set_current_value_text(item, value ? "ON" : "OFF"); @@ -50,7 +64,8 @@ static void xtreme_app_scene_interface_lockscreen_show_date_changed(VariableItem app->save_settings = true; } -static void xtreme_app_scene_interface_lockscreen_show_statusbar_changed(VariableItem* item) { +static void + xtreme_app_scene_interface_lockscreen_lockscreen_statusbar_changed(VariableItem* item) { XtremeApp* app = variable_item_get_context(item); bool value = variable_item_get_current_value_index(item); variable_item_set_current_value_text(item, value ? "ON" : "OFF"); @@ -58,7 +73,7 @@ static void xtreme_app_scene_interface_lockscreen_show_statusbar_changed(Variabl app->save_settings = true; } -static void xtreme_app_scene_interface_lockscreen_unlock_prompt_changed(VariableItem* item) { +static void xtreme_app_scene_interface_lockscreen_lockscreen_prompt_changed(VariableItem* item) { XtremeApp* app = variable_item_get_context(item); bool value = variable_item_get_current_value_index(item); variable_item_set_current_value_text(item, value ? "ON" : "OFF"); @@ -83,18 +98,28 @@ void xtreme_app_scene_interface_lockscreen_on_enter(void* context) { item = variable_item_list_add( var_item_list, - "Format on 10 bad PINs", + "Format on 10 Bad PINs", 2, xtreme_app_scene_interface_lockscreen_bad_pins_format_changed, app); variable_item_set_current_value_index(item, xtreme_settings->bad_pins_format); variable_item_set_current_value_text(item, xtreme_settings->bad_pins_format ? "ON" : "OFF"); + item = variable_item_list_add( + var_item_list, + "PIN Unlock From App", + 2, + xtreme_app_scene_interface_lockscreen_pin_unlock_from_app_changed, + app); + variable_item_set_current_value_index(item, xtreme_settings->pin_unlock_from_app); + variable_item_set_current_value_text( + item, xtreme_settings->pin_unlock_from_app ? "ON" : "OFF"); + item = variable_item_list_add( var_item_list, "Show Time", 2, - xtreme_app_scene_interface_lockscreen_show_time_changed, + xtreme_app_scene_interface_lockscreen_lockscreen_time_changed, app); variable_item_set_current_value_index(item, xtreme_settings->lockscreen_time); variable_item_set_current_value_text(item, xtreme_settings->lockscreen_time ? "ON" : "OFF"); @@ -103,7 +128,7 @@ void xtreme_app_scene_interface_lockscreen_on_enter(void* context) { var_item_list, "Show Seconds", 2, - xtreme_app_scene_interface_lockscreen_show_seconds_changed, + xtreme_app_scene_interface_lockscreen_lockscreen_seconds_changed, app); variable_item_set_current_value_index(item, xtreme_settings->lockscreen_seconds); variable_item_set_current_value_text(item, xtreme_settings->lockscreen_seconds ? "ON" : "OFF"); @@ -112,7 +137,7 @@ void xtreme_app_scene_interface_lockscreen_on_enter(void* context) { var_item_list, "Show Date", 2, - xtreme_app_scene_interface_lockscreen_show_date_changed, + xtreme_app_scene_interface_lockscreen_lockscreen_date_changed, app); variable_item_set_current_value_index(item, xtreme_settings->lockscreen_date); variable_item_set_current_value_text(item, xtreme_settings->lockscreen_date ? "ON" : "OFF"); @@ -121,7 +146,7 @@ void xtreme_app_scene_interface_lockscreen_on_enter(void* context) { var_item_list, "Show Statusbar", 2, - xtreme_app_scene_interface_lockscreen_show_statusbar_changed, + xtreme_app_scene_interface_lockscreen_lockscreen_statusbar_changed, app); variable_item_set_current_value_index(item, xtreme_settings->lockscreen_statusbar); variable_item_set_current_value_text( @@ -131,7 +156,7 @@ void xtreme_app_scene_interface_lockscreen_on_enter(void* context) { var_item_list, "Unlock Prompt", 2, - xtreme_app_scene_interface_lockscreen_unlock_prompt_changed, + xtreme_app_scene_interface_lockscreen_lockscreen_prompt_changed, app); variable_item_set_current_value_index(item, xtreme_settings->lockscreen_prompt); variable_item_set_current_value_text(item, xtreme_settings->lockscreen_prompt ? "ON" : "OFF"); diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_mainmenu.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_mainmenu.c index 8d4a6185e4..4a5b5779d4 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_mainmenu.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_mainmenu.c @@ -1,7 +1,7 @@ #include "../xtreme_app.h" enum VarItemListIndex { - VarItemListIndexWiiMenu, + VarItemListIndexMenuStyle, VarItemListIndexApp, VarItemListIndexRemoveApp, VarItemListIndexAddApp, diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_mainmenu_add.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_mainmenu_add.c index 57fa780905..dd93360228 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_mainmenu_add.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_mainmenu_add.c @@ -11,7 +11,7 @@ static bool xtreme_app_scene_interface_mainmenu_add_file_browser_callback( FuriString* item_name) { UNUSED(context); Storage* storage = furi_record_open(RECORD_STORAGE); - bool success = fap_loader_load_name_and_icon(file_path, storage, icon_ptr, item_name); + bool success = flipper_application_load_name_and_icon(file_path, storage, icon_ptr, item_name); furi_record_close(RECORD_STORAGE); return success; } @@ -32,7 +32,7 @@ void xtreme_app_scene_interface_mainmenu_add_on_enter(void* context) { if(dialog_file_browser_show(app->dialogs, string, string, &browser_options)) { CharList_push_back(app->mainmenu_app_paths, strdup(furi_string_get_cstr(string))); Storage* storage = furi_record_open(RECORD_STORAGE); - fap_loader_load_name_and_icon(string, storage, NULL, string); + flipper_application_load_name_and_icon(string, storage, NULL, string); furi_record_close(RECORD_STORAGE); CharList_push_back(app->mainmenu_app_names, strdup(furi_string_get_cstr(string))); app->save_mainmenu_apps = true; diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_statusbar.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_statusbar.c index 1a3eb36b17..50b3c14ecd 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_statusbar.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_statusbar.c @@ -2,6 +2,7 @@ enum VarItemListIndex { VarItemListIndexBatteryIcon, + VarItemListIndexShowClock, VarItemListIndexStatusIcons, VarItemListIndexBarBorders, VarItemListIndexBarBackground, @@ -20,6 +21,16 @@ static void xtreme_app_scene_interface_statusbar_battery_icon_changed(VariableIt variable_item_set_current_value_text(item, battery_icon_names[index]); XTREME_SETTINGS()->battery_icon = index; app->save_settings = true; + power_set_battery_icon_enabled(furi_record_open(RECORD_POWER), index != BatteryIconOff); + furi_record_close(RECORD_POWER); +} + +static void xtreme_app_scene_interface_statusbar_statusbar_clock_changed(VariableItem* item) { + XtremeApp* app = variable_item_get_context(item); + bool value = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, value ? "ON" : "OFF"); + XTREME_SETTINGS()->statusbar_clock = value; + app->save_settings = true; } static void xtreme_app_scene_interface_statusbar_status_icons_changed(VariableItem* item) { @@ -61,6 +72,15 @@ void xtreme_app_scene_interface_statusbar_on_enter(void* context) { variable_item_set_current_value_index(item, xtreme_settings->battery_icon); variable_item_set_current_value_text(item, battery_icon_names[xtreme_settings->battery_icon]); + item = variable_item_list_add( + var_item_list, + "Show Clock", + 2, + xtreme_app_scene_interface_statusbar_statusbar_clock_changed, + app); + variable_item_set_current_value_index(item, xtreme_settings->statusbar_clock); + variable_item_set_current_value_text(item, xtreme_settings->statusbar_clock ? "ON" : "OFF"); + item = variable_item_list_add( var_item_list, "Status Icons", diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_misc.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_misc.c index 84bb378869..1e5e61fc05 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_misc.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_misc.c @@ -1,13 +1,10 @@ #include "../xtreme_app.h" enum VarItemListIndex { + VarItemListIndexScreen, + VarItemListIndexDolphin, VarItemListIndexChangeDeviceName, - VarItemListIndexDolphinLevel, - VarItemListIndexDolphinAngry, - VarItemListIndexButthurtTimer, VarItemListIndexChargeCap, - VarItemListIndexRgbBacklight, - VarItemListIndexLcdColor, }; void xtreme_app_scene_misc_var_item_list_callback(void* context, uint32_t index) { @@ -15,37 +12,6 @@ void xtreme_app_scene_misc_var_item_list_callback(void* context, uint32_t index) view_dispatcher_send_custom_event(app->view_dispatcher, index); } -static void xtreme_app_scene_misc_dolphin_level_changed(VariableItem* item) { - XtremeApp* app = variable_item_get_context(item); - app->dolphin_level = variable_item_get_current_value_index(item) + 1; - char level_str[4]; - snprintf(level_str, 4, "%li", app->dolphin_level); - variable_item_set_current_value_text(item, level_str); - app->save_level = true; -} - -static void xtreme_app_scene_misc_dolphin_angry_changed(VariableItem* item) { - XtremeApp* app = variable_item_get_context(item); - app->dolphin_angry = variable_item_get_current_value_index(item); - char angry_str[4]; - snprintf(angry_str, 4, "%li", app->dolphin_angry); - variable_item_set_current_value_text(item, angry_str); - app->save_angry = true; -} - -const char* const butthurt_timer_names[] = - {"OFF", "30 M", "1 H", "2 H", "4 H", "6 H", "8 H", "12 H", "24 H", "48 H"}; -const uint32_t butthurt_timer_values[COUNT_OF(butthurt_timer_names)] = - {0, 1800, 3600, 7200, 14400, 21600, 28800, 43200, 86400, 172800}; -static void xtreme_app_scene_misc_butthurt_timer_changed(VariableItem* item) { - XtremeApp* app = variable_item_get_context(item); - uint8_t index = variable_item_get_current_value_index(item); - variable_item_set_current_value_text(item, butthurt_timer_names[index]); - XTREME_SETTINGS()->butthurt_timer = butthurt_timer_values[index]; - app->save_settings = true; - app->require_reboot = true; -} - #define CHARGE_CAP_INTV 5 static void xtreme_app_scene_misc_charge_cap_changed(VariableItem* item) { XtremeApp* app = variable_item_get_context(item); @@ -57,15 +23,6 @@ static void xtreme_app_scene_misc_charge_cap_changed(VariableItem* item) { app->save_settings = true; } -static void xtreme_app_scene_misc_lcd_color_changed(VariableItem* item) { - XtremeApp* app = variable_item_get_context(item); - uint8_t index = variable_item_get_current_value_index(item); - variable_item_set_current_value_text(item, rgb_backlight_get_color_text(index)); - rgb_backlight_set_color(index); - app->save_backlight = true; - notification_message(app->notification, &sequence_display_backlight_on); -} - void xtreme_app_scene_misc_on_enter(void* context) { XtremeApp* app = context; XtremeSettings* xtreme_settings = XTREME_SETTINGS(); @@ -73,40 +30,13 @@ void xtreme_app_scene_misc_on_enter(void* context) { VariableItem* item; uint8_t value_index; - variable_item_list_add(var_item_list, "Change Device Name", 0, NULL, app); - - char level_str[4]; - snprintf(level_str, 4, "%li", app->dolphin_level); - item = variable_item_list_add( - var_item_list, - "Dolphin Level", - DOLPHIN_LEVEL_COUNT + 1, - xtreme_app_scene_misc_dolphin_level_changed, - app); - variable_item_set_current_value_index(item, app->dolphin_level - 1); - variable_item_set_current_value_text(item, level_str); + item = variable_item_list_add(var_item_list, "Screen", 0, NULL, app); + variable_item_set_current_value_text(item, ">"); - char angry_str[4]; - snprintf(angry_str, 4, "%li", app->dolphin_angry); - item = variable_item_list_add( - var_item_list, - "Dolphin Angry", - BUTTHURT_MAX + 1, - xtreme_app_scene_misc_dolphin_angry_changed, - app); - variable_item_set_current_value_index(item, app->dolphin_angry); - variable_item_set_current_value_text(item, angry_str); + item = variable_item_list_add(var_item_list, "Dolphin", 0, NULL, app); + variable_item_set_current_value_text(item, ">"); - item = variable_item_list_add( - var_item_list, - "Butthurt Timer", - COUNT_OF(butthurt_timer_names), - xtreme_app_scene_misc_butthurt_timer_changed, - app); - value_index = value_index_uint32( - xtreme_settings->butthurt_timer, butthurt_timer_values, COUNT_OF(butthurt_timer_values)); - variable_item_set_current_value_index(item, value_index); - variable_item_set_current_value_text(item, butthurt_timer_names[value_index]); + variable_item_list_add(var_item_list, "Change Device Name", 0, NULL, app); char cap_str[6]; value_index = xtreme_settings->charge_cap / CHARGE_CAP_INTV; @@ -120,20 +50,6 @@ void xtreme_app_scene_misc_on_enter(void* context) { variable_item_set_current_value_index(item, value_index - 1); variable_item_set_current_value_text(item, cap_str); - item = variable_item_list_add(var_item_list, "RGB Backlight", 1, NULL, app); - variable_item_set_current_value_text(item, xtreme_settings->rgb_backlight ? "ON" : "OFF"); - - item = variable_item_list_add( - var_item_list, - "LCD Color", - rgb_backlight_get_color_count(), - xtreme_app_scene_misc_lcd_color_changed, - app); - value_index = rgb_backlight_get_settings()->display_color_index; - variable_item_set_current_value_index(item, value_index); - variable_item_set_current_value_text(item, rgb_backlight_get_color_text(value_index)); - variable_item_set_locked(item, !xtreme_settings->rgb_backlight, "Needs RGB\nBacklight!"); - variable_item_list_set_enter_callback( var_item_list, xtreme_app_scene_misc_var_item_list_callback, app); @@ -151,36 +67,15 @@ bool xtreme_app_scene_misc_on_event(void* context, SceneManagerEvent event) { scene_manager_set_scene_state(app->scene_manager, XtremeAppSceneMisc, event.event); consumed = true; switch(event.event) { + case VarItemListIndexScreen: + scene_manager_next_scene(app->scene_manager, XtremeAppSceneMiscScreen); + break; + case VarItemListIndexDolphin: + scene_manager_next_scene(app->scene_manager, XtremeAppSceneMiscDolphin); + break; case VarItemListIndexChangeDeviceName: scene_manager_next_scene(app->scene_manager, XtremeAppSceneMiscRename); break; - case VarItemListIndexRgbBacklight: { - bool change = XTREME_SETTINGS()->rgb_backlight; - if(!change) { - DialogMessage* msg = dialog_message_alloc(); - dialog_message_set_header(msg, "RGB Backlight", 64, 0, AlignCenter, AlignTop); - dialog_message_set_buttons(msg, "No", NULL, "Yes"); - dialog_message_set_text( - msg, - "This option requires installing\na hardware modification!\nIs it installed?", - 64, - 32, - AlignCenter, - AlignCenter); - if(dialog_message_show(app->dialogs, msg) == DialogMessageButtonRight) { - change = true; - } - dialog_message_free(msg); - } - if(change) { - XTREME_SETTINGS()->rgb_backlight = !XTREME_SETTINGS()->rgb_backlight; - app->save_settings = true; - notification_message(app->notification, &sequence_display_backlight_on); - scene_manager_previous_scene(app->scene_manager); - scene_manager_next_scene(app->scene_manager, XtremeAppSceneMisc); - } - break; - } default: break; } diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_misc_dolphin.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_misc_dolphin.c new file mode 100644 index 0000000000..e6425d20bd --- /dev/null +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_misc_dolphin.c @@ -0,0 +1,114 @@ +#include "../xtreme_app.h" + +enum VarItemListIndex { + VarItemListIndexDolphinLevel, + VarItemListIndexDolphinAngry, + VarItemListIndexButthurtTimer, +}; + +void xtreme_app_scene_misc_dolphin_var_item_list_callback(void* context, uint32_t index) { + XtremeApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +static void xtreme_app_scene_misc_dolphin_dolphin_level_changed(VariableItem* item) { + XtremeApp* app = variable_item_get_context(item); + app->dolphin_level = variable_item_get_current_value_index(item) + 1; + char level_str[4]; + snprintf(level_str, 4, "%li", app->dolphin_level); + variable_item_set_current_value_text(item, level_str); + app->save_level = true; +} + +static void xtreme_app_scene_misc_dolphin_dolphin_angry_changed(VariableItem* item) { + XtremeApp* app = variable_item_get_context(item); + app->dolphin_angry = variable_item_get_current_value_index(item); + char angry_str[4]; + snprintf(angry_str, 4, "%li", app->dolphin_angry); + variable_item_set_current_value_text(item, angry_str); + app->save_angry = true; +} + +const char* const butthurt_timer_names[] = + {"OFF", "30 M", "1 H", "2 H", "4 H", "6 H", "8 H", "12 H", "24 H", "48 H"}; +const uint32_t butthurt_timer_values[COUNT_OF(butthurt_timer_names)] = + {0, 1800, 3600, 7200, 14400, 21600, 28800, 43200, 86400, 172800}; +static void xtreme_app_scene_misc_dolphin_butthurt_timer_changed(VariableItem* item) { + XtremeApp* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, butthurt_timer_names[index]); + XTREME_SETTINGS()->butthurt_timer = butthurt_timer_values[index]; + app->save_settings = true; + app->require_reboot = true; +} + +void xtreme_app_scene_misc_dolphin_on_enter(void* context) { + XtremeApp* app = context; + XtremeSettings* xtreme_settings = XTREME_SETTINGS(); + VariableItemList* var_item_list = app->var_item_list; + VariableItem* item; + uint8_t value_index; + + char level_str[4]; + snprintf(level_str, 4, "%li", app->dolphin_level); + item = variable_item_list_add( + var_item_list, + "Dolphin Level", + DOLPHIN_LEVEL_COUNT + 1, + xtreme_app_scene_misc_dolphin_dolphin_level_changed, + app); + variable_item_set_current_value_index(item, app->dolphin_level - 1); + variable_item_set_current_value_text(item, level_str); + + char angry_str[4]; + snprintf(angry_str, 4, "%li", app->dolphin_angry); + item = variable_item_list_add( + var_item_list, + "Dolphin Angry", + BUTTHURT_MAX + 1, + xtreme_app_scene_misc_dolphin_dolphin_angry_changed, + app); + variable_item_set_current_value_index(item, app->dolphin_angry); + variable_item_set_current_value_text(item, angry_str); + + item = variable_item_list_add( + var_item_list, + "Butthurt Timer", + COUNT_OF(butthurt_timer_names), + xtreme_app_scene_misc_dolphin_butthurt_timer_changed, + app); + value_index = value_index_uint32( + xtreme_settings->butthurt_timer, butthurt_timer_values, COUNT_OF(butthurt_timer_values)); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, butthurt_timer_names[value_index]); + + variable_item_list_set_enter_callback( + var_item_list, xtreme_app_scene_misc_dolphin_var_item_list_callback, app); + + variable_item_list_set_selected_item( + var_item_list, + scene_manager_get_scene_state(app->scene_manager, XtremeAppSceneMiscDolphin)); + + view_dispatcher_switch_to_view(app->view_dispatcher, XtremeAppViewVarItemList); +} + +bool xtreme_app_scene_misc_dolphin_on_event(void* context, SceneManagerEvent event) { + XtremeApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + scene_manager_set_scene_state(app->scene_manager, XtremeAppSceneMiscDolphin, event.event); + consumed = true; + switch(event.event) { + default: + break; + } + } + + return consumed; +} + +void xtreme_app_scene_misc_dolphin_on_exit(void* context) { + XtremeApp* app = context; + variable_item_list_reset(app->var_item_list); +} diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_misc_screen.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_misc_screen.c new file mode 100644 index 0000000000..b86326aae5 --- /dev/null +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_misc_screen.c @@ -0,0 +1,130 @@ +#include "../xtreme_app.h" + +enum VarItemListIndex { + VarItemListIndexDarkMode, + VarItemListIndexLeftHanded, + VarItemListIndexRgbBacklight, + VarItemListIndexLcdColor, +}; + +void xtreme_app_scene_misc_screen_var_item_list_callback(void* context, uint32_t index) { + XtremeApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +static void xtreme_app_scene_misc_screen_dark_mode_changed(VariableItem* item) { + XtremeApp* app = variable_item_get_context(item); + bool value = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, value ? "ON" : "OFF"); + XTREME_SETTINGS()->dark_mode = value; + app->save_settings = true; +} + +static void xtreme_app_scene_misc_screen_hand_orient_changed(VariableItem* item) { + bool value = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, value ? "ON" : "OFF"); + if(value) { + furi_hal_rtc_set_flag(FuriHalRtcFlagHandOrient); + } else { + furi_hal_rtc_reset_flag(FuriHalRtcFlagHandOrient); + } +} + +static void xtreme_app_scene_misc_screen_lcd_color_changed(VariableItem* item) { + XtremeApp* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, rgb_backlight_get_color_text(index)); + rgb_backlight_set_color(index); + app->save_backlight = true; + notification_message(app->notification, &sequence_display_backlight_on); +} + +void xtreme_app_scene_misc_screen_on_enter(void* context) { + XtremeApp* app = context; + XtremeSettings* xtreme_settings = XTREME_SETTINGS(); + VariableItemList* var_item_list = app->var_item_list; + VariableItem* item; + uint8_t value_index; + + item = variable_item_list_add( + var_item_list, "Dark Mode", 2, xtreme_app_scene_misc_screen_dark_mode_changed, app); + variable_item_set_current_value_index(item, xtreme_settings->dark_mode); + variable_item_set_current_value_text(item, xtreme_settings->dark_mode ? "ON" : "OFF"); + + item = variable_item_list_add( + var_item_list, "Left Handed", 2, xtreme_app_scene_misc_screen_hand_orient_changed, app); + bool value = furi_hal_rtc_is_flag_set(FuriHalRtcFlagHandOrient); + variable_item_set_current_value_index(item, value); + variable_item_set_current_value_text(item, value ? "ON" : "OFF"); + + item = variable_item_list_add(var_item_list, "RGB Backlight", 1, NULL, app); + variable_item_set_current_value_text(item, xtreme_settings->rgb_backlight ? "ON" : "OFF"); + + item = variable_item_list_add( + var_item_list, + "LCD Color", + rgb_backlight_get_color_count(), + xtreme_app_scene_misc_screen_lcd_color_changed, + app); + value_index = rgb_backlight_get_settings()->display_color_index; + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, rgb_backlight_get_color_text(value_index)); + variable_item_set_locked(item, !xtreme_settings->rgb_backlight, "Needs RGB\nBacklight!"); + + variable_item_list_set_enter_callback( + var_item_list, xtreme_app_scene_misc_screen_var_item_list_callback, app); + + variable_item_list_set_selected_item( + var_item_list, + scene_manager_get_scene_state(app->scene_manager, XtremeAppSceneMiscScreen)); + + view_dispatcher_switch_to_view(app->view_dispatcher, XtremeAppViewVarItemList); +} + +bool xtreme_app_scene_misc_screen_on_event(void* context, SceneManagerEvent event) { + XtremeApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + scene_manager_set_scene_state(app->scene_manager, XtremeAppSceneMiscScreen, event.event); + consumed = true; + switch(event.event) { + case VarItemListIndexRgbBacklight: { + bool change = XTREME_SETTINGS()->rgb_backlight; + if(!change) { + DialogMessage* msg = dialog_message_alloc(); + dialog_message_set_header(msg, "RGB Backlight", 64, 0, AlignCenter, AlignTop); + dialog_message_set_buttons(msg, "No", NULL, "Yes"); + dialog_message_set_text( + msg, + "This option requires installing\na hardware modification!\nIs it installed?", + 64, + 32, + AlignCenter, + AlignCenter); + if(dialog_message_show(app->dialogs, msg) == DialogMessageButtonRight) { + change = true; + } + dialog_message_free(msg); + } + if(change) { + XTREME_SETTINGS()->rgb_backlight = !XTREME_SETTINGS()->rgb_backlight; + app->save_settings = true; + notification_message(app->notification, &sequence_display_backlight_on); + scene_manager_previous_scene(app->scene_manager); + scene_manager_next_scene(app->scene_manager, XtremeAppSceneMiscScreen); + } + break; + } + default: + break; + } + } + + return consumed; +} + +void xtreme_app_scene_misc_screen_on_exit(void* context) { + XtremeApp* app = context; + variable_item_list_reset(app->var_item_list); +} 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 28b97618e0..67c80b14eb 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols.c @@ -3,7 +3,7 @@ enum VarItemListIndex { VarItemListIndexBadkbMode, VarItemListIndexBadbtRemember, - VarItemListIndexSubghzFrequencies, + VarItemListIndexSubghzFreqs, VarItemListIndexSubghzExtend, }; @@ -12,7 +12,7 @@ void xtreme_app_scene_protocols_var_item_list_callback(void* context, uint32_t i view_dispatcher_send_custom_event(app->view_dispatcher, index); } -static void xtreme_app_scene_protocols_badkb_mode_changed(VariableItem* item) { +static void xtreme_app_scene_protocols_bad_bt_changed(VariableItem* item) { XtremeApp* app = variable_item_get_context(item); bool value = variable_item_get_current_value_index(item); variable_item_set_current_value_text(item, value ? "BT" : "USB"); @@ -20,7 +20,7 @@ static void xtreme_app_scene_protocols_badkb_mode_changed(VariableItem* item) { app->save_settings = true; } -static void xtreme_app_scene_protocols_badbt_remember_changed(VariableItem* item) { +static void xtreme_app_scene_protocols_bad_bt_remember_changed(VariableItem* item) { XtremeApp* app = variable_item_get_context(item); bool value = variable_item_get_current_value_index(item); variable_item_set_current_value_text(item, value ? "ON" : "OFF"); @@ -42,16 +42,21 @@ void xtreme_app_scene_protocols_on_enter(void* context) { VariableItem* item; item = variable_item_list_add( - var_item_list, "BadKB Mode", 2, xtreme_app_scene_protocols_badkb_mode_changed, app); + var_item_list, "BadKB Mode", 2, xtreme_app_scene_protocols_bad_bt_changed, app); variable_item_set_current_value_index(item, xtreme_settings->bad_bt); variable_item_set_current_value_text(item, xtreme_settings->bad_bt ? "BT" : "USB"); item = variable_item_list_add( - var_item_list, "BadBT Remember", 2, xtreme_app_scene_protocols_badbt_remember_changed, app); + var_item_list, + "BadBT Remember", + 2, + xtreme_app_scene_protocols_bad_bt_remember_changed, + app); variable_item_set_current_value_index(item, xtreme_settings->bad_bt_remember); variable_item_set_current_value_text(item, xtreme_settings->bad_bt_remember ? "ON" : "OFF"); - variable_item_list_add(var_item_list, "SubGHz Frequencies", 0, NULL, app); + item = variable_item_list_add(var_item_list, "SubGHz Freqs", 0, NULL, app); + variable_item_set_current_value_text(item, ">"); item = variable_item_list_add( var_item_list, "SubGHz Extend", 2, xtreme_app_scene_protocols_subghz_extend_changed, app); @@ -75,8 +80,8 @@ bool xtreme_app_scene_protocols_on_event(void* context, SceneManagerEvent event) scene_manager_set_scene_state(app->scene_manager, XtremeAppSceneProtocols, event.event); consumed = true; switch(event.event) { - case VarItemListIndexSubghzFrequencies: - scene_manager_next_scene(app->scene_manager, XtremeAppSceneProtocolsFrequencies); + case VarItemListIndexSubghzFreqs: + scene_manager_next_scene(app->scene_manager, XtremeAppSceneProtocolsFreqs); break; default: break; diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_freqs.c similarity index 57% rename from applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies.c rename to applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_freqs.c index 6b92d1eb01..f8c779cc58 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_freqs.c @@ -2,24 +2,24 @@ enum VarItemListIndex { VarItemListIndexUseDefaults, - VarItemListIndexStaticFrequencies, - VarItemListIndexHopperFrequencies, + VarItemListIndexStaticFreqs, + VarItemListIndexHopperFreqs, }; -void xtreme_app_scene_protocols_frequencies_var_item_list_callback(void* context, uint32_t index) { +void xtreme_app_scene_protocols_freqs_var_item_list_callback(void* context, uint32_t index) { XtremeApp* app = context; view_dispatcher_send_custom_event(app->view_dispatcher, index); } -static void xtreme_app_scene_protocols_frequencies_use_defaults_changed(VariableItem* item) { +static void xtreme_app_scene_protocols_freqs_use_defaults_changed(VariableItem* item) { XtremeApp* app = variable_item_get_context(item); bool value = variable_item_get_current_value_index(item); variable_item_set_current_value_text(item, value ? "ON" : "OFF"); app->subghz_use_defaults = value; - app->save_subghz_frequencies = true; + app->save_subghz_freqs = true; } -void xtreme_app_scene_protocols_frequencies_on_enter(void* context) { +void xtreme_app_scene_protocols_freqs_on_enter(void* context) { XtremeApp* app = context; VariableItemList* var_item_list = app->var_item_list; VariableItem* item; @@ -28,39 +28,41 @@ void xtreme_app_scene_protocols_frequencies_on_enter(void* context) { var_item_list, "Use Defaults", 2, - xtreme_app_scene_protocols_frequencies_use_defaults_changed, + xtreme_app_scene_protocols_freqs_use_defaults_changed, app); variable_item_set_current_value_index(item, app->subghz_use_defaults); variable_item_set_current_value_text(item, app->subghz_use_defaults ? "ON" : "OFF"); - variable_item_list_add(var_item_list, "Static Frequencies", 0, NULL, app); + item = variable_item_list_add(var_item_list, "Static Freqs", 0, NULL, app); + variable_item_set_current_value_text(item, ">"); - variable_item_list_add(var_item_list, "Hopper Frequencies", 0, NULL, app); + item = variable_item_list_add(var_item_list, "Hopper Freqs", 0, NULL, app); + variable_item_set_current_value_text(item, ">"); variable_item_list_set_enter_callback( - var_item_list, xtreme_app_scene_protocols_frequencies_var_item_list_callback, app); + var_item_list, xtreme_app_scene_protocols_freqs_var_item_list_callback, app); variable_item_list_set_selected_item( var_item_list, - scene_manager_get_scene_state(app->scene_manager, XtremeAppSceneProtocolsFrequencies)); + scene_manager_get_scene_state(app->scene_manager, XtremeAppSceneProtocolsFreqs)); view_dispatcher_switch_to_view(app->view_dispatcher, XtremeAppViewVarItemList); } -bool xtreme_app_scene_protocols_frequencies_on_event(void* context, SceneManagerEvent event) { +bool xtreme_app_scene_protocols_freqs_on_event(void* context, SceneManagerEvent event) { XtremeApp* app = context; bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { scene_manager_set_scene_state( - app->scene_manager, XtremeAppSceneProtocolsFrequencies, event.event); + app->scene_manager, XtremeAppSceneProtocolsFreqs, event.event); consumed = true; switch(event.event) { - case VarItemListIndexStaticFrequencies: - scene_manager_next_scene(app->scene_manager, XtremeAppSceneProtocolsFrequenciesStatic); + case VarItemListIndexStaticFreqs: + scene_manager_next_scene(app->scene_manager, XtremeAppSceneProtocolsFreqsStatic); break; - case VarItemListIndexHopperFrequencies: - scene_manager_next_scene(app->scene_manager, XtremeAppSceneProtocolsFrequenciesHopper); + case VarItemListIndexHopperFreqs: + scene_manager_next_scene(app->scene_manager, XtremeAppSceneProtocolsFreqsHopper); break; default: break; @@ -70,7 +72,7 @@ bool xtreme_app_scene_protocols_frequencies_on_event(void* context, SceneManager return consumed; } -void xtreme_app_scene_protocols_frequencies_on_exit(void* context) { +void xtreme_app_scene_protocols_freqs_on_exit(void* context) { XtremeApp* app = context; variable_item_list_reset(app->var_item_list); } diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies_add.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_freqs_add.c similarity index 80% rename from applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies_add.c rename to applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_freqs_add.c index 88e4211475..314a69e1e7 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies_add.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_freqs_add.c @@ -5,37 +5,37 @@ enum TextInputResult { TextInputResultError, }; -static void xtreme_app_scene_protocols_frequencies_add_text_input_callback(void* context) { +static void xtreme_app_scene_protocols_freqs_add_text_input_callback(void* context) { XtremeApp* app = context; char* end; - uint32_t value = strtol(app->subghz_freq_buffer, &end, 0) * 10000; + uint32_t value = strtol(app->subghz_freq_buffer, &end, 0) * 1000; if(*end || !furi_hal_subghz_is_frequency_valid(value)) { view_dispatcher_send_custom_event(app->view_dispatcher, TextInputResultError); return; } bool is_hopper = - scene_manager_get_scene_state(app->scene_manager, XtremeAppSceneProtocolsFrequenciesAdd); + scene_manager_get_scene_state(app->scene_manager, XtremeAppSceneProtocolsFreqsAdd); if(is_hopper) { FrequencyList_push_back(app->subghz_hopper_freqs, value); } else { FrequencyList_push_back(app->subghz_static_freqs, value); } - app->save_subghz_frequencies = true; + app->save_subghz_freqs = true; view_dispatcher_send_custom_event(app->view_dispatcher, TextInputResultOk); } -void xtreme_app_scene_protocols_frequencies_add_on_enter(void* context) { +void xtreme_app_scene_protocols_freqs_add_on_enter(void* context) { XtremeApp* app = context; TextInput* text_input = app->text_input; - text_input_set_header_text(text_input, "Format: 12356"); + text_input_set_header_text(text_input, "Ex: 123456 for 123.456 MHz"); strlcpy(app->subghz_freq_buffer, "", XTREME_SUBGHZ_FREQ_BUFFER_SIZE); text_input_set_result_callback( text_input, - xtreme_app_scene_protocols_frequencies_add_text_input_callback, + xtreme_app_scene_protocols_freqs_add_text_input_callback, app, app->subghz_freq_buffer, XTREME_SUBGHZ_FREQ_BUFFER_SIZE, @@ -49,7 +49,7 @@ void callback_return(void* context) { scene_manager_previous_scene(app->scene_manager); } -bool xtreme_app_scene_protocols_frequencies_add_on_event(void* context, SceneManagerEvent event) { +bool xtreme_app_scene_protocols_freqs_add_on_event(void* context, SceneManagerEvent event) { XtremeApp* app = context; bool consumed = false; @@ -77,7 +77,7 @@ bool xtreme_app_scene_protocols_frequencies_add_on_event(void* context, SceneMan return consumed; } -void xtreme_app_scene_protocols_frequencies_add_on_exit(void* context) { +void xtreme_app_scene_protocols_freqs_add_on_exit(void* context) { XtremeApp* app = context; text_input_reset(app->text_input); } diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies_hopper.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_freqs_hopper.c similarity index 74% rename from applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies_hopper.c rename to applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_freqs_hopper.c index b9e387252a..13fdc0ac22 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies_hopper.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_freqs_hopper.c @@ -6,14 +6,12 @@ enum VarItemListIndex { VarItemListIndexAddHopperFreq, }; -void xtreme_app_scene_protocols_frequencies_hopper_var_item_list_callback( - void* context, - uint32_t index) { +void xtreme_app_scene_protocols_freqs_hopper_var_item_list_callback(void* context, uint32_t index) { XtremeApp* app = context; view_dispatcher_send_custom_event(app->view_dispatcher, index); } -static void xtreme_app_scene_protocols_frequencies_hopper_frequency_changed(VariableItem* item) { +static void xtreme_app_scene_protocols_freqs_hopper_frequency_changed(VariableItem* item) { XtremeApp* app = variable_item_get_context(item); app->subghz_hopper_index = variable_item_get_current_value_index(item); uint32_t value = *FrequencyList_get(app->subghz_hopper_freqs, app->subghz_hopper_index); @@ -22,7 +20,7 @@ static void xtreme_app_scene_protocols_frequencies_hopper_frequency_changed(Vari variable_item_set_current_value_text(item, text); } -void xtreme_app_scene_protocols_frequencies_hopper_on_enter(void* context) { +void xtreme_app_scene_protocols_freqs_hopper_on_enter(void* context) { XtremeApp* app = context; VariableItemList* var_item_list = app->var_item_list; VariableItem* item; @@ -31,7 +29,7 @@ void xtreme_app_scene_protocols_frequencies_hopper_on_enter(void* context) { var_item_list, "Hopper Freq", FrequencyList_size(app->subghz_hopper_freqs), - xtreme_app_scene_protocols_frequencies_hopper_frequency_changed, + xtreme_app_scene_protocols_freqs_hopper_frequency_changed, app); app->subghz_hopper_index = 0; variable_item_set_current_value_index(item, app->subghz_hopper_index); @@ -49,23 +47,22 @@ void xtreme_app_scene_protocols_frequencies_hopper_on_enter(void* context) { variable_item_list_add(var_item_list, "Add Hopper Freq", 0, NULL, app); variable_item_list_set_enter_callback( - var_item_list, xtreme_app_scene_protocols_frequencies_hopper_var_item_list_callback, app); + var_item_list, xtreme_app_scene_protocols_freqs_hopper_var_item_list_callback, app); variable_item_list_set_selected_item( var_item_list, - scene_manager_get_scene_state( - app->scene_manager, XtremeAppSceneProtocolsFrequenciesHopper)); + scene_manager_get_scene_state(app->scene_manager, XtremeAppSceneProtocolsFreqsHopper)); view_dispatcher_switch_to_view(app->view_dispatcher, XtremeAppViewVarItemList); } -bool xtreme_app_scene_protocols_frequencies_hopper_on_event(void* context, SceneManagerEvent event) { +bool xtreme_app_scene_protocols_freqs_hopper_on_event(void* context, SceneManagerEvent event) { XtremeApp* app = context; bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { scene_manager_set_scene_state( - app->scene_manager, XtremeAppSceneProtocolsFrequenciesHopper, event.event); + app->scene_manager, XtremeAppSceneProtocolsFreqsHopper, event.event); consumed = true; switch(event.event) { case VarItemListIndexRemoveHopperFreq: @@ -81,14 +78,14 @@ bool xtreme_app_scene_protocols_frequencies_hopper_on_event(void* context, Scene FrequencyList_next(it); } } - app->save_subghz_frequencies = true; + app->save_subghz_freqs = true; scene_manager_previous_scene(app->scene_manager); - scene_manager_next_scene(app->scene_manager, XtremeAppSceneProtocolsFrequenciesHopper); + scene_manager_next_scene(app->scene_manager, XtremeAppSceneProtocolsFreqsHopper); break; case VarItemListIndexAddHopperFreq: scene_manager_set_scene_state( - app->scene_manager, XtremeAppSceneProtocolsFrequenciesAdd, true); - scene_manager_next_scene(app->scene_manager, XtremeAppSceneProtocolsFrequenciesAdd); + app->scene_manager, XtremeAppSceneProtocolsFreqsAdd, true); + scene_manager_next_scene(app->scene_manager, XtremeAppSceneProtocolsFreqsAdd); break; default: break; @@ -98,7 +95,7 @@ bool xtreme_app_scene_protocols_frequencies_hopper_on_event(void* context, Scene return consumed; } -void xtreme_app_scene_protocols_frequencies_hopper_on_exit(void* context) { +void xtreme_app_scene_protocols_freqs_hopper_on_exit(void* context) { XtremeApp* app = context; variable_item_list_reset(app->var_item_list); } diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies_static.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_freqs_static.c similarity index 74% rename from applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies_static.c rename to applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_freqs_static.c index b50c1f8966..2f139b2b29 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies_static.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_freqs_static.c @@ -6,14 +6,12 @@ enum VarItemListIndex { VarItemListIndexAddStaticFreq, }; -void xtreme_app_scene_protocols_frequencies_static_var_item_list_callback( - void* context, - uint32_t index) { +void xtreme_app_scene_protocols_freqs_static_var_item_list_callback(void* context, uint32_t index) { XtremeApp* app = context; view_dispatcher_send_custom_event(app->view_dispatcher, index); } -static void xtreme_app_scene_protocols_frequencies_static_frequency_changed(VariableItem* item) { +static void xtreme_app_scene_protocols_freqs_static_frequency_changed(VariableItem* item) { XtremeApp* app = variable_item_get_context(item); app->subghz_static_index = variable_item_get_current_value_index(item); uint32_t value = *FrequencyList_get(app->subghz_static_freqs, app->subghz_static_index); @@ -22,7 +20,7 @@ static void xtreme_app_scene_protocols_frequencies_static_frequency_changed(Vari variable_item_set_current_value_text(item, text); } -void xtreme_app_scene_protocols_frequencies_static_on_enter(void* context) { +void xtreme_app_scene_protocols_freqs_static_on_enter(void* context) { XtremeApp* app = context; VariableItemList* var_item_list = app->var_item_list; VariableItem* item; @@ -31,7 +29,7 @@ void xtreme_app_scene_protocols_frequencies_static_on_enter(void* context) { var_item_list, "Static Freq", FrequencyList_size(app->subghz_static_freqs), - xtreme_app_scene_protocols_frequencies_static_frequency_changed, + xtreme_app_scene_protocols_freqs_static_frequency_changed, app); app->subghz_static_index = 0; variable_item_set_current_value_index(item, app->subghz_static_index); @@ -49,23 +47,22 @@ void xtreme_app_scene_protocols_frequencies_static_on_enter(void* context) { variable_item_list_add(var_item_list, "Add Static Freq", 0, NULL, app); variable_item_list_set_enter_callback( - var_item_list, xtreme_app_scene_protocols_frequencies_static_var_item_list_callback, app); + var_item_list, xtreme_app_scene_protocols_freqs_static_var_item_list_callback, app); variable_item_list_set_selected_item( var_item_list, - scene_manager_get_scene_state( - app->scene_manager, XtremeAppSceneProtocolsFrequenciesStatic)); + scene_manager_get_scene_state(app->scene_manager, XtremeAppSceneProtocolsFreqsStatic)); view_dispatcher_switch_to_view(app->view_dispatcher, XtremeAppViewVarItemList); } -bool xtreme_app_scene_protocols_frequencies_static_on_event(void* context, SceneManagerEvent event) { +bool xtreme_app_scene_protocols_freqs_static_on_event(void* context, SceneManagerEvent event) { XtremeApp* app = context; bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { scene_manager_set_scene_state( - app->scene_manager, XtremeAppSceneProtocolsFrequenciesStatic, event.event); + app->scene_manager, XtremeAppSceneProtocolsFreqsStatic, event.event); consumed = true; switch(event.event) { case VarItemListIndexRemoveStaticFreq: @@ -81,14 +78,14 @@ bool xtreme_app_scene_protocols_frequencies_static_on_event(void* context, Scene FrequencyList_next(it); } } - app->save_subghz_frequencies = true; + app->save_subghz_freqs = true; scene_manager_previous_scene(app->scene_manager); - scene_manager_next_scene(app->scene_manager, XtremeAppSceneProtocolsFrequenciesStatic); + scene_manager_next_scene(app->scene_manager, XtremeAppSceneProtocolsFreqsStatic); break; case VarItemListIndexAddStaticFreq: scene_manager_set_scene_state( - app->scene_manager, XtremeAppSceneProtocolsFrequenciesAdd, false); - scene_manager_next_scene(app->scene_manager, XtremeAppSceneProtocolsFrequenciesAdd); + app->scene_manager, XtremeAppSceneProtocolsFreqsAdd, false); + scene_manager_next_scene(app->scene_manager, XtremeAppSceneProtocolsFreqsAdd); break; default: break; @@ -98,7 +95,7 @@ bool xtreme_app_scene_protocols_frequencies_static_on_event(void* context, Scene return consumed; } -void xtreme_app_scene_protocols_frequencies_static_on_exit(void* context) { +void xtreme_app_scene_protocols_freqs_static_on_exit(void* context) { XtremeApp* app = context; variable_item_list_reset(app->var_item_list); } diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_start.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_start.c index fabd7ce6cc..1f1c1de707 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_start.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_start.c @@ -15,10 +15,17 @@ void xtreme_app_scene_start_var_item_list_callback(void* context, uint32_t index void xtreme_app_scene_start_on_enter(void* context) { XtremeApp* app = context; VariableItemList* var_item_list = app->var_item_list; + VariableItem* item; + + item = variable_item_list_add(var_item_list, "Interface", 0, NULL, app); + variable_item_set_current_value_text(item, ">"); + + item = variable_item_list_add(var_item_list, "Protocols", 0, NULL, app); + variable_item_set_current_value_text(item, ">"); + + item = variable_item_list_add(var_item_list, "Misc", 0, NULL, app); + variable_item_set_current_value_text(item, ">"); - variable_item_list_add(var_item_list, "Interface", 0, NULL, app); - variable_item_list_add(var_item_list, "Protocols", 0, NULL, app); - variable_item_list_add(var_item_list, "Misc", 0, NULL, app); variable_item_list_add(var_item_list, furi_string_get_cstr(app->version_tag), 0, NULL, app); variable_item_list_set_enter_callback( @@ -48,12 +55,15 @@ bool xtreme_app_scene_start_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(app->scene_manager, XtremeAppSceneMisc); break; case VarItemListIndexVersion: { - if(storage_common_copy( - furi_record_open(RECORD_STORAGE), - EXT_PATH("dolphin/xfwfirstboot.bin"), - EXT_PATH(".slideshow"))) { - app->show_slideshow = true; - xtreme_app_apply(app); + for(int i = 0; i < 10; i++) { + if(storage_common_copy( + furi_record_open(RECORD_STORAGE), + EXT_PATH("dolphin/xfwfirstboot.bin"), + EXT_PATH(".slideshow"))) { + app->show_slideshow = true; + xtreme_app_apply(app); + break; + } } break; } diff --git a/applications/main/xtreme_app/xtreme_app.c b/applications/main/xtreme_app/xtreme_app.c index dedd9fcdeb..01822084fa 100644 --- a/applications/main/xtreme_app/xtreme_app.c +++ b/applications/main/xtreme_app/xtreme_app.c @@ -27,7 +27,7 @@ bool xtreme_app_apply(XtremeApp* app) { stream_free(stream); } - if(app->save_subghz_frequencies) { + if(app->save_subghz_freqs) { FlipperFormat* file = flipper_format_file_alloc(storage); do { FrequencyList_it_t it; @@ -229,7 +229,7 @@ XtremeApp* xtreme_app_alloc() { furi_string_replace_all(line, "\r", ""); furi_string_replace_all(line, "\n", ""); CharList_push_back(app->mainmenu_app_paths, strdup(furi_string_get_cstr(line))); - fap_loader_load_name_and_icon(line, storage, NULL, line); + flipper_application_load_name_and_icon(line, storage, NULL, line); CharList_push_back(app->mainmenu_app_names, strdup(furi_string_get_cstr(line))); } } diff --git a/applications/main/xtreme_app/xtreme_app.h b/applications/main/xtreme_app/xtreme_app.h index d02c5e9f08..da6029ed18 100644 --- a/applications/main/xtreme_app/xtreme_app.h +++ b/applications/main/xtreme_app/xtreme_app.h @@ -18,14 +18,14 @@ #include "dolphin/dolphin_i.h" #include #include -#include +#include #include #include #include #include #include -#define XTREME_SUBGHZ_FREQ_BUFFER_SIZE 6 +#define XTREME_SUBGHZ_FREQ_BUFFER_SIZE 7 ARRAY_DEF(CharList, char*) @@ -57,7 +57,7 @@ typedef struct { FuriString* version_tag; bool save_mainmenu_apps; - bool save_subghz_frequencies; + bool save_subghz_freqs; bool save_subghz; bool save_name; bool save_level; diff --git a/applications/services/applications.h b/applications/services/applications.h index 45e0be2e73..45b050a06e 100644 --- a/applications/services/applications.h +++ b/applications/services/applications.h @@ -4,9 +4,9 @@ #include typedef enum { - FlipperApplicationFlagDefault = 0, - FlipperApplicationFlagInsomniaSafe = (1 << 0), -} FlipperApplicationFlag; + FlipperInternalApplicationFlagDefault = 0, + FlipperInternalApplicationFlagInsomniaSafe = (1 << 0), +} FlipperInternalApplicationFlag; typedef struct { const FuriThreadCallback app; @@ -14,49 +14,41 @@ typedef struct { const char* appid; const size_t stack_size; const Icon* icon; - const FlipperApplicationFlag flags; - void* preload; -} FlipperApplication; + const FlipperInternalApplicationFlag flags; +} FlipperInternalApplication; -typedef void (*FlipperOnStartHook)(void); +typedef void (*FlipperInternalOnStartHook)(void); extern const char* FLIPPER_AUTORUN_APP_NAME; /* Services list * Spawned on startup */ -extern FlipperApplication FLIPPER_SERVICES[]; +extern const FlipperInternalApplication FLIPPER_SERVICES[]; extern const size_t FLIPPER_SERVICES_COUNT; /* Apps list * Spawned by loader */ -extern FlipperApplication FLIPPER_APPS[]; +extern const FlipperInternalApplication FLIPPER_APPS[]; extern const size_t FLIPPER_APPS_COUNT; /* On system start hooks * Called by loader, after OS initialization complete */ -extern FlipperOnStartHook FLIPPER_ON_SYSTEM_START[]; +extern const FlipperInternalOnStartHook FLIPPER_ON_SYSTEM_START[]; extern const size_t FLIPPER_ON_SYSTEM_START_COUNT; /* System apps * Can only be spawned by loader by name */ -extern FlipperApplication FLIPPER_SYSTEM_APPS[]; +extern const FlipperInternalApplication FLIPPER_SYSTEM_APPS[]; extern const size_t FLIPPER_SYSTEM_APPS_COUNT; -/* Separate scene app holder - * Spawned by loader - */ -extern const FlipperApplication FLIPPER_SCENE; -extern FlipperApplication FLIPPER_SCENE_APPS[]; -extern const size_t FLIPPER_SCENE_APPS_COUNT; - -extern const FlipperApplication FLIPPER_ARCHIVE; +extern const FlipperInternalApplication FLIPPER_ARCHIVE; /* Settings list * Spawned by loader */ -extern FlipperApplication FLIPPER_SETTINGS_APPS[]; +extern const FlipperInternalApplication FLIPPER_SETTINGS_APPS[]; extern const size_t FLIPPER_SETTINGS_APPS_COUNT; diff --git a/applications/services/bt/bt_service/bt.c b/applications/services/bt/bt_service/bt.c index 60ab1dd840..b8cb09734d 100644 --- a/applications/services/bt/bt_service/bt.c +++ b/applications/services/bt/bt_service/bt.c @@ -121,9 +121,7 @@ Bt* bt_alloc() { bt->max_packet_size = FURI_HAL_BT_SERIAL_PACKET_SIZE_MAX; bt->profile = BtProfileSerial; // Load settings - if(!bt_settings_load(&bt->bt_settings)) { - bt_settings_save(&bt->bt_settings); - } + bt_settings_load(&bt->bt_settings); // Keys storage bt->keys_storage = bt_keys_storage_alloc(BT_KEYS_STORAGE_PATH); // Alloc queue diff --git a/applications/services/desktop/animations/animation_storage.c b/applications/services/desktop/animations/animation_storage.c index a173de2a31..9e09982143 100644 --- a/applications/services/desktop/animations/animation_storage.c +++ b/applications/services/desktop/animations/animation_storage.c @@ -13,17 +13,9 @@ #include #include #define ANIMATION_META_FILE "meta.txt" -#define BASE_ANIMATION_DIR EXT_PATH("dolphin") #define TAG "AnimationStorage" -/* Unused old code, for safe-keeping - -#define ANIMATION_MANIFEST_FILE ANIMATION_DIR "/manifest.txt" - -*/ -// 59 Max length = strlen("/ext/dolphin_custom//Anims") + XTREME_ASSETS_PACK_NAME_LEN + 1 (Null terminator) -char ANIMATION_DIR[59]; -// 72 Max length = ANIMATION_DIR + strlen("/manifest.txt") -char ANIMATION_MANIFEST_FILE[72]; +char ANIMATION_DIR[26 /*"/ext/dolphin_custom//Anims"*/ + XTREME_ASSETS_PACK_NAME_LEN + 1]; +char ANIMATION_MANIFEST_FILE[sizeof(ANIMATION_DIR) + 13 /*"/manifest.txt"*/]; static void animation_storage_free_bubbles(BubbleAnimation* animation); static void animation_storage_free_frames(BubbleAnimation* animation); @@ -43,6 +35,7 @@ void animation_handler_select_manifest() { if(storage_common_stat(storage, furi_string_get_cstr(manifest), NULL) == FSE_OK) { FURI_LOG_I(TAG, "Custom manifest selected"); } else { + FURI_LOG_E(TAG, "Custom manifest does not exist!"); use_asset_pack = false; } furi_record_close(RECORD_STORAGE); diff --git a/applications/services/desktop/animations/animation_storage.h b/applications/services/desktop/animations/animation_storage.h index 16c0feab4b..1b9ce44e0c 100644 --- a/applications/services/desktop/animations/animation_storage.h +++ b/applications/services/desktop/animations/animation_storage.h @@ -3,6 +3,8 @@ #include #include "views/bubble_animation_view.h" +#define BASE_ANIMATION_DIR EXT_PATH("dolphin") + /** Main structure to handle animation data. * Contains all, including animation playing data (BubbleAnimation), * data for random animation selection (StorageAnimationMeta) and diff --git a/applications/services/desktop/animations/views/bubble_animation_view.c b/applications/services/desktop/animations/views/bubble_animation_view.c index e59ca0c988..199f7cd717 100644 --- a/applications/services/desktop/animations/views/bubble_animation_view.c +++ b/applications/services/desktop/animations/views/bubble_animation_view.c @@ -12,7 +12,6 @@ #include #include #include -#include #define ACTIVE_SHIFT 2 @@ -129,15 +128,11 @@ static bool bubble_animation_input_callback(InputEvent* event, void* context) { if(event->key == InputKeyRight) { /* Right button reserved for animation activation, so consume */ - consumed = true; if(event->type == InputTypeShort) { if(animation_view->interact_callback) { + consumed = true; animation_view->interact_callback(animation_view->interact_callback_context); } - } else if(event->type == InputTypeLong) { - Loader* loader = furi_record_open(RECORD_LOADER); - loader_start(loader, "Power", "about_battery"); - furi_record_close(RECORD_LOADER); } } diff --git a/applications/services/desktop/animations/views/one_shot_animation_view.c b/applications/services/desktop/animations/views/one_shot_animation_view.c index d11d35b170..e76856288d 100644 --- a/applications/services/desktop/animations/views/one_shot_animation_view.c +++ b/applications/services/desktop/animations/views/one_shot_animation_view.c @@ -6,7 +6,6 @@ #include #include #include -#include typedef void (*OneShotInteractCallback)(void*); @@ -66,15 +65,11 @@ static bool one_shot_view_input(InputEvent* event, void* context) { if(!consumed) { if(event->key == InputKeyRight) { /* Right button reserved for animation activation, so consume */ - consumed = true; if(event->type == InputTypeShort) { if(view->interact_callback) { + consumed = true; view->interact_callback(view->interact_callback_context); } - } else if(event->type == InputTypeLong) { - Loader* loader = furi_record_open(RECORD_LOADER); - loader_start(loader, "Power", "about_battery"); - furi_record_close(RECORD_LOADER); } } } diff --git a/applications/services/desktop/desktop.c b/applications/services/desktop/desktop.c index 956a1cbf8f..8ecf0e9367 100644 --- a/applications/services/desktop/desktop.c +++ b/applications/services/desktop/desktop.c @@ -37,6 +37,7 @@ static void desktop_loader_callback(const void* message, void* context) { view_dispatcher_send_custom_event(desktop->view_dispatcher, DesktopGlobalAfterAppFinished); } } + static void desktop_lock_icon_draw_callback(Canvas* canvas, void* context) { UNUSED(context); furi_assert(canvas); @@ -152,12 +153,7 @@ static bool desktop_custom_event_callback(void* context, uint32_t event) { return true; case DesktopGlobalAfterAppFinished: animation_manager_load_and_continue_animation(desktop->animation_manager); - // TODO: Implement a message mechanism for loading settings and (optionally) - // locking and unlocking - DESKTOP_SETTINGS_LOAD(&desktop->settings); - - desktop_clock_toggle_view(desktop, desktop->settings.display_clock); - + desktop_clock_toggle_view(desktop, XTREME_SETTINGS()->statusbar_clock); desktop_auto_lock_arm(desktop); return true; case DesktopGlobalAutoLock: @@ -237,7 +233,7 @@ static void desktop_clock_timer_callback(void* context) { } void desktop_lock(Desktop* desktop, bool pin_lock) { - pin_lock = pin_lock && desktop->settings.pin_code.length > 0; + pin_lock = pin_lock && desktop_pin_is_valid(&desktop->settings.pin_code); if(!furi_hal_rtc_is_flag_set(FuriHalRtcFlagLock)) { furi_hal_rtc_set_pin_fails(0); } @@ -439,7 +435,9 @@ bool desktop_api_is_locked(Desktop* instance) { void desktop_api_unlock(Desktop* instance) { furi_assert(instance); - view_dispatcher_send_custom_event(instance->view_dispatcher, DesktopLockedEventUnlocked); + if(!furi_hal_rtc_is_flag_set(FuriHalRtcFlagLock) || XTREME_SETTINGS()->pin_unlock_from_app) { + view_dispatcher_send_custom_event(instance->view_dispatcher, DesktopLockedEventUnlocked); + } } FuriPubSub* desktop_api_get_status_pubsub(Desktop* instance) { @@ -447,6 +445,45 @@ FuriPubSub* desktop_api_get_status_pubsub(Desktop* instance) { return instance->status_pubsub; } +static const KeybindType keybind_types[] = { + [InputTypeShort] = KeybindTypePress, + [InputTypeLong] = KeybindTypeHold, +}; + +static const KeybindKey keybind_keys[] = { + [InputKeyUp] = KeybindKeyUp, + [InputKeyDown] = KeybindKeyDown, + [InputKeyRight] = KeybindKeyRight, + [InputKeyLeft] = KeybindKeyLeft, +}; + +void desktop_run_keybind(Desktop* instance, InputType _type, InputKey _key) { + if(_type != InputTypeShort && _type != InputTypeLong) return; + if(_key != InputKeyUp && _key != InputKeyDown && _key != InputKeyRight && _key != InputKeyLeft) + return; + + KeybindType type = keybind_types[_type]; + KeybindKey key = keybind_keys[_key]; + const char* keybind = instance->keybinds[type][key].data; + if(!strnlen(keybind, MAX_KEYBIND_LENGTH)) return; + + if(!strncmp(keybind, "Apps Menu", MAX_KEYBIND_LENGTH)) { + loader_start_detached_with_gui_error(instance->loader, LOADER_APPLICATIONS_NAME, NULL); + } else if(!strncmp(keybind, "Archive", MAX_KEYBIND_LENGTH)) { + view_dispatcher_send_custom_event(instance->view_dispatcher, DesktopMainEventOpenArchive); + } else if(!strncmp(keybind, "Device Info", MAX_KEYBIND_LENGTH)) { + loader_start_detached_with_gui_error(instance->loader, "Power", "about_battery"); + } else if(!strncmp(keybind, "Lock Menu", MAX_KEYBIND_LENGTH)) { + view_dispatcher_send_custom_event(instance->view_dispatcher, DesktopMainEventOpenLockMenu); + } else if(!strncmp(keybind, "Lock Keypad", MAX_KEYBIND_LENGTH)) { + view_dispatcher_send_custom_event(instance->view_dispatcher, DesktopMainEventLockKeypad); + } else if(!strncmp(keybind, "Lock with PIN", MAX_KEYBIND_LENGTH)) { + view_dispatcher_send_custom_event(instance->view_dispatcher, DesktopMainEventLockWithPin); + } else { + loader_start_detached_with_gui_error(instance->loader, keybind, NULL); + } +} + int32_t desktop_srv(void* p) { UNUSED(p); @@ -457,13 +494,29 @@ int32_t desktop_srv(void* p) { Desktop* desktop = desktop_alloc(); - bool loaded = DESKTOP_SETTINGS_LOAD(&desktop->settings); - if(!loaded) { + bool ok = DESKTOP_SETTINGS_LOAD(&desktop->settings); + if(ok && desktop->settings.pin_code.length) { + ok = desktop_pin_is_valid(&desktop->settings.pin_code); + } + if(!ok) { memset(&desktop->settings, 0, sizeof(desktop->settings)); - DESKTOP_SETTINGS_SAVE(&desktop->settings); + furi_hal_rtc_reset_flag(FuriHalRtcFlagLock); + furi_hal_rtc_set_pin_fails(0); + } + + if(!DESKTOP_KEYBINDS_LOAD(&desktop->keybinds, sizeof(desktop->keybinds))) { + memset(&desktop->keybinds, 0, sizeof(desktop->keybinds)); + strcpy(desktop->keybinds[KeybindTypePress][KeybindKeyUp].data, "Lock Menu"); + strcpy(desktop->keybinds[KeybindTypePress][KeybindKeyDown].data, "Archive"); + strcpy(desktop->keybinds[KeybindTypePress][KeybindKeyRight].data, "Passport"); + strcpy( + desktop->keybinds[KeybindTypePress][KeybindKeyLeft].data, + EXT_PATH("apps/Misc/nightstand.fap")); + strcpy(desktop->keybinds[KeybindTypeHold][KeybindKeyRight].data, "Device Info"); + strcpy(desktop->keybinds[KeybindTypeHold][KeybindKeyLeft].data, "Lock with PIN"); } - desktop_clock_toggle_view(desktop, desktop->settings.display_clock); + desktop_clock_toggle_view(desktop, XTREME_SETTINGS()->statusbar_clock); scene_manager_next_scene(desktop->scene_manager, DesktopSceneMain); diff --git a/applications/services/desktop/desktop.h b/applications/services/desktop/desktop.h index 4eab24fcc5..4c7551f5cb 100644 --- a/applications/services/desktop/desktop.h +++ b/applications/services/desktop/desktop.h @@ -1,6 +1,7 @@ #pragma once #include +#include typedef struct Desktop Desktop; @@ -15,3 +16,5 @@ typedef struct { } DesktopStatus; FuriPubSub* desktop_api_get_status_pubsub(Desktop* instance); + +void desktop_run_keybind(Desktop* instance, InputType _type, InputKey _key); diff --git a/applications/services/desktop/desktop_i.h b/applications/services/desktop/desktop_i.h index 65c09d1127..d42bf26168 100644 --- a/applications/services/desktop/desktop_i.h +++ b/applications/services/desktop/desktop_i.h @@ -55,6 +55,7 @@ struct Desktop { ViewStack* locked_view_stack; DesktopSettings settings; + Keybind keybinds[KeybindTypeCount][KeybindKeyCount]; DesktopViewPinInput* pin_input_view; ViewPort* lock_icon_viewport; diff --git a/applications/services/desktop/desktop_settings.c b/applications/services/desktop/desktop_settings.c index d1df4a9a63..48d12b3ce0 100644 --- a/applications/services/desktop/desktop_settings.c +++ b/applications/services/desktop/desktop_settings.c @@ -17,3 +17,13 @@ bool DESKTOP_SETTINGS_LOAD(DesktopSettings* x) { DESKTOP_SETTINGS_MAGIC, DESKTOP_SETTINGS_VER); } + +bool DESKTOP_KEYBINDS_SAVE(Keybind (*x)[KeybindTypeCount][KeybindKeyCount], size_t size) { + return saved_struct_save( + DESKTOP_KEYBINDS_PATH, x, size, DESKTOP_KEYBINDS_MAGIC, DESKTOP_KEYBINDS_VER); +} + +bool DESKTOP_KEYBINDS_LOAD(Keybind (*x)[KeybindTypeCount][KeybindKeyCount], size_t size) { + return saved_struct_load( + DESKTOP_KEYBINDS_PATH, x, size, DESKTOP_KEYBINDS_MAGIC, DESKTOP_KEYBINDS_VER); +} diff --git a/applications/services/desktop/desktop_settings.h b/applications/services/desktop/desktop_settings.h index 4cc07e7355..3cd85870e6 100644 --- a/applications/services/desktop/desktop_settings.h +++ b/applications/services/desktop/desktop_settings.h @@ -5,20 +5,21 @@ #include #include #include -#include - -#define DESKTOP_SETTINGS_VER (9) #define DESKTOP_SETTINGS_OLD_PATH CFG_PATH("desktop.settings") #define DESKTOP_SETTINGS_PATH INT_PATH(".desktop.settings") #define DESKTOP_SETTINGS_MAGIC (0x17) -#define PIN_MAX_LENGTH 12 +#define DESKTOP_SETTINGS_VER (11) + +#define DESKTOP_KEYBINDS_PATH CFG_PATH(".desktop.keybinds") +#define DESKTOP_KEYBINDS_MAGIC (0x14) +#define DESKTOP_KEYBINDS_VER (1) #define DESKTOP_SETTINGS_RUN_PIN_SETUP_ARG "run_pin_setup" #define MAX_PIN_SIZE 10 #define MIN_PIN_SIZE 4 -#define MAX_APP_LENGTH 128 +#define MAX_KEYBIND_LENGTH 64 typedef struct { InputKey data[MAX_PIN_SIZE]; @@ -26,19 +27,33 @@ typedef struct { } PinCode; typedef struct { - bool is_external; - char name_or_path[MAX_APP_LENGTH]; -} FavoriteApp; + char data[MAX_KEYBIND_LENGTH]; +} Keybind; + +typedef enum { + KeybindTypePress, + KeybindTypeHold, + KeybindTypeCount, +} KeybindType; + +typedef enum { + KeybindKeyUp, + KeybindKeyDown, + KeybindKeyRight, + KeybindKeyLeft, + KeybindKeyCount, +} KeybindKey; typedef struct { - FavoriteApp favorite_primary; - FavoriteApp favorite_secondary; PinCode pin_code; uint32_t auto_lock_delay_ms; bool auto_lock_with_pin; - uint8_t display_clock; } DesktopSettings; bool DESKTOP_SETTINGS_SAVE(DesktopSettings* x); bool DESKTOP_SETTINGS_LOAD(DesktopSettings* x); + +bool DESKTOP_KEYBINDS_SAVE(Keybind (*x)[KeybindTypeCount][KeybindKeyCount], size_t size); + +bool DESKTOP_KEYBINDS_LOAD(Keybind (*x)[KeybindTypeCount][KeybindKeyCount], size_t size); diff --git a/applications/services/desktop/helpers/pin.c b/applications/services/desktop/helpers/pin.c index d6ab48df2c..b410015524 100644 --- a/applications/services/desktop/helpers/pin.c +++ b/applications/services/desktop/helpers/pin.c @@ -53,3 +53,12 @@ bool desktop_pin_compare(const PinCode* pin_code1, const PinCode* pin_code2) { return result; } + +bool desktop_pin_is_valid(const PinCode* pin_code) { + bool ok = pin_code->length >= MIN_PIN_SIZE && pin_code->length <= MAX_PIN_SIZE; + for(size_t i = 0; ok && i < pin_code->length; i++) { + ok = ok && (pin_code->data[i] == InputKeyUp || pin_code->data[i] == InputKeyDown || + pin_code->data[i] == InputKeyRight || pin_code->data[i] == InputKeyLeft); + } + return ok; +} diff --git a/applications/services/desktop/helpers/pin.h b/applications/services/desktop/helpers/pin.h index e5410723e5..ce9fcfec3d 100644 --- a/applications/services/desktop/helpers/pin.h +++ b/applications/services/desktop/helpers/pin.h @@ -9,3 +9,5 @@ void desktop_pin_lock_error_notify(); uint32_t desktop_pin_lock_get_fail_timeout(); bool desktop_pin_compare(const PinCode* pin_code1, const PinCode* pin_code2); + +bool desktop_pin_is_valid(const PinCode* pin_code); diff --git a/applications/services/desktop/scenes/desktop_scene_debug.c b/applications/services/desktop/scenes/desktop_scene_debug.c index e79c56e111..a5bd3a6b1e 100644 --- a/applications/services/desktop/scenes/desktop_scene_debug.c +++ b/applications/services/desktop/scenes/desktop_scene_debug.c @@ -34,13 +34,13 @@ bool desktop_scene_debug_on_event(void* context, SceneManagerEvent event) { break; case DesktopDebugEventDeed: - dolphin_deed(dolphin, DolphinDeedTestRight); + dolphin_deed(DolphinDeedTestRight); desktop_debug_get_dolphin_data(desktop->debug_view); consumed = true; break; case DesktopDebugEventWrongDeed: - dolphin_deed(dolphin, DolphinDeedTestLeft); + dolphin_deed(DolphinDeedTestLeft); desktop_debug_get_dolphin_data(desktop->debug_view); consumed = true; break; diff --git a/applications/services/desktop/scenes/desktop_scene_lock_menu.c b/applications/services/desktop/scenes/desktop_scene_lock_menu.c index c56fd16f2d..bcc8eee20e 100644 --- a/applications/services/desktop/scenes/desktop_scene_lock_menu.c +++ b/applications/services/desktop/scenes/desktop_scene_lock_menu.c @@ -4,7 +4,6 @@ #include #include #include -// #include #include #include "../desktop_i.h" @@ -24,10 +23,10 @@ void desktop_scene_lock_menu_callback(DesktopEvent event, void* context) { void desktop_scene_lock_menu_on_enter(void* context) { Desktop* desktop = (Desktop*)context; - DESKTOP_SETTINGS_LOAD(&desktop->settings); scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneLockMenu, 0); desktop_lock_menu_set_callback(desktop->lock_menu, desktop_scene_lock_menu_callback, desktop); - desktop_lock_menu_set_pin_state(desktop->lock_menu, desktop->settings.pin_code.length > 0); + desktop_lock_menu_set_pin_state( + desktop->lock_menu, desktop_pin_is_valid(&desktop->settings.pin_code)); desktop_lock_menu_set_stealth_mode_state( desktop->lock_menu, furi_hal_rtc_is_flag_set(FuriHalRtcFlagStealthMode)); desktop_lock_menu_set_idx(desktop->lock_menu, 3); @@ -62,8 +61,7 @@ bool desktop_scene_lock_menu_on_event(void* context, SceneManagerEvent event) { int check_pin_changed = scene_manager_get_scene_state(desktop->scene_manager, DesktopSceneLockMenu); if(check_pin_changed) { - DESKTOP_SETTINGS_LOAD(&desktop->settings); - if(desktop->settings.pin_code.length > 0) { + if(desktop_pin_is_valid(&desktop->settings.pin_code)) { desktop_lock_menu_set_pin_state(desktop->lock_menu, true); scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneLockMenu, 0); desktop_lock(desktop, true); @@ -90,11 +88,11 @@ bool desktop_scene_lock_menu_on_event(void* context, SceneManagerEvent event) { break; case DesktopLockMenuEventLockPin: desktop_scene_lock_menu_save_settings(desktop); - if(desktop->settings.pin_code.length > 0) { + if(desktop_pin_is_valid(&desktop->settings.pin_code)) { desktop_lock(desktop, true); } else { - LoaderStatus status = - loader_start(desktop->loader, "Desktop", DESKTOP_SETTINGS_RUN_PIN_SETUP_ARG); + LoaderStatus status = loader_start( + desktop->loader, "Desktop", DESKTOP_SETTINGS_RUN_PIN_SETUP_ARG, NULL); if(status == LoaderStatusOk) { scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneLockMenu, 1); } else { @@ -105,15 +103,15 @@ bool desktop_scene_lock_menu_on_event(void* context, SceneManagerEvent event) { break; case DesktopLockMenuEventLockPinOff: desktop_scene_lock_menu_save_settings(desktop); - if(desktop->settings.pin_code.length > 0) { + if(desktop_pin_is_valid(&desktop->settings.pin_code)) { desktop_lock(desktop, true); Power* power = furi_record_open(RECORD_POWER); furi_delay_ms(500); power_off(power); furi_record_close(RECORD_POWER); } else { - LoaderStatus status = - loader_start(desktop->loader, "Desktop", DESKTOP_SETTINGS_RUN_PIN_SETUP_ARG); + LoaderStatus status = loader_start( + desktop->loader, "Desktop", DESKTOP_SETTINGS_RUN_PIN_SETUP_ARG, NULL); if(status == LoaderStatusOk) { scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneLockMenu, 2); } else { @@ -124,7 +122,7 @@ bool desktop_scene_lock_menu_on_event(void* context, SceneManagerEvent event) { break; case DesktopLockMenuEventXtreme: desktop_scene_lock_menu_save_settings(desktop); - loader_start(desktop->loader, "Xtreme", NULL); + loader_start_detached_with_gui_error(desktop->loader, "Xtreme", NULL); consumed = true; break; case DesktopLockMenuEventStealthModeOn: diff --git a/applications/services/desktop/scenes/desktop_scene_locked.c b/applications/services/desktop/scenes/desktop_scene_locked.c index 11dd7f2225..ce2a6b96e3 100644 --- a/applications/services/desktop/scenes/desktop_scene_locked.c +++ b/applications/services/desktop/scenes/desktop_scene_locked.c @@ -15,6 +15,8 @@ #include "desktop_scene.h" #include "desktop_scene_i.h" +#define TAG "DesktopSrv" + #define WRONG_PIN_HEADER_TIMEOUT 3000 #define INPUT_PIN_VIEW_TIMEOUT 15000 @@ -52,7 +54,6 @@ void desktop_scene_locked_on_enter(void* context) { furi_record_close(RECORD_GUI); if(pin_locked) { - DESKTOP_SETTINGS_LOAD(&desktop->settings); desktop_view_locked_lock(desktop->locked_view, true); uint32_t pin_timeout = desktop_pin_lock_get_fail_timeout(); if(pin_timeout > 0) { @@ -83,6 +84,11 @@ bool desktop_scene_locked_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { switch(event.event) { + case DesktopLockedEventOpenPowerOff: { + loader_start(desktop->loader, "Power", "off", NULL); + consumed = true; + break; + } case DesktopLockedEventUnlocked: desktop_unlock(desktop); consumed = true; diff --git a/applications/services/desktop/scenes/desktop_scene_main.c b/applications/services/desktop/scenes/desktop_scene_main.c index e8dfbe2b67..b4ab2e4e8c 100644 --- a/applications/services/desktop/scenes/desktop_scene_main.c +++ b/applications/services/desktop/scenes/desktop_scene_main.c @@ -34,7 +34,8 @@ static void desktop_scene_main_interact_animation_callback(void* context) { } #ifdef APP_ARCHIVE -static void desktop_switch_to_app(Desktop* desktop, const FlipperApplication* flipper_app) { +static void + desktop_switch_to_app(Desktop* desktop, const FlipperInternalApplication* flipper_app) { furi_assert(desktop); furi_assert(flipper_app); furi_assert(flipper_app->app); @@ -60,23 +61,6 @@ static void desktop_switch_to_app(Desktop* desktop, const FlipperApplication* fl } #endif -static void desktop_scene_main_start_favorite(Desktop* desktop, FavoriteApp* application) { - LoaderStatus status = LoaderStatusErrorInternal; - if(application->is_external) { - status = loader_start(desktop->loader, FAP_LOADER_APP_NAME, application->name_or_path); - } else if(strlen(application->name_or_path) > 0) { - status = loader_start(desktop->loader, application->name_or_path, NULL); - } else { - // No favourite app is set! So we skipping this part - return; - //status = loader_start(desktop->loader, FAP_LOADER_APP_NAME, NULL); - } - - if(status != LoaderStatusOk) { - FURI_LOG_E(TAG, "loader_start failed: %d", status); - } -} - void desktop_scene_main_callback(DesktopEvent event, void* context) { Desktop* desktop = (Desktop*)context; if(desktop->in_transition) return; @@ -123,7 +107,12 @@ bool desktop_scene_main_on_event(void* context, SceneManagerEvent event) { consumed = true; break; - case DesktopMainEventLock: + case DesktopMainEventLockKeypad: + desktop_lock(desktop, false); + consumed = true; + break; + + case DesktopMainEventLockWithPin: desktop_lock(desktop, true); consumed = true; break; @@ -136,23 +125,10 @@ bool desktop_scene_main_on_event(void* context, SceneManagerEvent event) { break; case DesktopMainEventOpenPowerOff: { - LoaderStatus status = loader_start(desktop->loader, "Power", "off"); - if(status != LoaderStatusOk) { - FURI_LOG_E(TAG, "loader_start failed: %d", status); - } + loader_start(desktop->loader, "Power", "off", NULL); consumed = true; break; } - case DesktopMainEventOpenFavoritePrimary: - DESKTOP_SETTINGS_LOAD(&desktop->settings); - desktop_scene_main_start_favorite(desktop, &desktop->settings.favorite_primary); - consumed = true; - break; - case DesktopMainEventOpenFavoriteSecondary: - DESKTOP_SETTINGS_LOAD(&desktop->settings); - desktop_scene_main_start_favorite(desktop, &desktop->settings.favorite_secondary); - consumed = true; - break; case DesktopAnimationEventCheckAnimation: animation_manager_check_blocking_process(desktop->animation_manager); consumed = true; @@ -163,28 +139,10 @@ bool desktop_scene_main_on_event(void* context, SceneManagerEvent event) { break; case DesktopAnimationEventInteractAnimation: if(!animation_manager_interact_process(desktop->animation_manager)) { - LoaderStatus status = loader_start(desktop->loader, "Passport", NULL); - if(status != LoaderStatusOk) { - FURI_LOG_E(TAG, "loader_start failed: %d", status); - } + desktop_run_keybind(desktop, InputTypeShort, InputKeyRight); } consumed = true; break; - case DesktopMainEventOpenPassport: { - LoaderStatus status = loader_start(desktop->loader, "Passport", NULL); - if(status != LoaderStatusOk) { - FURI_LOG_E(TAG, "loader_start failed: %d", status); - } - break; - } - case DesktopMainEventOpenClock: { - LoaderStatus status = loader_start( - desktop->loader, FAP_LOADER_APP_NAME, EXT_PATH("apps/Misc/Nightstand.fap")); - if(status != LoaderStatusOk) { - FURI_LOG_E(TAG, "loader_start failed: %d", status); - } - break; - } case DesktopLockedEventUpdate: desktop_view_locked_update(desktop->locked_view); consumed = true; diff --git a/applications/services/desktop/views/desktop_events.h b/applications/services/desktop/views/desktop_events.h index af75970123..5fada45c1e 100644 --- a/applications/services/desktop/views/desktop_events.h +++ b/applications/services/desktop/views/desktop_events.h @@ -3,26 +3,13 @@ typedef enum { DesktopMainEventOpenLockMenu, DesktopMainEventOpenArchive, - DesktopMainEventOpenFavoritePrimary, - DesktopMainEventOpenFavoriteSecondary, DesktopMainEventOpenMenu, - DesktopMainEventOpenGames, DesktopMainEventOpenDebug, - DesktopMainEventOpenPassport, DesktopMainEventOpenPowerOff, - DesktopMainEventLock, - - DesktopMainEventOpenSnake, - DesktopMainEventOpen2048, - DesktopMainEventOpenZombiez, - DesktopMainEventOpenTetris, - DesktopMainEventOpenDOOM, - DesktopMainEventOpenDice, - DesktopMainEventOpenArkanoid, - DesktopMainEventOpenHeap, - DesktopMainEventOpenSubRemote, - DesktopMainEventOpenClock, + DesktopMainEventLockKeypad, + DesktopMainEventLockWithPin, + DesktopLockedEventOpenPowerOff, DesktopLockedEventUnlocked, DesktopLockedEventUpdate, DesktopLockedEventShowPinInput, diff --git a/applications/services/desktop/views/desktop_view_locked.c b/applications/services/desktop/views/desktop_view_locked.c index 21f24dde92..69ace11edd 100644 --- a/applications/services/desktop/views/desktop_view_locked.c +++ b/applications/services/desktop/views/desktop_view_locked.c @@ -231,6 +231,12 @@ static bool desktop_view_locked_input(InputEvent* event, void* context) { desktop_view_locked_update_hint_icon_timeout(locked_view); + if(event->key == InputKeyBack) { + if(event->type == InputTypeLong) { + locked_view->callback(DesktopLockedEventOpenPowerOff, locked_view->context); + } + } + if(pin_locked) { if(event->key == InputKeyUp) { locked_view->callback(DesktopLockedEventShowPinInput, locked_view->context); diff --git a/applications/services/desktop/views/desktop_view_main.c b/applications/services/desktop/views/desktop_view_main.c index c1009fde12..54c2f5566a 100644 --- a/applications/services/desktop/views/desktop_view_main.c +++ b/applications/services/desktop/views/desktop_view_main.c @@ -13,16 +13,8 @@ struct DesktopMainView { View* view; DesktopMainViewCallback callback; void* context; - TimerHandle_t poweroff_timer; }; -#define DESKTOP_MAIN_VIEW_POWEROFF_TIMEOUT 1300 - -static void desktop_main_poweroff_timer_callback(TimerHandle_t timer) { - DesktopMainView* main_view = pvTimerGetTimerID(timer); - main_view->callback(DesktopMainEventOpenPowerOff, main_view->context); -} - void desktop_main_set_callback( DesktopMainView* main_view, DesktopMainViewCallback callback, @@ -44,37 +36,21 @@ bool desktop_main_input_callback(InputEvent* event, void* context) { DesktopMainView* main_view = context; - if(event->type == InputTypeShort) { + // DesktopMainEventOpenDebug + if(event->type == InputTypeShort || event->type == InputTypeLong) { if(event->key == InputKeyOk) { - main_view->callback(DesktopMainEventOpenMenu, main_view->context); - } else if(event->key == InputKeyUp) { - main_view->callback(DesktopMainEventOpenLockMenu, main_view->context); - } else if(event->key == InputKeyDown) { - main_view->callback(DesktopMainEventOpenArchive, main_view->context); - } else if(event->key == InputKeyLeft) { - main_view->callback(DesktopMainEventOpenClock, main_view->context); - } - // Right key is handled by animation manager - } else if(event->type == InputTypeLong) { - if(event->key == InputKeyOk) { - main_view->callback(DesktopAnimationEventNewIdleAnimation, main_view->context); - } else if(event->key == InputKeyUp) { - main_view->callback(DesktopMainEventOpenFavoritePrimary, main_view->context); - } else if(event->key == InputKeyDown) { - main_view->callback(DesktopMainEventOpenFavoriteSecondary, main_view->context); - } else if(event->key == InputKeyLeft) { - main_view->callback(DesktopMainEventLock, main_view->context); + main_view->callback( + event->type == InputTypeShort ? DesktopMainEventOpenMenu : + DesktopAnimationEventNewIdleAnimation, + main_view->context); + } else { + desktop_run_keybind((Desktop*)main_view->context, event->type, event->key); } } if(event->key == InputKeyBack) { - if(event->type == InputTypePress) { - xTimerChangePeriod( - main_view->poweroff_timer, - pdMS_TO_TICKS(DESKTOP_MAIN_VIEW_POWEROFF_TIMEOUT), - portMAX_DELAY); - } else if(event->type == InputTypeRelease) { - xTimerStop(main_view->poweroff_timer, portMAX_DELAY); + if(event->type == InputTypeLong) { + main_view->callback(DesktopMainEventOpenPowerOff, main_view->context); } } @@ -88,19 +64,11 @@ DesktopMainView* desktop_main_alloc() { view_set_context(main_view->view, main_view); view_set_input_callback(main_view->view, desktop_main_input_callback); - main_view->poweroff_timer = xTimerCreate( - NULL, - pdMS_TO_TICKS(DESKTOP_MAIN_VIEW_POWEROFF_TIMEOUT), - pdFALSE, - main_view, - desktop_main_poweroff_timer_callback); - return main_view; } void desktop_main_free(DesktopMainView* main_view) { furi_assert(main_view); view_free(main_view->view); - furi_timer_free(main_view->poweroff_timer); free(main_view); } diff --git a/applications/services/dialogs/dialogs.c b/applications/services/dialogs/dialogs.c index 3908ca31b5..10c08a991b 100644 --- a/applications/services/dialogs/dialogs.c +++ b/applications/services/dialogs/dialogs.c @@ -9,12 +9,13 @@ void dialog_file_browser_set_basic_options( const char* extension, const Icon* icon) { options->extension = extension; + options->base_path = NULL; options->skip_assets = true; + options->hide_dot_files = true; options->icon = icon; options->hide_ext = true; options->item_loader_callback = NULL; options->item_loader_context = NULL; - options->base_path = NULL; } static DialogsApp* dialogs_app_alloc() { diff --git a/applications/services/dialogs/dialogs.h b/applications/services/dialogs/dialogs.h index 4c1b675a64..39b15c67c2 100644 --- a/applications/services/dialogs/dialogs.h +++ b/applications/services/dialogs/dialogs.h @@ -16,7 +16,8 @@ typedef struct DialogsApp DialogsApp; /****************** FILE BROWSER ******************/ /** - * File browser dialog extra options + * File browser dialog extra options. + * This can be default-initialized using {@link dialog_file_browser_set_basic_options}. * @param extension file extension to be offered for selection * @param base_path root folder path for navigation with back key * @param skip_assets true - do not show assets folders @@ -38,8 +39,10 @@ typedef struct { } DialogsFileBrowserOptions; /** - * Initialize file browser dialog options - * and set default values + * Initialize file browser dialog options and set default values. + * This is guaranteed to initialize all fields + * so it is safe to pass pointer to uninitialized {@code options} + * and assume that the data behind it becomes fully initialized after the call. * @param options pointer to options structure * @param extension file extension to filter * @param icon file icon pointer, NULL for default icon diff --git a/applications/services/dialogs/dialogs_i.h b/applications/services/dialogs/dialogs_i.h index 76495d31b0..29417b41b5 100644 --- a/applications/services/dialogs/dialogs_i.h +++ b/applications/services/dialogs/dialogs_i.h @@ -1,7 +1,7 @@ #pragma once #include "dialogs.h" #include "dialogs_message.h" -#include "view_holder.h" +#include #include #ifdef __cplusplus diff --git a/applications/services/dolphin/dolphin.c b/applications/services/dolphin/dolphin.c index 64fa41332a..128d8b0acb 100644 --- a/applications/services/dolphin/dolphin.c +++ b/applications/services/dolphin/dolphin.c @@ -14,12 +14,13 @@ static void dolphin_update_clear_limits_timer_period(Dolphin* dolphin); -void dolphin_deed(Dolphin* dolphin, DolphinDeed deed) { - furi_assert(dolphin); +void dolphin_deed(DolphinDeed deed) { + Dolphin* dolphin = (Dolphin*)furi_record_open(RECORD_DOLPHIN); DolphinEvent event; event.type = DolphinEventTypeDeed; event.deed = deed; dolphin_event_send_async(dolphin, &event); + furi_record_close(RECORD_DOLPHIN); } DolphinStats dolphin_stats(Dolphin* dolphin) { diff --git a/applications/services/dolphin/dolphin.h b/applications/services/dolphin/dolphin.h index 8757e2a377..1035247e71 100644 --- a/applications/services/dolphin/dolphin.h +++ b/applications/services/dolphin/dolphin.h @@ -26,18 +26,11 @@ typedef enum { DolphinPubsubEventUpdate, } DolphinPubsubEvent; -#define DOLPHIN_DEED(deed) \ - do { \ - Dolphin* dolphin = (Dolphin*)furi_record_open("dolphin"); \ - dolphin_deed(dolphin, deed); \ - furi_record_close("dolphin"); \ - } while(0) - /** Deed complete notification. Call it on deed completion. * See dolphin_deed.h for available deeds. In futures it will become part of assets. * Thread safe, async */ -void dolphin_deed(Dolphin* dolphin, DolphinDeed deed); +void dolphin_deed(DolphinDeed deed); /** Retrieve dolphin stats * Thread safe, blocking diff --git a/applications/services/gui/gui.c b/applications/services/gui/gui.c index 500d746f88..d6445d38e3 100644 --- a/applications/services/gui/gui.c +++ b/applications/services/gui/gui.c @@ -100,75 +100,67 @@ static void gui_redraw_status_bar(Gui* gui, bool need_attention) { } canvas_set_bitmap_mode(gui->canvas, 0); - uint8_t x; - // Right side - if(xtreme_settings->battery_icon != BatteryIconOff) { - x = GUI_DISPLAY_WIDTH - 1; - ViewPortArray_it(it, gui->layers[GuiLayerStatusBarRight]); - while(!ViewPortArray_end_p(it) && right_used < GUI_STATUS_BAR_WIDTH) { - ViewPort* view_port = *ViewPortArray_ref(it); - if(view_port_is_enabled(view_port)) { - width = view_port_get_width(view_port); - if(!width) width = 8; - // Recalculate next position - right_used += (width + 2); - x -= (width + 2); - // Prepare work area background - canvas_frame_set( - gui->canvas, - x - 1, - GUI_STATUS_BAR_Y + 1, - width + 2, - GUI_STATUS_BAR_WORKAREA_HEIGHT + 2); - // Hide battery background - if(xtreme_settings->bar_borders) { - canvas_set_color(gui->canvas, ColorWhite); - canvas_draw_box( - gui->canvas, - -1, - 0, - canvas_width(gui->canvas) + 1, - canvas_height(gui->canvas)); - } - canvas_set_color(gui->canvas, ColorBlack); - // ViewPort draw - canvas_frame_set( - gui->canvas, - x - xtreme_settings->bar_borders, - GUI_STATUS_BAR_Y + 2, - width, - GUI_STATUS_BAR_WORKAREA_HEIGHT); - view_port_draw(view_port, gui->canvas); - } - ViewPortArray_next(it); - } - // Draw frame around icons on the right - if(right_used) { + uint8_t x = GUI_DISPLAY_WIDTH - 1; + ViewPortArray_it(it, gui->layers[GuiLayerStatusBarRight]); + while(!ViewPortArray_end_p(it) && right_used < GUI_STATUS_BAR_WIDTH) { + ViewPort* view_port = *ViewPortArray_ref(it); + if(view_port_is_enabled(view_port)) { + width = view_port_get_width(view_port); + if(!width) width = 8; + // Recalculate next position + right_used += (width + 2); + x -= (width + 2); + // Prepare work area background canvas_frame_set( gui->canvas, - GUI_DISPLAY_WIDTH - 4 - right_used, - GUI_STATUS_BAR_Y, - right_used + 4, - GUI_STATUS_BAR_HEIGHT); - // Disable battery border + x - 1, + GUI_STATUS_BAR_Y + 1, + width + 2, + GUI_STATUS_BAR_WORKAREA_HEIGHT + 2); + // Hide battery background if(xtreme_settings->bar_borders) { - canvas_set_color(gui->canvas, ColorBlack); - canvas_draw_rframe( - gui->canvas, 0, 0, canvas_width(gui->canvas), canvas_height(gui->canvas), 1); - canvas_draw_line( - gui->canvas, - canvas_width(gui->canvas) - 2, - 1, - canvas_width(gui->canvas) - 2, - canvas_height(gui->canvas) - 2); - canvas_draw_line( - gui->canvas, - 1, - canvas_height(gui->canvas) - 2, - canvas_width(gui->canvas) - 2, - canvas_height(gui->canvas) - 2); + canvas_set_color(gui->canvas, ColorWhite); + canvas_draw_box( + gui->canvas, -1, 0, canvas_width(gui->canvas) + 1, canvas_height(gui->canvas)); } + canvas_set_color(gui->canvas, ColorBlack); + // ViewPort draw + canvas_frame_set( + gui->canvas, + x - xtreme_settings->bar_borders, + GUI_STATUS_BAR_Y + 2, + width, + GUI_STATUS_BAR_WORKAREA_HEIGHT); + view_port_draw(view_port, gui->canvas); + } + ViewPortArray_next(it); + } + // Draw frame around icons on the right + if(right_used) { + canvas_frame_set( + gui->canvas, + GUI_DISPLAY_WIDTH - 4 - right_used, + GUI_STATUS_BAR_Y, + right_used + 4, + GUI_STATUS_BAR_HEIGHT); + // Disable battery border + if(xtreme_settings->bar_borders) { + canvas_set_color(gui->canvas, ColorBlack); + canvas_draw_rframe( + gui->canvas, 0, 0, canvas_width(gui->canvas), canvas_height(gui->canvas), 1); + canvas_draw_line( + gui->canvas, + canvas_width(gui->canvas) - 2, + 1, + canvas_width(gui->canvas) - 2, + canvas_height(gui->canvas) - 2); + canvas_draw_line( + gui->canvas, + 1, + canvas_height(gui->canvas) - 2, + canvas_width(gui->canvas) - 2, + canvas_height(gui->canvas) - 2); } } diff --git a/applications/services/gui/modules/byte_input.c b/applications/services/gui/modules/byte_input.c index b2d21f7ae3..554a7ca5cb 100644 --- a/applications/services/gui/modules/byte_input.c +++ b/applications/services/gui/modules/byte_input.c @@ -25,7 +25,7 @@ typedef struct { bool selected_high_nibble; uint8_t selected_byte; - int8_t selected_row; // row -1 - input, row 0 & 1 - keyboard + int8_t selected_row; // row -2 - mini_editor, -1 - input, row 0 & 1 - keyboard uint8_t selected_column; uint8_t first_visible_byte; } ByteInputModel; @@ -149,6 +149,8 @@ static char byte_input_get_nibble_text(uint8_t byte, bool high_nibble) { return byte; } +const char num_to_char[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}; + /** * @brief Draw input box (common view) * @@ -158,6 +160,10 @@ static char byte_input_get_nibble_text(uint8_t byte, bool high_nibble) { static void byte_input_draw_input(Canvas* canvas, ByteInputModel* model) { const uint8_t text_x = 8; const uint8_t text_y = 25; + const uint8_t text_y2 = 40; + const bool draw_index_line = + (model->selected_row == -2) && + (model->first_visible_byte + MIN(model->bytes_count, max_drawable_bytes + 1) <= 100); elements_slightly_rounded_frame(canvas, 6, 14, 116, 15); @@ -225,6 +231,27 @@ static void byte_input_draw_input(Canvas* canvas, ByteInputModel* model) { text_y, byte_input_get_nibble_text(model->bytes[i], false)); } + + if(draw_index_line) { + canvas_draw_glyph( + canvas, text_x + 2 + byte_position * 14, text_y2, num_to_char[(i + 1) / 10]); + + canvas_draw_glyph( + canvas, text_x + 8 + byte_position * 14, text_y2, num_to_char[(i + 1) % 10]); + } + } + + if((model->selected_row == -2) && + (model->first_visible_byte + MIN(model->bytes_count, max_drawable_bytes + 1) > 100)) { + char str[20]; + + canvas_set_font(canvas, FontSecondary); + snprintf(str, 20, "Selected index"); + canvas_draw_str(canvas, text_x, text_y2, str); + + canvas_set_font(canvas, FontPrimary); + snprintf(str, 20, "%u", (model->selected_byte + 1)); + canvas_draw_str(canvas, text_x + 75, text_y2, str); } if(model->bytes_count - model->first_visible_byte > max_drawable_bytes) { @@ -379,6 +406,17 @@ static void byte_input_inc_selected_byte(ByteInputModel* model) { } } +static void byte_input_inc_selected_byte_mini(ByteInputModel* model) { + if((model->selected_byte < model->bytes_count - 1) || model->selected_high_nibble) { + if(!model->selected_high_nibble) { + model->selected_high_nibble = !model->selected_high_nibble; + byte_input_inc_selected_byte(model); + } else { + model->selected_high_nibble = !model->selected_high_nibble; + } + } +} + /** * @brief Decrease selected byte position * @@ -397,6 +435,17 @@ static void byte_input_dec_selected_byte(ByteInputModel* model) { } } +static void byte_input_dec_selected_byte_mini(ByteInputModel* model) { + if(model->selected_byte > 0 || !model->selected_high_nibble) { + if(model->selected_high_nibble) { + model->selected_high_nibble = !model->selected_high_nibble; + byte_input_dec_selected_byte(model); + } else { + model->selected_high_nibble = !model->selected_high_nibble; + } + } +} + /** * @brief Call input callback * @@ -436,8 +485,18 @@ static void byte_input_clear_selected_byte(ByteInputModel* model) { * @param model */ static void byte_input_handle_up(ByteInputModel* model) { - if(model->selected_row > -1) { + if(model->selected_row > -2) { model->selected_row -= 1; + } else if(model->selected_row == -2) { + if(!model->selected_high_nibble) { + model->bytes[model->selected_byte] = (model->bytes[model->selected_byte] & 0xF0) | + ((model->bytes[model->selected_byte] + 1) & 0x0F); + } else { + model->bytes[model->selected_byte] = + ((model->bytes[model->selected_byte] + 0x10) & 0xF0) | + (model->bytes[model->selected_byte] & 0x0F); + } + byte_input_call_changed_callback(model); } } @@ -447,12 +506,24 @@ static void byte_input_handle_up(ByteInputModel* model) { * @param model */ static void byte_input_handle_down(ByteInputModel* model) { - if(byte_input_keyboard_selected(model)) { - if(model->selected_row < keyboard_row_count - 1) { - model->selected_row += 1; + if(model->selected_row != -2) { + if(byte_input_keyboard_selected(model)) { + if(model->selected_row < keyboard_row_count - 1) { + model->selected_row += 1; + } + } else { + byte_input_transition_from_keyboard(model); } } else { - byte_input_transition_from_keyboard(model); + if(!model->selected_high_nibble) { + model->bytes[model->selected_byte] = (model->bytes[model->selected_byte] & 0xF0) | + ((model->bytes[model->selected_byte] - 1) & 0x0F); + } else { + model->bytes[model->selected_byte] = + ((model->bytes[model->selected_byte] - 0x10) & 0xF0) | + (model->bytes[model->selected_byte] & 0x0F); + } + byte_input_call_changed_callback(model); } } @@ -461,7 +532,7 @@ static void byte_input_handle_down(ByteInputModel* model) { * * @param model */ -static void byte_input_handle_left(ByteInputModel* model) { +static void byte_input_handle_left(ByteInputModel* model) { // XXX if(byte_input_keyboard_selected(model)) { if(model->selected_column > 0) { model->selected_column -= 1; @@ -469,7 +540,11 @@ static void byte_input_handle_left(ByteInputModel* model) { model->selected_column = byte_input_get_row_size(model->selected_row) - 1; } } else { - byte_input_dec_selected_byte(model); + if(model->selected_row != -2) { + byte_input_dec_selected_byte(model); + } else { + byte_input_dec_selected_byte_mini(model); + } } } @@ -478,7 +553,7 @@ static void byte_input_handle_left(ByteInputModel* model) { * * @param model */ -static void byte_input_handle_right(ByteInputModel* model) { +static void byte_input_handle_right(ByteInputModel* model) { // XXX if(byte_input_keyboard_selected(model)) { if(model->selected_column < byte_input_get_row_size(model->selected_row) - 1) { model->selected_column += 1; @@ -486,7 +561,11 @@ static void byte_input_handle_right(ByteInputModel* model) { model->selected_column = 0; } } else { - byte_input_inc_selected_byte(model); + if(model->selected_row != -2) { + byte_input_inc_selected_byte(model); + } else { + byte_input_inc_selected_byte_mini(model); + } } } @@ -514,6 +593,8 @@ static void byte_input_handle_ok(ByteInputModel* model) { } byte_input_call_changed_callback(model); } + } else if(model->selected_row == -2) { + byte_input_call_input_callback(model); } else { byte_input_transition_from_keyboard(model); } @@ -541,68 +622,77 @@ static void byte_input_view_draw_callback(Canvas* canvas, void* _model) { byte_input_draw_input(canvas, model); } - for(uint8_t row = 0; row < keyboard_row_count; row++) { - const uint8_t column_count = byte_input_get_row_size(row); - const ByteInputKey* keys = byte_input_get_row(row); + if(model->selected_row == -2) { + canvas_set_font(canvas, FontSecondary); + canvas_draw_icon(canvas, 3, 52, &I_Pin_back_arrow_10x8); + canvas_draw_str_aligned(canvas, 16, 60, AlignLeft, AlignBottom, "back to keyboard"); + } else { + // Draw keyboard + for(uint8_t row = 0; row < keyboard_row_count; row++) { + const uint8_t column_count = byte_input_get_row_size(row); + const ByteInputKey* keys = byte_input_get_row(row); - for(size_t column = 0; column < column_count; column++) { - if(keys[column].value == enter_symbol) { - canvas_set_color(canvas, ColorBlack); - if(model->selected_row == row && model->selected_column == column) { - canvas_draw_icon( - canvas, - keyboard_origin_x + keys[column].x, - keyboard_origin_y + keys[column].y, - &I_KeySaveSelected_24x11); - } else { - canvas_draw_icon( - canvas, - keyboard_origin_x + keys[column].x, - keyboard_origin_y + keys[column].y, - &I_KeySave_24x11); - } - } else if(keys[column].value == backspace_symbol) { - canvas_set_color(canvas, ColorBlack); - if(model->selected_row == row && model->selected_column == column) { - canvas_draw_icon( - canvas, - keyboard_origin_x + keys[column].x, - keyboard_origin_y + keys[column].y, - &I_KeyBackspaceSelected_16x9); + for(size_t column = 0; column < column_count; column++) { + if(keys[column].value == enter_symbol) { + canvas_set_color(canvas, ColorBlack); + if(model->selected_row == row && model->selected_column == column) { + canvas_draw_icon( + canvas, + keyboard_origin_x + keys[column].x, + keyboard_origin_y + keys[column].y, + &I_KeySaveSelected_24x11); + } else { + canvas_draw_icon( + canvas, + keyboard_origin_x + keys[column].x, + keyboard_origin_y + keys[column].y, + &I_KeySave_24x11); + } + } else if(keys[column].value == backspace_symbol) { + canvas_set_color(canvas, ColorBlack); + if(model->selected_row == row && model->selected_column == column) { + canvas_draw_icon( + canvas, + keyboard_origin_x + keys[column].x, + keyboard_origin_y + keys[column].y, + &I_KeyBackspaceSelected_16x9); + } else { + canvas_draw_icon( + canvas, + keyboard_origin_x + keys[column].x, + keyboard_origin_y + keys[column].y, + &I_KeyBackspace_16x9); + } } else { - canvas_draw_icon( + if(model->selected_row == row && model->selected_column == column) { + canvas_set_color(canvas, ColorBlack); + canvas_draw_box( + canvas, + keyboard_origin_x + keys[column].x - 3, + keyboard_origin_y + keys[column].y - 10, + 11, + 13); + canvas_set_color(canvas, ColorWhite); + } else if( + model->selected_row == -1 && row == 0 && + model->selected_column == column) { + canvas_set_color(canvas, ColorBlack); + canvas_draw_frame( + canvas, + keyboard_origin_x + keys[column].x - 3, + keyboard_origin_y + keys[column].y - 10, + 11, + 13); + } else { + canvas_set_color(canvas, ColorBlack); + } + + canvas_draw_glyph( canvas, keyboard_origin_x + keys[column].x, keyboard_origin_y + keys[column].y, - &I_KeyBackspace_16x9); + keys[column].value); } - } else { - if(model->selected_row == row && model->selected_column == column) { - canvas_set_color(canvas, ColorBlack); - canvas_draw_box( - canvas, - keyboard_origin_x + keys[column].x - 3, - keyboard_origin_y + keys[column].y - 10, - 11, - 13); - canvas_set_color(canvas, ColorWhite); - } else if(model->selected_row == -1 && row == 0 && model->selected_column == column) { - canvas_set_color(canvas, ColorBlack); - canvas_draw_frame( - canvas, - keyboard_origin_x + keys[column].x - 3, - keyboard_origin_y + keys[column].y - 10, - 11, - 13); - } else { - canvas_set_color(canvas, ColorBlack); - } - - canvas_draw_glyph( - canvas, - keyboard_origin_x + keys[column].x, - keyboard_origin_y + keys[column].y, - keys[column].value); } } } @@ -656,6 +746,20 @@ static bool byte_input_view_input_callback(InputEvent* event, void* context) { } } + if(event->type == InputTypeShort && event->key == InputKeyBack) { + // Back to keyboard + with_view_model( + byte_input->view, + ByteInputModel * model, + { + if(model->selected_row == -2) { + model->selected_row += 1; + consumed = true; + }; + }, + true); + } + if((event->type == InputTypeLong || event->type == InputTypeRepeat) && event->key == InputKeyBack) { with_view_model( diff --git a/applications/services/gui/modules/file_browser.c b/applications/services/gui/modules/file_browser.c index 4f11ffe8d8..b547077f7f 100644 --- a/applications/services/gui/modules/file_browser.c +++ b/applications/services/gui/modules/file_browser.c @@ -147,6 +147,8 @@ typedef struct { const Icon* file_icon; bool hide_ext; size_t scroll_counter; + + int32_t button_held_for_ticks; } FileBrowserModel; static const Icon* BrowserItemIcons[] = { @@ -661,9 +663,31 @@ static bool file_browser_view_input_callback(InputEvent* event, void* context) { browser->view, FileBrowserModel * model, { + int32_t scroll_speed = 1; + if(model->button_held_for_ticks > 5) { + if(model->button_held_for_ticks % 2) { + scroll_speed = 0; + } else { + scroll_speed = model->button_held_for_ticks > 9 ? 5 : 3; + } + } else if(model->button_held_for_ticks < 0) { + scroll_speed = 0; + } + if(event->key == InputKeyUp) { + if(model->item_idx < scroll_speed) { + scroll_speed = model->item_idx; + if(scroll_speed == 0) { + if(model->button_held_for_ticks > 0) { + model->button_held_for_ticks = -1; + } else { + scroll_speed = 1; + } + } + } + model->item_idx = - ((model->item_idx - 1) + model->item_cnt) % model->item_cnt; + ((model->item_idx - scroll_speed) + model->item_cnt) % model->item_cnt; if(browser_is_list_load_required(model)) { model->list_loading = true; int32_t load_offset = CLAMP( @@ -674,8 +698,25 @@ static bool file_browser_view_input_callback(InputEvent* event, void* context) { browser->worker, load_offset, ITEM_LIST_LEN_MAX); } model->scroll_counter = 0; + + if(model->button_held_for_ticks < -1) { + model->button_held_for_ticks = 0; + } + model->button_held_for_ticks += 1; } else if(event->key == InputKeyDown) { - model->item_idx = (model->item_idx + 1) % model->item_cnt; + int32_t count = model->item_cnt; + if(model->item_idx + scroll_speed >= count) { + scroll_speed = count - model->item_idx - 1; + if(scroll_speed == 0) { + if(model->button_held_for_ticks > 0) { + model->button_held_for_ticks = -1; + } else { + scroll_speed = 1; + } + } + } + + model->item_idx = (model->item_idx + scroll_speed) % model->item_cnt; if(browser_is_list_load_required(model)) { model->list_loading = true; int32_t load_offset = CLAMP( @@ -686,11 +727,22 @@ static bool file_browser_view_input_callback(InputEvent* event, void* context) { browser->worker, load_offset, ITEM_LIST_LEN_MAX); } model->scroll_counter = 0; + + if(model->button_held_for_ticks < -1) { + model->button_held_for_ticks = 0; + } + model->button_held_for_ticks += 1; } }, false); browser_update_offset(browser); consumed = true; + } else if(event->type == InputTypeRelease) { + with_view_model( + browser->view, + FileBrowserModel * model, + { model->button_held_for_ticks = 0; }, + true); } } else if(event->key == InputKeyOk) { if(event->type == InputTypeShort) { diff --git a/applications/services/gui/modules/file_browser_worker.c b/applications/services/gui/modules/file_browser_worker.c index 326040020a..d740cc9d49 100644 --- a/applications/services/gui/modules/file_browser_worker.c +++ b/applications/services/gui/modules/file_browser_worker.c @@ -16,7 +16,7 @@ #define ASSETS_DIR "assets" #define BROWSER_ROOT STORAGE_ANY_PATH_PREFIX -#define FILE_NAME_LEN_MAX 256 +#define FILE_NAME_LEN_MAX 254 #define LONG_LOAD_THRESHOLD 100 typedef enum { @@ -32,8 +32,6 @@ typedef enum { (WorkerEvtStop | WorkerEvtLoad | WorkerEvtFolderEnter | WorkerEvtFolderExit | \ WorkerEvtFolderRefresh | WorkerEvtConfigChange) -ARRAY_DEF(idx_last_array, int32_t) - struct BrowserWorker { FuriThread* thread; @@ -41,12 +39,11 @@ struct BrowserWorker { FuriString* path_start; FuriString* path_current; FuriString* path_next; - int32_t item_sel_idx; + bool keep_selection; uint32_t load_offset; uint32_t load_count; bool skip_assets; bool hide_dot_files; - idx_last_array_t idx_last; void* cb_ctx; BrowserWorkerFolderOpenCallback folder_cb; @@ -328,7 +325,6 @@ static int32_t browser_worker(void* context) { uint32_t items_cnt = 0; FuriString* path; path = furi_string_alloc_set(BROWSER_ROOT); - browser->item_sel_idx = -1; FuriString* filename; filename = furi_string_alloc(); @@ -341,22 +337,47 @@ static int32_t browser_worker(void* context) { furi_assert((flags & FuriFlagError) == 0); if(flags & WorkerEvtConfigChange) { - // If start path is a path to the file - try finding index of this file in a folder - if(browser_path_is_file(browser->path_next)) { - path_extract_filename(browser->path_next, filename, false); - } - idx_last_array_reset(browser->idx_last); + if(browser->keep_selection && furi_string_start_with(path, browser->path_next)) { + // New path is parent of current, keep prev selected in new view + furi_string_set(filename, path); + furi_string_right(filename, furi_string_size(browser->path_next)); + furi_string_trim(filename, "/"); + size_t pos = furi_string_search_char(filename, '/'); + if(pos != FURI_STRING_FAILURE) { + furi_string_left(filename, pos); + } + + furi_string_set(path, browser->path_next); + bool is_root = browser_folder_check_and_switch(path); + + int32_t file_idx = 0; + browser_folder_init(browser, path, filename, &items_cnt, &file_idx); + furi_string_set(browser->path_current, path); + FURI_LOG_D( + TAG, + "Config to: %s items: %lu idx: %ld", + furi_string_get_cstr(path), + items_cnt, + file_idx); + if(browser->folder_cb) { + browser->folder_cb(browser->cb_ctx, items_cnt, file_idx, is_root); + } + furi_string_reset(filename); - furi_thread_flags_set(furi_thread_get_id(browser->thread), WorkerEvtFolderEnter); + } else { + // If start path is a path to the file - try finding index of this file in a folder + if(browser_path_is_file(browser->path_next)) { + path_extract_filename(browser->path_next, filename, false); + } + + furi_thread_flags_set(furi_thread_get_id(browser->thread), WorkerEvtFolderEnter); + } } if(flags & WorkerEvtFolderEnter) { furi_string_set(path, browser->path_next); bool is_root = browser_folder_check_and_switch(path); - // Push previous selected item index to history array - idx_last_array_push_back(browser->idx_last, browser->item_sel_idx); - int32_t file_idx = 0; browser_folder_init(browser, path, filename, &items_cnt, &file_idx); furi_string_set(browser->path_current, path); @@ -373,15 +394,13 @@ static int32_t browser_worker(void* context) { } if(flags & WorkerEvtFolderExit) { + path_extract_basename(furi_string_get_cstr(path), filename); + browser_path_trim(path); bool is_root = browser_folder_check_and_switch(path); int32_t file_idx = 0; browser_folder_init(browser, path, filename, &items_cnt, &file_idx); - if(idx_last_array_size(browser->idx_last) > 0) { - // Pop previous selected item index from history array - idx_last_array_pop_back(&file_idx, browser->idx_last); - } furi_string_set(browser->path_current, path); FURI_LOG_D( TAG, @@ -392,23 +411,26 @@ static int32_t browser_worker(void* context) { if(browser->folder_cb) { browser->folder_cb(browser->cb_ctx, items_cnt, file_idx, is_root); } + furi_string_reset(filename); } if(flags & WorkerEvtFolderRefresh) { + furi_string_set(filename, browser->path_next); + bool is_root = browser_folder_check_and_switch(path); int32_t file_idx = 0; - furi_string_reset(filename); browser_folder_init(browser, path, filename, &items_cnt, &file_idx); FURI_LOG_D( TAG, "Refresh folder: %s items: %lu idx: %ld", furi_string_get_cstr(path), items_cnt, - browser->item_sel_idx); + file_idx); if(browser->folder_cb) { - browser->folder_cb(browser->cb_ctx, items_cnt, browser->item_sel_idx, is_root); + browser->folder_cb(browser->cb_ctx, items_cnt, file_idx, is_root); } + furi_string_reset(filename); } if(flags & WorkerEvtLoad) { @@ -442,8 +464,6 @@ BrowserWorker* file_browser_worker_alloc( bool hide_dot_files) { BrowserWorker* browser = malloc(sizeof(BrowserWorker)); - idx_last_array_init(browser->idx_last); - browser->filter_extension = furi_string_alloc_set(filter_ext); browser->skip_assets = skip_assets; browser->hide_dot_files = hide_dot_files; @@ -474,8 +494,6 @@ void file_browser_worker_free(BrowserWorker* browser) { furi_string_free(browser->path_current); furi_string_free(browser->path_start); - idx_last_array_clear(browser->idx_last); - free(browser); } @@ -520,16 +538,33 @@ void file_browser_worker_set_config( bool hide_dot_files) { furi_assert(browser); furi_string_set(browser->path_next, path); + browser->keep_selection = false; furi_string_set(browser->filter_extension, filter_ext); browser->skip_assets = skip_assets; browser->hide_dot_files = hide_dot_files; furi_thread_flags_set(furi_thread_get_id(browser->thread), WorkerEvtConfigChange); } +const char* file_browser_worker_get_filter_ext(BrowserWorker* browser) { + furi_assert(browser); + return furi_string_get_cstr(browser->filter_extension); +} + +void file_browser_worker_set_filter_ext( + BrowserWorker* browser, + FuriString* path, + const char* filter_ext) { + furi_assert(browser); + furi_string_set(browser->path_next, path); + browser->keep_selection = true; + furi_string_set(browser->filter_extension, filter_ext); + furi_thread_flags_set(furi_thread_get_id(browser->thread), WorkerEvtConfigChange); +} + void file_browser_worker_folder_enter(BrowserWorker* browser, FuriString* path, int32_t item_idx) { furi_assert(browser); furi_string_set(browser->path_next, path); - browser->item_sel_idx = item_idx; + UNUSED(item_idx); furi_thread_flags_set(furi_thread_get_id(browser->thread), WorkerEvtFolderEnter); } @@ -543,9 +578,13 @@ void file_browser_worker_folder_exit(BrowserWorker* browser) { furi_thread_flags_set(furi_thread_get_id(browser->thread), WorkerEvtFolderExit); } -void file_browser_worker_folder_refresh(BrowserWorker* browser, int32_t item_idx) { +void file_browser_worker_folder_refresh(BrowserWorker* browser, const char* item_name) { furi_assert(browser); - browser->item_sel_idx = item_idx; + if(item_name != NULL) { + furi_string_set(browser->path_next, item_name); + } else { + furi_string_reset(browser->path_next); + } furi_thread_flags_set(furi_thread_get_id(browser->thread), WorkerEvtFolderRefresh); } diff --git a/applications/services/gui/modules/file_browser_worker.h b/applications/services/gui/modules/file_browser_worker.h index 859b11be45..b4b7310925 100644 --- a/applications/services/gui/modules/file_browser_worker.h +++ b/applications/services/gui/modules/file_browser_worker.h @@ -58,13 +58,20 @@ void file_browser_worker_set_config( bool skip_assets, bool hide_dot_files); +const char* file_browser_worker_get_filter_ext(BrowserWorker* browser); + +void file_browser_worker_set_filter_ext( + BrowserWorker* browser, + FuriString* path, + const char* filter_ext); + void file_browser_worker_folder_enter(BrowserWorker* browser, FuriString* path, int32_t item_idx); bool file_browser_worker_is_in_start_folder(BrowserWorker* browser); void file_browser_worker_folder_exit(BrowserWorker* browser); -void file_browser_worker_folder_refresh(BrowserWorker* browser, int32_t item_idx); +void file_browser_worker_folder_refresh(BrowserWorker* browser, const char* item_name); void file_browser_worker_load(BrowserWorker* browser, uint32_t offset, uint32_t count); diff --git a/applications/services/gui/modules/submenu.c b/applications/services/gui/modules/submenu.c index 5f47ac179c..55f116c7a9 100644 --- a/applications/services/gui/modules/submenu.c +++ b/applications/services/gui/modules/submenu.c @@ -76,7 +76,7 @@ static void submenu_view_draw_callback(Canvas* canvas, void* _model) { SubmenuModel* model = _model; const uint8_t item_height = 16; - uint8_t item_width = 123; + uint8_t item_width = canvas_width(canvas) - 5; if(canvas->orientation == CanvasOrientationVertical || canvas->orientation == CanvasOrientationVerticalFlip) { diff --git a/applications/services/gui/modules/text_box.c b/applications/services/gui/modules/text_box.c index 6dc5588156..77d30b8eb1 100644 --- a/applications/services/gui/modules/text_box.c +++ b/applications/services/gui/modules/text_box.c @@ -6,6 +6,8 @@ struct TextBox { View* view; + + uint16_t button_held_for_ticks; }; typedef struct { @@ -19,36 +21,52 @@ typedef struct { bool formatted; } TextBoxModel; -static void text_box_process_down(TextBox* text_box) { +static void text_box_process_down(TextBox* text_box, uint8_t lines) { with_view_model( text_box->view, TextBoxModel * model, { - if(model->scroll_pos < model->scroll_num - 1) { - model->scroll_pos++; - // Search next line start - while(*model->text_pos++ != '\n') - ; + if(model->scroll_pos < model->scroll_num - lines) { + model->scroll_pos += lines; + for(uint8_t i = 0; i < lines; i++) { + // Search next line start + while(*model->text_pos++ != '\n') + ; + } + } else if(lines > 1) { + lines = model->scroll_num - model->scroll_pos - 1; + model->scroll_pos = model->scroll_num - 1; + for(uint8_t i = 0; i < lines; i++) { + // Search next line start + while(*model->text_pos++ != '\n') + ; + } } }, true); } -static void text_box_process_up(TextBox* text_box) { +static void text_box_process_up(TextBox* text_box, uint8_t lines) { with_view_model( text_box->view, TextBoxModel * model, { - if(model->scroll_pos > 0) { - model->scroll_pos--; - // Reach last symbol of previous line - model->text_pos--; - // Search previous line start - while((model->text_pos != model->text) && (*(--model->text_pos) != '\n')) - ; - if(*model->text_pos == '\n') { - model->text_pos++; + if(model->scroll_pos > lines - 1) { + model->scroll_pos -= lines; + for(uint8_t i = 0; i < lines; i++) { + // Reach last symbol of previous line + model->text_pos--; + // Search previous line start + while((model->text_pos != model->text) && (*(--model->text_pos) != '\n')) + ; + if(*model->text_pos == '\n') { + model->text_pos++; + } } + } else if(lines > 1) { + lines = model->scroll_pos; + model->scroll_pos = 0; + model->text_pos = (char*)model->text; } }, true); @@ -120,14 +138,28 @@ static bool text_box_view_input_callback(InputEvent* event, void* context) { TextBox* text_box = context; bool consumed = false; - if(event->type == InputTypeShort) { + if(event->type == InputTypeShort || event->type == InputTypeRepeat) { + int32_t scroll_speed = 1; + if(text_box->button_held_for_ticks > 5) { + if(text_box->button_held_for_ticks % 2) { + scroll_speed = 0; + } else { + scroll_speed = text_box->button_held_for_ticks > 9 ? 5 : 3; + } + } + if(event->key == InputKeyDown) { - text_box_process_down(text_box); + text_box_process_down(text_box, scroll_speed); consumed = true; } else if(event->key == InputKeyUp) { - text_box_process_up(text_box); + text_box_process_up(text_box, scroll_speed); consumed = true; } + + text_box->button_held_for_ticks++; + } else if(event->type == InputTypeRelease) { + text_box->button_held_for_ticks = 0; + consumed = true; } return consumed; } diff --git a/applications/services/gui/modules/widget.c b/applications/services/gui/modules/widget.c index 21b8e56162..5174a556a8 100644 --- a/applications/services/gui/modules/widget.c +++ b/applications/services/gui/modules/widget.c @@ -119,7 +119,7 @@ static void widget_add_element(Widget* widget, WidgetElement* element) { true); } -void widget_add_string_multiline_element( +WidgetElement* widget_add_string_multiline_element( Widget* widget, uint8_t x, uint8_t y, @@ -131,9 +131,10 @@ void widget_add_string_multiline_element( WidgetElement* string_multiline_element = widget_element_string_multiline_create(x, y, horizontal, vertical, font, text); widget_add_element(widget, string_multiline_element); + return string_multiline_element; } -void widget_add_string_element( +WidgetElement* widget_add_string_element( Widget* widget, uint8_t x, uint8_t y, @@ -145,9 +146,10 @@ void widget_add_string_element( WidgetElement* string_element = widget_element_string_create(x, y, horizontal, vertical, font, text); widget_add_element(widget, string_element); + return string_element; } -void widget_add_text_box_element( +WidgetElement* widget_add_text_box_element( Widget* widget, uint8_t x, uint8_t y, @@ -161,9 +163,10 @@ void widget_add_text_box_element( WidgetElement* text_box_element = widget_element_text_box_create( x, y, width, height, horizontal, vertical, text, strip_to_dots); widget_add_element(widget, text_box_element); + return text_box_element; } -void widget_add_text_scroll_element( +WidgetElement* widget_add_text_scroll_element( Widget* widget, uint8_t x, uint8_t y, @@ -174,9 +177,10 @@ void widget_add_text_scroll_element( WidgetElement* text_scroll_element = widget_element_text_scroll_create(x, y, width, height, text); widget_add_element(widget, text_scroll_element); + return text_scroll_element; } -void widget_add_button_element( +WidgetElement* widget_add_button_element( Widget* widget, GuiButtonType button_type, const char* text, @@ -186,16 +190,18 @@ void widget_add_button_element( WidgetElement* button_element = widget_element_button_create(button_type, text, callback, context); widget_add_element(widget, button_element); + return button_element; } -void widget_add_icon_element(Widget* widget, uint8_t x, uint8_t y, const Icon* icon) { +WidgetElement* widget_add_icon_element(Widget* widget, uint8_t x, uint8_t y, const Icon* icon) { furi_assert(widget); furi_assert(icon); WidgetElement* icon_element = widget_element_icon_create(x, y, icon); widget_add_element(widget, icon_element); + return icon_element; } -void widget_add_frame_element( +WidgetElement* widget_add_frame_element( Widget* widget, uint8_t x, uint8_t y, @@ -205,4 +211,5 @@ void widget_add_frame_element( furi_assert(widget); WidgetElement* frame_element = widget_element_frame_create(x, y, width, height, radius); widget_add_element(widget, frame_element); + return frame_element; } diff --git a/applications/services/gui/modules/widget.h b/applications/services/gui/modules/widget.h index f86b14cc9e..88d0dd6678 100644 --- a/applications/services/gui/modules/widget.h +++ b/applications/services/gui/modules/widget.h @@ -51,7 +51,7 @@ View* widget_get_view(Widget* widget); * @param font Font instance * @param[in] text The text */ -void widget_add_string_multiline_element( +WidgetElement* widget_add_string_multiline_element( Widget* widget, uint8_t x, uint8_t y, @@ -70,7 +70,7 @@ void widget_add_string_multiline_element( * @param font Font instance * @param[in] text The text */ -void widget_add_string_element( +WidgetElement* widget_add_string_element( Widget* widget, uint8_t x, uint8_t y, @@ -94,7 +94,7 @@ void widget_add_string_element( * "\e!Inverted text\e!" - white text on black background * @param strip_to_dots Strip text to ... if does not fit to width */ -void widget_add_text_box_element( +WidgetElement* widget_add_text_box_element( Widget* widget, uint8_t x, uint8_t y, @@ -119,7 +119,7 @@ void widget_add_text_box_element( * "\ecCenter-aligned text" - sets center horizontal align until the next '\n' symbol * "\erRight-aligned text" - sets right horizontal align until the next '\n' symbol */ -void widget_add_text_scroll_element( +WidgetElement* widget_add_text_scroll_element( Widget* widget, uint8_t x, uint8_t y, @@ -135,7 +135,7 @@ void widget_add_text_scroll_element( * @param callback ButtonCallback instance * @param context pointer to context */ -void widget_add_button_element( +WidgetElement* widget_add_button_element( Widget* widget, GuiButtonType button_type, const char* text, @@ -149,7 +149,7 @@ void widget_add_button_element( * @param y top left y coordinate * @param icon Icon instance */ -void widget_add_icon_element(Widget* widget, uint8_t x, uint8_t y, const Icon* icon); +WidgetElement* widget_add_icon_element(Widget* widget, uint8_t x, uint8_t y, const Icon* icon); /** Add Frame Element * @@ -160,7 +160,7 @@ void widget_add_icon_element(Widget* widget, uint8_t x, uint8_t y, const Icon* i * @param height frame height * @param radius frame radius */ -void widget_add_frame_element( +WidgetElement* widget_add_frame_element( Widget* widget, uint8_t x, uint8_t y, diff --git a/applications/services/gui/modules/widget_elements/widget_element_i.h b/applications/services/gui/modules/widget_elements/widget_element_i.h index 67dea4b1f6..72f4b4ef1f 100644 --- a/applications/services/gui/modules/widget_elements/widget_element_i.h +++ b/applications/services/gui/modules/widget_elements/widget_element_i.h @@ -61,6 +61,9 @@ WidgetElement* widget_element_text_box_create( const char* text, bool strip_to_dots); +/** Update text box element */ +void widget_element_text_box_set_text(WidgetElement* gui_string, const char* text); + /** Create button element */ WidgetElement* widget_element_button_create( GuiButtonType button_type, @@ -88,4 +91,4 @@ WidgetElement* widget_element_text_scroll_create( #ifdef __cplusplus } -#endif \ No newline at end of file +#endif diff --git a/applications/services/gui/modules/widget_elements/widget_element_text_box.c b/applications/services/gui/modules/widget_elements/widget_element_text_box.c index 98f8e83d82..552025f960 100644 --- a/applications/services/gui/modules/widget_elements/widget_element_text_box.c +++ b/applications/services/gui/modules/widget_elements/widget_element_text_box.c @@ -72,3 +72,8 @@ WidgetElement* widget_element_text_box_create( return gui_string; } //-V773 + +void widget_element_text_box_set_text(WidgetElement* gui_string, const char* text) { + GuiTextBoxModel* model = gui_string->model; + furi_string_set(model->text, text); +} diff --git a/applications/services/dialogs/view_holder.c b/applications/services/gui/view_holder.c similarity index 100% rename from applications/services/dialogs/view_holder.c rename to applications/services/gui/view_holder.c diff --git a/applications/services/dialogs/view_holder.h b/applications/services/gui/view_holder.h similarity index 100% rename from applications/services/dialogs/view_holder.h rename to applications/services/gui/view_holder.h diff --git a/applications/services/loader/loader.c b/applications/services/loader/loader.c index ff187e99e9..f8fe9f699d 100644 --- a/applications/services/loader/loader.c +++ b/applications/services/loader/loader.c @@ -1,26 +1,30 @@ #include "loader.h" #include "loader_i.h" -#include "loader_menu.h" -#include "loader_preload.h" #include +#include #include -#include -#include -#include -#include "applications/main/fap_loader/fap_loader_app.h" #include +#include +#include +#include +#include +#include +#include + #define TAG "Loader" #define LOADER_MAGIC_THREAD_VALUE 0xDEADBEEF // api -LoaderStatus loader_start(Loader* loader, const char* name, const char* args) { +LoaderStatus + loader_start(Loader* loader, const char* name, const char* args, FuriString* error_message) { LoaderMessage message; LoaderMessageLoaderStatusResult result; message.type = LoaderMessageTypeStartByName; message.start.name = name; message.start.args = args; + message.start.error_message = error_message; message.api_lock = api_lock_alloc_locked(); message.status_value = &result; furi_message_queue_put(loader->queue, &message, FuriWaitForever); @@ -28,6 +32,42 @@ LoaderStatus loader_start(Loader* loader, const char* name, const char* args) { return result.value; } +static void loader_show_gui_error(LoaderStatus status, FuriString* error_message) { + // TODO: we have many places where we can emit a double start, ex: desktop, menu + // so i prefer to not show LoaderStatusErrorAppStarted error message for now + if(status == LoaderStatusErrorUnknownApp || status == LoaderStatusErrorInternal) { + DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); + DialogMessage* message = dialog_message_alloc(); + dialog_message_set_header(message, "Error", 64, 0, AlignCenter, AlignTop); + dialog_message_set_buttons(message, NULL, NULL, NULL); + + furi_string_replace(error_message, ":", "\n"); + dialog_message_set_text( + message, furi_string_get_cstr(error_message), 64, 32, AlignCenter, AlignCenter); + + dialog_message_show(dialogs, message); + dialog_message_free(message); + furi_record_close(RECORD_DIALOGS); + } +} + +LoaderStatus loader_start_with_gui_error(Loader* loader, const char* name, const char* args) { + FuriString* error_message = furi_string_alloc(); + LoaderStatus status = loader_start(loader, name, args, error_message); + loader_show_gui_error(status, error_message); + furi_string_free(error_message); + return status; +} + +void loader_start_detached_with_gui_error(Loader* loader, const char* name, const char* args) { + LoaderMessage message; + + message.type = LoaderMessageTypeStartByNameDetachedWithGuiError; + message.start.name = name; + message.start.args = args; + furi_message_queue_put(loader->queue, &message, FuriWaitForever); +} + bool loader_lock(Loader* loader) { LoaderMessage message; LoaderMessageBoolResult result; @@ -76,6 +116,11 @@ FuriPubSub* loader_get_pubsub(Loader* loader) { return loader->pubsub; } +ExtMainAppList_t* loader_get_ext_main_apps(Loader* loader) { + furi_assert(loader); + return &loader->ext_main_apps; +} + // callbacks static void loader_menu_closed_callback(void* context) { @@ -85,34 +130,31 @@ static void loader_menu_closed_callback(void* context) { furi_message_queue_put(loader->queue, &message, FuriWaitForever); } -static void loader_menu_click_callback(const char* name, bool external, void* context) { +static void loader_applications_closed_callback(void* context) { Loader* loader = context; - if(external) { - loader_start(loader, FAP_LOADER_APP_NAME, name); - } else { - loader_start(loader, name, NULL); - } + LoaderMessage message; + message.type = LoaderMessageTypeApplicationsClosed; + furi_message_queue_put(loader->queue, &message, FuriWaitForever); } static void loader_thread_state_callback(FuriThreadState thread_state, void* context) { furi_assert(context); Loader* loader = context; - LoaderEvent event; if(thread_state == FuriThreadStateRunning) { + LoaderEvent event; event.type = LoaderEventTypeApplicationStarted; furi_pubsub_publish(loader->pubsub, &event); } else if(thread_state == FuriThreadStateStopped) { LoaderMessage message; message.type = LoaderMessageTypeAppClosed; furi_message_queue_put(loader->queue, &message, FuriWaitForever); - - event.type = LoaderEventTypeApplicationStopped; - furi_pubsub_publish(loader->pubsub, &event); } } +// implementation + bool loader_menu_load_fap_meta( Storage* storage, FuriString* path, @@ -120,7 +162,7 @@ bool loader_menu_load_fap_meta( const Icon** icon) { *icon = NULL; uint8_t* icon_buf = malloc(CUSTOM_ICON_MAX_SIZE); - if(!fap_loader_load_name_and_icon(path, storage, &icon_buf, name)) { + if(!flipper_application_load_name_and_icon(path, storage, &icon_buf, name)) { free(icon_buf); icon_buf = NULL; return false; @@ -135,28 +177,20 @@ bool loader_menu_load_fap_meta( return true; } -// implementation - static Loader* loader_alloc() { Loader* loader = malloc(sizeof(Loader)); loader->pubsub = furi_pubsub_alloc(); loader->queue = furi_message_queue_alloc(1, sizeof(LoaderMessage)); loader->loader_menu = NULL; + loader->loader_applications = NULL; loader->app.args = NULL; - loader->app.name = NULL; loader->app.thread = NULL; loader->app.insomniac = false; + loader->app.fap = NULL; ExtMainAppList_init(loader->ext_main_apps); if(furi_hal_is_normal_boot()) { Storage* storage = furi_record_open(RECORD_STORAGE); - for(size_t i = 0; i < FLIPPER_APPS_COUNT; i++) { - if(FLIPPER_APPS[i].app != NULL || FLIPPER_APPS[i].stack_size != 1) continue; - if(storage_common_exists(storage, FLIPPER_APPS[i].appid)) { - void* preload = loader_preload(storage, FLIPPER_APPS[i].appid); - FLIPPER_APPS[i].preload = preload; - } - } FuriString* path = furi_string_alloc(); FuriString* name = furi_string_alloc(); Stream* stream = file_stream_alloc(storage); @@ -183,9 +217,9 @@ static Loader* loader_alloc() { return loader; } -static FlipperApplication const* loader_find_application_by_name_in_list( +static FlipperInternalApplication const* loader_find_application_by_name_in_list( const char* name, - const FlipperApplication* list, + const FlipperInternalApplication* list, const uint32_t n_apps) { for(size_t i = 0; i < n_apps; i++) { if(strcmp(name, list[i].name) == 0) { @@ -195,17 +229,8 @@ static FlipperApplication const* loader_find_application_by_name_in_list( return NULL; } -static const FlipperApplication* loader_find_application_by_name(const char* name) { - if(!strncmp(name, "Bad USB", strlen("Bad USB"))) - name = "Bad KB"; - else if(!strncmp(name, "Applications", strlen("Applications"))) - name = "Apps"; - else if(!strncmp(name, "125 kHz RFID", strlen("125 kHz RFID"))) - name = "RFID"; - else if(!strncmp(name, "Sub-GHz", strlen("Sub-GHz"))) - name = "SubGHz"; - - const FlipperApplication* application = NULL; +static const FlipperInternalApplication* loader_find_application_by_name(const char* name) { + const FlipperInternalApplication* application = NULL; application = loader_find_application_by_name_in_list(name, FLIPPER_APPS, FLIPPER_APPS_COUNT); if(!application) { application = loader_find_application_by_name_in_list( @@ -219,35 +244,7 @@ static const FlipperApplication* loader_find_application_by_name(const char* nam return application; } -static void - loader_start_internal_app(Loader* loader, const FlipperApplication* app, const char* args) { - FURI_LOG_I(TAG, "Starting %s", app->name); - - if(app->app == NULL) { - if(app->preload != NULL) { - loader_preload_start(app->preload, app->appid); - return; - } - args = app->appid; - app = loader_find_application_by_name_in_list( - FAP_LOADER_APP_NAME, FLIPPER_APPS, FLIPPER_APPS_COUNT); - } - - // store args - furi_assert(loader->app.args == NULL); - if(args && strlen(args) > 0) { - loader->app.args = strdup(args); - } - - // store name - furi_assert(loader->app.name == NULL); - loader->app.name = strdup(app->name); - - // setup app thread - loader->app.thread = - furi_thread_alloc_ex(app->name, app->stack_size, app->app, loader->app.args); - furi_thread_set_appid(loader->app.thread, app->appid); - +static void loader_start_app_thread(Loader* loader, FlipperInternalApplicationFlag flags) { // setup heap trace FuriHalRtcHeapTrackMode mode = furi_hal_rtc_get_heap_track_mode(); if(mode > FuriHalRtcHeapTrackModeNone) { @@ -257,14 +254,14 @@ static void } // setup insomnia - if(!(app->flags & FlipperApplicationFlagInsomniaSafe)) { + if(!(flags & FlipperInternalApplicationFlagInsomniaSafe)) { furi_hal_power_insomnia_enter(); loader->app.insomniac = true; } else { loader->app.insomniac = false; } - // setup app thread callbacks + // setup thread state callbacks furi_thread_set_state_context(loader->app.thread, loader); furi_thread_set_state_callback(loader->app.thread, loader_thread_state_callback); @@ -272,42 +269,245 @@ static void furi_thread_start(loader->app.thread); } +static void loader_start_internal_app( + Loader* loader, + const FlipperInternalApplication* app, + const char* args) { + FURI_LOG_I(TAG, "Starting %s", app->name); + + // store args + furi_assert(loader->app.args == NULL); + if(args && strlen(args) > 0) { + loader->app.args = strdup(args); + } + + loader->app.thread = + furi_thread_alloc_ex(app->name, app->stack_size, app->app, loader->app.args); + furi_thread_set_appid(loader->app.thread, app->appid); + + loader_start_app_thread(loader, app->flags); +} + +static void loader_log_status_error( + LoaderStatus status, + FuriString* error_message, + const char* format, + va_list args) { + if(error_message) { + furi_string_vprintf(error_message, format, args); + FURI_LOG_E(TAG, "Status [%d]: %s", status, furi_string_get_cstr(error_message)); + } else { + FuriString* tmp = furi_string_alloc(); + FURI_LOG_E(TAG, "Status [%d]: %s", status, furi_string_get_cstr(tmp)); + furi_string_free(tmp); + } +} + +static LoaderStatus loader_make_status_error( + LoaderStatus status, + FuriString* error_message, + const char* format, + ...) { + va_list args; + va_start(args, format); + loader_log_status_error(status, error_message, format, args); + va_end(args); + return status; +} + +static LoaderStatus loader_make_success_status(FuriString* error_message) { + if(error_message) { + furi_string_set(error_message, "App started"); + } + + return LoaderStatusOk; +} + +static LoaderStatus loader_start_external_app( + Loader* loader, + Storage* storage, + const char* path, + const char* args, + FuriString* error_message) { + LoaderStatus status = loader_make_success_status(error_message); + + do { + loader->app.fap = flipper_application_alloc(storage, firmware_api_interface); + size_t start = furi_get_tick(); + + FURI_LOG_I(TAG, "Loading %s", path); + + FlipperApplicationPreloadStatus preload_res = + flipper_application_preload(loader->app.fap, path); + bool api_mismatch = false; + if(preload_res == FlipperApplicationPreloadStatusApiMismatch) { + api_mismatch = true; + } else if(preload_res != FlipperApplicationPreloadStatusSuccess) { + const char* err_msg = flipper_application_preload_status_to_string(preload_res); + status = loader_make_status_error( + LoaderStatusErrorInternal, error_message, "Preload failed %s: %s", path, err_msg); + break; + } + + FURI_LOG_I(TAG, "Mapping"); + FlipperApplicationLoadStatus load_status = + flipper_application_map_to_memory(loader->app.fap); + FURI_LOG_I(TAG, "Loaded in %zums", (size_t)(furi_get_tick() - start)); + if(load_status != FlipperApplicationLoadStatusSuccess) { + const char* err_msg = flipper_application_load_status_to_string(load_status); + status = loader_make_status_error( + LoaderStatusErrorInternal, error_message, "Load failed %s: %s", path, err_msg); + break; + } else if(api_mismatch) { + // Successful map, but found api mismatch -> warn user + DialogMessage* message = dialog_message_alloc(); + dialog_message_set_header(message, "API Mismatch", 64, 0, AlignCenter, AlignTop); + dialog_message_set_buttons(message, "Cancel", NULL, "Continue"); + dialog_message_set_text( + message, + "This app might not\nwork correctly\nContinue anyways?", + 64, + 32, + AlignCenter, + AlignCenter); + DialogMessageButton res = + dialog_message_show(furi_record_open(RECORD_DIALOGS), message); + dialog_message_free(message); + furi_record_close(RECORD_DIALOGS); + if(res != DialogMessageButtonRight) { + break; + } + } + + FURI_LOG_I(TAG, "Starting app"); + + loader->app.thread = flipper_application_alloc_thread(loader->app.fap, args); + FuriString* app_name = furi_string_alloc(); + path_extract_filename_no_ext(path, app_name); + furi_thread_set_appid(loader->app.thread, furi_string_get_cstr(app_name)); + furi_string_free(app_name); + + /* This flag is set by the debugger - to break on app start */ + if(furi_hal_debug_is_gdb_session_active()) { + FURI_LOG_W(TAG, "Triggering BP for debugger"); + /* After hitting this, you can set breakpoints in your .fap's code + * Note that you have to toggle breakpoints that were set before */ + __asm volatile("bkpt 0"); + } + + loader_start_app_thread(loader, FlipperInternalApplicationFlagDefault); + + return status; + } while(0); + + flipper_application_free(loader->app.fap); + loader->app.fap = NULL; + + return status; +} + // process messages static void loader_do_menu_show(Loader* loader, bool settings) { if(!loader->loader_menu) { - loader->loader_menu = loader_menu_alloc(); - loader_menu_set_closed_callback(loader->loader_menu, loader_menu_closed_callback, loader); - loader_menu_set_click_callback(loader->loader_menu, loader_menu_click_callback, loader); - loader_menu_start(loader->loader_menu, settings, &loader->ext_main_apps); + loader->loader_menu = loader_menu_alloc(loader_menu_closed_callback, loader, settings); } } static void loader_do_menu_closed(Loader* loader) { if(loader->loader_menu) { - loader_menu_stop(loader->loader_menu); loader_menu_free(loader->loader_menu); loader->loader_menu = NULL; } } +static void loader_do_applications_show(Loader* loader) { + if(!loader->loader_applications) { + loader->loader_applications = + loader_applications_alloc(loader_applications_closed_callback, loader); + } +} + +static void loader_do_applications_closed(Loader* loader) { + if(loader->loader_applications) { + loader_applications_free(loader->loader_applications); + loader->loader_applications = NULL; + } +} + static bool loader_do_is_locked(Loader* loader) { return loader->app.thread != NULL; } -static LoaderStatus loader_do_start_by_name(Loader* loader, const char* name, const char* args) { - if(loader_do_is_locked(loader)) { - return LoaderStatusErrorAppStarted; - } +static LoaderStatus loader_do_start_by_name( + Loader* loader, + const char* name, + const char* args, + FuriString* error_message) { + LoaderStatus status; + do { + // check lock + if(loader_do_is_locked(loader)) { + const char* current_thread_name = + furi_thread_get_name(furi_thread_get_id(loader->app.thread)); + status = loader_make_status_error( + LoaderStatusErrorAppStarted, + error_message, + "Loader is locked, please close the \"%s\" first", + current_thread_name); + break; + } + + // Translate app names (mainly for RPC, thanks OFW for not using a smart system like appid's :/) + if(!strncmp(name, "Bad USB", strlen("Bad USB"))) + name = "Bad KB"; + else if(!strncmp(name, "Applications", strlen("Applications"))) + name = "Apps"; + else if(!strncmp(name, "125 kHz RFID", strlen("125 kHz RFID"))) + name = "RFID"; + else if(!strncmp(name, "Sub-GHz", strlen("Sub-GHz"))) + name = "SubGHz"; + + // check internal apps + { + const FlipperInternalApplication* app = loader_find_application_by_name(name); + if(app) { + if(app->app == NULL) { + // FAPP support + status = loader_start_external_app( + loader, furi_record_open(RECORD_STORAGE), app->appid, args, error_message); + furi_record_close(RECORD_STORAGE); + } else { + loader_start_internal_app(loader, app, args); + status = loader_make_success_status(error_message); + } + break; + } + } - const FlipperApplication* app = loader_find_application_by_name(name); + // check Applications + if(strcmp(name, LOADER_APPLICATIONS_NAME) == 0) { + loader_do_applications_show(loader); + status = loader_make_success_status(error_message); + break; + } - if(!app) { - return LoaderStatusErrorUnknownApp; - } + // check external apps + { + Storage* storage = furi_record_open(RECORD_STORAGE); + if(storage_file_exists(storage, name)) { + status = loader_start_external_app(loader, storage, name, args, error_message); + furi_record_close(RECORD_STORAGE); + break; + } + furi_record_close(RECORD_STORAGE); + } - loader_start_internal_app(loader, app, args); - return LoaderStatusOk; + status = loader_make_status_error( + LoaderStatusErrorUnknownApp, error_message, "Application \"%s\" not found", name); + } while(false); + + return status; } static bool loader_do_lock(Loader* loader) { @@ -320,13 +520,16 @@ static bool loader_do_lock(Loader* loader) { } static void loader_do_unlock(Loader* loader) { - furi_assert(loader->app.thread == (FuriThread*)LOADER_MAGIC_THREAD_VALUE); + furi_check(loader->app.thread == (FuriThread*)LOADER_MAGIC_THREAD_VALUE); loader->app.thread = NULL; } static void loader_do_app_closed(Loader* loader) { furi_assert(loader->app.thread); - FURI_LOG_I(TAG, "Application stopped. Free heap: %zu", memmgr_get_free_heap()); + + furi_thread_join(loader->app.thread); + FURI_LOG_I(TAG, "App returned: %li", furi_thread_get_return_code(loader->app.thread)); + if(loader->app.args) { free(loader->app.args); loader->app.args = NULL; @@ -336,12 +539,20 @@ static void loader_do_app_closed(Loader* loader) { furi_hal_power_insomnia_exit(); } - free(loader->app.name); - loader->app.name = NULL; + if(loader->app.fap) { + flipper_application_free(loader->app.fap); + loader->app.fap = NULL; + loader->app.thread = NULL; + } else { + furi_thread_free(loader->app.thread); + loader->app.thread = NULL; + } - furi_thread_join(loader->app.thread); - furi_thread_free(loader->app.thread); - loader->app.thread = NULL; + FURI_LOG_I(TAG, "Application stopped. Free heap: %zu", memmgr_get_free_heap()); + + LoaderEvent event; + event.type = LoaderEventTypeApplicationStopped; + furi_pubsub_publish(loader->pubsub, &event); } // app @@ -357,7 +568,7 @@ int32_t loader_srv(void* p) { } if(FLIPPER_AUTORUN_APP_NAME && strlen(FLIPPER_AUTORUN_APP_NAME)) { - loader_do_start_by_name(loader, FLIPPER_AUTORUN_APP_NAME, NULL); + loader_do_start_by_name(loader, FLIPPER_AUTORUN_APP_NAME, NULL, NULL); } LoaderMessage message; @@ -365,10 +576,18 @@ int32_t loader_srv(void* p) { if(furi_message_queue_get(loader->queue, &message, FuriWaitForever) == FuriStatusOk) { switch(message.type) { case LoaderMessageTypeStartByName: - message.status_value->value = - loader_do_start_by_name(loader, message.start.name, message.start.args); + message.status_value->value = loader_do_start_by_name( + loader, message.start.name, message.start.args, message.start.error_message); api_lock_unlock(message.api_lock); break; + case LoaderMessageTypeStartByNameDetachedWithGuiError: { + FuriString* error_message = furi_string_alloc(); + LoaderStatus status = loader_do_start_by_name( + loader, message.start.name, message.start.args, error_message); + loader_show_gui_error(status, error_message); + furi_string_free(error_message); + break; + } case LoaderMessageTypeShowMenu: loader_do_menu_show(loader, false); break; @@ -391,6 +610,10 @@ int32_t loader_srv(void* p) { break; case LoaderMessageTypeUnlock: loader_do_unlock(loader); + break; + case LoaderMessageTypeApplicationsClosed: + loader_do_applications_closed(loader); + break; } } } diff --git a/applications/services/loader/loader.h b/applications/services/loader/loader.h index b31bb1bdd6..0073df3de8 100644 --- a/applications/services/loader/loader.h +++ b/applications/services/loader/loader.h @@ -1,14 +1,12 @@ #pragma once #include -#include "loader_extmainapp.h" #ifdef __cplusplus extern "C" { #endif #define RECORD_LOADER "loader" - -#define FAP_LOADER_APP_NAME "Apps" +#define LOADER_APPLICATIONS_NAME "Apps" typedef struct Loader Loader; @@ -28,31 +26,71 @@ typedef struct { LoaderEventType type; } LoaderEvent; -/** Start application - * @param name - application name - * @param args - application arguments - * @retval true on success +/** + * @brief Start application + * @param[in] instance loader instance + * @param[in] name application name + * @param[in] args application arguments + * @param[out] error_message detailed error message, can be NULL + * @return LoaderStatus + */ +LoaderStatus + loader_start(Loader* instance, const char* name, const char* args, FuriString* error_message); + +/** + * @brief Start application with GUI error message + * @param[in] instance loader instance + * @param[in] name application name + * @param[in] args application arguments + * @return LoaderStatus + */ +LoaderStatus loader_start_with_gui_error(Loader* loader, const char* name, const char* args); + +/** + * @brief Start application detached with GUI error message + * @param[in] instance loader instance + * @param[in] name application name + * @param[in] args application arguments */ -LoaderStatus loader_start(Loader* instance, const char* name, const char* args); +void loader_start_detached_with_gui_error(Loader* loader, const char* name, const char* args); -/** Lock application start - * @retval true on success +/** + * @brief Lock application start + * @param[in] instance loader instance + * @return true on success */ bool loader_lock(Loader* instance); -/** Unlock application start */ +/** + * @brief Unlock application start + * @param[in] instance loader instance + */ void loader_unlock(Loader* instance); -/** Get loader lock status */ +/** + * @brief Check if loader is locked + * @param[in] instance loader instance + * @return true if locked + */ bool loader_is_locked(Loader* instance); -/** Show primary loader */ +/** + * @brief Show loader menu + * @param[in] instance loader instance + */ void loader_show_menu(Loader* instance); -/** Show settings menu */ -void loader_show_settings(Loader* loader); +/** + * @brief Show settings menu + * @param[in] instance loader instance + */ +void loader_show_settings(Loader* instance); -/** Show primary loader */ +/** + * @brief Get loader pubsub + * @param[in] instance loader instance + * @return FuriPubSub* + */ FuriPubSub* loader_get_pubsub(Loader* instance); #ifdef __cplusplus diff --git a/applications/services/loader/loader_applications.c b/applications/services/loader/loader_applications.c new file mode 100644 index 0000000000..1801edef9f --- /dev/null +++ b/applications/services/loader/loader_applications.c @@ -0,0 +1,146 @@ +#include "loader.h" +#include "loader_applications.h" +#include +#include +#include +#include +#include +#include + +#define TAG "LoaderApplications" + +struct LoaderApplications { + FuriThread* thread; + void (*closed_cb)(void*); + void* context; +}; + +static int32_t loader_applications_thread(void* p); + +LoaderApplications* loader_applications_alloc(void (*closed_cb)(void*), void* context) { + LoaderApplications* loader_applications = malloc(sizeof(LoaderApplications)); + loader_applications->thread = + furi_thread_alloc_ex(TAG, 512, loader_applications_thread, (void*)loader_applications); + loader_applications->closed_cb = closed_cb; + loader_applications->context = context; + furi_thread_start(loader_applications->thread); + return loader_applications; +} + +void loader_applications_free(LoaderApplications* loader_applications) { + furi_assert(loader_applications); + furi_thread_join(loader_applications->thread); + furi_thread_free(loader_applications->thread); + free(loader_applications); +} + +typedef struct { + FuriString* fap_path; + DialogsApp* dialogs; + Storage* storage; +} LoaderApplicationsApp; + +static LoaderApplicationsApp* loader_applications_app_alloc() { + LoaderApplicationsApp* app = malloc(sizeof(LoaderApplicationsApp)); //-V799 + app->fap_path = furi_string_alloc_set(EXT_PATH("apps")); + app->dialogs = furi_record_open(RECORD_DIALOGS); + app->storage = furi_record_open(RECORD_STORAGE); + return app; +} //-V773 + +static void loader_applications_app_free(LoaderApplicationsApp* loader_applications_app) { + furi_assert(loader_applications_app); + furi_record_close(RECORD_DIALOGS); + furi_record_close(RECORD_STORAGE); + furi_string_free(loader_applications_app->fap_path); + free(loader_applications_app); +} + +static bool loader_applications_item_callback( + FuriString* path, + void* context, + uint8_t** icon_ptr, + FuriString* item_name) { + LoaderApplicationsApp* loader_applications_app = context; + furi_assert(loader_applications_app); + return flipper_application_load_name_and_icon( + path, loader_applications_app->storage, icon_ptr, item_name); +} + +static bool loader_applications_select_app(LoaderApplicationsApp* loader_applications_app) { + const DialogsFileBrowserOptions browser_options = { + .extension = ".fap", + .skip_assets = true, + .icon = &I_unknown_10px, + .hide_ext = true, + .item_loader_callback = loader_applications_item_callback, + .item_loader_context = loader_applications_app, + .base_path = EXT_PATH("apps"), + }; + + return dialog_file_browser_show( + loader_applications_app->dialogs, + loader_applications_app->fap_path, + loader_applications_app->fap_path, + &browser_options); +} + +#define APPLICATION_STOP_EVENT 1 + +static void loader_pubsub_callback(const void* message, void* context) { + const LoaderEvent* event = message; + const FuriThreadId thread_id = (FuriThreadId)context; + + if(event->type == LoaderEventTypeApplicationStopped) { + furi_thread_flags_set(thread_id, APPLICATION_STOP_EVENT); + } +} + +static void loader_applications_start_app(const char* name) { + // start loading animation + Gui* gui = furi_record_open(RECORD_GUI); + ViewHolder* view_holder = view_holder_alloc(); + Loading* loading = loading_alloc(); + + view_holder_attach_to_gui(view_holder, gui); + view_holder_set_view(view_holder, loading_get_view(loading)); + view_holder_start(view_holder); + + // load app + FuriThreadId thread_id = furi_thread_get_current_id(); + Loader* loader = furi_record_open(RECORD_LOADER); + FuriPubSubSubscription* subscription = + furi_pubsub_subscribe(loader_get_pubsub(loader), loader_pubsub_callback, thread_id); + + LoaderStatus status = loader_start_with_gui_error(loader, name, NULL); + + if(status == LoaderStatusOk) { + furi_thread_flags_wait(APPLICATION_STOP_EVENT, FuriFlagWaitAny, FuriWaitForever); + } + + furi_pubsub_unsubscribe(loader_get_pubsub(loader), subscription); + furi_record_close(RECORD_LOADER); + + // stop loading animation + view_holder_stop(view_holder); + view_holder_free(view_holder); + loading_free(loading); + furi_record_close(RECORD_GUI); +} + +static int32_t loader_applications_thread(void* p) { + LoaderApplications* loader_applications = p; + LoaderApplicationsApp* loader_applications_app = loader_applications_app_alloc(); + + while(loader_applications_select_app(loader_applications_app)) { + loader_applications_start_app(furi_string_get_cstr(loader_applications_app->fap_path)); + } + + loader_applications_app_free(loader_applications_app); + + if(loader_applications->closed_cb) { + loader_applications->closed_cb(loader_applications->context); + } + + return 0; +} \ No newline at end of file diff --git a/applications/services/loader/loader_applications.h b/applications/services/loader/loader_applications.h new file mode 100644 index 0000000000..6b132af055 --- /dev/null +++ b/applications/services/loader/loader_applications.h @@ -0,0 +1,16 @@ +#pragma once +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct LoaderApplications LoaderApplications; + +LoaderApplications* loader_applications_alloc(void (*closed_cb)(void*), void* context); + +void loader_applications_free(LoaderApplications* loader_applications); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/applications/services/loader/loader_cli.c b/applications/services/loader/loader_cli.c index 2d46022157..af3ebf9e00 100644 --- a/applications/services/loader/loader_cli.c +++ b/applications/services/loader/loader_cli.c @@ -50,21 +50,11 @@ static void loader_cli_open(FuriString* args, Loader* loader) { const char* app_name_str = furi_string_get_cstr(app_name); - LoaderStatus status = loader_start(loader, app_name_str, args_str); - - switch(status) { - case LoaderStatusOk: - break; - case LoaderStatusErrorAppStarted: - printf("Can't start, application is running"); - break; - case LoaderStatusErrorUnknownApp: - printf("%s doesn't exists\r\n", app_name_str); - break; - case LoaderStatusErrorInternal: - printf("Internal error\r\n"); - break; + FuriString* error_message = furi_string_alloc(); + if(loader_start(loader, app_name_str, args_str, error_message) != LoaderStatusOk) { + printf("%s\r\n", furi_string_get_cstr(error_message)); } + furi_string_free(error_message); } while(false); furi_string_free(app_name); diff --git a/applications/services/loader/loader_extmainapp.h b/applications/services/loader/loader_extmainapp.h index 2e10b16a3d..4eeb263c7e 100644 --- a/applications/services/loader/loader_extmainapp.h +++ b/applications/services/loader/loader_extmainapp.h @@ -10,3 +10,5 @@ typedef struct { } ExtMainApp; LIST_DEF(ExtMainAppList, ExtMainApp, M_POD_OPLIST) + +ExtMainAppList_t* loader_get_ext_main_apps(Loader* loader); diff --git a/applications/services/loader/loader_i.h b/applications/services/loader/loader_i.h index 6887b4b509..5ed0441ba9 100644 --- a/applications/services/loader/loader_i.h +++ b/applications/services/loader/loader_i.h @@ -1,38 +1,45 @@ #pragma once #include #include +#include #include "loader.h" #include "loader_menu.h" +#include "loader_extmainapp.h" +#include "loader_applications.h" typedef struct { char* args; - char* name; FuriThread* thread; bool insomniac; + FlipperApplication* fap; } LoaderAppData; struct Loader { FuriPubSub* pubsub; FuriMessageQueue* queue; LoaderMenu* loader_menu; + LoaderApplications* loader_applications; LoaderAppData app; ExtMainAppList_t ext_main_apps; }; typedef enum { LoaderMessageTypeStartByName, + LoaderMessageTypeStartByNameDetachedWithGuiError, LoaderMessageTypeAppClosed, LoaderMessageTypeShowMenu, + LoaderMessageTypeShowSettings, LoaderMessageTypeMenuClosed, + LoaderMessageTypeApplicationsClosed, LoaderMessageTypeLock, LoaderMessageTypeUnlock, LoaderMessageTypeIsLocked, - LoaderMessageTypeShowSettings, } LoaderMessageType; typedef struct { const char* name; const char* args; + FuriString* error_message; } LoaderMessageStartByName; typedef struct { diff --git a/applications/services/loader/loader_menu.c b/applications/services/loader/loader_menu.c index c4e14038c5..de063083ca 100644 --- a/applications/services/loader/loader_menu.c +++ b/applications/services/loader/loader_menu.c @@ -5,119 +5,80 @@ #include #include +#include "loader.h" #include "loader_menu.h" +#include "loader_extmainapp.h" #define TAG "LoaderMenu" struct LoaderMenu { - Gui* gui; - ViewDispatcher* view_dispatcher; - Menu* primary_menu; - Submenu* settings_menu; - - bool settings; - ExtMainAppList_t* ext_main_apps; - - void (*closed_callback)(void*); - void* closed_callback_context; - - void (*click_callback)(const char*, bool, void*); - void* click_callback_context; - FuriThread* thread; + bool settings; + void (*closed_cb)(void*); + void* context; }; -typedef enum { - LoaderMenuViewPrimary, - LoaderMenuViewSettings, -} LoaderMenuView; - static int32_t loader_menu_thread(void* p); -LoaderMenu* loader_menu_alloc() { +LoaderMenu* loader_menu_alloc(void (*closed_cb)(void*), void* context, bool settings) { LoaderMenu* loader_menu = malloc(sizeof(LoaderMenu)); - loader_menu->gui = furi_record_open(RECORD_GUI); - loader_menu->view_dispatcher = view_dispatcher_alloc(); - loader_menu->primary_menu = menu_alloc(); - loader_menu->settings_menu = submenu_alloc(); - loader_menu->thread = NULL; - return loader_menu; -} - -void loader_menu_free(LoaderMenu* loader_menu) { - furi_assert(loader_menu); - // check if thread is running - furi_assert(!loader_menu->thread); - - submenu_free(loader_menu->settings_menu); - menu_free(loader_menu->primary_menu); - view_dispatcher_free(loader_menu->view_dispatcher); - furi_record_close(RECORD_GUI); - free(loader_menu); -} - -void loader_menu_start(LoaderMenu* loader_menu, bool settings, ExtMainAppList_t* ext_main_apps) { - furi_assert(loader_menu); - furi_assert(!loader_menu->thread); + loader_menu->closed_cb = closed_cb; + loader_menu->context = context; loader_menu->settings = settings; - loader_menu->ext_main_apps = ext_main_apps; loader_menu->thread = furi_thread_alloc_ex(TAG, 1024, loader_menu_thread, loader_menu); furi_thread_start(loader_menu->thread); + return loader_menu; } -void loader_menu_stop(LoaderMenu* loader_menu) { +void loader_menu_free(LoaderMenu* loader_menu) { furi_assert(loader_menu); - furi_assert(loader_menu->thread); - view_dispatcher_stop(loader_menu->view_dispatcher); furi_thread_join(loader_menu->thread); furi_thread_free(loader_menu->thread); - loader_menu->thread = NULL; + free(loader_menu); } -void loader_menu_set_closed_callback( - LoaderMenu* loader_menu, - void (*callback)(void*), - void* context) { - loader_menu->closed_callback = callback; - loader_menu->closed_callback_context = context; -} +typedef enum { + LoaderMenuViewPrimary, + LoaderMenuViewSettings, +} LoaderMenuView; -void loader_menu_set_click_callback( - LoaderMenu* loader_menu, - void (*callback)(const char*, bool, void*), - void* context) { - loader_menu->click_callback = callback; - loader_menu->click_callback_context = context; +typedef struct { + Gui* gui; + ViewDispatcher* view_dispatcher; + Menu* primary_menu; + Submenu* settings_menu; + bool settings; +} LoaderMenuApp; + +static void loader_menu_start(const char* name) { + Loader* loader = furi_record_open(RECORD_LOADER); + loader_start_with_gui_error(loader, name, NULL); + furi_record_close(RECORD_LOADER); } static void loader_menu_callback(void* context, uint32_t index) { - LoaderMenu* loader_menu = context; - const char* name = FLIPPER_APPS[index].name; - if(loader_menu->click_callback) { - loader_menu->click_callback(name, false, loader_menu->click_callback_context); - } + UNUSED(context); + const char* name_or_path = (const char*)index; + loader_menu_start(name_or_path); } -static void loader_menu_external_callback(void* context, uint32_t index) { - LoaderMenu* loader_menu = context; - const char* path = (const char*)index; - if(loader_menu->click_callback) { - loader_menu->click_callback(path, true, loader_menu->click_callback_context); - } +static void loader_menu_applications_callback(void* context, uint32_t index) { + UNUSED(index); + UNUSED(context); + const char* name = LOADER_APPLICATIONS_NAME; + loader_menu_start(name); } static void loader_menu_settings_menu_callback(void* context, uint32_t index) { - LoaderMenu* loader_menu = context; + UNUSED(context); const char* name = FLIPPER_SETTINGS_APPS[index].name; - if(loader_menu->click_callback) { - loader_menu->click_callback(name, false, loader_menu->click_callback_context); - } + loader_menu_start(name); } static void loader_menu_switch_to_settings(void* context, uint32_t index) { UNUSED(index); - LoaderMenu* loader_menu = context; - view_dispatcher_switch_to_view(loader_menu->view_dispatcher, LoaderMenuViewSettings); + LoaderMenuApp* app = context; + view_dispatcher_switch_to_view(app->view_dispatcher, LoaderMenuViewSettings); } static uint32_t loader_menu_switch_to_primary(void* context) { @@ -130,41 +91,44 @@ static uint32_t loader_menu_exit(void* context) { return VIEW_NONE; } -static void loader_menu_build_menu(LoaderMenu* loader_menu) { - size_t i; - for(i = 0; i < FLIPPER_APPS_COUNT; i++) { +static void loader_menu_build_menu(LoaderMenuApp* app, LoaderMenu* menu) { + menu_add_item( + app->primary_menu, + LOADER_APPLICATIONS_NAME, + &A_Plugins_14, + 0, + loader_menu_applications_callback, + (void*)menu); + for(size_t i = 0; i < FLIPPER_APPS_COUNT; i++) { menu_add_item( - loader_menu->primary_menu, + app->primary_menu, FLIPPER_APPS[i].name, FLIPPER_APPS[i].icon, - i, + (uint32_t)FLIPPER_APPS[i].name, loader_menu_callback, - (void*)loader_menu); + (void*)menu); } menu_add_item( - loader_menu->primary_menu, - "Settings", - &A_Settings_14, - i++, - loader_menu_switch_to_settings, - loader_menu); - - for(i = 0; i < ExtMainAppList_size(*loader_menu->ext_main_apps); i++) { - const ExtMainApp* app = ExtMainAppList_get(*loader_menu->ext_main_apps, i); + app->primary_menu, "Settings", &A_Settings_14, 0, loader_menu_switch_to_settings, app); + Loader* loader = furi_record_open(RECORD_LOADER); + ExtMainAppList_t* ext_main_apps = loader_get_ext_main_apps(loader); + for(size_t i = 0; i < ExtMainAppList_size(*ext_main_apps); i++) { + const ExtMainApp* ext_main_app = ExtMainAppList_get(*ext_main_apps, i); menu_add_item( - loader_menu->primary_menu, - app->name, - app->icon, - (uint32_t)app->path, - loader_menu_external_callback, - (void*)loader_menu); + app->primary_menu, + ext_main_app->name, + ext_main_app->icon, + (uint32_t)ext_main_app->path, + loader_menu_callback, + (void*)menu); } + furi_record_close(RECORD_LOADER); }; -static void loader_menu_build_submenu(LoaderMenu* loader_menu) { +static void loader_menu_build_submenu(LoaderMenuApp* app, LoaderMenu* loader_menu) { for(size_t i = 0; i < FLIPPER_SETTINGS_APPS_COUNT; i++) { submenu_add_item( - loader_menu->settings_menu, + app->settings_menu, FLIPPER_SETTINGS_APPS[i].name, i, loader_menu_settings_menu_callback, @@ -172,47 +136,66 @@ static void loader_menu_build_submenu(LoaderMenu* loader_menu) { } } -static int32_t loader_menu_thread(void* p) { - LoaderMenu* loader_menu = p; - furi_assert(loader_menu); - - if(!loader_menu->settings) loader_menu_build_menu(loader_menu); - loader_menu_build_submenu(loader_menu); - - view_dispatcher_attach_to_gui( - loader_menu->view_dispatcher, loader_menu->gui, ViewDispatcherTypeFullscreen); +static LoaderMenuApp* loader_menu_app_alloc(LoaderMenu* loader_menu) { + LoaderMenuApp* app = malloc(sizeof(LoaderMenuApp)); + app->gui = furi_record_open(RECORD_GUI); + app->view_dispatcher = view_dispatcher_alloc(); + app->settings = loader_menu->settings; // Primary menu - if(!loader_menu->settings) { - View* primary_view = menu_get_view(loader_menu->primary_menu); - view_set_context(primary_view, loader_menu->primary_menu); + if(!app->settings) { + app->primary_menu = menu_alloc(); + loader_menu_build_menu(app, loader_menu); + View* primary_view = menu_get_view(app->primary_menu); + view_set_context(primary_view, app->primary_menu); view_set_previous_callback(primary_view, loader_menu_exit); - view_dispatcher_add_view( - loader_menu->view_dispatcher, LoaderMenuViewPrimary, primary_view); + view_dispatcher_add_view(app->view_dispatcher, LoaderMenuViewPrimary, primary_view); } // Settings menu - View* settings_view = submenu_get_view(loader_menu->settings_menu); - view_set_context(settings_view, loader_menu->settings_menu); + app->settings_menu = submenu_alloc(); + loader_menu_build_submenu(app, loader_menu); + View* settings_view = submenu_get_view(app->settings_menu); + view_set_context(settings_view, app->settings_menu); view_set_previous_callback( - settings_view, loader_menu->settings ? loader_menu_exit : loader_menu_switch_to_primary); - view_dispatcher_add_view(loader_menu->view_dispatcher, LoaderMenuViewSettings, settings_view); + settings_view, app->settings ? loader_menu_exit : loader_menu_switch_to_primary); + view_dispatcher_add_view(app->view_dispatcher, LoaderMenuViewSettings, settings_view); - view_dispatcher_enable_queue(loader_menu->view_dispatcher); + view_dispatcher_enable_queue(app->view_dispatcher); view_dispatcher_switch_to_view( - loader_menu->view_dispatcher, - loader_menu->settings ? LoaderMenuViewSettings : LoaderMenuViewPrimary); + app->view_dispatcher, app->settings ? LoaderMenuViewSettings : LoaderMenuViewPrimary); - // run view dispatcher - view_dispatcher_run(loader_menu->view_dispatcher); + return app; +} - if(!loader_menu->settings) - view_dispatcher_remove_view(loader_menu->view_dispatcher, LoaderMenuViewPrimary); - view_dispatcher_remove_view(loader_menu->view_dispatcher, LoaderMenuViewSettings); +static void loader_menu_app_free(LoaderMenuApp* app) { + if(!app->settings) { + view_dispatcher_remove_view(app->view_dispatcher, LoaderMenuViewPrimary); + menu_free(app->primary_menu); + } + view_dispatcher_remove_view(app->view_dispatcher, LoaderMenuViewSettings); + submenu_free(app->settings_menu); + + view_dispatcher_free(app->view_dispatcher); - if(loader_menu->closed_callback) { - loader_menu->closed_callback(loader_menu->closed_callback_context); + furi_record_close(RECORD_GUI); + free(app); +} + +static int32_t loader_menu_thread(void* p) { + LoaderMenu* loader_menu = p; + furi_assert(loader_menu); + + LoaderMenuApp* app = loader_menu_app_alloc(loader_menu); + + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + view_dispatcher_run(app->view_dispatcher); + + if(loader_menu->closed_cb) { + loader_menu->closed_cb(loader_menu->context); } + loader_menu_app_free(app); + return 0; } diff --git a/applications/services/loader/loader_menu.h b/applications/services/loader/loader_menu.h index f4188b7335..bfb45285a9 100644 --- a/applications/services/loader/loader_menu.h +++ b/applications/services/loader/loader_menu.h @@ -1,6 +1,5 @@ #pragma once #include -#include "loader_extmainapp.h" #ifdef __cplusplus extern "C" { @@ -8,24 +7,10 @@ extern "C" { typedef struct LoaderMenu LoaderMenu; -LoaderMenu* loader_menu_alloc(); +LoaderMenu* loader_menu_alloc(void (*closed_cb)(void*), void* context, bool settings); void loader_menu_free(LoaderMenu* loader_menu); -void loader_menu_start(LoaderMenu* loader_menu, bool settings, ExtMainAppList_t* ext_main_apps); - -void loader_menu_stop(LoaderMenu* loader_menu); - -void loader_menu_set_closed_callback( - LoaderMenu* loader_menu, - void (*callback)(void*), - void* context); - -void loader_menu_set_click_callback( - LoaderMenu* loader_menu, - void (*callback)(const char*, bool, void*), - void* context); - #ifdef __cplusplus } #endif diff --git a/applications/services/loader/loader_preload.c b/applications/services/loader/loader_preload.c deleted file mode 100644 index 21e3770b75..0000000000 --- a/applications/services/loader/loader_preload.c +++ /dev/null @@ -1,54 +0,0 @@ -#include -#include -#include -#include - -#define TAG "Preload" - -void* loader_preload(Storage* storage, const char* path) { - FlipperApplication* app = flipper_application_alloc(storage, firmware_api_interface); - size_t start = furi_get_tick(); - - FURI_LOG_I(TAG, "Loading %s", path); - - FlipperApplicationPreloadStatus preload_res = flipper_application_preload(app, path); - if(preload_res != FlipperApplicationPreloadStatusSuccess) { - return NULL; - } - - FURI_LOG_I(TAG, "Mapping"); - FlipperApplicationLoadStatus load_status = flipper_application_map_to_memory(app); - if(load_status != FlipperApplicationLoadStatusSuccess) { - const char* err_msg = flipper_application_load_status_to_string(load_status); - FURI_LOG_E(TAG, "Failed to map to memory %s: %s", path, err_msg); - return NULL; - } - - FURI_LOG_I(TAG, "Loaded in %ums", (size_t)(furi_get_tick() - start)); - return app; -} - -void loader_preload_start(void* _app, const char* path) { - FlipperApplication* app = _app; - FURI_LOG_I(TAG, "Starting app"); - - FuriThread* thread = flipper_application_spawn(app, NULL); - - /* This flag is set by the debugger - to break on app start */ - if(furi_hal_debug_is_gdb_session_active()) { - FURI_LOG_W(TAG, "Triggering BP for debugger"); - /* After hitting this, you can set breakpoints in your .fap's code - * Note that you have to toggle breakpoints that were set before */ - __asm volatile("bkpt 0"); - } - - FuriString* app_name = furi_string_alloc(); - path_extract_filename_no_ext(path, app_name); - furi_thread_set_appid(thread, furi_string_get_cstr(app_name)); - furi_string_free(app_name); - - furi_thread_start(thread); - furi_thread_join(thread); - - flipper_application_despawn(app); -} diff --git a/applications/services/loader/loader_preload.h b/applications/services/loader/loader_preload.h deleted file mode 100644 index 1257c9890a..0000000000 --- a/applications/services/loader/loader_preload.h +++ /dev/null @@ -1,6 +0,0 @@ -#pragma once - -#include - -void* loader_preload(Storage* storage, const char* path); -void loader_preload_start(void* app, const char* path); diff --git a/applications/services/notification/notification.h b/applications/services/notification/notification.h index b38620f0f5..0e1c07e5df 100644 --- a/applications/services/notification/notification.h +++ b/applications/services/notification/notification.h @@ -75,6 +75,8 @@ typedef enum { NotificationMessageTypeForceDisplayBrightnessSetting, NotificationMessageTypeLedBrightnessSettingApply, + + NotificationMessageTypeLcdContrastUpdate, } NotificationMessageType; typedef struct { diff --git a/applications/services/notification/notification_app.c b/applications/services/notification/notification_app.c index 409c519573..cce407cc4e 100644 --- a/applications/services/notification/notification_app.c +++ b/applications/services/notification/notification_app.c @@ -4,6 +4,9 @@ #include #include #include +#include +#include + #include "notification.h" #include "notification_messages.h" #include "notification_app.h" @@ -21,14 +24,14 @@ static const uint8_t reset_sound_mask = 1 << 4; static const uint8_t reset_display_mask = 1 << 5; static const uint8_t reset_blink_mask = 1 << 6; -void notification_vibro_on(bool force); -void notification_vibro_off(); -void notification_sound_on(float freq, float volume, bool force); -void notification_sound_off(); +static void notification_vibro_on(bool force); +static void notification_vibro_off(); +static void notification_sound_on(float freq, float volume, bool force); +static void notification_sound_off(); -uint8_t notification_settings_get_display_brightness(NotificationApp* app, uint8_t value); -uint8_t notification_settings_get_rgb_led_brightness(NotificationApp* app, uint8_t value); -uint32_t notification_settings_display_off_delay_ticks(NotificationApp* app); +static uint8_t notification_settings_get_display_brightness(NotificationApp* app, uint8_t value); +static uint8_t notification_settings_get_rgb_led_brightness(NotificationApp* app, uint8_t value); +static uint32_t notification_settings_display_off_delay_ticks(NotificationApp* app); void notification_message_save_settings(NotificationApp* app) { NotificationAppMessage m = { @@ -40,7 +43,8 @@ void notification_message_save_settings(NotificationApp* app) { } // internal layer -void notification_apply_internal_led_layer(NotificationLedLayer* layer, uint8_t layer_value) { +static void + notification_apply_internal_led_layer(NotificationLedLayer* layer, uint8_t layer_value) { furi_assert(layer); furi_assert(layer->index < LayerMAX); @@ -53,7 +57,13 @@ void notification_apply_internal_led_layer(NotificationLedLayer* layer, uint8_t } } -bool notification_is_any_led_layer_internal_and_not_empty(NotificationApp* app) { +static void notification_apply_lcd_contrast(NotificationApp* app) { + Gui* gui = furi_record_open(RECORD_GUI); + u8x8_d_st756x_set_contrast(&gui->canvas->fb.u8x8, app->settings.contrast); + furi_record_close(RECORD_GUI); +} + +static bool notification_is_any_led_layer_internal_and_not_empty(NotificationApp* app) { bool result = false; if((app->led[0].index == LayerInternal) || (app->led[1].index == LayerInternal) || (app->led[2].index == LayerInternal)) { @@ -68,7 +78,7 @@ bool notification_is_any_led_layer_internal_and_not_empty(NotificationApp* app) } // notification layer -void notification_apply_notification_led_layer( +static void notification_apply_notification_led_layer( NotificationLedLayer* layer, const uint8_t layer_value) { furi_assert(layer); @@ -82,7 +92,7 @@ void notification_apply_notification_led_layer( furi_hal_light_set(layer->light, layer->value[LayerNotification]); } -void notification_reset_notification_led_layer(NotificationLedLayer* layer) { +static void notification_reset_notification_led_layer(NotificationLedLayer* layer) { furi_assert(layer); furi_assert(layer->index < LayerMAX); @@ -95,7 +105,7 @@ void notification_reset_notification_led_layer(NotificationLedLayer* layer) { furi_hal_light_set(layer->light, layer->value[LayerInternal]); } -void notification_reset_notification_layer(NotificationApp* app, uint8_t reset_mask) { +static void notification_reset_notification_layer(NotificationApp* app, uint8_t reset_mask) { if(reset_mask & reset_blink_mask) { furi_hal_light_blink_stop(); } @@ -131,28 +141,28 @@ uint8_t notification_settings_get_display_brightness(NotificationApp* app, uint8 return (value * app->settings.display_brightness); } -uint8_t notification_settings_get_rgb_led_brightness(NotificationApp* app, uint8_t value) { +static uint8_t notification_settings_get_rgb_led_brightness(NotificationApp* app, uint8_t value) { return (value * app->settings.led_brightness); } -uint32_t notification_settings_display_off_delay_ticks(NotificationApp* app) { +static uint32_t notification_settings_display_off_delay_ticks(NotificationApp* app) { return ( (float)(app->settings.display_off_delay_ms) / (1000.0f / furi_kernel_get_tick_frequency())); } // generics -void notification_vibro_on(bool force) { +static void notification_vibro_on(bool force) { if(!furi_hal_rtc_is_flag_set(FuriHalRtcFlagStealthMode) || force) { furi_hal_vibro_on(true); } } -void notification_vibro_off() { +static void notification_vibro_off() { furi_hal_vibro_on(false); } -void notification_sound_on(float freq, float volume, bool force) { +static void notification_sound_on(float freq, float volume, bool force) { if(!furi_hal_rtc_is_flag_set(FuriHalRtcFlagStealthMode) || force) { if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(30)) { furi_hal_speaker_start(freq, volume); @@ -160,7 +170,7 @@ void notification_sound_on(float freq, float volume, bool force) { } } -void notification_sound_off() { +static void notification_sound_off() { if(furi_hal_speaker_is_mine()) { furi_hal_speaker_stop(); furi_hal_speaker_release(); @@ -175,7 +185,7 @@ static void notification_display_timer(void* ctx) { } // message processing -void notification_process_notification_message( +static void notification_process_notification_message( NotificationApp* app, NotificationAppMessage* message) { uint32_t notification_message_index = 0; @@ -334,6 +344,9 @@ void notification_process_notification_message( reset_mask |= reset_green_mask; reset_mask |= reset_blue_mask; break; + case NotificationMessageTypeLcdContrastUpdate: + notification_apply_lcd_contrast(app); + break; } notification_message_index++; notification_message = (*message->sequence)[notification_message_index]; @@ -362,7 +375,8 @@ void notification_process_notification_message( } } -void notification_process_internal_message(NotificationApp* app, NotificationAppMessage* message) { +static void + notification_process_internal_message(NotificationApp* app, NotificationAppMessage* message) { uint32_t notification_message_index = 0; const NotificationMessage* notification_message; notification_message = (*message->sequence)[notification_message_index]; @@ -483,11 +497,7 @@ int32_t notification_srv(void* p) { UNUSED(p); NotificationApp* app = notification_app_alloc(); - if(furi_hal_is_normal_boot()) { - if(!notification_load_settings(app)) { - notification_save_settings(app); - } - } + notification_load_settings(app); notification_vibro_off(); notification_sound_off(); @@ -495,6 +505,7 @@ int32_t notification_srv(void* p) { notification_apply_internal_led_layer(&app->led[0], 0x00); notification_apply_internal_led_layer(&app->led[1], 0x00); notification_apply_internal_led_layer(&app->led[2], 0x00); + notification_apply_lcd_contrast(app); furi_record_create(RECORD_NOTIFICATION, app); diff --git a/applications/services/notification/notification_app.h b/applications/services/notification/notification_app.h index 92a668ee9f..fe315ef88a 100644 --- a/applications/services/notification/notification_app.h +++ b/applications/services/notification/notification_app.h @@ -31,7 +31,7 @@ typedef struct { Light light; } NotificationLedLayer; -#define NOTIFICATION_SETTINGS_VERSION 0x01 +#define NOTIFICATION_SETTINGS_VERSION 0x02 #define NOTIFICATION_SETTINGS_MAGIC 0x16 #define NOTIFICATION_SETTINGS_OLD_PATH INT_PATH(".notification.settings") #define NOTIFICATION_SETTINGS_PATH CFG_PATH("notification.settings") @@ -42,6 +42,7 @@ typedef struct { float led_brightness; float speaker_volume; uint32_t display_off_delay_ms; + int8_t contrast; bool vibro_on; } NotificationSettings; diff --git a/applications/services/notification/notification_messages.c b/applications/services/notification/notification_messages.c index 51f3533c32..28ec327c6e 100644 --- a/applications/services/notification/notification_messages.c +++ b/applications/services/notification/notification_messages.c @@ -197,6 +197,10 @@ const NotificationMessage message_force_display_brightness_setting_1f = { .data.forced_settings.display_brightness = 1.0f, }; +const NotificationMessage message_lcd_contrast_update = { + .type = NotificationMessageTypeLcdContrastUpdate, +}; + /****************************** Message sequences ******************************/ // Reset @@ -566,3 +570,8 @@ const NotificationSequence sequence_audiovisual_alert = { &message_vibro_off, NULL, }; + +const NotificationSequence sequence_lcd_contrast_update = { + &message_lcd_contrast_update, + NULL, +}; diff --git a/applications/services/notification/notification_messages.h b/applications/services/notification/notification_messages.h index 1007969176..d87cf74f4e 100644 --- a/applications/services/notification/notification_messages.h +++ b/applications/services/notification/notification_messages.h @@ -63,6 +63,9 @@ extern const NotificationMessage message_force_vibro_setting_on; extern const NotificationMessage message_force_vibro_setting_off; extern const NotificationMessage message_force_display_brightness_setting_1f; +// LCD Messages +extern const NotificationMessage message_lcd_contrast_update; + /****************************** Message sequences ******************************/ // Reset @@ -138,6 +141,9 @@ extern const NotificationSequence sequence_success; extern const NotificationSequence sequence_error; extern const NotificationSequence sequence_audiovisual_alert; +// LCD +extern const NotificationSequence sequence_lcd_contrast_update; + #ifdef __cplusplus } #endif diff --git a/applications/services/power/power_service/power.c b/applications/services/power/power_service/power.c index 0810c84d69..a42d700b58 100644 --- a/applications/services/power/power_service/power.c +++ b/applications/services/power/power_service/power.c @@ -7,6 +7,12 @@ #define POWER_OFF_TIMEOUT 90 #define TAG "Power" +void power_set_battery_icon_enabled(Power* power, bool is_enabled) { + furi_assert(power); + + view_port_enabled_set(power->battery_view_port, is_enabled); +} + void power_draw_battery_callback(Canvas* canvas, void* context) { furi_assert(context); Power* power = context; @@ -356,6 +362,7 @@ Power* power_alloc() { // Battery view port power->battery_view_port = power_battery_view_port_alloc(power); + power_set_battery_icon_enabled(power, XTREME_SETTINGS()->battery_icon != BatteryIconOff); power->show_low_bat_level_message = true; //Auto shutdown timer @@ -502,7 +509,6 @@ int32_t power_srv(void* p) { Power* power = power_alloc(); if(!LOAD_POWER_SETTINGS(&power->shutdown_idle_delay_ms)) { power->shutdown_idle_delay_ms = 0; - SAVE_POWER_SETTINGS(&power->shutdown_idle_delay_ms); } power_auto_shutdown_arm(power); power_update_info(power); diff --git a/applications/services/power/power_service/power.h b/applications/services/power/power_service/power.h index 8a9d5947d1..a2dc34f90c 100644 --- a/applications/services/power/power_service/power.h +++ b/applications/services/power/power_service/power.h @@ -120,6 +120,13 @@ void power_enable_low_battery_level_notification(Power* power, bool enable); */ void power_trigger_ui_update(Power* power); +/** Enable or disable battery icon + * + * @param power Power instance + * @param is_enabled Show battery or not + */ +void power_set_battery_icon_enabled(Power* power, bool is_enabled); + #ifdef __cplusplus } #endif diff --git a/applications/services/rpc/rpc_app.c b/applications/services/rpc/rpc_app.c index cc18b6cec3..bf44ed2dee 100644 --- a/applications/services/rpc/rpc_app.c +++ b/applications/services/rpc/rpc_app.c @@ -52,7 +52,7 @@ static void rpc_system_app_start_process(const PB_Main* request, void* context) snprintf(args_temp, RPC_SYSTEM_APP_TEMP_ARGS_SIZE, "RPC %08lX", (uint32_t)rpc_app); app_args = args_temp; } - LoaderStatus status = loader_start(loader, app_name, app_args); + LoaderStatus status = loader_start(loader, app_name, app_args, NULL); if(status == LoaderStatusErrorAppStarted) { result = PB_CommandStatus_ERROR_APP_SYSTEM_LOCKED; } else if(status == LoaderStatusErrorInternal) { diff --git a/applications/services/rpc/rpc_storage.c b/applications/services/rpc/rpc_storage.c index c3a4a04704..96f43e1d7f 100644 --- a/applications/services/rpc/rpc_storage.c +++ b/applications/services/rpc/rpc_storage.c @@ -15,7 +15,7 @@ #define TAG "RpcStorage" -#define MAX_NAME_LENGTH 255 +#define MAX_NAME_LENGTH 254 static const size_t MAX_DATA_SIZE = 512; @@ -282,7 +282,7 @@ static void rpc_system_storage_list_process(const PB_Main* request, void* contex while(!finish) { FileInfo fileinfo; - char* name = malloc(MAX_NAME_LENGTH + 1); + char* name = malloc(MAX_NAME_LENGTH); if(storage_dir_read(dir, &fileinfo, name, MAX_NAME_LENGTH)) { if(path_contains_only_ascii(name)) { if(i == COUNT_OF(list->file)) { diff --git a/applications/services/storage/storage_cli.c b/applications/services/storage/storage_cli.c index d720de9d19..096060d822 100644 --- a/applications/services/storage/storage_cli.c +++ b/applications/services/storage/storage_cli.c @@ -9,7 +9,7 @@ #include #include -#define MAX_NAME_LENGTH 255 +#define MAX_NAME_LENGTH 254 static void storage_cli_print_usage() { printf("Usage:\r\n"); diff --git a/applications/services/storage/storage_external_api.c b/applications/services/storage/storage_external_api.c index bfedb3e1e7..d2c98f290a 100644 --- a/applications/services/storage/storage_external_api.c +++ b/applications/services/storage/storage_external_api.c @@ -7,8 +7,7 @@ #include #include "toolbox/path.h" -#define MAX_NAME_LENGTH 256 -#define MAX_EXT_LEN 16 +#define MAX_NAME_LENGTH 254 #define FILE_BUFFER_SIZE 512 #define TAG "StorageAPI" @@ -666,30 +665,31 @@ FS_Error storage_common_merge(Storage* storage, const char* old_path, const char error = storage_common_stat(storage, new_path, &fileinfo); if(error == FSE_OK) { furi_string_set(new_path_next, new_path); - FuriString* dir_path; - FuriString* filename; - char extension[MAX_EXT_LEN] = {0}; - - dir_path = furi_string_alloc(); - filename = furi_string_alloc(); + FuriString* dir_path = furi_string_alloc(); + FuriString* filename = furi_string_alloc(); + FuriString* file_ext = furi_string_alloc(); path_extract_filename(new_path_next, filename, true); path_extract_dirname(new_path, dir_path); - path_extract_extension(new_path_next, extension, MAX_EXT_LEN); + path_extract_ext_str(new_path_next, file_ext); storage_get_next_filename( storage, furi_string_get_cstr(dir_path), furi_string_get_cstr(filename), - extension, + furi_string_get_cstr(file_ext), new_path_next, 255); furi_string_cat_printf( - dir_path, "/%s%s", furi_string_get_cstr(new_path_next), extension); + dir_path, + "/%s%s", + furi_string_get_cstr(new_path_next), + furi_string_get_cstr(file_ext)); furi_string_set(new_path_next, dir_path); furi_string_free(dir_path); furi_string_free(filename); + furi_string_free(file_ext); new_path_tmp = furi_string_get_cstr(new_path_next); } else { new_path_tmp = new_path; @@ -893,7 +893,7 @@ bool storage_simply_remove_recursive(Storage* storage, const char* path) { return true; } - char* name = malloc(MAX_NAME_LENGTH + 1); //-V799 + char* name = malloc(MAX_NAME_LENGTH); //-V799 File* dir = storage_file_alloc(storage); cur_dir = furi_string_alloc_set(path); bool go_deeper = false; diff --git a/applications/settings/about/about.c b/applications/settings/about/about.c index 3fc31b1d63..b7ebbd1595 100644 --- a/applications/settings/about/about.c +++ b/applications/settings/about/about.c @@ -138,7 +138,7 @@ static DialogMessageButton fw_version_screen(DialogsApp* dialogs, DialogMessage* furi_hal_info_get_api_version(&api_major, &api_minor); furi_string_cat_printf( buffer, - "%s %s\n%s F%d %d.%d %s\nhttps://flipper-xtre.me/", + "%s %s\n%s F%d:%d.%d %s\nhttps://flipper-xtre.me/", version_get_version(ver), version_get_builddate(ver), version_get_githash(ver), diff --git a/applications/settings/desktop_settings/desktop_settings_app.c b/applications/settings/desktop_settings/desktop_settings_app.c index afb5d59ec6..d394f389cc 100644 --- a/applications/settings/desktop_settings/desktop_settings_app.c +++ b/applications/settings/desktop_settings/desktop_settings_app.c @@ -6,6 +6,17 @@ #include "scenes/desktop_settings_scene.h" #include +const char* EXTRA_KEYBINDS[] = { + "Apps Menu", + "Archive", + "Device Info", + "Lock Menu", + "Lock Keypad", + "Lock with PIN", + "Passport", +}; +const size_t EXTRA_KEYBINDS_COUNT = COUNT_OF(EXTRA_KEYBINDS); + static bool desktop_settings_custom_event_callback(void* context, uint32_t event) { furi_assert(context); DesktopSettingsApp* app = context; @@ -18,10 +29,19 @@ static bool desktop_settings_back_event_callback(void* context) { return scene_manager_handle_back_event(app->scene_manager); } +char* desktop_settings_app_get_keybind(DesktopSettingsApp* app) { + KeybindType type = + scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppSceneKeybindsType); + KeybindKey key = + scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppSceneKeybindsKey); + return app->desktop->keybinds[type][key].data; +} + DesktopSettingsApp* desktop_settings_app_alloc() { DesktopSettingsApp* app = malloc(sizeof(DesktopSettingsApp)); app->gui = furi_record_open(RECORD_GUI); + app->desktop = furi_record_open(RECORD_DESKTOP); app->dialogs = furi_record_open(RECORD_DIALOGS); app->view_dispatcher = view_dispatcher_alloc(); app->scene_manager = scene_manager_alloc(&desktop_settings_scene_handlers, app); @@ -85,13 +105,13 @@ void desktop_settings_app_free(DesktopSettingsApp* app) { scene_manager_free(app->scene_manager); // Records furi_record_close(RECORD_DIALOGS); + furi_record_close(RECORD_DESKTOP); furi_record_close(RECORD_GUI); free(app); } extern int32_t desktop_settings_app(void* p) { DesktopSettingsApp* app = desktop_settings_app_alloc(); - DESKTOP_SETTINGS_LOAD(&app->settings); if(p && (strcmp(p, DESKTOP_SETTINGS_RUN_PIN_SETUP_ARG) == 0)) { scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinSetupHowto); } else { @@ -99,6 +119,9 @@ extern int32_t desktop_settings_app(void* p) { } view_dispatcher_run(app->view_dispatcher); + if(app->save_settings) { + DESKTOP_SETTINGS_SAVE(&app->desktop->settings); + } desktop_settings_app_free(app); return 0; } diff --git a/applications/settings/desktop_settings/desktop_settings_app.h b/applications/settings/desktop_settings/desktop_settings_app.h index 6f97564c94..092ed8e862 100644 --- a/applications/settings/desktop_settings/desktop_settings_app.h +++ b/applications/settings/desktop_settings/desktop_settings_app.h @@ -9,7 +9,7 @@ #include #include -#include +#include #include #include "views/desktop_settings_view_pin_setup_howto.h" #include "views/desktop_settings_view_pin_setup_howto2.h" @@ -23,10 +23,19 @@ typedef enum { DesktopSettingsAppViewIdPinSetupHowto2, } DesktopSettingsAppView; -typedef struct { - DesktopSettings settings; +typedef enum { + DesktopSettingsAppKeybindActionTypeMainApp, + DesktopSettingsAppKeybindActionTypeExternalApp, + DesktopSettingsAppKeybindActionTypeMoreActions, + DesktopSettingsAppKeybindActionTypeRemoveKeybind, +} DesktopSettingsAppKeybindActionType; + +extern const char* EXTRA_KEYBINDS[]; +extern const size_t EXTRA_KEYBINDS_COUNT; +typedef struct { Gui* gui; + Desktop* desktop; DialogsApp* dialogs; SceneManager* scene_manager; ViewDispatcher* view_dispatcher; @@ -41,4 +50,8 @@ typedef struct { bool pincode_buffer_filled; uint8_t menu_idx; + + bool save_settings; } DesktopSettingsApp; + +char* desktop_settings_app_get_keybind(DesktopSettingsApp* app); diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_config.h b/applications/settings/desktop_settings/scenes/desktop_settings_scene_config.h index 5bc52172b9..458ba6bf2d 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_config.h +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_config.h @@ -1,5 +1,8 @@ ADD_SCENE(desktop_settings, start, Start) -ADD_SCENE(desktop_settings, favorite, Favorite) +ADD_SCENE(desktop_settings, keybinds_type, KeybindsType) +ADD_SCENE(desktop_settings, keybinds_key, KeybindsKey) +ADD_SCENE(desktop_settings, keybinds_action_type, KeybindsActionType) +ADD_SCENE(desktop_settings, keybinds_action, KeybindsAction) ADD_SCENE(desktop_settings, pin_menu, PinMenu) ADD_SCENE(desktop_settings, pin_auth, PinAuth) diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_favorite.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_favorite.c deleted file mode 100644 index 94c5ee9f04..0000000000 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_favorite.c +++ /dev/null @@ -1,153 +0,0 @@ -#include "../desktop_settings_app.h" -#include "applications.h" -#include "desktop_settings_scene.h" -#include -#include -#include - -static bool favorite_fap_selector_item_callback( - FuriString* file_path, - void* context, - uint8_t** icon_ptr, - FuriString* item_name) { - UNUSED(context); -#ifdef APP_FAP_LOADER - Storage* storage = furi_record_open(RECORD_STORAGE); - bool success = fap_loader_load_name_and_icon(file_path, storage, icon_ptr, item_name); - furi_record_close(RECORD_STORAGE); -#else - UNUSED(file_path); - UNUSED(icon_ptr); - UNUSED(item_name); - bool success = false; -#endif - return success; -} - -static bool favorite_fap_selector_file_exists(char* file_path) { - Storage* storage = furi_record_open(RECORD_STORAGE); - bool exists = storage_file_exists(storage, file_path); - furi_record_close(RECORD_STORAGE); - return exists; -} - -static void desktop_settings_scene_favorite_submenu_callback(void* context, uint32_t index) { - DesktopSettingsApp* app = context; - view_dispatcher_send_custom_event(app->view_dispatcher, index); -} - -void desktop_settings_scene_favorite_on_enter(void* context) { - DesktopSettingsApp* app = context; - Submenu* submenu = app->submenu; - submenu_reset(submenu); - - uint32_t primary_favorite = - scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppSceneFavorite); - uint32_t pre_select_item = 0; - - for(size_t i = 0; i < FLIPPER_APPS_COUNT; i++) { - submenu_add_item( - submenu, - FLIPPER_APPS[i].name, - i, - desktop_settings_scene_favorite_submenu_callback, - app); - - if(primary_favorite) { // Select favorite item in submenu - if((app->settings.favorite_primary.is_external && - !strcmp(FLIPPER_APPS[i].name, FAP_LOADER_APP_NAME)) || - (!strcmp(FLIPPER_APPS[i].name, app->settings.favorite_primary.name_or_path))) { - pre_select_item = i; - } - } else { - if((app->settings.favorite_secondary.is_external && - !strcmp(FLIPPER_APPS[i].name, FAP_LOADER_APP_NAME)) || - (!strcmp(FLIPPER_APPS[i].name, app->settings.favorite_secondary.name_or_path))) { - pre_select_item = i; - } - } - } - - submenu_set_header( - submenu, primary_favorite ? "Primary favorite app:" : "Secondary favorite app:"); - submenu_set_selected_item(submenu, pre_select_item); // If set during loop, visual glitch. - - view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewMenu); -} - -bool desktop_settings_scene_favorite_on_event(void* context, SceneManagerEvent event) { - DesktopSettingsApp* app = context; - bool consumed = false; - FuriString* temp_path = furi_string_alloc_set_str(EXT_PATH("apps")); - - uint32_t primary_favorite = - scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppSceneFavorite); - - if(event.type == SceneManagerEventTypeCustom) { - if(strcmp(FLIPPER_APPS[event.event].name, FAP_LOADER_APP_NAME) != 0) { - if(primary_favorite) { - app->settings.favorite_primary.is_external = false; - strncpy( - app->settings.favorite_primary.name_or_path, - FLIPPER_APPS[event.event].name, - MAX_APP_LENGTH); - } else { - app->settings.favorite_secondary.is_external = false; - strncpy( - app->settings.favorite_secondary.name_or_path, - FLIPPER_APPS[event.event].name, - MAX_APP_LENGTH); - } - } else { - const DialogsFileBrowserOptions browser_options = { - .extension = ".fap", - .icon = &I_unknown_10px, - .skip_assets = true, - .hide_ext = true, - .item_loader_callback = favorite_fap_selector_item_callback, - .item_loader_context = app, - .base_path = EXT_PATH("apps"), - }; - - if(primary_favorite) { // Select favorite fap in file browser - if(favorite_fap_selector_file_exists( - app->settings.favorite_primary.name_or_path)) { - furi_string_set_str(temp_path, app->settings.favorite_primary.name_or_path); - } - } else { - if(favorite_fap_selector_file_exists( - app->settings.favorite_secondary.name_or_path)) { - furi_string_set_str(temp_path, app->settings.favorite_secondary.name_or_path); - } - } - - submenu_reset(app->submenu); - if(dialog_file_browser_show(app->dialogs, temp_path, temp_path, &browser_options)) { - if(primary_favorite) { - app->settings.favorite_primary.is_external = true; - strncpy( - app->settings.favorite_primary.name_or_path, - furi_string_get_cstr(temp_path), - MAX_APP_LENGTH); - } else { - app->settings.favorite_secondary.is_external = true; - strncpy( - app->settings.favorite_secondary.name_or_path, - furi_string_get_cstr(temp_path), - MAX_APP_LENGTH); - } - } - } - scene_manager_previous_scene(app->scene_manager); - consumed = true; - } - - furi_string_free(temp_path); - return consumed; -} - -void desktop_settings_scene_favorite_on_exit(void* context) { - DesktopSettingsApp* app = context; - DESKTOP_SETTINGS_SAVE(&app->settings); - submenu_reset(app->submenu); -} diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_keybinds_action.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_keybinds_action.c new file mode 100644 index 0000000000..4d2f01fcad --- /dev/null +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_keybinds_action.c @@ -0,0 +1,83 @@ +#include "../desktop_settings_app.h" +#include "applications.h" +#include "desktop_settings_scene.h" + +static void + desktop_settings_scene_keybinds_action_submenu_callback(void* context, uint32_t index) { + DesktopSettingsApp* app = context; + DesktopSettingsAppKeybindActionType action_type = scene_manager_get_scene_state( + app->scene_manager, DesktopSettingsAppSceneKeybindsActionType); + char* keybind = desktop_settings_app_get_keybind(app); + + if(action_type == DesktopSettingsAppKeybindActionTypeMainApp) { + strncpy(keybind, FLIPPER_APPS[index].name, MAX_KEYBIND_LENGTH); + } else if(action_type == DesktopSettingsAppKeybindActionTypeMoreActions) { + strncpy(keybind, EXTRA_KEYBINDS[index], MAX_KEYBIND_LENGTH); + } + + DESKTOP_KEYBINDS_SAVE(&app->desktop->keybinds, sizeof(app->desktop->keybinds)); + scene_manager_search_and_switch_to_previous_scene( + app->scene_manager, DesktopSettingsAppSceneStart); +} + +void desktop_settings_scene_keybinds_action_on_enter(void* context) { + DesktopSettingsApp* app = context; + Submenu* submenu = app->submenu; + char* keybind = desktop_settings_app_get_keybind(app); + submenu_reset(submenu); + + uint32_t pre_select_item = 0; + DesktopSettingsAppKeybindActionType action_type = scene_manager_get_scene_state( + app->scene_manager, DesktopSettingsAppSceneKeybindsActionType); + + if(action_type == DesktopSettingsAppKeybindActionTypeMainApp) { + for(size_t i = 0; i < FLIPPER_APPS_COUNT; i++) { + submenu_add_item( + submenu, + FLIPPER_APPS[i].name, + i, + desktop_settings_scene_keybinds_action_submenu_callback, + app); + + // Select keybind item in submenu + if(!strncmp(FLIPPER_APPS[i].name, keybind, MAX_KEYBIND_LENGTH)) { + pre_select_item = i; + } + } + } else if(action_type == DesktopSettingsAppKeybindActionTypeMoreActions) { + for(size_t i = 0; i < EXTRA_KEYBINDS_COUNT; i++) { + submenu_add_item( + submenu, + EXTRA_KEYBINDS[i], + i, + desktop_settings_scene_keybinds_action_submenu_callback, + app); + + // Select keybind item in submenu + if(!strncmp(EXTRA_KEYBINDS[i], keybind, MAX_KEYBIND_LENGTH)) { + pre_select_item = i; + } + } + } + + // submenu_set_header(submenu, "Keybind action:"); + submenu_set_selected_item(submenu, pre_select_item); // If set during loop, visual glitch. + + view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewMenu); +} + +bool desktop_settings_scene_keybinds_action_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + } + + return consumed; +} + +void desktop_settings_scene_keybinds_action_on_exit(void* context) { + DesktopSettingsApp* app = context; + submenu_reset(app->submenu); +} diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_keybinds_action_type.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_keybinds_action_type.c new file mode 100644 index 0000000000..9fd078f7e2 --- /dev/null +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_keybinds_action_type.c @@ -0,0 +1,147 @@ +#include "../desktop_settings_app.h" +#include "applications.h" +#include "desktop_settings_scene.h" +#include +#include +#include + +static bool keybinds_fap_selector_item_callback( + FuriString* file_path, + void* context, + uint8_t** icon_ptr, + FuriString* item_name) { + UNUSED(context); + Storage* storage = furi_record_open(RECORD_STORAGE); + bool success = flipper_application_load_name_and_icon(file_path, storage, icon_ptr, item_name); + furi_record_close(RECORD_STORAGE); + return success; +} + +static void + desktop_settings_scene_keybinds_action_type_submenu_callback(void* context, uint32_t index) { + DesktopSettingsApp* app = context; + scene_manager_set_scene_state( + app->scene_manager, DesktopSettingsAppSceneKeybindsActionType, index); + char* keybind = desktop_settings_app_get_keybind(app); + + switch(index) { + case DesktopSettingsAppKeybindActionTypeMainApp: + case DesktopSettingsAppKeybindActionTypeMoreActions: + scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneKeybindsAction); + break; + case DesktopSettingsAppKeybindActionTypeExternalApp: { + const DialogsFileBrowserOptions browser_options = { + .extension = ".fap", + .icon = &I_unknown_10px, + .skip_assets = true, + .hide_ext = true, + .item_loader_callback = keybinds_fap_selector_item_callback, + .item_loader_context = app, + .base_path = EXT_PATH("apps"), + }; + FuriString* temp_path = furi_string_alloc_set_str(EXT_PATH("apps")); + if(storage_file_exists(furi_record_open(RECORD_STORAGE), keybind)) { + furi_string_set_str(temp_path, keybind); + } + furi_record_close(RECORD_STORAGE); + if(dialog_file_browser_show(app->dialogs, temp_path, temp_path, &browser_options)) { + submenu_reset(app->submenu); // Prevent menu from being shown when we exiting scene + strncpy(keybind, furi_string_get_cstr(temp_path), MAX_KEYBIND_LENGTH); + DESKTOP_KEYBINDS_SAVE(&app->desktop->keybinds, sizeof(app->desktop->keybinds)); + scene_manager_search_and_switch_to_previous_scene( + app->scene_manager, DesktopSettingsAppSceneStart); + } + furi_string_free(temp_path); + break; + } + case DesktopSettingsAppKeybindActionTypeRemoveKeybind: + strncpy(keybind, "", MAX_KEYBIND_LENGTH); + DESKTOP_KEYBINDS_SAVE(&app->desktop->keybinds, sizeof(app->desktop->keybinds)); + scene_manager_search_and_switch_to_previous_scene( + app->scene_manager, DesktopSettingsAppSceneStart); + break; + default: + break; + } +} + +void desktop_settings_scene_keybinds_action_type_on_enter(void* context) { + DesktopSettingsApp* app = context; + Submenu* submenu = app->submenu; + char* keybind = desktop_settings_app_get_keybind(app); + submenu_reset(submenu); + + submenu_add_item( + submenu, + "Main App", + DesktopSettingsAppKeybindActionTypeMainApp, + desktop_settings_scene_keybinds_action_type_submenu_callback, + app); + + submenu_add_item( + submenu, + "External App", + DesktopSettingsAppKeybindActionTypeExternalApp, + desktop_settings_scene_keybinds_action_type_submenu_callback, + app); + + submenu_add_item( + submenu, + "More Actions", + DesktopSettingsAppKeybindActionTypeMoreActions, + desktop_settings_scene_keybinds_action_type_submenu_callback, + app); + + submenu_add_item( + submenu, + "Remove Keybind", + DesktopSettingsAppKeybindActionTypeRemoveKeybind, + desktop_settings_scene_keybinds_action_type_submenu_callback, + app); + + DesktopSettingsAppKeybindActionType selected = scene_manager_get_scene_state( + app->scene_manager, DesktopSettingsAppSceneKeybindsActionType); + if(selected == DesktopSettingsAppKeybindActionTypeRemoveKeybind) { + for(size_t i = 0; i < FLIPPER_APPS_COUNT; i++) { + if(!strncmp(FLIPPER_APPS[i].name, keybind, MAX_KEYBIND_LENGTH)) { + selected = DesktopSettingsAppKeybindActionTypeMainApp; + } + } + + if(storage_file_exists(furi_record_open(RECORD_STORAGE), keybind)) { + selected = DesktopSettingsAppKeybindActionTypeExternalApp; + } + furi_record_close(RECORD_STORAGE); + + for(size_t i = 0; i < EXTRA_KEYBINDS_COUNT; i++) { + if(!strncmp(EXTRA_KEYBINDS[i], keybind, MAX_KEYBIND_LENGTH)) { + selected = DesktopSettingsAppKeybindActionTypeMoreActions; + } + } + + if(!strnlen(keybind, MAX_KEYBIND_LENGTH)) { + selected = DesktopSettingsAppKeybindActionTypeRemoveKeybind; + } + } + + submenu_set_header(submenu, "Keybind action:"); + submenu_set_selected_item(submenu, selected); + + view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewMenu); +} + +bool desktop_settings_scene_keybinds_action_type_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + } + + return consumed; +} + +void desktop_settings_scene_keybinds_action_type_on_exit(void* context) { + DesktopSettingsApp* app = context; + submenu_reset(app->submenu); +} diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_keybinds_key.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_keybinds_key.c new file mode 100644 index 0000000000..7ac1e72f59 --- /dev/null +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_keybinds_key.c @@ -0,0 +1,61 @@ +#include + +#include "../desktop_settings_app.h" +#include "desktop_settings_scene.h" + +static void desktop_settings_scene_keybinds_key_submenu_callback(void* context, uint32_t index) { + DesktopSettingsApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +void desktop_settings_scene_keybinds_key_on_enter(void* context) { + DesktopSettingsApp* app = context; + Submenu* submenu = app->submenu; + submenu_reset(submenu); + + submenu_add_item( + submenu, "Up", KeybindKeyUp, desktop_settings_scene_keybinds_key_submenu_callback, app); + + submenu_add_item( + submenu, "Down", KeybindKeyDown, desktop_settings_scene_keybinds_key_submenu_callback, app); + + submenu_add_item( + submenu, + "Right", + KeybindKeyRight, + desktop_settings_scene_keybinds_key_submenu_callback, + app); + + submenu_add_item( + submenu, "Left", KeybindKeyLeft, desktop_settings_scene_keybinds_key_submenu_callback, app); + + submenu_set_header(submenu, "Keybind key:"); + + submenu_set_selected_item( + submenu, + scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppSceneKeybindsKey)); + + view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewMenu); +} + +bool desktop_settings_scene_keybinds_key_on_event(void* context, SceneManagerEvent event) { + DesktopSettingsApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + scene_manager_set_scene_state( + app->scene_manager, DesktopSettingsAppSceneKeybindsKey, event.event); + scene_manager_set_scene_state( + app->scene_manager, + DesktopSettingsAppSceneKeybindsActionType, + DesktopSettingsAppKeybindActionTypeRemoveKeybind); + scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneKeybindsActionType); + } + return consumed; +} + +void desktop_settings_scene_keybinds_key_on_exit(void* context) { + DesktopSettingsApp* app = context; + submenu_reset(app->submenu); +} diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_keybinds_type.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_keybinds_type.c new file mode 100644 index 0000000000..645f887389 --- /dev/null +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_keybinds_type.c @@ -0,0 +1,56 @@ +#include + +#include "../desktop_settings_app.h" +#include "desktop_settings_scene.h" + +static void desktop_settings_scene_keybinds_type_submenu_callback(void* context, uint32_t index) { + DesktopSettingsApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +void desktop_settings_scene_keybinds_type_on_enter(void* context) { + DesktopSettingsApp* app = context; + Submenu* submenu = app->submenu; + submenu_reset(submenu); + + submenu_add_item( + submenu, + "Press", + KeybindTypePress, + desktop_settings_scene_keybinds_type_submenu_callback, + app); + + submenu_add_item( + submenu, + "Hold", + KeybindTypeHold, + desktop_settings_scene_keybinds_type_submenu_callback, + app); + + submenu_set_header(submenu, "Keybind type:"); + + submenu_set_selected_item( + submenu, + scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppSceneKeybindsType)); + + view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewMenu); +} + +bool desktop_settings_scene_keybinds_type_on_event(void* context, SceneManagerEvent event) { + DesktopSettingsApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + scene_manager_set_scene_state( + app->scene_manager, DesktopSettingsAppSceneKeybindsType, event.event); + scene_manager_set_scene_state(app->scene_manager, DesktopSettingsAppSceneKeybindsKey, 0); + scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneKeybindsKey); + } + return consumed; +} + +void desktop_settings_scene_keybinds_type_on_exit(void* context) { + DesktopSettingsApp* app = context; + submenu_reset(app->submenu); +} diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_auth.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_auth.c index f153aac7ab..26b58d9ac0 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_auth.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_auth.c @@ -18,7 +18,7 @@ static void pin_auth_done_callback(const PinCode* pin_code, void* context) { DesktopSettingsApp* app = context; app->pincode_buffer = *pin_code; - if(desktop_pin_compare(&app->settings.pin_code, pin_code)) { + if(desktop_pin_compare(&app->desktop->settings.pin_code, pin_code)) { view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_PINS_EQUAL); } else { view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_PINS_DIFFERENT); @@ -33,8 +33,7 @@ static void pin_auth_back_callback(void* context) { void desktop_settings_scene_pin_auth_on_enter(void* context) { DesktopSettingsApp* app = context; - DESKTOP_SETTINGS_LOAD(&app->settings); - furi_assert(app->settings.pin_code.length > 0); + furi_assert(desktop_pin_is_valid(&app->desktop->settings.pin_code)); desktop_view_pin_input_set_context(app->pin_input_view, app); desktop_view_pin_input_set_back_callback(app->pin_input_view, pin_auth_back_callback); diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_disable.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_disable.c index 2969db9ea3..dfa5f8b91d 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_disable.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_disable.c @@ -18,9 +18,10 @@ static void pin_disable_back_callback(void* context) { void desktop_settings_scene_pin_disable_on_enter(void* context) { furi_assert(context); DesktopSettingsApp* app = context; - app->settings.pin_code.length = 0; - memset(app->settings.pin_code.data, '0', sizeof(app->settings.pin_code.data)); - DESKTOP_SETTINGS_SAVE(&app->settings); + app->desktop->settings.pin_code.length = 0; + memset( + app->desktop->settings.pin_code.data, '0', sizeof(app->desktop->settings.pin_code.data)); + app->save_settings = true; popup_set_context(app->popup, app); popup_set_callback(app->popup, pin_disable_back_callback); diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_menu.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_menu.c index 5721846c65..d9738f7a4d 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_menu.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_menu.c @@ -4,6 +4,7 @@ #include "../desktop_settings_app.h" #include "desktop_settings_scene.h" #include "desktop_settings_scene_i.h" +#include "desktop/helpers/pin.h" #define SCENE_EVENT_SET_PIN 0 #define SCENE_EVENT_CHANGE_PIN 1 @@ -19,7 +20,7 @@ void desktop_settings_scene_pin_menu_on_enter(void* context) { Submenu* submenu = app->submenu; submenu_reset(submenu); - if(!app->settings.pin_code.length) { + if(!desktop_pin_is_valid(&app->desktop->settings.pin_code)) { submenu_add_item( submenu, "Set Pin", diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup_done.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup_done.c index f75be807a7..24ac68006f 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup_done.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup_done.c @@ -23,8 +23,6 @@ static void pin_setup_done_callback(const PinCode* pin_code, void* context) { void desktop_settings_scene_pin_setup_done_on_enter(void* context) { DesktopSettingsApp* app = context; - app->settings.pin_code = app->pincode_buffer; - DESKTOP_SETTINGS_SAVE(&app->settings); NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); notification_message(notification, &sequence_single_vibro); furi_record_close(RECORD_NOTIFICATION); @@ -32,7 +30,7 @@ void desktop_settings_scene_pin_setup_done_on_enter(void* context) { desktop_view_pin_input_set_context(app->pin_input_view, app); desktop_view_pin_input_set_back_callback(app->pin_input_view, NULL); desktop_view_pin_input_set_done_callback(app->pin_input_view, pin_setup_done_callback); - desktop_view_pin_input_set_pin(app->pin_input_view, &app->settings.pin_code); + desktop_view_pin_input_set_pin(app->pin_input_view, &app->pincode_buffer); desktop_view_pin_input_set_label_button(app->pin_input_view, "Done"); desktop_view_pin_input_set_label_primary(app->pin_input_view, 29, 8, "PIN Activated!"); desktop_view_pin_input_set_label_secondary( @@ -49,6 +47,8 @@ bool desktop_settings_scene_pin_setup_done_on_event(void* context, SceneManagerE if(event.type == SceneManagerEventTypeCustom) { switch(event.event) { case SCENE_EVENT_DONE: { + memcpy(&app->desktop->settings.pin_code, &app->pincode_buffer, sizeof(PinCode)); + app->save_settings = true; bool scene_found = false; scene_found = scene_manager_search_and_switch_to_previous_scene( app->scene_manager, DesktopSettingsAppScenePinMenu); diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_start.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_start.c index 48031904b9..05a18b9ea6 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_start.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_start.c @@ -3,15 +3,13 @@ #include "../desktop_settings_app.h" #include "desktop_settings_scene.h" -#include -#define SCENE_EVENT_SELECT_FAVORITE_PRIMARY 0 -#define SCENE_EVENT_SELECT_FAVORITE_SECONDARY 1 -// #define SCENE_EVENT_SELECT_FAVORITE_GAME 2 -#define SCENE_EVENT_SELECT_PIN_SETUP 2 -#define SCENE_EVENT_SELECT_AUTO_LOCK_DELAY 3 -#define SCENE_EVENT_SELECT_AUTO_LOCK_PIN 4 -#define SCENE_EVENT_SELECT_CLOCK_DISPLAY 5 +enum VarItemListIndex { + VarItemListIndexKeybinds, + VarItemListIndexPinSetup, + VarItemListIndexAutoLockTime, + VarItemListIndexAutoLockPin, +}; #define AUTO_LOCK_DELAY_COUNT 9 const char* const auto_lock_delay_text[AUTO_LOCK_DELAY_COUNT] = { @@ -28,33 +26,18 @@ const char* const auto_lock_delay_text[AUTO_LOCK_DELAY_COUNT] = { const uint32_t auto_lock_delay_value[AUTO_LOCK_DELAY_COUNT] = {0, 10000, 15000, 30000, 60000, 90000, 120000, 300000, 600000}; -#define CLOCK_ENABLE_COUNT 2 -const char* const clock_enable_text[CLOCK_ENABLE_COUNT] = { - "OFF", - "ON", -}; - -const uint32_t clock_enable_value[CLOCK_ENABLE_COUNT] = {0, 1}; - static void desktop_settings_scene_start_var_list_enter_callback(void* context, uint32_t index) { DesktopSettingsApp* app = context; view_dispatcher_send_custom_event(app->view_dispatcher, index); } -static void desktop_settings_scene_start_clock_enable_changed(VariableItem* item) { - DesktopSettingsApp* app = variable_item_get_context(item); - uint8_t index = variable_item_get_current_value_index(item); - - variable_item_set_current_value_text(item, clock_enable_text[index]); - app->settings.display_clock = index; -} - static void desktop_settings_scene_start_auto_lock_delay_changed(VariableItem* item) { DesktopSettingsApp* app = variable_item_get_context(item); uint8_t index = variable_item_get_current_value_index(item); variable_item_set_current_value_text(item, auto_lock_delay_text[index]); - app->settings.auto_lock_delay_ms = auto_lock_delay_value[index]; + app->desktop->settings.auto_lock_delay_ms = auto_lock_delay_value[index]; + app->save_settings = true; } static void desktop_settings_scene_start_auto_lock_pin_changed(VariableItem* item) { @@ -62,7 +45,8 @@ static void desktop_settings_scene_start_auto_lock_pin_changed(VariableItem* ite uint8_t value = variable_item_get_current_value_index(item); variable_item_set_current_value_text(item, value ? "ON" : "OFF"); - app->settings.auto_lock_with_pin = value; + app->desktop->settings.auto_lock_with_pin = value; + app->save_settings = true; } void desktop_settings_scene_start_on_enter(void* context) { @@ -72,11 +56,7 @@ void desktop_settings_scene_start_on_enter(void* context) { VariableItem* item; uint8_t value_index; - variable_item_list_add(variable_item_list, "Primary Fav App (Up)", 1, NULL, NULL); - - variable_item_list_add(variable_item_list, "Secondary Fav App (Down)", 1, NULL, NULL); - - // variable_item_list_add(variable_item_list, "Favorite Game", 1, NULL, NULL); + variable_item_list_add(variable_item_list, "Keybinds Setup", 1, NULL, NULL); variable_item_list_add(variable_item_list, "PIN Setup", 1, NULL, NULL); @@ -88,7 +68,7 @@ void desktop_settings_scene_start_on_enter(void* context) { app); value_index = value_index_uint32( - app->settings.auto_lock_delay_ms, auto_lock_delay_value, AUTO_LOCK_DELAY_COUNT); + app->desktop->settings.auto_lock_delay_ms, auto_lock_delay_value, AUTO_LOCK_DELAY_COUNT); variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_text(item, auto_lock_delay_text[value_index]); @@ -99,20 +79,9 @@ void desktop_settings_scene_start_on_enter(void* context) { desktop_settings_scene_start_auto_lock_pin_changed, app); - variable_item_set_current_value_index(item, app->settings.auto_lock_with_pin); - variable_item_set_current_value_text(item, app->settings.auto_lock_with_pin ? "ON" : "OFF"); - - item = variable_item_list_add( - variable_item_list, - "Show Clock", - CLOCK_ENABLE_COUNT, - desktop_settings_scene_start_clock_enable_changed, // - app); - - value_index = - value_index_uint32(app->settings.display_clock, clock_enable_value, CLOCK_ENABLE_COUNT); - variable_item_set_current_value_index(item, value_index); - variable_item_set_current_value_text(item, clock_enable_text[value_index]); + variable_item_set_current_value_index(item, app->desktop->settings.auto_lock_with_pin); + variable_item_set_current_value_text( + item, app->desktop->settings.auto_lock_with_pin ? "ON" : "OFF"); variable_item_list_set_enter_callback( variable_item_list, desktop_settings_scene_start_var_list_enter_callback, app); @@ -120,39 +89,22 @@ void desktop_settings_scene_start_on_enter(void* context) { view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewVarItemList); } -bool desktop_settings_scene_start_on_event(void* context, SceneManagerEvent sme) { +bool desktop_settings_scene_start_on_event(void* context, SceneManagerEvent event) { DesktopSettingsApp* app = context; bool consumed = false; - if(sme.type == SceneManagerEventTypeCustom) { - switch(sme.event) { - case SCENE_EVENT_SELECT_FAVORITE_PRIMARY: + if(event.type == SceneManagerEventTypeCustom) { + switch(event.event) { + case VarItemListIndexKeybinds: scene_manager_set_scene_state( - app->scene_manager, DesktopSettingsAppSceneFavorite, true); - scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneFavorite); + app->scene_manager, DesktopSettingsAppSceneKeybindsType, 0); + scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneKeybindsType); consumed = true; break; - case SCENE_EVENT_SELECT_FAVORITE_SECONDARY: - scene_manager_set_scene_state( - app->scene_manager, DesktopSettingsAppSceneFavorite, false); - scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneFavorite); - consumed = true; - break; - // case SCENE_EVENT_SELECT_FAVORITE_GAME: - // scene_manager_set_scene_state(app->scene_manager, DesktopSettingsAppSceneFavorite, 2); - // scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneFavorite); - // consumed = true; - // break; - case SCENE_EVENT_SELECT_PIN_SETUP: + case VarItemListIndexPinSetup: scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinMenu); consumed = true; break; - case SCENE_EVENT_SELECT_AUTO_LOCK_DELAY: - consumed = true; - break; - case SCENE_EVENT_SELECT_CLOCK_DISPLAY: - consumed = true; - break; } } return consumed; @@ -161,10 +113,4 @@ bool desktop_settings_scene_start_on_event(void* context, SceneManagerEvent sme) void desktop_settings_scene_start_on_exit(void* context) { DesktopSettingsApp* app = context; variable_item_list_reset(app->variable_item_list); - DESKTOP_SETTINGS_SAVE(&app->settings); - - // Trigger UI update in case we changed battery layout - Power* power = furi_record_open(RECORD_POWER); - power_trigger_ui_update(power); - furi_record_close(RECORD_POWER); } diff --git a/applications/settings/notification_settings/notification_settings_app.c b/applications/settings/notification_settings/notification_settings_app.c index 364734a62d..04c0219138 100644 --- a/applications/settings/notification_settings/notification_settings_app.c +++ b/applications/settings/notification_settings/notification_settings_app.c @@ -20,6 +20,34 @@ static const NotificationSequence sequence_note_c = { NULL, }; +#define CONTRAST_COUNT 11 +const char* const contrast_text[CONTRAST_COUNT] = { + "-5", + "-4", + "-3", + "-2", + "-1", + "0", + "+1", + "+2", + "+3", + "+4", + "+5", +}; +const int32_t contrast_value[CONTRAST_COUNT] = { + -5, + -4, + -3, + -2, + -1, + 0, + 1, + 2, + 3, + 4, + 5, +}; + #define BACKLIGHT_COUNT 21 const char* const backlight_text[BACKLIGHT_COUNT] = { "0%", "5%", "10%", "15%", "20%", "25%", "30%", "35%", "40%", "45%", "50%", @@ -64,6 +92,15 @@ const char* const vibro_text[VIBRO_COUNT] = { }; const bool vibro_value[VIBRO_COUNT] = {false, true}; +static void contrast_changed(VariableItem* item) { + NotificationAppSettings* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + + variable_item_set_current_value_text(item, contrast_text[index]); + app->notification->settings.contrast = contrast_value[index]; + notification_message(app->notification, &sequence_lcd_contrast_update); +} + static void backlight_changed(VariableItem* item) { NotificationAppSettings* app = variable_item_get_context(item); uint8_t index = variable_item_get_current_value_index(item); @@ -136,6 +173,13 @@ static NotificationAppSettings* alloc_settings() { VariableItem* item; uint8_t value_index; + item = variable_item_list_add( + app->variable_item_list, "LCD Contrast", CONTRAST_COUNT, contrast_changed, app); + value_index = + value_index_int32(app->notification->settings.contrast, contrast_value, CONTRAST_COUNT); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, contrast_text[value_index]); + item = variable_item_list_add( app->variable_item_list, "LCD Brightness", BACKLIGHT_COUNT, backlight_changed, app); value_index = value_index_float( diff --git a/applications/settings/power_settings_app/power_settings_app.c b/applications/settings/power_settings_app/power_settings_app.c index 1d5aafef19..3f969128f2 100644 --- a/applications/settings/power_settings_app/power_settings_app.c +++ b/applications/settings/power_settings_app/power_settings_app.c @@ -19,7 +19,7 @@ static void power_settings_tick_event_callback(void* context) { scene_manager_handle_tick_event(app->scene_manager); } -PowerSettingsApp* power_settings_app_alloc(uint32_t first_scene) { +PowerSettingsApp* power_settings_app_alloc(uint32_t first_scene, ViewDispatcherType type) { PowerSettingsApp* app = malloc(sizeof(PowerSettingsApp)); app->about_battery = first_scene == PowerSettingsAppSceneBatteryInfo; @@ -42,7 +42,10 @@ PowerSettingsApp* power_settings_app_alloc(uint32_t first_scene) { app->view_dispatcher, power_settings_back_event_callback); view_dispatcher_set_tick_event_callback( app->view_dispatcher, power_settings_tick_event_callback, 2000); - view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, type); + if(type == ViewDispatcherTypeDesktop) { + gui_set_hide_statusbar(app->gui, true); + } // Views app->battery_info = battery_info_alloc(); @@ -95,14 +98,16 @@ void power_settings_app_free(PowerSettingsApp* app) { int32_t power_settings_app(void* p) { uint32_t first_scene = PowerSettingsAppSceneStart; + ViewDispatcherType type = ViewDispatcherTypeFullscreen; if(p && strlen(p)) { if(!strcmp(p, "off")) { first_scene = PowerSettingsAppScenePowerOff; + type = ViewDispatcherTypeDesktop; } else if(!strcmp(p, "about_battery")) { first_scene = PowerSettingsAppSceneBatteryInfo; } } - PowerSettingsApp* app = power_settings_app_alloc(first_scene); + PowerSettingsApp* app = power_settings_app_alloc(first_scene, type); while(true) { view_dispatcher_run(app->view_dispatcher); if(app->battery_info->exit_to_about) { @@ -114,6 +119,9 @@ int32_t power_settings_app(void* p) { } break; } + if(type == ViewDispatcherTypeDesktop) { + gui_set_hide_statusbar(app->gui, false); + } power_settings_app_free(app); return 0; } diff --git a/applications/system/storage_move_to_sd/storage_move_to_sd.c b/applications/system/storage_move_to_sd/storage_move_to_sd.c index 9c91b92668..25893f0110 100644 --- a/applications/system/storage_move_to_sd/storage_move_to_sd.c +++ b/applications/system/storage_move_to_sd/storage_move_to_sd.c @@ -172,7 +172,7 @@ static void storage_move_to_sd_mount_callback(const void* message, void* context if(storage_event->type == StorageEventTypeCardMount) { Loader* loader = furi_record_open(RECORD_LOADER); - loader_start(loader, "StorageMoveToSd", NULL); + loader_start(loader, "StorageMoveToSd", NULL, NULL); furi_record_close(RECORD_LOADER); } } diff --git a/applications/system/updater/cli/updater_cli.c b/applications/system/updater/cli/updater_cli.c index 659c431f70..cebdc4d7ca 100644 --- a/applications/system/updater/cli/updater_cli.c +++ b/applications/system/updater/cli/updater_cli.c @@ -99,7 +99,7 @@ static void updater_start_app(void* context, uint32_t arg) { * So, accessing its record would cause a deadlock */ Loader* loader = furi_record_open(RECORD_LOADER); - loader_start(loader, "UpdaterApp", NULL); + loader_start(loader, "UpdaterApp", NULL, NULL); furi_record_close(RECORD_LOADER); } diff --git a/applications/system/updater/updater.c b/applications/system/updater/updater.c index 59af8a3b01..9d3a7bab86 100644 --- a/applications/system/updater/updater.c +++ b/applications/system/updater/updater.c @@ -7,7 +7,6 @@ #include #include #include -#include static bool updater_custom_event_callback(void* context, uint32_t event) { furi_assert(context); @@ -35,7 +34,6 @@ static void Updater* updater_alloc(char* arg) { Updater* updater = malloc(sizeof(Updater)); - process_favorite_launch(&arg); if(arg && strlen(arg)) { updater->startup_arg = furi_string_alloc_set(arg); furi_string_replace(updater->startup_arg, ANY_PATH(""), EXT_PATH("")); diff --git a/assets/SConscript b/assets/SConscript index 9bd273626d..848b730f46 100644 --- a/assets/SConscript +++ b/assets/SConscript @@ -9,9 +9,12 @@ assetsenv = env.Clone( ) assetsenv.ApplyLibFlags() -icons = assetsenv.CompileIcons( - assetsenv.Dir("compiled"), assetsenv.Dir("#/assets/icons") +icons_path = ( + assetsenv.Dir("#/build/icons") + if assetsenv["IS_BASE_FIRMWARE"] + else assetsenv.Dir("compiled") ) +icons = assetsenv.CompileIcons(icons_path, assetsenv.Dir("#/assets/icons")) assetsenv.Alias("icons", icons) diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_0.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_0.png new file mode 100644 index 0000000000..8b8dc80bce Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_0.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_1.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_1.png new file mode 100644 index 0000000000..956de61707 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_1.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_10.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_10.png new file mode 100644 index 0000000000..93fdaa0767 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_10.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_11.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_11.png new file mode 100644 index 0000000000..a4e194825e Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_11.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_12.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_12.png new file mode 100644 index 0000000000..9cbcbd070e Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_12.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_13.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_13.png new file mode 100644 index 0000000000..a745cdb03f Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_13.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_14.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_14.png new file mode 100644 index 0000000000..768f471ec1 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_14.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_15.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_15.png new file mode 100644 index 0000000000..2f50fb8c94 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_15.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_16.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_16.png new file mode 100644 index 0000000000..fc7b76696c Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_16.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_17.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_17.png new file mode 100644 index 0000000000..013e2008a2 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_17.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_18.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_18.png new file mode 100644 index 0000000000..795120e772 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_18.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_19.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_19.png new file mode 100644 index 0000000000..52061a5a26 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_19.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_2.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_2.png new file mode 100644 index 0000000000..10afed391e Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_2.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_20.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_20.png new file mode 100644 index 0000000000..52f87f3a88 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_20.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_21.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_21.png new file mode 100644 index 0000000000..9696e0b5cc Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_21.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_22.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_22.png new file mode 100644 index 0000000000..d23ee492b0 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_22.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_23.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_23.png new file mode 100644 index 0000000000..9d368900c9 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_23.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_24.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_24.png new file mode 100644 index 0000000000..daf0788ad7 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_24.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_25.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_25.png new file mode 100644 index 0000000000..b40333654f Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_25.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_26.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_26.png new file mode 100644 index 0000000000..9d499f23d2 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_26.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_27.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_27.png new file mode 100644 index 0000000000..0291dfb583 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_27.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_28.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_28.png new file mode 100644 index 0000000000..54a889d815 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_28.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_29.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_29.png new file mode 100644 index 0000000000..ba79b3b88f Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_29.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_3.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_3.png new file mode 100644 index 0000000000..bedf366c6c Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_3.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_30.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_30.png new file mode 100644 index 0000000000..0731760dff Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_30.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_31.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_31.png new file mode 100644 index 0000000000..898efdc4b8 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_31.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_32.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_32.png new file mode 100644 index 0000000000..39f5db8a01 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_32.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_33.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_33.png new file mode 100644 index 0000000000..bee4cff087 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_33.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_34.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_34.png new file mode 100644 index 0000000000..969b91193d Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_34.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_35.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_35.png new file mode 100644 index 0000000000..a72cf1823e Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_35.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_36.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_36.png new file mode 100644 index 0000000000..9a13e7c67d Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_36.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_37.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_37.png new file mode 100644 index 0000000000..da3ee77f30 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_37.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_38.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_38.png new file mode 100644 index 0000000000..93da7f4f94 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_38.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_39.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_39.png new file mode 100644 index 0000000000..7510931b44 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_39.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_4.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_4.png new file mode 100644 index 0000000000..f99454b164 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_4.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_40.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_40.png new file mode 100644 index 0000000000..a17a14044a Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_40.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_41.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_41.png new file mode 100644 index 0000000000..f763540c91 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_41.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_42.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_42.png new file mode 100644 index 0000000000..97a829e9b7 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_42.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_43.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_43.png new file mode 100644 index 0000000000..7eadf75185 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_43.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_44.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_44.png new file mode 100644 index 0000000000..5241195d32 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_44.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_45.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_45.png new file mode 100644 index 0000000000..2a3ea8e23a Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_45.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_46.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_46.png new file mode 100644 index 0000000000..f4b263b199 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_46.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_47.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_47.png new file mode 100644 index 0000000000..1563ff39b2 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_47.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_5.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_5.png new file mode 100644 index 0000000000..394bb53df4 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_5.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_6.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_6.png new file mode 100644 index 0000000000..3f84ad47e3 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_6.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_7.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_7.png new file mode 100644 index 0000000000..8ea650ad07 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_7.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_8.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_8.png new file mode 100644 index 0000000000..2c8cf3f5d7 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_8.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_9.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_9.png new file mode 100644 index 0000000000..caf6a90ef6 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_9.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/meta.txt b/assets/dolphin/external/L1_Kaiju_128x64/meta.txt new file mode 100644 index 0000000000..0276e3eef4 --- /dev/null +++ b/assets/dolphin/external/L1_Kaiju_128x64/meta.txt @@ -0,0 +1,50 @@ +Filetype: Flipper Animation +Version: 1 + +Width: 128 +Height: 64 +Passive frames: 16 +Active frames: 60 +Frames order: 0 1 2 1 0 3 4 5 6 3 7 1 8 1 7 9 0 10 11 12 13 14 15 16 14 12 17 18 19 20 21 22 22 23 22 24 25 26 27 26 25 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 19 18 14 45 14 45 14 45 14 45 14 16 14 14 11 46 47 1 +Active cycles: 1 +Frame rate: 2 +Duration: 360 +Active cooldown: 7 + +Bubble slots: 1 + +Slot: 0 +X: 83 +Y: 42 +Text: Sup +AlignH: Left +AlignV: Top +StartFrame: 38 +EndFrame: 40 + +Slot: 0 +X: 66 +Y: 35 +Text: What just +AlignH: Left +AlignV: Center +StartFrame: 62 +EndFrame: 65 + +Slot: 0 +X: 66 +Y: 35 +Text: happened? +AlignH: Left +AlignV: Center +StartFrame: 66 +EndFrame: 68 + +Slot: 0 +X: 87 +Y: 38 +Text: Idk +AlignH: Left +AlignV: Top +StartFrame: 70 +EndFrame: 70 diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_0.png b/assets/dolphin/external/L1_Senpai_128x64/frame_0.png new file mode 100644 index 0000000000..ed37723ac2 Binary files /dev/null and b/assets/dolphin/external/L1_Senpai_128x64/frame_0.png differ diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_1.png b/assets/dolphin/external/L1_Senpai_128x64/frame_1.png new file mode 100644 index 0000000000..ad708ee431 Binary files /dev/null and b/assets/dolphin/external/L1_Senpai_128x64/frame_1.png differ diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_10.png b/assets/dolphin/external/L1_Senpai_128x64/frame_10.png new file mode 100644 index 0000000000..e385018bf6 Binary files /dev/null and b/assets/dolphin/external/L1_Senpai_128x64/frame_10.png differ diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_11.png b/assets/dolphin/external/L1_Senpai_128x64/frame_11.png new file mode 100644 index 0000000000..553a979be1 Binary files /dev/null and b/assets/dolphin/external/L1_Senpai_128x64/frame_11.png differ diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_12.png b/assets/dolphin/external/L1_Senpai_128x64/frame_12.png new file mode 100644 index 0000000000..9f8ca7e9b8 Binary files /dev/null and b/assets/dolphin/external/L1_Senpai_128x64/frame_12.png differ diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_13.png b/assets/dolphin/external/L1_Senpai_128x64/frame_13.png new file mode 100644 index 0000000000..a996443fe4 Binary files /dev/null and b/assets/dolphin/external/L1_Senpai_128x64/frame_13.png differ diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_14.png b/assets/dolphin/external/L1_Senpai_128x64/frame_14.png new file mode 100644 index 0000000000..628d58b93a Binary files /dev/null and b/assets/dolphin/external/L1_Senpai_128x64/frame_14.png differ diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_15.png b/assets/dolphin/external/L1_Senpai_128x64/frame_15.png new file mode 100644 index 0000000000..cc8431ade8 Binary files /dev/null and b/assets/dolphin/external/L1_Senpai_128x64/frame_15.png differ diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_16.png b/assets/dolphin/external/L1_Senpai_128x64/frame_16.png new file mode 100644 index 0000000000..3ec3727989 Binary files /dev/null and b/assets/dolphin/external/L1_Senpai_128x64/frame_16.png differ diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_17.png b/assets/dolphin/external/L1_Senpai_128x64/frame_17.png new file mode 100644 index 0000000000..11b247eca2 Binary files /dev/null and b/assets/dolphin/external/L1_Senpai_128x64/frame_17.png differ diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_18.png b/assets/dolphin/external/L1_Senpai_128x64/frame_18.png new file mode 100644 index 0000000000..bb15041331 Binary files /dev/null and b/assets/dolphin/external/L1_Senpai_128x64/frame_18.png differ diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_19.png b/assets/dolphin/external/L1_Senpai_128x64/frame_19.png new file mode 100644 index 0000000000..f953c8ef19 Binary files /dev/null and b/assets/dolphin/external/L1_Senpai_128x64/frame_19.png differ diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_2.png b/assets/dolphin/external/L1_Senpai_128x64/frame_2.png new file mode 100644 index 0000000000..36c3b4abe1 Binary files /dev/null and b/assets/dolphin/external/L1_Senpai_128x64/frame_2.png differ diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_20.png b/assets/dolphin/external/L1_Senpai_128x64/frame_20.png new file mode 100644 index 0000000000..d683b9f625 Binary files /dev/null and b/assets/dolphin/external/L1_Senpai_128x64/frame_20.png differ diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_21.png b/assets/dolphin/external/L1_Senpai_128x64/frame_21.png new file mode 100644 index 0000000000..66cbfe1d8f Binary files /dev/null and b/assets/dolphin/external/L1_Senpai_128x64/frame_21.png differ diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_22.png b/assets/dolphin/external/L1_Senpai_128x64/frame_22.png new file mode 100644 index 0000000000..dd241d24ae Binary files /dev/null and b/assets/dolphin/external/L1_Senpai_128x64/frame_22.png differ diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_23.png b/assets/dolphin/external/L1_Senpai_128x64/frame_23.png new file mode 100644 index 0000000000..944bdc74e9 Binary files /dev/null and b/assets/dolphin/external/L1_Senpai_128x64/frame_23.png differ diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_24.png b/assets/dolphin/external/L1_Senpai_128x64/frame_24.png new file mode 100644 index 0000000000..3f445593af Binary files /dev/null and b/assets/dolphin/external/L1_Senpai_128x64/frame_24.png differ diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_25.png b/assets/dolphin/external/L1_Senpai_128x64/frame_25.png new file mode 100644 index 0000000000..ea7823bd74 Binary files /dev/null and b/assets/dolphin/external/L1_Senpai_128x64/frame_25.png differ diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_26.png b/assets/dolphin/external/L1_Senpai_128x64/frame_26.png new file mode 100644 index 0000000000..0b378fbccd Binary files /dev/null and b/assets/dolphin/external/L1_Senpai_128x64/frame_26.png differ diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_27.png b/assets/dolphin/external/L1_Senpai_128x64/frame_27.png new file mode 100644 index 0000000000..66eec542a9 Binary files /dev/null and b/assets/dolphin/external/L1_Senpai_128x64/frame_27.png differ diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_28.png b/assets/dolphin/external/L1_Senpai_128x64/frame_28.png new file mode 100644 index 0000000000..1e232ba91b Binary files /dev/null and b/assets/dolphin/external/L1_Senpai_128x64/frame_28.png differ diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_29.png b/assets/dolphin/external/L1_Senpai_128x64/frame_29.png new file mode 100644 index 0000000000..e2767bd655 Binary files /dev/null and b/assets/dolphin/external/L1_Senpai_128x64/frame_29.png differ diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_3.png b/assets/dolphin/external/L1_Senpai_128x64/frame_3.png new file mode 100644 index 0000000000..9a3c13f662 Binary files /dev/null and b/assets/dolphin/external/L1_Senpai_128x64/frame_3.png differ diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_30.png b/assets/dolphin/external/L1_Senpai_128x64/frame_30.png new file mode 100644 index 0000000000..36d1212bec Binary files /dev/null and b/assets/dolphin/external/L1_Senpai_128x64/frame_30.png differ diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_31.png b/assets/dolphin/external/L1_Senpai_128x64/frame_31.png new file mode 100644 index 0000000000..037bdc8ed6 Binary files /dev/null and b/assets/dolphin/external/L1_Senpai_128x64/frame_31.png differ diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_32.png b/assets/dolphin/external/L1_Senpai_128x64/frame_32.png new file mode 100644 index 0000000000..91ce188694 Binary files /dev/null and b/assets/dolphin/external/L1_Senpai_128x64/frame_32.png differ diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_33.png b/assets/dolphin/external/L1_Senpai_128x64/frame_33.png new file mode 100644 index 0000000000..e3e7799db4 Binary files /dev/null and b/assets/dolphin/external/L1_Senpai_128x64/frame_33.png differ diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_34.png b/assets/dolphin/external/L1_Senpai_128x64/frame_34.png new file mode 100644 index 0000000000..a28aac4e0f Binary files /dev/null and b/assets/dolphin/external/L1_Senpai_128x64/frame_34.png differ diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_35.png b/assets/dolphin/external/L1_Senpai_128x64/frame_35.png new file mode 100644 index 0000000000..04f8c1a7f3 Binary files /dev/null and b/assets/dolphin/external/L1_Senpai_128x64/frame_35.png differ diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_4.png b/assets/dolphin/external/L1_Senpai_128x64/frame_4.png new file mode 100644 index 0000000000..d065b77a12 Binary files /dev/null and b/assets/dolphin/external/L1_Senpai_128x64/frame_4.png differ diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_5.png b/assets/dolphin/external/L1_Senpai_128x64/frame_5.png new file mode 100644 index 0000000000..7a111afd03 Binary files /dev/null and b/assets/dolphin/external/L1_Senpai_128x64/frame_5.png differ diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_6.png b/assets/dolphin/external/L1_Senpai_128x64/frame_6.png new file mode 100644 index 0000000000..318c7eca0a Binary files /dev/null and b/assets/dolphin/external/L1_Senpai_128x64/frame_6.png differ diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_7.png b/assets/dolphin/external/L1_Senpai_128x64/frame_7.png new file mode 100644 index 0000000000..b56b995dc6 Binary files /dev/null and b/assets/dolphin/external/L1_Senpai_128x64/frame_7.png differ diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_8.png b/assets/dolphin/external/L1_Senpai_128x64/frame_8.png new file mode 100644 index 0000000000..6c4b875706 Binary files /dev/null and b/assets/dolphin/external/L1_Senpai_128x64/frame_8.png differ diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_9.png b/assets/dolphin/external/L1_Senpai_128x64/frame_9.png new file mode 100644 index 0000000000..00b02330e1 Binary files /dev/null and b/assets/dolphin/external/L1_Senpai_128x64/frame_9.png differ diff --git a/assets/dolphin/external/L1_Senpai_128x64/meta.txt b/assets/dolphin/external/L1_Senpai_128x64/meta.txt new file mode 100644 index 0000000000..122dafe7e1 --- /dev/null +++ b/assets/dolphin/external/L1_Senpai_128x64/meta.txt @@ -0,0 +1,23 @@ +Filetype: Flipper Animation +Version: 1 + +Width: 128 +Height: 64 +Passive frames: 16 +Active frames: 22 +Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 0 12 13 14 0 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 +Active cycles: 1 +Frame rate: 2 +Duration: 360 +Active cooldown: 7 + +Bubble slots: 1 + +Slot: 0 +X: 5 +Y: 29 +Text: SENPAI !!! +AlignH: Right +AlignV: Center +StartFrame: 28 +EndFrame: 31 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_0.png b/assets/dolphin/external/L2_Dj_128x64/frame_0.png new file mode 100644 index 0000000000..95f72f901a Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_0.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_1.png b/assets/dolphin/external/L2_Dj_128x64/frame_1.png new file mode 100644 index 0000000000..32e13541d8 Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_1.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_10.png b/assets/dolphin/external/L2_Dj_128x64/frame_10.png new file mode 100644 index 0000000000..3cce11f998 Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_10.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_11.png b/assets/dolphin/external/L2_Dj_128x64/frame_11.png new file mode 100644 index 0000000000..eca4a1296e Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_11.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_12.png b/assets/dolphin/external/L2_Dj_128x64/frame_12.png new file mode 100644 index 0000000000..5f92e47fdd Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_12.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_13.png b/assets/dolphin/external/L2_Dj_128x64/frame_13.png new file mode 100644 index 0000000000..1b1017ce25 Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_13.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_14.png b/assets/dolphin/external/L2_Dj_128x64/frame_14.png new file mode 100644 index 0000000000..2cf408c979 Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_14.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_15.png b/assets/dolphin/external/L2_Dj_128x64/frame_15.png new file mode 100644 index 0000000000..9b796498c0 Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_15.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_16.png b/assets/dolphin/external/L2_Dj_128x64/frame_16.png new file mode 100644 index 0000000000..c1545500cb Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_16.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_17.png b/assets/dolphin/external/L2_Dj_128x64/frame_17.png new file mode 100644 index 0000000000..80863f0b69 Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_17.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_18.png b/assets/dolphin/external/L2_Dj_128x64/frame_18.png new file mode 100644 index 0000000000..b4527bc833 Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_18.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_19.png b/assets/dolphin/external/L2_Dj_128x64/frame_19.png new file mode 100644 index 0000000000..f1531da4ef Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_19.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_2.png b/assets/dolphin/external/L2_Dj_128x64/frame_2.png new file mode 100644 index 0000000000..391cfe1e10 Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_2.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_20.png b/assets/dolphin/external/L2_Dj_128x64/frame_20.png new file mode 100644 index 0000000000..f63904f29b Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_20.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_21.png b/assets/dolphin/external/L2_Dj_128x64/frame_21.png new file mode 100644 index 0000000000..076448fa94 Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_21.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_22.png b/assets/dolphin/external/L2_Dj_128x64/frame_22.png new file mode 100644 index 0000000000..8651f12f8b Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_22.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_23.png b/assets/dolphin/external/L2_Dj_128x64/frame_23.png new file mode 100644 index 0000000000..d2d8e7e514 Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_23.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_24.png b/assets/dolphin/external/L2_Dj_128x64/frame_24.png new file mode 100644 index 0000000000..6080e7aa3f Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_24.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_25.png b/assets/dolphin/external/L2_Dj_128x64/frame_25.png new file mode 100644 index 0000000000..ec483addf8 Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_25.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_26.png b/assets/dolphin/external/L2_Dj_128x64/frame_26.png new file mode 100644 index 0000000000..90fc3b1380 Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_26.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_27.png b/assets/dolphin/external/L2_Dj_128x64/frame_27.png new file mode 100644 index 0000000000..39ddf46ab3 Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_27.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_28.png b/assets/dolphin/external/L2_Dj_128x64/frame_28.png new file mode 100644 index 0000000000..f433c969c6 Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_28.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_29.png b/assets/dolphin/external/L2_Dj_128x64/frame_29.png new file mode 100644 index 0000000000..263ffe15a8 Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_29.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_3.png b/assets/dolphin/external/L2_Dj_128x64/frame_3.png new file mode 100644 index 0000000000..40d1314c95 Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_3.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_30.png b/assets/dolphin/external/L2_Dj_128x64/frame_30.png new file mode 100644 index 0000000000..27c297e8d5 Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_30.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_31.png b/assets/dolphin/external/L2_Dj_128x64/frame_31.png new file mode 100644 index 0000000000..f2aefbfae2 Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_31.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_32.png b/assets/dolphin/external/L2_Dj_128x64/frame_32.png new file mode 100644 index 0000000000..852c24233f Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_32.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_33.png b/assets/dolphin/external/L2_Dj_128x64/frame_33.png new file mode 100644 index 0000000000..665ac5eafd Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_33.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_34.png b/assets/dolphin/external/L2_Dj_128x64/frame_34.png new file mode 100644 index 0000000000..81f133ac55 Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_34.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_35.png b/assets/dolphin/external/L2_Dj_128x64/frame_35.png new file mode 100644 index 0000000000..c828207d33 Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_35.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_36.png b/assets/dolphin/external/L2_Dj_128x64/frame_36.png new file mode 100644 index 0000000000..fc923b4023 Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_36.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_4.png b/assets/dolphin/external/L2_Dj_128x64/frame_4.png new file mode 100644 index 0000000000..d372ff643b Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_4.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_5.png b/assets/dolphin/external/L2_Dj_128x64/frame_5.png new file mode 100644 index 0000000000..5b52f95175 Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_5.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_6.png b/assets/dolphin/external/L2_Dj_128x64/frame_6.png new file mode 100644 index 0000000000..8a1e84a11e Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_6.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_7.png b/assets/dolphin/external/L2_Dj_128x64/frame_7.png new file mode 100644 index 0000000000..1fddffaa49 Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_7.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_8.png b/assets/dolphin/external/L2_Dj_128x64/frame_8.png new file mode 100644 index 0000000000..14ef1aded6 Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_8.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_9.png b/assets/dolphin/external/L2_Dj_128x64/frame_9.png new file mode 100644 index 0000000000..05de5d5c69 Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_9.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/meta.txt b/assets/dolphin/external/L2_Dj_128x64/meta.txt new file mode 100644 index 0000000000..c783962fd4 --- /dev/null +++ b/assets/dolphin/external/L2_Dj_128x64/meta.txt @@ -0,0 +1,14 @@ +Filetype: Flipper Animation +Version: 1 + +Width: 128 +Height: 64 +Passive frames: 15 +Active frames: 23 +Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 26 28 29 30 31 32 33 34 35 36 +Active cycles: 1 +Frame rate: 2 +Duration: 360 +Active cooldown: 5 + +Bubble slots: 0 diff --git a/assets/dolphin/external/manifest.txt b/assets/dolphin/external/manifest.txt index ae6129ff1c..24dd8ab9df 100644 --- a/assets/dolphin/external/manifest.txt +++ b/assets/dolphin/external/manifest.txt @@ -44,23 +44,23 @@ Max level: 30 Weight: 3 Name: L1_Cry_128x64 -Min butthurt: 0 +Min butthurt: 6 Max butthurt: 14 -Min level: 12 +Min level: 1 Max level: 30 Weight: 3 Name: L1_Boxing_128x64 -Min butthurt: 0 +Min butthurt: 12 Max butthurt: 14 -Min level: 3 +Min level: 16 Max level: 30 Weight: 3 Name: L1_Mad_fist_128x64 -Min butthurt: 0 +Min butthurt: 10 Max butthurt: 14 -Min level: 4 +Min level: 9 Max level: 30 Weight: 3 @@ -79,16 +79,30 @@ Max level: 30 Weight: 3 Name: L1_Leaving_sad_128x64 +Min butthurt: 8 +Max butthurt: 14 +Min level: 23 +Max level: 30 +Weight: 3 + +Name: L1_Senpai_128x64 Min butthurt: 0 Max butthurt: 14 -Min level: 21 +Min level: 3 Max level: 30 Weight: 3 +Name: L1_Kaiju_128x64 +Min butthurt: 0 +Max butthurt: 14 +Min level: 13 +Max level: 30 +Weight: 4 + Name: L2_Wake_up_128x64 Min butthurt: 0 Max butthurt: 14 -Min level: 22 +Min level: 12 Max level: 30 Weight: 3 @@ -113,6 +127,13 @@ Min level: 25 Max level: 30 Weight: 3 +Name: L2_Dj_128x64 +Min butthurt: 0 +Max butthurt: 14 +Min level: 21 +Max level: 30 +Weight: 4 + Name: L3_Furippa3_128x64 Min butthurt: 0 Max butthurt: 14 diff --git a/assets/icons/Archive/ir_scope_10px.png b/assets/icons/Archive/ir_scope_10px.png new file mode 100644 index 0000000000..c0d7eaba0e Binary files /dev/null and b/assets/icons/Archive/ir_scope_10px.png differ diff --git a/assets/icons/Archive/search_10px.png b/assets/icons/Archive/search_10px.png new file mode 100644 index 0000000000..6b52110745 Binary files /dev/null and b/assets/icons/Archive/search_10px.png differ diff --git a/assets/icons/Archive/subplaylist_10px.png b/assets/icons/Archive/subplaylist_10px.png new file mode 100644 index 0000000000..3d3f1d27f6 Binary files /dev/null and b/assets/icons/Archive/subplaylist_10px.png differ diff --git a/applications/external/hid_app/assets/Ble_connected_15x15.png b/assets/icons/NFC/ArrowC_1_36x36.png similarity index 86% rename from applications/external/hid_app/assets/Ble_connected_15x15.png rename to assets/icons/NFC/ArrowC_1_36x36.png index 64dab9b530..3a0c6dd0cb 100644 Binary files a/applications/external/hid_app/assets/Ble_connected_15x15.png and b/assets/icons/NFC/ArrowC_1_36x36.png differ diff --git a/assets/icons/NFC/Restoring_38x32.png b/assets/icons/NFC/Restoring_38x32.png deleted file mode 100644 index 9e058869f1..0000000000 Binary files a/assets/icons/NFC/Restoring_38x32.png and /dev/null differ diff --git a/assets/resources/apps_data/barcode_data/codabar_encodings.txt b/assets/resources/apps_data/barcode_data/codabar_encodings.txt new file mode 100644 index 0000000000..5f0684cbdf --- /dev/null +++ b/assets/resources/apps_data/barcode_data/codabar_encodings.txt @@ -0,0 +1,22 @@ +# alternates between bars and spaces, always begins with bar +# 0 for narrow, 1 for wide +0: 0000011 +1: 0000110 +2: 0001001 +3: 1100000 +4: 0010010 +5: 1000010 +6: 0100001 +7: 0100100 +8: 0110000 +9: 1001000 +-: 0001100 +$: 0011000 +:: 1000101 +/: 1010001 +.: 1010100 ++: 0010101 +A: 0011010 +B: 0101001 +C: 0001011 +D: 0001110 \ No newline at end of file diff --git a/assets/resources/apps_data/barcode_data/code128c_encodings.txt b/assets/resources/apps_data/barcode_data/code128c_encodings.txt new file mode 100644 index 0000000000..75cc71135d --- /dev/null +++ b/assets/resources/apps_data/barcode_data/code128c_encodings.txt @@ -0,0 +1,106 @@ +00: 11011001100 +01: 11001101100 +02: 11001100110 +03: 10010011000 +04: 10010001100 +05: 10001001100 +06: 10011001000 +07: 10011000100 +08: 10001100100 +09: 11001001000 +10: 11001000100 +11: 11000100100 +12: 10110011100 +13: 10011011100 +14: 10011001110 +15: 10111001100 +16: 10011101100 +17: 10011100110 +18: 11001110010 +19: 11001011100 +20: 11001001110 +21: 11011100100 +22: 11001110100 +23: 11101101110 +24: 11101001100 +25: 11100101100 +26: 11100100110 +27: 11101100100 +28: 11100110100 +29: 11100110010 +30: 11011011000 +31: 11011000110 +32: 11000110110 +33: 10100011000 +34: 10001011000 +35: 10001000110 +36: 10110001000 +37: 10001101000 +38: 10001100010 +39: 11010001000 +40: 11000101000 +41: 11000100010 +42: 10110111000 +43: 10110001110 +44: 10001101110 +45: 10111011000 +46: 10111000110 +47: 10001110110 +48: 11101110110 +49: 11010001110 +50: 11000101110 +51: 11011101000 +52: 11011100010 +53: 11011101110 +54: 11101011000 +55: 11101000110 +56: 11100010110 +57: 11101101000 +58: 11101100010 +59: 11100011010 +60: 11101111010 +61: 11001000010 +62: 11110001010 +63: 10100110000 +64: 10100001100 +65: 10010110000 +66: 10010000110 +67: 10000101100 +68: 10000100110 +69: 10110010000 +70: 10110000100 +71: 10011010000 +72: 10011000010 +73: 10000110100 +74: 10000110010 +75: 11000010010 +76: 11001010000 +77: 11110111010 +78: 11000010100 +79: 10001111010 +80: 10100111100 +81: 10010111100 +82: 10010011110 +83: 10111100100 +84: 10011110100 +85: 10011110010 +86: 11110100100 +87: 11110010100 +88: 11110010010 +89: 11011011110 +90: 11011110110 +91: 11110110110 +92: 10101111000 +93: 10100011110 +94: 10001011110 +95: 10111101000 +96: 10111100010 +97: 11110101000 +98: 11110100010 +99: 10111011110 +100: 10111101110 +101: 11101011110 +102: 11110101110 +103: 11010000100 +104: 11010010000 +105: 11010011100 diff --git a/assets/resources/apps_data/nrf24batch/CO2_mini.txt b/assets/resources/apps_data/nrf24batch/CO2_mini.txt new file mode 100644 index 0000000000..1fb281b414 --- /dev/null +++ b/assets/resources/apps_data/nrf24batch/CO2_mini.txt @@ -0,0 +1,83 @@ +Info: CO2 sensor mini +Address: C8C8CF +Rate: 1 +Ch: 122 +CRC: 2 +DPL: 0 +RETR: 0x0F +Resend: 3 +Delay_ms: 30 +ReadCmd repeat: 3 + +Payload struct: 2,1,1 +ROM=0x41;ROM2=0x42;WROM=0x81;WROM2=0x82;RAM=0x51;RAM2=0x52;WRAM=0x91;WRAM2=0x92;PGM=0x61;SET=0xC0 + +R default: ,,ROM +W default: n,,WROM +Write start: 0,0,0x2F + +Listen: C8C8C1=CO2,Alarm,- + +R: ID*=,0,0x65 + +R: _CO2*2=0x6C,,RAM2 +R: _PORTA=0x39,,RAM +W: _PORTA=,0x3B,WRAM +R: _PORTB=0x36,,RAM +W: _PORTB=,0x38,WRAM +R: _OSCCAL=0x51,,RAM +W: _OSCCAL=,0x51,WRAM +R: OSCCAL_EMEM=0 + +R: RxAddr=1# +W: RxAddr=,1 + +R: Ch=2 +W: Ch=,2 + +R: nRF RETR=3# +W: nRF RETR=,3 + +R: Send period=4 +W: Send period=,4 + +R: CO2 threshold*2=5,,ROM2 +W: CO2 threshold=,5,WROM2 + +R: CO2 correct*2=7,,ROM2 +W: CO2 correct*2=,7,WROM2 + +R: FanLSB[10]=i:9# +W: FanLSB=,i:9 + +R: Transmit pause=19 +W: Transmit pause=,19 + +R: Flags=20# +W: Flags=,20 + +W: Reset=0xEEEE,14,SET + +R: _LED Warning=0x74,,RAM +W: _LED Warning=,0x74,WRAM + +S: LED=,0,SET + +SBatch: LED On: LED=1 +SBatch: LED Off: LED=0 + +RBatch: Settings: ID;RxAddr;Ch;Send period;CO2 threshold;CO2 correct;FanLSB;nRF RETR;Transmit pause;Flags + +WBatch: Default: RxAddr=0xCF;Ch=122;Send period=30;CO2 threshold=1000;CO2 correct=0;FanLSB={0xC1,0,0,0,0,0,0,0};nRF RETR=0x3;Transmit pause=1;Flags=0;Reset +WBatch: CO2: Send period=60;CO2 threshold=1000;CO2 correct=0 +WBatch: Fan: FanLSB={0xC1,0,0,0,0,0,0,0,0,0};Reset +WBatch: RETR: nRF RETR=0x2F;Reset +WBatch: Transmit pause: Transmit pause=30 +WBatch: Flags: Flags=0x00 + +WBatch: LED Warning: _LED Warning=0x30 +WBatch: PORTA: _PORTA=0x0C +WBatch: PORTB: _PORTB=0xC +WBatch: OSCCAL: _OSCCAL=128 + +WBatch: Reset: Reset diff --git a/assets/resources/apps_data/nrf24batch/CO2_mini_old.txt b/assets/resources/apps_data/nrf24batch/CO2_mini_old.txt new file mode 100644 index 0000000000..9aaa05db05 --- /dev/null +++ b/assets/resources/apps_data/nrf24batch/CO2_mini_old.txt @@ -0,0 +1,83 @@ +Info: CO2 sensor mini +Address: C8C8CF +Rate: 1 +Ch: 122 +CRC: 2 +DPL: 0 +RETR: 0x0F +Resend: 3 +Delay_ms: 30 +ReadCmd repeat: 3 + +Payload struct: 2,1,1 +EEPROM=0; RAM=1; PROGMEM=2; ID=3; RESET=4; WRAM=0x89 + +R default: ,EEPROM,0xC1 +W default: n,,0x81 +Write start: 0,0,0x8F + +Listen: C8C8C1=CO2,Alarm,- + +R: ID*=,ID + +R: _CO2*2=0x6C,RAM,0xC2 +R: _PORTA=0x39,RAM +W: _PORTA=,0x3B,WRAM +R: _PORTB=0x36,RAM +W: _PORTB=,0x38,WRAM +R: _OSCCAL=0x51,RAM +W: _OSCCAL=,0x51,WRAM +R: OSCCAL_EMEM=0 + +R: RxAddr=1# +W: RxAddr=,1 + +R: Ch=2 +W: Ch=,2 + +R: nRF RETR=3# +W: nRF RETR=,3 + +R: Send period=4 +W: Send period=,4 + +R: CO2 threshold*2=5,,0xC2 +W: CO2 threshold=,5,0x82 + +R: CO2 correct*2=7,,0xC2 +W: CO2 correct*2=,7,0x82 + +R: FanLSB[10]=i:9# +W: FanLSB=,i:9 + +R: Transmit pause=19 +W: Transmit pause=,19 + +R: Flags=20# +W: Flags=,20 + +W: Reset=,RESET,0xC1 + +R: _LED Warning=0x74,RAM +W: _LED Warning=,0x74,WRAM + +S: LED=,0,0x40 + +SBatch: LED On: LED=1 +SBatch: LED Off: LED=0 + +RBatch: Settings: ID;RxAddr;Ch;Send period;CO2 threshold;CO2 correct;FanLSB;nRF RETR;Transmit pause;Flags + +WBatch: Default: RxAddr=0xCF;Ch=122;Send period=30;CO2 threshold=1000;CO2 correct=0;FanLSB={0xC1,0,0,0,0,0,0,0};nRF RETR=0x3;Transmit pause=1;Flags=0;Reset +WBatch: CO2: CO2 threshold=1000;CO2 correct=0 +WBatch: Fan: FanLSB={0xC1,0,0,0,0,0,0,0,0,0};Reset +WBatch: RETR: nRF RETR=0x2F;Reset +WBatch: Transmit pause: Transmit pause=30 +WBatch: Flags: Flags=0x00 + +WBatch: LED Warning: _LED Warning=0x30 +WBatch: PORTA: _PORTA=0x0C +WBatch: PORTB: _PORTB=0xC +WBatch: OSCCAL: _OSCCAL=128 + +WBatch: Reset: Reset diff --git a/assets/resources/apps_data/nrf24batch/Kitchen Vent Dimmer.txt b/assets/resources/apps_data/nrf24batch/Kitchen Vent Dimmer.txt new file mode 100644 index 0000000000..65b036c484 --- /dev/null +++ b/assets/resources/apps_data/nrf24batch/Kitchen Vent Dimmer.txt @@ -0,0 +1,164 @@ +Info: Kitchen Vent Dimmer +Address: C8C8C1 +Rate: 1 +Ch: 122 +CRC: 2 +DPL: 0 +RETR: 0x2F +Resend: 3 +Delay_ms: 50 + +Payload struct: 2,1,1 +ROM=0x41;ROM2=0x42;WROM=0x81;WROM2=0x82;RAM=0x51;RAM2=0x52;WRAM=0x91;WRAM2=0x92;PGM=0x61;SET=0xC0 + +R default: ,,ROM +W default: n,,WROM +Write start: 0,0,0x2F + +R: ID*=,0,0x65 + +R: CO2 level*2[3]=i:4,,ROM2 +W: CO2 level*2=,i:4,WROM2 + +R: Fan speeds=25 +W: Fan speeds=,25 +R: FanSpeed[3]=i:19 +W: FanSpeed=,i:19 +R: FanCook speeds=26 +W: FanCook speeds=,26 +R: FanCookSpeed[3]=i:22 +W: FanCookSpeed=,i:22 + +R: FanOnTime=10 +W: FanOnTime=,10 + +R: FanOffTime=11 +W: FanOffTime=,11 + +R: FanCookOnTime=12 +W: FanCookOnTime=,12 + +R: FanCookOffTime=13 +W: FanCookOffTime=,13 + +R: FanSleep=14 +W: FanSleep=,14 + +R: FanCookSleep=15 +W: FanCookSleep=,15 + +R: SpeedInitIdx=16 +W: SpeedInitIdx=,16 + +R: SpeedKeyIdx=17 +W: SpeedKeyIdx=,17 + +R: OutSpeedMax=18 +W: OutSpeedMax=,18 + +R: PauseSetByCO2,min=27 +W: PauseSetByCO2,min=,27 + +R: SSR_PulseWidth,us*2=28,,ROM2 +W: SSR_PulseWidth,us*2=,28,WROM2 + +R: SSR_PulseSafeTime,us*2=30,,ROM2 +W: SSR_PulseSafeTime,us*2=,30,WROM2 + +R: SSR2_PulseWidth,us*2=32,,ROM2 +W: SSR2_PulseWidth,us*2=,32,WROM2 + +R: DamperOpenTime=34 +W: DamperOpenTime=,34 + +R: FanCookSpIdxDnKey=35 +W: FanCookSpIdxDnKey=,35 +R: FanCookSpIdxUpKey=36 +W: FanCookSpIdxUpKey=,36 + +R: IRRemotes=37 +W: IRRemotes=,37 + +R: IRRemotesHash*2[27]=i:38,,ROM2# +W: IRRemotesHash*2=,i:38,WROM2 + +R: Flags=1# +W: Flags=,1 + +R: RxAddr=2# +W: RxAddr=,2 + +R: Ch=3 +W: Ch=,3 + +R: OSCCAL_EMEM=0 +R: _OSCCAL=0x51,,RAM +W: _OSCCAL=,0x51,,WRAM +R: _PORTA=0x39,,RAM +W: _PORTA=,0x3B,,WRAM +R: _PORTB=0x36,,RAM +W: _PORTB=,0x38,WRAM + +R: _LED_Warning=0x71,,RAM +W: _LED_Warning=,0x71,WRAM +R: _IRHashLast*2=0x89,,RAM2# +R: _IRCntLast=0x87,,RAM +R: _IRHash*2=0x8B,,RAM2# +R: _SleepTimer=0x8E,,RAM +R: _FanOn=0x7F,,RAM +R: _FanOnNext=0x7D,,RAM +R: _FanOnNextCnt=0x7B,,RAM +R: _FanOnLast=0x7C,,RAM + +R: _OutSpeedMax=0x90,,RAM +W: _OutSpeedMax=,0x90,WRAM + +R: _Fanspeed=0x7E,,RAM +R: _SSR_full_period*2=0x64,,RAM2 + +R: _PulseDelayAfterZero*2=0x9F,,RAM2 +W: _PulseDelayAfterZero*2=,0x9F,WRAM2 +R: _PulseWidth*2=0x92,,RAM2 +W: _PulseWidth*2=,0x92,WRAM2 +R: _PulseWidth2=0xA3,,RAM +W: _PulseWidth2=,0xA3,WRAM + +W: Reset=0xEEEE,14,SET + +S: Lamp=,0,SET +S: Fan=,1,SET +S: FanAdd=,2,SET +S: FanSpdUp=,3,SET +S: FanSpdDn=,4,SET +S: FanSpdSave=,5,SET +S: SetupIR=,6,SET + +SBatch: Fan Cooker Max: Fan=6 +SBatch: Fan Cooker Min: Fan=4 +SBatch: Fan Max: Fan=3 +SBatch: Fan Min: Fan=1 +SBatch: Fan Off: Fan=0 +SBatch: Fan +: FanAdd=1 +SBatch: Fan -: FanAdd=-1 +SBatch: Lamp On: Lamp=1 +SBatch: Lamp Off: Lamp=0 +SBatch: Fan Up: FanSpdUp=0 +SBatch: Fan Down: FanSpdDn=0 +SBatch: Fan SAVE: FanSpdSave=0; +SBatch: Add new IR(Set,Off,FUp,FDn,CUp,CDn,C1,C2,C3): SetupIR=1 + +RBatch: Work: ID;CO2 level;FanCookSpeed;FanCookOnTime;FanCookOffTime;FanCookSleep;FanCookSpIdxDnKey;FanCookSpIdxUpKey;FanSpeed;FanOnTime;FanOffTime;FanSleep;DamperOpenTime;PauseSetByCO2,min +RBatch: Hardware: ID;RxAddr;Ch;SpeedInitIdx;Flags;OutSpeedMax;IRRemotes;SSR_PulseWidth,us;SSR_PulseSafeTime,us;SSR2_PulseWidth;OSCCAL_EMEM +RBatch: All: ID;CO2 level;FanCookSpeed;FanCookOnTime;FanCookOffTime;FanCookSleep;FanCookSpIdxDnKey;FanCookSpIdxUpKey;FanSpeed;FanOnTime;FanOffTime;FanSleep;DamperOpenTime;PauseSetByCO2,min;RxAddr;Ch;SpeedInitIdx;Flags;OutSpeedMax;SSR_PulseWidth,us;SSR_PulseSafeTime,us;SSR2_PulseWidth,us;IRRemotes;IRRemotesHash + +WBatch: Init(Cook2spd,Lamp-F2.2): RxAddr=0xC1;Ch=122;CO2 level={820,910,1000};Fan speeds=3;FanSpeed={10,12,16};FanCook speeds=3;FanCookSpeed={12,16,32};FanSleep=20;FanCookSleep=6;FanOnTime=1;FanCookOnTime=3;FanOffTime=100;FanCookOffTime=0;DamperOpenTime=60;SpeedInitIdx=0;SpeedKeyIdx=6;Flags=0x0D;OutSpeedMax=16;SSR_PulseWidth,us=9990 +WBatch: IRRemotes clear: IRRemotes=0 +WBatch: IRRemotes 1 kitchen: IRRemotes=1;IRRemotesHash={0x6DA5,0xFCA5,0x7CA5,0xB425,0x3425,0x1625,0x8525} +WBatch: Fans Speeds: Fan speeds=3;FanSpeed={13,15,20};FanCook speeds=3;FanCookSpeed={14,20,40};OutSpeedMax=20 +WBatch: FanCooker Down/Up key idx: FanCookSpIdxDnKey=5;FanCookSpIdxUpKey=6 +WBatch: LED Warning: _LED Warning=0x10 +WBatch: OSCCAL: _OSCCAL=146 +WBatch: CO2 Level: CO2 level={820,910,1000};PauseSetByCO2,min=10 +WBatch: Zero cross EEPROM: SSR_PulseWidth,us=9990;SSR2_PulseWidth,us=0;SSR_PulseSafeTime,us=100;Reset +WBatch: Zero cross RAM: _PulseDelayAfterZero=0001;_PulseWidth=200;_PulseWidth2=200 +WBatch: Reset: Reset diff --git a/assets/resources/apps_data/nrf24batch/Kitchen Vent.txt b/assets/resources/apps_data/nrf24batch/Kitchen Vent.txt new file mode 100644 index 0000000000..4853381054 --- /dev/null +++ b/assets/resources/apps_data/nrf24batch/Kitchen Vent.txt @@ -0,0 +1,127 @@ +Info: Kitchen Vent (FanControl) +Address: C8C8C1 +Rate: 1 +Ch: 122 +CRC: 2 +DPL: 0 +RETR: 0x0F +Resend: 3 +Delay_ms: 30 + +Payload struct: 2,1,1 +ROM=0x41;ROM2=0x42;WROM=0x81;WROM2=0x82;RAM=0x51;RAM2=0x52;WRAM=0x91;WRAM2=0x92;PGM=0x61;SET=0xC0 + +R default: ,,ROM +W default: n,,WROM +Write start: 0,0,0x2F + +R: ID*=,0,0x65 + +R: CO2 level*2[3]=i:4,,ROM2 +W: CO2 level*2=,i:4,WROM2 + +R: Fan speeds=25 +W: Fan speeds=,25 +R: FanSpeed[3]=i:19 +W: FanSpeed=,i:19 +R: FanCook speeds=26 +W: FanCook speeds=,26 +R: FanCookSpeed[3]=i:22 +W: FanCookSpeed=,i:22 + +R: FanOnTime=10 +W: FanOnTime=,10 + +R: FanOffTime=11 +W: FanOffTime=,11 + +R: FanCookOnTime=12 +W: FanCookOnTime=,12 + +R: FanCookOffTime=13 +W: FanCookOffTime=,13 + +R: FanSleep=14 +W: FanSleep=,14 + +R: FanCookSleep=15 +W: FanCookSleep=,15 + +R: SpeedInitIdx=16 +W: SpeedInitIdx=,16 + +R: SpeedKeyIdx=17 +W: SpeedKeyIdx=,17 + +R: IRRemotes=32 +W: IRRemotes=,32 + +R: IRRemotesHash*2[70]=i:33,,ROM2# +W: IRRemotesHash*2=,i:33,WROM2 + +R: OutPeriod=18 +W: OutPeriod=,18 + +R: Flags=1# +W: Flags=,1 + +R: RxAddr=2# +W: RxAddr=,2 + +R: Ch=3 +W: Ch=,3 + +R: OSCCAL_EMEM=0 +R: _OSCCAL=0x51,,RAM +W: _OSCCAL=,0x51,WRAM + +R: _PORTA=0x39,,RAM +W: _PORTA=,0x3B,WRAM +R: _PORTB=0x36,,RAM +W: _PORTB=,0x38,WRAM + +R: _OutPeriod=0x8B,,RAM +W: _OutPeriod=,0x8B,WRAM + +R: _Fanspeed=0x74,,RAM +R: _FanOn=0x75,,RAM +R: _FanOnNext=0x73,,RAM +R: _FanOnNextCnt=0x71,,RAM +R: _FanOnLast=0x72,,RAM +R: _IRHashLast*2=0x79,,RAM2# + +R: _LED Warning=0x6B,,RAM +W: _LED Warning=,0x6B,WRAM + +W: Reset=0xEEEE,14,SET + +S: Lamp=,0,SET +S: Fan=,1,SET +S: FanSpdUp=,2,SET +S: FanSpdDn=,3,SET +S: FanSpdSave=,4,SET +S: SetupIR=,5,SET + +SBatch: Fan Cooker On: Fan=6 +SBatch: Fan On: Fan=3 +SBatch: Fan Off: Fan=0 +SBatch: Lamp On: Lamp=1 +SBatch: Lamp Off: Lamp=0 +SBatch: Fan Up: FanSpdUp=0 +SBatch: Fan Down: FanSpdDn=0 +SBatch: Fan SAVE: FanSpdSave=0; +SBatch: Add new IR: SetupIR=1 + +RBatch: Work: ID;CO2 level;FanCookSpeed;FanCookOnTime;FanCookOffTime;FanCookSleep;FanSpeed;FanOnTime;FanOffTime;FanSleep +RBatch: Hardware: ID;RxAddr;Ch;SpeedInitIdx;Flags;OutPeriod;IRRemotes;OSCCAL_EMEM +RBatch: All: ID;CO2 level;FanCookSpeed;FanCookOnTime;FanCookOffTime;FanCookSleep;FanSpeed;FanOnTime;FanOffTime;FanSleep;RxAddr;Ch;SpeedInitIdx;Flags;OutPeriod;IRRemotes;IRRemotesHash + +WBatch: Init(Cook2spd,Lamp-F2.2): RxAddr=0xC1;Ch=122;CO2 level={950,1050,1200};Fan speeds=1;FanSpeed={1,1,1};FanCook speeds=2;FanCookSpeed={1,2,2};FanSleep=20;FanCookSleep=6;FanOnTime=150;FanCookOnTime=0;FanOffTime=100;FanCookOffTime=0;SpeedInitIdx=0;SpeedKeyIdx=6;Flags=0x0D;OutPeriod=1 +WBatch: IRRemotes clear: IRRemotes=0 +WBatch: IRRemotes 1 kitchen: IRRemotes=1;IRRemotesHash={0x6DA5,0xFCA5,0x7CA5,0xB425,0x3425,0x1625,0x8525} +WBatch: Fans Speeds: Fan speeds=1;FanSpeed={1,1,1};FanCook speeds=2;FanCookSpeed={1,2,2} +WBatch: LED Warning: _LED Warning=0x10 +WBatch: OutPeriod: _OutPeriod=1 +WBatch: OSCCAL: _OSCCAL=146 +WBatch: CO2 Level: CO2 level={750,850,1000} +WBatch: Reset: Reset diff --git a/applications/external/nrf24scan/Distr/nrf24scan/addr-CO2mini.txt b/assets/resources/apps_data/nrf24scan/addr-CO2mini.txt similarity index 100% rename from applications/external/nrf24scan/Distr/nrf24scan/addr-CO2mini.txt rename to assets/resources/apps_data/nrf24scan/addr-CO2mini.txt diff --git a/applications/external/nrf24scan/Distr/nrf24scan/addr-WCO1.txt b/assets/resources/apps_data/nrf24scan/addr-WCO1.txt similarity index 100% rename from applications/external/nrf24scan/Distr/nrf24scan/addr-WCO1.txt rename to assets/resources/apps_data/nrf24scan/addr-WCO1.txt diff --git a/assets/resources/swd_scripts/100us.swd b/assets/resources/apps_data/swd/100us.swd similarity index 100% rename from assets/resources/swd_scripts/100us.swd rename to assets/resources/apps_data/swd/100us.swd diff --git a/assets/resources/swd_scripts/call_test_1.swd b/assets/resources/apps_data/swd/call_test_1.swd similarity index 100% rename from assets/resources/swd_scripts/call_test_1.swd rename to assets/resources/apps_data/swd/call_test_1.swd diff --git a/assets/resources/swd_scripts/call_test_2.swd b/assets/resources/apps_data/swd/call_test_2.swd similarity index 100% rename from assets/resources/swd_scripts/call_test_2.swd rename to assets/resources/apps_data/swd/call_test_2.swd diff --git a/assets/resources/swd_scripts/dump_0x00000000_1k.swd b/assets/resources/apps_data/swd/dump_0x00000000_1k.swd similarity index 55% rename from assets/resources/swd_scripts/dump_0x00000000_1k.swd rename to assets/resources/apps_data/swd/dump_0x00000000_1k.swd index a8870fe30e..0c75d8e1ba 100644 --- a/assets/resources/swd_scripts/dump_0x00000000_1k.swd +++ b/assets/resources/apps_data/swd/dump_0x00000000_1k.swd @@ -1,6 +1,6 @@ ap_select 0 max_tries 50 block_size 4 -mem_dump /ext/swd_scripts/flash.bin 0x00000000 0x100000 2 +mem_dump /ext/apps_data/swd/flash.bin 0x00000000 0x100000 2 beep 1 message 5 "Reading sucessful" diff --git a/assets/resources/swd_scripts/dump_0x00000000_4b.swd b/assets/resources/apps_data/swd/dump_0x00000000_4b.swd similarity index 100% rename from assets/resources/swd_scripts/dump_0x00000000_4b.swd rename to assets/resources/apps_data/swd/dump_0x00000000_4b.swd diff --git a/assets/resources/swd_scripts/dump_STM32.swd b/assets/resources/apps_data/swd/dump_STM32.swd similarity index 100% rename from assets/resources/swd_scripts/dump_STM32.swd rename to assets/resources/apps_data/swd/dump_STM32.swd diff --git a/assets/resources/swd_scripts/goto_test.swd b/assets/resources/apps_data/swd/goto_test.swd similarity index 100% rename from assets/resources/swd_scripts/goto_test.swd rename to assets/resources/apps_data/swd/goto_test.swd diff --git a/assets/resources/swd_scripts/halt.swd b/assets/resources/apps_data/swd/halt.swd similarity index 100% rename from assets/resources/swd_scripts/halt.swd rename to assets/resources/apps_data/swd/halt.swd diff --git a/assets/resources/swd_scripts/reset.swd b/assets/resources/apps_data/swd/reset.swd similarity index 100% rename from assets/resources/swd_scripts/reset.swd rename to assets/resources/apps_data/swd/reset.swd diff --git a/assets/resources/swd_scripts/test_write.swd b/assets/resources/apps_data/swd/test_write.swd similarity index 100% rename from assets/resources/swd_scripts/test_write.swd rename to assets/resources/apps_data/swd/test_write.swd diff --git a/assets/resources/tama_p1/rom.bin b/assets/resources/apps_data/tama_p1/rom.bin similarity index 100% rename from assets/resources/tama_p1/rom.bin rename to assets/resources/apps_data/tama_p1/rom.bin diff --git a/assets/resources/badkb/assets/layouts/fr-FR-mac.kl b/assets/resources/badkb/assets/layouts/fr-FR-mac.kl index 0906936547..2887aae0fd 100644 Binary files a/assets/resources/badkb/assets/layouts/fr-FR-mac.kl and b/assets/resources/badkb/assets/layouts/fr-FR-mac.kl differ diff --git a/assets/resources/badkb/demo_ios.txt b/assets/resources/badkb/demo_ios.txt index ed7b4d8ccd..eca294e479 100644 --- a/assets/resources/badkb/demo_ios.txt +++ b/assets/resources/badkb/demo_ios.txt @@ -1,16 +1,13 @@ REM Version 1.0 REM OS: iOS -REM Author: Peaakss +REM Author: Peaakss | Optimized for older versions by WillyJL REM Description: A simple payload that opens safari and inserts a link REM NOTICE CHANGE "STRING" to your desired link | EXAMPLE: STRING https://github.com/ClaraCrazy/Flipper-Xtreme | -REM NOITCE Payload was made on iOS 16.1 - iPhone | Timing may have have to be changed based on version/model GUI h -DELAY 100 +DELAY 420 GUI SPACE -DELAY 150 -BACKSPACE -DELAY 250 +DELAY 420 STRING https://github.com/ClaraCrazy/Flipper-Xtreme -DELAY 250 +DELAY 69 ENTER diff --git a/assets/resources/ibtnfuzzer/example_uids_cyfral.txt b/assets/resources/ibutton_fuzzer/example_uids_cyfral.txt similarity index 100% rename from assets/resources/ibtnfuzzer/example_uids_cyfral.txt rename to assets/resources/ibutton_fuzzer/example_uids_cyfral.txt diff --git a/assets/resources/ibtnfuzzer/example_uids_ds1990.txt b/assets/resources/ibutton_fuzzer/example_uids_ds1990.txt similarity index 100% rename from assets/resources/ibtnfuzzer/example_uids_ds1990.txt rename to assets/resources/ibutton_fuzzer/example_uids_ds1990.txt diff --git a/assets/resources/ibtnfuzzer/example_uids_metakom.txt b/assets/resources/ibutton_fuzzer/example_uids_metakom.txt similarity index 100% rename from assets/resources/ibtnfuzzer/example_uids_metakom.txt rename to assets/resources/ibutton_fuzzer/example_uids_metakom.txt diff --git a/assets/resources/infrared/assets/ac.ir b/assets/resources/infrared/assets/ac.ir index b4bea9dcb1..4edb9eb66a 100644 --- a/assets/resources/infrared/assets/ac.ir +++ b/assets/resources/infrared/assets/ac.ir @@ -471,39 +471,261 @@ frequency: 38000 duty_cycle: 0.330000 data: 4467 4390 571 1583 572 505 595 1560 572 1583 572 505 572 505 596 1559 596 482 596 481 597 1559 626 451 655 422 625 1529 596 1559 596 481 572 1582 573 1583 571 505 572 1583 595 1560 594 1561 592 1562 594 1561 593 1562 593 484 593 1563 592 485 592 485 592 485 592 485 593 484 593 485 592 485 592 1562 593 485 592 1563 592 1562 593 1562 594 483 593 485 593 1562 592 485 593 1561 593 484 593 484 593 484 593 1562 593 1562 592 5163 4462 4370 592 1563 593 484 592 1563 592 1563 592 485 592 485 593 1562 593 484 593 485 592 1562 593 484 593 485 592 1562 593 1562 593 485 592 1563 592 1563 592 485 592 1563 592 1562 593 1562 593 1563 592 1563 592 1562 592 485 593 1562 592 485 592 485 592 485 592 485 592 485 592 485 592 485 592 1563 592 485 592 1563 591 1563 592 1563 593 485 592 485 592 1563 592 485 592 1563 591 485 593 485 592 485 592 1563 592 1563 591 # -# Model: Chigo AC +# Model: Chigo CS-21H3A-B155 / KRF-51G/79F name: Off type: raw frequency: 38000 duty_cycle: 0.330000 -data: 6069 7329 602 533 605 506 602 508 576 533 604 507 603 507 603 507 602 508 601 510 599 511 598 512 573 537 597 514 573 537 573 537 573 537 573 537 573 538 572 537 573 538 572 538 572 538 572 538 572 538 572 537 573 537 573 538 572 537 573 538 572 1638 573 538 572 538 572 538 572 538 572 538 572 538 572 538 572 538 572 538 572 538 572 538 572 538 572 538 572 538 572 538 572 538 572 538 572 538 572 538 572 538 572 538 572 538 572 538 572 538 572 538 572 538 572 538 572 538 572 1639 571 538 572 538 572 1639 572 538 572 538 572 538 572 538 572 538 572 539 571 538 572 538 572 538 572 1639 571 538 572 539 571 538 572 538 572 539 571 1639 572 539 571 539 571 1639 572 539 571 1639 571 539 571 539 571 1639 572 539 571 1639 571 1639 572 539 571 539 571 1639 571 539 571 539 571 539 571 1639 571 7361 598 +data: 6058 7359 592 1635 591 1634 592 1634 592 1634 592 1634 592 1633 592 1634 591 1634 592 515 591 516 590 516 590 516 590 517 589 518 588 518 589 518 589 1637 589 1637 589 1637 589 1637 589 1637 589 1637 589 1638 588 1638 588 518 589 518 588 518 589 518 588 518 589 518 589 518 589 518 588 1637 589 1638 588 1638 588 1638 588 1638 588 1638 588 1638 588 1638 588 518 588 519 588 518 588 519 588 518 589 519 587 519 588 519 587 1638 588 1638 588 519 588 1638 588 519 587 519 588 1638 588 1638 588 519 588 519 587 1639 587 519 587 1638 588 1639 587 519 588 519 587 1639 587 1639 587 1639 587 1639 587 1639 587 520 587 1639 587 1639 587 519 587 520 586 520 586 520 587 520 587 1640 586 521 586 521 586 522 585 1664 562 544 562 1664 562 544 562 1664 562 544 562 544 563 1664 562 544 562 1664 562 545 562 1664 562 545 561 1664 562 1664 562 7386 562 +# +name: Cool_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 6033 7358 591 1634 593 1634 592 1634 592 1634 592 1634 592 1634 592 1634 591 1634 592 515 617 490 616 491 590 517 590 517 590 517 590 517 590 517 590 1637 589 1637 589 1637 589 1637 589 1637 589 1637 589 1637 589 1637 589 518 589 517 590 517 589 518 589 517 616 492 589 517 615 492 590 517 589 518 613 1613 589 1637 614 1612 589 1637 589 1637 589 1637 589 1637 589 1638 588 518 613 493 613 494 596 512 589 518 612 494 589 1637 614 492 589 518 588 1637 614 1612 614 492 615 1612 613 1613 613 493 590 1637 589 1637 589 518 614 492 614 1612 614 492 614 493 613 1613 613 1612 614 1612 614 1612 614 1612 614 492 614 1612 614 1612 614 493 614 493 614 493 614 493 614 492 614 1612 614 493 614 492 615 492 614 1612 614 492 614 1612 614 492 614 1612 614 493 613 493 614 1612 614 492 614 1612 614 493 614 1612 614 493 614 1612 614 1612 614 7334 614 +# +name: Heat_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 6090 7300 672 1579 646 1579 622 1604 622 1604 622 1605 620 1605 620 1606 619 1607 618 488 618 489 617 490 616 490 616 491 616 491 616 490 617 491 616 1610 616 1610 616 1610 616 1610 616 1610 616 1611 616 1610 616 1610 616 491 616 491 615 491 616 491 615 491 615 491 615 491 616 491 616 1611 615 491 616 1611 615 1611 615 1611 615 1611 615 1611 615 1611 615 491 615 1611 615 491 615 491 616 491 615 492 615 491 615 491 616 1611 615 491 615 492 614 1611 615 1611 615 492 615 1611 615 1611 615 492 614 1611 615 1611 615 492 615 492 615 1611 615 492 615 492 614 1611 615 492 614 492 615 493 615 1611 615 1611 615 1612 614 492 615 492 614 1611 615 1611 615 1612 614 492 615 492 614 492 614 1612 614 492 614 1612 614 492 615 1612 614 492 614 1612 614 492 614 492 615 1612 614 493 614 1612 614 493 614 1612 614 493 614 1612 614 1612 614 7334 614 +# +name: Heat_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 6091 7301 646 1604 647 1579 647 1579 622 1604 621 1605 620 1605 620 1606 619 1607 618 488 618 488 618 489 617 490 616 491 615 491 615 491 616 491 616 1610 616 1611 615 1611 615 1611 616 1611 615 1610 616 1611 615 1611 615 491 615 491 616 491 616 491 615 491 615 491 616 491 615 491 615 1611 615 491 615 1611 615 1611 615 1611 615 1611 615 1611 615 1611 615 491 615 1611 615 491 615 492 615 491 615 492 615 492 615 491 616 1611 615 492 614 492 615 1611 615 1611 615 492 614 1611 615 1611 615 492 615 1611 615 1612 614 492 614 492 615 1611 615 492 615 492 614 492 615 492 615 492 614 1611 615 1612 614 1612 614 1612 614 492 615 1612 614 1612 614 1612 614 492 615 492 615 492 614 493 614 1612 614 492 615 1612 614 492 614 1612 614 492 614 1612 614 492 615 492 614 1612 614 493 613 1612 614 493 613 1612 614 493 613 1612 614 1612 614 7334 614 +# +name: Cool_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 6114 7274 647 1604 621 1605 620 1604 622 1604 621 1604 621 1605 620 1605 619 1606 619 488 618 488 618 489 617 490 616 490 616 491 616 490 616 490 616 1610 616 1610 616 1610 615 1610 615 1610 616 1610 616 1610 616 1610 616 491 615 491 616 491 615 491 615 491 615 491 616 491 615 491 615 1610 616 491 615 1610 616 1610 616 1610 616 1610 616 1610 615 1610 616 491 615 1610 616 491 615 491 615 491 616 491 615 491 615 491 616 1610 616 491 615 491 615 1610 616 1610 615 491 615 1610 617 1611 615 491 616 1610 616 1610 616 491 615 491 616 1610 616 491 615 491 615 491 615 491 615 491 615 1611 615 1611 614 491 615 1611 615 1611 615 1611 615 1611 615 1611 614 492 614 492 614 1611 614 492 615 492 615 492 614 1611 615 492 615 1611 615 492 614 1611 616 492 615 492 614 1611 614 492 615 1611 615 492 614 1611 615 492 614 1611 615 1611 615 7332 614 +# +name: Dh +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 6089 7302 644 1582 644 1582 644 1583 643 1584 642 1586 640 1607 618 1608 617 1609 617 490 616 491 615 491 615 492 615 492 614 492 615 492 615 492 614 1612 614 1612 614 1611 615 1611 615 1612 614 1612 614 1611 615 1611 615 492 615 492 615 492 614 492 614 492 614 492 615 492 615 492 615 492 615 1612 614 1612 614 1612 614 1612 614 1612 614 1612 614 1612 614 1612 614 492 614 492 614 492 614 492 615 492 615 492 614 492 615 1612 614 493 614 492 615 1612 614 1612 614 492 614 493 614 1612 614 492 615 1612 614 1612 614 493 614 493 613 1612 614 1612 614 493 614 493 613 1612 614 1612 614 493 613 1612 614 1612 614 493 614 1612 614 1612 614 493 613 493 614 1612 614 493 613 493 614 1613 613 494 613 493 614 1613 613 493 613 1613 613 493 613 1613 613 493 613 493 614 1613 613 494 613 1613 613 493 613 1613 613 494 613 1613 613 1613 613 7335 613 +# +# Model: Tosot T24H-ILF/I/T24H-ILU/O +name: Off +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9067 4428 600 1590 599 481 597 481 598 482 596 484 595 487 592 511 568 511 568 511 568 1621 569 511 568 1622 568 511 568 511 568 511 569 511 568 511 569 511 568 511 568 511 568 511 568 1622 568 511 568 511 568 511 568 511 569 511 568 511 568 1622 568 511 568 1622 568 511 568 511 568 1622 568 511 648 20161 569 511 568 511 568 511 568 511 568 511 569 511 568 511 568 511 568 511 568 511 568 511 568 511 568 511 568 1622 568 511 568 511 568 511 568 511 568 511 568 511 569 511 568 511 568 511 569 511 568 512 568 511 568 511 568 511 568 1622 568 1622 568 1622 568 511 648 40402 9171 4434 594 1621 569 510 569 511 568 511 568 510 569 511 568 511 568 511 568 511 568 1621 569 511 568 1621 569 511 568 511 568 511 568 511 568 511 568 511 568 511 568 511 568 511 568 1622 568 511 568 511 568 511 569 511 568 511 568 511 568 1622 568 1622 568 1622 568 512 568 512 567 1622 568 512 673 20162 568 511 568 511 568 510 569 510 569 511 568 511 568 511 568 511 569 511 568 511 569 511 568 511 568 511 568 511 568 511 568 511 569 511 568 511 569 511 568 511 568 511 568 511 568 511 568 511 568 511 568 511 568 511 569 511 568 1622 568 511 568 1622 568 512 567 +# +name: Cool_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9087 4428 598 1591 598 482 596 482 596 1594 595 1595 594 1596 593 486 593 486 593 486 593 486 593 486 593 486 593 486 593 486 593 486 593 486 593 486 593 486 593 486 593 486 593 486 593 1597 593 486 593 486 593 486 593 486 593 487 592 486 593 1597 592 487 592 1597 592 487 592 487 592 1597 593 487 671 20132 593 1596 593 486 593 486 593 486 593 1596 593 486 593 486 593 486 593 486 593 486 593 486 593 486 593 486 593 1597 592 487 592 486 593 486 593 486 593 487 592 486 593 486 593 487 592 487 592 487 592 487 592 487 592 487 592 486 593 487 592 1597 593 1597 592 487 672 40393 9168 4432 594 1596 593 486 593 486 593 1597 593 1596 593 1597 592 486 593 486 593 486 593 486 593 486 593 486 593 486 593 486 593 486 593 486 593 486 593 487 592 487 592 486 593 486 593 1597 592 487 592 486 593 487 592 487 592 487 592 487 592 1597 593 1597 593 1597 592 487 592 487 592 1597 592 487 698 20131 593 486 593 486 593 486 593 486 593 486 593 486 593 486 593 486 593 486 593 486 593 486 593 486 593 486 593 486 593 486 593 486 593 486 593 486 593 486 593 486 593 1597 593 486 593 1597 592 487 592 486 593 487 592 486 593 487 592 487 592 487 592 487 592 1597 592 +# +name: Heat_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9065 4429 598 482 597 482 597 1593 596 1594 595 1596 594 1597 593 487 592 487 592 487 593 1598 592 1598 592 1598 592 488 592 487 592 488 591 511 568 487 592 488 591 511 568 511 568 488 591 1599 591 511 568 511 568 511 568 511 568 511 569 511 568 1622 568 511 568 1622 568 511 568 511 568 1622 568 511 648 20137 593 1598 592 487 593 487 592 488 591 1599 591 488 591 487 592 488 591 511 568 488 591 511 568 489 590 511 568 1599 591 511 568 511 568 511 568 511 568 511 568 511 568 511 568 511 568 511 569 511 568 511 568 511 569 511 568 511 568 1622 568 1622 568 1622 568 511 648 40403 9170 4434 594 486 593 487 592 1597 593 1598 592 1598 592 1597 593 487 592 487 592 487 593 1598 592 1598 592 1598 592 488 591 488 591 488 591 511 568 488 591 488 591 511 568 511 569 511 568 1622 568 511 568 511 568 511 568 511 569 511 568 511 568 1622 568 1622 568 1622 568 511 569 511 568 1622 568 511 674 20138 592 488 591 488 591 511 568 488 591 511 568 511 568 511 568 511 568 511 568 511 568 489 590 511 568 511 568 511 568 511 568 511 568 511 568 511 568 511 568 511 568 1622 568 511 568 1622 568 511 568 511 568 511 569 511 568 511 568 1622 568 511 569 511 568 1622 568 +# +name: Heat_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9104 4416 604 474 604 475 603 1587 602 1588 601 1590 599 1590 599 480 600 481 598 1591 599 1591 599 1591 599 481 598 481 598 481 599 481 598 481 598 481 598 481 598 481 598 481 599 481 598 1592 598 481 598 481 599 481 598 481 598 481 598 481 598 1592 598 481 598 1592 598 481 598 481 598 1592 598 481 678 20131 599 1591 599 481 598 481 598 481 598 1591 599 481 599 481 598 481 598 481 599 481 598 481 598 481 598 481 598 1592 598 481 598 481 599 481 598 481 598 481 598 481 598 481 599 481 598 481 598 481 598 481 598 481 599 481 598 481 598 481 598 481 598 481 598 481 678 40398 9182 4421 599 479 600 480 599 1591 599 1591 599 1591 599 1591 599 480 599 481 598 1591 599 1591 599 1591 599 481 598 481 598 481 598 481 598 481 599 481 598 481 599 480 599 481 599 481 598 1592 598 481 598 481 598 481 599 481 598 481 598 481 598 1592 598 1592 598 1592 598 481 598 481 598 1592 598 481 704 20131 599 481 598 481 598 481 599 480 599 481 598 481 598 481 598 481 599 481 598 481 598 481 598 481 598 481 598 481 598 481 598 481 598 481 598 481 598 481 598 481 598 1592 598 481 598 1592 598 481 599 481 598 481 598 481 598 481 598 481 598 1592 598 481 598 482 597 +# +name: Cool_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9045 4446 576 1613 576 503 575 503 601 1589 575 1615 574 1617 572 506 573 507 572 1618 571 1618 572 1618 571 507 572 507 572 507 572 507 572 508 571 507 572 507 572 507 572 508 571 508 571 1618 571 508 571 508 571 508 571 508 571 508 571 508 571 1619 571 508 571 1618 571 508 571 508 571 1619 571 508 651 20153 571 1618 571 508 571 508 571 507 572 1618 571 508 571 508 571 508 571 508 571 508 571 508 571 508 571 508 571 1619 571 508 571 508 571 508 571 508 571 508 571 508 571 508 571 508 571 508 571 508 571 508 571 508 571 508 571 508 571 1619 571 508 571 1619 570 1619 650 40414 9150 4450 573 1617 572 507 572 508 571 1618 572 1618 571 1618 571 508 571 508 571 1619 570 1618 572 1618 571 508 571 508 571 508 571 508 571 508 571 508 571 508 571 508 571 509 570 508 571 1619 571 509 570 509 570 509 570 508 571 509 570 509 570 1619 570 1620 569 1620 570 509 570 509 570 1620 570 509 676 20154 570 508 571 508 571 508 571 508 571 508 571 508 571 508 571 508 571 508 571 508 571 508 571 509 570 508 571 508 571 508 571 508 571 509 570 509 570 509 570 509 570 1619 570 509 570 1620 569 509 570 509 570 509 570 509 570 509 570 1620 569 1621 569 1620 569 1620 570 +# +name: Dh +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9102 4396 624 476 602 1587 602 478 600 1590 600 1591 599 481 598 481 598 481 598 1592 598 1592 598 1592 598 481 598 481 599 481 598 481 598 481 598 481 598 481 598 481 598 481 599 481 598 1592 598 481 598 481 598 481 599 481 599 481 598 481 599 1592 598 481 598 1592 598 482 597 481 598 1592 598 481 678 20131 599 1592 598 481 598 481 598 481 598 1592 598 481 598 481 598 481 599 481 598 481 598 481 598 481 599 481 598 1592 598 481 599 481 598 481 598 481 598 481 598 481 598 481 598 482 597 481 598 481 598 481 598 481 598 481 598 481 598 482 597 1592 598 1592 598 1592 677 40398 9182 4421 599 480 599 1591 599 481 599 1592 598 1592 598 481 598 481 598 481 598 1592 598 1592 598 1592 598 481 598 481 598 481 598 481 598 481 599 481 598 481 598 481 598 481 598 481 598 1592 598 481 599 481 598 482 597 481 598 481 598 481 598 1592 598 1592 598 1592 598 481 598 482 597 1592 598 482 703 20131 598 481 598 481 598 481 598 481 598 481 599 481 598 481 598 481 599 481 598 481 598 481 598 481 598 481 598 481 599 481 598 481 598 481 598 481 599 481 598 481 598 1592 598 481 598 481 598 481 598 481 598 481 598 481 598 481 598 481 598 481 598 1592 598 1592 598 +# +# Model: LG Generic +name: Off +type: parsed +protocol: NECext +address: 81 66 00 00 +command: 81 7E 00 00 +# +name: Cool_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8725 4071 527 1527 501 521 501 517 500 520 500 1545 499 523 498 521 521 525 495 529 470 550 470 549 494 527 470 552 471 549 470 552 471 547 495 529 470 548 471 1572 470 1574 470 548 494 1551 470 547 470 552 470 551 470 1571 496 1547 469 1573 471 +# +name: Heat_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8755 4064 528 1524 502 523 501 517 501 520 500 1543 500 519 498 524 497 523 497 527 496 521 496 525 521 500 496 527 496 1545 496 525 496 526 496 1545 495 1550 496 1541 496 1549 496 523 495 1546 496 526 496 523 521 499 496 1550 495 1544 495 1548 495 +# +name: Heat_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8783 4042 556 1497 502 521 501 518 501 520 500 1546 499 517 499 523 497 525 496 527 522 496 497 523 497 524 496 528 522 1516 521 500 496 526 497 1542 520 501 496 527 496 522 497 524 496 1548 496 521 521 500 521 500 520 503 497 521 521 500 497 +# +name: Cool_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8721 4070 527 1526 501 524 501 517 501 518 500 1544 500 519 498 524 497 526 496 550 471 549 470 549 470 551 494 531 470 548 470 549 470 552 470 1572 494 526 494 528 470 551 470 549 470 1573 470 551 470 548 495 1548 470 1572 469 551 494 527 495 +# +name: Dh +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8780 4015 578 1498 553 471 526 494 525 494 524 1520 525 495 523 498 522 499 521 502 522 497 522 499 522 500 521 1519 522 500 521 499 521 1520 546 1496 521 503 523 494 522 500 521 499 522 1520 521 499 522 499 522 502 522 1518 521 501 521 1522 522 +# +# Model: Shivaki General +name: Off +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3163 1577 577 1009 579 1008 579 342 485 342 485 342 485 1033 579 339 462 342 485 1033 553 1034 552 342 485 1035 550 342 484 343 483 1039 547 1040 546 341 486 1041 546 1041 545 341 486 341 486 1041 545 341 486 340 487 1041 545 341 486 341 486 340 487 340 488 340 487 340 487 341 486 341 486 340 487 340 487 341 486 340 487 341 487 341 486 341 486 341 486 340 487 341 486 340 487 340 487 1041 545 341 486 341 486 1042 545 1041 545 341 486 340 487 340 487 340 487 340 487 341 486 1042 544 341 486 1042 544 341 486 341 486 341 486 341 486 341 486 341 486 341 486 341 486 1042 544 1042 544 1042 544 1042 544 340 488 341 486 341 486 341 486 341 486 341 486 341 486 341 486 341 486 340 487 340 487 341 487 341 486 341 486 341 487 341 486 341 486 341 486 341 486 341 486 341 486 341 486 341 486 341 486 340 487 341 486 341 487 341 486 341 487 341 486 341 486 340 487 1043 543 1043 544 341 486 1043 543 341 486 1043 543 1044 542 340 487 340 488 +# +name: Cool_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3193 1549 579 1007 580 1007 579 342 485 342 485 342 511 1007 579 338 463 342 485 1034 552 1034 551 342 484 1035 550 342 484 340 487 1039 547 1040 546 341 487 1041 545 1041 545 340 487 341 487 1041 545 341 486 340 487 1041 545 341 486 341 486 340 487 340 487 341 486 340 487 341 486 341 486 340 487 341 486 340 487 340 488 341 486 340 487 340 487 340 487 341 487 1041 545 340 487 340 487 1041 545 340 488 341 486 1042 544 1042 544 341 487 341 486 341 486 340 487 341 487 341 486 1042 544 1042 544 1042 544 1042 545 341 486 341 487 341 486 341 486 1042 545 341 486 1042 544 1042 544 1042 544 1042 544 341 487 341 486 340 487 341 486 341 486 340 487 341 486 340 487 341 486 341 486 341 486 341 486 341 487 341 486 341 486 341 486 340 488 341 486 341 486 341 486 341 486 341 486 340 488 341 486 341 486 341 487 341 486 341 486 341 486 1043 544 341 486 341 486 341 487 1043 543 341 486 341 486 341 486 341 486 1044 542 340 487 341 486 341 486 +# +name: Heat_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3169 1547 579 1006 580 1006 580 342 485 342 485 342 484 1033 553 342 485 342 485 1033 552 1034 551 342 484 1035 550 342 484 342 485 1039 546 1040 546 342 485 1040 546 1040 546 342 485 343 484 1040 546 342 485 342 485 1040 546 342 486 342 485 343 484 342 485 343 484 342 485 342 485 342 485 343 484 342 485 342 485 342 485 343 484 342 485 342 485 342 485 342 485 1041 545 342 485 343 484 1041 545 342 485 343 484 1041 545 342 485 343 484 342 485 342 485 342 485 342 485 342 485 1041 545 342 485 343 484 342 485 342 485 342 485 343 484 342 485 1041 545 342 485 1041 545 1041 545 1041 545 1041 545 342 485 342 485 343 484 342 485 343 484 342 485 342 485 342 485 342 485 342 485 342 485 342 485 343 484 342 485 342 485 342 485 342 485 342 485 342 485 342 485 342 485 343 484 342 485 342 485 342 485 342 485 342 485 343 484 342 485 1042 544 343 484 343 484 342 485 1042 544 341 486 342 485 342 485 343 484 342 485 342 485 342 485 340 487 +# +name: Heat_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3162 1605 523 1064 522 1064 523 343 484 343 484 343 484 1063 523 343 484 343 484 1063 523 1063 523 343 484 1063 523 343 484 343 484 1065 521 1066 520 343 484 1068 518 1068 518 343 484 343 484 1068 518 343 484 343 484 1068 518 343 484 343 484 343 484 343 484 343 484 342 485 343 484 343 485 343 484 343 484 343 484 343 484 343 484 343 484 343 484 343 484 343 484 1068 518 343 484 343 484 1069 517 343 484 343 485 1069 517 343 484 343 484 343 485 343 484 343 484 343 484 343 485 343 484 343 484 343 484 1070 516 344 483 344 483 344 484 343 484 1070 517 344 483 1094 493 1070 517 1093 493 1094 492 344 483 343 484 343 484 343 484 343 484 343 484 343 485 343 484 343 484 343 484 344 483 343 484 344 483 343 484 344 483 343 484 344 483 343 484 343 484 344 483 344 483 343 484 343 484 344 483 343 484 343 484 343 484 344 483 343 484 1094 492 343 484 344 484 343 484 1094 492 1094 492 1094 492 1094 492 343 484 344 483 343 484 343 484 344 483 +# +name: Cool_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3138 1604 523 1063 523 1063 523 343 484 343 484 343 484 1063 523 343 484 343 484 1062 523 1063 523 343 484 1063 522 343 484 343 484 1065 521 1066 519 343 484 1067 519 1067 519 343 484 343 484 1067 519 343 484 343 484 1067 519 343 484 343 484 343 484 343 484 343 484 343 484 343 484 342 485 343 484 343 484 343 484 343 484 343 484 343 484 343 484 343 484 343 484 1068 518 343 484 343 484 1068 518 343 484 343 484 1068 518 1068 518 343 484 343 484 343 484 343 484 343 484 343 484 343 484 343 484 343 484 1067 519 343 484 343 484 343 484 343 484 1067 519 343 484 1067 519 1067 519 1068 518 1067 519 343 484 343 484 343 484 343 484 343 484 343 484 343 484 343 484 343 484 343 484 343 484 343 484 343 484 343 484 343 484 343 484 343 484 343 484 343 484 343 484 343 484 343 484 343 484 343 484 343 484 343 484 343 484 343 484 343 484 1068 518 343 484 343 484 343 484 1068 518 1068 518 343 484 343 484 1068 518 343 484 343 484 343 484 343 484 +# +name: Dh +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3165 1578 549 1037 549 1037 550 342 485 343 484 343 513 1034 551 340 487 340 487 1035 551 1035 550 340 486 1037 548 341 485 341 485 1041 545 1041 545 341 486 1042 544 1042 544 340 487 341 486 1042 544 341 486 340 488 1042 544 341 487 341 486 341 486 341 461 343 509 341 487 341 461 343 484 343 510 341 486 341 461 343 484 343 509 341 462 343 509 341 462 343 484 1068 518 343 510 341 461 1068 518 343 484 343 484 343 484 1068 518 343 484 343 484 343 484 343 484 342 485 343 485 1068 518 1068 518 1068 518 343 485 343 484 343 485 343 484 343 484 343 484 343 484 343 484 1068 518 1068 518 1068 518 343 484 343 484 343 485 343 484 343 484 343 484 343 484 343 484 343 484 342 485 343 485 343 484 343 484 343 484 343 484 343 484 343 484 343 485 343 484 343 484 343 484 343 484 343 484 343 485 343 484 343 484 343 484 343 484 343 484 1070 516 343 484 343 484 343 484 1071 515 343 484 1094 492 343 484 343 484 343 485 344 483 343 484 343 484 +# +# Model: Mitsubishi MSZ-AP25VGK +name: Off +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3531 1667 500 1225 499 1225 499 376 499 377 498 377 498 1224 500 377 498 377 498 1224 500 1225 499 377 527 1195 557 318 556 318 555 1167 530 1194 529 374 499 1224 499 1225 497 377 497 378 497 1228 496 379 496 380 495 1229 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 496 380 495 380 495 380 495 380 495 9028 3526 1672 495 1229 495 1229 495 380 495 380 495 380 495 1230 494 380 495 380 495 1229 495 1229 495 380 495 1229 495 380 495 380 495 1229 495 1229 495 380 495 1229 495 1229 495 380 495 380 495 1229 495 380 495 380 495 1229 495 380 495 381 494 381 494 380 495 380 495 380 495 380 495 381 494 380 495 381 494 381 494 381 494 381 494 381 494 380 495 381 494 381 494 381 494 381 494 381 494 381 494 381 494 381 494 381 494 381 494 381 494 1230 494 1230 494 381 494 381 494 381 494 381 494 381 494 381 494 1230 494 381 494 381 494 381 494 381 494 381 494 1230 494 1230 494 381 494 1230 494 1230 494 381 494 381 494 381 494 381 494 381 494 381 494 381 494 381 494 1230 494 381 494 1230 494 381 494 381 494 1230 494 381 494 1230 494 1230 494 381 494 381 494 381 494 381 494 381 494 381 494 381 494 381 494 381 494 381 494 381 494 381 494 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 1231 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 1231 493 382 493 1231 493 382 493 1231 493 382 493 383 492 382 493 +# +name: Dh +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3561 1666 500 1195 529 1193 531 374 501 375 500 375 500 1196 529 374 501 375 500 1223 501 1223 501 375 529 1194 558 318 557 318 555 1168 530 1193 530 345 529 1194 529 1196 527 348 526 350 525 1200 524 352 522 353 522 1228 496 379 496 379 496 379 496 379 496 379 496 379 497 379 496 379 496 379 496 379 496 379 496 379 496 379 496 379 496 379 496 9028 3529 1670 496 1228 496 1228 496 379 496 379 496 379 496 1228 496 379 496 379 496 1228 496 1228 496 379 496 1228 496 379 496 379 496 1228 496 1228 496 379 496 1228 496 1228 496 379 496 379 496 1228 496 379 496 379 496 1228 496 379 496 379 496 379 496 379 496 379 496 379 496 379 496 379 496 379 496 379 496 379 496 380 495 379 496 379 496 379 496 379 496 380 495 379 496 380 495 379 496 1229 495 380 495 380 495 379 496 380 495 380 495 380 495 1229 495 380 495 380 495 380 495 380 495 380 495 380 495 1229 495 380 495 380 495 380 495 380 495 380 495 1229 495 380 495 380 495 1229 495 1229 495 380 495 380 495 380 495 380 495 380 496 380 495 380 495 380 495 1229 495 380 495 1229 495 380 495 380 495 1229 495 380 495 1229 495 1229 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 381 494 380 495 380 495 380 495 380 495 1230 494 380 495 381 494 381 494 380 495 380 495 380 495 380 495 381 494 381 495 380 495 381 494 381 495 380 495 381 494 381 495 380 495 381 494 381 494 381 494 381 494 381 494 381 494 381 494 1230 494 381 494 381 494 1230 494 381 494 1230 494 381 494 381 494 # name: Cool_hi type: raw frequency: 38000 duty_cycle: 0.330000 -data: 6073 7299 631 504 632 478 632 478 606 504 606 504 605 504 605 505 604 506 603 508 601 509 600 510 600 511 599 511 599 511 599 511 599 511 599 511 599 511 599 511 599 511 599 511 599 511 599 511 599 511 599 511 599 511 599 511 599 511 599 511 599 1612 598 512 598 511 599 511 599 511 599 512 598 512 598 512 598 512 598 512 598 512 598 1612 598 1612 599 512 598 512 598 511 599 512 598 512 598 512 598 512 598 512 598 512 598 512 598 512 598 512 598 512 598 512 598 512 598 1612 598 1613 597 512 598 512 598 1613 598 512 598 512 598 512 598 512 598 512 598 512 598 513 597 512 598 512 598 1613 597 513 597 513 597 513 597 513 597 513 597 1613 597 513 597 513 597 1614 597 513 597 1614 596 513 597 513 597 1614 597 513 597 1614 597 513 597 1614 596 1614 596 1614 597 514 596 513 597 513 597 1614 596 7336 623 +data: 3534 1637 530 1192 533 1195 529 375 500 375 500 375 500 1195 530 375 501 375 500 1224 500 1224 501 376 528 1195 557 319 556 318 555 1168 530 1193 530 345 530 1194 529 1196 527 348 526 350 525 1201 523 353 522 378 497 1228 496 379 497 379 496 379 497 379 496 379 497 379 497 379 497 379 496 379 496 379 496 379 496 379 497 379 497 379 496 379 496 9030 3530 1671 496 1229 496 1229 495 379 496 379 496 379 497 1229 496 379 496 379 497 1229 496 1229 495 380 496 1229 495 379 497 379 496 1229 496 1229 495 379 497 1229 495 1228 497 379 496 380 496 1229 496 379 497 379 496 1229 496 379 496 380 496 380 495 380 496 379 496 380 496 380 495 380 496 380 496 380 496 379 496 380 496 380 495 380 496 380 495 380 496 380 495 380 496 380 495 380 495 1229 496 380 495 380 495 380 495 380 496 380 495 1229 496 1229 496 380 495 380 496 380 495 380 496 380 496 380 495 380 495 380 496 380 496 380 495 380 496 380 495 1229 495 1230 495 380 495 1229 496 1229 496 380 495 381 495 380 495 380 495 380 496 380 496 380 495 380 496 1229 496 380 495 1230 495 380 495 380 495 1230 495 380 495 1230 495 1230 495 380 495 380 496 380 495 380 495 380 495 380 496 380 496 380 495 380 496 380 495 380 495 380 496 380 495 381 495 380 495 380 495 380 495 381 495 380 495 381 495 380 495 381 494 381 495 381 495 380 495 1230 495 381 495 380 495 381 494 381 495 381 494 381 495 381 494 381 495 381 495 381 494 381 495 381 494 381 495 381 495 381 494 381 495 381 494 381 494 381 495 381 494 381 494 381 494 381 495 1230 495 381 495 1230 495 1230 495 381 494 1230 494 381 495 381 494 +# +name: Cool_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3534 1667 500 1224 501 1224 501 376 500 375 500 376 500 1224 501 376 500 376 499 1224 501 1225 500 376 500 1225 556 320 556 318 555 1167 530 1194 530 345 530 1195 529 1196 528 348 527 377 498 1227 498 378 497 379 496 1229 496 379 496 379 497 379 497 379 497 379 497 380 496 379 497 379 496 379 497 380 496 380 496 380 496 380 496 379 497 379 497 9033 3530 1672 496 1229 496 1229 496 380 496 380 496 380 496 1229 496 380 496 380 496 1229 496 1229 496 380 496 1229 496 380 496 380 496 1229 496 1229 496 380 496 1230 495 1229 496 380 495 380 496 1229 496 380 496 380 496 1229 496 380 496 380 496 380 496 380 496 380 496 380 496 380 496 380 496 380 496 380 496 380 496 380 496 380 495 380 496 380 496 380 496 380 496 380 496 380 496 380 496 1230 495 380 496 380 495 380 496 381 495 380 495 1230 495 1230 495 380 496 380 496 380 496 1230 495 1230 495 1230 495 380 496 380 496 380 496 380 496 380 496 381 495 1230 495 1230 495 381 495 1230 495 1230 495 380 495 381 495 381 495 381 495 381 495 381 495 380 496 381 495 1230 495 381 495 1230 495 381 495 380 496 1230 495 381 495 1230 495 1230 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 380 496 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 1231 494 381 495 381 495 381 495 381 495 381 495 381 494 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 382 494 382 494 1231 494 381 495 1231 494 1231 494 381 495 382 494 # name: Heat_hi type: raw frequency: 38000 duty_cycle: 0.330000 -data: 6098 7325 631 478 606 504 606 504 606 504 606 504 606 505 604 505 604 507 602 508 601 510 600 510 600 511 599 511 599 511 599 512 598 511 599 511 599 511 599 512 598 511 599 511 599 512 598 512 598 511 599 511 599 511 599 512 598 511 599 512 598 1612 599 512 598 512 598 512 598 512 598 512 598 512 598 512 598 512 598 512 598 512 598 512 598 1612 598 512 598 512 598 512 598 512 598 512 598 512 598 512 598 512 598 512 598 512 598 512 598 512 598 512 598 512 598 512 598 1613 597 1613 598 512 598 512 598 1613 598 513 597 513 597 512 598 512 598 513 597 513 597 513 597 513 597 513 597 1613 598 513 597 1614 597 1613 597 1613 598 513 597 513 597 513 597 1614 597 1614 597 513 597 1614 596 514 596 513 597 1614 596 513 597 1614 597 1614 596 1614 597 514 596 1614 596 1614 597 1614 596 1614 596 1615 596 7336 623 +data: 3539 1670 501 1226 502 1226 501 376 501 377 500 376 501 1226 501 376 499 378 500 1226 501 1226 501 377 528 1199 557 320 557 318 529 1197 531 1195 531 346 530 1196 530 1198 528 349 527 352 524 1229 498 379 498 379 498 1230 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 9045 3536 1674 496 1231 497 1231 497 380 497 380 497 380 497 1231 496 380 497 380 497 1231 497 1231 496 380 497 1231 496 380 497 380 497 1231 496 1231 497 380 497 1231 496 1231 496 380 497 380 497 1231 496 381 496 380 497 1231 497 381 496 380 497 380 497 380 497 380 497 381 496 381 496 381 496 380 497 380 497 381 496 381 496 381 496 381 496 380 497 381 496 381 496 381 496 381 496 380 497 1231 496 381 496 381 496 380 497 381 496 381 496 1232 495 381 496 381 496 381 496 381 496 1232 495 1232 495 1232 496 1232 495 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 1232 496 1232 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 1232 496 381 496 1232 495 381 496 1232 495 381 496 1232 495 1232 495 381 496 381 496 382 495 381 496 381 496 381 496 381 496 382 495 381 496 381 496 381 496 381 496 381 496 382 495 381 496 381 496 381 496 381 496 382 495 382 495 382 495 382 495 382 495 382 495 382 495 1232 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 1233 495 1233 494 1233 495 382 495 382 495 1233 495 1233 494 382 495 # name: Heat_lo type: raw frequency: 38000 duty_cycle: 0.330000 -data: 6095 7331 630 1607 630 1606 630 1607 629 1607 629 1609 626 1612 623 1639 572 1665 572 544 572 544 572 544 572 544 572 544 572 544 572 544 572 544 572 1665 572 1665 572 1665 572 1665 572 1665 572 1665 572 1665 572 1665 572 544 572 544 572 544 572 544 572 545 571 544 572 544 572 544 572 1665 572 545 571 1665 572 1665 572 1666 571 1665 572 1665 572 1665 572 545 571 1666 571 545 571 545 571 545 571 544 572 545 571 545 571 1666 571 545 571 545 571 1666 571 1666 571 545 571 1666 571 1666 571 545 571 1666 571 1666 571 545 571 545 571 1666 571 545 571 545 571 545 571 545 571 545 571 1666 571 1666 571 1666 571 1666 571 545 571 1666 571 1666 571 1666 571 545 571 545 571 545 571 545 571 1666 571 546 570 1666 571 545 571 1666 571 545 571 1667 570 545 571 546 570 1667 570 546 570 1667 570 546 570 1667 570 546 570 1667 570 1667 570 7389 570 -# +data: 3539 1637 533 1225 502 1193 534 375 501 376 501 376 500 1226 501 376 501 376 500 1226 500 1227 501 376 529 1197 558 320 557 319 555 1169 531 1195 531 346 529 1196 530 1198 528 349 526 352 524 1229 497 379 497 379 497 1230 497 380 497 380 497 379 498 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 9042 3535 1674 496 1230 497 1230 497 380 497 380 497 380 497 1230 497 380 497 380 496 1230 497 1230 497 380 497 1230 497 380 497 380 497 1231 496 1230 497 380 497 1231 496 1231 496 380 496 380 497 1231 496 380 497 380 496 1231 496 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 496 380 497 380 497 380 497 380 497 380 496 381 496 380 497 380 497 380 497 381 496 1231 496 381 496 380 497 380 497 381 496 381 496 1231 496 381 496 381 496 380 497 381 495 1231 496 1231 496 1231 496 380 497 381 496 381 496 380 496 381 496 381 496 381 496 381 496 381 496 1231 496 1231 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 1231 496 381 496 381 496 1232 495 381 495 1232 495 381 496 1231 496 1231 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 495 381 496 381 496 381 496 381 496 1232 495 381 496 381 496 381 496 381 496 381 495 381 496 381 495 382 495 381 496 382 495 381 495 382 495 381 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 381 495 1232 495 1232 495 1232 495 1232 495 1232 495 382 495 382 495 382 495 +# +# Model: Subtropic SUB/in-07HN1 +name: Off +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9019 4453 576 1665 603 1639 602 504 602 505 601 507 599 508 598 1644 573 1669 573 1669 572 1670 572 1670 572 1670 572 1670 572 1670 572 1670 572 535 572 535 572 535 572 535 572 535 572 535 572 1670 572 1670 572 1670 571 535 572 535 572 535 572 535 572 535 572 535 572 535 572 535 572 535 572 535 572 535 572 535 572 536 571 1670 572 535 572 1670 572 535 572 535 572 536 571 536 571 536 571 536 571 535 572 536 571 536 571 536 571 536 571 536 571 536 571 1670 572 536 571 536 571 536 571 536 571 536 571 536 571 536 571 536 571 536 571 536 571 536 571 536 571 536 571 536 571 536 571 537 570 536 571 537 570 536 571 537 570 536 571 537 570 537 570 537 570 537 570 537 570 537 570 538 569 538 569 538 569 561 546 562 545 562 545 562 545 1696 546 562 545 1697 545 562 545 562 545 562 545 562 545 562 545 1696 546 1697 545 1697 545 562 545 562 545 1697 545 1697 545 1697 545 +# +name: Cool_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9048 4454 577 1665 576 1665 576 531 601 506 600 508 599 509 572 1670 572 1670 572 1670 572 1670 572 1670 572 535 572 535 572 536 571 1670 572 536 571 536 571 536 571 535 572 536 571 536 571 1670 572 1670 572 1671 571 536 571 536 571 536 571 536 571 536 571 536 571 536 571 536 571 536 571 536 571 536 571 536 571 536 571 1671 571 536 571 1671 571 536 571 536 571 536 571 536 571 536 571 536 571 537 570 537 570 536 571 537 570 537 570 536 571 537 570 1671 571 537 570 536 571 537 570 536 571 537 570 537 570 537 570 537 570 536 571 537 570 537 570 537 570 537 570 537 570 537 570 537 570 537 570 537 570 537 570 537 570 537 570 537 570 537 570 1671 571 537 570 537 570 537 570 537 570 537 570 537 570 537 570 537 571 537 570 537 570 1672 570 537 570 537 570 537 570 537 570 537 570 537 570 537 570 1672 570 1672 570 538 569 1672 570 537 570 537 570 1672 570 1672 570 +# +name: Heat_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9049 4454 603 1638 604 1638 603 504 602 506 600 507 600 508 598 1644 573 1670 572 1670 572 1670 572 1670 572 535 572 535 572 535 572 1670 597 1645 572 535 573 535 572 535 597 510 573 535 572 1670 572 1670 572 1670 572 535 597 510 572 535 573 535 572 535 572 535 572 535 572 535 572 535 572 535 572 536 571 535 572 535 572 1670 572 536 571 1671 571 536 571 536 571 536 572 536 571 536 571 536 572 536 571 536 571 536 571 536 571 536 571 536 571 536 571 537 570 537 570 1672 570 537 570 537 570 537 570 538 569 537 570 537 570 561 546 562 545 562 545 562 546 562 545 562 545 562 545 562 546 562 545 562 545 562 545 562 545 562 546 562 545 1697 545 1697 545 562 545 562 545 562 545 562 545 562 545 562 545 562 546 562 545 562 545 562 545 562 545 562 545 563 544 562 546 562 545 563 544 562 545 562 545 562 545 1697 545 563 545 1697 545 1698 544 1697 545 563 544 1698 544 +# +name: Heat_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9048 4454 576 1665 602 1640 603 504 602 506 600 507 599 509 573 1669 573 1670 572 1670 572 1670 572 1670 572 1670 572 1670 572 1670 572 1670 572 535 572 535 573 535 572 535 572 535 572 535 573 1670 572 1670 572 1670 572 535 572 535 572 535 572 535 572 535 572 535 572 535 572 535 572 535 572 535 572 535 572 535 572 535 572 1670 572 535 572 1670 572 535 572 535 572 536 572 535 572 535 572 535 572 535 572 536 571 535 572 536 571 535 572 536 571 536 571 536 571 536 571 1671 571 536 571 536 571 536 571 536 596 511 571 536 572 536 571 536 571 536 571 536 571 536 571 536 571 536 571 536 571 536 571 536 571 536 571 536 571 536 572 536 571 1671 595 1647 571 536 571 536 571 536 572 536 571 536 595 512 571 536 571 536 571 536 572 536 571 1671 571 536 571 536 571 537 570 536 571 536 571 537 570 537 570 1672 570 1671 571 537 570 537 571 1671 571 1672 570 1672 570 537 570 +# name: Cool_lo type: raw frequency: 38000 duty_cycle: 0.330000 -data: 6095 7303 654 1581 656 1605 631 1606 630 1607 629 1608 628 1609 627 1610 626 1611 626 491 625 491 625 491 625 491 625 491 625 491 625 491 625 491 625 1613 624 1612 625 1612 625 1613 624 1612 625 1612 625 1612 625 1612 625 492 624 491 625 491 625 491 625 491 625 492 624 492 624 492 624 1613 624 492 624 1613 624 1612 625 1613 624 1613 624 1613 624 1613 624 492 624 1613 624 492 624 492 624 492 624 492 624 492 624 492 624 1613 624 492 624 492 624 1613 624 1613 624 492 624 1613 624 1613 624 492 624 1613 624 1613 624 492 624 492 624 1614 623 493 623 492 624 492 624 492 624 493 623 1614 623 1614 623 492 624 1614 623 1614 623 1614 623 1614 623 1614 623 493 623 493 623 1614 623 493 623 493 623 493 623 1614 623 493 623 1614 623 493 623 1614 623 493 623 493 623 1614 623 493 623 1614 623 493 623 1614 623 493 623 1614 623 1614 623 7337 621 +data: 9017 4455 577 1665 576 1665 576 531 575 532 600 508 574 535 572 1670 572 1671 571 1671 571 1671 571 1671 571 1671 571 1671 571 1671 571 1671 571 536 571 536 571 536 571 536 571 536 571 536 571 1671 571 1671 571 1671 571 536 571 536 571 536 571 536 571 536 571 536 571 536 571 536 571 536 571 536 571 536 571 536 571 536 571 1671 571 536 571 1671 571 537 570 537 570 537 570 537 570 537 570 537 570 537 570 538 569 538 569 538 569 538 569 538 569 538 569 1695 546 538 569 561 546 561 546 561 546 561 546 561 546 561 546 561 546 561 546 561 546 561 546 561 546 561 546 561 546 561 546 561 546 561 546 561 546 561 546 562 545 561 546 561 546 561 546 1696 545 562 545 562 545 562 545 562 545 562 545 562 545 562 545 562 545 562 545 562 545 562 545 562 545 562 545 563 544 563 544 563 544 563 544 563 544 563 544 1698 544 563 544 564 543 563 544 564 543 564 543 564 543 +# +name: Dh +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9046 4455 577 1665 577 1665 576 531 601 506 600 508 599 509 573 1670 572 1670 572 1670 572 1671 571 1670 572 1671 571 1671 571 1671 571 1671 571 536 571 536 571 536 571 536 571 536 571 536 571 1671 571 1671 571 1671 571 536 571 536 571 536 571 536 571 536 571 536 571 537 570 536 571 536 571 536 571 536 571 537 570 536 571 1672 570 537 570 1671 571 537 570 537 570 537 570 537 570 537 570 537 570 537 570 537 570 537 570 537 570 537 570 537 570 537 570 537 570 1672 570 537 570 538 569 538 569 537 570 537 570 537 570 537 570 537 570 538 569 537 570 538 569 537 570 538 569 538 569 538 569 538 569 562 545 561 546 539 569 561 546 562 545 562 545 1696 546 562 545 562 546 562 545 562 545 562 545 562 545 562 545 562 545 562 545 562 545 1696 546 562 545 562 545 562 545 562 545 562 545 562 545 562 545 1697 545 1697 545 562 545 562 545 562 545 1697 545 562 545 562 545 +# +# Model: Hitachi RAK-50PEB +name: Off +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 30683 50966 3411 1600 493 1186 493 347 492 348 491 348 491 349 490 349 490 350 489 351 488 351 488 352 487 352 488 351 488 1192 487 352 487 351 488 352 487 352 487 352 488 352 488 351 488 1192 487 1191 488 352 487 352 487 352 487 352 487 352 487 352 487 352 487 352 487 1192 487 352 487 1192 487 1192 487 1192 487 1192 488 1192 487 1192 488 352 487 1192 487 1192 487 352 487 352 487 352 487 352 488 352 487 352 488 352 487 352 487 1192 487 1192 487 1192 487 1192 487 1192 487 1192 487 1192 487 1192 487 352 487 352 488 352 487 1192 487 352 487 352 487 352 487 352 487 1192 487 352 487 353 486 1192 487 353 486 353 486 353 486 353 486 1193 486 353 487 353 486 353 486 353 486 353 486 353 486 1193 486 1193 486 353 487 353 486 353 486 353 486 353 487 353 486 353 486 353 486 1193 486 353 486 353 486 1193 486 353 487 353 486 353 487 353 486 353 486 353 486 353 486 353 487 353 486 353 486 1193 486 1193 486 353 487 353 486 353 486 353 486 354 485 353 486 354 485 353 486 354 486 353 486 353 486 354 486 353 486 353 487 1193 486 1194 485 353 487 353 486 354 485 354 485 354 486 354 485 354 485 354 485 354 485 354 485 354 485 354 485 354 485 354 485 354 486 354 485 354 485 354 485 354 485 354 485 354 485 354 485 354 485 354 485 354 486 354 485 354 485 354 485 354 485 354 485 354 485 354 485 354 485 354 485 354 485 354 485 354 485 355 484 355 484 355 484 354 485 354 485 355 484 355 484 355 484 355 484 354 486 355 484 378 461 356 484 378 461 355 485 355 484 355 484 355 485 355 484 378 461 378 461 355 484 356 483 355 484 378 461 378 462 355 485 378 461 378 462 378 461 379 461 356 483 378 461 1195 484 378 461 379 461 356 484 378 461 379 460 379 461 378 461 378 462 378 461 379 461 378 461 378 461 379 460 379 460 379 461 378 461 378 461 378 461 378 461 378 461 379 460 379 460 379 460 379 461 379 460 1219 460 1219 460 379 461 1219 460 379 461 1219 460 # name: Dh type: raw frequency: 38000 duty_cycle: 0.330000 -data: 6064 7357 602 1634 602 1635 602 1634 603 1634 603 1634 603 1634 602 1634 602 1635 627 489 626 490 600 516 600 517 599 518 598 517 599 518 598 517 599 1639 598 1639 598 1638 599 1638 599 1639 598 1639 598 1639 598 1639 598 517 599 518 598 518 598 518 598 518 598 518 598 518 598 518 598 1639 598 1639 598 518 598 1639 598 1639 598 1639 598 1639 598 1639 598 518 598 518 598 1639 598 518 598 518 598 518 598 518 598 518 598 1639 598 518 598 518 598 1639 598 1639 598 518 598 518 598 1640 597 518 598 1639 598 1640 597 518 598 518 598 1639 598 1639 598 519 597 518 598 1640 597 1640 597 519 597 1640 597 1640 597 519 597 1640 597 1640 597 519 597 519 597 1641 596 519 597 519 597 1640 597 519 597 519 597 1640 597 519 597 1640 597 519 597 1641 596 520 596 519 597 1641 596 519 597 1641 596 520 596 1641 596 520 596 1642 595 1641 596 7363 596 +data: 30684 50965 3412 1599 494 1185 494 346 493 346 493 347 492 348 491 349 490 349 490 350 489 351 489 350 489 350 489 351 489 1191 488 351 488 351 488 351 489 351 488 351 488 351 488 351 488 1191 488 1191 488 351 488 351 488 351 488 351 488 351 488 351 489 351 488 351 488 1191 488 351 489 1191 488 1191 488 1191 488 1191 488 1191 488 1191 488 351 488 1191 488 1192 487 351 488 352 487 351 488 351 488 352 487 352 487 352 487 352 488 1192 487 1192 487 1216 463 1192 487 1192 488 1192 487 1193 486 1192 488 352 487 352 487 352 487 1193 486 376 463 376 463 376 464 352 487 1216 463 376 463 376 463 1216 464 376 463 376 463 376 463 1216 463 376 463 376 463 353 486 353 487 376 463 376 463 376 463 1216 463 376 463 1216 463 376 464 376 463 376 463 376 464 376 463 376 463 376 463 376 463 1216 463 376 463 1216 463 376 463 376 463 376 463 376 463 376 463 376 463 376 463 376 463 376 464 376 463 1216 463 1217 463 376 463 377 462 377 462 377 463 376 463 376 463 377 462 376 463 377 462 377 462 377 463 376 463 377 462 377 462 1217 462 1216 463 377 463 377 462 377 462 377 462 377 463 376 463 377 462 377 463 377 462 377 462 377 462 377 462 377 462 377 462 377 462 377 462 377 463 377 462 377 463 377 462 377 462 1217 462 377 462 377 462 377 463 377 462 377 463 377 462 377 462 377 462 377 462 377 463 377 462 377 462 377 462 377 462 377 462 377 463 377 462 377 462 377 462 377 462 377 462 377 462 377 463 377 462 377 462 377 462 377 462 377 462 377 462 377 462 377 462 377 462 377 462 377 462 377 463 377 462 377 463 377 462 377 462 377 462 377 462 377 462 377 462 377 462 377 462 377 462 377 462 377 462 1217 462 377 462 377 462 377 462 377 462 378 461 377 462 377 462 378 462 377 462 377 462 377 463 377 462 378 461 378 462 377 462 378 461 377 462 378 461 378 461 378 461 378 462 377 462 378 462 1217 462 1218 461 1218 461 378 461 378 461 1218 462 377 462 378 462 +# +name: Cool_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 30747 50897 3484 1554 543 1137 542 310 529 309 530 309 530 309 530 310 529 309 530 310 529 309 530 309 530 309 530 309 530 1138 541 309 531 309 530 309 530 309 530 309 530 309 530 309 530 1138 541 1138 541 310 529 309 530 309 531 309 530 309 530 309 530 309 530 310 529 1139 541 309 530 1138 541 1138 541 1138 541 1138 542 1138 541 1138 541 310 529 1138 541 1138 541 309 530 309 530 309 531 310 529 309 530 309 530 309 530 309 530 1139 541 1139 540 1139 541 1138 541 1139 540 1139 540 1138 541 1139 540 310 529 310 529 309 530 1139 541 310 529 309 530 309 530 309 530 1139 540 309 530 309 530 1139 541 309 530 309 530 309 530 1139 540 309 531 309 530 1139 541 309 530 309 530 309 530 309 530 309 530 309 530 1139 540 309 531 309 530 309 530 309 530 309 530 309 530 309 531 309 530 309 531 309 530 1139 540 310 529 309 530 309 530 309 530 309 530 309 531 310 529 310 529 309 531 310 529 1140 540 309 530 309 531 309 530 309 530 309 530 309 531 309 530 309 530 309 530 309 530 309 531 309 530 309 531 309 530 310 529 1140 539 1140 539 309 530 309 530 309 530 309 530 310 529 309 530 309 530 309 530 309 530 309 531 309 530 309 530 309 530 309 530 309 530 309 531 309 530 309 530 309 530 309 530 309 530 1140 540 309 530 309 530 309 530 309 530 309 531 309 530 309 531 309 530 309 530 310 530 309 530 309 531 309 530 309 530 309 530 309 530 309 530 309 531 309 530 309 530 309 531 309 530 309 530 309 531 309 531 309 530 309 530 309 530 309 530 309 531 309 531 309 530 309 530 309 530 309 530 309 530 309 531 309 530 309 531 309 530 309 531 309 530 309 530 309 531 309 530 309 530 309 530 309 530 1141 538 309 531 309 530 309 530 309 530 309 531 309 530 309 530 309 530 309 531 309 530 309 530 309 530 309 530 309 530 309 530 309 530 310 530 309 531 309 530 309 530 309 530 309 530 309 530 309 531 1142 537 309 530 1142 538 309 530 1141 538 309 530 309 530 +# +name: Cool_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 30694 50951 3483 1555 542 1137 542 308 531 309 530 308 531 308 531 308 531 308 531 308 531 308 531 308 532 308 531 308 532 1139 541 308 531 308 531 308 531 308 531 308 531 309 531 308 531 1139 541 1139 540 308 531 308 531 308 531 308 531 309 530 308 531 308 531 308 532 1139 541 308 532 1139 540 1139 541 1139 540 1139 540 1139 540 1139 541 309 530 1139 540 1139 540 308 531 308 531 308 531 308 532 308 531 308 531 308 531 308 531 1140 540 1139 541 1139 540 1139 540 1139 540 1139 541 1139 540 1139 540 308 531 308 531 308 531 1140 540 309 530 308 531 308 532 308 531 1140 540 308 531 308 532 1140 539 308 531 308 531 308 531 308 532 308 531 308 531 1140 540 308 531 308 531 308 531 308 531 308 532 308 531 1140 540 308 531 308 531 308 531 308 531 308 531 308 531 1140 540 1140 540 1140 539 308 531 1140 539 308 531 308 531 308 532 308 531 306 533 308 531 306 533 308 531 307 533 308 531 1141 539 308 532 308 531 308 531 306 534 306 533 306 534 306 533 306 533 306 533 306 533 306 534 307 532 307 533 306 533 308 532 1141 539 1141 539 308 531 308 532 308 531 307 533 307 481 352 538 307 533 307 532 307 533 307 481 352 539 307 532 307 533 306 482 352 487 352 537 306 534 307 482 352 538 307 482 352 487 1192 539 309 530 307 531 306 483 352 487 352 538 307 482 352 538 306 483 352 487 352 487 352 487 353 486 352 488 353 486 352 487 352 487 353 487 353 486 353 486 353 487 352 487 353 486 353 486 353 486 353 486 353 487 353 487 353 486 353 486 353 487 353 486 353 486 353 486 353 487 353 486 353 487 352 487 353 486 353 486 353 487 353 486 353 486 353 487 353 486 353 487 353 486 353 487 353 486 1193 537 306 483 353 486 353 487 353 486 353 487 353 486 353 486 353 487 353 486 353 486 353 487 353 486 353 486 353 486 353 486 353 487 353 486 353 486 353 486 353 486 353 486 353 486 353 486 1194 485 353 487 1193 538 1142 486 1193 486 353 486 353 486 353 568 +# +name: Heat_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 30703 50953 3432 1606 490 1189 490 349 490 349 490 349 490 350 489 350 489 350 489 350 490 350 489 350 489 350 490 350 489 1190 490 350 489 350 489 350 489 350 489 350 490 350 489 350 490 1190 490 1190 489 350 489 350 489 350 489 350 490 350 490 350 489 350 490 350 490 1190 489 350 490 1190 489 1190 490 1190 489 1190 490 1190 489 1190 489 350 490 1190 489 1190 489 350 490 350 489 350 490 350 489 350 489 350 490 350 489 350 490 1190 489 1190 489 1190 490 1190 489 1190 490 1190 489 1190 489 1191 489 350 489 350 489 350 490 1190 490 350 489 350 490 350 489 350 490 1190 489 350 490 350 489 1190 490 350 489 350 490 350 490 350 489 350 489 350 489 1191 489 350 489 350 490 350 489 350 489 1191 489 1190 489 350 489 350 490 350 489 350 490 350 489 351 489 350 489 350 489 350 489 350 490 350 489 350 489 1191 489 350 489 351 489 351 488 351 489 351 488 351 489 350 489 351 489 351 489 1191 488 351 488 351 488 351 489 350 489 351 488 350 490 350 489 351 488 351 488 351 489 350 489 350 489 351 489 351 489 350 489 1191 488 1191 489 350 489 351 488 351 489 351 488 351 489 351 488 351 488 351 489 351 489 351 489 350 489 351 489 351 488 351 488 351 488 351 489 351 488 351 488 351 488 351 489 351 489 1191 488 351 489 351 488 351 489 351 488 351 489 351 489 351 488 351 488 351 489 351 489 351 488 351 488 351 488 351 489 351 488 351 488 351 489 351 488 351 488 351 489 351 489 351 488 351 488 351 489 351 489 351 488 351 489 351 488 351 489 351 488 351 489 351 488 351 489 351 488 351 489 351 488 351 488 351 489 351 488 351 489 351 489 351 489 351 488 351 489 351 488 351 488 351 489 351 488 1192 488 351 488 351 488 351 489 351 488 351 488 351 489 351 489 351 489 351 488 351 488 351 489 351 488 351 489 351 488 351 489 351 488 351 488 351 489 351 488 351 489 351 488 351 489 351 488 351 489 351 489 1191 488 1191 488 352 488 351 488 352 488 351 489 +# +name: Heat_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 30675 50953 3432 1606 490 1190 489 350 489 350 489 350 489 350 489 351 488 350 489 351 489 351 488 351 489 351 488 351 489 1191 488 351 488 351 489 351 488 351 488 351 488 351 489 351 488 1191 489 1191 489 351 488 351 488 351 488 351 488 351 489 351 489 351 488 351 489 1191 488 351 488 1191 489 1191 488 1191 488 1191 488 1191 488 1191 488 351 489 1191 488 1191 489 351 488 351 489 351 488 351 489 351 488 351 488 351 488 351 488 1191 488 1191 488 1191 488 1191 489 1191 488 1191 488 1191 489 1191 488 351 488 351 489 351 488 1191 488 351 489 351 488 351 488 351 489 1191 488 351 489 351 488 1191 488 351 488 351 488 351 489 351 489 351 488 351 489 1191 488 351 488 351 489 351 488 351 488 1191 488 1191 488 351 488 351 489 351 488 351 489 351 488 351 489 351 489 1191 488 1192 488 1191 488 351 489 1191 489 351 488 351 488 351 489 351 489 351 488 351 488 351 489 351 488 351 488 351 488 1192 488 351 489 351 488 351 488 351 489 351 489 351 488 351 488 351 488 352 487 352 488 351 488 352 488 351 488 351 488 351 488 1192 488 1192 487 352 488 352 487 352 487 352 488 352 487 352 488 351 488 352 488 352 488 352 487 352 488 351 488 351 488 352 488 352 487 352 488 352 487 352 488 352 488 352 488 352 487 1192 487 352 487 352 488 352 488 352 488 352 487 352 487 352 488 352 487 352 487 352 487 352 488 352 488 352 487 352 487 352 488 352 487 352 488 352 487 352 487 352 487 352 487 352 487 352 488 352 487 352 487 352 487 352 488 352 487 352 488 352 487 352 488 352 487 352 487 352 488 352 487 352 488 352 487 352 488 352 488 352 487 352 487 352 487 352 487 352 487 352 488 352 487 352 487 352 487 1193 486 352 487 352 488 352 487 352 487 352 488 352 487 352 488 352 488 352 487 352 488 352 487 353 486 353 487 352 487 352 488 352 488 352 487 352 487 352 487 352 487 353 487 352 488 352 488 352 487 1193 486 1193 486 1193 486 1193 487 353 487 352 487 353 486 diff --git a/assets/resources/infrared/assets/audio.ir b/assets/resources/infrared/assets/audio.ir index 0aa0dcebe8..74538cad12 100644 --- a/assets/resources/infrared/assets/audio.ir +++ b/assets/resources/infrared/assets/audio.ir @@ -1,7 +1,7 @@ Filetype: IR library file Version: 1 -# Last Updated 30th May, 2023 -# Last Checked 30th May, 2023 +# Last Updated 1st Jul, 2023 +# Last Checked 1st Jul, 2023 # name: Power type: parsed @@ -176,7 +176,7 @@ type: parsed protocol: NECext address: 02 A0 00 00 command: EA 15 00 00 -# Standby +# name: Power type: parsed protocol: NEC @@ -258,19 +258,19 @@ command: 05 00 00 00 name: Vol_up type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4617 4406 584 448 557 448 557 449 556 449 555 1430 580 1432 577 452 552 454 550 1460 549 1462 548 1462 549 1462 548 457 548 457 548 457 548 457 548 4463 548 457 548 457 548 457 548 458 548 1462 548 1462 548 1462 548 457 548 1463 547 1462 548 1462 548 458 547 458 548 458 548 458 547 1463 547 458 547 458 547 458 548 1463 547 55451 4606 4440 549 457 548 457 548 457 548 457 548 1462 548 1462 548 457 548 457 548 1462 548 1461 549 1462 548 1462 548 457 548 457 548 457 548 457 548 4462 548 457 548 457 548 457 548 457 548 1462 548 1462 548 1462 548 457 548 1462 548 1462 548 1462 547 457 548 458 547 458 547 458 547 1462 548 458 547 458 547 458 547 1462 548 # name: Vol_dn type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4614 4408 583 449 555 450 555 451 553 451 554 1432 577 1434 576 453 551 454 550 1460 549 1461 549 1461 549 1461 549 457 548 457 548 457 548 457 548 4461 548 457 548 457 548 457 548 457 548 457 548 457 548 457 548 1462 548 1461 549 1461 549 1461 548 457 548 1462 549 1461 549 1461 548 457 548 457 548 457 548 458 547 1462 548 55443 4606 4440 549 456 549 456 549 456 549 456 549 1461 549 1461 549 457 548 457 548 1461 549 1461 549 1461 549 1461 549 456 549 457 548 457 548 457 548 4461 549 456 549 457 548 457 548 457 548 457 548 457 548 457 548 1462 548 1461 549 1461 549 1461 548 457 548 1461 549 1461 549 1461 549 457 548 457 548 458 547 457 548 1462 548 # name: Power type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4588 4435 556 477 556 449 555 450 555 451 554 1428 582 1429 581 451 553 452 552 1457 552 1458 551 1461 548 1463 547 458 547 458 547 458 547 458 547 4464 547 458 547 458 547 458 547 458 548 458 547 458 547 458 548 458 547 1463 547 1463 547 1464 546 459 547 1464 546 1464 546 1463 547 1464 546 459 546 459 546 459 546 1464 547 55456 4581 4468 546 458 547 458 547 458 547 458 547 1463 547 1463 547 458 547 459 547 1463 547 1464 546 1464 546 1464 547 459 546 459 546 459 546 459 547 4465 546 459 546 459 546 459 546 459 546 459 547 459 546 459 546 459 546 1464 546 1464 546 1465 546 460 545 1465 545 1465 545 1465 546 1465 546 460 545 460 546 460 545 1466 544 # name: Power @@ -374,8 +374,7 @@ type: parsed protocol: NEC address: 20 00 00 00 command: 10 00 00 00 -# -# ON +# name: Power type: parsed protocol: RC5 @@ -397,25 +396,25 @@ command: 09 F6 00 00 name: Power type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 3309 1906 410 1178 411 1177 412 416 435 445 406 448 414 440 411 1150 439 415 436 1178 411 1177 412 442 409 444 407 1181 408 419 432 1182 407 420 442 1146 433 448 414 440 411 442 409 444 407 446 416 438 413 441 410 443 408 419 432 1182 407 446 405 422 440 414 437 416 435 445 406 1181 408 446 405 448 414 440 411 442 409 418 433 446 416 438 413 1175 414 413 438 1176 413 414 437 443 408 419 432 421 441 413 438 42493 3308 3343 355 43011 3309 3316 382 43009 3310 3314 384 43007 3303 3347 361 # name: Vol_up type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 3307 1879 437 1177 412 1176 413 415 436 417 434 446 405 448 414 1174 415 412 439 1149 440 1147 442 438 413 440 411 1177 412 441 410 1178 411 442 409 1179 410 417 434 419 432 448 414 440 411 442 409 444 407 446 416 438 413 441 410 1151 438 415 436 444 407 446 416 412 439 414 437 1177 412 1176 413 414 437 416 435 1152 437 1177 412 416 435 444 407 446 416 1146 433 421 441 1173 406 422 440 413 438 442 409 444 407 40133 3308 3341 357 42862 3301 3347 361 42859 3304 3319 379 # name: Vol_dn type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 3310 1876 440 1174 415 1173 405 422 440 414 437 443 408 445 406 1182 407 420 442 1173 406 1182 407 420 442 412 439 1175 414 440 411 1150 439 415 436 1152 437 416 435 419 432 447 415 440 411 442 409 418 433 420 442 438 413 440 411 1177 412 415 436 418 433 420 442 438 413 441 410 1151 438 1150 439 415 436 1178 411 1150 439 1149 440 414 437 417 434 445 406 1155 434 420 442 438 413 1175 414 413 438 442 409 444 407 39503 3303 3320 388 42857 3304 3319 379 42867 3305 3317 381 # name: Mute type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 3305 1907 409 1178 411 1177 412 442 409 418 433 447 415 439 412 1176 413 441 410 1177 412 1176 413 441 410 443 408 1180 409 445 406 1181 408 446 416 1172 406 448 413 440 411 442 409 445 406 447 414 439 412 442 409 444 407 447 414 1173 405 449 413 441 410 443 408 446 405 448 413 1174 415 440 411 442 409 1178 411 1177 412 1177 412 442 409 444 407 447 415 439 412 441 410 444 407 1180 409 445 406 448 414 440 411 41125 3303 3347 360 42906 3308 3315 382 # name: Mute @@ -475,7 +474,7 @@ command: 06 00 00 00 name: Power type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 9150 4435 643 1608 643 468 644 469 642 364 749 468 643 447 665 449 663 469 643 452 660 470 642 450 662 442 670 449 662 469 643 1579 672 1608 642 1580 671 1609 641 1607 643 1578 672 1607 643 1608 642 1606 644 1606 644 1606 644 1607 643 1576 675 1579 671 1605 674 438 645 466 673 438 646 466 674 437 673 439 672 439 673 438 646 1604 673 1577 673 1578 673 1577 674 1577 673 23799 9095 4485 616 # name: Vol_up @@ -487,13 +486,13 @@ command: 0C 00 00 00 name: Vol_dn type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 9151 4434 644 1608 643 376 737 379 733 446 666 449 663 468 644 469 643 468 644 468 644 468 644 447 665 448 664 468 644 450 662 1608 643 1607 644 1576 676 1607 644 1608 643 1578 674 1608 643 1577 674 1579 672 1607 643 1608 643 1607 644 1607 644 1608 643 448 664 1608 643 448 664 468 644 469 643 380 732 468 644 469 643 1607 644 468 644 1608 643 1608 644 1609 643 1608 643 23837 9152 4434 642 # name: Power type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 8968 4344 670 460 670 460 670 1566 669 462 668 486 643 487 642 489 641 1595 640 490 640 491 640 1596 640 491 640 1596 640 1596 640 1596 640 491 640 1596 640 1596 640 1596 640 1596 640 1596 640 1596 640 1596 640 1622 640 491 640 491 640 491 640 491 640 491 640 491 640 491 640 491 639 # name: Power @@ -529,61 +528,61 @@ command: 07 F8 00 00 name: Power type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 1042 1461 540 1460 541 1460 541 1460 541 1459 542 1459 542 454 536 460 540 456 544 452 538 458 542 454 546 450 540 456 544 1457 544 1456 545 1448 542 50531 1041 1462 539 1462 539 1461 540 1461 540 1460 541 1460 541 455 545 451 539 457 543 480 510 459 541 481 519 451 539 457 543 1457 543 1457 544 1449 541 50515 1037 1467 544 1456 545 1456 545 1455 546 1455 535 1465 536 486 514 483 517 479 511 485 515 481 509 487 513 483 517 478 512 1462 539 1462 539 1454 536 50537 1035 1467 544 1457 544 1457 544 1456 545 1456 544 1456 545 477 513 483 517 479 511 486 514 481 519 477 513 484 516 479 511 1464 536 1463 538 1455 546 # name: Power type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 1010 1491 509 1490 510 1488 512 487 513 487 513 1486 514 1485 515 484 516 1483 517 482 518 482 518 1481 509 1489 511 488 512 487 513 1486 514 484 516 50963 1011 1489 511 1488 512 1487 513 485 515 486 514 1485 515 1484 516 484 516 1483 517 482 518 482 518 1481 509 1489 511 489 511 489 511 1487 513 486 514 50986 1008 1492 518 1480 510 1488 512 487 513 487 513 1486 514 1484 516 484 516 1483 517 482 518 481 519 1480 510 1488 512 487 513 487 513 1486 514 484 516 50972 1012 1488 512 1486 514 1484 516 483 517 483 517 1482 518 1480 510 489 511 1487 513 486 514 486 514 1485 515 1483 517 483 517 483 517 1481 509 490 510 50976 1008 1491 509 1489 511 1487 513 485 515 485 515 1484 516 1481 509 491 509 1489 511 488 512 488 512 1487 513 1485 515 484 516 484 516 1483 517 481 509 # name: Power type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 1011 1479 517 477 516 481 512 1480 516 1473 513 482 511 485 518 1473 513 482 511 1481 515 1474 512 482 511 485 518 1473 513 1477 509 486 517 1473 513 50728 1014 1475 511 484 509 487 516 1475 511 1479 517 477 516 480 513 1478 508 487 516 1475 511 1479 517 478 514 480 513 1479 517 1473 513 508 484 1479 517 50725 1016 1473 513 481 512 484 519 1473 513 1477 509 485 518 478 515 1476 510 485 518 1473 513 1477 509 486 517 478 515 1476 510 1480 516 479 514 1476 510 50735 1069 1421 513 481 512 484 509 1483 513 1477 509 486 517 479 514 1477 509 486 517 1474 512 1479 507 488 515 480 513 1479 507 1483 513 482 511 1479 517 50733 1011 1478 508 513 490 506 486 1478 508 1483 513 508 485 511 482 1482 514 508 485 1480 516 1473 513 508 485 511 482 1483 513 1477 509 512 491 1473 513 50735 1008 1480 516 479 514 508 485 1480 516 1474 512 509 484 486 517 1472 514 508 485 1480 516 1474 512 509 484 512 481 1482 514 1477 509 512 491 1472 514 50738 1006 1509 487 508 485 486 507 1509 487 1503 483 513 490 505 488 1502 484 512 491 1473 513 1503 483 512 491 505 488 1476 510 1507 489 506 487 1503 483 # name: Mute type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 1010 1479 517 1474 512 509 484 512 491 505 488 507 486 510 483 512 491 505 488 508 485 1505 491 1474 512 1478 508 1483 513 1477 509 1482 514 1475 511 50713 1005 1483 513 1475 511 511 482 513 490 505 488 507 486 483 510 511 482 487 516 505 488 1501 485 1478 508 1483 513 1476 510 1479 507 1483 513 1474 512 50707 1012 1501 485 1479 506 513 490 505 488 507 486 509 484 511 482 487 516 505 488 508 485 1504 482 1482 514 1476 592 1396 590 1400 513 1476 510 1478 508 50715 1015 1473 513 1476 510 484 508 513 490 504 489 480 513 508 485 483 510 511 482 488 515 1473 513 1477 509 1480 516 1474 512 1477 591 1397 516 1472 514 # name: Vol_up type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 1041 1462 538 1462 538 485 515 481 519 478 512 484 516 1458 542 1458 542 1459 541 481 519 1481 519 1455 545 1482 518 1456 544 479 511 486 514 474 516 50532 1039 1464 536 1490 510 487 513 483 517 480 510 486 514 1460 540 1486 514 1460 540 483 517 1457 543 1484 516 1458 542 1458 542 481 519 477 513 476 513 50534 1036 1467 543 1457 543 480 509 460 540 483 517 479 511 1463 537 1463 537 1464 536 486 514 1487 513 1461 539 1461 539 1462 538 484 516 481 519 469 510 # name: Vol_dn type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 1042 1461 539 457 543 1458 542 1458 542 1458 542 1458 542 454 536 461 539 457 543 1457 543 453 537 460 540 456 544 452 538 1463 537 1463 537 1456 544 50530 1065 1438 572 424 566 1434 566 1435 565 1435 565 1436 544 452 537 459 572 424 545 1456 544 451 539 458 542 454 536 460 540 1461 539 1461 539 1454 536 50538 1036 1467 543 452 537 1464 536 1464 536 1464 536 1465 545 450 540 456 544 452 537 1464 536 460 540 455 545 452 538 458 542 1459 541 1460 540 1452 538 # name: Vol_up type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 1044 1456 544 455 545 455 534 1464 536 1462 538 1460 540 1458 542 1456 544 1455 545 1453 537 1461 539 460 540 459 541 459 561 437 542 457 543 456 544 50915 1016 1483 517 482 518 482 518 1481 509 1489 511 1487 513 1486 514 1485 515 1483 517 1482 508 1490 510 489 511 488 512 487 513 487 513 486 514 485 515 50956 1047 1452 538 462 538 462 538 1461 539 1460 540 1458 542 1457 543 1456 544 1454 546 1453 547 1451 539 461 539 460 540 460 540 459 541 459 541 457 543 # name: Vol_dn type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 1017 1484 516 1482 518 481 519 1480 510 1489 511 1487 513 1486 514 1485 515 1483 517 482 518 1480 510 490 510 489 511 489 511 488 512 488 512 486 514 50956 1015 1486 514 1484 516 484 516 1483 517 1482 518 1480 510 1489 511 1488 512 1486 514 486 514 1485 515 484 516 484 516 483 517 483 517 482 518 481 509 50960 1011 1488 512 1486 514 486 514 1485 515 1483 517 1482 518 1480 510 1488 512 1486 514 486 514 1484 516 483 517 483 517 482 518 481 519 481 508 489 511 50961 1040 1461 539 1459 541 459 541 1458 542 1456 544 1455 545 1454 546 1452 538 1460 540 460 540 1458 542 457 543 456 544 456 544 455 545 455 545 453 547 # name: Vol_up type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 1006 1485 511 1479 517 1473 513 509 484 511 482 514 489 506 487 508 485 511 482 513 490 505 488 1477 509 1481 515 1475 511 1480 516 1474 512 1477 509 50734 1007 1483 513 1477 509 1482 514 507 486 509 484 511 492 503 490 506 487 508 485 510 483 513 490 1474 512 1479 517 1473 513 1477 509 1481 515 1474 512 50729 1012 1477 509 1481 515 1474 512 509 484 512 491 504 489 506 487 508 485 511 482 513 490 506 487 1477 509 1481 515 1475 511 1479 517 1473 513 1476 510 # name: Vol_dn type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 1012 1503 483 514 489 1475 511 483 510 486 517 478 515 481 512 483 510 486 517 1473 513 508 485 1480 516 1475 511 1479 507 1483 513 1477 509 1480 588 50646 1012 1476 510 512 491 1473 513 508 485 485 508 488 515 480 513 482 511 512 481 1482 514 482 511 1480 516 1473 513 1478 508 1482 514 1476 510 1479 590 50651 1007 1507 489 507 485 1478 508 514 489 506 487 509 484 485 508 488 515 506 487 1503 483 513 490 1474 512 1478 508 1483 513 1477 509 1481 515 1473 513 50721 1009 1505 491 478 515 1475 511 510 483 486 517 478 515 507 486 483 510 512 491 1472 514 508 485 1505 491 1473 513 1477 509 1481 587 1403 510 1478 508 50733 1008 1506 490 479 514 1477 509 512 481 514 489 506 487 508 485 484 509 513 490 1473 513 509 483 1481 515 1474 512 1479 507 1482 514 1476 510 1478 590 50642 1006 1508 488 508 485 1478 508 487 516 479 514 481 512 510 483 486 517 504 489 1474 512 510 483 1482 514 1475 511 1479 507 1483 513 1477 509 1480 516 # name: Power @@ -907,13 +906,13 @@ command: 02 FD 00 00 name: Vol_up type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 1014 1477 517 478 514 509 483 487 515 480 512 484 508 488 514 1479 515 1476 508 1483 511 1481 513 1478 516 1475 509 1482 512 1479 515 480 512 483 509 50775 1014 1477 517 504 488 508 484 513 489 506 486 510 482 514 488 1478 516 1475 509 1483 511 1480 514 1477 517 1474 510 1482 512 1478 516 505 487 509 483 50770 1009 1481 513 508 484 512 490 506 486 510 482 514 488 508 484 1481 513 1478 516 1475 509 1483 511 1480 514 1477 517 1475 509 1482 512 509 483 513 489 # name: Vol_dn type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 1010 1508 486 509 483 513 489 507 485 512 490 505 487 510 482 1509 485 511 491 1501 483 1509 485 1507 487 1504 490 1502 482 1509 485 511 491 1500 484 50779 1010 1506 488 508 484 512 490 505 487 509 483 513 489 506 486 1506 488 507 485 1506 488 1503 481 1510 484 1508 486 1505 489 1503 481 514 488 1503 491 # name: Power @@ -931,13 +930,13 @@ command: 01 FD 00 00 name: Power type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 1004 1513 481 515 487 1478 516 505 487 510 482 1484 510 1481 513 508 484 512 490 1475 509 513 489 1477 507 1484 510 511 481 515 487 1479 515 1474 510 50774 1005 1484 510 513 489 1476 508 513 489 508 484 1482 512 1479 515 506 486 511 481 1483 511 512 490 1475 509 1483 511 510 482 515 487 1504 490 1475 509 50777 1013 1503 491 506 486 1505 489 507 485 512 490 1501 483 1508 486 510 482 514 488 1503 481 515 487 1504 490 1500 484 512 490 506 486 1506 488 1502 482 # name: Mute type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 1014 1477 507 514 488 508 484 512 490 505 487 509 483 513 489 507 485 1480 514 1477 517 1475 509 1483 511 1480 514 1477 517 1475 509 1482 512 508 484 50774 1004 1486 508 513 489 507 485 511 491 504 488 508 484 513 489 505 487 1479 515 1476 508 1484 510 1481 513 1478 516 1475 509 1482 512 1480 514 507 485 50771 1007 1507 487 509 483 513 489 507 485 511 481 515 487 508 484 513 489 1502 482 1483 511 1481 513 1479 515 1476 508 1484 510 1481 513 1478 516 506 486 # name: Power @@ -1141,67 +1140,67 @@ command: 0C 00 00 00 name: Vol_up type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4559 4461 546 490 515 495 521 490 515 495 489 1484 516 1482 550 486 519 491 493 1480 520 1478 522 1477 523 1475 546 490 515 495 521 490 515 495 489 4493 545 491 525 486 519 491 514 496 488 1484 516 1483 517 1481 551 486 488 1485 515 1483 517 1482 550 486 519 491 525 486 519 491 493 1479 542 494 522 489 516 467 517 1482 550 # name: Vol_dn type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4550 4469 548 462 543 467 549 461 544 466 518 1481 520 1479 542 468 548 462 522 1477 523 1476 514 1485 515 1483 549 461 544 466 550 461 544 466 518 4491 547 463 542 468 548 462 543 467 549 462 543 467 549 461 523 1476 514 1485 515 1484 516 1482 550 461 513 1486 514 1485 515 1483 549 461 544 466 550 461 544 493 491 1481 540 # name: Vol_up type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4496 4442 513 503 488 502 489 501 490 500 491 1505 487 1508 484 505 486 504 487 1508 484 1511 492 1503 489 1499 514 484 486 504 487 502 489 501 490 4449 517 499 492 499 492 497 483 507 484 1511 492 1504 488 1499 514 483 487 1509 483 1512 491 1504 488 503 488 501 490 500 491 499 492 1504 488 501 490 500 491 492 509 1494 488 55126 4496 4446 541 482 488 502 489 501 490 500 491 1505 487 1508 484 505 486 504 487 1508 484 1503 510 1493 489 1480 512 504 487 503 488 502 489 500 491 4449 517 498 493 497 483 507 484 505 486 1502 511 1492 490 1504 488 502 489 1507 485 1509 483 1512 491 473 518 498 493 496 484 505 486 1510 483 499 512 484 486 504 487 1508 484 # name: Vol_dn type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4492 4434 510 505 486 505 486 504 487 503 488 1481 511 1484 518 499 492 498 493 1476 516 1479 513 1483 519 1469 596 402 516 500 491 499 492 498 493 4447 518 498 493 497 483 507 484 506 485 504 487 503 488 494 517 1485 486 1483 519 1476 516 1480 512 504 486 1508 484 1486 517 1479 565 425 513 502 488 501 490 492 509 1467 515 # name: Vol_up type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4500 4436 516 504 486 510 490 505 485 510 490 1500 491 1474 517 505 485 484 516 1501 490 1501 490 1501 490 1501 490 505 485 511 489 506 484 485 515 4449 513 482 508 513 487 508 482 514 486 1504 487 1504 487 1504 487 482 508 1509 482 1509 492 1499 492 504 486 509 491 504 486 509 491 1500 491 504 486 510 490 478 512 1505 486 55017 4492 4444 508 512 488 508 482 513 487 508 482 1508 483 1482 509 513 487 508 482 1509 482 1509 482 1483 508 1483 508 513 487 508 482 514 486 509 481 4457 515 506 484 511 489 506 484 511 489 1501 490 1475 516 1501 490 506 484 1480 511 1507 484 1480 511 511 489 506 484 512 488 507 483 1507 484 512 488 507 483 512 488 1503 488 # name: Vol_dn type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4493 4443 509 512 488 507 483 512 488 507 483 1482 509 1482 509 512 488 507 483 1482 509 1482 509 1482 509 1482 509 512 488 507 483 513 487 508 482 4457 516 505 485 510 490 505 485 510 490 505 485 511 489 505 485 1480 511 1480 511 1480 511 1481 510 484 516 1476 515 1476 515 1476 515 481 509 486 514 481 509 486 514 1476 515 55014 4498 4438 514 482 508 487 513 482 508 488 512 1477 514 1477 514 482 508 487 513 1477 514 1477 514 1477 514 1477 514 482 518 477 513 482 518 477 513 4451 511 485 515 479 511 485 515 480 510 485 515 480 510 485 515 1475 516 1475 516 1476 515 1476 515 480 510 1481 510 1481 510 1481 510 486 514 481 509 486 514 481 509 1482 509 # name: Vol_up type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4524 4473 512 516 491 511 486 516 491 511 496 1482 522 1482 522 506 491 512 495 1483 521 1483 521 1483 521 1483 521 506 491 512 495 507 490 512 495 4475 521 507 490 513 494 508 489 513 494 1484 520 1484 520 1483 521 507 490 1488 516 1488 516 1488 516 511 496 506 491 511 496 506 491 1488 516 511 496 506 491 512 495 1483 521 55356 4533 4463 512 516 491 511 486 516 491 511 496 1507 487 1492 512 515 492 510 487 1492 512 1492 512 1491 513 1491 513 515 492 510 487 515 492 510 487 4484 512 516 491 511 496 506 491 512 495 1483 521 1482 522 1482 522 506 491 1488 516 1487 517 1487 517 511 496 506 491 512 495 506 491 1488 516 512 495 507 490 512 495 1483 521 # name: Vol_dn type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4533 4464 521 507 490 512 495 507 490 513 494 1484 520 1484 520 508 489 513 494 1484 520 1484 520 1483 521 1483 521 506 491 511 496 506 491 512 495 4475 521 507 490 512 495 507 490 513 494 508 489 513 494 508 489 1490 514 1490 514 1490 514 1490 514 514 493 1485 519 1485 519 1485 519 509 488 515 492 510 487 516 491 1487 517 55369 4531 4465 520 508 489 514 493 509 488 515 492 1486 518 1486 518 509 488 514 493 1486 518 1485 519 1485 519 1485 519 509 488 514 493 509 488 515 492 4478 518 511 486 516 491 511 496 506 491 512 495 507 490 512 495 1483 521 1483 521 1483 521 1483 521 507 490 1488 516 1488 516 1488 516 512 495 507 490 513 494 508 489 1490 514 # name: Power type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4530 4465 521 507 490 513 494 507 490 513 494 1509 496 1483 521 506 491 512 495 1508 486 1492 512 1492 513 1491 513 514 493 509 488 514 493 509 488 4483 513 514 493 509 488 515 492 510 487 515 492 510 487 515 492 510 487 1517 488 1491 513 1490 515 513 494 1510 494 1484 520 1483 521 1483 521 506 491 512 495 506 491 1487 517 55357 4528 4468 518 510 487 515 492 510 487 515 492 1512 493 1485 519 509 488 514 493 1510 494 1483 522 1483 521 1482 512 516 491 511 486 516 491 511 486 4484 512 516 491 511 486 516 491 511 486 516 491 511 486 516 491 511 496 1507 487 1491 513 1491 514 514 493 1510 495 1484 521 1483 522 1483 522 506 491 511 486 516 491 1513 491 # name: Power type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4495 4440 512 509 491 504 486 509 491 504 486 1478 513 1479 512 509 491 504 486 1478 513 1479 512 1479 512 1479 512 509 491 504 486 509 491 504 486 4452 511 511 489 506 484 511 489 506 484 511 489 506 484 511 489 506 484 1480 511 1480 511 1480 511 510 491 1500 491 1475 516 1475 516 1475 516 504 486 510 490 505 485 1479 512 55017 4497 4439 513 507 483 513 487 507 483 513 487 1476 515 1477 514 507 483 513 487 1476 515 1476 515 1477 514 1477 514 481 509 512 488 507 483 512 488 4451 512 509 491 504 486 509 491 504 486 483 517 504 486 509 492 504 486 1504 487 1504 487 1478 513 508 482 1509 482 1509 482 1509 482 1483 508 513 487 508 482 513 487 1504 487 # name: Mute type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4501 4435 517 504 486 510 490 505 485 510 490 1475 516 1475 516 506 484 511 489 1476 515 1476 515 1476 515 1477 514 507 483 512 488 507 483 513 487 4451 511 510 490 479 511 484 516 506 484 1481 510 511 489 506 484 511 489 1476 515 1476 515 1477 514 507 483 512 488 1477 514 1477 514 1478 513 508 482 513 487 508 482 1484 517 55011 4496 4440 512 509 491 478 512 483 517 504 486 1480 511 1480 511 484 516 505 485 1480 511 1480 511 1481 510 1481 510 484 516 506 484 511 489 506 484 4455 517 504 486 509 491 504 486 483 517 1474 517 505 485 510 490 505 485 1480 511 1480 511 1481 510 511 489 480 510 1481 510 1481 510 1482 509 485 515 507 483 512 488 1477 514 # name: Power @@ -1219,31 +1218,31 @@ command: 1F 00 00 00 name: Power type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4504 4432 511 509 492 504 486 509 492 504 486 1478 514 1477 515 507 484 512 489 1476 516 1475 517 1475 517 1474 518 503 487 509 492 503 487 508 493 4446 508 513 488 507 483 512 489 507 483 512 489 506 484 511 490 506 484 1481 511 1480 512 1479 513 509 492 1473 519 1473 519 1472 509 1482 510 511 490 506 484 511 490 1475 517 54985 4498 4437 517 504 486 509 492 504 486 509 492 1473 519 1472 509 512 489 506 484 1481 511 1480 512 1480 512 1479 513 508 493 502 488 507 483 512 489 4449 516 506 485 511 490 505 485 510 491 505 485 510 491 504 486 509 492 1473 519 1473 508 1483 561 434 515 1476 516 1475 517 1474 518 1473 519 502 488 507 483 512 489 1476 516 # name: Mute type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4531 4406 516 478 512 484 538 457 544 452 538 1453 518 1473 519 476 546 450 540 1451 509 1482 510 1481 511 1480 512 483 539 457 544 452 538 457 544 4420 513 482 540 456 545 450 540 455 546 1445 547 449 541 454 536 458 543 1449 543 1449 543 1448 544 451 539 456 545 1445 547 1445 547 1444 537 458 543 453 537 457 544 1448 544 54957 4495 4440 514 481 520 477 513 482 519 476 514 1476 516 1475 517 478 512 484 517 1474 518 1473 519 1472 520 1471 521 474 516 479 511 484 517 479 511 4452 512 509 492 503 487 508 493 503 487 1477 515 507 483 512 489 506 484 1481 511 1481 511 1480 512 509 492 503 487 1478 514 1478 514 1477 515 506 484 511 490 505 485 1480 512 # name: Power type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4551 4468 549 461 544 493 523 460 545 466 518 1480 520 1479 542 468 548 489 495 1477 523 1475 515 1484 516 1483 549 461 544 493 523 487 518 466 518 4490 548 462 543 493 523 488 517 466 550 461 544 466 550 460 545 465 519 1480 520 1478 522 1477 544 465 519 1480 520 1479 521 1477 523 1476 545 464 541 496 520 490 494 1479 542 55901 4554 4465 542 494 522 489 516 494 522 488 496 1477 523 1476 545 490 515 495 489 1484 516 1483 517 1481 519 1479 542 494 522 489 516 494 522 488 496 4487 541 495 521 490 515 495 521 489 516 494 522 488 517 493 523 488 496 1476 525 1475 515 1483 549 488 496 1476 524 1475 515 1484 516 1482 550 486 519 491 525 486 488 1485 547 55897 4548 4470 548 462 543 468 548 462 543 467 517 1482 518 1480 541 469 547 490 494 1478 575 1423 515 1485 515 1483 549 461 544 466 550 461 544 466 518 4491 547 462 543 467 549 462 543 467 549 461 544 466 539 497 519 492 492 1480 520 1478 522 1477 544 465 519 1480 520 1479 521 1477 513 1486 546 464 541 469 547 464 520 1478 543 # name: Mute type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4553 4467 550 459 546 464 541 469 547 464 520 1479 521 1477 544 492 524 486 498 1475 515 1484 516 1482 518 1481 540 496 520 490 515 495 521 490 494 4488 550 486 519 491 525 486 519 491 493 1479 542 494 522 489 516 494 490 1482 518 1481 519 1480 541 494 522 489 495 1478 522 1476 514 1485 547 489 516 494 522 489 495 1477 544 # name: Power type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4555 4483 516 516 488 519 495 512 492 515 489 1525 493 1519 489 518 496 511 493 1521 487 1525 493 1520 488 1524 494 513 491 517 487 520 494 514 490 4501 519 513 491 516 488 520 494 513 491 517 487 521 493 514 490 518 486 1527 491 1522 496 1517 491 516 488 1525 493 1520 488 1525 493 1520 488 519 495 512 492 516 488 1525 493 # name: Vol_up @@ -1285,25 +1284,25 @@ command: 13 00 00 00 name: Vol_up type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4552 4468 574 436 566 444 568 442 570 441 540 1458 546 1453 572 439 573 437 544 1455 539 1460 544 1456 548 1450 575 436 566 444 568 442 570 440 541 4468 594 416 565 445 567 443 569 441 540 1459 545 1453 541 1459 566 444 547 1452 542 1457 547 1452 593 417 564 446 566 444 568 442 539 1460 565 446 566 444 568 442 539 1460 565 55957 4581 4437 543 467 545 466 546 464 538 472 519 1480 514 1485 540 471 541 469 512 1486 518 1481 513 1486 518 1481 575 436 566 444 568 442 570 440 541 4468 543 468 544 465 547 464 538 472 519 1480 514 1485 519 1479 546 465 516 1483 511 1488 516 1483 542 468 544 466 546 464 538 473 519 1480 545 466 546 464 538 472 519 1480 545 # name: Vol_dn type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4546 4473 538 473 539 470 542 469 543 467 514 1484 520 1479 545 465 547 463 518 1481 513 1486 518 1481 513 1487 538 472 540 470 542 469 543 466 515 4495 546 463 539 472 540 470 542 468 544 466 546 491 521 462 519 1480 513 1485 519 1480 514 1486 539 472 519 1479 515 1484 520 1479 546 464 548 463 539 471 541 469 512 1487 548 # name: Power type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4549 4469 542 468 544 467 545 465 537 473 519 1481 513 1485 540 471 541 469 512 1487 517 1482 512 1487 517 1482 543 467 545 465 547 463 539 472 519 4489 543 468 544 466 546 464 538 473 539 470 542 468 544 466 546 465 516 1482 512 1487 517 1482 543 468 513 1485 519 1480 514 1485 519 1480 545 465 547 463 539 472 520 1479 546 55899 4549 4470 541 469 574 436 566 445 546 463 549 1450 513 1486 539 471 572 438 543 1456 548 1451 543 1456 548 1451 574 436 566 445 567 443 569 441 540 4469 573 437 575 435 567 443 569 441 571 439 573 437 565 445 567 443 549 1451 543 1456 548 1451 574 436 545 1454 540 1459 545 1454 540 1459 566 444 568 442 570 440 541 1458 567 55878 4580 4439 572 439 573 437 565 445 567 443 548 1451 543 1456 569 441 571 439 542 1457 547 1452 542 1457 547 1452 573 437 575 435 567 443 569 442 539 4469 573 438 574 436 566 444 568 442 570 440 572 438 574 436 566 444 547 1452 542 1457 547 1452 573 437 544 1455 539 1460 544 1455 539 1460 575 435 567 444 568 442 539 1459 566 55879 4578 4442 569 441 571 439 573 437 575 435 546 1453 541 1458 567 444 568 441 540 1459 545 1454 540 1459 545 1454 571 439 573 437 575 435 567 444 548 4461 571 440 572 438 574 436 566 444 568 442 570 440 572 438 574 436 545 1454 540 1459 545 1454 571 439 542 1457 547 1452 542 1457 547 1452 573 437 575 435 567 444 547 1451 574 55871 4554 4465 546 464 538 473 539 471 541 469 512 1487 517 1481 544 467 545 465 516 1483 511 1488 516 1483 511 1488 547 463 539 472 540 470 542 468 513 4496 546 464 538 472 540 470 542 468 544 466 546 464 538 473 539 471 510 1488 516 1483 511 1488 547 463 518 1481 513 1486 518 1481 513 1486 539 472 540 470 542 467 514 1485 540 # name: Mute type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4555 4465 546 464 538 472 540 470 542 468 513 1486 518 1481 544 467 545 465 516 1483 511 1488 516 1483 511 1488 537 473 539 472 540 470 542 468 513 4496 546 464 538 473 539 471 541 469 512 1487 538 472 540 470 542 469 512 1486 518 1481 544 1455 570 441 571 439 542 1457 547 1452 542 1457 568 442 570 440 572 438 543 1456 569 55920 4555 4464 547 464 538 472 540 470 542 468 513 1486 518 1481 544 466 546 464 517 1482 512 1487 517 1482 512 1487 538 473 539 471 541 469 543 467 514 4495 547 463 539 472 540 470 542 468 513 1486 539 471 541 469 543 467 514 1485 519 1479 515 1485 540 470 542 468 513 1486 518 1481 513 1486 539 471 541 469 543 467 514 1485 540 # name: Power @@ -1405,25 +1404,25 @@ command: 28 D7 00 00 name: Vol_up type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 1048 580 597 1165 596 582 595 1167 594 875 599 578 599 1455 593 585 592 1171 600 577 600 576 601 13191 1042 586 591 1170 601 576 601 1161 600 869 595 582 595 1459 599 578 599 1163 598 580 597 579 598 13195 1048 580 597 1165 596 581 596 1167 594 875 599 578 599 1456 592 585 592 1171 600 577 600 577 600 13192 1052 576 601 1162 599 578 599 1163 598 871 593 584 593 1462 596 581 596 1166 595 582 595 582 595 # name: Vol_dn type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 1048 553 624 1138 623 554 623 1139 622 848 626 551 626 1427 621 556 652 1403 655 522 655 814 650 12579 1050 552 656 1106 624 553 624 1138 623 847 627 550 627 1427 621 556 621 1434 624 553 624 845 619 12612 1079 523 623 1139 622 555 622 1139 622 848 626 550 627 1427 621 556 621 1434 624 553 624 845 619 12613 1047 556 621 1141 620 556 621 1141 620 850 624 552 625 1429 619 558 619 1435 623 554 623 846 628 12600 1050 552 625 1137 624 553 624 1137 624 846 618 558 619 1435 623 554 623 1431 627 550 627 842 622 12609 1051 551 626 1136 625 551 626 1136 625 844 620 557 620 1434 624 554 623 1431 627 550 627 843 621 # name: Power type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 1073 528 649 1114 647 530 647 1116 645 825 649 527 650 1405 653 1402 625 552 646 1409 649 1113 648 11417 1077 524 653 1110 651 526 651 1112 649 820 654 523 654 1400 648 1407 651 526 651 1404 654 1109 652 11414 1079 523 654 1109 652 524 653 1111 650 819 624 554 654 1401 647 1408 650 529 648 1406 652 1111 650 11416 1077 525 652 1110 651 526 651 1112 649 820 654 523 654 1401 647 1408 650 528 649 1405 622 1141 651 11414 1080 521 646 1116 624 553 645 1117 654 816 648 529 648 1406 652 1403 655 523 644 1410 648 1114 647 11418 1075 526 651 1111 650 527 650 1112 649 820 623 554 654 1400 648 1407 651 526 651 1403 655 1107 654 # name: Mute type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 1051 550 627 1135 626 551 626 1136 625 845 619 850 624 553 675 794 629 842 622 555 622 555 622 13780 1047 555 622 1140 621 557 620 1141 620 850 624 845 619 558 619 850 624 846 618 559 618 559 618 13782 1045 558 619 1143 618 559 618 1144 627 842 622 847 627 550 627 842 622 848 626 551 626 551 626 13774 1053 549 618 1144 627 550 627 1135 626 843 621 849 625 551 626 844 620 850 624 553 624 552 625 13776 1051 551 626 1137 624 553 624 1138 623 846 618 851 623 554 623 846 628 841 623 554 623 554 623 13776 1051 551 626 1136 625 552 625 1137 624 845 619 850 624 553 624 846 618 852 622 555 622 554 623 13778 1049 554 623 1139 622 555 622 1140 621 849 625 844 620 557 620 850 624 846 618 559 618 558 619 # name: Power @@ -1453,31 +1452,31 @@ command: 1C 00 00 00 name: Power type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 274 789 254 1792 275 814 250 787 246 816 248 1798 279 784 249 813 251 785 248 788 245 1827 281 1791 275 1825 272 790 253 783 250 43886 277 786 278 1795 272 791 252 783 281 782 251 785 269 1804 273 1800 277 1822 275 1798 279 783 270 766 277 759 274 1825 272 1800 277 43886 277 759 274 1825 272 764 279 756 277 786 278 1795 282 781 272 763 280 755 278 785 279 1794 273 1827 270 1802 275 761 272 791 273 43888 276 761 272 1800 277 786 278 758 275 760 273 790 274 1799 278 1821 276 1796 281 1792 275 788 276 760 273 789 275 1798 279 1794 273 43889 278 785 248 1825 272 790 253 782 272 764 279 1793 274 790 274 761 282 781 273 763 280 1793 273 1825 272 1800 277 813 220 789 275 # name: Power type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 270 793 250 1795 272 818 246 791 252 809 244 766 277 1795 282 781 272 790 253 809 224 1822 275 1797 280 1820 277 785 248 788 245 43889 273 790 274 1799 278 785 248 788 276 787 246 1826 271 792 251 1794 273 1827 270 1802 275 788 245 791 273 790 253 1819 278 1794 273 43889 274 789 254 1818 269 767 276 786 247 789 275 788 245 1827 270 792 251 785 248 814 250 1796 281 1819 248 1825 272 790 253 783 271 43889 245 791 273 1799 278 786 278 784 249 787 246 1827 281 781 252 1821 276 1796 281 1792 274 814 250 786 247 789 244 1829 279 1793 274 43888 275 815 218 1828 280 783 250 786 278 785 248 788 245 1827 280 782 251 785 268 794 249 1797 280 1819 278 1794 272 791 252 810 254 # name: Vol_up type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 277 759 274 1799 278 784 280 783 250 812 242 1804 273 816 227 808 246 791 252 1819 248 1825 273 1826 251 1822 276 786 247 789 244 43888 274 815 249 1797 280 783 250 812 252 784 249 813 241 1805 272 1827 250 1822 275 787 246 816 217 819 255 807 226 1820 278 1795 272 43888 273 789 254 1817 270 793 251 785 248 814 250 1795 282 807 247 790 253 783 250 1822 276 1796 281 1818 249 1824 274 814 229 807 247 # name: Vol_dn type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 275 762 271 1800 277 786 278 784 249 813 241 795 248 1824 253 784 270 792 251 1821 246 1826 272 1801 276 1823 275 762 271 790 243 43889 274 789 275 1797 280 783 250 812 252 784 249 1823 275 762 271 1827 250 1822 276 787 246 816 217 818 246 790 253 1819 268 1804 273 43886 277 786 247 1825 273 764 280 783 250 811 253 784 249 1822 276 761 272 816 228 1819 279 1794 273 1826 251 1821 277 786 247 815 249 # name: Mute type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 275 762 271 1800 277 786 278 784 249 813 241 795 248 814 219 817 247 789 254 1818 249 1824 274 1798 279 1820 278 759 274 788 245 43887 274 789 275 1798 279 783 250 812 252 784 249 1823 274 1798 279 1820 247 1826 271 765 278 809 224 812 252 784 249 1823 275 1798 279 43880 281 782 251 1821 277 760 273 789 244 818 246 790 253 808 246 791 252 809 224 1822 275 1797 280 1819 248 1825 273 790 253 808 246 # name: Power @@ -1675,25 +1674,25 @@ command: 15 00 00 00 name: Power type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 8437 4188 538 1565 539 1565 539 513 544 508 538 513 544 1559 545 507 539 1564 540 1564 540 1563 541 1563 541 511 546 1557 547 505 542 511 546 505 542 20497 597 1507 545 1559 545 507 539 512 545 507 539 1564 540 512 545 1558 546 1558 546 1558 546 1557 547 505 542 1562 542 510 547 505 542 510 547 20492 540 1564 540 1564 540 512 545 506 540 511 546 1558 546 505 542 1562 542 1562 542 1562 542 1561 543 509 548 1555 538 514 543 509 537 514 543 20495 547 1558 546 1557 547 505 541 511 546 505 542 1562 542 510 547 1556 548 1556 548 1556 548 1556 537 514 543 1560 544 508 538 514 543 508 538 20501 541 1562 542 1562 542 510 547 505 541 510 547 1556 548 504 543 1561 543 1561 543 1560 544 1560 544 508 538 1565 539 513 544 507 539 513 544 20494 548 1556 548 1556 548 504 543 509 548 504 543 1560 544 508 539 1565 539 1565 539 1564 540 1564 540 512 545 1559 545 506 540 512 545 506 540 20499 543 1560 544 1560 544 508 539 513 544 508 538 1564 540 512 545 1559 545 1558 546 1558 546 1558 546 506 541 1563 541 510 547 505 542 510 547 # name: Vol_up type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 8430 4194 542 1562 542 1562 542 510 547 505 541 510 547 1556 548 504 542 1561 543 509 548 1556 548 1556 548 1556 548 1556 548 504 542 509 537 514 543 20496 545 1559 545 1559 545 507 539 512 545 507 539 1564 540 512 545 1559 545 507 539 1564 540 1564 540 1563 541 1563 541 511 546 506 540 511 546 20494 546 1557 547 1557 547 505 541 510 547 505 541 1562 542 510 547 1556 548 505 541 1562 542 1562 542 1561 543 1561 543 509 548 504 542 509 537 20501 540 1565 539 1565 539 512 545 507 539 512 545 1559 545 507 539 1564 540 512 545 1559 545 1558 546 1558 546 1558 546 506 540 511 546 506 540 20498 543 1562 542 1562 542 510 547 505 541 510 547 1557 547 505 541 1562 542 509 548 1556 548 1556 548 1556 548 1556 548 504 542 509 548 504 542 20497 543 1560 544 1560 544 508 538 513 544 508 538 1565 539 513 544 1560 544 508 538 1565 539 1565 539 1565 539 1564 540 513 544 507 539 512 545 20495 545 1558 546 1558 546 506 540 511 546 506 540 1563 541 511 546 1558 546 506 540 1563 541 1563 541 1563 541 1562 542 510 547 505 541 510 547 20493 548 1556 548 1556 548 504 542 509 548 504 542 1561 543 509 537 1566 538 514 543 1560 544 1560 544 1560 544 1560 544 508 538 513 544 508 538 20501 539 1564 540 1564 540 512 545 507 539 512 545 1559 545 507 539 1564 540 512 545 1559 545 1558 546 1558 546 1558 546 506 540 511 546 506 540 # name: Vol_dn type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 8434 4191 545 1559 545 1559 545 534 512 513 544 507 539 1564 540 538 519 1559 545 1558 546 1558 546 1558 546 1558 546 1558 546 533 513 511 546 506 540 19446 547 1557 547 1557 547 532 514 511 546 532 514 1562 593 459 546 1557 547 1557 547 1557 547 1557 547 1557 547 1557 547 531 515 510 547 505 541 19446 548 1556 548 1556 548 530 516 509 548 504 543 1561 543 535 512 1566 538 1565 539 1565 539 1565 539 1565 539 1565 539 540 517 509 537 514 543 19444 539 1565 539 1565 539 513 544 508 538 513 544 1559 545 534 512 1564 540 1564 540 1564 540 1564 540 1564 540 1564 540 539 518 507 539 513 544 19442 541 1563 541 1563 541 538 519 507 539 512 545 1558 546 506 540 1563 541 1563 541 1563 541 1563 541 1563 541 1562 542 537 520 505 541 511 546 19440 595 1509 543 1561 543 536 521 504 542 509 548 1555 538 540 517 1560 544 1560 544 1560 544 1560 544 1560 544 1560 544 534 512 513 544 508 538 19448 546 1559 545 1559 545 533 513 512 545 507 539 1563 541 538 519 1558 546 1558 546 1558 546 1557 547 1558 546 1558 546 533 513 512 545 506 540 19447 546 1557 547 1557 547 532 514 511 546 506 540 1562 542 537 520 1557 547 1557 547 1557 547 1557 547 1557 547 1557 547 505 542 510 547 505 541 19445 548 1556 548 1556 548 531 515 510 547 504 542 1561 543 536 521 1555 538 1566 538 1566 538 1566 538 1566 538 1565 539 514 543 509 537 514 543 # name: Mute type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 8435 4189 547 1557 547 1557 547 505 541 510 547 505 541 1562 542 510 547 1557 547 505 541 510 547 1557 546 1557 547 1557 547 505 541 510 547 505 541 21550 547 1558 545 1558 546 506 540 511 546 506 540 1563 541 511 546 1558 545 506 540 511 546 1558 546 1558 546 1558 546 506 540 511 546 506 540 21551 546 1558 546 1558 546 506 540 512 545 506 540 1563 540 512 545 1558 546 506 540 512 545 1558 546 1558 546 1558 546 506 540 512 545 506 540 21551 546 1559 545 1559 545 507 539 512 545 507 539 1564 539 512 545 1559 545 507 539 512 545 1559 545 1559 545 1559 545 507 539 512 545 507 539 21552 545 1559 545 1559 545 507 539 513 544 507 539 1564 540 512 545 1559 545 507 539 512 545 1559 545 1559 545 1559 545 507 539 512 545 507 539 21552 545 1559 545 1559 545 507 539 513 544 507 539 1565 538 513 544 1559 545 507 539 513 544 1559 545 1559 544 1559 545 534 512 513 544 507 539 21553 544 1560 544 1560 544 534 512 513 544 508 538 1565 539 539 518 1560 544 534 512 513 544 1560 544 1560 544 1560 544 534 512 513 544 508 538 # name: Power @@ -1723,19 +1722,19 @@ command: 1B 00 00 00 name: Vol_up type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 8498 4205 651 1471 576 530 550 1572 547 535 545 536 544 1578 541 540 550 1572 547 535 545 1576 543 539 541 1580 549 1572 547 534 546 1576 543 539 541 540 550 1571 548 533 547 1574 545 537 543 539 541 541 549 532 548 1574 545 536 544 1578 541 540 550 1571 548 1572 547 1574 545 1576 543 26533 8497 4203 653 1468 569 538 542 1579 550 531 549 533 547 1573 546 535 545 1576 543 538 542 1579 550 531 549 1571 548 1573 546 536 544 1576 543 539 541 540 550 1571 548 533 547 1574 545 536 544 538 542 540 550 530 550 1572 547 534 546 1575 544 537 543 1578 541 1579 550 1570 549 1572 547 26524 8496 4207 576 1570 549 533 547 1574 545 537 543 539 541 1580 549 532 548 1573 546 536 544 1577 542 539 551 1570 549 1572 547 534 546 1575 544 538 542 540 550 1571 548 534 546 1575 544 538 542 540 550 531 549 533 547 1575 544 537 543 1579 550 531 549 1571 548 1572 547 1574 545 1576 543 26529 8491 4211 573 1573 546 535 545 1576 543 539 551 530 550 1571 548 533 547 1574 545 536 544 1578 541 540 550 1570 549 1572 547 534 546 1575 544 538 542 539 541 1580 549 532 548 1572 547 535 545 537 543 539 541 540 550 1571 548 533 547 1574 545 537 543 1578 541 1579 550 1571 548 1574 545 26522 8498 4202 571 1574 545 537 543 1578 541 541 549 532 548 1573 546 534 546 1575 544 537 543 1577 542 540 550 1570 549 1571 548 533 547 1574 545 537 543 538 542 1579 550 531 549 1572 547 534 546 536 544 537 543 538 542 1579 550 531 549 1572 547 535 545 1575 544 1577 542 1579 550 1571 548 26522 8498 4203 570 1575 544 537 543 1578 541 541 549 532 548 1573 546 535 545 1575 544 538 542 1579 550 531 549 1571 548 1572 547 535 545 1575 544 538 542 539 541 1580 549 532 548 1572 547 534 546 536 544 537 543 539 541 1579 550 531 549 1571 548 533 547 1573 546 1574 545 1575 544 1577 542 # name: Vol_dn type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 8490 4211 573 1573 546 535 545 1576 543 539 541 540 550 1571 548 532 548 1573 546 535 545 1576 543 539 541 1579 550 1571 548 533 547 1574 545 536 544 1576 543 1578 541 540 550 1570 549 533 547 534 546 536 544 537 543 539 541 540 550 1570 549 532 548 1572 547 1573 546 1574 545 1576 543 26528 8492 4208 648 1475 572 534 546 1575 544 537 543 539 541 1580 549 532 548 1573 546 535 545 1576 543 538 542 1579 550 1571 548 533 547 1575 544 537 543 1577 542 1579 550 532 548 1573 546 535 545 537 543 539 541 541 549 532 548 533 547 1575 544 537 543 1578 541 1579 550 1571 548 1573 546 26523 8496 4203 622 1499 569 537 543 1578 541 541 549 532 548 1572 547 534 546 1574 545 537 543 1578 541 540 550 1570 549 1571 548 534 546 1575 544 538 542 1578 541 1580 549 532 548 1573 546 536 544 538 542 540 550 532 548 533 547 535 545 1576 543 539 541 1579 550 1571 548 1573 546 1574 545 26521 8498 4201 572 1573 546 536 544 1577 542 540 550 531 549 1571 548 533 547 1573 546 536 544 1576 543 539 541 1579 550 1570 549 532 548 1573 546 535 545 1576 543 1578 541 540 550 1571 548 533 547 535 545 537 543 538 542 540 550 531 549 1572 547 534 546 1575 544 1576 543 1577 542 1579 550 26522 8498 4203 570 1575 544 538 542 1579 550 532 548 533 547 1575 544 537 543 1578 541 540 550 1570 549 533 547 1573 546 1575 544 537 543 1578 541 540 550 1571 548 1573 546 536 544 1578 551 531 549 533 547 535 545 537 543 539 541 540 550 1571 548 533 547 1575 544 1576 543 1578 541 1580 549 26521 8498 4203 570 1576 543 538 542 1580 549 532 548 533 547 1574 545 536 544 1577 542 540 550 1570 549 533 547 1573 546 1575 544 538 542 1579 550 531 549 1571 548 1573 546 536 544 1576 543 539 551 531 549 532 548 534 546 536 544 538 542 1579 550 531 549 1572 547 1573 546 1574 545 1576 543 # name: Power type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 8500 4203 653 1468 569 538 542 1579 550 532 548 534 546 1575 544 536 544 1578 541 540 550 1572 547 534 546 1575 544 1577 542 539 541 1580 550 532 548 534 546 535 545 1576 543 1577 542 1579 551 532 548 534 546 536 544 1577 542 1578 552 531 549 533 547 534 546 1574 545 1575 544 1577 542 26533 8492 4211 573 1573 546 535 545 1576 543 539 551 530 550 1571 548 533 547 1574 545 536 544 1577 542 540 550 1570 549 1572 547 534 546 1576 543 539 541 541 549 532 548 1573 546 1574 545 1576 543 539 551 531 549 533 547 1573 546 1576 543 539 541 541 549 532 548 1573 546 1574 545 1576 543 26532 8493 4209 575 1571 548 533 547 1575 544 537 543 539 541 1580 550 531 549 1572 547 534 546 1576 543 538 542 1579 551 1570 549 532 548 1574 545 537 543 538 542 540 550 1570 549 1572 547 1575 544 537 543 539 541 540 550 1571 548 1573 546 536 544 538 542 539 551 1570 549 1572 547 1574 545 26530 8496 4207 567 1579 551 531 549 1572 547 535 545 536 544 1576 543 538 542 1579 551 530 550 1571 548 534 546 1574 545 1576 543 538 542 1579 551 532 548 533 547 534 546 1576 543 1577 542 1579 551 532 548 534 546 535 545 1576 543 1578 541 540 550 532 548 533 547 1574 545 1575 544 1577 542 26531 8495 4210 574 1571 548 534 546 1575 544 538 542 539 551 1569 550 531 549 1572 547 535 545 1576 543 539 541 1579 550 1571 548 534 546 1575 544 538 542 540 550 531 549 1572 547 1549 570 1551 568 539 551 531 549 532 548 1573 546 1550 569 538 542 540 550 531 549 1571 548 1548 571 1550 569 # name: Power @@ -1783,7 +1782,7 @@ command: 0E 00 00 00 name: Power type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 8441 4184 542 1562 541 1562 542 511 546 506 540 511 546 1557 546 506 540 1563 541 1563 540 1563 541 512 545 507 539 512 545 507 539 512 545 507 539 22605 538 1565 539 1565 538 514 543 509 537 514 543 1560 543 509 548 1555 548 1556 548 1556 547 504 542 510 547 505 541 510 547 505 541 510 547 22597 546 1559 544 1559 545 507 539 513 544 507 539 1564 539 513 544 1559 544 1559 545 545 508 549 513 508 538 513 544 508 538 513 544 22601 542 1562 542 1561 543 510 547 505 541 510 547 1557 547 505 541 1562 542 1562 542 1562 542 510 547 505 541 510 547 505 541 511 546 505 541 22603 540 1564 539 1565 538 513 544 508 538 513 544 1560 544 508 538 1565 538 1565 538 1566 537 514 543 509 548 504 542 509 548 504 542 509 548 22597 546 1558 546 1558 546 506 540 512 545 507 539 1564 540 512 545 1559 545 1559 544 1559 544 507 539 513 544 508 538 513 544 508 538 513 544 22600 543 1561 542 1562 542 510 547 505 541 510 547 1557 547 505 541 1562 541 1563 540 1563 541 511 546 506 540 511 546 506 540 511 546 506 540 22604 539 1565 538 1566 537 514 543 509 548 504 542 1561 543 509 548 1556 548 1556 548 1556 547 504 542 510 547 505 541 510 547 505 541 510 547 22598 545 1559 544 1559 545 507 539 512 545 507 539 1564 540 512 545 1559 544 1559 545 1559 545 508 538 513 544 508 538 513 544 508 538 513 544 22601 542 1562 542 1562 542 511 546 505 541 511 546 1557 546 506 540 1563 541 1563 540 1563 541 511 546 506 540 511 546 506 540 512 545 506 540 22604 539 1565 538 1566 537 514 543 509 548 504 542 1561 543 509 548 1556 547 1556 547 1556 548 504 542 510 547 505 541 510 547 505 541 510 547 # name: Power @@ -1881,8 +1880,7 @@ type: parsed protocol: NECext address: 12 36 00 00 command: 01 FE 00 00 -# -# OFF +# name: Power type: parsed protocol: RC5 @@ -1964,25 +1962,25 @@ command: 06 00 00 00 name: Power type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4567 4454 549 481 522 481 523 480 498 506 498 1509 499 1509 523 480 499 505 523 1484 523 1484 523 1485 521 1487 520 484 519 485 519 485 519 471 519 4488 518 485 519 485 518 485 519 485 518 485 519 485 519 485 519 485 519 1489 519 1489 518 1489 518 485 519 1489 518 1490 518 1490 517 1490 518 486 518 486 518 486 518 1490 518 # name: Vol_up type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4571 4453 552 478 525 478 526 478 500 504 500 1507 501 1506 526 478 525 477 526 1481 526 1481 526 1482 524 1483 523 481 522 482 521 481 522 468 522 4484 521 482 521 482 521 482 522 481 522 1486 521 1486 521 1486 521 482 521 1486 522 1486 522 1486 522 482 522 482 522 482 522 482 522 1486 522 483 521 482 521 483 521 1487 521 55474 4547 4477 524 479 524 480 524 480 523 480 523 1485 523 1484 524 481 523 481 523 1485 523 1485 522 1485 523 1485 523 480 524 480 524 481 523 467 524 4484 523 481 523 481 523 481 523 481 523 1485 523 1485 523 1485 523 481 523 1485 523 1485 523 1485 523 481 523 481 523 481 523 481 523 1485 523 481 523 481 523 481 523 1486 522 # name: Vol_dn type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4572 4453 552 456 548 479 524 479 500 504 500 1482 526 1482 551 478 526 478 525 1483 524 1484 523 1486 521 1487 521 483 521 483 521 483 521 470 520 4487 521 483 521 483 521 483 521 483 521 483 521 483 521 483 521 1488 520 1488 520 1488 520 1488 520 483 521 1488 520 1488 520 1488 520 483 521 483 521 483 521 483 521 1488 520 55457 4565 4483 521 483 521 483 521 483 521 483 520 1488 520 1487 521 483 521 483 520 1488 520 1488 520 1488 520 1488 520 483 520 484 520 484 520 470 521 4487 520 484 520 483 520 484 520 484 520 484 520 484 519 484 520 1488 520 1488 520 1488 519 1488 520 484 520 1488 520 1488 520 1488 520 484 520 484 520 484 520 484 520 1489 519 # name: Mute type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4573 4451 552 477 527 478 526 477 526 478 501 1507 501 1506 502 502 527 477 526 1481 527 1481 527 1481 526 1482 525 479 524 480 523 481 523 468 523 4485 522 481 522 482 522 482 522 482 522 1486 522 482 521 482 522 482 522 1486 522 1486 522 1486 522 482 522 482 521 1487 521 1487 521 1487 521 482 521 483 521 483 521 1487 521 55462 4547 4474 524 478 525 479 524 480 523 480 523 1484 523 1484 523 480 523 480 523 1484 523 1484 524 1484 523 1485 522 480 523 480 524 480 523 467 523 4484 522 480 524 481 523 481 523 481 523 1485 523 481 522 481 523 481 523 1485 522 1485 523 1485 523 481 523 481 522 1485 522 1485 522 1485 523 481 522 481 522 481 523 1485 523 # name: Vol_dn @@ -2014,7 +2012,7 @@ type: parsed protocol: NEC address: 00 00 00 00 command: 47 00 00 00 -# +# name: Power type: parsed protocol: NEC @@ -2038,7 +2036,7 @@ type: parsed protocol: NEC42 address: 6E 00 00 00 command: 4C 00 00 00 -# +# name: Vol_up type: parsed protocol: NEC @@ -2054,19 +2052,19 @@ command: 10 00 00 00 name: Power type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4555 4463 532 473 532 473 531 474 530 474 530 1478 531 1478 531 475 529 476 528 1482 551 1480 529 1480 528 1481 528 477 527 478 527 478 526 478 527 4498 526 477 527 477 527 478 526 478 526 478 526 478 526 478 527 478 526 1483 527 1483 526 1483 526 478 526 1483 527 1483 527 1483 526 1483 526 478 526 478 527 478 527 1484 526 55527 4527 4492 526 478 526 478 526 478 526 479 526 1483 527 1483 526 478 526 478 526 1483 527 1483 526 1483 526 1483 526 478 527 478 526 479 526 478 526 4497 526 478 526 478 526 478 526 478 526 478 526 478 526 478 526 478 526 1484 525 1483 526 1484 525 478 526 1484 525 1484 525 1484 525 1484 525 478 526 479 526 479 525 1484 525 # name: Vol_up type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4552 4463 531 474 530 474 530 474 530 474 530 1478 531 1478 531 475 529 476 553 1455 554 1478 530 1479 529 1481 527 477 527 478 526 478 526 478 526 4497 526 478 526 478 526 478 526 477 527 1483 526 1482 527 1482 527 478 526 1483 526 1483 526 1483 526 478 526 478 526 478 526 478 526 1483 526 478 526 478 526 478 526 1483 526 # name: Vol_dn type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 325 50440 173 137541 4551 4465 530 475 529 475 529 474 530 474 530 1479 530 1479 529 476 528 477 527 1480 554 1457 551 1480 528 1481 527 477 527 478 526 478 526 478 526 4497 525 478 526 478 526 478 526 478 526 479 525 479 525 479 525 1484 525 1483 526 1484 525 1483 526 479 525 1483 526 1483 526 1483 526 479 525 479 525 479 525 479 525 1484 525 # name: Power @@ -2098,7 +2096,7 @@ type: parsed protocol: NEC address: 00 00 00 00 command: 04 00 00 00 -# +# name: Power type: parsed protocol: NEC @@ -2126,13 +2124,13 @@ command: B9 00 00 00 name: Power type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 2760 833 499 418 470 419 416 887 445 872 893 468 416 469 414 442 443 441 444 440 446 440 446 440 446 440 892 882 445 444 466 446 439 447 438 448 437 449 884 447 437 450 437 894 436 449 437 449 437 449 437 449 437 449 884 448 436 894 437 450 436 116126 2673 887 445 473 440 449 439 893 438 880 884 448 437 449 437 449 437 449 437 449 437 449 437 449 437 449 884 890 437 449 437 449 437 450 436 449 437 449 884 448 436 451 437 894 436 449 437 450 436 450 436 449 437 450 883 448 436 894 436 449 437 116123 2672 888 469 449 439 450 437 893 438 881 883 448 437 449 437 449 437 449 437 449 438 449 437 449 437 449 884 891 437 449 437 449 437 449 437 449 437 449 884 448 436 451 437 894 437 449 437 449 437 449 437 449 437 449 884 448 436 894 437 449 437 # name: Mute type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 2701 861 496 420 445 444 444 885 446 871 892 441 444 441 444 441 471 415 471 415 470 417 467 444 440 446 883 891 436 449 437 449 437 450 436 450 436 449 883 448 437 451 436 894 436 450 436 450 436 450 436 450 436 450 882 449 436 894 883 116552 2698 862 469 448 438 449 438 894 437 880 884 448 437 449 437 448 438 449 437 449 437 449 437 449 437 449 884 890 437 449 437 449 437 449 437 449 437 449 884 448 437 450 437 893 437 449 437 449 437 449 437 449 437 449 884 448 436 894 883 # name: Vol_up @@ -2177,8 +2175,6 @@ protocol: NEC address: 00 00 00 00 command: 46 00 00 00 # -# Audio_Receivers -# name: Prev type: parsed protocol: RC5 @@ -2503,8 +2499,6 @@ protocol: NECext address: 7F 01 00 00 command: 67 98 00 00 # -# CD Players -# name: Next type: parsed protocol: Kaseikyo @@ -2538,13 +2532,13 @@ command: 07 00 00 00 name: Play type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 8546 4227 563 511 563 1558 566 511 563 483 566 508 567 1558 566 509 566 1558 566 1586 564 483 566 1561 588 1558 566 1560 564 536 539 1558 566 535 539 1558 566 1586 564 1557 567 486 589 1560 564 511 563 510 539 508 567 485 589 484 565 508 567 1559 565 509 566 1560 564 1560 590 1585 539 25430 8575 4251 538 485 590 1557 567 486 588 511 538 536 539 1557 567 509 566 1558 566 1560 590 483 566 1560 589 1559 565 1562 562 536 539 1560 564 509 566 1558 566 1564 586 1586 538 484 590 1559 565 487 588 482 567 509 566 511 563 483 566 508 567 1558 566 509 566 1560 564 1559 591 1586 538 # name: Pause type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 8571 4226 564 509 540 1587 563 510 539 510 565 510 564 1558 566 510 565 1584 540 1585 539 535 540 1587 537 1586 564 1585 539 510 564 1584 540 511 563 482 567 535 539 485 590 1584 540 1585 539 509 566 510 564 510 539 1586 564 1585 539 1585 539 536 539 510 564 1585 539 1557 567 1558 592 25434 8571 4199 591 510 539 1586 564 509 540 535 540 485 590 1585 539 484 591 1561 563 1584 540 507 568 1585 539 1586 564 1585 539 486 589 1585 539 484 591 425 624 535 540 486 589 1585 539 1585 539 511 564 511 563 486 563 1586 564 1558 566 1558 566 508 567 486 589 1560 564 1558 566 1561 589 # name: Next @@ -2589,18 +2583,6 @@ protocol: Samsung32 address: 81 00 00 00 command: 01 00 00 00 # -name: Play -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 8546 4227 563 511 563 1558 566 511 563 483 566 508 567 1558 566 509 566 1558 566 1586 564 483 566 1561 588 1558 566 1560 564 536 539 1558 566 535 539 1558 566 1586 564 1557 567 486 589 1560 564 511 563 510 539 508 567 485 589 484 565 508 567 1559 565 509 566 1560 564 1560 590 1585 539 25430 8575 4251 538 485 590 1557 567 486 588 511 538 536 539 1557 567 509 566 1558 566 1560 590 483 566 1560 589 1559 565 1562 562 536 539 1560 564 509 566 1558 566 1564 586 1586 538 484 590 1559 565 487 588 482 567 509 566 511 563 483 566 508 567 1558 566 509 566 1560 564 1559 591 1586 538 -# -name: Pause -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 8571 4226 564 509 540 1587 563 510 539 510 565 510 564 1558 566 510 565 1584 540 1585 539 535 540 1587 537 1586 564 1585 539 510 564 1584 540 511 563 482 567 535 539 485 590 1584 540 1585 539 509 566 510 564 510 539 1586 564 1585 539 1585 539 536 539 510 564 1585 539 1557 567 1558 592 25434 8571 4199 591 510 539 1586 564 509 540 535 540 485 590 1585 539 484 591 1561 563 1584 540 507 568 1585 539 1586 564 1585 539 486 589 1585 539 484 591 425 624 535 540 486 589 1585 539 1585 539 511 564 511 563 486 563 1586 564 1558 566 1558 566 508 567 486 589 1560 564 1558 566 1561 589 -# name: Pause type: parsed protocol: NEC @@ -2673,9 +2655,6 @@ protocol: NECext address: 00 EF 00 00 command: 01 FE 00 00 # -# SoundBars -# -# name: Play type: parsed protocol: NECext @@ -2709,97 +2688,97 @@ command: 24 DB 00 00 name: Play type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4572 4451 552 478 526 478 526 478 500 503 501 1483 525 1482 551 478 525 478 525 1481 526 1482 524 1483 523 1485 522 482 521 482 522 482 522 469 521 4486 521 482 522 482 522 483 521 483 521 483 521 1487 521 483 521 1487 521 483 521 483 521 483 521 1487 521 1487 521 483 520 1487 521 483 521 1488 520 1487 521 1487 521 483 521 55487 4544 4482 522 482 522 482 522 482 522 482 522 1487 521 1487 521 482 522 482 522 1487 521 1487 521 1487 521 1487 521 482 522 483 521 483 521 469 522 4486 521 483 521 483 520 483 521 483 521 483 521 1487 521 483 521 1487 521 483 521 483 521 483 521 1488 521 1487 521 483 521 1488 520 483 521 1488 521 1488 521 1488 520 483 521 # name: Play type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4497 4438 513 508 492 503 487 509 491 504 486 1479 512 1479 511 510 490 505 485 1481 510 1481 510 1481 509 1482 509 512 488 507 483 513 487 508 482 4457 515 506 484 511 489 506 484 512 488 507 483 1482 509 513 487 1478 513 508 482 513 487 509 491 1473 518 1474 517 504 486 1479 512 510 490 1475 515 1475 516 1476 514 480 510 55019 4494 4442 509 511 489 507 483 512 488 507 483 1508 483 1508 483 513 487 508 482 1482 509 1483 508 1509 492 1499 492 504 486 510 490 505 485 510 490 4449 513 508 482 487 513 508 482 488 512 483 517 1499 492 504 486 1505 486 510 490 479 511 510 490 1501 490 1501 490 506 484 1506 485 511 489 1502 489 1476 515 1476 515 507 483 # name: Pause type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4572 4451 552 478 526 478 526 478 500 503 501 1483 525 1482 551 478 525 478 525 1481 526 1482 524 1483 523 1485 522 482 521 482 522 482 522 469 521 4486 521 482 522 482 522 483 521 483 521 483 521 1487 521 483 521 1487 521 483 521 483 521 483 521 1487 521 1487 521 483 520 1487 521 483 521 1488 520 1487 521 1487 521 483 521 55487 4544 4482 522 482 522 482 522 482 522 482 522 1487 521 1487 521 482 522 482 522 1487 521 1487 521 1487 521 1487 521 482 522 483 521 483 521 469 522 4486 521 483 521 483 520 483 521 483 521 483 521 1487 521 483 521 1487 521 483 521 483 521 483 521 1488 521 1487 521 483 521 1488 520 483 521 1488 521 1488 521 1488 520 483 521 # name: Pause type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4497 4438 513 508 492 503 487 509 491 504 486 1479 512 1479 511 510 490 505 485 1481 510 1481 510 1481 509 1482 509 512 488 507 483 513 487 508 482 4457 515 506 484 511 489 506 484 512 488 507 483 1482 509 513 487 1478 513 508 482 513 487 509 491 1473 518 1474 517 504 486 1479 512 510 490 1475 515 1475 516 1476 514 480 510 55019 4494 4442 509 511 489 507 483 512 488 507 483 1508 483 1508 483 513 487 508 482 1482 509 1483 508 1509 492 1499 492 504 486 510 490 505 485 510 490 4449 513 508 482 487 513 508 482 488 512 483 517 1499 492 504 486 1505 486 510 490 479 511 510 490 1501 490 1501 490 506 484 1506 485 511 489 1502 489 1476 515 1476 515 507 483 # name: Next type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4503 4434 517 504 486 510 490 505 485 510 490 1475 515 1476 514 507 483 512 488 1477 513 1477 513 1478 512 1479 511 483 517 479 511 484 516 479 511 4455 516 478 512 483 517 479 511 485 515 480 510 486 514 1477 513 1477 513 482 518 477 513 483 517 1474 516 1474 516 1475 515 480 510 485 515 1476 514 1477 513 1477 513 483 517 55012 4497 4440 511 484 516 480 510 485 515 480 510 1481 509 1482 509 487 513 482 518 1473 517 1474 516 1474 516 1475 515 480 510 486 514 481 509 486 514 4451 510 485 567 428 510 486 514 481 509 487 565 430 570 1421 518 1473 518 478 512 483 517 478 512 1479 511 1480 510 1480 510 486 514 481 509 1481 510 1481 509 1482 509 487 513 # name: Play type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4583 4441 575 454 550 455 549 457 548 456 549 1461 550 1460 549 456 549 457 549 1461 548 1462 549 1461 549 1461 548 457 548 457 549 457 547 457 549 4462 548 458 548 457 548 457 548 457 548 457 548 1462 548 457 548 1461 549 457 548 457 548 457 548 1462 548 1461 549 457 548 1461 549 457 548 1461 549 1461 548 1462 547 458 547 55448 4580 4465 550 456 549 456 549 456 550 456 549 1461 549 1460 549 457 549 456 549 1461 549 1461 549 1461 548 1461 549 457 548 457 549 457 548 457 548 4462 549 457 548 457 548 458 548 457 548 457 548 1462 549 457 547 1462 549 457 548 457 548 457 548 1462 548 1462 549 457 548 1462 548 457 548 1462 548 1461 549 1462 548 457 548 # name: Pause type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4583 4441 575 454 550 455 549 457 548 456 549 1461 550 1460 549 456 549 457 549 1461 548 1462 549 1461 549 1461 548 457 548 457 549 457 547 457 549 4462 548 458 548 457 548 457 548 457 548 457 548 1462 548 457 548 1461 549 457 548 457 548 457 548 1462 548 1461 549 457 548 1461 549 457 548 1461 549 1461 548 1462 547 458 547 55448 4580 4465 550 456 549 456 549 456 550 456 549 1461 549 1460 549 457 549 456 549 1461 549 1461 549 1461 548 1461 549 457 548 457 549 457 548 457 548 4462 549 457 548 457 548 458 548 457 548 457 548 1462 549 457 547 1462 549 457 548 457 548 457 548 1462 548 1462 549 457 548 1462 548 457 548 1462 548 1461 549 1462 548 457 548 # name: Next type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4582 4465 551 454 550 455 550 455 549 456 549 1460 550 1460 549 457 548 456 549 1461 549 1461 548 1461 549 1460 549 457 549 456 548 457 548 457 548 4461 549 456 548 457 548 457 548 457 548 457 548 457 548 1462 548 1461 547 457 549 457 548 456 549 1460 549 1461 547 1461 549 457 547 457 548 1461 549 1461 548 1461 548 457 548 55436 4578 4464 549 455 549 456 549 455 549 456 550 1460 549 1460 549 456 550 455 550 1460 550 1460 549 1460 550 1460 549 456 549 457 548 457 548 457 549 4461 548 457 548 457 548 457 548 457 547 457 549 457 547 1462 548 1461 549 457 548 457 548 457 548 1461 548 1461 548 1462 548 457 548 457 549 1461 549 1461 548 1461 548 457 548 # name: Prev type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4554 4466 540 469 547 490 515 495 521 490 494 1478 522 1477 544 466 539 471 513 1512 488 1484 516 1483 570 1429 540 497 519 465 540 470 546 491 493 4490 548 462 543 467 549 488 517 493 491 1481 519 1480 541 495 489 1510 522 462 543 494 521 489 495 1476 545 466 539 471 513 1485 547 464 520 1505 495 1477 513 1487 545 465 540 # name: Next type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4555 4464 542 468 548 462 543 467 549 488 496 1477 523 1476 545 465 551 459 525 1475 515 1484 516 1483 517 1482 550 486 519 465 551 459 546 464 520 4490 547 488 517 467 549 462 543 467 548 462 543 467 517 1482 518 1481 540 496 520 464 541 469 515 1484 516 1483 517 1482 550 460 545 465 519 1480 520 1479 521 1478 543 493 522 # name: Play type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4549 4471 546 464 541 469 547 463 542 469 515 1484 516 1483 549 461 544 466 518 1481 519 1480 520 1479 521 1477 544 466 550 461 544 467 549 461 523 4486 541 469 546 464 541 469 547 464 541 469 515 1483 549 462 522 1477 544 465 540 471 545 465 572 1428 520 1479 542 468 569 1430 549 462 522 1476 514 1486 514 1485 547 463 542 # name: Play type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4552 4464 586 420 584 420 584 420 612 392 529 1478 531 1478 531 476 553 451 553 1455 554 1479 528 1480 528 1481 527 478 526 478 526 478 526 478 526 4497 526 478 526 478 526 478 526 478 526 478 526 1483 526 478 526 1483 526 478 526 478 526 478 526 1483 526 1483 526 478 526 1483 526 479 525 1483 526 1483 526 1483 526 478 526 # name: Play type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4548 4471 540 471 541 468 544 467 545 465 516 1482 512 1488 537 473 539 472 519 1479 514 1484 520 1479 515 1485 539 470 542 468 544 466 546 464 517 4493 538 472 540 470 542 468 544 466 546 464 517 1482 543 468 513 544 471 469 574 436 514 1485 519 1480 545 465 547 1452 573 438 543 1456 548 1451 543 1456 569 442 570 # name: Pause type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4549 4471 546 464 541 469 547 463 542 469 515 1484 516 1483 549 461 544 466 518 1481 519 1480 520 1479 521 1477 544 466 550 461 544 467 549 461 523 4486 541 469 546 464 541 469 547 464 541 469 515 1483 549 462 522 1477 544 465 540 471 545 465 572 1428 520 1479 542 468 569 1430 549 462 522 1476 514 1486 514 1485 547 463 542 # name: Pause type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4552 4464 586 420 584 420 584 420 612 392 529 1478 531 1478 531 476 553 451 553 1455 554 1479 528 1480 528 1481 527 478 526 478 526 478 526 478 526 4497 526 478 526 478 526 478 526 478 526 478 526 1483 526 478 526 1483 526 478 526 478 526 478 526 1483 526 1483 526 478 526 1483 526 479 525 1483 526 1483 526 1483 526 478 526 # name: Pause type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4548 4471 540 471 541 468 544 467 545 465 516 1482 512 1488 537 473 539 472 519 1479 514 1484 520 1479 515 1485 539 470 542 468 544 466 546 464 517 4493 538 472 540 470 542 468 544 466 546 464 517 1482 543 468 513 544 471 469 574 436 514 1485 519 1480 545 465 547 1452 573 438 543 1456 548 1451 543 1456 569 442 570 # name: Next @@ -2837,7 +2816,7 @@ type: parsed protocol: NEC address: 00 00 00 00 command: 41 00 00 00 -# +# name: Play type: parsed protocol: NECext @@ -2983,72 +2962,6 @@ address: C8 91 00 00 command: 21 DE 00 00 # name: Play -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 4583 4441 575 454 550 455 549 457 548 456 549 1461 550 1460 549 456 549 457 549 1461 548 1462 549 1461 549 1461 548 457 548 457 549 457 547 457 549 4462 548 458 548 457 548 457 548 457 548 457 548 1462 548 457 548 1461 549 457 548 457 548 457 548 1462 548 1461 549 457 548 1461 549 457 548 1461 549 1461 548 1462 547 458 547 55448 4580 4465 550 456 549 456 549 456 550 456 549 1461 549 1460 549 457 549 456 549 1461 549 1461 549 1461 548 1461 549 457 548 457 549 457 548 457 548 4462 549 457 548 457 548 458 548 457 548 457 548 1462 549 457 547 1462 549 457 548 457 548 457 548 1462 548 1462 549 457 548 1462 548 457 548 1462 548 1461 549 1462 548 457 548 -# -name: Pause -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 4583 4441 575 454 550 455 549 457 548 456 549 1461 550 1460 549 456 549 457 549 1461 548 1462 549 1461 549 1461 548 457 548 457 549 457 547 457 549 4462 548 458 548 457 548 457 548 457 548 457 548 1462 548 457 548 1461 549 457 548 457 548 457 548 1462 548 1461 549 457 548 1461 549 457 548 1461 549 1461 548 1462 547 458 547 55448 4580 4465 550 456 549 456 549 456 550 456 549 1461 549 1460 549 457 549 456 549 1461 549 1461 549 1461 548 1461 549 457 548 457 549 457 548 457 548 4462 549 457 548 457 548 458 548 457 548 457 548 1462 549 457 547 1462 549 457 548 457 548 457 548 1462 548 1462 549 457 548 1462 548 457 548 1462 548 1461 549 1462 548 457 548 -# -name: Next -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 4582 4465 551 454 550 455 550 455 549 456 549 1460 550 1460 549 457 548 456 549 1461 549 1461 548 1461 549 1460 549 457 549 456 548 457 548 457 548 4461 549 456 548 457 548 457 548 457 548 457 548 457 548 1462 548 1461 547 457 549 457 548 456 549 1460 549 1461 547 1461 549 457 547 457 548 1461 549 1461 548 1461 548 457 548 55436 4578 4464 549 455 549 456 549 455 549 456 550 1460 549 1460 549 456 550 455 550 1460 550 1460 549 1460 550 1460 549 456 549 457 548 457 548 457 549 4461 548 457 548 457 548 457 548 457 547 457 549 457 547 1462 548 1461 549 457 548 457 548 457 548 1461 548 1461 548 1462 548 457 548 457 549 1461 549 1461 548 1461 548 457 548 -# -name: Prev -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 4554 4466 540 469 547 490 515 495 521 490 494 1478 522 1477 544 466 539 471 513 1512 488 1484 516 1483 570 1429 540 497 519 465 540 470 546 491 493 4490 548 462 543 467 549 488 517 493 491 1481 519 1480 541 495 489 1510 522 462 543 494 521 489 495 1476 545 466 539 471 513 1485 547 464 520 1505 495 1477 513 1487 545 465 540 -# -name: Next -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 4555 4464 542 468 548 462 543 467 549 488 496 1477 523 1476 545 465 551 459 525 1475 515 1484 516 1483 517 1482 550 486 519 465 551 459 546 464 520 4490 547 488 517 467 549 462 543 467 548 462 543 467 517 1482 518 1481 540 496 520 464 541 469 515 1484 516 1483 517 1482 550 460 545 465 519 1480 520 1479 521 1478 543 493 522 -# -name: Play -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 4549 4471 546 464 541 469 547 463 542 469 515 1484 516 1483 549 461 544 466 518 1481 519 1480 520 1479 521 1477 544 466 550 461 544 467 549 461 523 4486 541 469 546 464 541 469 547 464 541 469 515 1483 549 462 522 1477 544 465 540 471 545 465 572 1428 520 1479 542 468 569 1430 549 462 522 1476 514 1486 514 1485 547 463 542 -# -name: Play -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 4552 4464 586 420 584 420 584 420 612 392 529 1478 531 1478 531 476 553 451 553 1455 554 1479 528 1480 528 1481 527 478 526 478 526 478 526 478 526 4497 526 478 526 478 526 478 526 478 526 478 526 1483 526 478 526 1483 526 478 526 478 526 478 526 1483 526 1483 526 478 526 1483 526 479 525 1483 526 1483 526 1483 526 478 526 -# -name: Play -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 4548 4471 540 471 541 468 544 467 545 465 516 1482 512 1488 537 473 539 472 519 1479 514 1484 520 1479 515 1485 539 470 542 468 544 466 546 464 517 4493 538 472 540 470 542 468 544 466 546 464 517 1482 543 468 513 544 471 469 574 436 514 1485 519 1480 545 465 547 1452 573 438 543 1456 548 1451 543 1456 569 442 570 -# -name: Pause -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 4549 4471 546 464 541 469 547 463 542 469 515 1484 516 1483 549 461 544 466 518 1481 519 1480 520 1479 521 1477 544 466 550 461 544 467 549 461 523 4486 541 469 546 464 541 469 547 464 541 469 515 1483 549 462 522 1477 544 465 540 471 545 465 572 1428 520 1479 542 468 569 1430 549 462 522 1476 514 1486 514 1485 547 463 542 -# -name: Pause -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 4552 4464 586 420 584 420 584 420 612 392 529 1478 531 1478 531 476 553 451 553 1455 554 1479 528 1480 528 1481 527 478 526 478 526 478 526 478 526 4497 526 478 526 478 526 478 526 478 526 478 526 1483 526 478 526 1483 526 478 526 478 526 478 526 1483 526 1483 526 478 526 1483 526 479 525 1483 526 1483 526 1483 526 478 526 -# -name: Pause -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 4548 4471 540 471 541 468 544 467 545 465 516 1482 512 1488 537 473 539 472 519 1479 514 1484 520 1479 515 1485 539 470 542 468 544 466 546 464 517 4493 538 472 540 470 542 468 544 466 546 464 517 1482 543 468 513 544 471 469 574 436 514 1485 519 1480 545 465 547 1452 573 438 543 1456 548 1451 543 1456 569 442 570 -# -name: Play type: parsed protocol: SIRC20 address: 10 01 00 00 @@ -3096,8 +3009,6 @@ protocol: NEC address: 20 00 00 00 command: 1B 00 00 00 # -# Speakers -# name: Play type: raw frequency: 38000 @@ -3392,42 +3303,6 @@ protocol: NECext address: 2D D3 00 00 command: 06 F9 00 00 # -name: Play -type: raw -frequency: 38000 -duty_cycle: 0.330000 -data: 1041 1461 540 457 543 453 537 459 541 1459 541 1459 542 1459 541 1459 542 1458 542 1458 543 1458 542 1458 542 453 537 459 541 455 545 451 539 450 540 50518 1041 1461 540 457 543 452 538 459 541 1459 541 1459 542 1459 541 1458 543 1458 542 1458 542 1458 542 1457 544 453 537 459 541 455 545 451 539 449 541 50534 1036 1467 544 452 538 458 542 454 546 1455 545 1454 546 1454 536 1464 537 1464 536 1463 537 1463 537 1463 537 459 541 454 546 450 540 457 543 445 545 -# -name: Pause -type: raw -frequency: 38000 -duty_cycle: 0.330000 -data: 1041 1461 540 457 543 453 537 459 541 1459 541 1459 542 1459 541 1459 542 1458 542 1458 543 1458 542 1458 542 453 537 459 541 455 545 451 539 450 540 50518 1041 1461 540 457 543 452 538 459 541 1459 541 1459 542 1459 541 1458 543 1458 542 1458 542 1458 542 1457 544 453 537 459 541 455 545 451 539 449 541 50534 1036 1467 544 452 538 458 542 454 546 1455 545 1454 546 1454 536 1464 537 1464 536 1463 537 1463 537 1463 537 459 541 454 546 450 540 457 543 445 545 -# -name: Next -type: raw -frequency: 38000 -duty_cycle: 0.330000 -data: 1044 1460 540 456 544 1457 543 1457 543 453 536 1465 545 450 539 457 543 1458 542 1458 542 454 546 451 539 1462 538 458 542 1459 541 1460 540 448 542 50525 1042 1462 538 458 542 1458 542 1459 541 455 545 1456 544 452 537 459 541 1460 540 1460 540 456 544 452 537 1464 536 460 540 1461 539 1461 539 449 540 50542 1046 1458 542 480 520 1455 545 1455 545 477 512 1463 547 474 515 481 519 1456 544 1457 543 478 522 475 514 1460 540 482 518 1457 543 1458 542 445 544 -# -name: Prev -type: raw -frequency: 38000 -duty_cycle: 0.330000 -data: 1046 1457 543 1458 542 454 546 450 539 1461 539 456 544 1458 542 1458 542 454 546 451 539 1461 539 1462 538 458 542 1459 541 454 546 451 539 1454 546 50538 1043 1461 539 1461 539 456 544 453 547 1454 546 449 541 1461 539 1462 538 457 543 453 547 1455 545 1455 545 451 539 1462 538 458 542 454 546 1447 543 50526 1044 1459 541 1459 541 455 545 451 539 1463 537 458 542 1459 541 1460 540 455 545 452 538 1463 537 1464 536 460 540 1461 539 456 544 453 536 1456 544 -# -name: Play -type: raw -frequency: 38000 -duty_cycle: 0.330000 -data: 1013 1488 512 486 514 486 514 1485 515 484 516 484 516 1483 517 1482 518 1481 509 1490 510 1488 512 488 512 1487 513 1485 515 484 516 483 517 481 519 50939 1010 1489 511 488 512 488 512 1486 514 486 514 486 514 1485 515 1483 517 1482 518 1481 509 1489 511 489 511 1487 513 1485 515 484 516 484 516 482 518 50958 1013 1488 512 487 513 487 513 1485 515 485 515 485 515 1484 516 1483 517 1482 518 1481 509 1490 510 490 572 1426 512 1487 513 486 514 486 514 485 566 50908 1011 1490 510 489 511 489 511 1487 513 486 514 487 513 1485 515 1484 516 1483 517 1482 518 1480 510 491 571 1427 511 1488 512 488 512 487 513 486 565 50905 1013 1488 512 488 512 488 512 1486 514 486 514 486 514 1485 515 1484 516 1483 517 1481 509 1489 511 489 511 1488 512 1486 514 485 515 485 515 484 516 -# -name: Pause -type: raw -frequency: 38000 -duty_cycle: 0.330000 -data: 1013 1488 512 486 514 486 514 1485 515 484 516 484 516 1483 517 1482 518 1481 509 1490 510 1488 512 488 512 1487 513 1485 515 484 516 483 517 481 519 50939 1010 1489 511 488 512 488 512 1486 514 486 514 486 514 1485 515 1483 517 1482 518 1481 509 1489 511 489 511 1487 513 1485 515 484 516 484 516 482 518 50958 1013 1488 512 487 513 487 513 1485 515 485 515 485 515 1484 516 1483 517 1482 518 1481 509 1490 510 490 572 1426 512 1487 513 486 514 486 514 485 566 50908 1011 1490 510 489 511 489 511 1487 513 486 514 487 513 1485 515 1484 516 1483 517 1482 518 1480 510 491 571 1427 511 1488 512 488 512 487 513 486 565 50905 1013 1488 512 488 512 488 512 1486 514 486 514 486 514 1485 515 1484 516 1483 517 1481 509 1489 511 489 511 1488 512 1486 514 485 515 485 515 484 516 -# name: Next type: parsed protocol: NEC @@ -3476,50 +3351,332 @@ protocol: NEC address: 02 00 00 00 command: 05 00 00 00 # -name: Next -type: raw -frequency: 38000 -duty_cycle: 0.330000 -data: 3303 1908 408 1179 410 1177 412 442 409 444 407 446 405 448 414 1173 405 448 414 1173 416 1172 406 447 415 439 412 1175 414 440 411 1175 414 440 411 1175 414 440 411 442 409 444 407 447 415 438 413 440 411 442 409 444 407 446 405 1182 407 447 415 438 413 440 411 442 409 444 407 1180 409 1178 411 443 408 445 406 1180 409 445 406 447 415 438 413 440 411 442 409 444 407 1180 409 444 407 446 405 448 414 440 411 41789 3301 3346 362 42926 3307 3341 357 -# -name: Prev -type: raw -frequency: 38000 -duty_cycle: 0.330000 -data: 3301 1910 406 1155 434 1180 409 419 432 421 441 439 412 441 410 1178 411 416 435 1152 437 1177 412 442 409 417 434 1180 409 418 433 1180 409 445 406 1155 434 419 432 447 415 413 438 442 409 444 407 419 432 448 414 439 412 442 409 1177 412 442 409 444 407 420 431 448 414 413 438 1176 413 1174 415 413 438 1175 414 1173 405 422 440 440 411 416 435 418 433 447 415 439 412 441 410 1177 412 415 436 417 434 446 405 41164 3301 3321 387 42935 3307 3316 381 42941 3311 3312 385 -# name: Play type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 3309 1901 415 1173 405 1182 407 420 442 412 439 441 410 443 408 1179 410 444 407 1180 409 1179 410 444 407 420 431 1183 406 447 415 1147 442 412 439 1148 441 439 412 441 410 443 408 446 405 448 414 440 411 415 436 444 407 446 405 1182 407 447 415 412 439 440 411 443 408 418 433 1181 408 1180 409 418 433 447 415 439 412 441 410 443 408 445 406 1181 408 1180 409 1178 411 417 434 445 406 421 441 439 412 415 436 41161 3303 3320 388 42939 3301 3347 361 # name: Pause type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 3309 1901 415 1173 405 1182 407 420 442 412 439 441 410 443 408 1179 410 444 407 1180 409 1179 410 444 407 420 431 1183 406 447 415 1147 442 412 439 1148 441 439 412 441 410 443 408 446 405 448 414 440 411 415 436 444 407 446 405 1182 407 447 415 412 439 440 411 443 408 418 433 1181 408 1180 409 418 433 447 415 439 412 441 410 443 408 445 406 1181 408 1180 409 1178 411 417 434 445 406 421 441 439 412 415 436 41161 3303 3320 388 42939 3301 3347 361 # -name: Play -type: raw -frequency: 38000 -duty_cycle: 0.330000 -data: 9175 4433 644 1605 645 442 669 466 646 445 666 438 673 466 645 440 671 466 673 410 700 365 720 465 675 367 742 367 744 438 645 1604 675 1574 674 1549 702 1575 673 1576 646 1604 675 1574 676 1574 675 1576 644 1604 645 1606 672 1576 645 466 645 466 645 465 646 1575 675 1605 645 466 645 467 644 467 644 1605 644 1605 645 1606 644 467 644 467 644 1606 643 1578 672 1607 643 -# -name: Next -type: raw -frequency: 38000 -duty_cycle: 0.330000 -data: 9146 4435 642 1577 673 449 662 454 658 451 661 469 642 468 644 469 642 453 659 470 642 470 642 469 643 469 642 469 642 469 642 1608 642 1606 643 1607 642 1607 642 1607 642 1577 673 1607 643 1607 642 1607 642 1607 642 1606 643 1576 674 1606 643 468 643 439 672 1607 642 1607 642 448 663 469 642 439 672 469 642 1577 672 1607 642 468 643 469 642 1607 642 1608 641 1608 640 -# -name: Prev -type: raw -frequency: 38000 -duty_cycle: 0.330000 -data: 9144 4405 671 1607 642 469 642 469 642 469 642 470 641 365 747 469 641 443 669 469 642 468 643 446 665 469 642 469 642 470 642 1580 669 1607 643 1607 642 1608 641 1578 672 1580 670 1608 641 1608 641 1609 590 1660 590 1635 615 1660 590 1660 590 1632 618 1633 617 521 590 1660 590 521 590 521 618 493 590 521 590 520 591 519 592 1657 592 519 592 1658 617 1633 616 1633 615 23844 9114 4461 615 -# name: Next type: parsed protocol: NEC address: FD 00 00 00 command: EA 00 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 45 00 00 00 +# +name: Power +type: parsed +protocol: NEC42 +address: 51 00 00 00 +command: 00 00 00 00 +# +name: Vol_up +type: parsed +protocol: NEC42 +address: 51 00 00 00 +command: 1F 00 00 00 +# +name: Vol_dn +type: parsed +protocol: NEC42 +address: 51 00 00 00 +command: 1E 00 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 10 E7 00 00 +command: 00 FF 00 00 +# +name: Mute +type: parsed +protocol: NECext +address: 10 E7 00 00 +command: 01 FE 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 00 79 00 00 +command: 80 7F 00 00 +# +name: Mute +type: parsed +protocol: NECext +address: 00 79 00 00 +command: 99 66 00 00 +# +name: Power +type: parsed +protocol: NECext +address: D2 6C 00 00 +command: 47 B8 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 55 00 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 40 00 00 00 +command: 02 00 00 00 +# +name: Vol_up +type: parsed +protocol: NEC +address: 40 00 00 00 +command: 01 00 00 00 +# +name: Vol_dn +type: parsed +protocol: NEC +address: 40 00 00 00 +command: 12 00 00 00 +# +name: Power +type: parsed +protocol: RC5X +address: 1B 00 00 00 +command: 0C 00 00 00 +# +name: Mute +type: parsed +protocol: RC5X +address: 1B 00 00 00 +command: 0D 00 00 00 +# +name: Mute +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 01 00 00 00 +# +name: Pause +type: parsed +protocol: Kaseikyo +address: AC 02 20 00 +command: 61 00 00 00 +# +name: Vol_up +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 8560 4222 563 1558 563 511 562 1557 565 537 536 512 561 1558 564 488 585 1558 564 486 587 1559 563 486 587 1558 564 1557 565 509 565 1559 563 510 564 485 588 1557 565 487 586 1557 565 486 587 511 536 514 560 485 588 1557 565 487 586 1559 563 486 587 1558 564 1558 563 1585 562 1557 565 25407 8561 4220 564 1557 564 511 563 1558 563 508 566 485 588 1558 564 512 561 1557 565 486 587 1557 565 487 586 1558 564 1559 562 536 537 1558 564 508 565 487 586 1585 536 485 589 1558 564 486 587 485 563 511 562 487 586 1558 564 488 585 1557 565 485 588 1557 565 1558 564 1586 561 1556 566 25433 8534 4220 565 1584 537 536 538 1585 537 508 565 511 562 1584 538 511 562 1585 537 511 562 1584 538 511 562 1584 537 1584 538 536 537 1585 537 536 538 511 562 1584 538 510 563 1584 537 510 564 510 537 506 568 483 590 1584 538 511 562 1585 537 510 563 1584 538 1557 565 1585 562 1584 538 +# +name: Vol_dn +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 8562 4223 564 1557 591 509 538 1560 588 484 563 508 566 1558 563 508 566 1555 566 510 564 1556 565 536 538 1556 565 1558 590 483 564 1557 591 510 537 1559 589 1583 539 483 590 1555 567 510 563 482 565 536 538 484 589 509 538 510 565 1556 565 536 537 1585 537 1558 590 1583 539 1559 562 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 8571 4203 588 509 539 1561 589 510 538 536 539 510 565 1555 569 511 699 1449 539 1585 539 536 539 1558 566 1562 588 1585 539 511 563 1556 703 350 590 482 567 535 540 1560 564 1559 591 1584 540 485 590 510 539 507 568 1558 566 1586 564 510 539 508 567 511 563 1585 539 1585 539 1559 591 25433 8571 4228 563 510 539 1586 564 510 539 509 565 511 564 1558 566 486 589 1585 539 1585 539 536 539 1585 539 1586 621 1528 596 455 620 1501 623 454 620 453 674 400 596 1528 674 1425 647 1528 655 394 681 393 673 402 656 1468 672 1453 681 393 671 404 655 395 679 1469 655 1470 668 1457 678 +# +name: Mute +type: parsed +protocol: NECext +address: 83 22 00 00 +command: 0C F3 00 00 +# +name: Vol_dn +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 1E 00 00 00 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 9028 3517 173 801 573 559 573 559 573 557 575 583 539 565 567 1676 568 590 542 563 569 1674 570 1675 569 1676 568 1677 567 1678 576 556 566 1679 575 1669 575 557 575 556 566 1678 576 1696 548 557 575 556 566 565 567 564 568 1675 569 1676 568 564 568 563 569 1674 570 1702 542 1703 541 1678 576 40469 9030 2232 568 96806 9035 2229 571 96799 9033 2230 570 +# +name: Power +type: parsed +protocol: NEC +address: 80 00 00 00 +command: 1E 00 00 00 +# +name: Pause +type: parsed +protocol: Samsung32 +address: 2C 00 00 00 +command: 4F 00 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 83 B6 00 00 +command: 4D B2 00 00 +# +name: Mute +type: parsed +protocol: NECext +address: 83 B6 00 00 +command: 43 BC 00 00 +# +name: Play +type: parsed +protocol: NECext +address: C8 91 00 00 +command: 21 DE 00 00 +# +name: Pause +type: parsed +protocol: NECext +address: 86 FF 00 00 +command: 2A D5 00 00 +# +name: Mute +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 45 00 00 00 +# +name: Vol_up +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 16 00 00 00 +# +name: Pause +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 15 00 00 00 +# +name: Vol_dn +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 03 00 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 00 00 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 48 00 00 00 +# +name: Mute +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 1D 00 00 00 +# +name: Play +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 1F 00 00 00 +# +name: Pause +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 1F 00 00 00 +# +name: Vol_dn +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 11 00 00 00 +# +name: Vol_up +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 15 00 00 00 +# +name: Mute +type: parsed +protocol: NEC +address: 01 00 00 00 +command: 02 00 00 00 +# +name: Vol_up +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 09 00 00 00 +# +name: Pause +type: parsed +protocol: SIRC +address: 10 00 00 00 +command: 0C 00 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 0C 00 00 00 +# +name: Vol_up +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 42 00 00 00 +# +name: Vol_dn +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 43 00 00 00 +# +name: Next +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 40 00 00 00 +# +name: Play +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 44 00 00 00 +# +name: Pause +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 44 00 00 00 +# +name: Power +type: parsed +protocol: SIRC15 +address: 30 00 00 00 +command: 2F 00 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 80 00 00 00 +command: 05 00 00 00 +# +name: Vol_up +type: parsed +protocol: NEC +address: 80 00 00 00 +command: 02 00 00 00 diff --git a/assets/resources/infrared/assets/fans.ir b/assets/resources/infrared/assets/fans.ir index 734de79e0f..185573ecec 100644 --- a/assets/resources/infrared/assets/fans.ir +++ b/assets/resources/infrared/assets/fans.ir @@ -1,7 +1,7 @@ Filetype: IR library file Version: 1 -# Last Updated 17th May, 2023 -# Last Checked 17th May, 2023 +# Last Updated 1st Jul, 2023 +# Last Checked 1st Jul, 2023 # name: Power type: raw @@ -1533,3 +1533,303 @@ 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 diff --git a/assets/resources/infrared/assets/projectors.ir b/assets/resources/infrared/assets/projectors.ir index 673120e616..7df0fe127e 100644 --- a/assets/resources/infrared/assets/projectors.ir +++ b/assets/resources/infrared/assets/projectors.ir @@ -1,7 +1,7 @@ Filetype: IR library file Version: 1 -# Last Updated 2nd May, 2023 -# Last Checked 17th May, 2023 +# Last Updated 16th Jun, 2023 +# Last Checked 1st Jul, 2023 # # ON name: Power @@ -910,3 +910,81 @@ type: parsed protocol: NEC address: 02 00 00 00 command: 00 00 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 04 B1 00 00 +command: 58 A7 00 00 +# +name: Mute +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 9107 4376 681 1573 681 472 655 472 654 474 652 475 652 476 651 476 652 476 651 476 651 1604 651 1604 651 1604 651 1603 652 1604 651 1604 651 1604 651 1604 650 476 651 477 650 477 650 476 651 477 651 1604 650 476 651 477 650 1604 651 1604 650 1604 651 1604 650 1604 651 477 650 1604 650 39498 9079 2178 651 +# +name: Power +type: parsed +protocol: NECext +address: 8B CA 00 00 +command: 12 ED 00 00 +# +name: Vol_up +type: parsed +protocol: NECext +address: 8B CA 00 00 +command: 44 BB 00 00 +# +name: Vol_dn +type: parsed +protocol: NECext +address: 8B CA 00 00 +command: 46 B9 00 00 +# +name: Mute +type: parsed +protocol: NECext +address: 8B CA 00 00 +command: 11 EE 00 00 +# +name: Vol_dn +type: parsed +protocol: NECext +address: 81 03 00 00 +command: F0 0F 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 01 00 00 00 +command: 40 00 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 00 BD 00 00 +command: 01 FE 00 00 +# +name: Vol_dn +type: parsed +protocol: NECext +address: 00 BD 00 00 +command: 10 EF 00 00 +# +name: Vol_up +type: parsed +protocol: NECext +address: 00 BD 00 00 +command: 0C F3 00 00 +# +name: Mute +type: parsed +protocol: NECext +address: 00 BD 00 00 +command: 6A 95 00 00 +# +name: Vol_up +type: parsed +protocol: NEC +address: 02 00 00 00 +command: 11 00 00 00 diff --git a/assets/resources/infrared/assets/tv.ir b/assets/resources/infrared/assets/tv.ir index ab608cf1bc..32ddfded21 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 17th May, 2023 -# Last Checked 17th May, 2023 +# Last Updated 1st Jul, 2023 +# Last Checked 1st Jul, 2023 # name: Power type: parsed @@ -63,12 +63,6 @@ protocol: NEC address: 40 00 00 00 command: 13 00 00 00 # -name: Vol_dn -type: parsed -protocol: NEC -address: 40 00 00 00 -command: 12 00 00 00 -# name: Mute type: parsed protocol: NEC @@ -453,6 +447,12 @@ type: parsed protocol: Samsung32 address: 0E 00 00 00 command: 15 00 00 00 +# +name: Vol_dn +type: parsed +protocol: NEC +address: 40 00 00 00 +command: 12 00 00 00 # name: Ch_next type: parsed @@ -1143,13 +1143,17 @@ type: parsed protocol: RC6 address: 00 00 00 00 command: 0D 00 00 00 +# # Samsung Standby +# name: Power type: parsed protocol: Samsung32 address: 07 00 00 00 command: E0 00 00 00 +# # Samsung Power Off +# name: Power type: parsed protocol: Samsung32 @@ -2047,3 +2051,227 @@ type: parsed protocol: Samsung32 address: 3E 00 00 00 command: 0D 00 00 00 +# +# TCL LED49D2930 / Thomson Remote RC3000E02 +# +name: Power +type: parsed +protocol: RCA +address: 0F 00 00 00 +command: 54 00 00 00 +# +name: Mute +type: parsed +protocol: RCA +address: 0F 00 00 00 +command: FC 00 00 00 +# +name: Ch_next +type: parsed +protocol: RCA +address: 0F 00 00 00 +command: B4 00 00 00 +# +name: Ch_prev +type: parsed +protocol: RCA +address: 0F 00 00 00 +command: 34 00 00 00 +# +name: Vol_up +type: parsed +protocol: RCA +address: 0F 00 00 00 +command: F4 00 00 00 +# +name: Vol_dn +type: parsed +protocol: RCA +address: 0F 00 00 00 +command: 74 00 00 00 +# +name: Vol_up +type: parsed +protocol: NEC +address: 01 00 00 00 +command: 18 00 00 00 +# +name: Ch_prev +type: parsed +protocol: NEC +address: 08 00 00 00 +command: DD 00 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 04 F4 00 00 +command: 08 F7 00 00 +# +name: Vol_up +type: parsed +protocol: NECext +address: 04 F4 00 00 +command: 02 FD 00 00 +# +name: Vol_dn +type: parsed +protocol: NECext +address: 04 F4 00 00 +command: 03 FC 00 00 +# +name: Mute +type: parsed +protocol: NECext +address: 04 F4 00 00 +command: 09 F6 00 00 +# +name: Ch_prev +type: parsed +protocol: Samsung32 +address: 07 00 00 00 +command: 13 00 00 00 +# +name: Vol_up +type: parsed +protocol: SIRC +address: 10 00 00 00 +command: 12 00 00 00 +# +name: Vol_dn +type: parsed +protocol: SIRC +address: 10 00 00 00 +command: 13 00 00 00 +# +name: Mute +type: parsed +protocol: SIRC +address: 10 00 00 00 +command: 14 00 00 00 +# +name: Ch_next +type: parsed +protocol: SIRC +address: 0D 00 00 00 +command: 10 00 00 00 +# +name: Ch_prev +type: parsed +protocol: SIRC +address: 0D 00 00 00 +command: 11 00 00 00 +# +name: Mute +type: parsed +protocol: NEC +address: 01 00 00 00 +command: 11 00 00 00 +# +name: Ch_prev +type: parsed +protocol: RC5 +address: 01 00 00 00 +command: 22 00 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 40 00 00 00 +command: 12 00 00 00 +# +name: Vol_dn +type: parsed +protocol: NEC +address: 01 00 00 00 +command: 10 00 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 00 7F 00 00 +command: 1E E1 00 00 +# +name: Mute +type: parsed +protocol: NECext +address: 00 7F 00 00 +command: 5C A3 00 00 +# +name: Vol_up +type: parsed +protocol: NEC +address: 20 00 00 00 +command: 02 00 00 00 +# +name: Vol_dn +type: parsed +protocol: NEC +address: 20 00 00 00 +command: 09 00 00 00 +# +name: Ch_next +type: parsed +protocol: NEC +address: 20 00 00 00 +command: 03 00 00 00 +# +name: Ch_prev +type: parsed +protocol: NEC +address: 20 00 00 00 +command: 41 00 00 00 +# +name: Vol_up +type: parsed +protocol: NECext +address: 02 7D 00 00 +command: 0F F0 00 00 +# +name: Vol_dn +type: parsed +protocol: NECext +address: 02 7D 00 00 +command: 5A A5 00 00 +# +name: Ch_next +type: parsed +protocol: NECext +address: 02 7D 00 00 +command: 0C F3 00 00 +# +name: Ch_prev +type: parsed +protocol: NECext +address: 02 7D 00 00 +command: 19 E6 00 00 +# +name: Mute +type: parsed +protocol: NEC +address: 40 00 00 00 +command: 10 00 00 00 +# +name: Ch_next +type: parsed +protocol: Samsung32 +address: 07 00 00 00 +command: 10 00 00 00 +# +name: Ch_prev +type: parsed +protocol: Samsung32 +address: 07 00 00 00 +command: 12 00 00 00 +# +name: Power +type: parsed +protocol: NEC +address: A0 00 00 00 +command: 1C 00 00 00 +# +name: Mute +type: parsed +protocol: NEC +address: A0 00 00 00 +command: 5F 00 00 00 diff --git a/assets/resources/lfrfid/rfidfuzzer/example_uids_em4100.txt b/assets/resources/lfrfid_fuzzer/example_uids_em4100.txt similarity index 100% rename from assets/resources/lfrfid/rfidfuzzer/example_uids_em4100.txt rename to assets/resources/lfrfid_fuzzer/example_uids_em4100.txt diff --git a/assets/resources/lfrfid/rfidfuzzer/example_uids_h10301.txt b/assets/resources/lfrfid_fuzzer/example_uids_h10301.txt similarity index 100% rename from assets/resources/lfrfid/rfidfuzzer/example_uids_h10301.txt rename to assets/resources/lfrfid_fuzzer/example_uids_h10301.txt diff --git a/assets/resources/lfrfid/rfidfuzzer/example_uids_hidprox.txt b/assets/resources/lfrfid_fuzzer/example_uids_hidprox.txt similarity index 100% rename from assets/resources/lfrfid/rfidfuzzer/example_uids_hidprox.txt rename to assets/resources/lfrfid_fuzzer/example_uids_hidprox.txt diff --git a/assets/resources/lfrfid/rfidfuzzer/example_uids_pac.txt b/assets/resources/lfrfid_fuzzer/example_uids_pac.txt similarity index 100% rename from assets/resources/lfrfid/rfidfuzzer/example_uids_pac.txt rename to assets/resources/lfrfid_fuzzer/example_uids_pac.txt diff --git a/assets/resources/mifare_fuzzer/example_uids04.txt b/assets/resources/mifare_fuzzer/example_uids04.txt new file mode 100644 index 0000000000..0bd1b29967 --- /dev/null +++ b/assets/resources/mifare_fuzzer/example_uids04.txt @@ -0,0 +1,9 @@ +# One UID per line +# Keep an empty line at the end +01020304 +02030405 +03040506 +04050607 +05060708 +06070809 + diff --git a/assets/resources/mifare_fuzzer/example_uids07.txt b/assets/resources/mifare_fuzzer/example_uids07.txt new file mode 100644 index 0000000000..fc53dcdea1 --- /dev/null +++ b/assets/resources/mifare_fuzzer/example_uids07.txt @@ -0,0 +1,10 @@ +# One UID per line +# Keep an empty line at the end +01020304050607 +02030405060701 +03040506070102 +04050607010203 +05060701020304 +06070102030405 +07010203040506 + diff --git a/fbt_options.py b/fbt_options.py index 7c5857e09f..2cd82501d7 100644 --- a/fbt_options.py +++ b/fbt_options.py @@ -1,5 +1,6 @@ from pathlib import Path import posixpath +import datetime # For more details on these options, run 'fbt -h' @@ -16,13 +17,15 @@ # Suffix to add to files when building distribution # If OS environment has DIST_SUFFIX set, it will be used instead -DIST_SUFFIX = "XFW-0048_03062023" + +# How about we add the timestamp automatically. Solves some problems +DIST_SUFFIX = f"XFW-0049_{datetime.datetime.today().strftime('%d%m%Y')}" # Coprocessor firmware COPRO_OB_DATA = "scripts/ob.data" # Must match lib/stm32wb_copro version -COPRO_CUBE_VERSION = "1.13.3" +COPRO_CUBE_VERSION = "1.15.0" COPRO_CUBE_DIR = "lib/stm32wb_copro" diff --git a/firmware/targets/f18/api_symbols.csv b/firmware/targets/f18/api_symbols.csv index 3c075e0d15..a689d5a21b 100644 --- a/firmware/targets/f18/api_symbols.csv +++ b/firmware/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,28.2,, +Version,+,31.3,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -128,6 +128,7 @@ Header,+,lib/mlib/m-list.h,, Header,+,lib/mlib/m-rbtree.h,, Header,+,lib/mlib/m-tuple.h,, Header,+,lib/mlib/m-variant.h,, +Header,+,lib/music_worker/music_worker.h,, Header,+,lib/one_wire/maxim_crc.h,, Header,+,lib/one_wire/one_wire_host.h,, Header,+,lib/one_wire/one_wire_slave.h,, @@ -288,6 +289,8 @@ Function,+,__clear_cache,void,"void*, void*" Function,-,__eprintf,void,"const char*, const char*, unsigned int, const char*" Function,+,__errno,int*, Function,+,__furi_crash,void, +Function,+,__furi_critical_enter,__FuriCriticalInfo, +Function,+,__furi_critical_exit,void,__FuriCriticalInfo Function,+,__furi_halt,void, Function,-,__getdelim,ssize_t,"char**, size_t*, int, FILE*" Function,-,__getline,ssize_t,"char**, size_t*, FILE*" @@ -646,7 +649,7 @@ Function,+,dir_walk_read,DirWalkResult,"DirWalk*, FuriString*, FileInfo*" Function,+,dir_walk_set_filter_cb,void,"DirWalk*, DirWalkFilterCb, void*" Function,+,dir_walk_set_recursive,void,"DirWalk*, _Bool" Function,-,div,div_t,"int, int" -Function,+,dolphin_deed,void,"Dolphin*, DolphinDeed" +Function,+,dolphin_deed,void,DolphinDeed Function,+,dolphin_deed_get_app,DolphinApp,DolphinDeed Function,+,dolphin_deed_get_app_limit,uint8_t,DolphinApp Function,+,dolphin_deed_get_weight,uint8_t,DolphinDeed @@ -677,7 +680,8 @@ Function,+,elements_slightly_rounded_box,void,"Canvas*, uint8_t, uint8_t, uint8_ Function,+,elements_slightly_rounded_frame,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t" Function,+,elements_string_fit_width,void,"Canvas*, FuriString*, uint8_t" Function,+,elements_text_box,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, Align, Align, const char*, _Bool" -Function,+,elf_resolve_from_hashtable,_Bool,"const ElfApiInterface*, const char*, Elf32_Addr*" +Function,+,elf_resolve_from_hashtable,_Bool,"const ElfApiInterface*, uint32_t, Elf32_Addr*" +Function,+,elf_symbolname_hash,uint32_t,const char* Function,+,empty_screen_alloc,EmptyScreen*, Function,+,empty_screen_free,void,EmptyScreen* Function,+,empty_screen_get_view,View*,EmptyScreen* @@ -733,9 +737,11 @@ Function,+,filesystem_api_error_get_desc,const char*,FS_Error Function,-,fiprintf,int,"FILE*, const char*, ..." Function,-,fiscanf,int,"FILE*, const char*, ..." Function,+,flipper_application_alloc,FlipperApplication*,"Storage*, const ElfApiInterface*" +Function,+,flipper_application_alloc_thread,FuriThread*,"FlipperApplication*, const char*" Function,+,flipper_application_free,void,FlipperApplication* Function,+,flipper_application_get_manifest,const FlipperApplicationManifest*,FlipperApplication* Function,+,flipper_application_is_plugin,_Bool,FlipperApplication* +Function,+,flipper_application_load_name_and_icon,_Bool,"FuriString*, Storage*, uint8_t**, FuriString*" Function,+,flipper_application_load_status_to_string,const char*,FlipperApplicationLoadStatus Function,+,flipper_application_manifest_is_compatible,_Bool,"const FlipperApplicationManifest*, const ElfApiInterface*" Function,+,flipper_application_manifest_is_target_compatible,_Bool,const FlipperApplicationManifest* @@ -745,7 +751,6 @@ Function,+,flipper_application_plugin_get_descriptor,const FlipperAppPluginDescr Function,+,flipper_application_preload,FlipperApplicationPreloadStatus,"FlipperApplication*, const char*" Function,+,flipper_application_preload_manifest,FlipperApplicationPreloadStatus,"FlipperApplication*, const char*" Function,+,flipper_application_preload_status_to_string,const char*,FlipperApplicationPreloadStatus -Function,+,flipper_application_spawn,FuriThread*,"FlipperApplication*, void*" Function,+,flipper_format_buffered_file_alloc,FlipperFormat*,Storage* Function,+,flipper_format_buffered_file_close,_Bool,FlipperFormat* Function,+,flipper_format_buffered_file_open_always,_Bool,"FlipperFormat*, const char*" @@ -1057,6 +1062,7 @@ Function,+,furi_hal_power_sleep,void, Function,+,furi_hal_power_sleep_available,_Bool, Function,+,furi_hal_power_suppress_charge_enter,void, Function,+,furi_hal_power_suppress_charge_exit,void, +Function,+,furi_hal_pwm_is_running,_Bool,FuriHalPwmOutputId Function,+,furi_hal_pwm_set_params,void,"FuriHalPwmOutputId, uint32_t, uint8_t" Function,+,furi_hal_pwm_start,void,"FuriHalPwmOutputId, uint32_t, uint8_t" Function,+,furi_hal_pwm_stop,void,FuriHalPwmOutputId @@ -1417,7 +1423,8 @@ Function,+,loader_get_pubsub,FuriPubSub*,Loader* Function,+,loader_is_locked,_Bool,Loader* Function,+,loader_lock,_Bool,Loader* Function,+,loader_show_menu,void,Loader* -Function,+,loader_start,LoaderStatus,"Loader*, const char*, const char*" +Function,+,loader_start,LoaderStatus,"Loader*, const char*, const char*, FuriString*" +Function,+,loader_start_with_gui_error,LoaderStatus,"Loader*, const char*, const char*" Function,+,loader_unlock,void,Loader* Function,+,loading_alloc,Loading*, Function,+,loading_free,void,Loading* @@ -1513,6 +1520,18 @@ Function,-,mkstemps,int,"char*, int" Function,-,mktemp,char*,char* Function,-,mktime,time_t,tm* Function,-,mrand48,long, +Function,-,music_worker_alloc,MusicWorker*, +Function,-,music_worker_clear,void,MusicWorker* +Function,-,music_worker_free,void,MusicWorker* +Function,-,music_worker_is_playing,_Bool,MusicWorker* +Function,-,music_worker_load,_Bool,"MusicWorker*, const char*" +Function,-,music_worker_load_fmf_from_file,_Bool,"MusicWorker*, const char*" +Function,-,music_worker_load_rtttl_from_file,_Bool,"MusicWorker*, const char*" +Function,-,music_worker_load_rtttl_from_string,_Bool,"MusicWorker*, const char*" +Function,-,music_worker_set_callback,void,"MusicWorker*, MusicWorkerCallback, void*" +Function,-,music_worker_set_volume,void,"MusicWorker*, float" +Function,-,music_worker_start,void,MusicWorker* +Function,-,music_worker_stop,void,MusicWorker* Function,+,notification_internal_message,void,"NotificationApp*, const NotificationSequence*" Function,+,notification_internal_message_block,void,"NotificationApp*, const NotificationSequence*" Function,+,notification_message,void,"NotificationApp*, const NotificationSequence*" @@ -1999,6 +2018,7 @@ Function,+,validator_is_file_callback,_Bool,"const char*, FuriString*, void*" Function,+,validator_is_file_free,void,ValidatorIsFile* Function,+,value_index_bool,uint8_t,"const _Bool, const _Bool[], uint8_t" Function,+,value_index_float,uint8_t,"const float, const float[], uint8_t" +Function,+,value_index_int32,uint8_t,"const int32_t, const int32_t[], uint8_t" Function,+,value_index_uint32,uint8_t,"const uint32_t, const uint32_t[], uint8_t" Function,+,variable_item_get_context,void*,VariableItem* Function,+,variable_item_get_current_value_index,uint8_t,VariableItem* @@ -2259,6 +2279,7 @@ Variable,+,message_force_vibro_setting_off,const NotificationMessage, Variable,+,message_force_vibro_setting_on,const NotificationMessage, Variable,+,message_green_0,const NotificationMessage, Variable,+,message_green_255,const NotificationMessage, +Variable,+,message_lcd_contrast_update,const NotificationMessage, Variable,+,message_note_a0,const NotificationMessage, Variable,+,message_note_a1,const NotificationMessage, Variable,+,message_note_a2,const NotificationMessage, @@ -2402,6 +2423,7 @@ Variable,+,sequence_display_backlight_off_delay_1000,const NotificationSequence, Variable,+,sequence_display_backlight_on,const NotificationSequence, Variable,+,sequence_double_vibro,const NotificationSequence, Variable,+,sequence_error,const NotificationSequence, +Variable,+,sequence_lcd_contrast_update,const NotificationSequence, Variable,+,sequence_not_charging,const NotificationSequence, Variable,+,sequence_reset_blue,const NotificationSequence, Variable,+,sequence_reset_display,const NotificationSequence, diff --git a/firmware/targets/f18/target.json b/firmware/targets/f18/target.json index 14d395d222..2d14813f6d 100644 --- a/firmware/targets/f18/target.json +++ b/firmware/targets/f18/target.json @@ -25,6 +25,7 @@ "appframe", "assets", "one_wire", + "music_worker", "misc", "flipper_application", "flipperformat", diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 863cbcb6fb..88d52d6df2 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,7 +1,6 @@ entry,status,name,type,params -Version,+,28.2,, +Version,+,31.3,, Header,+,applications/main/archive/helpers/favorite_timeout.h,, -Header,+,applications/main/fap_loader/fap_loader_app.h,, Header,+,applications/main/subghz/helpers/subghz_txrx.h,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, @@ -40,6 +39,7 @@ Header,+,applications/services/notification/notification_messages.h,, Header,+,applications/services/power/power_service/power.h,, Header,+,applications/services/rpc/rpc_app.h,, Header,+,applications/services/storage/storage.h,, +Header,+,build/icons/assets_icons.h,, Header,+,firmware/targets/f7/furi_hal/furi_hal_bus.h,, Header,+,firmware/targets/f7/furi_hal/furi_hal_clock.h,, Header,+,firmware/targets/f7/furi_hal/furi_hal_console.h,, @@ -151,6 +151,7 @@ Header,+,lib/mlib/m-list.h,, Header,+,lib/mlib/m-rbtree.h,, Header,+,lib/mlib/m-tuple.h,, Header,+,lib/mlib/m-variant.h,, +Header,+,lib/music_worker/music_worker.h,, Header,+,lib/nfc/nfc_device.h,, Header,+,lib/nfc/protocols/nfc_util.h,, Header,+,lib/one_wire/maxim_crc.h,, @@ -192,10 +193,10 @@ Header,+,lib/subghz/blocks/encoder.h,, Header,+,lib/subghz/blocks/generic.h,, Header,+,lib/subghz/blocks/math.h,, Header,+,lib/subghz/environment.h,, -Header,+,lib/subghz/protocols/protocol_items.h,, Header,+,lib/subghz/protocols/raw.h,, Header,+,lib/subghz/receiver.h,, Header,+,lib/subghz/registry.h,, +Header,+,lib/subghz/subghz_protocol_registry.h,, Header,+,lib/subghz/subghz_setting.h,, Header,+,lib/subghz/subghz_tx_rx_worker.h,, Header,+,lib/subghz/subghz_worker.h,, @@ -337,6 +338,8 @@ Function,+,__errno,int*, Function,-,__fpclassifyd,int,double Function,-,__fpclassifyf,int,float Function,+,__furi_crash,void, +Function,+,__furi_critical_enter,__FuriCriticalInfo, +Function,+,__furi_critical_exit,void,__FuriCriticalInfo Function,+,__furi_halt,void, Function,-,__getdelim,ssize_t,"char**, size_t*, int, FILE*" Function,-,__getline,ssize_t,"char**, size_t*, FILE*" @@ -558,8 +561,6 @@ Function,-,atoff,float,const char* Function,+,atoi,int,const char* Function,-,atol,long,const char* Function,-,atoll,long long,const char* -Function,-,atomo_decrypt,void,uint8_t* -Function,-,atomo_encrypt,void,uint8_t* Function,-,basename,char*,const char* Function,-,bcmp,int,"const void*, const void*, size_t" Function,-,bcopy,void,"const void*, void*, size_t" @@ -798,9 +799,10 @@ Function,+,dir_walk_get_error,FS_Error,DirWalk* Function,+,dir_walk_open,_Bool,"DirWalk*, const char*" Function,+,dir_walk_read,DirWalkResult,"DirWalk*, FuriString*, FileInfo*" Function,+,dir_walk_set_filter_cb,void,"DirWalk*, DirWalkFilterCb, void*" +Function,+,dir_walk_set_recurse_filter,void,"DirWalk*, const char**, size_t" Function,+,dir_walk_set_recursive,void,"DirWalk*, _Bool" Function,-,div,div_t,"int, int" -Function,+,dolphin_deed,void,"Dolphin*, DolphinDeed" +Function,+,dolphin_deed,void,DolphinDeed Function,+,dolphin_deed_get_app,DolphinApp,DolphinDeed Function,+,dolphin_deed_get_app_limit,uint8_t,DolphinApp Function,+,dolphin_deed_get_weight,uint8_t,DolphinDeed @@ -848,7 +850,8 @@ Function,+,elements_slightly_rounded_box,void,"Canvas*, uint8_t, uint8_t, uint8_ Function,+,elements_slightly_rounded_frame,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t" Function,+,elements_string_fit_width,void,"Canvas*, FuriString*, uint8_t" Function,+,elements_text_box,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, Align, Align, const char*, _Bool" -Function,+,elf_resolve_from_hashtable,_Bool,"const ElfApiInterface*, const char*, Elf32_Addr*" +Function,+,elf_resolve_from_hashtable,_Bool,"const ElfApiInterface*, uint32_t, Elf32_Addr*" +Function,+,elf_symbolname_hash,uint32_t,const char* Function,+,empty_screen_alloc,EmptyScreen*, Function,+,empty_screen_free,void,EmptyScreen* Function,+,empty_screen_get_view,View*,EmptyScreen* @@ -877,7 +880,6 @@ Function,-,expm1l,long double,long double Function,-,fabs,double,double Function,-,fabsf,float,float Function,-,fabsl,long double,long double -Function,+,fap_loader_load_name_and_icon,_Bool,"FuriString*, Storage*, uint8_t**, FuriString*" Function,+,favorite_timeout_callback,void,void* Function,+,favorite_timeout_run,void,"ViewDispatcher*, SceneManager*" Function,-,fclose,int,FILE* @@ -893,8 +895,8 @@ Function,-,felica_estimate_timing_us,uint_least32_t,"uint_least8_t, uint_least8_ Function,-,felica_get_ic_type,FelicaICType,uint8_t* Function,-,felica_get_service_name,FuriString*,FelicaService* Function,-,felica_get_system_name,FuriString*,FelicaSystem* -Function,-,felica_lite_can_read_without_mac,_Bool,"uint8_t*, uint8_t" Function,-,felica_lite_dump_data,_Bool,"FuriHalNfcTxRxContext*, FelicaReader*, FelicaData*, FelicaSystem*" +Function,-,felica_lite_is_issued,_Bool,FelicaLiteInfo* Function,-,felica_lite_prepare_unencrypted_read,uint8_t,"uint8_t*, const FelicaReader*, _Bool, const uint8_t*, uint8_t" Function,-,felica_lite_prepare_unencrypted_write,uint8_t,"uint8_t*, const FelicaReader*, const uint8_t*, uint8_t, const uint8_t*" Function,-,felica_parse_unencrypted_read,uint16_t,"uint8_t*, uint8_t, FelicaReader*, uint8_t*, uint16_t" @@ -928,12 +930,14 @@ Function,+,file_browser_stop,void,FileBrowser* Function,+,file_browser_worker_alloc,BrowserWorker*,"FuriString*, const char*, const char*, _Bool, _Bool" Function,+,file_browser_worker_folder_enter,void,"BrowserWorker*, FuriString*, int32_t" Function,+,file_browser_worker_folder_exit,void,BrowserWorker* -Function,+,file_browser_worker_folder_refresh,void,"BrowserWorker*, int32_t" +Function,+,file_browser_worker_folder_refresh,void,"BrowserWorker*, const char*" Function,+,file_browser_worker_free,void,BrowserWorker* +Function,+,file_browser_worker_get_filter_ext,const char*,BrowserWorker* Function,+,file_browser_worker_is_in_start_folder,_Bool,BrowserWorker* Function,+,file_browser_worker_load,void,"BrowserWorker*, uint32_t, uint32_t" Function,+,file_browser_worker_set_callback_context,void,"BrowserWorker*, void*" Function,+,file_browser_worker_set_config,void,"BrowserWorker*, FuriString*, const char*, _Bool, _Bool" +Function,+,file_browser_worker_set_filter_ext,void,"BrowserWorker*, FuriString*, const char*" Function,+,file_browser_worker_set_folder_callback,void,"BrowserWorker*, BrowserWorkerFolderOpenCallback" Function,+,file_browser_worker_set_item_callback,void,"BrowserWorker*, BrowserWorkerListItemCallback" Function,+,file_browser_worker_set_list_callback,void,"BrowserWorker*, BrowserWorkerListLoadCallback" @@ -952,10 +956,11 @@ Function,-,finitel,int,long double Function,-,fiprintf,int,"FILE*, const char*, ..." Function,-,fiscanf,int,"FILE*, const char*, ..." Function,+,flipper_application_alloc,FlipperApplication*,"Storage*, const ElfApiInterface*" -Function,+,flipper_application_despawn,void,FlipperApplication* +Function,+,flipper_application_alloc_thread,FuriThread*,"FlipperApplication*, const char*" Function,+,flipper_application_free,void,FlipperApplication* Function,+,flipper_application_get_manifest,const FlipperApplicationManifest*,FlipperApplication* Function,+,flipper_application_is_plugin,_Bool,FlipperApplication* +Function,+,flipper_application_load_name_and_icon,_Bool,"FuriString*, Storage*, uint8_t**, FuriString*" Function,+,flipper_application_load_status_to_string,const char*,FlipperApplicationLoadStatus Function,+,flipper_application_manifest_is_compatible,_Bool,"const FlipperApplicationManifest*, const ElfApiInterface*" Function,+,flipper_application_manifest_is_target_compatible,_Bool,const FlipperApplicationManifest* @@ -965,7 +970,6 @@ Function,+,flipper_application_plugin_get_descriptor,const FlipperAppPluginDescr Function,+,flipper_application_preload,FlipperApplicationPreloadStatus,"FlipperApplication*, const char*" Function,+,flipper_application_preload_manifest,FlipperApplicationPreloadStatus,"FlipperApplication*, const char*" Function,+,flipper_application_preload_status_to_string,const char*,FlipperApplicationPreloadStatus -Function,+,flipper_application_spawn,FuriThread*,"FlipperApplication*, void*" Function,+,flipper_format_buffered_file_alloc,FlipperFormat*,Storage* Function,+,flipper_format_buffered_file_close,_Bool,FlipperFormat* Function,+,flipper_format_buffered_file_open_always,_Bool,"FlipperFormat*, const char*" @@ -1089,7 +1093,7 @@ Function,-,furi_hal_bt_get_hardfault_info,const FuriHalBtHardfaultInfo*, Function,+,furi_hal_bt_get_key_storage_buff,void,"uint8_t**, uint16_t*" Function,+,furi_hal_bt_get_profile_adv_name,const char*,FuriHalBtProfile Function,+,furi_hal_bt_get_profile_mac_addr,const uint8_t*,FuriHalBtProfile -Function,-,furi_hal_bt_get_profile_pairing_method,GapPairing,FuriHalBtProfile +Function,+,furi_hal_bt_get_profile_pairing_method,GapPairing,FuriHalBtProfile Function,+,furi_hal_bt_get_radio_stack,FuriHalBtStack, Function,+,furi_hal_bt_get_rssi,float, Function,+,furi_hal_bt_get_transmitted_packets,uint32_t, @@ -1117,6 +1121,7 @@ Function,+,furi_hal_bt_lock_core2,void, Function,+,furi_hal_bt_nvm_sram_sem_acquire,void, Function,+,furi_hal_bt_nvm_sram_sem_release,void, Function,+,furi_hal_bt_reinit,void, +Function,+,furi_hal_bt_reverse_mac_addr,void,uint8_t[( 6 )] Function,+,furi_hal_bt_serial_notify_buffer_is_empty,void, Function,+,furi_hal_bt_serial_set_event_callback,void,"uint16_t, FuriHalBtSerialCallback, void*" Function,+,furi_hal_bt_serial_set_rpc_status,void,FuriHalBtSerialRpcStatus @@ -1295,6 +1300,8 @@ Function,+,furi_hal_nfc_deinit,void, Function,+,furi_hal_nfc_detect,_Bool,"FuriHalNfcDevData*, uint32_t" Function,+,furi_hal_nfc_emulate_nfca,_Bool,"uint8_t*, uint8_t, uint8_t*, uint8_t, FuriHalNfcEmulateCallback, void*, uint32_t" Function,+,furi_hal_nfc_exit_sleep,void, +Function,+,furi_hal_nfc_field_detect_start,void, +Function,+,furi_hal_nfc_field_is_present,_Bool, Function,+,furi_hal_nfc_field_off,void, Function,+,furi_hal_nfc_field_on,void, Function,+,furi_hal_nfc_init,void, @@ -1355,6 +1362,7 @@ Function,+,furi_hal_power_sleep,void, Function,+,furi_hal_power_sleep_available,_Bool, Function,+,furi_hal_power_suppress_charge_enter,void, Function,+,furi_hal_power_suppress_charge_exit,void, +Function,+,furi_hal_pwm_is_running,_Bool,FuriHalPwmOutputId Function,+,furi_hal_pwm_set_params,void,"FuriHalPwmOutputId, uint32_t, uint8_t" Function,+,furi_hal_pwm_start,void,"FuriHalPwmOutputId, uint32_t, uint8_t" Function,+,furi_hal_pwm_stop,void,FuriHalPwmOutputId @@ -1374,6 +1382,9 @@ Function,-,furi_hal_resources_init_early,void, Function,+,furi_hal_rfid_comp_set_callback,void,"FuriHalRfidCompCallback, void*" Function,+,furi_hal_rfid_comp_start,void, Function,+,furi_hal_rfid_comp_stop,void, +Function,+,furi_hal_rfid_field_detect_start,void, +Function,+,furi_hal_rfid_field_detect_stop,void, +Function,+,furi_hal_rfid_field_is_present,_Bool,uint32_t* Function,-,furi_hal_rfid_init,void, Function,+,furi_hal_rfid_pin_pull_pulldown,void, Function,+,furi_hal_rfid_pin_pull_release,void, @@ -1455,7 +1466,6 @@ Function,+,furi_hal_subghz_get_lqi,uint8_t, Function,+,furi_hal_subghz_get_radio_type,SubGhzRadioType, Function,+,furi_hal_subghz_get_rolling_counter_mult,uint8_t, Function,+,furi_hal_subghz_get_rssi,float, -Function,+,furi_hal_subghz_get_timestamp_file_names,_Bool, Function,+,furi_hal_subghz_idle,void, Function,-,furi_hal_subghz_init,void, Function,-,furi_hal_subghz_init_check,_Bool, @@ -1479,7 +1489,6 @@ Function,+,furi_hal_subghz_set_frequency,uint32_t,uint32_t Function,+,furi_hal_subghz_set_frequency_and_path,uint32_t,uint32_t Function,+,furi_hal_subghz_set_path,void,FuriHalSubGhzPath Function,-,furi_hal_subghz_set_rolling_counter_mult,void,uint8_t -Function,-,furi_hal_subghz_set_timestamp_file_names,void,_Bool Function,-,furi_hal_subghz_shutdown,void, Function,+,furi_hal_subghz_sleep,void, Function,+,furi_hal_subghz_start_async_rx,void,"FuriHalSubGhzCaptureCallback, void*" @@ -1499,6 +1508,7 @@ Function,+,furi_hal_uart_tx,void,"FuriHalUartId, uint8_t*, size_t" Function,+,furi_hal_usb_disable,void, Function,+,furi_hal_usb_enable,void, Function,+,furi_hal_usb_get_config,FuriHalUsbInterface*, +Function,+,furi_hal_usb_get_config_context,void*, Function,-,furi_hal_usb_init,void, Function,+,furi_hal_usb_is_locked,_Bool, Function,+,furi_hal_usb_lock,void, @@ -1531,6 +1541,7 @@ Function,+,furi_hal_version_get_otp_version,FuriHalVersionOtpVersion, Function,-,furi_hal_version_init,void, Function,-,furi_hal_version_set_name,void,const char* Function,+,furi_hal_version_uid,const uint8_t*, +Function,+,furi_hal_version_uid_default,const uint8_t*, Function,+,furi_hal_version_uid_size,size_t, Function,-,furi_hal_vibro_init,void, Function,+,furi_hal_vibro_on,void,_Bool @@ -1919,7 +1930,9 @@ Function,+,loader_is_locked,_Bool,Loader* Function,+,loader_lock,_Bool,Loader* Function,+,loader_show_menu,void,Loader* Function,+,loader_show_settings,void,Loader* -Function,+,loader_start,LoaderStatus,"Loader*, const char*, const char*" +Function,+,loader_start,LoaderStatus,"Loader*, const char*, const char*, FuriString*" +Function,+,loader_start_detached_with_gui_error,void,"Loader*, const char*, const char*" +Function,+,loader_start_with_gui_error,LoaderStatus,"Loader*, const char*, const char*" Function,+,loader_unlock,void,Loader* Function,+,loading_alloc,Loading*, Function,+,loading_free,void,Loading* @@ -2051,7 +2064,7 @@ Function,-,mf_classic_dict_get_total_keys,uint32_t,MfClassicDict* Function,-,mf_classic_dict_is_key_present,_Bool,"MfClassicDict*, uint8_t*" Function,-,mf_classic_dict_is_key_present_str,_Bool,"MfClassicDict*, FuriString*" Function,-,mf_classic_dict_rewind,_Bool,MfClassicDict* -Function,-,mf_classic_emulator,_Bool,"MfClassicEmulator*, FuriHalNfcTxRxContext*" +Function,-,mf_classic_emulator,_Bool,"MfClassicEmulator*, FuriHalNfcTxRxContext*, _Bool" Function,-,mf_classic_get_classic_type,MfClassicType,FuriHalNfcADevData* Function,-,mf_classic_get_read_sectors_and_keys,void,"MfClassicData*, uint8_t*, uint8_t*" Function,-,mf_classic_get_sector_by_block,uint8_t,uint8_t @@ -2148,6 +2161,18 @@ Function,-,modf,double,"double, double*" Function,-,modff,float,"float, float*" Function,-,modfl,long double,"long double, long double*" Function,-,mrand48,long, +Function,-,music_worker_alloc,MusicWorker*, +Function,-,music_worker_clear,void,MusicWorker* +Function,-,music_worker_free,void,MusicWorker* +Function,-,music_worker_is_playing,_Bool,MusicWorker* +Function,-,music_worker_load,_Bool,"MusicWorker*, const char*" +Function,-,music_worker_load_fmf_from_file,_Bool,"MusicWorker*, const char*" +Function,-,music_worker_load_rtttl_from_file,_Bool,"MusicWorker*, const char*" +Function,-,music_worker_load_rtttl_from_string,_Bool,"MusicWorker*, const char*" +Function,-,music_worker_set_callback,void,"MusicWorker*, MusicWorkerCallback, void*" +Function,-,music_worker_set_volume,void,"MusicWorker*, float" +Function,-,music_worker_start,void,MusicWorker* +Function,-,music_worker_stop,void,MusicWorker* Function,-,nan,double,const char* Function,-,nanf,float,const char* Function,-,nanl,long double,const char* @@ -2231,6 +2256,7 @@ Function,+,path_concat,void,"const char*, const char*, FuriString*" Function,+,path_contains_only_ascii,_Bool,const char* Function,+,path_extract_basename,void,"const char*, FuriString*" Function,+,path_extract_dirname,void,"const char*, FuriString*" +Function,+,path_extract_ext_str,void,"FuriString*, FuriString*" Function,+,path_extract_extension,void,"FuriString*, char*, size_t" Function,+,path_extract_filename,void,"FuriString*, FuriString*, _Bool" Function,+,path_extract_filename_no_ext,void,"const char*, FuriString*" @@ -2275,6 +2301,7 @@ Function,+,power_get_settings_events_pubsub,FuriPubSub*,Power* Function,+,power_is_battery_healthy,_Bool,Power* Function,+,power_off,void,Power* Function,+,power_reboot,void,PowerBootMode +Function,+,power_set_battery_icon_enabled,void,"Power*, _Bool" Function,-,power_trigger_ui_update,void,Power* Function,+,powf,float,"float, float" Function,-,powl,long double,"long double, long double" @@ -2734,7 +2761,7 @@ Function,-,strncat,char*,"char*, const char*, size_t" Function,+,strncmp,int,"const char*, const char*, size_t" Function,+,strncpy,char*,"char*, const char*, size_t" Function,-,strndup,char*,"const char*, size_t" -Function,-,strnlen,size_t,"const char*, size_t" +Function,+,strnlen,size_t,"const char*, size_t" Function,-,strnstr,char*,"const char*, const char*, size_t" Function,-,strpbrk,char*,"const char*, const char*" Function,-,strptime,char*,"const char*, const char*, tm*" @@ -2780,13 +2807,13 @@ Function,+,subghz_environment_get_came_atomo_rainbow_table_file_name,const char* Function,+,subghz_environment_get_keystore,SubGhzKeystore*,SubGhzEnvironment* Function,+,subghz_environment_get_nice_flor_s_rainbow_table_file_name,const char*,SubGhzEnvironment* Function,+,subghz_environment_get_protocol_name_registry,const char*,"SubGhzEnvironment*, size_t" -Function,+,subghz_environment_get_protocol_registry,void*,SubGhzEnvironment* +Function,+,subghz_environment_get_protocol_registry,const SubGhzProtocolRegistry*,SubGhzEnvironment* Function,+,subghz_environment_load_keystore,_Bool,"SubGhzEnvironment*, const char*" Function,+,subghz_environment_reset_keeloq,void,SubGhzEnvironment* Function,+,subghz_environment_set_alutech_at_4n_rainbow_table_file_name,void,"SubGhzEnvironment*, const char*" Function,+,subghz_environment_set_came_atomo_rainbow_table_file_name,void,"SubGhzEnvironment*, const char*" Function,+,subghz_environment_set_nice_flor_s_rainbow_table_file_name,void,"SubGhzEnvironment*, const char*" -Function,+,subghz_environment_set_protocol_registry,void,"SubGhzEnvironment*, void*" +Function,+,subghz_environment_set_protocol_registry,void,"SubGhzEnvironment*, const SubGhzProtocolRegistry*" Function,-,subghz_keystore_alloc,SubGhzKeystore*, Function,-,subghz_keystore_free,void,SubGhzKeystore* Function,-,subghz_keystore_get_data,SubGhzKeyArray_t*,SubGhzKeystore* @@ -2795,7 +2822,6 @@ Function,-,subghz_keystore_raw_encrypted_save,_Bool,"const char*, const char*, u Function,-,subghz_keystore_raw_get_data,_Bool,"const char*, size_t, uint8_t*, size_t" Function,-,subghz_keystore_reset_kl,void,SubGhzKeystore* Function,-,subghz_keystore_save,_Bool,"SubGhzKeystore*, const char*, uint8_t*" -Function,-,subghz_protocol_alutech_at_4n_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, SubGhzRadioPreset*" Function,+,subghz_protocol_blocks_add_bit,void,"SubGhzBlockDecoder*, uint8_t" Function,+,subghz_protocol_blocks_add_bytes,uint8_t,"const uint8_t[], size_t" Function,+,subghz_protocol_blocks_add_to_128_bit,void,"SubGhzBlockDecoder*, uint8_t, uint64_t*" @@ -2817,547 +2843,22 @@ Function,+,subghz_protocol_blocks_parity_bytes,uint8_t,"const uint8_t[], size_t" Function,+,subghz_protocol_blocks_reverse_key,uint64_t,"uint64_t, uint8_t" Function,+,subghz_protocol_blocks_set_bit_array,void,"_Bool, uint8_t[], size_t, size_t" Function,+,subghz_protocol_blocks_xor_bytes,uint8_t,"const uint8_t[], size_t" -Function,-,subghz_protocol_came_atomo_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint16_t, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_alutech_at_4n_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_alutech_at_4n_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_alutech_at_4n_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_alutech_at_4n_free,void,void* -Function,-,subghz_protocol_decoder_alutech_at_4n_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_alutech_at_4n_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_alutech_at_4n_reset,void,void* -Function,-,subghz_protocol_decoder_alutech_at_4n_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_ansonic_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_ansonic_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_ansonic_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_ansonic_free,void,void* -Function,-,subghz_protocol_decoder_ansonic_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_ansonic_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_ansonic_reset,void,void* -Function,-,subghz_protocol_decoder_ansonic_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,+,subghz_protocol_decoder_base_deserialize,SubGhzProtocolStatus,"SubGhzProtocolDecoderBase*, FlipperFormat*" Function,+,subghz_protocol_decoder_base_get_hash_data,uint8_t,SubGhzProtocolDecoderBase* Function,+,subghz_protocol_decoder_base_get_string,_Bool,"SubGhzProtocolDecoderBase*, FuriString*" Function,+,subghz_protocol_decoder_base_serialize,SubGhzProtocolStatus,"SubGhzProtocolDecoderBase*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_base_set_decoder_callback,void,"SubGhzProtocolDecoderBase*, SubGhzProtocolDecoderBaseRxCallback, void*" -Function,-,subghz_protocol_decoder_bett_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_bett_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_bett_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_bett_free,void,void* -Function,-,subghz_protocol_decoder_bett_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_bett_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_bett_reset,void,void* -Function,-,subghz_protocol_decoder_bett_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_bin_raw_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_bin_raw_data_input_rssi,void,"SubGhzProtocolDecoderBinRAW*, float" -Function,-,subghz_protocol_decoder_bin_raw_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_bin_raw_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_bin_raw_free,void,void* -Function,-,subghz_protocol_decoder_bin_raw_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_bin_raw_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_bin_raw_reset,void,void* -Function,-,subghz_protocol_decoder_bin_raw_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_came_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_came_atomo_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_came_atomo_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_came_atomo_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_came_atomo_free,void,void* -Function,-,subghz_protocol_decoder_came_atomo_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_came_atomo_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_came_atomo_reset,void,void* -Function,-,subghz_protocol_decoder_came_atomo_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_came_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_came_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_came_free,void,void* -Function,-,subghz_protocol_decoder_came_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_came_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_came_reset,void,void* -Function,-,subghz_protocol_decoder_came_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_came_twee_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_came_twee_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_came_twee_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_came_twee_free,void,void* -Function,-,subghz_protocol_decoder_came_twee_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_came_twee_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_came_twee_reset,void,void* -Function,-,subghz_protocol_decoder_came_twee_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_chamb_code_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_chamb_code_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_chamb_code_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_chamb_code_free,void,void* -Function,-,subghz_protocol_decoder_chamb_code_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_chamb_code_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_chamb_code_reset,void,void* -Function,-,subghz_protocol_decoder_chamb_code_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_clemsa_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_clemsa_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_clemsa_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_clemsa_free,void,void* -Function,-,subghz_protocol_decoder_clemsa_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_clemsa_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_clemsa_reset,void,void* -Function,-,subghz_protocol_decoder_clemsa_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_doitrand_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_doitrand_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_doitrand_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_doitrand_free,void,void* -Function,-,subghz_protocol_decoder_doitrand_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_doitrand_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_doitrand_reset,void,void* -Function,-,subghz_protocol_decoder_doitrand_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_dooya_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_dooya_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_dooya_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_dooya_free,void,void* -Function,-,subghz_protocol_decoder_dooya_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_dooya_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_dooya_reset,void,void* -Function,-,subghz_protocol_decoder_dooya_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_faac_slh_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_faac_slh_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_faac_slh_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_faac_slh_free,void,void* -Function,-,subghz_protocol_decoder_faac_slh_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_faac_slh_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_faac_slh_reset,void,void* -Function,-,subghz_protocol_decoder_faac_slh_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_gate_tx_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_gate_tx_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_gate_tx_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_gate_tx_free,void,void* -Function,-,subghz_protocol_decoder_gate_tx_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_gate_tx_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_gate_tx_reset,void,void* -Function,-,subghz_protocol_decoder_gate_tx_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_holtek_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_holtek_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_holtek_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_holtek_free,void,void* -Function,-,subghz_protocol_decoder_holtek_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_holtek_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_holtek_reset,void,void* -Function,-,subghz_protocol_decoder_holtek_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_holtek_th12x_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_holtek_th12x_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_holtek_th12x_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_holtek_th12x_free,void,void* -Function,-,subghz_protocol_decoder_holtek_th12x_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_holtek_th12x_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_holtek_th12x_reset,void,void* -Function,-,subghz_protocol_decoder_holtek_th12x_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_honeywell_wdb_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_honeywell_wdb_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_honeywell_wdb_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_honeywell_wdb_free,void,void* -Function,-,subghz_protocol_decoder_honeywell_wdb_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_honeywell_wdb_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_honeywell_wdb_reset,void,void* -Function,-,subghz_protocol_decoder_honeywell_wdb_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_hormann_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_hormann_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_hormann_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_hormann_free,void,void* -Function,-,subghz_protocol_decoder_hormann_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_hormann_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_hormann_reset,void,void* -Function,-,subghz_protocol_decoder_hormann_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_ido_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_ido_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_ido_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_ido_free,void,void* -Function,-,subghz_protocol_decoder_ido_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_ido_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_ido_reset,void,void* -Function,-,subghz_protocol_decoder_ido_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_intertechno_v3_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_intertechno_v3_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_intertechno_v3_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_intertechno_v3_free,void,void* -Function,-,subghz_protocol_decoder_intertechno_v3_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_intertechno_v3_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_intertechno_v3_reset,void,void* -Function,-,subghz_protocol_decoder_intertechno_v3_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_keeloq_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_keeloq_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_keeloq_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_keeloq_free,void,void* -Function,-,subghz_protocol_decoder_keeloq_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_keeloq_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_keeloq_reset,void,void* -Function,-,subghz_protocol_decoder_keeloq_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_kia_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_kia_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_kia_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_kia_free,void,void* -Function,-,subghz_protocol_decoder_kia_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_kia_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_kia_reset,void,void* -Function,-,subghz_protocol_decoder_kia_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_kinggates_stylo_4k_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_kinggates_stylo_4k_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_kinggates_stylo_4k_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_kinggates_stylo_4k_free,void,void* -Function,-,subghz_protocol_decoder_kinggates_stylo_4k_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_kinggates_stylo_4k_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_kinggates_stylo_4k_reset,void,void* -Function,-,subghz_protocol_decoder_kinggates_stylo_4k_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_linear_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_linear_delta3_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_linear_delta3_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_linear_delta3_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_linear_delta3_free,void,void* -Function,-,subghz_protocol_decoder_linear_delta3_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_linear_delta3_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_linear_delta3_reset,void,void* -Function,-,subghz_protocol_decoder_linear_delta3_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_linear_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_linear_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_linear_free,void,void* -Function,-,subghz_protocol_decoder_linear_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_linear_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_linear_reset,void,void* -Function,-,subghz_protocol_decoder_linear_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_magellan_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_magellan_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_magellan_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_magellan_free,void,void* -Function,-,subghz_protocol_decoder_magellan_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_magellan_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_magellan_reset,void,void* -Function,-,subghz_protocol_decoder_magellan_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_marantec_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_marantec_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_marantec_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_marantec_free,void,void* -Function,-,subghz_protocol_decoder_marantec_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_marantec_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_marantec_reset,void,void* -Function,-,subghz_protocol_decoder_marantec_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_megacode_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_megacode_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_megacode_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_megacode_free,void,void* -Function,-,subghz_protocol_decoder_megacode_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_megacode_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_megacode_reset,void,void* -Function,-,subghz_protocol_decoder_megacode_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_nero_radio_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_nero_radio_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_nero_radio_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_nero_radio_free,void,void* -Function,-,subghz_protocol_decoder_nero_radio_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_nero_radio_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_nero_radio_reset,void,void* -Function,-,subghz_protocol_decoder_nero_radio_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_nero_sketch_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_nero_sketch_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_nero_sketch_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_nero_sketch_free,void,void* -Function,-,subghz_protocol_decoder_nero_sketch_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_nero_sketch_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_nero_sketch_reset,void,void* -Function,-,subghz_protocol_decoder_nero_sketch_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_nice_flo_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_nice_flo_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_nice_flo_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_nice_flo_free,void,void* -Function,-,subghz_protocol_decoder_nice_flo_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_nice_flo_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_nice_flo_reset,void,void* -Function,-,subghz_protocol_decoder_nice_flo_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_nice_flor_s_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_nice_flor_s_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_nice_flor_s_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_nice_flor_s_free,void,void* -Function,-,subghz_protocol_decoder_nice_flor_s_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_nice_flor_s_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_nice_flor_s_reset,void,void* -Function,-,subghz_protocol_decoder_nice_flor_s_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_phoenix_v2_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_phoenix_v2_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_phoenix_v2_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_phoenix_v2_free,void,void* -Function,-,subghz_protocol_decoder_phoenix_v2_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_phoenix_v2_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_phoenix_v2_reset,void,void* -Function,-,subghz_protocol_decoder_phoenix_v2_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_power_smart_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_power_smart_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_power_smart_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_power_smart_free,void,void* -Function,-,subghz_protocol_decoder_power_smart_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_power_smart_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_power_smart_reset,void,void* -Function,-,subghz_protocol_decoder_power_smart_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_princeton_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_princeton_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_princeton_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_princeton_free,void,void* -Function,-,subghz_protocol_decoder_princeton_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_princeton_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_princeton_reset,void,void* -Function,-,subghz_protocol_decoder_princeton_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,+,subghz_protocol_decoder_raw_alloc,void*,SubGhzEnvironment* Function,+,subghz_protocol_decoder_raw_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,+,subghz_protocol_decoder_raw_feed,void,"void*, _Bool, uint32_t" Function,+,subghz_protocol_decoder_raw_free,void,void* Function,+,subghz_protocol_decoder_raw_get_string,void,"void*, FuriString*" Function,+,subghz_protocol_decoder_raw_reset,void,void* -Function,-,subghz_protocol_decoder_scher_khan_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_scher_khan_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_scher_khan_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_scher_khan_free,void,void* -Function,-,subghz_protocol_decoder_scher_khan_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_scher_khan_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_scher_khan_reset,void,void* -Function,-,subghz_protocol_decoder_scher_khan_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_secplus_v1_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_secplus_v1_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_secplus_v1_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_secplus_v1_free,void,void* -Function,-,subghz_protocol_decoder_secplus_v1_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_secplus_v1_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_secplus_v1_reset,void,void* -Function,-,subghz_protocol_decoder_secplus_v1_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_secplus_v2_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_secplus_v2_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_secplus_v2_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_secplus_v2_free,void,void* -Function,-,subghz_protocol_decoder_secplus_v2_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_secplus_v2_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_secplus_v2_reset,void,void* -Function,-,subghz_protocol_decoder_secplus_v2_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_smc5326_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_smc5326_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_smc5326_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_smc5326_free,void,void* -Function,-,subghz_protocol_decoder_smc5326_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_smc5326_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_smc5326_reset,void,void* -Function,-,subghz_protocol_decoder_smc5326_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_somfy_keytis_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_somfy_keytis_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_somfy_keytis_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_somfy_keytis_free,void,void* -Function,-,subghz_protocol_decoder_somfy_keytis_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_somfy_keytis_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_somfy_keytis_reset,void,void* -Function,-,subghz_protocol_decoder_somfy_keytis_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_somfy_telis_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_somfy_telis_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_somfy_telis_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_somfy_telis_free,void,void* -Function,-,subghz_protocol_decoder_somfy_telis_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_somfy_telis_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_somfy_telis_reset,void,void* -Function,-,subghz_protocol_decoder_somfy_telis_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_star_line_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_star_line_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_star_line_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_star_line_free,void,void* -Function,-,subghz_protocol_decoder_star_line_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_star_line_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_star_line_reset,void,void* -Function,-,subghz_protocol_decoder_star_line_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_encoder_alutech_at_4n_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_alutech_at_4n_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_alutech_at_4n_free,void,void* -Function,-,subghz_protocol_encoder_alutech_at_4n_stop,void,void* -Function,-,subghz_protocol_encoder_alutech_at_4n_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_ansonic_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_ansonic_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_ansonic_free,void,void* -Function,-,subghz_protocol_encoder_ansonic_stop,void,void* -Function,-,subghz_protocol_encoder_ansonic_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_bett_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_bett_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_bett_free,void,void* -Function,-,subghz_protocol_encoder_bett_stop,void,void* -Function,-,subghz_protocol_encoder_bett_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_bin_raw_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_bin_raw_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_bin_raw_free,void,void* -Function,-,subghz_protocol_encoder_bin_raw_stop,void,void* -Function,-,subghz_protocol_encoder_bin_raw_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_came_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_came_atomo_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_came_atomo_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_came_atomo_free,void,void* -Function,-,subghz_protocol_encoder_came_atomo_stop,void,void* -Function,-,subghz_protocol_encoder_came_atomo_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_came_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_came_free,void,void* -Function,-,subghz_protocol_encoder_came_stop,void,void* -Function,-,subghz_protocol_encoder_came_twee_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_came_twee_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_came_twee_free,void,void* -Function,-,subghz_protocol_encoder_came_twee_stop,void,void* -Function,-,subghz_protocol_encoder_came_twee_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_came_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_chamb_code_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_chamb_code_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_chamb_code_free,void,void* -Function,-,subghz_protocol_encoder_chamb_code_stop,void,void* -Function,-,subghz_protocol_encoder_chamb_code_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_clemsa_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_clemsa_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_clemsa_free,void,void* -Function,-,subghz_protocol_encoder_clemsa_stop,void,void* -Function,-,subghz_protocol_encoder_clemsa_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_doitrand_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_doitrand_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_doitrand_free,void,void* -Function,-,subghz_protocol_encoder_doitrand_stop,void,void* -Function,-,subghz_protocol_encoder_doitrand_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_dooya_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_dooya_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_dooya_free,void,void* -Function,-,subghz_protocol_encoder_dooya_stop,void,void* -Function,-,subghz_protocol_encoder_dooya_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_faac_slh_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_faac_slh_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_faac_slh_free,void,void* -Function,-,subghz_protocol_encoder_faac_slh_stop,void,void* -Function,-,subghz_protocol_encoder_faac_slh_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_gate_tx_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_gate_tx_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_gate_tx_free,void,void* -Function,-,subghz_protocol_encoder_gate_tx_stop,void,void* -Function,-,subghz_protocol_encoder_gate_tx_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_holtek_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_holtek_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_holtek_free,void,void* -Function,-,subghz_protocol_encoder_holtek_stop,void,void* -Function,-,subghz_protocol_encoder_holtek_th12x_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_holtek_th12x_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_holtek_th12x_free,void,void* -Function,-,subghz_protocol_encoder_holtek_th12x_stop,void,void* -Function,-,subghz_protocol_encoder_holtek_th12x_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_holtek_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_honeywell_wdb_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_honeywell_wdb_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_honeywell_wdb_free,void,void* -Function,-,subghz_protocol_encoder_honeywell_wdb_stop,void,void* -Function,-,subghz_protocol_encoder_honeywell_wdb_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_hormann_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_hormann_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_hormann_free,void,void* -Function,-,subghz_protocol_encoder_hormann_stop,void,void* -Function,-,subghz_protocol_encoder_hormann_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_intertechno_v3_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_intertechno_v3_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_intertechno_v3_free,void,void* -Function,-,subghz_protocol_encoder_intertechno_v3_stop,void,void* -Function,-,subghz_protocol_encoder_intertechno_v3_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_keeloq_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_keeloq_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_keeloq_free,void,void* -Function,-,subghz_protocol_encoder_keeloq_stop,void,void* -Function,-,subghz_protocol_encoder_keeloq_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_kinggates_stylo_4k_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_kinggates_stylo_4k_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_kinggates_stylo_4k_free,void,void* -Function,-,subghz_protocol_encoder_kinggates_stylo_4k_stop,void,void* -Function,-,subghz_protocol_encoder_kinggates_stylo_4k_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_linear_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_linear_delta3_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_linear_delta3_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_linear_delta3_free,void,void* -Function,-,subghz_protocol_encoder_linear_delta3_stop,void,void* -Function,-,subghz_protocol_encoder_linear_delta3_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_linear_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_linear_free,void,void* -Function,-,subghz_protocol_encoder_linear_stop,void,void* -Function,-,subghz_protocol_encoder_linear_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_magellan_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_magellan_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_magellan_free,void,void* -Function,-,subghz_protocol_encoder_magellan_stop,void,void* -Function,-,subghz_protocol_encoder_magellan_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_marantec_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_marantec_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_marantec_free,void,void* -Function,-,subghz_protocol_encoder_marantec_stop,void,void* -Function,-,subghz_protocol_encoder_marantec_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_megacode_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_megacode_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_megacode_free,void,void* -Function,-,subghz_protocol_encoder_megacode_stop,void,void* -Function,-,subghz_protocol_encoder_megacode_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_nero_radio_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_nero_radio_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_nero_radio_free,void,void* -Function,-,subghz_protocol_encoder_nero_radio_stop,void,void* -Function,-,subghz_protocol_encoder_nero_radio_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_nero_sketch_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_nero_sketch_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_nero_sketch_free,void,void* -Function,-,subghz_protocol_encoder_nero_sketch_stop,void,void* -Function,-,subghz_protocol_encoder_nero_sketch_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_nice_flo_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_nice_flo_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_nice_flo_free,void,void* -Function,-,subghz_protocol_encoder_nice_flo_stop,void,void* -Function,-,subghz_protocol_encoder_nice_flo_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_nice_flor_s_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_nice_flor_s_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_nice_flor_s_free,void,void* -Function,-,subghz_protocol_encoder_nice_flor_s_stop,void,void* -Function,-,subghz_protocol_encoder_nice_flor_s_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_phoenix_v2_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_phoenix_v2_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_phoenix_v2_free,void,void* -Function,-,subghz_protocol_encoder_phoenix_v2_stop,void,void* -Function,-,subghz_protocol_encoder_phoenix_v2_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_power_smart_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_power_smart_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_power_smart_free,void,void* -Function,-,subghz_protocol_encoder_power_smart_stop,void,void* -Function,-,subghz_protocol_encoder_power_smart_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_princeton_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_princeton_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_princeton_free,void,void* -Function,-,subghz_protocol_encoder_princeton_stop,void,void* -Function,-,subghz_protocol_encoder_princeton_yield,LevelDuration,void* Function,+,subghz_protocol_encoder_raw_alloc,void*,SubGhzEnvironment* Function,+,subghz_protocol_encoder_raw_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,+,subghz_protocol_encoder_raw_free,void,void* Function,+,subghz_protocol_encoder_raw_stop,void,void* Function,+,subghz_protocol_encoder_raw_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_secplus_v1_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_secplus_v1_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_secplus_v1_free,void,void* -Function,-,subghz_protocol_encoder_secplus_v1_stop,void,void* -Function,-,subghz_protocol_encoder_secplus_v1_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_secplus_v2_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_secplus_v2_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_secplus_v2_free,void,void* -Function,-,subghz_protocol_encoder_secplus_v2_stop,void,void* -Function,-,subghz_protocol_encoder_secplus_v2_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_smc5326_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_smc5326_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_smc5326_free,void,void* -Function,-,subghz_protocol_encoder_smc5326_stop,void,void* -Function,-,subghz_protocol_encoder_smc5326_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_somfy_keytis_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_somfy_keytis_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_somfy_keytis_free,void,void* -Function,-,subghz_protocol_encoder_somfy_keytis_stop,void,void* -Function,-,subghz_protocol_encoder_somfy_keytis_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_somfy_telis_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_somfy_telis_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_somfy_telis_free,void,void* -Function,-,subghz_protocol_encoder_somfy_telis_stop,void,void* -Function,-,subghz_protocol_encoder_somfy_telis_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_star_line_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_star_line_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_star_line_free,void,void* -Function,-,subghz_protocol_encoder_star_line_stop,void,void* -Function,-,subghz_protocol_encoder_star_line_yield,LevelDuration,void* -Function,-,subghz_protocol_faac_slh_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint32_t, uint32_t, const char*, SubGhzRadioPreset*" -Function,-,subghz_protocol_keeloq_bft_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, uint32_t, const char*, SubGhzRadioPreset*" -Function,-,subghz_protocol_keeloq_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, const char*, SubGhzRadioPreset*" -Function,-,subghz_protocol_nice_flor_s_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, SubGhzRadioPreset*, _Bool" -Function,-,subghz_protocol_nice_flor_s_encrypt,uint64_t,"uint64_t, const char*" Function,+,subghz_protocol_raw_file_encoder_worker_set_callback_end,void,"SubGhzProtocolEncoderRAW*, SubGhzProtocolEncoderRAWCallbackEnd, void*" Function,+,subghz_protocol_raw_gen_fff_data,void,"FlipperFormat*, const char*" Function,+,subghz_protocol_raw_get_sample_write,size_t,SubGhzProtocolDecoderRAW* @@ -3367,11 +2868,6 @@ Function,+,subghz_protocol_raw_save_to_file_stop,void,SubGhzProtocolDecoderRAW* Function,+,subghz_protocol_registry_count,size_t,const SubGhzProtocolRegistry* Function,+,subghz_protocol_registry_get_by_index,const SubGhzProtocol*,"const SubGhzProtocolRegistry*, size_t" Function,+,subghz_protocol_registry_get_by_name,const SubGhzProtocol*,"const SubGhzProtocolRegistry*, const char*" -Function,-,subghz_protocol_secplus_v1_check_fixed,_Bool,uint32_t -Function,-,subghz_protocol_secplus_v2_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint32_t, SubGhzRadioPreset*" -Function,-,subghz_protocol_somfy_keytis_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, SubGhzRadioPreset*" -Function,-,subghz_protocol_somfy_telis_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, SubGhzRadioPreset*" -Function,-,subghz_protocol_star_line_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, const char*, SubGhzRadioPreset*" Function,+,subghz_receiver_alloc_init,SubGhzReceiver*,SubGhzEnvironment* Function,+,subghz_receiver_decode,void,"SubGhzReceiver*, _Bool, uint32_t" Function,+,subghz_receiver_free,void,SubGhzReceiver* @@ -3380,6 +2876,7 @@ Function,+,subghz_receiver_search_decoder_base_by_name,SubGhzProtocolDecoderBase Function,+,subghz_receiver_set_filter,void,"SubGhzReceiver*, SubGhzProtocolFlag" Function,+,subghz_receiver_set_rx_callback,void,"SubGhzReceiver*, SubGhzReceiverCallback, void*" Function,+,subghz_setting_alloc,SubGhzSetting*, +Function,-,subghz_setting_customs_presets_to_log,uint8_t,SubGhzSetting* Function,+,subghz_setting_delete_custom_preset,_Bool,"SubGhzSetting*, const char*" Function,+,subghz_setting_free,void,SubGhzSetting* Function,+,subghz_setting_get_default_frequency,uint32_t,SubGhzSetting* @@ -3468,7 +2965,7 @@ Function,+,submenu_set_header,void,"Submenu*, const char*" Function,+,submenu_set_selected_item,void,"Submenu*, uint32_t" Function,-,system,int,const char* Function,+,t5577_write,void,LFRFIDT5577* -Function,-,t5577_write_with_pass,void,"LFRFIDT5577*, uint32_t" +Function,+,t5577_write_with_pass,void,"LFRFIDT5577*, uint32_t" Function,-,tan,double,double Function,-,tanf,float,float Function,-,tanh,double,double @@ -3717,13 +3214,13 @@ Function,-,vsprintf,int,"char*, const char*, __gnuc_va_list" Function,-,vsscanf,int,"const char*, const char*, __gnuc_va_list" Function,-,wcstombs,size_t,"char*, const wchar_t*, size_t" Function,-,wctomb,int,"char*, wchar_t" -Function,+,widget_add_button_element,void,"Widget*, GuiButtonType, const char*, ButtonCallback, void*" -Function,+,widget_add_frame_element,void,"Widget*, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t" -Function,+,widget_add_icon_element,void,"Widget*, uint8_t, uint8_t, const Icon*" -Function,+,widget_add_string_element,void,"Widget*, uint8_t, uint8_t, Align, Align, Font, const char*" -Function,+,widget_add_string_multiline_element,void,"Widget*, uint8_t, uint8_t, Align, Align, Font, const char*" -Function,+,widget_add_text_box_element,void,"Widget*, uint8_t, uint8_t, uint8_t, uint8_t, Align, Align, const char*, _Bool" -Function,+,widget_add_text_scroll_element,void,"Widget*, uint8_t, uint8_t, uint8_t, uint8_t, const char*" +Function,+,widget_add_button_element,WidgetElement*,"Widget*, GuiButtonType, const char*, ButtonCallback, void*" +Function,+,widget_add_frame_element,WidgetElement*,"Widget*, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t" +Function,+,widget_add_icon_element,WidgetElement*,"Widget*, uint8_t, uint8_t, const Icon*" +Function,+,widget_add_string_element,WidgetElement*,"Widget*, uint8_t, uint8_t, Align, Align, Font, const char*" +Function,+,widget_add_string_multiline_element,WidgetElement*,"Widget*, uint8_t, uint8_t, Align, Align, Font, const char*" +Function,+,widget_add_text_box_element,WidgetElement*,"Widget*, uint8_t, uint8_t, uint8_t, uint8_t, Align, Align, const char*, _Bool" +Function,+,widget_add_text_scroll_element,WidgetElement*,"Widget*, uint8_t, uint8_t, uint8_t, uint8_t, const char*" Function,+,widget_alloc,Widget*, Function,+,widget_free,void,Widget* Function,+,widget_get_view,View*,Widget* @@ -3772,7 +3269,211 @@ Function,-,yn,double,"int, double" Function,-,ynf,float,"int, float" Variable,-,AHBPrescTable,const uint32_t[16], Variable,-,APBPrescTable,const uint32_t[8], +Variable,+,A_125khz_14,Icon, +Variable,+,A_BadKb_14,Icon, +Variable,+,A_GPIO_14,Icon, +Variable,+,A_Infrared_14,Icon, +Variable,+,A_Levelup_128x64,Icon, +Variable,+,A_Loading_24,Icon, +Variable,+,A_NFC_14,Icon, +Variable,+,A_Plugins_14,Icon, +Variable,+,A_Round_loader_8x8,Icon, +Variable,+,A_Settings_14,Icon, +Variable,+,A_Sub1ghz_14,Icon, +Variable,+,A_U2F_14,Icon, +Variable,+,A_Xtreme_14,Icon, +Variable,+,A_iButton_14,Icon, +Variable,+,ICON_PATHS,const IconPath[], +Variable,+,ICON_PATHS_COUNT,const size_t, Variable,-,ITM_RxBuffer,volatile int32_t, +Variable,+,I_125_10px,Icon, +Variable,+,I_ActiveConnection_50x64,Icon, +Variable,+,I_Apps_10px,Icon, +Variable,+,I_ArrowC_1_36x36,Icon, +Variable,+,I_ArrowUpEmpty_14x15,Icon, +Variable,+,I_ArrowUpFilled_14x15,Icon, +Variable,+,I_Auth_62x31,Icon, +Variable,+,I_BLE_Pairing_128x64,Icon, +Variable,+,I_Background_128x11,Icon, +Variable,+,I_BatteryBody_52x28,Icon, +Variable,+,I_Battery_16x16,Icon, +Variable,+,I_Battery_25x8,Icon, +Variable,+,I_Ble_connected_15x15,Icon, +Variable,+,I_Ble_disconnected_15x15,Icon, +Variable,+,I_Bluetooth_Connected_16x8,Icon, +Variable,+,I_Bluetooth_Idle_5x8,Icon, +Variable,+,I_ButtonCenter_7x7,Icon, +Variable,+,I_ButtonDown_7x4,Icon, +Variable,+,I_ButtonLeftSmall_3x5,Icon, +Variable,+,I_ButtonLeft_4x7,Icon, +Variable,+,I_ButtonRightSmall_3x5,Icon, +Variable,+,I_ButtonRight_4x7,Icon, +Variable,+,I_ButtonUp_7x4,Icon, +Variable,+,I_Button_18x18,Icon, +Variable,+,I_CC_Bluetooth_16x16,Icon, +Variable,+,I_CC_DarkMode_16x16,Icon, +Variable,+,I_CC_LefthandedMode_16x16,Icon, +Variable,+,I_CC_Lock_16x16,Icon, +Variable,+,I_CC_Settings_16x16,Icon, +Variable,+,I_CC_Xtreme_16x16,Icon, +Variable,+,I_Certification1_103x56,Icon, +Variable,+,I_Certification2_98x33,Icon, +Variable,+,I_Charging_lightning_9x10,Icon, +Variable,+,I_Charging_lightning_mask_9x10,Icon, +Variable,+,I_Circles_47x47,Icon, +Variable,+,I_Clock_18x18,Icon, +Variable,+,I_Connect_me_62x31,Icon, +Variable,+,I_Connected_62x31,Icon, +Variable,+,I_CoolHi_25x27,Icon, +Variable,+,I_CoolHi_hvr_25x27,Icon, +Variable,+,I_CoolLo_25x27,Icon, +Variable,+,I_CoolLo_hvr_25x27,Icon, +Variable,+,I_Cry_dolph_55x52,Icon, +Variable,+,I_DFU_128x50,Icon, +Variable,+,I_Dehumidify_25x27,Icon, +Variable,+,I_Dehumidify_hvr_25x27,Icon, +Variable,+,I_DolphinCommon_56x48,Icon, +Variable,+,I_DolphinMafia_115x62,Icon, +Variable,+,I_DolphinNice_96x59,Icon, +Variable,+,I_DolphinReadingSuccess_59x63,Icon, +Variable,+,I_DolphinWait_61x59,Icon, +Variable,+,I_Down_25x27,Icon, +Variable,+,I_Down_hvr_25x27,Icon, +Variable,+,I_Drive_112x35,Icon, +Variable,+,I_Dynamic_9x7,Icon, +Variable,+,I_Erase_pin_128x64,Icon, +Variable,+,I_Error_18x18,Icon, +Variable,+,I_Error_62x31,Icon, +Variable,+,I_EviSmile1_18x21,Icon, +Variable,+,I_EviSmile2_18x21,Icon, +Variable,+,I_EviWaiting1_18x21,Icon, +Variable,+,I_EviWaiting2_18x21,Icon, +Variable,+,I_Exit_25x27,Icon, +Variable,+,I_Exit_hvr_25x27,Icon, +Variable,+,I_FaceCharging_29x14,Icon, +Variable,+,I_FaceConfused_29x14,Icon, +Variable,+,I_FaceNopower_29x14,Icon, +Variable,+,I_FaceNormal_29x14,Icon, +Variable,+,I_Fishing_123x52,Icon, +Variable,+,I_Flash_25x27,Icon, +Variable,+,I_Flash_hvr_25x27,Icon, +Variable,+,I_Health_16x16,Icon, +Variable,+,I_HeatHi_25x27,Icon, +Variable,+,I_HeatHi_hvr_25x27,Icon, +Variable,+,I_HeatLo_25x27,Icon, +Variable,+,I_HeatLo_hvr_25x27,Icon, +Variable,+,I_Hidden_window_9x8,Icon, +Variable,+,I_InfraredArrowDown_4x8,Icon, +Variable,+,I_InfraredArrowUp_4x8,Icon, +Variable,+,I_InfraredLearnShort_128x31,Icon, +Variable,+,I_Input_25x27,Icon, +Variable,+,I_Input_hvr_25x27,Icon, +Variable,+,I_KeyBackspaceSelected_16x9,Icon, +Variable,+,I_KeyBackspace_16x9,Icon, +Variable,+,I_KeyKeyboardSelected_10x11,Icon, +Variable,+,I_KeyKeyboard_10x11,Icon, +Variable,+,I_KeySaveSelected_24x11,Icon, +Variable,+,I_KeySave_24x11,Icon, +Variable,+,I_Keychain_39x36,Icon, +Variable,+,I_Left_mouse_icon_9x9,Icon, +Variable,+,I_Lock_7x8,Icon, +Variable,+,I_Lockscreen,Icon, +Variable,+,I_MHz_25x11,Icon, +Variable,+,I_Mode_25x27,Icon, +Variable,+,I_Mode_hvr_25x27,Icon, +Variable,+,I_Modern_reader_18x34,Icon, +Variable,+,I_Move_flipper_26x39,Icon, +Variable,+,I_Mute_25x27,Icon, +Variable,+,I_Mute_hvr_25x27,Icon, +Variable,+,I_Muted_8x8,Icon, +Variable,+,I_NFC_dolphin_emulation_47x61,Icon, +Variable,+,I_NFC_manual_60x50,Icon, +Variable,+,I_Nfc_10px,Icon, +Variable,+,I_Off_25x27,Icon, +Variable,+,I_Off_hvr_25x27,Icon, +Variable,+,I_Ok_btn_9x9,Icon, +Variable,+,I_Ok_btn_pressed_13x13,Icon, +Variable,+,I_Pause_25x27,Icon, +Variable,+,I_Pause_hvr_25x27,Icon, +Variable,+,I_Percent_10x14,Icon, +Variable,+,I_Pin_arrow_up_7x9,Icon, +Variable,+,I_Pin_attention_dpad_29x29,Icon, +Variable,+,I_Pin_back_arrow_10x8,Icon, +Variable,+,I_Pin_cell_13x13,Icon, +Variable,+,I_Pin_pointer_5x3,Icon, +Variable,+,I_Pin_star_7x7,Icon, +Variable,+,I_Play_25x27,Icon, +Variable,+,I_Play_hvr_25x27,Icon, +Variable,+,I_Power_25x27,Icon, +Variable,+,I_Power_hvr_25x27,Icon, +Variable,+,I_Pressed_Button_13x13,Icon, +Variable,+,I_Quest_7x8,Icon, +Variable,+,I_RFIDDolphinReceive_97x61,Icon, +Variable,+,I_RFIDDolphinSend_97x61,Icon, +Variable,+,I_RFIDDolphinSuccess_108x57,Icon, +Variable,+,I_RFIDSmallChip_14x14,Icon, +Variable,+,I_Raw_9x7,Icon, +Variable,+,I_Release_arrow_18x15,Icon, +Variable,+,I_Right_mouse_icon_9x9,Icon, +Variable,+,I_Rotate_25x27,Icon, +Variable,+,I_Rotate_hvr_25x27,Icon, +Variable,+,I_Rpc_active_7x8,Icon, +Variable,+,I_SDQuestion_35x43,Icon, +Variable,+,I_SDcardFail_11x8,Icon, +Variable,+,I_SDcardMounted_11x8,Icon, +Variable,+,I_Scanning_123x52,Icon, +Variable,+,I_SmallArrowDown_3x5,Icon, +Variable,+,I_SmallArrowUp_3x5,Icon, +Variable,+,I_Smile_18x18,Icon, +Variable,+,I_Space_65x18,Icon, +Variable,+,I_Static_9x7,Icon, +Variable,+,I_Stop_25x27,Icon, +Variable,+,I_Stop_hvr_25x27,Icon, +Variable,+,I_Temperature_16x16,Icon, +Variable,+,I_Timer_25x27,Icon, +Variable,+,I_Timer_hvr_25x27,Icon, +Variable,+,I_TrackNext_25x27,Icon, +Variable,+,I_TrackNext_hvr_25x27,Icon, +Variable,+,I_TrackPrev_25x27,Icon, +Variable,+,I_TrackPrev_hvr_25x27,Icon, +Variable,+,I_Unlock_7x8,Icon, +Variable,+,I_Unplug_bg_bottom_128x10,Icon, +Variable,+,I_Unplug_bg_top_128x14,Icon, +Variable,+,I_Up_25x27,Icon, +Variable,+,I_Up_hvr_25x27,Icon, +Variable,+,I_Updating_32x40,Icon, +Variable,+,I_UsbTree_48x22,Icon, +Variable,+,I_Vol_down_25x27,Icon, +Variable,+,I_Vol_down_hvr_25x27,Icon, +Variable,+,I_Vol_up_25x27,Icon, +Variable,+,I_Vol_up_hvr_25x27,Icon, +Variable,+,I_Voldwn_6x6,Icon, +Variable,+,I_Voltage_16x16,Icon, +Variable,+,I_Volup_8x6,Icon, +Variable,+,I_WarningDolphin_45x42,Icon, +Variable,+,I_Warning_30x23,Icon, +Variable,+,I_back_10px,Icon, +Variable,+,I_badkb_10px,Icon, +Variable,+,I_dir_10px,Icon, +Variable,+,I_iButtonDolphinVerySuccess_108x52,Icon, +Variable,+,I_iButtonKey_49x44,Icon, +Variable,+,I_ibutt_10px,Icon, +Variable,+,I_ir_10px,Icon, +Variable,+,I_ir_scope_10px,Icon, +Variable,+,I_keyboard_10px,Icon, +Variable,+,I_loading_10px,Icon, +Variable,+,I_music_10px,Icon, +Variable,+,I_passport_DB,Icon, +Variable,+,I_passport_bad_46x49,Icon, +Variable,+,I_passport_happy_46x49,Icon, +Variable,+,I_passport_okay_46x49,Icon, +Variable,+,I_search_10px,Icon, +Variable,+,I_sub1_10px,Icon, +Variable,+,I_subplaylist_10px,Icon, +Variable,+,I_subrem_10px,Icon, +Variable,+,I_u2f_10px,Icon, +Variable,+,I_unknown_10px,Icon, +Variable,+,I_update_10px,Icon, Variable,-,MSIRangeTable,const uint32_t[16], Variable,-,SmpsPrescalerTable,const uint32_t[4][6], Variable,+,SystemCoreClock,uint32_t, @@ -3896,6 +3597,7 @@ Variable,+,message_force_vibro_setting_off,const NotificationMessage, Variable,+,message_force_vibro_setting_on,const NotificationMessage, Variable,+,message_green_0,const NotificationMessage, Variable,+,message_green_255,const NotificationMessage, +Variable,+,message_lcd_contrast_update,const NotificationMessage, Variable,+,message_note_a0,const NotificationMessage, Variable,+,message_note_a1,const NotificationMessage, Variable,+,message_note_a2,const NotificationMessage, @@ -4039,6 +3741,7 @@ Variable,+,sequence_display_backlight_off_delay_1000,const NotificationSequence, Variable,+,sequence_display_backlight_on,const NotificationSequence, Variable,+,sequence_double_vibro,const NotificationSequence, Variable,+,sequence_error,const NotificationSequence, +Variable,+,sequence_lcd_contrast_update,const NotificationSequence, Variable,+,sequence_not_charging,const NotificationSequence, Variable,+,sequence_reset_blue,const NotificationSequence, Variable,+,sequence_reset_display,const NotificationSequence, @@ -4057,133 +3760,10 @@ Variable,+,sequence_set_vibro_on,const NotificationSequence, Variable,+,sequence_single_vibro,const NotificationSequence, Variable,+,sequence_solid_yellow,const NotificationSequence, Variable,+,sequence_success,const NotificationSequence, -Variable,-,subghz_protocol_alutech_at_4n,const SubGhzProtocol, -Variable,-,subghz_protocol_alutech_at_4n_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_alutech_at_4n_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_ansonic,const SubGhzProtocol, -Variable,-,subghz_protocol_ansonic_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_ansonic_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_bett,const SubGhzProtocol, -Variable,-,subghz_protocol_bett_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_bett_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_bin_raw,const SubGhzProtocol, -Variable,-,subghz_protocol_bin_raw_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_bin_raw_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_came,const SubGhzProtocol, -Variable,-,subghz_protocol_came_atomo,const SubGhzProtocol, -Variable,-,subghz_protocol_came_atomo_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_came_atomo_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_came_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_came_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_came_twee,const SubGhzProtocol, -Variable,-,subghz_protocol_came_twee_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_came_twee_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_chamb_code,const SubGhzProtocol, -Variable,-,subghz_protocol_chamb_code_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_chamb_code_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_clemsa,const SubGhzProtocol, -Variable,-,subghz_protocol_clemsa_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_clemsa_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_doitrand,const SubGhzProtocol, -Variable,-,subghz_protocol_doitrand_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_doitrand_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_dooya,const SubGhzProtocol, -Variable,-,subghz_protocol_dooya_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_dooya_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_faac_slh,const SubGhzProtocol, -Variable,-,subghz_protocol_faac_slh_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_faac_slh_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_gate_tx,const SubGhzProtocol, -Variable,-,subghz_protocol_gate_tx_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_gate_tx_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_holtek,const SubGhzProtocol, -Variable,-,subghz_protocol_holtek_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_holtek_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_holtek_th12x,const SubGhzProtocol, -Variable,-,subghz_protocol_holtek_th12x_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_holtek_th12x_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_honeywell_wdb,const SubGhzProtocol, -Variable,-,subghz_protocol_honeywell_wdb_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_honeywell_wdb_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_hormann,const SubGhzProtocol, -Variable,-,subghz_protocol_hormann_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_hormann_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_ido,const SubGhzProtocol, -Variable,-,subghz_protocol_ido_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_ido_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_intertechno_v3,const SubGhzProtocol, -Variable,-,subghz_protocol_intertechno_v3_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_intertechno_v3_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_keeloq,const SubGhzProtocol, -Variable,-,subghz_protocol_keeloq_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_keeloq_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_kia,const SubGhzProtocol, -Variable,-,subghz_protocol_kia_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_kia_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_kinggates_stylo_4k,const SubGhzProtocol, -Variable,-,subghz_protocol_kinggates_stylo_4k_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_kinggates_stylo_4k_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_linear,const SubGhzProtocol, -Variable,-,subghz_protocol_linear_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_linear_delta3,const SubGhzProtocol, -Variable,-,subghz_protocol_linear_delta3_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_linear_delta3_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_linear_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_magellan,const SubGhzProtocol, -Variable,-,subghz_protocol_magellan_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_magellan_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_marantec,const SubGhzProtocol, -Variable,-,subghz_protocol_marantec_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_marantec_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_megacode,const SubGhzProtocol, -Variable,-,subghz_protocol_megacode_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_megacode_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_nero_radio,const SubGhzProtocol, -Variable,-,subghz_protocol_nero_radio_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_nero_radio_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_nero_sketch,const SubGhzProtocol, -Variable,-,subghz_protocol_nero_sketch_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_nero_sketch_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_nice_flo,const SubGhzProtocol, -Variable,-,subghz_protocol_nice_flo_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_nice_flo_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_nice_flor_s,const SubGhzProtocol, -Variable,-,subghz_protocol_nice_flor_s_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_nice_flor_s_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_phoenix_v2,const SubGhzProtocol, -Variable,-,subghz_protocol_phoenix_v2_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_phoenix_v2_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_power_smart,const SubGhzProtocol, -Variable,-,subghz_protocol_power_smart_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_power_smart_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_princeton,const SubGhzProtocol, -Variable,-,subghz_protocol_princeton_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_princeton_encoder,const SubGhzProtocolEncoder, Variable,+,subghz_protocol_raw,const SubGhzProtocol, Variable,+,subghz_protocol_raw_decoder,const SubGhzProtocolDecoder, Variable,+,subghz_protocol_raw_encoder,const SubGhzProtocolEncoder, Variable,+,subghz_protocol_registry,const SubGhzProtocolRegistry, -Variable,-,subghz_protocol_scher_khan,const SubGhzProtocol, -Variable,-,subghz_protocol_scher_khan_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_scher_khan_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_secplus_v1,const SubGhzProtocol, -Variable,-,subghz_protocol_secplus_v1_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_secplus_v1_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_secplus_v2,const SubGhzProtocol, -Variable,-,subghz_protocol_secplus_v2_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_secplus_v2_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_smc5326,const SubGhzProtocol, -Variable,-,subghz_protocol_smc5326_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_smc5326_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_somfy_keytis,const SubGhzProtocol, -Variable,-,subghz_protocol_somfy_keytis_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_somfy_keytis_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_somfy_telis,const SubGhzProtocol, -Variable,-,subghz_protocol_somfy_telis_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_somfy_telis_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_star_line,const SubGhzProtocol, -Variable,-,subghz_protocol_star_line_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_star_line_encoder,const SubGhzProtocolEncoder, Variable,-,suboptarg,char*, Variable,+,usb_cdc_dual,FuriHalUsbInterface, Variable,+,usb_cdc_single,FuriHalUsbInterface, diff --git a/firmware/targets/f7/ble_glue/app_common.h b/firmware/targets/f7/ble_glue/app_common.h index 214c85acd2..8eaf230859 100644 --- a/firmware/targets/f7/ble_glue/app_common.h +++ b/firmware/targets/f7/ble_glue/app_common.h @@ -33,6 +33,7 @@ extern "C" { #include #include +#include #include "app_conf.h" diff --git a/firmware/targets/f7/ble_glue/app_conf.h b/firmware/targets/f7/ble_glue/app_conf.h index aaa755a362..ee5115cfed 100644 --- a/firmware/targets/f7/ble_glue/app_conf.h +++ b/firmware/targets/f7/ble_glue/app_conf.h @@ -8,6 +8,8 @@ #define CFG_TX_POWER (0x19) /* +0dBm */ +#define CFG_IDENTITY_ADDRESS GAP_PUBLIC_ADDR + /** * Define Advertising parameters */ diff --git a/firmware/targets/f7/ble_glue/app_debug.c b/firmware/targets/f7/ble_glue/app_debug.c index 78e789ac30..b443bee21f 100644 --- a/firmware/targets/f7/ble_glue/app_debug.c +++ b/firmware/targets/f7/ble_glue/app_debug.c @@ -33,7 +33,8 @@ PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static SHCI_C2_DEBUG_TracesConfig_t APPD_TracesConfig = {0, 0, 0, 0}; PLACE_IN_SECTION("MB_MEM2") ALIGN(4) -static SHCI_C2_DEBUG_GeneralConfig_t APPD_GeneralConfig = {BLE_DTB_CFG, SYS_DBG_CFG1, {0, 0}}; +static SHCI_C2_DEBUG_GeneralConfig_t APPD_GeneralConfig = + {BLE_DTB_CFG, SYS_DBG_CFG1, {0, 0}, 0, 0, 0, 0, 0}; /** * THE DEBUG ON GPIO FOR CPU2 IS INTENDED TO BE USED ONLY ON REQUEST FROM ST SUPPORT diff --git a/firmware/targets/f7/ble_glue/ble_app.c b/firmware/targets/f7/ble_glue/ble_app.c index 4fc4d521be..37d8f7cd04 100644 --- a/firmware/targets/f7/ble_glue/ble_app.c +++ b/firmware/targets/f7/ble_glue/ble_app.c @@ -18,8 +18,8 @@ PLACE_IN_SECTION("MB_MEM1") ALIGN(4) static TL_CmdPacket_t ble_app_cmd_buffer; PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static uint32_t ble_app_nvm[BLE_NVM_SRAM_SIZE]; _Static_assert( - sizeof(SHCI_C2_Ble_Init_Cmd_Packet_t) == 49, - "Ble stack config structure size mismatch"); + sizeof(SHCI_C2_Ble_Init_Cmd_Packet_t) == 57, + "Ble stack config structure size mismatch (check new config options - last updated for v.1.15.0)"); typedef struct { FuriMutex* hci_mtx; @@ -88,6 +88,12 @@ bool ble_app_init() { .min_tx_power = 0, .max_tx_power = 0, .rx_model_config = 1, + /* New stack (13.3->15.0) */ + .max_adv_set_nbr = 1, // Only used if SHCI_C2_BLE_INIT_OPTIONS_EXT_ADV is set + .max_adv_data_len = 31, // Only used if SHCI_C2_BLE_INIT_OPTIONS_EXT_ADV is set + .tx_path_compens = 0, // RF TX Path Compensation, * 0.1 dB + .rx_path_compens = 0, // RF RX Path Compensation, * 0.1 dB + .ble_core_version = 11, // BLE Core Version: 11(5.2), 12(5.3) }}; status = SHCI_C2_BLE_Init(&ble_init_cmd_packet); if(status) { diff --git a/firmware/targets/f7/ble_glue/ble_const.h b/firmware/targets/f7/ble_glue/ble_const.h index 0e4c8b398d..85f734b62c 100644 --- a/firmware/targets/f7/ble_glue/ble_const.h +++ b/firmware/targets/f7/ble_glue/ble_const.h @@ -23,6 +23,7 @@ #include #include #include "osal.h" +#include "compiler.h" /* Default BLE variant */ #ifndef BASIC_FEATURES @@ -34,6 +35,9 @@ #ifndef LL_ONLY #define LL_ONLY 0 #endif +#ifndef LL_ONLY_BASIC +#define LL_ONLY_BASIC 0 +#endif #ifndef BEACON_ONLY #define BEACON_ONLY 0 #endif diff --git a/firmware/targets/f7/ble_glue/compiler.h b/firmware/targets/f7/ble_glue/compiler.h index 1c39628197..98a93d7126 100644 --- a/firmware/targets/f7/ble_glue/compiler.h +++ b/firmware/targets/f7/ble_glue/compiler.h @@ -5,7 +5,7 @@ ***************************************************************************** * @attention * - * Copyright (c) 2018-2022 STMicroelectronics. + * Copyright (c) 2018-2023 STMicroelectronics. * All rights reserved. * * This software is licensed under terms that can be found in the LICENSE file @@ -18,6 +18,14 @@ #ifndef COMPILER_H__ #define COMPILER_H__ +#ifndef __PACKED_STRUCT +#define __PACKED_STRUCT PACKED(struct) +#endif + +#ifndef __PACKED_UNION +#define __PACKED_UNION PACKED(union) +#endif + /** * @brief This is the section dedicated to IAR toolchain */ diff --git a/firmware/targets/f7/ble_glue/gap.c b/firmware/targets/f7/ble_glue/gap.c index 9dfb5af89c..94c556e544 100644 --- a/firmware/targets/f7/ble_glue/gap.c +++ b/firmware/targets/f7/ble_glue/gap.c @@ -1,5 +1,6 @@ #include "gap.h" +#include "app_common.h" #include #include @@ -100,7 +101,7 @@ static void gap_verify_connection_parameters(Gap* gap) { SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { hci_event_pckt* event_pckt; evt_le_meta_event* meta_evt; - evt_blue_aci* blue_evt; + evt_blecore_aci* blue_evt; hci_le_phy_update_complete_event_rp0* evt_le_phy_update_complete; uint8_t tx_phy; uint8_t rx_phy; @@ -112,7 +113,7 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { furi_mutex_acquire(gap->state_mutex, FuriWaitForever); } switch(event_pckt->evt) { - case EVT_DISCONN_COMPLETE: { + case HCI_DISCONNECTION_COMPLETE_EVT_CODE: { hci_disconnection_complete_event_rp0* disconnection_complete_event = (hci_disconnection_complete_event_rp0*)event_pckt->data; if(disconnection_complete_event->Connection_Handle == gap->service.connection_handle) { @@ -131,10 +132,10 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { gap->on_event_cb(event, gap->context); } break; - case EVT_LE_META_EVENT: + case HCI_LE_META_EVT_CODE: meta_evt = (evt_le_meta_event*)event_pckt->data; switch(meta_evt->subevent) { - case EVT_LE_CONN_UPDATE_COMPLETE: { + case HCI_LE_CONNECTION_UPDATE_COMPLETE_SUBEVT_CODE: { hci_le_connection_update_complete_event_rp0* event = (hci_le_connection_update_complete_event_rp0*)meta_evt->data; gap->connection_params.conn_interval = event->Conn_Interval; @@ -148,7 +149,7 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { break; } - case EVT_LE_PHY_UPDATE_COMPLETE: + case HCI_LE_PHY_UPDATE_COMPLETE_SUBEVT_CODE: evt_le_phy_update_complete = (hci_le_phy_update_complete_event_rp0*)meta_evt->data; if(evt_le_phy_update_complete->Status) { FURI_LOG_E( @@ -164,7 +165,7 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { } break; - case EVT_LE_CONN_COMPLETE: { + case HCI_LE_CONNECTION_COMPLETE_SUBEVT_CODE: { hci_le_connection_complete_event_rp0* event = (hci_le_connection_complete_event_rp0*)meta_evt->data; gap->connection_params.conn_interval = event->Conn_Interval; @@ -191,16 +192,16 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { } break; - case EVT_VENDOR: - blue_evt = (evt_blue_aci*)event_pckt->data; + case HCI_VENDOR_SPECIFIC_DEBUG_EVT_CODE: + blue_evt = (evt_blecore_aci*)event_pckt->data; switch(blue_evt->ecode) { aci_gap_pairing_complete_event_rp0* pairing_complete; - case EVT_BLUE_GAP_LIMITED_DISCOVERABLE: + case ACI_GAP_LIMITED_DISCOVERABLE_VSEVT_CODE: FURI_LOG_I(TAG, "Limited discoverable event"); break; - case EVT_BLUE_GAP_PASS_KEY_REQUEST: { + case ACI_GAP_PASS_KEY_REQ_VSEVT_CODE: { // Generate random PIN code uint32_t pin = rand() % 999999; //-V1064 aci_gap_pass_key_resp(gap->service.connection_handle, pin); @@ -213,7 +214,7 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { gap->on_event_cb(event, gap->context); } break; - case EVT_BLUE_ATT_EXCHANGE_MTU_RESP: { + case ACI_ATT_EXCHANGE_MTU_RESP_VSEVT_CODE: { aci_att_exchange_mtu_resp_event_rp0* pr = (void*)blue_evt->data; FURI_LOG_I(TAG, "Rx MTU size: %d", pr->Server_RX_MTU); // Set maximum packet size given header size is 3 bytes @@ -222,32 +223,28 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { gap->on_event_cb(event, gap->context); } break; - case EVT_BLUE_GAP_AUTHORIZATION_REQUEST: + case ACI_GAP_AUTHORIZATION_REQ_VSEVT_CODE: FURI_LOG_D(TAG, "Authorization request event"); break; - case EVT_BLUE_GAP_SLAVE_SECURITY_INITIATED: + case ACI_GAP_SLAVE_SECURITY_INITIATED_VSEVT_CODE: FURI_LOG_D(TAG, "Slave security initiated"); break; - case EVT_BLUE_GAP_BOND_LOST: + case ACI_GAP_BOND_LOST_VSEVT_CODE: FURI_LOG_D(TAG, "Bond lost event. Start rebonding"); aci_gap_allow_rebond(gap->service.connection_handle); break; - case EVT_BLUE_GAP_DEVICE_FOUND: - FURI_LOG_D(TAG, "Device found event"); - break; - - case EVT_BLUE_GAP_ADDR_NOT_RESOLVED: + case ACI_GAP_ADDR_NOT_RESOLVED_VSEVT_CODE: FURI_LOG_D(TAG, "Address not resolved event"); break; - case EVT_BLUE_GAP_KEYPRESS_NOTIFICATION: + case ACI_GAP_KEYPRESS_NOTIFICATION_VSEVT_CODE: FURI_LOG_D(TAG, "Key press notification event"); break; - case EVT_BLUE_GAP_NUMERIC_COMPARISON_VALUE: { + case ACI_GAP_NUMERIC_COMPARISON_VALUE_VSEVT_CODE: { uint32_t pin = ((aci_gap_numeric_comparison_value_event_rp0*)(blue_evt->data))->Numeric_Value; FURI_LOG_I(TAG, "Verify numeric comparison: %06lu", pin); @@ -257,7 +254,7 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { break; } - case EVT_BLUE_GAP_PAIRING_CMPLT: + case ACI_GAP_PAIRING_COMPLETE_VSEVT_CODE: pairing_complete = (aci_gap_pairing_complete_event_rp0*)blue_evt->data; if(pairing_complete->Status) { FURI_LOG_E( @@ -275,11 +272,11 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { } break; - case EVT_BLUE_GAP_PROCEDURE_COMPLETE: + case ACI_L2CAP_CONNECTION_UPDATE_RESP_VSEVT_CODE: FURI_LOG_D(TAG, "Procedure complete event"); break; - case EVT_BLUE_L2CAP_CONNECTION_UPDATE_RESP: { + case ACI_L2CAP_CONNECTION_UPDATE_REQ_VSEVT_CODE: { uint16_t result = ((aci_l2cap_connection_update_resp_event_rp0*)(blue_evt->data))->Result; if(result == 0) { @@ -357,6 +354,7 @@ static void gap_init_svc(Gap* gap) { if(status) { FURI_LOG_E(TAG, "Failed updating name characteristic: %d", status); } + uint8_t gap_appearence_char_uuid[2] = { gap->config->appearance_char & 0xff, gap->config->appearance_char >> 8}; status = aci_gatt_update_char_value( @@ -402,7 +400,7 @@ static void gap_init_svc(Gap* gap) { CFG_ENCRYPTION_KEY_SIZE_MAX, conf_used_fixed_pin, // 0x0 for no pin 0, - PUBLIC_ADDR); + CFG_IDENTITY_ADDRESS); // Configure whitelist aci_gap_configure_whitelist(); } @@ -437,7 +435,7 @@ static void gap_advertise_start(GapState new_state) { ADV_IND, min_interval, max_interval, - PUBLIC_ADDR, + CFG_IDENTITY_ADDRESS, 0, strlen(gap->service.adv_name), (uint8_t*)gap->service.adv_name, diff --git a/firmware/targets/f7/furi_hal/furi_hal_bt.c b/firmware/targets/f7/furi_hal/furi_hal_bt.c index 05fbeceba1..3bd93552c1 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_bt.c +++ b/firmware/targets/f7/furi_hal/furi_hal_bt.c @@ -14,16 +14,22 @@ #define TAG "FuriHalBt" -#define FURI_HAL_BT_DEFAULT_MAC_ADDR \ - { 0x6c, 0x7a, 0xd8, 0xac, 0x57, 0x72 } - /* Time, in ms, to wait for mode transition before crashing */ #define C2_MODE_SWITCH_TIMEOUT 10000 #define FURI_HAL_BT_HARDFAULT_INFO_MAGIC 0x1170FD0F -FuriMutex* furi_hal_bt_core2_mtx = NULL; -static FuriHalBtStack furi_hal_bt_stack = FuriHalBtStackUnknown; +typedef struct { + FuriMutex* core2_mtx; + FuriTimer* hardfault_check_timer; + FuriHalBtStack stack; +} FuriHalBt; + +static FuriHalBt furi_hal_bt = { + .core2_mtx = NULL, + .hardfault_check_timer = NULL, + .stack = FuriHalBtStackUnknown, +}; typedef void (*FuriHalBtProfileStart)(void); typedef void (*FuriHalBtProfileStop)(void); @@ -80,6 +86,13 @@ FuriHalBtProfileConfig profile_config[FuriHalBtProfileNumber] = { }; FuriHalBtProfileConfig* current_profile = NULL; +static void furi_hal_bt_hardfault_check(void* context) { + UNUSED(context); + if(furi_hal_bt_get_hardfault_info()) { + furi_crash("ST(R) Copro(R) HardFault"); + } +} + void furi_hal_bt_init() { furi_hal_bus_enable(FuriHalBusHSEM); furi_hal_bus_enable(FuriHalBusIPCC); @@ -87,9 +100,15 @@ void furi_hal_bt_init() { furi_hal_bus_enable(FuriHalBusPKA); furi_hal_bus_enable(FuriHalBusCRC); - if(!furi_hal_bt_core2_mtx) { - furi_hal_bt_core2_mtx = furi_mutex_alloc(FuriMutexTypeNormal); - furi_assert(furi_hal_bt_core2_mtx); + if(!furi_hal_bt.core2_mtx) { + furi_hal_bt.core2_mtx = furi_mutex_alloc(FuriMutexTypeNormal); + furi_assert(furi_hal_bt.core2_mtx); + } + + if(!furi_hal_bt.hardfault_check_timer) { + furi_hal_bt.hardfault_check_timer = + furi_timer_alloc(furi_hal_bt_hardfault_check, FuriTimerTypePeriodic, NULL); + furi_timer_start(furi_hal_bt.hardfault_check_timer, 5000); } // Explicitly tell that we are in charge of CLK48 domain @@ -100,13 +119,13 @@ void furi_hal_bt_init() { } void furi_hal_bt_lock_core2() { - furi_assert(furi_hal_bt_core2_mtx); - furi_check(furi_mutex_acquire(furi_hal_bt_core2_mtx, FuriWaitForever) == FuriStatusOk); + furi_assert(furi_hal_bt.core2_mtx); + furi_check(furi_mutex_acquire(furi_hal_bt.core2_mtx, FuriWaitForever) == FuriStatusOk); } void furi_hal_bt_unlock_core2() { - furi_assert(furi_hal_bt_core2_mtx); - furi_check(furi_mutex_release(furi_hal_bt_core2_mtx) == FuriStatusOk); + furi_assert(furi_hal_bt.core2_mtx); + furi_check(furi_mutex_release(furi_hal_bt.core2_mtx) == FuriStatusOk); } static bool furi_hal_bt_radio_stack_is_supported(const BleGlueC2Info* info) { @@ -114,26 +133,26 @@ static bool furi_hal_bt_radio_stack_is_supported(const BleGlueC2Info* info) { if(info->StackType == INFO_STACK_TYPE_BLE_LIGHT) { if(info->VersionMajor >= FURI_HAL_BT_STACK_VERSION_MAJOR && info->VersionMinor >= FURI_HAL_BT_STACK_VERSION_MINOR) { - furi_hal_bt_stack = FuriHalBtStackLight; + furi_hal_bt.stack = FuriHalBtStackLight; supported = true; } } else if(info->StackType == INFO_STACK_TYPE_BLE_FULL) { if(info->VersionMajor >= FURI_HAL_BT_STACK_VERSION_MAJOR && info->VersionMinor >= FURI_HAL_BT_STACK_VERSION_MINOR) { - furi_hal_bt_stack = FuriHalBtStackFull; + furi_hal_bt.stack = FuriHalBtStackFull; supported = true; } } else { - furi_hal_bt_stack = FuriHalBtStackUnknown; + furi_hal_bt.stack = FuriHalBtStackUnknown; } return supported; } bool furi_hal_bt_start_radio_stack() { bool res = false; - furi_assert(furi_hal_bt_core2_mtx); + furi_assert(furi_hal_bt.core2_mtx); - furi_mutex_acquire(furi_hal_bt_core2_mtx, FuriWaitForever); + furi_mutex_acquire(furi_hal_bt.core2_mtx, FuriWaitForever); // Explicitly tell that we are in charge of CLK48 domain furi_check(LL_HSEM_1StepLock(HSEM, CFG_HW_CLK48_CONFIG_SEMID) == 0); @@ -167,17 +186,17 @@ bool furi_hal_bt_start_radio_stack() { } res = true; } while(false); - furi_mutex_release(furi_hal_bt_core2_mtx); + furi_mutex_release(furi_hal_bt.core2_mtx); return res; } FuriHalBtStack furi_hal_bt_get_radio_stack() { - return furi_hal_bt_stack; + return furi_hal_bt.stack; } bool furi_hal_bt_is_ble_gatt_gap_supported() { - if(furi_hal_bt_stack == FuriHalBtStackLight || furi_hal_bt_stack == FuriHalBtStackFull) { + if(furi_hal_bt.stack == FuriHalBtStackLight || furi_hal_bt.stack == FuriHalBtStackFull) { return true; } else { return false; @@ -185,7 +204,7 @@ bool furi_hal_bt_is_ble_gatt_gap_supported() { } bool furi_hal_bt_is_testing_supported() { - if(furi_hal_bt_stack == FuriHalBtStackFull) { + if(furi_hal_bt.stack == FuriHalBtStackFull) { return true; } else { return false; @@ -221,12 +240,13 @@ bool furi_hal_bt_start_app(FuriHalBtProfile profile, GapEventCallback event_cb, config->adv_service_uuid |= furi_hal_version_get_hw_color(); } else if(profile == FuriHalBtProfileHidKeyboard) { // Change MAC address for HID profile - uint8_t default_mac[sizeof(config->mac_address)] = FURI_HAL_BT_DEFAULT_MAC_ADDR; const uint8_t* normal_mac = furi_hal_version_get_ble_mac(); - if(memcmp(config->mac_address, default_mac, sizeof(config->mac_address)) == 0) { + uint8_t empty_mac[sizeof(config->mac_address)] = FURI_HAL_BT_EMPTY_MAC_ADDR; + uint8_t default_mac[sizeof(config->mac_address)] = FURI_HAL_BT_DEFAULT_MAC_ADDR; + if(memcmp(config->mac_address, empty_mac, sizeof(config->mac_address)) == 0 || + memcmp(config->mac_address, normal_mac, sizeof(config->mac_address)) == 0 || + memcmp(config->mac_address, default_mac, sizeof(config->mac_address)) == 0) { memcpy(config->mac_address, normal_mac, sizeof(config->mac_address)); - } - if(memcmp(config->mac_address, normal_mac, sizeof(config->mac_address)) == 0) { config->mac_address[2]++; } // Change name Flipper -> Control @@ -236,8 +256,8 @@ bool furi_hal_bt_start_app(FuriHalBtProfile profile, GapEventCallback event_cb, config->adv_name, FURI_HAL_VERSION_DEVICE_NAME_LENGTH, "%cControl %s", - *furi_hal_version_get_ble_local_device_name_ptr(), - furi_hal_version_get_ble_local_device_name_ptr() + 1); + AD_TYPE_COMPLETE_LOCAL_NAME, + furi_hal_version_get_name_ptr()); } } if(!gap_init(config, event_cb, context)) { @@ -463,6 +483,15 @@ uint32_t furi_hal_bt_get_conn_rssi(uint8_t* rssi) { return since; } +void furi_hal_bt_reverse_mac_addr(uint8_t mac_addr[GAP_MAC_ADDR_SIZE]) { + uint8_t tmp; + for(size_t i = 0; i < GAP_MAC_ADDR_SIZE / 2; i++) { + tmp = mac_addr[i]; + mac_addr[i] = mac_addr[GAP_MAC_ADDR_SIZE - 1 - i]; + mac_addr[GAP_MAC_ADDR_SIZE - 1 - i] = tmp; + } +} + void furi_hal_bt_set_profile_adv_name( FuriHalBtProfile profile, const char name[FURI_HAL_BT_ADV_NAME_LENGTH]) { diff --git a/firmware/targets/f7/furi_hal/furi_hal_clock.c b/firmware/targets/f7/furi_hal/furi_hal_clock.c index 9d228f0f55..736ad9f7c0 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_clock.c +++ b/firmware/targets/f7/furi_hal/furi_hal_clock.c @@ -48,6 +48,10 @@ void furi_hal_clock_init() { LL_RCC_LSI1_Enable(); while(!LS_CLOCK_IS_READY()) ; + + /* RF wakeup */ + LL_RCC_SetRFWKPClockSource(LL_RCC_RFWKP_CLKSOURCE_LSE); + LL_EXTI_EnableIT_0_31( LL_EXTI_LINE_18); /* Why? Because that's why. See RM0434, Table 61. CPU1 vector table. */ LL_EXTI_EnableRisingTrig_0_31(LL_EXTI_LINE_18); @@ -111,7 +115,7 @@ void furi_hal_clock_init() { LL_RCC_SetCLK48ClockSource(LL_RCC_CLK48_CLKSOURCE_PLLSAI1); LL_RCC_HSI_EnableInStopMode(); // Ensure that MR is capable of work in STOP0 - LL_RCC_SetSMPSClockSource(LL_RCC_SMPS_CLKSOURCE_HSE); + LL_RCC_SetSMPSClockSource(LL_RCC_SMPS_CLKSOURCE_HSI); LL_RCC_SetSMPSPrescaler(LL_RCC_SMPS_DIV_1); LL_RCC_SetRFWKPClockSource(LL_RCC_RFWKP_CLKSOURCE_LSE); @@ -124,8 +128,8 @@ void furi_hal_clock_switch_to_hsi() { while(!LL_RCC_HSI_IsReady()) ; - LL_RCC_SetSMPSClockSource(LL_RCC_SMPS_CLKSOURCE_HSI); LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_HSI); + furi_assert(LL_RCC_GetSMPSClockSource() == LL_RCC_SMPS_CLKSOURCE_HSI); while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_HSI) ; @@ -138,6 +142,10 @@ void furi_hal_clock_switch_to_hsi() { } void furi_hal_clock_switch_to_pll() { +#ifdef FURI_HAL_CLOCK_TRACK_STARTUP + uint32_t clock_start_time = DWT->CYCCNT; +#endif + LL_RCC_HSE_Enable(); LL_RCC_PLL_Enable(); LL_RCC_PLLSAI1_Enable(); @@ -156,10 +164,16 @@ void furi_hal_clock_switch_to_pll() { ; LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_PLL); - LL_RCC_SetSMPSClockSource(LL_RCC_SMPS_CLKSOURCE_HSE); while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_PLL) ; + +#ifdef FURI_HAL_CLOCK_TRACK_STARTUP + uint32_t total = DWT->CYCCNT - clock_start_time; + if(total > (20 * 0x148)) { + furi_crash("Slow HSE/PLL startup"); + } +#endif } void furi_hal_clock_suspend_tick() { diff --git a/firmware/targets/f7/furi_hal/furi_hal_info.c b/firmware/targets/f7/furi_hal/furi_hal_info.c index 47672c97a3..a2c9232c05 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_info.c +++ b/firmware/targets/f7/furi_hal/furi_hal_info.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -23,10 +24,10 @@ void furi_hal_info_get(PropertyValueCallback out, char sep, void* context) { // Device Info version if(sep == '.') { property_value_out(&property_context, NULL, 2, "format", "major", "3"); - property_value_out(&property_context, NULL, 2, "format", "minor", "1"); + property_value_out(&property_context, NULL, 2, "format", "minor", "2"); } else { property_value_out(&property_context, NULL, 3, "device", "info", "major", "2"); - property_value_out(&property_context, NULL, 3, "device", "info", "minor", "2"); + property_value_out(&property_context, NULL, 3, "device", "info", "minor", "3"); } // Model name @@ -297,6 +298,18 @@ void furi_hal_info_get(PropertyValueCallback out, char sep, void* context) { property_value_out(&property_context, NULL, 2, "radio", "alive", "false"); } + property_value_out( + &property_context, + "%u", + 2, + "system", + "debug", + furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)); + property_value_out( + &property_context, "%u", 3, "system", "heap", "track", furi_hal_rtc_get_heap_track_mode()); + property_value_out( + &property_context, "%u", 3, "system", "log", "level", furi_hal_rtc_get_log_level()); + property_value_out( &property_context, "%u", 3, "protobuf", "version", "major", PROTOBUF_MAJOR_VERSION); property_context.last = true; diff --git a/firmware/targets/f7/furi_hal/furi_hal_light.c b/firmware/targets/f7/furi_hal/furi_hal_light.c index 76b401d952..2673b8a58d 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_light.c +++ b/firmware/targets/f7/furi_hal/furi_hal_light.c @@ -97,26 +97,39 @@ void furi_hal_light_blink_set_color(Light light) { void furi_hal_light_sequence(const char* sequence) { do { - if(*sequence == 'R') { + switch(*sequence) { + case 'R': furi_hal_light_set(LightRed, 0xFF); - } else if(*sequence == 'r') { + break; + case 'r': furi_hal_light_set(LightRed, 0x00); - } else if(*sequence == 'G') { + break; + case 'G': furi_hal_light_set(LightGreen, 0xFF); - } else if(*sequence == 'g') { + break; + case 'g': furi_hal_light_set(LightGreen, 0x00); - } else if(*sequence == 'B') { + break; + case 'B': furi_hal_light_set(LightBlue, 0xFF); - } else if(*sequence == 'b') { + break; + case 'b': furi_hal_light_set(LightBlue, 0x00); - } else if(*sequence == 'W') { + break; + case 'W': furi_hal_light_set(LightBacklight, 0xFF); - } else if(*sequence == 'w') { + break; + case 'w': furi_hal_light_set(LightBacklight, 0x00); - } else if(*sequence == '.') { + break; + case '.': furi_delay_ms(250); - } else if(*sequence == '-') { + break; + case '-': furi_delay_ms(500); + break; + default: + break; } sequence++; } while(*sequence != 0); diff --git a/firmware/targets/f7/furi_hal/furi_hal_memory.c b/firmware/targets/f7/furi_hal/furi_hal_memory.c index 9716f1e529..7f69b90ca2 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_memory.c +++ b/firmware/targets/f7/furi_hal/furi_hal_memory.c @@ -4,6 +4,9 @@ #define TAG "FuriHalMemory" +// STM(TM) Copro(TM) bug(TM) workaround size +#define RAM2B_COPRO_GAP_SIZE_KB 2 + typedef enum { SRAM_A, SRAM_B, @@ -30,53 +33,47 @@ void furi_hal_memory_init() { return; } - if(!ble_glue_wait_for_c2_start(FURI_HAL_BT_C2_START_TIMEOUT)) { - FURI_LOG_E(TAG, "C2 start timeout"); - return; - } - FuriHalMemory* memory = malloc(sizeof(FuriHalMemory)); - const BleGlueC2Info* c2_ver = ble_glue_get_c2_info(); + uint32_t sbrsa = (FLASH->SRRVR & FLASH_SRRVR_SBRSA_Msk) >> FLASH_SRRVR_SBRSA_Pos; + uint32_t snbrsa = (FLASH->SRRVR & FLASH_SRRVR_SNBRSA_Msk) >> FLASH_SRRVR_SNBRSA_Pos; + + uint32_t sram2a_busy_size = (uint32_t)&__sram2a_free__ - (uint32_t)&__sram2a_start__; + uint32_t sram2a_unprotected_size = (sbrsa)*1024; + uint32_t sram2b_unprotected_size = (snbrsa)*1024; - if(c2_ver->mode == BleGlueC2ModeStack) { - uint32_t sram2a_busy_size = (uint32_t)&__sram2a_free__ - (uint32_t)&__sram2a_start__; - uint32_t sram2a_unprotected_size = (32 - c2_ver->MemorySizeSram2A) * 1024; - uint32_t sram2b_unprotected_size = (32 - c2_ver->MemorySizeSram2B) * 1024; + // STM(TM) Copro(TM) bug(TM) workaround + sram2b_unprotected_size -= 1024 * RAM2B_COPRO_GAP_SIZE_KB; - memory->region[SRAM_A].start = (uint8_t*)&__sram2a_free__; - memory->region[SRAM_B].start = (uint8_t*)&__sram2b_start__; + memory->region[SRAM_A].start = (uint8_t*)&__sram2a_free__; + memory->region[SRAM_B].start = (uint8_t*)&__sram2b_start__; - if(sram2a_unprotected_size > sram2a_busy_size) { - memory->region[SRAM_A].size = sram2a_unprotected_size - sram2a_busy_size; - } else { - memory->region[SRAM_A].size = 0; + if(sram2a_unprotected_size > sram2a_busy_size) { + memory->region[SRAM_A].size = sram2a_unprotected_size - sram2a_busy_size; + } else { + memory->region[SRAM_A].size = 0; + } + memory->region[SRAM_B].size = sram2b_unprotected_size; + + FURI_LOG_I( + TAG, "SRAM2A: 0x%p, %lu", memory->region[SRAM_A].start, memory->region[SRAM_A].size); + FURI_LOG_I( + TAG, "SRAM2B: 0x%p, %lu", memory->region[SRAM_B].start, memory->region[SRAM_B].size); + + if((memory->region[SRAM_A].size > 0) || (memory->region[SRAM_B].size > 0)) { + if((memory->region[SRAM_A].size > 0)) { + FURI_LOG_I(TAG, "SRAM2A clear"); + memset(memory->region[SRAM_A].start, 0, memory->region[SRAM_A].size); } - memory->region[SRAM_B].size = sram2b_unprotected_size; - - FURI_LOG_I( - TAG, "SRAM2A: 0x%p, %lu", memory->region[SRAM_A].start, memory->region[SRAM_A].size); - FURI_LOG_I( - TAG, "SRAM2B: 0x%p, %lu", memory->region[SRAM_B].start, memory->region[SRAM_B].size); - - if((memory->region[SRAM_A].size > 0) || (memory->region[SRAM_B].size > 0)) { - if((memory->region[SRAM_A].size > 0)) { - FURI_LOG_I(TAG, "SRAM2A clear"); - memset(memory->region[SRAM_A].start, 0, memory->region[SRAM_A].size); - } - if((memory->region[SRAM_B].size > 0)) { - FURI_LOG_I(TAG, "SRAM2B clear"); - memset(memory->region[SRAM_B].start, 0, memory->region[SRAM_B].size); - } - furi_hal_memory = memory; - FURI_LOG_I(TAG, "Enabled"); - } else { - free(memory); - FURI_LOG_E(TAG, "No SRAM2 available"); + if((memory->region[SRAM_B].size > 0)) { + FURI_LOG_I(TAG, "SRAM2B clear"); + memset(memory->region[SRAM_B].start, 0, memory->region[SRAM_B].size); } + furi_hal_memory = memory; + FURI_LOG_I(TAG, "Enabled"); } else { free(memory); - FURI_LOG_E(TAG, "No Core2 available"); + FURI_LOG_E(TAG, "No SRAM2 available"); } } @@ -89,15 +86,20 @@ void* furi_hal_memory_alloc(size_t size) { return NULL; } + void* allocated_memory = NULL; + FURI_CRITICAL_ENTER(); for(int i = 0; i < SRAM_MAX; i++) { if(furi_hal_memory->region[i].size >= size) { void* ptr = furi_hal_memory->region[i].start; furi_hal_memory->region[i].start += size; furi_hal_memory->region[i].size -= size; - return ptr; + allocated_memory = ptr; + break; } } - return NULL; + FURI_CRITICAL_EXIT(); + + return allocated_memory; } size_t furi_hal_memory_get_free() { diff --git a/firmware/targets/f7/furi_hal/furi_hal_nfc.c b/firmware/targets/f7/furi_hal/furi_hal_nfc.c index ed04a206fa..cdf31954fa 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_nfc.c +++ b/firmware/targets/f7/furi_hal/furi_hal_nfc.c @@ -482,7 +482,7 @@ bool furi_hal_nfc_emulate_nfca( buff_tx, buff_tx_len, buff_rx, - sizeof(buff_rx), + rfalConvBytesToBits(buff_rx_size), &buff_rx_len, data_type, RFAL_FWT_NONE); @@ -506,7 +506,7 @@ bool furi_hal_nfc_emulate_nfca( buff_tx, buff_tx_len, buff_rx, - sizeof(buff_rx), + rfalConvBytesToBits(buff_rx_size), &buff_rx_len, data_type, RFAL_FWT_NONE); @@ -834,3 +834,17 @@ FuriHalNfcReturn furi_hal_nfc_ll_txrx_bits( void furi_hal_nfc_ll_poll() { rfalWorker(); } + +void furi_hal_nfc_field_detect_start() { + st25r3916WriteRegister( + ST25R3916_REG_OP_CONTROL, + ST25R3916_REG_OP_CONTROL_en | ST25R3916_REG_OP_CONTROL_en_fd_mask); + st25r3916WriteRegister(ST25R3916_REG_MODE, ST25R3916_REG_MODE_targ | ST25R3916_REG_MODE_om0); +} + +bool furi_hal_nfc_field_is_present() { + return st25r3916CheckReg( + ST25R3916_REG_AUX_DISPLAY, + ST25R3916_REG_AUX_DISPLAY_efd_o, + ST25R3916_REG_AUX_DISPLAY_efd_o); +} \ No newline at end of file diff --git a/firmware/targets/f7/furi_hal/furi_hal_nfc.h b/firmware/targets/f7/furi_hal/furi_hal_nfc.h index 0d3a043fa3..e970f345dd 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_nfc.h +++ b/firmware/targets/f7/furi_hal/furi_hal_nfc.h @@ -434,6 +434,10 @@ FuriHalNfcReturn furi_hal_nfc_ll_txrx_bits( void furi_hal_nfc_ll_poll(); +void furi_hal_nfc_field_detect_start(); + +bool furi_hal_nfc_field_is_present(); + #ifdef __cplusplus } #endif diff --git a/firmware/targets/f7/furi_hal/furi_hal_pwm.c b/firmware/targets/f7/furi_hal/furi_hal_pwm.c index 7e985cbb11..879460e6bd 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_pwm.c +++ b/firmware/targets/f7/furi_hal/furi_hal_pwm.c @@ -82,6 +82,15 @@ void furi_hal_pwm_stop(FuriHalPwmOutputId channel) { } } +bool furi_hal_pwm_is_running(FuriHalPwmOutputId channel) { + if(channel == FuriHalPwmOutputIdTim1PA7) { + return furi_hal_bus_is_enabled(FuriHalBusTIM1); + } else if(channel == FuriHalPwmOutputIdLptim2PA4) { + return furi_hal_bus_is_enabled(FuriHalBusLPTIM2); + } + return false; +} + void furi_hal_pwm_set_params(FuriHalPwmOutputId channel, uint32_t freq, uint8_t duty) { furi_assert(freq > 0); uint32_t freq_div = 64000000LU / freq; diff --git a/firmware/targets/f7/furi_hal/furi_hal_pwm.h b/firmware/targets/f7/furi_hal/furi_hal_pwm.h index a8682c5fbb..16acca05ef 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_pwm.h +++ b/firmware/targets/f7/furi_hal/furi_hal_pwm.h @@ -9,6 +9,7 @@ extern "C" { #endif #include +#include typedef enum { FuriHalPwmOutputIdTim1PA7, @@ -37,6 +38,13 @@ void furi_hal_pwm_stop(FuriHalPwmOutputId channel); */ void furi_hal_pwm_set_params(FuriHalPwmOutputId channel, uint32_t freq, uint8_t duty); +/** Is PWM channel running? + * + * @param[in] channel PWM channel (FuriHalPwmOutputId) + * @return bool - true if running +*/ +bool furi_hal_pwm_is_running(FuriHalPwmOutputId channel); + #ifdef __cplusplus } #endif diff --git a/firmware/targets/f7/furi_hal/furi_hal_resources.c b/firmware/targets/f7/furi_hal/furi_hal_resources.c index 9d6cd7cafa..63507bd7b8 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_resources.c +++ b/firmware/targets/f7/furi_hal/furi_hal_resources.c @@ -86,6 +86,7 @@ const GpioPinRecord gpio_pins[] = { /* Dangerous pins, may damage hardware */ {.pin = &gpio_usart_rx, .name = "PB7", .debug = true}, {.pin = &gpio_speaker, .name = "PB8", .debug = true}, + {.pin = &gpio_infrared_tx, .name = "PB9", .debug = true}, }; const size_t gpio_pins_count = sizeof(gpio_pins) / sizeof(GpioPinRecord); diff --git a/firmware/targets/f7/furi_hal/furi_hal_rfid.c b/firmware/targets/f7/furi_hal/furi_hal_rfid.c index fa0c19b098..67f11d6ff7 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_rfid.c +++ b/firmware/targets/f7/furi_hal/furi_hal_rfid.c @@ -25,6 +25,19 @@ #define RFID_CAPTURE_IND_CH LL_TIM_CHANNEL_CH3 #define RFID_CAPTURE_DIR_CH LL_TIM_CHANNEL_CH4 +// Field presence detection +#define FURI_HAL_RFID_FIELD_FREQUENCY_MIN 80000 +#define FURI_HAL_RFID_FIELD_FREQUENCY_MAX 200000 + +#define FURI_HAL_RFID_FIELD_COUNTER_TIMER TIM2 +#define FURI_HAL_RFID_FIELD_COUNTER_TIMER_BUS FuriHalBusTIM2 +#define FURI_HAL_RFID_FIELD_COUNTER_TIMER_CHANNEL LL_TIM_CHANNEL_CH3 + +#define FURI_HAL_RFID_FIELD_TIMEOUT_TIMER TIM1 +#define FURI_HAL_RFID_FIELD_TIMEOUT_TIMER_BUS FuriHalBusTIM1 + +#define FURI_HAL_RFID_FIELD_DMAMUX_DMA LL_DMAMUX_REQ_TIM1_UP + /* DMA Channels definition */ #define RFID_DMA DMA2 #define RFID_DMA_CH1_CHANNEL LL_DMA_CHANNEL_1 @@ -33,10 +46,16 @@ #define RFID_DMA_CH1_DEF RFID_DMA, RFID_DMA_CH1_CHANNEL #define RFID_DMA_CH2_DEF RFID_DMA, RFID_DMA_CH2_CHANNEL +typedef struct { + uint32_t counter; + uint32_t set_tim_counter_cnt; +} FuriHalRfidField; + typedef struct { FuriHalRfidDMACallback dma_callback; FuriHalRfidReadCaptureCallback read_capture_callback; void* context; + FuriHalRfidField field; } FuriHalRfid; FuriHalRfid* furi_hal_rfid = NULL; @@ -51,6 +70,8 @@ FuriHalRfid* furi_hal_rfid = NULL; void furi_hal_rfid_init() { furi_assert(furi_hal_rfid == NULL); furi_hal_rfid = malloc(sizeof(FuriHalRfid)); + furi_hal_rfid->field.counter = 0; + furi_hal_rfid->field.set_tim_counter_cnt = 0; furi_hal_rfid_pins_reset(); @@ -133,6 +154,23 @@ static void furi_hal_rfid_pins_read() { furi_hal_gpio_init(&gpio_rfid_data_in, GpioModeAnalog, GpioPullNo, GpioSpeedLow); } +static void furi_hal_rfid_pins_field() { + // ibutton low + furi_hal_ibutton_pin_configure(); + furi_hal_ibutton_pin_write(false); + + // pull pin to timer out + furi_hal_gpio_init(&gpio_nfc_irq_rfid_pull, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_write(&gpio_nfc_irq_rfid_pull, false); + + // pull rfid antenna from carrier side + furi_hal_gpio_init(&gpio_rfid_carrier_out, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_write(&gpio_rfid_carrier_out, false); + + furi_hal_gpio_init_ex( + &gpio_rfid_carrier, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedLow, GpioAltFn2TIM2); +} + void furi_hal_rfid_pin_pull_release() { furi_hal_gpio_write(&gpio_nfc_irq_rfid_pull, true); } @@ -427,3 +465,124 @@ void COMP_IRQHandler() { furi_hal_rfid_comp_callback_context); } } + +static void furi_hal_rfid_field_tim_setup() { + // setup timer counter + furi_hal_bus_enable(FURI_HAL_RFID_FIELD_COUNTER_TIMER_BUS); + + LL_TIM_SetPrescaler(FURI_HAL_RFID_FIELD_COUNTER_TIMER, 0); + LL_TIM_SetCounterMode(FURI_HAL_RFID_FIELD_COUNTER_TIMER, LL_TIM_COUNTERMODE_UP); + LL_TIM_SetAutoReload(FURI_HAL_RFID_FIELD_COUNTER_TIMER, 0xFFFFFFFF); + LL_TIM_DisableARRPreload(FURI_HAL_RFID_FIELD_COUNTER_TIMER); + LL_TIM_SetRepetitionCounter(FURI_HAL_RFID_FIELD_COUNTER_TIMER, 0); + + LL_TIM_SetClockDivision(FURI_HAL_RFID_FIELD_COUNTER_TIMER, LL_TIM_CLOCKDIVISION_DIV1); + LL_TIM_SetClockSource(FURI_HAL_RFID_FIELD_COUNTER_TIMER, LL_TIM_CLOCKSOURCE_EXT_MODE2); + LL_TIM_ConfigETR( + FURI_HAL_RFID_FIELD_COUNTER_TIMER, + LL_TIM_ETR_POLARITY_INVERTED, + LL_TIM_ETR_PRESCALER_DIV1, + LL_TIM_ETR_FILTER_FDIV1); + + LL_TIM_OC_InitTypeDef TIM_OC_InitStruct = {0}; + TIM_OC_InitStruct.OCMode = LL_TIM_OCMODE_PWM1; + TIM_OC_InitStruct.OCState = LL_TIM_OCSTATE_ENABLE; + TIM_OC_InitStruct.CompareValue = 1; + LL_TIM_OC_Init( + FURI_HAL_RFID_FIELD_COUNTER_TIMER, + FURI_HAL_RFID_FIELD_COUNTER_TIMER_CHANNEL, + &TIM_OC_InitStruct); + + LL_TIM_GenerateEvent_UPDATE(FURI_HAL_RFID_FIELD_COUNTER_TIMER); + LL_TIM_OC_SetPolarity( + FURI_HAL_RFID_FIELD_COUNTER_TIMER, + FURI_HAL_RFID_FIELD_COUNTER_TIMER_CHANNEL, + LL_TIM_OCPOLARITY_HIGH); + LL_TIM_EnableDMAReq_UPDATE(FURI_HAL_RFID_FIELD_COUNTER_TIMER); + + // setup timer timeouts dma + furi_hal_bus_enable(FURI_HAL_RFID_FIELD_TIMEOUT_TIMER_BUS); + + LL_TIM_SetPrescaler(FURI_HAL_RFID_FIELD_TIMEOUT_TIMER, 64000 - 1); + LL_TIM_SetCounterMode(FURI_HAL_RFID_FIELD_TIMEOUT_TIMER, LL_TIM_COUNTERMODE_UP); + LL_TIM_SetAutoReload(FURI_HAL_RFID_FIELD_TIMEOUT_TIMER, 100 - 1); // 100 ms + LL_TIM_SetClockDivision(FURI_HAL_RFID_FIELD_TIMEOUT_TIMER, LL_TIM_CLOCKDIVISION_DIV1); + LL_TIM_SetClockSource(FURI_HAL_RFID_FIELD_TIMEOUT_TIMER, LL_TIM_CLOCKSOURCE_INTERNAL); + + LL_TIM_DisableARRPreload(FURI_HAL_RFID_FIELD_TIMEOUT_TIMER); + + LL_TIM_EnableDMAReq_UPDATE(FURI_HAL_RFID_FIELD_TIMEOUT_TIMER); + LL_TIM_GenerateEvent_UPDATE(FURI_HAL_RFID_FIELD_TIMEOUT_TIMER); +} + +void furi_hal_rfid_field_detect_start(void) { + // setup pins + furi_hal_rfid_pins_field(); + + // configure timer + furi_hal_rfid_field_tim_setup(); + + // configure DMA "TIM_COUNTER_CNT -> counter" + LL_DMA_SetMemoryAddress(RFID_DMA_CH1_DEF, (uint32_t) & (furi_hal_rfid->field.counter)); + LL_DMA_SetPeriphAddress( + RFID_DMA_CH1_DEF, (uint32_t) & (FURI_HAL_RFID_FIELD_COUNTER_TIMER->CNT)); + LL_DMA_ConfigTransfer( + RFID_DMA_CH1_DEF, + LL_DMA_DIRECTION_PERIPH_TO_MEMORY | LL_DMA_MODE_CIRCULAR | LL_DMA_PERIPH_NOINCREMENT | + LL_DMA_MEMORY_NOINCREMENT | LL_DMA_PDATAALIGN_WORD | LL_DMA_MDATAALIGN_WORD | + LL_DMA_PRIORITY_MEDIUM); + LL_DMA_SetDataLength(RFID_DMA_CH1_DEF, 1); + LL_DMA_SetPeriphRequest(RFID_DMA_CH1_DEF, FURI_HAL_RFID_FIELD_DMAMUX_DMA); + LL_DMA_EnableChannel(RFID_DMA_CH1_DEF); + + // configure DMA "mem -> TIM_COUNTER_CNT" + LL_DMA_SetMemoryAddress( + RFID_DMA_CH2_DEF, (uint32_t) & (furi_hal_rfid->field.set_tim_counter_cnt)); + LL_DMA_SetPeriphAddress( + RFID_DMA_CH2_DEF, (uint32_t) & (FURI_HAL_RFID_FIELD_COUNTER_TIMER->CNT)); + LL_DMA_ConfigTransfer( + RFID_DMA_CH2_DEF, + LL_DMA_DIRECTION_MEMORY_TO_PERIPH | LL_DMA_MODE_CIRCULAR | LL_DMA_PERIPH_NOINCREMENT | + LL_DMA_MEMORY_NOINCREMENT | LL_DMA_PDATAALIGN_WORD | LL_DMA_MDATAALIGN_WORD | + LL_DMA_PRIORITY_LOW); + LL_DMA_SetDataLength(RFID_DMA_CH2_DEF, 1); + LL_DMA_SetPeriphRequest(RFID_DMA_CH2_DEF, FURI_HAL_RFID_FIELD_DMAMUX_DMA); + LL_DMA_EnableChannel(RFID_DMA_CH2_DEF); + + // start tim counter + LL_TIM_EnableAllOutputs(FURI_HAL_RFID_FIELD_COUNTER_TIMER); + + LL_TIM_SetCounter(FURI_HAL_RFID_FIELD_COUNTER_TIMER, 0); + LL_TIM_EnableCounter(FURI_HAL_RFID_FIELD_COUNTER_TIMER); + + // start tim timeout + LL_TIM_SetCounter(FURI_HAL_RFID_FIELD_TIMEOUT_TIMER, 0); + LL_TIM_EnableCounter(FURI_HAL_RFID_FIELD_TIMEOUT_TIMER); + LL_TIM_EnableIT_UPDATE(FURI_HAL_RFID_FIELD_TIMEOUT_TIMER); +} + +void furi_hal_rfid_field_detect_stop(void) { + LL_TIM_DisableCounter(FURI_HAL_RFID_FIELD_COUNTER_TIMER); + LL_TIM_DisableAllOutputs(FURI_HAL_RFID_FIELD_COUNTER_TIMER); + + LL_TIM_DisableCounter(FURI_HAL_RFID_FIELD_TIMEOUT_TIMER); + + FURI_CRITICAL_ENTER(); + + LL_DMA_DeInit(RFID_DMA_CH1_DEF); + LL_DMA_DeInit(RFID_DMA_CH2_DEF); + + furi_hal_bus_disable(FURI_HAL_RFID_FIELD_COUNTER_TIMER_BUS); + furi_hal_bus_disable(FURI_HAL_RFID_FIELD_TIMEOUT_TIMER_BUS); + + furi_hal_rfid_pins_reset(); + + FURI_CRITICAL_EXIT(); +} + +bool furi_hal_rfid_field_is_present(uint32_t* frequency) { + *frequency = furi_hal_rfid->field.counter * 10; + return ( + (*frequency >= FURI_HAL_RFID_FIELD_FREQUENCY_MIN) && + (*frequency <= FURI_HAL_RFID_FIELD_FREQUENCY_MAX)); +} \ No newline at end of file diff --git a/firmware/targets/f7/furi_hal/furi_hal_rfid.h b/firmware/targets/f7/furi_hal/furi_hal_rfid.h index 78d9b66587..7087ba991f 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_rfid.h +++ b/firmware/targets/f7/furi_hal/furi_hal_rfid.h @@ -87,6 +87,20 @@ typedef void (*FuriHalRfidCompCallback)(bool level, void* context); /** Set comparator callback */ void furi_hal_rfid_comp_set_callback(FuriHalRfidCompCallback callback, void* context); +/** Start/Enable Field Presence detect */ +void furi_hal_rfid_field_detect_start(); + +/** Stop/Disable Field Presence detect */ +void furi_hal_rfid_field_detect_stop(); + +/** Check Field Presence + * + * @param[out] frequency pointer to frequency value to be set if filed detected + * + * @return true if field is present, false if not + */ +bool furi_hal_rfid_field_is_present(uint32_t* frequency); + #ifdef __cplusplus } #endif diff --git a/firmware/targets/f7/furi_hal/furi_hal_subghz.c b/firmware/targets/f7/furi_hal/furi_hal_subghz.c index 7008f387a7..f8c0ad9992 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_subghz.c +++ b/firmware/targets/f7/furi_hal/furi_hal_subghz.c @@ -41,7 +41,6 @@ volatile FuriHalSubGhz furi_hal_subghz = { .cc1101_g0_pin = &gpio_cc1101_g0, .rolling_counter_mult = 1, .ext_module_power_disabled = false, - .timestamp_file_names = false, .extended_frequency_i = false, }; @@ -90,14 +89,6 @@ bool furi_hal_subghz_get_external_power_disable(void) { return furi_hal_subghz.ext_module_power_disabled; } -void furi_hal_subghz_set_timestamp_file_names(bool state) { - furi_hal_subghz.timestamp_file_names = state; -} - -bool furi_hal_subghz_get_timestamp_file_names(void) { - return furi_hal_subghz.timestamp_file_names; -} - void furi_hal_subghz_set_extended_frequency(bool state_i) { furi_hal_subghz.extended_frequency_i = state_i; } diff --git a/firmware/targets/f7/furi_hal/furi_hal_subghz.h b/firmware/targets/f7/furi_hal/furi_hal_subghz.h index d583c90f91..9b6056d77b 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_subghz.h +++ b/firmware/targets/f7/furi_hal/furi_hal_subghz.h @@ -332,14 +332,6 @@ void furi_hal_subghz_set_external_power_disable(bool state); */ bool furi_hal_subghz_get_external_power_disable(void); -/** If true - disable generation of random name and add timestamp to filenames instead - */ -void furi_hal_subghz_set_timestamp_file_names(bool state); - -/** Get the current state of the timestamp instead of random name flag - */ -bool furi_hal_subghz_get_timestamp_file_names(void); - /** Set what radio module we will be using */ void furi_hal_subghz_select_radio_type(SubGhzRadioType state); diff --git a/firmware/targets/f7/furi_hal/furi_hal_usb.c b/firmware/targets/f7/furi_hal/furi_hal_usb.c index b88168d5d0..3f9f7cbee3 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_usb.c +++ b/firmware/targets/f7/furi_hal/furi_hal_usb.c @@ -17,6 +17,7 @@ typedef enum { UsbApiEventTypeSetConfig, UsbApiEventTypeGetConfig, + UsbApiEventTypeGetConfigContext, UsbApiEventTypeLock, UsbApiEventTypeUnlock, UsbApiEventTypeIsLocked, @@ -168,6 +169,21 @@ FuriHalUsbInterface* furi_hal_usb_get_config() { return return_data.void_value; } +void* furi_hal_usb_get_config_context() { + UsbApiEventReturnData return_data = { + .void_value = NULL, + }; + + UsbApiEventMessage msg = { + .lock = api_lock_alloc_locked(), + .type = UsbApiEventTypeGetConfigContext, + .return_data = &return_data, + }; + + furi_hal_usb_send_message(&msg); + return return_data.void_value; +} + void furi_hal_usb_lock() { UsbApiEventMessage msg = { .lock = api_lock_alloc_locked(), @@ -411,6 +427,9 @@ static void usb_process_message(UsbApiEventMessage* message) { case UsbApiEventTypeGetConfig: message->return_data->void_value = usb.interface; break; + case UsbApiEventTypeGetConfigContext: + message->return_data->void_value = usb.interface_context; + break; case UsbApiEventTypeLock: FURI_LOG_I(TAG, "Mode lock"); usb.mode_lock = true; diff --git a/firmware/targets/f7/furi_hal/furi_hal_usb_hid.c b/firmware/targets/f7/furi_hal/furi_hal_usb_hid.c index 334aa01026..a7a5742ef8 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_usb_hid.c +++ b/firmware/targets/f7/furi_hal/furi_hal_usb_hid.c @@ -12,9 +12,6 @@ #define HID_INTERVAL 2 -#define HID_VID_DEFAULT 0x046D -#define HID_PID_DEFAULT 0xC529 - struct HidIntfDescriptor { struct usb_interface_descriptor hid; struct usb_hid_descriptor hid_desc; diff --git a/firmware/targets/f7/furi_hal/furi_hal_version.c b/firmware/targets/f7/furi_hal/furi_hal_version.c index 814ec39267..66580945e3 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_version.c +++ b/firmware/targets/f7/furi_hal/furi_hal_version.c @@ -318,6 +318,10 @@ size_t furi_hal_version_uid_size() { return 64 / 8; } +const uint8_t* furi_hal_version_uid_default() { + return (const uint8_t*)UID64_BASE; +} + const uint8_t* furi_hal_version_uid() { if(version_get_custom_name(NULL) != NULL) { return (const uint8_t*)&(*((uint32_t*)version_get_custom_name(NULL))); diff --git a/firmware/targets/f7/target.json b/firmware/targets/f7/target.json index e3dc78325e..9bb87000c7 100644 --- a/firmware/targets/f7/target.json +++ b/firmware/targets/f7/target.json @@ -38,6 +38,7 @@ "assets", "one_wire", "ibutton", + "music_worker", "misc", "mbedtls", "lfrfid", diff --git a/firmware/targets/furi_hal_include/furi_hal_bt.h b/firmware/targets/furi_hal_include/furi_hal_bt.h index bfe4a67c3c..132d0b95dc 100644 --- a/firmware/targets/furi_hal_include/furi_hal_bt.h +++ b/firmware/targets/furi_hal_include/furi_hal_bt.h @@ -218,6 +218,11 @@ float furi_hal_bt_get_rssi(); */ uint32_t furi_hal_bt_get_transmitted_packets(); +/** Reverse a MAC address byte order in-place + * @param[in] mac mac address to reverse +*/ +void furi_hal_bt_reverse_mac_addr(uint8_t mac_addr[GAP_MAC_ADDR_SIZE]); + /** Modify profile advertisement name and restart bluetooth * @param[in] profile profile type * @param[in] name new adv name diff --git a/firmware/targets/furi_hal_include/furi_hal_rtc.h b/firmware/targets/furi_hal_include/furi_hal_rtc.h index 186d22f079..98dfc7952a 100644 --- a/firmware/targets/furi_hal_include/furi_hal_rtc.h +++ b/firmware/targets/furi_hal_include/furi_hal_rtc.h @@ -27,7 +27,7 @@ typedef struct { typedef enum { FuriHalRtcFlagDebug = (1 << 0), FuriHalRtcFlagFactoryReset = (1 << 1), - FuriHalRtcFlagLock = (1 << 2), + FuriHalRtcFlagLock = (1 << 2), // WITH PIN, on OFW also for keypad (removes option to do both) FuriHalRtcFlagC2Update = (1 << 3), FuriHalRtcFlagHandOrient = (1 << 4), FuriHalRtcFlagLegacySleep = (1 << 5), diff --git a/firmware/targets/furi_hal_include/furi_hal_usb.h b/firmware/targets/furi_hal_include/furi_hal_usb.h index 8b49f6c653..2affb3d6d4 100644 --- a/firmware/targets/furi_hal_include/furi_hal_usb.h +++ b/firmware/targets/furi_hal_include/furi_hal_usb.h @@ -56,6 +56,12 @@ bool furi_hal_usb_set_config(FuriHalUsbInterface* new_if, void* ctx); */ FuriHalUsbInterface* furi_hal_usb_get_config(); +/** Get USB device configuration context + * + * @return current USB device context + */ +void* furi_hal_usb_get_config_context(); + /** Lock USB device mode switch */ void furi_hal_usb_lock(); diff --git a/firmware/targets/furi_hal_include/furi_hal_usb_hid.h b/firmware/targets/furi_hal_include/furi_hal_usb_hid.h index 13e83ef675..887f816177 100644 --- a/firmware/targets/furi_hal_include/furi_hal_usb_hid.h +++ b/firmware/targets/furi_hal_include/furi_hal_usb_hid.h @@ -9,6 +9,11 @@ extern "C" { #endif +#define HID_MANUF_PRODUCT_NAME_LEN 32 + +#define HID_VID_DEFAULT 0x046D +#define HID_PID_DEFAULT 0xC529 + /** Max number of simultaneously pressed keys (keyboard) */ #define HID_KB_MAX_KEYS 6 /** Max number of simultaneously pressed keys (consumer control) */ @@ -161,10 +166,11 @@ static const uint16_t hid_asciimap[] = { }; typedef struct { + // Good job knobheads, these should be uint16_t uint32_t vid; uint32_t pid; - char manuf[32]; - char product[32]; + char manuf[HID_MANUF_PRODUCT_NAME_LEN]; + char product[HID_MANUF_PRODUCT_NAME_LEN]; } FuriHalUsbHidConfig; typedef void (*HidStateCallback)(bool state, void* context); diff --git a/firmware/targets/furi_hal_include/furi_hal_version.h b/firmware/targets/furi_hal_include/furi_hal_version.h index df97a988a7..92f2ec9cab 100644 --- a/firmware/targets/furi_hal_include/furi_hal_version.h +++ b/firmware/targets/furi_hal_include/furi_hal_version.h @@ -20,6 +20,12 @@ extern "C" { #define FURI_HAL_VERSION_DEVICE_NAME_LENGTH \ (1 + FURI_HAL_BT_ADV_NAME_LENGTH) // Used for custom BT name, BLE symbol + name +#define FURI_HAL_BT_EMPTY_MAC_ADDR \ + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } + +#define FURI_HAL_BT_DEFAULT_MAC_ADDR \ + { 0x6c, 0x7a, 0xd8, 0xac, 0x57, 0x72 } + /** OTP Versions enum */ typedef enum { FuriHalVersionOtpVersion0 = 0x00, @@ -204,6 +210,8 @@ size_t furi_hal_version_uid_size(); */ const uint8_t* furi_hal_version_uid(); +const uint8_t* furi_hal_version_uid_default(); + #ifdef __cplusplus } #endif diff --git a/furi/core/check.c b/furi/core/check.c index 478f3aaccc..c5c4ef1a4a 100644 --- a/furi/core/check.c +++ b/furi/core/check.c @@ -166,7 +166,11 @@ FURI_NORETURN void __furi_crash() { RESTORE_REGISTERS_AND_HALT_MCU(true); #ifndef FURI_DEBUG } else { - furi_hal_rtc_set_fault_data((uint32_t)__furi_check_message); + uint32_t ptr = (uint32_t)__furi_check_message; + if(ptr < FLASH_BASE || ptr > (FLASH_BASE + FLASH_SIZE)) { + ptr = (uint32_t) "Check serial logs"; + } + furi_hal_rtc_set_fault_data(ptr); furi_hal_console_puts("\r\nRebooting system.\r\n"); furi_hal_console_puts("\033[0m\r\n"); furi_hal_power_reset(); diff --git a/furi/core/check.h b/furi/core/check.h index ea83f2219c..004422e807 100644 --- a/furi/core/check.h +++ b/furi/core/check.h @@ -13,6 +13,8 @@ */ #pragma once +#include + #ifdef __cplusplus extern "C" { #define FURI_NORETURN [[noreturn]] @@ -48,28 +50,47 @@ FURI_NORETURN void __furi_halt(); } while(0) /** Check condition and crash if check failed */ -#define furi_check(__e) \ - do { \ - if(!(__e)) { \ - furi_crash(__FURI_CHECK_MESSAGE_FLAG); \ - } \ +#define __furi_check(__e, __m) \ + do { \ + if(!(__e)) { \ + furi_crash(__m); \ + } \ } while(0) +/** Check condition and crash if failed + * + * @param condition to check + * @param optional message + */ +#define furi_check(...) \ + M_APPLY(__furi_check, M_DEFAULT_ARGS(2, (__FURI_CHECK_MESSAGE_FLAG), __VA_ARGS__)) + /** Only in debug build: Assert condition and crash if assert failed */ #ifdef FURI_DEBUG -#define furi_assert(__e) \ - do { \ - if(!(__e)) { \ - furi_crash(__FURI_ASSERT_MESSAGE_FLAG); \ - } \ +#define __furi_assert(__e, __m) \ + do { \ + if(!(__e)) { \ + furi_crash(__m); \ + } \ } while(0) #else -#define furi_assert(__e) \ - do { \ - ((void)(__e)); \ +#define __furi_assert(__e, __m) \ + do { \ + ((void)(__e)); \ + ((void)(__m)); \ } while(0) #endif +/** Assert condition and crash if failed + * + * @warning only will do check if firmware compiled in debug mode + * + * @param condition to check + * @param optional message + */ +#define furi_assert(...) \ + M_APPLY(__furi_assert, M_DEFAULT_ARGS(2, (__FURI_ASSERT_MESSAGE_FLAG), __VA_ARGS__)) + #ifdef __cplusplus } #endif diff --git a/furi/core/common_defines.h b/furi/core/common_defines.h index d7bfaf2076..5bd218d357 100644 --- a/furi/core/common_defines.h +++ b/furi/core/common_defines.h @@ -31,29 +31,22 @@ extern "C" { #define FURI_IS_ISR() (FURI_IS_IRQ_MODE() || FURI_IS_IRQ_MASKED()) #endif +typedef struct { + uint32_t isrm; + bool from_isr; + bool kernel_running; +} __FuriCriticalInfo; + +__FuriCriticalInfo __furi_critical_enter(void); + +void __furi_critical_exit(__FuriCriticalInfo info); + #ifndef FURI_CRITICAL_ENTER -#define FURI_CRITICAL_ENTER() \ - uint32_t __isrm = 0; \ - bool __from_isr = FURI_IS_ISR(); \ - bool __kernel_running = (xTaskGetSchedulerState() == taskSCHEDULER_RUNNING); \ - if(__from_isr) { \ - __isrm = taskENTER_CRITICAL_FROM_ISR(); \ - } else if(__kernel_running) { \ - taskENTER_CRITICAL(); \ - } else { \ - __disable_irq(); \ - } +#define FURI_CRITICAL_ENTER() __FuriCriticalInfo __furi_critical_info = __furi_critical_enter(); #endif #ifndef FURI_CRITICAL_EXIT -#define FURI_CRITICAL_EXIT() \ - if(__from_isr) { \ - taskEXIT_CRITICAL_FROM_ISR(__isrm); \ - } else if(__kernel_running) { \ - taskEXIT_CRITICAL(); \ - } else { \ - __enable_irq(); \ - } +#define FURI_CRITICAL_EXIT() __furi_critical_exit(__furi_critical_info); #endif #ifdef __cplusplus diff --git a/furi/core/critical.c b/furi/core/critical.c new file mode 100644 index 0000000000..57fe2403be --- /dev/null +++ b/furi/core/critical.c @@ -0,0 +1,29 @@ +#include "common_defines.h" + +__FuriCriticalInfo __furi_critical_enter(void) { + __FuriCriticalInfo info; + + info.isrm = 0; + info.from_isr = FURI_IS_ISR(); + info.kernel_running = (xTaskGetSchedulerState() == taskSCHEDULER_RUNNING); + + if(info.from_isr) { + info.isrm = taskENTER_CRITICAL_FROM_ISR(); + } else if(info.kernel_running) { + taskENTER_CRITICAL(); + } else { + __disable_irq(); + } + + return info; +} + +void __furi_critical_exit(__FuriCriticalInfo info) { + if(info.from_isr) { + taskEXIT_CRITICAL_FROM_ISR(info.isrm); + } else if(info.kernel_running) { + taskEXIT_CRITICAL(); + } else { + __enable_irq(); + } +} \ No newline at end of file diff --git a/furi/core/string.c b/furi/core/string.c index 4384fe06a2..682c8d4097 100644 --- a/furi/core/string.c +++ b/furi/core/string.c @@ -296,7 +296,9 @@ static FuriStringUTF8State state_to_furi_state(m_str1ng_utf8_state_e state) { } void furi_string_utf8_decode(char c, FuriStringUTF8State* state, FuriStringUnicodeValue* unicode) { + string_unicode_t m_u = *unicode; m_str1ng_utf8_state_e m_state = furi_state_to_state(*state); - m_str1ng_utf8_decode(c, &m_state, unicode); + m_str1ng_utf8_decode(c, &m_state, &m_u); *state = state_to_furi_state(m_state); + *unicode = m_u; } diff --git a/furi/core/string.h b/furi/core/string.h index 0523d3ba04..7529deacd7 100644 --- a/furi/core/string.h +++ b/furi/core/string.h @@ -633,20 +633,17 @@ void furi_string_utf8_decode(char c, FuriStringUTF8State* state, FuriStringUnico * @brief Search for a string (or C string) in a string * (string, [c]string[, start=0]) */ -#define furi_string_search(v, ...) \ - M_APPLY( \ - FURI_STRING_SELECT3, \ - furi_string_search, \ - furi_string_search_str, \ - v, \ - M_IF_DEFAULT1(0, __VA_ARGS__)) - +#define furi_string_search(...) \ + M_APPLY( \ + FURI_STRING_SELECT3, \ + furi_string_search, \ + furi_string_search_str, \ + M_DEFAULT_ARGS(3, (0), __VA_ARGS__)) /** * @brief Search for a C string in a string * (string, cstring[, start=0]) */ -#define furi_string_search_str(v, ...) \ - M_APPLY(furi_string_search_str, v, M_IF_DEFAULT1(0, __VA_ARGS__)) +#define furi_string_search_str(...) furi_string_search_str(M_DEFAULT_ARGS(3, (0), __VA_ARGS__)) /** * @brief Test if the string starts with the given string (or C string). @@ -672,41 +669,36 @@ void furi_string_utf8_decode(char c, FuriStringUTF8State* state, FuriStringUnico * @brief Trim a string from the given set of characters (default is " \n\r\t"). * (string[, set=" \n\r\t"]) */ -#define furi_string_trim(...) M_APPLY(furi_string_trim, M_IF_DEFAULT1(" \n\r\t", __VA_ARGS__)) +#define furi_string_trim(...) furi_string_trim(M_DEFAULT_ARGS(2, (" \n\r\t"), __VA_ARGS__)) /** * @brief Search for a character in a string. * (string, character[, start=0]) */ -#define furi_string_search_char(v, ...) \ - M_APPLY(furi_string_search_char, v, M_IF_DEFAULT1(0, __VA_ARGS__)) +#define furi_string_search_char(...) furi_string_search_char(M_DEFAULT_ARGS(3, (0), __VA_ARGS__)) /** * @brief Reverse Search for a character in a string. * (string, character[, start=0]) */ -#define furi_string_search_rchar(v, ...) \ - M_APPLY(furi_string_search_rchar, v, M_IF_DEFAULT1(0, __VA_ARGS__)) +#define furi_string_search_rchar(...) furi_string_search_rchar(M_DEFAULT_ARGS(3, (0), __VA_ARGS__)) /** * @brief Replace a string to another string (or C string to another C string) in a string. * (string, [c]string, [c]string[, start=0]) */ -#define furi_string_replace(a, b, ...) \ - M_APPLY( \ - FURI_STRING_SELECT4, \ - furi_string_replace, \ - furi_string_replace_str, \ - a, \ - b, \ - M_IF_DEFAULT1(0, __VA_ARGS__)) +#define furi_string_replace(...) \ + M_APPLY( \ + FURI_STRING_SELECT4, \ + furi_string_replace, \ + furi_string_replace_str, \ + M_DEFAULT_ARGS(4, (0), __VA_ARGS__)) /** * @brief Replace a C string to another C string in a string. * (string, cstring, cstring[, start=0]) */ -#define furi_string_replace_str(a, b, ...) \ - M_APPLY(furi_string_replace_str, a, b, M_IF_DEFAULT1(0, __VA_ARGS__)) +#define furi_string_replace_str(...) furi_string_replace_str(M_DEFAULT_ARGS(4, (0), __VA_ARGS__)) /** * @brief INIT OPLIST for FuriString. @@ -743,4 +735,4 @@ void furi_string_utf8_decode(char c, FuriStringUTF8State* state, FuriStringUnico #ifdef __cplusplus } -#endif \ No newline at end of file +#endif diff --git a/furi/core/thread.c b/furi/core/thread.c index facbcb4117..657b867d1e 100644 --- a/furi/core/thread.c +++ b/furi/core/thread.c @@ -56,6 +56,8 @@ static int32_t __furi_thread_stdout_flush(FuriThread* thread); /** Catch threads that are trying to exit wrong way */ __attribute__((__noreturn__)) void furi_thread_catch() { //-V1082 + // If you're here it means you're probably doing something wrong + // with critical sections or with scheduler state asm volatile("nop"); // extra magic furi_crash("You are doing it wrong"); //-V779 __builtin_unreachable(); diff --git a/furi/flipper.c b/furi/flipper.c index c42ba0fdd1..bdb80c8515 100644 --- a/furi/flipper.c +++ b/furi/flipper.c @@ -74,7 +74,7 @@ void flipper_migrate_files() { furi_record_close(RECORD_STORAGE); } -void flipper_start_service(const FlipperApplication* service) { +void flipper_start_service(const FlipperInternalApplication* service) { FURI_LOG_D(TAG, "Starting service %s", service->name); FuriThread* thread = diff --git a/lib/SConscript b/lib/SConscript index 3344a56610..f3d96a55c8 100644 --- a/lib/SConscript +++ b/lib/SConscript @@ -15,6 +15,7 @@ env.Append( Dir("u8g2"), Dir("update_util"), Dir("print"), + Dir("music_worker"), ], ) @@ -26,6 +27,7 @@ env.Append( "#/lib/mlib", # Ugly hack Dir("../assets/compiled"), + Dir("../../icons"), ], SDK_HEADERS=[ *( @@ -43,6 +45,7 @@ env.Append( "variant", ) ), + File("../../icons/assets_icons.h"), File("xtreme/xtreme.h"), ], CPPDEFINES=[ @@ -101,6 +104,7 @@ libs = env.BuildModules( "misc", "lfrfid", "flipper_application", + "music_worker", ], ) diff --git a/lib/digital_signal/digital_signal.c b/lib/digital_signal/digital_signal.c index 6ccfcf2803..25adb878b7 100644 --- a/lib/digital_signal/digital_signal.c +++ b/lib/digital_signal/digital_signal.c @@ -9,7 +9,7 @@ #include /* must be on bank B */ -//#define DEBUG_OUTPUT gpio_ext_pb3 +// For debugging purposes use `--extra-define=DIGITAL_SIGNAL_DEBUG_OUTPUT_PIN=gpio_ext_pb3` fbt option struct ReloadBuffer { uint32_t* buffer; /* DMA ringbuffer */ @@ -51,8 +51,16 @@ struct DigitalSignalInternals { #define T_TIM 1562 /* 15.625 ns *100 */ #define T_TIM_DIV2 781 /* 15.625 ns / 2 *100 */ +/* end marker in DMA ringbuffer, will get written into timer register at the end */ +#define SEQ_TIMER_MAX 0xFFFFFFFF + +/* time to wait in loops before returning */ +#define SEQ_LOCK_WAIT_MS 10UL +#define SEQ_LOCK_WAIT_TICKS (SEQ_LOCK_WAIT_MS * 1000 * 64) + /* maximum entry count of the sequence dma ring buffer */ -#define SEQUENCE_DMA_RINGBUFFER_SIZE 32 +#define RINGBUFFER_SIZE 128 + /* maximum number of DigitalSignals in a sequence */ #define SEQUENCE_SIGNALS_SIZE 32 /* @@ -194,9 +202,9 @@ void digital_signal_prepare_arr(DigitalSignal* signal) { uint32_t bit_set = internals->gpio->pin; uint32_t bit_reset = internals->gpio->pin << 16; -#ifdef DEBUG_OUTPUT - bit_set |= DEBUG_OUTPUT.pin; - bit_reset |= DEBUG_OUTPUT.pin << 16; +#ifdef DIGITAL_SIGNAL_DEBUG_OUTPUT_PIN + bit_set |= DIGITAL_SIGNAL_DEBUG_OUTPUT_PIN.pin; + bit_reset |= DIGITAL_SIGNAL_DEBUG_OUTPUT_PIN.pin << 16; #endif if(signal->start_level) { @@ -214,12 +222,12 @@ void digital_signal_prepare_arr(DigitalSignal* signal) { for(size_t pos = 0; pos < signal->edge_cnt; pos++) { uint32_t pulse_duration = signal->edge_timings[pos] + internals->reload_reg_remainder; if(pulse_duration < 10 || pulse_duration > 10000000) { - /*FURI_LOG_D( + FURI_LOG_D( TAG, "[prepare] pulse_duration out of range: %lu = %lu * %llu", pulse_duration, signal->edge_timings[pos], - internals->factor);*/ + internals->factor); pulse_duration = 100; } uint32_t pulse_ticks = (pulse_duration + T_TIM_DIV2) / T_TIM; @@ -252,7 +260,7 @@ static void digital_signal_setup_timer() { LL_TIM_SetCounterMode(TIM2, LL_TIM_COUNTERMODE_UP); LL_TIM_SetClockDivision(TIM2, LL_TIM_CLOCKDIVISION_DIV1); LL_TIM_SetPrescaler(TIM2, 0); - LL_TIM_SetAutoReload(TIM2, 0xFFFFFFFF); + LL_TIM_SetAutoReload(TIM2, SEQ_TIMER_MAX); LL_TIM_SetCounter(TIM2, 0); } @@ -335,7 +343,7 @@ DigitalSequence* digital_sequence_alloc(uint32_t size, const GpioPin* gpio) { sequence->bake = false; sequence->dma_buffer = malloc(sizeof(struct ReloadBuffer)); - sequence->dma_buffer->size = SEQUENCE_DMA_RINGBUFFER_SIZE; + sequence->dma_buffer->size = RINGBUFFER_SIZE; sequence->dma_buffer->buffer = malloc(sequence->dma_buffer->size * sizeof(uint32_t)); sequence->dma_config_gpio.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; @@ -454,42 +462,26 @@ static DigitalSignal* digital_sequence_bake(DigitalSequence* sequence) { return ret; } -static void digital_sequence_update_pos(DigitalSequence* sequence) { - struct ReloadBuffer* dma_buffer = sequence->dma_buffer; - - dma_buffer->read_pos = dma_buffer->size - LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_2); -} - -static const uint32_t wait_ms = 10; -static const uint32_t wait_ticks = wait_ms * 1000 * 64; - static void digital_sequence_finish(DigitalSequence* sequence) { struct ReloadBuffer* dma_buffer = sequence->dma_buffer; if(dma_buffer->dma_active) { uint32_t prev_timer = DWT->CYCCNT; - uint32_t end_pos = (dma_buffer->write_pos + 1) % dma_buffer->size; do { - uint32_t last_pos = dma_buffer->read_pos; - - digital_sequence_update_pos(sequence); - - /* we are finished, when the DMA transferred the 0xFFFFFFFF-timer which is the current write_pos */ - if(dma_buffer->read_pos == end_pos) { + /* we are finished, when the DMA transferred the SEQ_TIMER_MAX marker */ + if(TIM2->ARR == SEQ_TIMER_MAX) { break; } - - if(last_pos != dma_buffer->read_pos) { //-V547 - prev_timer = DWT->CYCCNT; - } - if(DWT->CYCCNT - prev_timer > wait_ticks) { - /*FURI_LOG_D( + if(DWT->CYCCNT - prev_timer > SEQ_LOCK_WAIT_TICKS) { + dma_buffer->read_pos = + RINGBUFFER_SIZE - LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_2); + FURI_LOG_D( TAG, "[SEQ] hung %lu ms in finish (ARR 0x%08lx, read %lu, write %lu)", - wait_ms, + SEQ_LOCK_WAIT_MS, TIM2->ARR, dma_buffer->read_pos, - dma_buffer->write_pos);*/ + dma_buffer->write_pos); break; } } while(1); @@ -504,34 +496,42 @@ static void digital_sequence_queue_pulse(DigitalSequence* sequence, uint32_t len if(dma_buffer->dma_active) { uint32_t prev_timer = DWT->CYCCNT; - uint32_t end_pos = (dma_buffer->write_pos + 1) % dma_buffer->size; do { - uint32_t last_pos = dma_buffer->read_pos; - digital_sequence_update_pos(sequence); + dma_buffer->read_pos = RINGBUFFER_SIZE - LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_2); + + uint32_t free = + (RINGBUFFER_SIZE + dma_buffer->read_pos - dma_buffer->write_pos) % RINGBUFFER_SIZE; - if(dma_buffer->read_pos != end_pos) { + if(free > 2) { break; } - if(last_pos != dma_buffer->read_pos) { //-V547 - prev_timer = DWT->CYCCNT; - } - if(DWT->CYCCNT - prev_timer > wait_ticks) { - /*FURI_LOG_D( + if(DWT->CYCCNT - prev_timer > SEQ_LOCK_WAIT_TICKS) { + FURI_LOG_D( TAG, "[SEQ] hung %lu ms in queue (ARR 0x%08lx, read %lu, write %lu)", - wait_ms, + SEQ_LOCK_WAIT_MS, + TIM2->ARR, + dma_buffer->read_pos, + dma_buffer->write_pos); + break; + } + if(TIM2->ARR == SEQ_TIMER_MAX) { + FURI_LOG_D( + TAG, + "[SEQ] buffer underrun in queue (ARR 0x%08lx, read %lu, write %lu)", TIM2->ARR, dma_buffer->read_pos, - dma_buffer->write_pos);*/ + dma_buffer->write_pos); break; } } while(1); } dma_buffer->buffer[dma_buffer->write_pos] = length; - dma_buffer->write_pos = (dma_buffer->write_pos + 1) % dma_buffer->size; - dma_buffer->buffer[dma_buffer->write_pos] = 0xFFFFFFFF; + dma_buffer->write_pos++; + dma_buffer->write_pos %= RINGBUFFER_SIZE; + dma_buffer->buffer[dma_buffer->write_pos] = SEQ_TIMER_MAX; } bool digital_sequence_send(DigitalSequence* sequence) { @@ -540,8 +540,9 @@ bool digital_sequence_send(DigitalSequence* sequence) { struct ReloadBuffer* dma_buffer = sequence->dma_buffer; furi_hal_gpio_init(sequence->gpio, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); -#ifdef DEBUG_OUTPUT - furi_hal_gpio_init(&DEBUG_OUTPUT, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); +#ifdef DIGITAL_SIGNAL_DEBUG_OUTPUT_PIN + furi_hal_gpio_init( + &DIGITAL_SIGNAL_DEBUG_OUTPUT_PIN, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); #endif if(sequence->bake) { @@ -552,90 +553,97 @@ bool digital_sequence_send(DigitalSequence* sequence) { return true; } - int32_t remainder = 0; - bool traded_first = false; + if(!sequence->sequence_used) { + return false; + } - FURI_CRITICAL_ENTER(); + int32_t remainder = 0; + uint32_t trade_for_next = 0; + uint32_t seq_pos_next = 1; dma_buffer->dma_active = false; - dma_buffer->buffer[0] = 0xFFFFFFFF; + dma_buffer->buffer[0] = SEQ_TIMER_MAX; dma_buffer->read_pos = 0; dma_buffer->write_pos = 0; - for(uint32_t seq_pos = 0; seq_pos < sequence->sequence_used; seq_pos++) { - uint8_t signal_index = sequence->sequence[seq_pos]; - DigitalSignal* sig = sequence->signals[signal_index]; - bool last_signal = ((seq_pos + 1) == sequence->sequence_used); + /* already prepare the current signal pointer */ + DigitalSignal* sig = sequence->signals[sequence->sequence[0]]; + DigitalSignal* sig_next = NULL; + /* re-use the GPIO buffer from the first signal */ + sequence->gpio_buff = sig->internals->gpio_buff; + + FURI_CRITICAL_ENTER(); + + while(sig) { + bool last_signal = (seq_pos_next >= sequence->sequence_used); - /* all signals are prepared and we can re-use the GPIO buffer from the fist signal */ - if(seq_pos == 0) { - sequence->gpio_buff = sig->internals->gpio_buff; + if(!last_signal) { + sig_next = sequence->signals[sequence->sequence[seq_pos_next++]]; } for(uint32_t pulse_pos = 0; pulse_pos < sig->internals->reload_reg_entries; pulse_pos++) { - if(traded_first) { - traded_first = false; - continue; - } - uint32_t pulse_length = 0; - bool last_pulse = ((pulse_pos + 1) == sig->internals->reload_reg_entries); + bool last_pulse = ((pulse_pos + 1) >= sig->internals->reload_reg_entries); + uint32_t pulse_length = sig->reload_reg_buff[pulse_pos] + trade_for_next; - pulse_length = sig->reload_reg_buff[pulse_pos]; + trade_for_next = 0; /* when we are too late more than half a tick, make the first edge temporarily longer */ if(remainder >= T_TIM_DIV2) { remainder -= T_TIM; pulse_length += 1; } - remainder += sig->internals->reload_reg_remainder; - - /* last pulse in that signal and have a next signal? */ - if(last_pulse) { - if((seq_pos + 1) < sequence->sequence_used) { - DigitalSignal* sig_next = sequence->signals[sequence->sequence[seq_pos + 1]]; - /* when a signal ends with the same level as the next signal begins, let the fist signal generate the whole pulse */ - /* beware, we do not want the level after the last edge, but the last level before that edge */ - bool end_level = sig->start_level ^ ((sig->edge_cnt % 2) == 0); + /* last pulse in current signal and have a next signal? */ + if(last_pulse && sig_next) { + /* when a signal ends with the same level as the next signal begins, let the next signal generate the whole pulse. + beware, we do not want the level after the last edge, but the last level before that edge */ + bool end_level = sig->start_level ^ ((sig->edge_cnt % 2) == 0); - /* take from the next, add it to the current if they have the same level */ - if(end_level == sig_next->start_level) { - pulse_length += sig_next->reload_reg_buff[0]; - traded_first = true; - } + /* if they have the same level, pass the duration to the next pulse(s) */ + if(end_level == sig_next->start_level) { + trade_for_next = pulse_length; } } - digital_sequence_queue_pulse(sequence, pulse_length); + /* if it was decided, that the next signal's first pulse shall also handle our "length", then do not queue here */ + if(!trade_for_next) { + digital_sequence_queue_pulse(sequence, pulse_length); - /* start transmission when buffer was filled enough */ - bool start_send = sequence->dma_buffer->write_pos >= (sequence->dma_buffer->size - 4); - - /* or it was the last pulse */ - if(last_pulse && last_signal) { - start_send = true; - } + if(!dma_buffer->dma_active) { + /* start transmission when buffer was filled enough */ + bool start_send = sequence->dma_buffer->write_pos >= (RINGBUFFER_SIZE - 2); - /* start transmission */ - if(start_send && !dma_buffer->dma_active) { - digital_sequence_setup_dma(sequence); - digital_signal_setup_timer(); + /* or it was the last pulse */ + if(last_pulse && last_signal) { + start_send = true; + } - /* if the send time is specified, wait till the core timer passed beyond that time */ - if(sequence->send_time_active) { - sequence->send_time_active = false; - while(sequence->send_time - DWT->CYCCNT < 0x80000000) { + /* start transmission */ + if(start_send) { + digital_sequence_setup_dma(sequence); + digital_signal_setup_timer(); + + /* if the send time is specified, wait till the core timer passed beyond that time */ + if(sequence->send_time_active) { + sequence->send_time_active = false; + while(sequence->send_time - DWT->CYCCNT < 0x80000000) { + } + } + digital_signal_start_timer(); + dma_buffer->dma_active = true; } } - digital_signal_start_timer(); - dma_buffer->dma_active = true; } } + + remainder += sig->internals->reload_reg_remainder; + sig = sig_next; + sig_next = NULL; } /* wait until last dma transaction was finished */ - digital_sequence_finish(sequence); FURI_CRITICAL_EXIT(); + digital_sequence_finish(sequence); return true; } diff --git a/lib/flipper_application/api_hashtable/api_hashtable.cpp b/lib/flipper_application/api_hashtable/api_hashtable.cpp index 022792dce6..6db5fb5fde 100644 --- a/lib/flipper_application/api_hashtable/api_hashtable.cpp +++ b/lib/flipper_application/api_hashtable/api_hashtable.cpp @@ -7,27 +7,22 @@ bool elf_resolve_from_hashtable( const ElfApiInterface* interface, - const char* name, + uint32_t hash, Elf32_Addr* address) { + bool result = false; const HashtableApiInterface* hashtable_interface = static_cast(interface); - bool result = false; - uint32_t gnu_sym_hash = elf_gnu_hash(name); sym_entry key = { - .hash = gnu_sym_hash, + .hash = hash, .address = 0, }; auto find_res = std::lower_bound(hashtable_interface->table_cbegin, hashtable_interface->table_cend, key); - if((find_res == hashtable_interface->table_cend || (find_res->hash != gnu_sym_hash))) { + if((find_res == hashtable_interface->table_cend || (find_res->hash != hash))) { FURI_LOG_W( - TAG, - "Can't find symbol '%s' (hash %lx) @ %p!", - name, - gnu_sym_hash, - hashtable_interface->table_cbegin); + TAG, "Can't find symbol with hash %lx @ %p!", hash, hashtable_interface->table_cbegin); result = false; } else { result = true; @@ -36,3 +31,7 @@ bool elf_resolve_from_hashtable( return result; } + +uint32_t elf_symbolname_hash(const char* s) { + return elf_gnu_hash(s); +} \ No newline at end of file diff --git a/lib/flipper_application/api_hashtable/api_hashtable.h b/lib/flipper_application/api_hashtable/api_hashtable.h index 7e4b4aba1d..7ba6aab972 100644 --- a/lib/flipper_application/api_hashtable/api_hashtable.h +++ b/lib/flipper_application/api_hashtable/api_hashtable.h @@ -19,15 +19,17 @@ struct sym_entry { /** * @brief Resolver for API entries using a pre-sorted table with hashes * @param interface pointer to HashtableApiInterface - * @param name function name + * @param hash gnu hash of function name * @param address output for function address * @return true if the table contains a function */ bool elf_resolve_from_hashtable( const ElfApiInterface* interface, - const char* name, + uint32_t hash, Elf32_Addr* address); +uint32_t elf_symbolname_hash(const char* s); + #ifdef __cplusplus } @@ -48,8 +50,10 @@ struct HashtableApiInterface : public ElfApiInterface { .hash = elf_gnu_hash(#x), .address = (uint32_t)(static_cast(x)) \ } -#define API_VARIABLE(x, var_type) \ - sym_entry { .hash = elf_gnu_hash(#x), .address = (uint32_t)(&(x)), } +#define API_VARIABLE(x, var_type) \ + sym_entry { \ + .hash = elf_gnu_hash(#x), .address = (uint32_t)(&(x)), \ + } constexpr bool operator<(const sym_entry& k1, const sym_entry& k2) { return k1.hash < k2.hash; diff --git a/lib/flipper_application/elf/elf_api_interface.h b/lib/flipper_application/elf/elf_api_interface.h index f07df4edb7..facdc44473 100644 --- a/lib/flipper_application/elf/elf_api_interface.h +++ b/lib/flipper_application/elf/elf_api_interface.h @@ -11,6 +11,6 @@ typedef struct ElfApiInterface { uint16_t api_version_minor; bool (*resolver_callback)( const struct ElfApiInterface* interface, - const char* name, + uint32_t hash, Elf32_Addr* address); } ElfApiInterface; diff --git a/lib/flipper_application/elf/elf_file.c b/lib/flipper_application/elf/elf_file.c index e982e3ca1f..9f37fc4e08 100644 --- a/lib/flipper_application/elf/elf_file.c +++ b/lib/flipper_application/elf/elf_file.c @@ -2,6 +2,7 @@ #include "elf_file.h" #include "elf_file_i.h" #include "elf_api_interface.h" +#include "../api_hashtable/api_hashtable.h" #define TAG "elf" @@ -9,6 +10,7 @@ #define SECTION_OFFSET(e, n) ((e)->section_table + (n) * sizeof(Elf32_Shdr)) #define IS_FLAGS_SET(v, m) (((v) & (m)) == (m)) #define RESOLVER_THREAD_YIELD_STEP 30 +#define FAST_RELOCATION_VERSION 1 // #define ELF_DEBUG_LOG 1 @@ -71,6 +73,7 @@ static ELFSection* elf_file_get_or_put_section(ELFFile* elf, const char* name) { .size = 0, .rel_count = 0, .rel_offset = 0, + .fast_rel = NULL, }); section_p = elf_file_get_section(elf, name); } @@ -168,7 +171,8 @@ static ELFSection* elf_section_of(ELFFile* elf, int index) { static Elf32_Addr elf_address_of(ELFFile* elf, Elf32_Sym* sym, const char* sName) { if(sym->st_shndx == SHN_UNDEF) { Elf32_Addr addr = 0; - if(elf->api_interface->resolver_callback(elf->api_interface, sName, &addr)) { + uint32_t hash = elf_symbolname_hash(sName); + if(elf->api_interface->resolver_callback(elf->api_interface, hash, &addr)) { return addr; } } else { @@ -424,6 +428,7 @@ typedef enum { SectionTypeSymTab = 1 << 3, SectionTypeStrTab = 1 << 4, SectionTypeDebugLink = 1 << 5, + SectionTypeFastRelData = 1 << 6, SectionTypeValid = SectionTypeSymTab | SectionTypeStrTab, } SectionType; @@ -505,7 +510,8 @@ static SectionType elf_preload_section( // TODO: how to do it not by name? // .ARM: type 0x70000001, flags SHF_ALLOC | SHF_LINK_ORDER // .rel.ARM: type 0x9, flags SHT_REL - if(str_prefix(name, ".ARM.") || str_prefix(name, ".rel.ARM.")) { + if(str_prefix(name, ".ARM.") || str_prefix(name, ".rel.ARM.") || + str_prefix(name, ".fast.rel.ARM.")) { FURI_LOG_D(TAG, "Ignoring ARM section"); return SectionTypeUnused; } @@ -536,11 +542,31 @@ static SectionType elf_preload_section( // Load link info section if(section_header->sh_flags & SHF_INFO_LINK) { - name = name + strlen(".rel"); + if(str_prefix(name, ".rel")) { + name = name + strlen(".rel"); + ELFSection* section_p = elf_file_get_or_put_section(elf, name); + section_p->rel_count = section_header->sh_size / sizeof(Elf32_Rel); + section_p->rel_offset = section_header->sh_offset; + return SectionTypeRelData; + } else { + FURI_LOG_E(TAG, "Unknown link info section '%s'", name); + return SectionTypeERROR; + } + } + + // Load fast rel section + if(str_prefix(name, ".fast.rel")) { + name = name + strlen(".fast.rel"); ELFSection* section_p = elf_file_get_or_put_section(elf, name); - section_p->rel_count = section_header->sh_size / sizeof(Elf32_Rel); - section_p->rel_offset = section_header->sh_offset; - return SectionTypeRelData; + section_p->fast_rel = malloc(sizeof(ELFSection)); + + if(!elf_load_section_data(elf, section_p->fast_rel, section_header)) { + FURI_LOG_E(TAG, "Error loading section '%s'", name); + return SectionTypeERROR; + } + + FURI_LOG_D(TAG, "Loaded fast rel section for '%s'", name); + return SectionTypeFastRelData; } // Load symbol table @@ -571,8 +597,90 @@ static SectionType elf_preload_section( return SectionTypeUnused; } +static Elf32_Addr elf_address_of_by_hash(ELFFile* elf, uint32_t hash) { + Elf32_Addr addr = 0; + if(elf->api_interface->resolver_callback(elf->api_interface, hash, &addr)) { + return addr; + } + return ELF_INVALID_ADDRESS; +} + +static bool elf_relocate_fast(ELFFile* elf, ELFSection* s) { + UNUSED(elf); + const uint8_t* start = s->fast_rel->data; + const uint8_t version = *start; + + if(version != FAST_RELOCATION_VERSION) { + FURI_LOG_E(TAG, "Unsupported fast relocation version %d", version); + return false; + } + start += 1; + + const uint32_t records_count = *((uint32_t*)start); + start += 4; + FURI_LOG_D(TAG, "Fast relocation records count: %ld", records_count); + + for(uint32_t i = 0; i < records_count; i++) { + bool is_section = (*start & (0x1 << 7)) ? true : false; + uint8_t type = *start & 0x7F; + start += 1; + uint32_t hash_or_section_index = *((uint32_t*)start); + start += 4; + + uint32_t section_value = ELF_INVALID_ADDRESS; + if(is_section) { + section_value = *((uint32_t*)start); + start += 4; + } + + const uint32_t offsets_count = *((uint32_t*)start); + start += 4; + + FURI_LOG_D( + TAG, + "Fast relocation record %ld: is_section=%d, type=%d, hash_or_section_index=%lX, offsets_count=%ld", + i, + is_section, + type, + hash_or_section_index, + offsets_count); + + Elf32_Addr address = 0; + if(is_section) { + ELFSection* symSec = elf_section_of(elf, hash_or_section_index); + if(symSec) { + address = ((Elf32_Addr)symSec->data) + section_value; + } + } else { + address = elf_address_of_by_hash(elf, hash_or_section_index); + } + + if(address == ELF_INVALID_ADDRESS) { + FURI_LOG_E(TAG, "Failed to resolve address for hash %lX", hash_or_section_index); + return false; + } + + for(uint32_t j = 0; j < offsets_count; j++) { + uint32_t offset = *((uint32_t*)start) & 0x00FFFFFF; + start += 3; + // FURI_LOG_I(TAG, " Fast relocation offset %ld: %ld", j, offset); + Elf32_Addr relAddr = ((Elf32_Addr)s->data) + offset; + elf_relocate_symbol(elf, relAddr, type, address); + } + } + + aligned_free(s->fast_rel->data); + free(s->fast_rel); + s->fast_rel = NULL; + + return true; +} + static bool elf_relocate_section(ELFFile* elf, ELFSection* section) { - if(section->rel_count) { + if(section->fast_rel) { + FURI_LOG_D(TAG, "Fast relocating section"); + return elf_relocate_fast(elf, section); + } else if(section->rel_count) { FURI_LOG_D(TAG, "Relocating section"); return elf_relocate(elf, section); } else { @@ -630,6 +738,10 @@ void elf_file_free(ELFFile* elf) { if(itref->value.data) { aligned_free(itref->value.data); } + if(itref->value.fast_rel) { + aligned_free(itref->value.fast_rel->data); + free(itref->value.fast_rel); + } free((void*)itref->key); } diff --git a/lib/flipper_application/elf/elf_file_i.h b/lib/flipper_application/elf/elf_file_i.h index af9a1d9b4f..39cadfdc6f 100644 --- a/lib/flipper_application/elf/elf_file_i.h +++ b/lib/flipper_application/elf/elf_file_i.h @@ -13,14 +13,18 @@ DICT_DEF2(AddressCache, int, M_DEFAULT_OPLIST, Elf32_Addr, M_DEFAULT_OPLIST) */ typedef int32_t(entry_t)(void*); -typedef struct { +typedef struct ELFSection ELFSection; + +struct ELFSection { void* data; - uint16_t sec_idx; Elf32_Word size; size_t rel_count; Elf32_Off rel_offset; -} ELFSection; + ELFSection* fast_rel; + + uint16_t sec_idx; +}; DICT_DEF2(ELFSectionDict, const char*, M_CSTR_OPLIST, ELFSection, M_POD_OPLIST) diff --git a/lib/flipper_application/flipper_application.c b/lib/flipper_application/flipper_application.c index 9272b90936..620bd59946 100644 --- a/lib/flipper_application/flipper_application.c +++ b/lib/flipper_application/flipper_application.c @@ -2,6 +2,8 @@ #include "elf/elf_file.h" #include #include "application_assets.h" +#include +#include #include @@ -81,6 +83,12 @@ void flipper_application_free(FlipperApplication* app) { } elf_file_free(app->elf); + + if(app->ep_thread_args) { + free(app->ep_thread_args); + app->ep_thread_args = NULL; + } + free(app); } @@ -224,10 +232,19 @@ static int32_t flipper_application_thread(void* context) { return ret_code; } -FuriThread* flipper_application_spawn(FlipperApplication* app, void* args) { +FuriThread* flipper_application_alloc_thread(FlipperApplication* app, const char* args) { furi_check(app->thread == NULL); furi_check(!flipper_application_is_plugin(app)); - app->ep_thread_args = args; + + if(app->ep_thread_args) { + free(app->ep_thread_args); + } + + if(args) { + app->ep_thread_args = strdup(args); + } else { + app->ep_thread_args = NULL; + } const FlipperApplicationManifest* manifest = flipper_application_get_manifest(app); app->thread = furi_thread_alloc_ex( @@ -236,15 +253,6 @@ FuriThread* flipper_application_spawn(FlipperApplication* app, void* args) { return app->thread; } -void flipper_application_despawn(FlipperApplication* app) { - furi_check(app->thread != NULL); - furi_check(!flipper_application_is_plugin(app)); - - furi_thread_join(app->thread); - furi_thread_free(app->thread); - app->thread = NULL; -} - static const char* preload_status_strings[] = { [FlipperApplicationPreloadStatusSuccess] = "Success", [FlipperApplicationPreloadStatusUnspecifiedError] = "Unknown error", @@ -299,3 +307,51 @@ const FlipperAppPluginDescriptor* return lib_descriptor; } + +bool flipper_application_load_name_and_icon( + FuriString* path, + Storage* storage, + uint8_t** icon_ptr, + FuriString* item_name) { + bool load_success = true; + + StorageData* storage_data; + if(storage_get_data(storage, path, &storage_data) == FSE_OK && + storage_path_already_open(path, storage_data)) { + load_success = false; + } + + if(load_success) { + load_success = false; + + FlipperApplication* app = flipper_application_alloc(storage, firmware_api_interface); + + FlipperApplicationPreloadStatus preload_res = + flipper_application_preload_manifest(app, furi_string_get_cstr(path)); + + if(preload_res == FlipperApplicationPreloadStatusSuccess || + preload_res == FlipperApplicationPreloadStatusApiMismatch) { + const FlipperApplicationManifest* manifest = flipper_application_get_manifest(app); + if(manifest->has_icon && icon_ptr != NULL && *icon_ptr != NULL) { + memcpy(*icon_ptr, manifest->icon, FAP_MANIFEST_MAX_ICON_SIZE); + } + furi_string_set(item_name, manifest->name); + load_success = true; + } else { + FURI_LOG_E(TAG, "Failed to preload %s", furi_string_get_cstr(path)); + } + + flipper_application_free(app); + } + + if(!load_success) { + size_t offset = furi_string_search_rchar(path, '/'); + if(offset != FURI_STRING_FAILURE) { + furi_string_set_n(item_name, path, offset + 1, furi_string_size(path) - offset - 1); + } else { + furi_string_set(item_name, path); + } + } + + return load_success; +} diff --git a/lib/flipper_application/flipper_application.h b/lib/flipper_application/flipper_application.h index 42665080b0..20baae8264 100644 --- a/lib/flipper_application/flipper_application.h +++ b/lib/flipper_application/flipper_application.h @@ -106,19 +106,14 @@ const FlipperApplicationManifest* flipper_application_get_manifest(FlipperApplic FlipperApplicationLoadStatus flipper_application_map_to_memory(FlipperApplication* app); /** - * @brief Create application thread at entry point address, using app name and - * stack size from metadata. Returned thread isn't started yet. + * @brief Allocate application thread at entry point address, using app name and + * stack size from metadata. Returned thread isn't started yet. + * Can be only called once for application instance. * @param app Applicaiton pointer - * @param args Object to pass to app's entry point + * @param args Args to pass to app's entry point * @return Created thread */ -FuriThread* flipper_application_spawn(FlipperApplication* app, void* args); - -/** - * @brief Cleanup application in order to re-spawn later. - * @param app Applicaiton pointer - */ -void flipper_application_despawn(FlipperApplication* app); +FuriThread* flipper_application_alloc_thread(FlipperApplication* app, const char* args); /** * @brief Check if application is a plugin (not a runnable standalone app) @@ -154,6 +149,21 @@ typedef const FlipperAppPluginDescriptor* (*FlipperApplicationPluginEntryPoint)( const FlipperAppPluginDescriptor* flipper_application_plugin_get_descriptor(FlipperApplication* app); +/** + * @brief Load name and icon from FAP file. + * + * @param path Path to FAP file. + * @param storage Storage instance. + * @param icon_ptr Icon pointer. + * @param item_name Application name. + * @return true if icon and name were loaded successfully. + */ +bool flipper_application_load_name_and_icon( + FuriString* path, + Storage* storage, + uint8_t** icon_ptr, + FuriString* item_name); + #ifdef __cplusplus } #endif diff --git a/lib/flipper_application/plugins/composite_resolver.c b/lib/flipper_application/plugins/composite_resolver.c index 1402c3ad08..7cc2b340a9 100644 --- a/lib/flipper_application/plugins/composite_resolver.c +++ b/lib/flipper_application/plugins/composite_resolver.c @@ -13,12 +13,12 @@ struct CompositeApiResolver { static bool composite_api_resolver_callback( const ElfApiInterface* interface, - const char* name, + uint32_t hash, Elf32_Addr* address) { CompositeApiResolver* resolver = (CompositeApiResolver*)interface; for M_EACH(interface, resolver->interfaces, ElfApiInterfaceList_t) { - if((*interface)->resolver_callback(*interface, name, address)) { + if((*interface)->resolver_callback(*interface, hash, address)) { return true; } } diff --git a/lib/flipper_application/plugins/plugin_manager.c b/lib/flipper_application/plugins/plugin_manager.c index 101471dc5e..ca4d99dcb0 100644 --- a/lib/flipper_application/plugins/plugin_manager.c +++ b/lib/flipper_application/plugins/plugin_manager.c @@ -11,6 +11,8 @@ #define TAG "libmgr" +#define MAX_NAME_LEN 254 + ARRAY_DEF(FlipperApplicationList, FlipperApplication*, M_PTR_OPLIST) #define M_OPL_FlipperApplicationList_t() ARRAY_OPLIST(FlipperApplicationList, M_PTR_OPLIST) @@ -103,7 +105,7 @@ PluginManagerError plugin_manager_load_single(PluginManager* manager, const char PluginManagerError plugin_manager_load_all(PluginManager* manager, const char* path) { File* directory = storage_file_alloc(manager->storage); - char file_name_buffer[256]; + char file_name_buffer[MAX_NAME_LEN]; FuriString* file_name = furi_string_alloc(); do { if(!storage_dir_open(directory, path)) { diff --git a/lib/infrared/encoder_decoder/infrared.c b/lib/infrared/encoder_decoder/infrared.c index fcfc5da2b2..56f2c3f9ee 100644 --- a/lib/infrared/encoder_decoder/infrared.c +++ b/lib/infrared/encoder_decoder/infrared.c @@ -11,6 +11,7 @@ #include "rc6/infrared_protocol_rc6.h" #include "sirc/infrared_protocol_sirc.h" #include "kaseikyo/infrared_protocol_kaseikyo.h" +#include "rca/infrared_protocol_rca.h" typedef struct { InfraredAlloc alloc; @@ -127,6 +128,20 @@ static const InfraredEncoderDecoder infrared_encoder_decoder[] = { .free = infrared_encoder_kaseikyo_free}, .get_protocol_variant = infrared_protocol_kaseikyo_get_variant, }, + { + .decoder = + {.alloc = infrared_decoder_rca_alloc, + .decode = infrared_decoder_rca_decode, + .reset = infrared_decoder_rca_reset, + .check_ready = infrared_decoder_rca_check_ready, + .free = infrared_decoder_rca_free}, + .encoder = + {.alloc = infrared_encoder_rca_alloc, + .encode = infrared_encoder_rca_encode, + .reset = infrared_encoder_rca_reset, + .free = infrared_encoder_rca_free}, + .get_protocol_variant = infrared_protocol_rca_get_variant, + }, }; static int infrared_find_index_by_protocol(InfraredProtocol protocol); diff --git a/lib/infrared/encoder_decoder/infrared.h b/lib/infrared/encoder_decoder/infrared.h index 3ab46cbbf5..ada449b983 100644 --- a/lib/infrared/encoder_decoder/infrared.h +++ b/lib/infrared/encoder_decoder/infrared.h @@ -33,6 +33,7 @@ typedef enum { InfraredProtocolSIRC15, InfraredProtocolSIRC20, InfraredProtocolKaseikyo, + InfraredProtocolRCA, InfraredProtocolMAX, } InfraredProtocol; diff --git a/lib/infrared/encoder_decoder/rca/infrared_decoder_rca.c b/lib/infrared/encoder_decoder/rca/infrared_decoder_rca.c new file mode 100644 index 0000000000..b6d02a38c7 --- /dev/null +++ b/lib/infrared/encoder_decoder/rca/infrared_decoder_rca.c @@ -0,0 +1,45 @@ +#include "infrared_protocol_rca_i.h" +#include + +InfraredMessage* infrared_decoder_rca_check_ready(void* ctx) { + return infrared_common_decoder_check_ready(ctx); +} + +bool infrared_decoder_rca_interpret(InfraredCommonDecoder* decoder) { + furi_assert(decoder); + + uint32_t* data = (void*)&decoder->data; + + uint8_t address = (*data & 0xF); + uint8_t command = (*data >> 4) & 0xFF; + uint8_t address_inverse = (*data >> 12) & 0xF; + uint8_t command_inverse = (*data >> 16) & 0xFF; + uint8_t inverse_address_inverse = (uint8_t)~address_inverse & 0xF; + uint8_t inverse_command_inverse = (uint8_t)~command_inverse; + + if((command == inverse_command_inverse) && (address == inverse_address_inverse)) { + decoder->message.protocol = InfraredProtocolRCA; + decoder->message.address = address; + decoder->message.command = command; + decoder->message.repeat = false; + return true; + } + + return false; +} + +void* infrared_decoder_rca_alloc(void) { + return infrared_common_decoder_alloc(&infrared_protocol_rca); +} + +InfraredMessage* infrared_decoder_rca_decode(void* decoder, bool level, uint32_t duration) { + return infrared_common_decode(decoder, level, duration); +} + +void infrared_decoder_rca_free(void* decoder) { + infrared_common_decoder_free(decoder); +} + +void infrared_decoder_rca_reset(void* decoder) { + infrared_common_decoder_reset(decoder); +} diff --git a/lib/infrared/encoder_decoder/rca/infrared_encoder_rca.c b/lib/infrared/encoder_decoder/rca/infrared_encoder_rca.c new file mode 100644 index 0000000000..f0be4a6a9e --- /dev/null +++ b/lib/infrared/encoder_decoder/rca/infrared_encoder_rca.c @@ -0,0 +1,37 @@ +#include "infrared_protocol_rca_i.h" + +#include + +void infrared_encoder_rca_reset(void* encoder_ptr, const InfraredMessage* message) { + furi_assert(encoder_ptr); + furi_assert(message); + + InfraredCommonEncoder* encoder = encoder_ptr; + infrared_common_encoder_reset(encoder); + + uint32_t* data = (void*)encoder->data; + + uint8_t address = message->address; + uint8_t address_inverse = ~address; + uint8_t command = message->command; + uint8_t command_inverse = ~command; + + *data = address & 0xF; + *data |= command << 4; + *data |= (address_inverse & 0xF) << 12; + *data |= command_inverse << 16; + + encoder->bits_to_encode = encoder->protocol->databit_len[0]; +} + +void* infrared_encoder_rca_alloc(void) { + return infrared_common_encoder_alloc(&infrared_protocol_rca); +} + +void infrared_encoder_rca_free(void* encoder_ptr) { + infrared_common_encoder_free(encoder_ptr); +} + +InfraredStatus infrared_encoder_rca_encode(void* encoder_ptr, uint32_t* duration, bool* level) { + return infrared_common_encode(encoder_ptr, duration, level); +} diff --git a/lib/infrared/encoder_decoder/rca/infrared_protocol_rca.c b/lib/infrared/encoder_decoder/rca/infrared_protocol_rca.c new file mode 100644 index 0000000000..8e1e76dbd3 --- /dev/null +++ b/lib/infrared/encoder_decoder/rca/infrared_protocol_rca.c @@ -0,0 +1,40 @@ +#include "infrared_protocol_rca_i.h" + +const InfraredCommonProtocolSpec infrared_protocol_rca = { + .timings = + { + .preamble_mark = INFRARED_RCA_PREAMBLE_MARK, + .preamble_space = INFRARED_RCA_PREAMBLE_SPACE, + .bit1_mark = INFRARED_RCA_BIT1_MARK, + .bit1_space = INFRARED_RCA_BIT1_SPACE, + .bit0_mark = INFRARED_RCA_BIT0_MARK, + .bit0_space = INFRARED_RCA_BIT0_SPACE, + .preamble_tolerance = INFRARED_RCA_PREAMBLE_TOLERANCE, + .bit_tolerance = INFRARED_RCA_BIT_TOLERANCE, + .silence_time = INFRARED_RCA_SILENCE, + .min_split_time = INFRARED_RCA_MIN_SPLIT_TIME, + }, + .databit_len[0] = 24, + .no_stop_bit = false, + .decode = infrared_common_decode_pdwm, + .encode = infrared_common_encode_pdwm, + .interpret = infrared_decoder_rca_interpret, + .decode_repeat = NULL, + .encode_repeat = NULL, +}; + +static const InfraredProtocolVariant infrared_protocol_variant_rca = { + .name = "RCA", + .address_length = 4, + .command_length = 8, + .frequency = INFRARED_COMMON_CARRIER_FREQUENCY, + .duty_cycle = INFRARED_COMMON_DUTY_CYCLE, + .repeat_count = INFRARED_RCA_REPEAT_COUNT_MIN, +}; + +const InfraredProtocolVariant* infrared_protocol_rca_get_variant(InfraredProtocol protocol) { + if(protocol == InfraredProtocolRCA) + return &infrared_protocol_variant_rca; + else + return NULL; +} diff --git a/lib/infrared/encoder_decoder/rca/infrared_protocol_rca.h b/lib/infrared/encoder_decoder/rca/infrared_protocol_rca.h new file mode 100644 index 0000000000..d9cae48e48 --- /dev/null +++ b/lib/infrared/encoder_decoder/rca/infrared_protocol_rca.h @@ -0,0 +1,30 @@ +#pragma once + +#include "../infrared_i.h" + +/*************************************************************************************************** +* RCA protocol description +* https://www.sbprojects.net/knowledge/ir/rca.php +**************************************************************************************************** +* Preamble Preamble Pulse Distance/Width Pause Preamble Preamble +* mark space Modulation up to period repeat repeat +* mark space +* +* 4000 4000 24 bit ...8000 4000 4000 +* __________ _ _ _ _ _ _ _ _ _ _ _ _ _ ___________ +* ____ __________ _ _ _ __ __ __ _ _ __ __ _ _ ________________ ___________ +* +***************************************************************************************************/ + +void* infrared_decoder_rca_alloc(void); +void infrared_decoder_rca_reset(void* decoder); +void infrared_decoder_rca_free(void* decoder); +InfraredMessage* infrared_decoder_rca_check_ready(void* decoder); +InfraredMessage* infrared_decoder_rca_decode(void* decoder, bool level, uint32_t duration); + +void* infrared_encoder_rca_alloc(void); +InfraredStatus infrared_encoder_rca_encode(void* encoder_ptr, uint32_t* duration, bool* level); +void infrared_encoder_rca_reset(void* encoder_ptr, const InfraredMessage* message); +void infrared_encoder_rca_free(void* encoder_ptr); + +const InfraredProtocolVariant* infrared_protocol_rca_get_variant(InfraredProtocol protocol); diff --git a/lib/infrared/encoder_decoder/rca/infrared_protocol_rca_i.h b/lib/infrared/encoder_decoder/rca/infrared_protocol_rca_i.h new file mode 100644 index 0000000000..9ec4fe3b17 --- /dev/null +++ b/lib/infrared/encoder_decoder/rca/infrared_protocol_rca_i.h @@ -0,0 +1,30 @@ +#pragma once + +#include "../common/infrared_common_i.h" + +#define INFRARED_RCA_PREAMBLE_MARK 4000 +#define INFRARED_RCA_PREAMBLE_SPACE 4000 +#define INFRARED_RCA_BIT1_MARK 500 +#define INFRARED_RCA_BIT1_SPACE 2000 +#define INFRARED_RCA_BIT0_MARK 500 +#define INFRARED_RCA_BIT0_SPACE 1000 +#define INFRARED_RCA_REPEAT_PERIOD 8000 +#define INFRARED_RCA_SILENCE INFRARED_RCA_REPEAT_PERIOD + +#define INFRARED_RCA_MIN_SPLIT_TIME INFRARED_RCA_REPEAT_PAUSE_MIN +#define INFRARED_RCA_REPEAT_PAUSE_MIN 4000 +#define INFRARED_RCA_REPEAT_PAUSE_MAX 150000 +#define INFRARED_RCA_REPEAT_COUNT_MIN 1 +#define INFRARED_RCA_REPEAT_MARK INFRARED_RCA_PREAMBLE_MARK +#define INFRARED_RCA_REPEAT_SPACE INFRARED_RCA_PREAMBLE_SPACE +#define INFRARED_RCA_PREAMBLE_TOLERANCE 200 // us +#define INFRARED_RCA_BIT_TOLERANCE 120 // us + +extern const InfraredCommonProtocolSpec infrared_protocol_rca; + +bool infrared_decoder_rca_interpret(InfraredCommonDecoder* decoder); +InfraredStatus infrared_decoder_rca_decode_repeat(InfraredCommonDecoder* decoder); +InfraredStatus infrared_encoder_rca_encode_repeat( + InfraredCommonEncoder* encoder, + uint32_t* duration, + bool* level); diff --git a/lib/music_worker/SConscript b/lib/music_worker/SConscript new file mode 100644 index 0000000000..36d01d8596 --- /dev/null +++ b/lib/music_worker/SConscript @@ -0,0 +1,27 @@ +Import("env") + +env.Append( + CPPPATH=[ + "#/lib/music_worker", + ], + SDK_HEADERS=[ + File("music_worker.h"), + ], +) + +libenv = env.Clone(FW_LIB_NAME="music_worker") +libenv.ApplyLibFlags() + +libenv.AppendUnique( + CCFLAGS=[ + # Required for lib to be linkable with .faps + "-mword-relocations", + "-mlong-calls", + ], +) + +sources = libenv.GlobRecursive("*.c*") + +lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources) +libenv.Install("${LIB_DIST_DIR}", lib) +Return("lib") diff --git a/applications/external/music_player/music_player_worker.c b/lib/music_worker/music_worker.c similarity index 85% rename from applications/external/music_player/music_player_worker.c rename to lib/music_worker/music_worker.c index 6a712d3e3f..57efc138c3 100644 --- a/applications/external/music_player/music_player_worker.c +++ b/lib/music_worker/music_worker.c @@ -1,4 +1,4 @@ -#include "music_player_worker.h" +#include "music_worker.h" #include #include @@ -9,7 +9,7 @@ #include #include -#define TAG "MusicPlayerWorker" +#define TAG "MusicWorker" #define MUSIC_PLAYER_FILETYPE "Flipper Music Format" #define MUSIC_PLAYER_VERSION 0 @@ -28,11 +28,11 @@ typedef struct { ARRAY_DEF(NoteBlockArray, NoteBlock, M_POD_OPLIST); -struct MusicPlayerWorker { +struct MusicWorker { FuriThread* thread; bool should_work; - MusicPlayerWorkerCallback callback; + MusicWorkerCallback callback; void* callback_context; float volume; @@ -42,9 +42,9 @@ struct MusicPlayerWorker { NoteBlockArray_t notes; }; -static int32_t music_player_worker_thread_callback(void* context) { +static int32_t music_worker_thread_callback(void* context) { furi_assert(context); - MusicPlayerWorker* instance = context; + MusicWorker* instance = context; NoteBlockArray_it_t it; NoteBlockArray_it(it, instance->notes); @@ -97,24 +97,24 @@ static int32_t music_player_worker_thread_callback(void* context) { return 0; } -MusicPlayerWorker* music_player_worker_alloc() { - MusicPlayerWorker* instance = malloc(sizeof(MusicPlayerWorker)); +MusicWorker* music_worker_alloc() { + MusicWorker* instance = malloc(sizeof(MusicWorker)); NoteBlockArray_init(instance->notes); - instance->thread = furi_thread_alloc_ex( - "MusicPlayerWorker", 1024, music_player_worker_thread_callback, instance); + instance->thread = + furi_thread_alloc_ex("MusicWorker", 1024, music_worker_thread_callback, instance); instance->volume = 1.0f; return instance; } -void music_player_worker_clear(MusicPlayerWorker* instance) { +void music_worker_clear(MusicWorker* instance) { NoteBlockArray_reset(instance->notes); } -void music_player_worker_free(MusicPlayerWorker* instance) { +void music_worker_free(MusicWorker* instance) { furi_assert(instance); furi_thread_free(instance->thread); NoteBlockArray_clear(instance->notes); @@ -186,11 +186,8 @@ static size_t skip_till(const char* string, const char symbol) { return ret; } -static bool music_player_worker_add_note( - MusicPlayerWorker* instance, - uint8_t semitone, - uint8_t duration, - uint8_t dots) { +static bool + music_worker_add_note(MusicWorker* instance, uint8_t semitone, uint8_t duration, uint8_t dots) { NoteBlock note_block; note_block.semitone = semitone; @@ -228,7 +225,7 @@ static int8_t note_to_semitone(const char note) { } } -static bool music_player_worker_parse_notes(MusicPlayerWorker* instance, const char* string) { +static bool music_worker_parse_notes(MusicWorker* instance, const char* string) { const char* cursor = string; bool result = true; @@ -286,7 +283,7 @@ static bool music_player_worker_parse_notes(MusicPlayerWorker* instance, const c semitone += sharp_char == '#' ? 1 : 0; } - if(music_player_worker_add_note(instance, semitone, duration, dots)) { + if(music_worker_add_note(instance, semitone, duration, dots)) { FURI_LOG_D( TAG, "Added note: %c%c%lu.%lu = %u %lu", @@ -316,20 +313,20 @@ static bool music_player_worker_parse_notes(MusicPlayerWorker* instance, const c return result; } -bool music_player_worker_load(MusicPlayerWorker* instance, const char* file_path) { +bool music_worker_load(MusicWorker* instance, const char* file_path) { furi_assert(instance); furi_assert(file_path); bool ret = false; if(strcasestr(file_path, ".fmf")) { - ret = music_player_worker_load_fmf_from_file(instance, file_path); + ret = music_worker_load_fmf_from_file(instance, file_path); } else { - ret = music_player_worker_load_rtttl_from_file(instance, file_path); + ret = music_worker_load_rtttl_from_file(instance, file_path); } return ret; } -bool music_player_worker_load_fmf_from_file(MusicPlayerWorker* instance, const char* file_path) { +bool music_worker_load_fmf_from_file(MusicWorker* instance, const char* file_path) { furi_assert(instance); furi_assert(file_path); @@ -369,7 +366,7 @@ bool music_player_worker_load_fmf_from_file(MusicPlayerWorker* instance, const c break; } - if(!music_player_worker_parse_notes(instance, furi_string_get_cstr(temp_str))) { + if(!music_worker_parse_notes(instance, furi_string_get_cstr(temp_str))) { break; } @@ -383,7 +380,7 @@ bool music_player_worker_load_fmf_from_file(MusicPlayerWorker* instance, const c return result; } -bool music_player_worker_load_rtttl_from_file(MusicPlayerWorker* instance, const char* file_path) { +bool music_worker_load_rtttl_from_file(MusicWorker* instance, const char* file_path) { furi_assert(instance); furi_assert(file_path); @@ -414,7 +411,7 @@ bool music_player_worker_load_rtttl_from_file(MusicPlayerWorker* instance, const break; } - if(!music_player_worker_load_rtttl_from_string(instance, furi_string_get_cstr(content))) { + if(!music_worker_load_rtttl_from_string(instance, furi_string_get_cstr(content))) { FURI_LOG_E(TAG, "Invalid file content"); break; } @@ -429,7 +426,7 @@ bool music_player_worker_load_rtttl_from_file(MusicPlayerWorker* instance, const return result; } -bool music_player_worker_load_rtttl_from_string(MusicPlayerWorker* instance, const char* string) { +bool music_worker_load_rtttl_from_string(MusicWorker* instance, const char* string) { furi_assert(instance); const char* cursor = string; @@ -470,28 +467,25 @@ bool music_player_worker_load_rtttl_from_string(MusicPlayerWorker* instance, con return false; } cursor++; - if(!music_player_worker_parse_notes(instance, cursor)) { + if(!music_worker_parse_notes(instance, cursor)) { return false; } return true; } -void music_player_worker_set_callback( - MusicPlayerWorker* instance, - MusicPlayerWorkerCallback callback, - void* context) { +void music_worker_set_callback(MusicWorker* instance, MusicWorkerCallback callback, void* context) { furi_assert(instance); instance->callback = callback; instance->callback_context = context; } -void music_player_worker_set_volume(MusicPlayerWorker* instance, float volume) { +void music_worker_set_volume(MusicWorker* instance, float volume) { furi_assert(instance); instance->volume = volume; } -void music_player_worker_start(MusicPlayerWorker* instance) { +void music_worker_start(MusicWorker* instance) { furi_assert(instance); furi_assert(instance->should_work == false); @@ -499,10 +493,15 @@ void music_player_worker_start(MusicPlayerWorker* instance) { furi_thread_start(instance->thread); } -void music_player_worker_stop(MusicPlayerWorker* instance) { +void music_worker_stop(MusicWorker* instance) { furi_assert(instance); furi_assert(instance->should_work == true); instance->should_work = false; furi_thread_join(instance->thread); } + +bool music_worker_is_playing(MusicWorker* instance) { + furi_assert(instance); + return instance->should_work; +} diff --git a/lib/music_worker/music_worker.h b/lib/music_worker/music_worker.h new file mode 100644 index 0000000000..5a7cb4936a --- /dev/null +++ b/lib/music_worker/music_worker.h @@ -0,0 +1,37 @@ +#pragma once + +#include +#include + +typedef void (*MusicWorkerCallback)( + uint8_t semitone, + uint8_t dots, + uint8_t duration, + float position, + void* context); + +typedef struct MusicWorker MusicWorker; + +MusicWorker* music_worker_alloc(); + +void music_worker_clear(MusicWorker* instance); + +void music_worker_free(MusicWorker* instance); + +bool music_worker_load(MusicWorker* instance, const char* file_path); + +bool music_worker_load_fmf_from_file(MusicWorker* instance, const char* file_path); + +bool music_worker_load_rtttl_from_file(MusicWorker* instance, const char* file_path); + +bool music_worker_load_rtttl_from_string(MusicWorker* instance, const char* string); + +void music_worker_set_callback(MusicWorker* instance, MusicWorkerCallback callback, void* context); + +void music_worker_set_volume(MusicWorker* instance, float volume); + +void music_worker_start(MusicWorker* instance); + +void music_worker_stop(MusicWorker* instance); + +bool music_worker_is_playing(MusicWorker* instance); diff --git a/lib/nfc/nfc_device.c b/lib/nfc/nfc_device.c index 8c08509a55..38bb767cd2 100644 --- a/lib/nfc/nfc_device.c +++ b/lib/nfc/nfc_device.c @@ -20,6 +20,7 @@ static const uint32_t nfc_keys_file_version = 1; // Protocols format versions static const uint32_t nfc_mifare_classic_data_format_version = 2; static const uint32_t nfc_mifare_ultralight_data_format_version = 1; +static const uint32_t nfc_felica_data_format_version = 1; NfcDevice* nfc_device_alloc() { NfcDevice* nfc_dev = malloc(sizeof(NfcDevice)); @@ -60,6 +61,8 @@ static void nfc_device_prepare_format_string(NfcDevice* dev, FuriString* format_ furi_string_set(format_string, "Mifare DESFire"); } else if(dev->format == NfcDeviceSaveFormatNfcV) { furi_string_set(format_string, "ISO15693"); + } else if(dev->format == NfcDeviceSaveFormatFelica) { + furi_string_set(format_string, "FeliCa"); } else { furi_string_set(format_string, "Unknown"); } @@ -657,178 +660,167 @@ bool nfc_device_load_mifare_df_data(FlipperFormat* file, NfcDevice* dev) { return parsed; } -static bool nfc_device_save_slix_data(FlipperFormat* file, NfcDevice* dev) { - bool saved = false; - NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; - - do { - if(!flipper_format_write_comment_cstr(file, "SLIX specific data")) break; - if(!flipper_format_write_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas))) - break; - saved = true; - } while(false); - - return saved; -} - -bool nfc_device_load_slix_data(FlipperFormat* file, NfcDevice* dev) { - bool parsed = false; - NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; - memset(data, 0, sizeof(NfcVSlixData)); - - do { - if(!flipper_format_read_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas))) - break; - - parsed = true; - } while(false); - - return parsed; -} - -static bool nfc_device_save_slix_s_data(FlipperFormat* file, NfcDevice* dev) { - bool saved = false; - NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; - - do { - if(!flipper_format_write_comment_cstr(file, "SLIX-S specific data")) break; - if(!flipper_format_write_hex(file, "Password Read", data->key_read, sizeof(data->key_read))) - break; - if(!flipper_format_write_hex( - file, "Password Write", data->key_write, sizeof(data->key_write))) - break; - if(!flipper_format_write_hex( - file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy))) - break; - if(!flipper_format_write_hex( - file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy))) - break; - if(!flipper_format_write_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas))) - break; - if(!flipper_format_write_bool(file, "Privacy Mode", &data->privacy, 1)) break; - saved = true; - } while(false); - - return saved; -} - -bool nfc_device_load_slix_s_data(FlipperFormat* file, NfcDevice* dev) { - bool parsed = false; - NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; - memset(data, 0, sizeof(NfcVSlixData)); - - do { - if(!flipper_format_read_hex(file, "Password Read", data->key_read, sizeof(data->key_read))) - break; - if(!flipper_format_read_hex( - file, "Password Write", data->key_write, sizeof(data->key_write))) - break; - if(!flipper_format_read_hex( - file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy))) - break; - if(!flipper_format_read_hex( - file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy))) - break; - if(!flipper_format_read_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas))) - break; - if(!flipper_format_read_bool(file, "Privacy Mode", &data->privacy, 1)) break; - - parsed = true; - } while(false); - - return parsed; -} - -static bool nfc_device_save_slix_l_data(FlipperFormat* file, NfcDevice* dev) { +static bool nfc_device_save_slix_data( + FlipperFormat* file, + NfcDevice* dev, + SlixTypeFeatures features, + const char* type) { bool saved = false; NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; do { - if(!flipper_format_write_comment_cstr(file, "SLIX-L specific data")) break; - if(!flipper_format_write_hex( - file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy))) - break; - if(!flipper_format_write_hex( - file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy))) - break; - if(!flipper_format_write_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas))) - break; - if(!flipper_format_write_bool(file, "Privacy Mode", &data->privacy, 1)) break; - saved = true; - } while(false); - - return saved; -} - -bool nfc_device_load_slix_l_data(FlipperFormat* file, NfcDevice* dev) { - bool parsed = false; - NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; - memset(data, 0, sizeof(NfcVSlixData)); - - do { - if(!flipper_format_read_hex( - file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy))) - break; - if(!flipper_format_read_hex( - file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy))) - break; - if(!flipper_format_read_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas))) + char msg[64]; + snprintf(msg, sizeof(msg), "%s specific data", type); + if(!flipper_format_write_comment_cstr(file, msg)) break; + if(!flipper_format_write_comment_cstr( + file, "Passwords are optional. If password is omitted, any password is accepted")) break; - if(!flipper_format_read_bool(file, "Privacy Mode", &data->privacy, 1)) break; - parsed = true; - } while(false); - - return parsed; -} - -static bool nfc_device_save_slix2_data(FlipperFormat* file, NfcDevice* dev) { - bool saved = false; - NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; - - do { - if(!flipper_format_write_comment_cstr(file, "SLIX2 specific data")) break; - if(!flipper_format_write_hex(file, "Password Read", data->key_read, sizeof(data->key_read))) - break; - if(!flipper_format_write_hex( - file, "Password Write", data->key_write, sizeof(data->key_write))) - break; - if(!flipper_format_write_hex( - file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy))) - break; - if(!flipper_format_write_hex( - file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy))) - break; - if(!flipper_format_write_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas))) - break; - if(!flipper_format_write_bool(file, "Privacy Mode", &data->privacy, 1)) break; + if(features & SlixFeatureRead) { + if(data->flags & NfcVSlixDataFlagsHasKeyRead) { + if(!flipper_format_write_hex( + file, "Password Read", data->key_read, sizeof(data->key_read))) + break; + } + } + if(features & SlixFeatureWrite) { + if(data->flags & NfcVSlixDataFlagsHasKeyWrite) { + if(!flipper_format_write_hex( + file, "Password Write", data->key_write, sizeof(data->key_write))) + break; + } + } + if(features & SlixFeaturePrivacy) { + if(data->flags & NfcVSlixDataFlagsHasKeyPrivacy) { + if(!flipper_format_write_hex( + file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy))) + break; + } + } + if(features & SlixFeatureDestroy) { + if(data->flags & NfcVSlixDataFlagsHasKeyDestroy) { + if(!flipper_format_write_hex( + file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy))) + break; + } + } + if(features & SlixFeatureEas) { + if(data->flags & NfcVSlixDataFlagsHasKeyEas) { + if(!flipper_format_write_hex( + file, "Password EAS", data->key_eas, sizeof(data->key_eas))) + break; + } + } + if(features & SlixFeatureSignature) { + if(!flipper_format_write_comment_cstr( + file, + "This is the card's secp128r1 elliptic curve signature. It can not be calculated without knowing NXP's private key.")) + break; + if(!flipper_format_write_hex( + file, "Signature", data->signature, sizeof(data->signature))) + break; + } + if(features & SlixFeaturePrivacy) { + bool privacy = (data->flags & NfcVSlixDataFlagsPrivacy) ? true : false; + if(!flipper_format_write_bool(file, "Privacy Mode", &privacy, 1)) break; + } + if(features & SlixFeatureProtection) { + if(!flipper_format_write_comment_cstr(file, "Protection pointer configuration")) break; + if(!flipper_format_write_hex(file, "Protection pointer", &data->pp_pointer, 1)) break; + if(!flipper_format_write_hex(file, "Protection condition", &data->pp_condition, 1)) + break; + } saved = true; } while(false); return saved; } -bool nfc_device_load_slix2_data(FlipperFormat* file, NfcDevice* dev) { +bool nfc_device_load_slix_data(FlipperFormat* file, NfcDevice* dev, SlixTypeFeatures features) { bool parsed = false; NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; memset(data, 0, sizeof(NfcVSlixData)); do { - if(!flipper_format_read_hex(file, "Password Read", data->key_read, sizeof(data->key_read))) - break; - if(!flipper_format_read_hex( - file, "Password Write", data->key_write, sizeof(data->key_write))) - break; - if(!flipper_format_read_hex( - file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy))) - break; - if(!flipper_format_read_hex( - file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy))) - break; - if(!flipper_format_read_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas))) - break; - if(!flipper_format_read_bool(file, "Privacy Mode", &data->privacy, 1)) break; + data->flags = 0; + if(features & SlixFeatureRead) { + if(flipper_format_key_exist(file, "Password Read")) { + if(!flipper_format_read_hex( + file, "Password Read", data->key_read, sizeof(data->key_read))) { + FURI_LOG_D(TAG, "Failed reading Password Read"); + break; + } + data->flags |= NfcVSlixDataFlagsHasKeyRead; + } + } + if(features & SlixFeatureWrite) { + if(flipper_format_key_exist(file, "Password Write")) { + if(!flipper_format_read_hex( + file, "Password Write", data->key_write, sizeof(data->key_write))) { + FURI_LOG_D(TAG, "Failed reading Password Write"); + break; + } + data->flags |= NfcVSlixDataFlagsHasKeyWrite; + } + } + if(features & SlixFeaturePrivacy) { + if(flipper_format_key_exist(file, "Password Privacy")) { + if(!flipper_format_read_hex( + file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy))) { + FURI_LOG_D(TAG, "Failed reading Password Privacy"); + break; + } + data->flags |= NfcVSlixDataFlagsHasKeyPrivacy; + } + } + if(features & SlixFeatureDestroy) { + if(flipper_format_key_exist(file, "Password Destroy")) { + if(!flipper_format_read_hex( + file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy))) { + FURI_LOG_D(TAG, "Failed reading Password Destroy"); + break; + } + data->flags |= NfcVSlixDataFlagsHasKeyDestroy; + } + } + if(features & SlixFeatureEas) { + if(flipper_format_key_exist(file, "Password EAS")) { + if(!flipper_format_read_hex( + file, "Password EAS", data->key_eas, sizeof(data->key_eas))) { + FURI_LOG_D(TAG, "Failed reading Password EAS"); + break; + } + data->flags |= NfcVSlixDataFlagsHasKeyEas; + } + } + if(features & SlixFeatureSignature) { + if(!flipper_format_read_hex( + file, "Signature", data->signature, sizeof(data->signature))) { + FURI_LOG_D(TAG, "Failed reading Signature"); + break; + } + } + if(features & SlixFeaturePrivacy) { + bool privacy; + if(!flipper_format_read_bool(file, "Privacy Mode", &privacy, 1)) { + FURI_LOG_D(TAG, "Failed reading Privacy Mode"); + break; + } + if(privacy) { + data->flags |= NfcVSlixDataFlagsPrivacy; + } + } + if(features & SlixFeatureProtection) { + if(!flipper_format_read_hex(file, "Protection pointer", &(data->pp_pointer), 1)) { + FURI_LOG_D(TAG, "Failed reading Protection pointer"); + break; + } + if(!flipper_format_read_hex(file, "Protection condition", &(data->pp_condition), 1)) { + FURI_LOG_D(TAG, "Failed reading Protection condition"); + break; + } + } parsed = true; } while(false); @@ -859,7 +851,8 @@ static bool nfc_device_save_nfcv_data(FlipperFormat* file, NfcDevice* dev) { file, "Data Content", data->data, data->block_num * data->block_size)) break; if(!flipper_format_write_comment_cstr( - file, "First byte: DSFID (0x01) / AFI (0x02) lock info, others: block lock info")) + file, + "First byte: DSFID (0x01) / AFI (0x02) / EAS (0x04) / PPL (0x08) lock info, others: block lock info")) break; if(!flipper_format_write_hex( file, "Security Status", data->security_status, 1 + data->block_num)) @@ -877,16 +870,16 @@ static bool nfc_device_save_nfcv_data(FlipperFormat* file, NfcDevice* dev) { saved = true; break; case NfcVTypeSlix: - saved = nfc_device_save_slix_data(file, dev); + saved = nfc_device_save_slix_data(file, dev, SlixFeatureSlix, "SLIX"); break; case NfcVTypeSlixS: - saved = nfc_device_save_slix_s_data(file, dev); + saved = nfc_device_save_slix_data(file, dev, SlixFeatureSlixS, "SLIX-S"); break; case NfcVTypeSlixL: - saved = nfc_device_save_slix_l_data(file, dev); + saved = nfc_device_save_slix_data(file, dev, SlixFeatureSlixL, "SLIX-L"); break; case NfcVTypeSlix2: - saved = nfc_device_save_slix2_data(file, dev); + saved = nfc_device_save_slix_data(file, dev, SlixFeatureSlix2, "SLIX2"); break; default: break; @@ -906,23 +899,45 @@ bool nfc_device_load_nfcv_data(FlipperFormat* file, NfcDevice* dev) { uint32_t temp_uint32 = 0; uint8_t temp_value = 0; - if(!flipper_format_read_hex(file, "DSFID", &(data->dsfid), 1)) break; - if(!flipper_format_read_hex(file, "AFI", &(data->afi), 1)) break; - if(!flipper_format_read_hex(file, "IC Reference", &(data->ic_ref), 1)) break; - if(!flipper_format_read_uint32(file, "Block Count", &temp_uint32, 1)) break; + if(!flipper_format_read_hex(file, "DSFID", &(data->dsfid), 1)) { + FURI_LOG_D(TAG, "Failed reading DSFID"); + break; + } + if(!flipper_format_read_hex(file, "AFI", &(data->afi), 1)) { + FURI_LOG_D(TAG, "Failed reading AFI"); + break; + } + if(!flipper_format_read_hex(file, "IC Reference", &(data->ic_ref), 1)) { + FURI_LOG_D(TAG, "Failed reading IC Reference"); + break; + } + if(!flipper_format_read_uint32(file, "Block Count", &temp_uint32, 1)) { + FURI_LOG_D(TAG, "Failed reading Block Count"); + break; + } data->block_num = temp_uint32; - if(!flipper_format_read_hex(file, "Block Size", &(data->block_size), 1)) break; + if(!flipper_format_read_hex(file, "Block Size", &(data->block_size), 1)) { + FURI_LOG_D(TAG, "Failed reading Block Size"); + break; + } if(!flipper_format_read_hex( - file, "Data Content", data->data, data->block_num * data->block_size)) + file, "Data Content", data->data, data->block_num * data->block_size)) { + FURI_LOG_D(TAG, "Failed reading Data Content"); break; + } /* optional, as added later */ if(flipper_format_key_exist(file, "Security Status")) { if(!flipper_format_read_hex( - file, "Security Status", data->security_status, 1 + data->block_num)) + file, "Security Status", data->security_status, 1 + data->block_num)) { + FURI_LOG_D(TAG, "Failed reading Security Status"); break; + } + } + if(!flipper_format_read_hex(file, "Subtype", &temp_value, 1)) { + FURI_LOG_D(TAG, "Failed reading Subtype"); + break; } - if(!flipper_format_read_hex(file, "Subtype", &temp_value, 1)) break; data->sub_type = temp_value; switch(data->sub_type) { @@ -930,16 +945,16 @@ bool nfc_device_load_nfcv_data(FlipperFormat* file, NfcDevice* dev) { parsed = true; break; case NfcVTypeSlix: - parsed = nfc_device_load_slix_data(file, dev); + parsed = nfc_device_load_slix_data(file, dev, SlixFeatureSlix); break; case NfcVTypeSlixS: - parsed = nfc_device_load_slix_s_data(file, dev); + parsed = nfc_device_load_slix_data(file, dev, SlixFeatureSlixS); break; case NfcVTypeSlixL: - parsed = nfc_device_load_slix_l_data(file, dev); + parsed = nfc_device_load_slix_data(file, dev, SlixFeatureSlixL); break; case NfcVTypeSlix2: - parsed = nfc_device_load_slix2_data(file, dev); + parsed = nfc_device_load_slix_data(file, dev, SlixFeatureSlix2); break; default: break; @@ -1114,6 +1129,211 @@ static bool nfc_device_save_mifare_classic_data(FlipperFormat* file, NfcDevice* return saved; } +static bool nfc_device_save_felica_lite(FlipperFormat* file, FelicaLiteInfo* info) { + bool saved = false; + FuriString* key = furi_string_alloc(); + FuriString* temp = furi_string_alloc(); + + do { + flipper_format_write_comment_cstr(file, "Lite(-S) System"); + flipper_format_write_hex( + file, "Data Format Code", (uint8_t*)&info->data_format_code, sizeof(uint16_t)); + flipper_format_write_hex(file, "ID Arbitrary Value", info->ID_value, 6); + flipper_format_write_hex(file, "Memory Config", info->memory_config, FELICA_BLOCK_SIZE); + + for(uint8_t block_num = 0; block_num < 14; block_num++) { + furi_string_reset(temp); + for(size_t i = 0; i < FELICA_BLOCK_SIZE; i++) { + if(info->S_PAD[block_num] != NULL) { + furi_string_cat_printf(temp, "%02X ", info->S_PAD[block_num][i]); + } else { + furi_string_cat_printf(temp, "?? "); + } + } + + furi_string_printf(key, "S_PAD%d", block_num); + flipper_format_write_string(file, furi_string_get_cstr(key), temp); + } + + furi_string_reset(temp); + for(size_t i = 0; i < FELICA_BLOCK_SIZE; i++) { + if(info->REG != NULL) { + furi_string_cat_printf(temp, "%02X ", info->REG[i]); + } else { + furi_string_cat_printf(temp, "?? "); + } + } + flipper_format_write_string(file, "REG", temp); + + flipper_format_write_hex( + file, "Card Key Version", (uint8_t*)&info->card_key_version, sizeof(uint16_t)); + furi_string_reset(temp); + for(size_t i = 0; i < FELICA_BLOCK_SIZE; i++) { + if(info->REG != NULL) { + furi_string_cat_printf(temp, "%02X ", info->card_key_1[i]); + } else { + furi_string_cat_printf(temp, "?? "); + } + } + flipper_format_write_string(file, "Card Key 1", temp); + + furi_string_reset(temp); + for(size_t i = 0; i < FELICA_BLOCK_SIZE; i++) { + if(info->REG != NULL) { + furi_string_cat_printf(temp, "%02X ", info->card_key_2[i]); + } else { + furi_string_cat_printf(temp, "?? "); + } + } + flipper_format_write_string(file, "Card Key 2", temp); + + flipper_format_write_hex(file, "Fixed Challenge MAC Response", info->MAC, 8); + + flipper_format_write_bool(file, "Is Lite-S", &info->is_lite_s, 1); + if(info->is_lite_s) { + flipper_format_write_hex(file, "Fixed Challenge MAC-A Response", info->MAC_A, 8); + flipper_format_write_uint32(file, "Write Count", &info->write_count, 1); + } + + } while(false); + + furi_string_free(temp); + furi_string_free(key); + return saved; +} + +static bool nfc_device_save_felica_node(FlipperFormat* file, FelicaNode* node); + +static bool nfc_device_save_felica_area(FlipperFormat* file, FelicaArea* area) { + bool saved = false; + FuriString* prefix = furi_string_alloc_printf("Area %d", area->number); + FuriString* key = furi_string_alloc(); + + do { + furi_string_printf(key, "%s Can Create Subareas", furi_string_get_cstr(prefix)); + flipper_format_write_bool(file, furi_string_get_cstr(key), &area->can_create_subareas, 1); + furi_string_printf(key, "%s End Service Code", furi_string_get_cstr(prefix)); + flipper_format_write_hex( + file, furi_string_get_cstr(key), (uint8_t*)&area->end_service_code, sizeof(uint16_t)); + + bool node_saved = true; + for + M_EACH(node, area->nodes, FelicaNodeArray_t) { + if(nfc_device_save_felica_node(file, node)) { + node_saved = false; + break; + } + } + + if(!node_saved) break; + saved = true; + } while(false); + + furi_string_free(prefix); + furi_string_free(key); + return saved; +} + +static bool nfc_device_save_felica_service(FlipperFormat* file, FelicaService* service) { + bool saved = false; + FuriString* prefix = furi_string_alloc_printf("Service %d", service->number); + FuriString* key = furi_string_alloc(); + + do { + furi_string_printf(key, "%s Is Extended Overlap", furi_string_get_cstr(prefix)); + flipper_format_write_bool( + file, furi_string_get_cstr(key), &service->is_extended_overlap, 1); + if(service->is_extended_overlap) { + furi_string_printf(key, "%s Overlap Target", furi_string_get_cstr(prefix)); + flipper_format_write_hex( + file, + furi_string_get_cstr(key), + (uint8_t*)&service->overlap_target, + sizeof(uint16_t)); + + furi_string_printf(key, "%s Block Start", furi_string_get_cstr(prefix)); + const uint32_t block_start = service->block_start; + flipper_format_write_uint32(file, furi_string_get_cstr(key), &block_start, 1); + + furi_string_printf(key, "%s Block Count", furi_string_get_cstr(prefix)); + const uint32_t block_count = service->block_count; + flipper_format_write_uint32(file, furi_string_get_cstr(key), &block_count, 1); + + uint32_t i = 0; + for + M_EACH(block, service->blocks, FelicaBlockArray_t) { + furi_string_printf(key, "%s Block %ld", furi_string_get_cstr(prefix), i); + flipper_format_write_hex( + file, furi_string_get_cstr(key), block->data, FELICA_BLOCK_SIZE); + } + } else { + furi_string_printf(key, "%s Block Count", furi_string_get_cstr(prefix)); + uint32_t block_count = FelicaBlockArray_size(service->blocks); + flipper_format_write_uint32(file, furi_string_get_cstr(key), &block_count, 1); + uint32_t i = 0; + for + M_EACH(block, service->blocks, FelicaBlockArray_t) { + furi_string_printf(key, "%s Block %ld", furi_string_get_cstr(prefix), i); + flipper_format_write_hex( + file, furi_string_get_cstr(key), block->data, FELICA_BLOCK_SIZE); + } + } + + saved = true; + } while(false); + + furi_string_free(prefix); + furi_string_free(key); + return saved; +} + +static bool nfc_device_save_felica_node(FlipperFormat* file, FelicaNode* node) { + bool saved = false; + + do { + if(node->type == FelicaNodeTypeArea) { + if(!nfc_device_save_felica_area(file, node->area)) { + saved = false; + break; + } + } else if(node->type == FelicaNodeTypeService) { + if(!nfc_device_save_felica_service(file, node->service)) { + saved = false; + break; + } + } + + saved = true; + } while(false); + + return saved; +} + +static bool nfc_device_save_felica_data(FlipperFormat* file, NfcDevice* dev) { + bool saved = false; + FelicaData* data = &dev->dev_data.felica_data; + // Save FeliCa specific data + do { + if(!flipper_format_write_comment_cstr(file, "FeliCa specific data")) break; + if(!flipper_format_write_uint32( + file, "Data format version", &nfc_felica_data_format_version, 1)) + break; + + for + M_EACH(system, data->systems, FelicaSystemArray_t) { + flipper_format_write_hex(file, "System", &system->number, sizeof(uint8_t)); + flipper_format_write_hex(file, "Code", (uint8_t*)&system->code, sizeof(uint16_t)); + if(system->code == LITE_SYSTEM_CODE) { + nfc_device_save_felica_lite(file, &system->lite_info); + } else { + nfc_device_save_felica_node(file, &system->root); + } + } + } while(false); + + return saved; +} + static void nfc_device_load_mifare_classic_block( FuriString* block_str, MfClassicData* data, @@ -1403,39 +1623,42 @@ bool nfc_device_save(NfcDevice* dev, const char* dev_name) { // Write nfc device type if(!flipper_format_write_comment_cstr( file, - "Nfc device type can be UID, Mifare Ultralight, Mifare Classic, Bank card or ISO15693")) + "Nfc device type can be UID, Mifare Ultralight, Mifare Classic, FeliCa or ISO15693")) break; nfc_device_prepare_format_string(dev, temp_str); if(!flipper_format_write_string(file, "Device type", temp_str)) break; - // Write UID - if(!flipper_format_write_comment_cstr(file, "UID is common for all formats")) break; - if(!flipper_format_write_hex(file, "UID", data->uid, data->uid_len)) break; - - if(dev->format != NfcDeviceSaveFormatNfcV) { - // Write ATQA, SAK - if(!flipper_format_write_comment_cstr(file, "ISO14443 specific fields")) break; - // Save ATQA in MSB order for correct companion apps display - uint8_t atqa[2] = {data->a_data.atqa[1], data->a_data.atqa[0]}; - if(!flipper_format_write_hex(file, "ATQA", atqa, 2)) break; - if(!flipper_format_write_hex(file, "SAK", &data->a_data.sak, 1)) break; - } + if(data->type == FuriHalNfcTypeA) { + if(!flipper_format_write_comment_cstr(file, "UID is common for all formats")) break; + if(!flipper_format_write_hex(file, "UID", data->uid, data->uid_len)) break; + + if(dev->format != NfcDeviceSaveFormatNfcV) { + // Write ATQA, SAK + if(!flipper_format_write_comment_cstr(file, "ISO14443 specific fields")) break; + // Save ATQA in MSB order for correct companion apps display + uint8_t atqa[2] = {data->a_data.atqa[1], data->a_data.atqa[0]}; + if(!flipper_format_write_hex(file, "ATQA", atqa, 2)) break; + if(!flipper_format_write_hex(file, "SAK", &data->a_data.sak, 1)) break; + } - // Save more data if necessary - if(dev->format == NfcDeviceSaveFormatMifareUl) { - if(!nfc_device_save_mifare_ul_data(file, dev)) break; - } else if(dev->format == NfcDeviceSaveFormatMifareDesfire) { - if(!nfc_device_save_mifare_df_data(file, dev)) break; - } else if(dev->format == NfcDeviceSaveFormatNfcV) { - if(!nfc_device_save_nfcv_data(file, dev)) break; - } else if(dev->format == NfcDeviceSaveFormatBankCard) { - if(!nfc_device_save_bank_card_data(file, dev)) break; - } else if(dev->format == NfcDeviceSaveFormatMifareClassic) { - // Save data - if(!nfc_device_save_mifare_classic_data(file, dev)) break; - // Save keys cache - if(!nfc_device_save_mifare_classic_keys(dev)) break; + // Save more data if necessary + if(dev->format == NfcDeviceSaveFormatMifareUl) { + if(!nfc_device_save_mifare_ul_data(file, dev)) break; + } else if(dev->format == NfcDeviceSaveFormatMifareDesfire) { + if(!nfc_device_save_mifare_df_data(file, dev)) break; + } else if(dev->format == NfcDeviceSaveFormatNfcV) { + if(!nfc_device_save_nfcv_data(file, dev)) break; + } else if(dev->format == NfcDeviceSaveFormatBankCard) { + if(!nfc_device_save_bank_card_data(file, dev)) break; + } else if(dev->format == NfcDeviceSaveFormatMifareClassic) { + // Save data + if(!nfc_device_save_mifare_classic_data(file, dev)) break; + // Save keys cache + if(!nfc_device_save_mifare_classic_keys(dev)) break; + } + saved = true; + } else if(data->type == FuriHalNfcTypeF) { + if(!nfc_device_save_felica_data(file, dev)) break; } - saved = true; } while(0); if(!saved) { //-V547 diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c index f8b22f2c8f..984108a927 100644 --- a/lib/nfc/nfc_worker.c +++ b/lib/nfc/nfc_worker.c @@ -175,7 +175,6 @@ void nfc_worker_nfcv_emulate(NfcWorker* nfc_worker) { } } } - furi_delay_ms(10); } nfcv_emu_deinit(nfcv_data); @@ -203,7 +202,6 @@ void nfc_worker_nfcv_sniff(NfcWorker* nfc_worker) { nfc_worker->callback(NfcWorkerEventNfcVCommandExecuted, nfc_worker->context); } } - furi_delay_ms(10); } nfcv_emu_deinit(nfcv_data); @@ -299,10 +297,10 @@ void nfc_worker_nfcv_unlock(NfcWorker* nfc_worker) { ret = slix_unlock(nfcv_data, 4); } else { key = 0x7FFD6E5B; - key_data[0] = key >> 24; - key_data[1] = key >> 16; - key_data[2] = key >> 8; - key_data[3] = key >> 0; + key_data[0] = (key >> 24) & 0xFF; + key_data[1] = (key >> 16) & 0xFF; + key_data[2] = (key >> 8) & 0xFF; + key_data[3] = (key >> 0) & 0xFF; ret = slix_unlock(nfcv_data, 4); if(ret != ERR_NONE) { @@ -319,10 +317,10 @@ void nfc_worker_nfcv_unlock(NfcWorker* nfc_worker) { } key = 0x0F0F0F0F; - key_data[0] = key >> 24; - key_data[1] = key >> 16; - key_data[2] = key >> 8; - key_data[3] = key >> 0; + key_data[0] = (key >> 24) & 0xFF; + key_data[1] = (key >> 16) & 0xFF; + key_data[2] = (key >> 8) & 0xFF; + key_data[3] = (key >> 0) & 0xFF; ret = slix_unlock(nfcv_data, 4); } } @@ -1087,14 +1085,14 @@ void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker) { deactivated = true; } else { // If the key A is marked as found and matches the searching key, invalidate it - uint8_t found_key[6]; - memcpy(found_key, data->block[i].value, 6); + MfClassicSectorTrailer* sec_trailer = + mf_classic_get_sector_trailer_by_sector(data, i); uint8_t current_key[6]; - memcpy(current_key, &key, 6); + nfc_util_num2bytes(key, 6, current_key); if(mf_classic_is_key_found(data, i, MfClassicKeyA) && - found_key == current_key) { + memcmp(sec_trailer->key_a, current_key, 6) == 0) { mf_classic_set_key_not_found(data, i, MfClassicKeyA); is_key_a_found = false; FURI_LOG_D(TAG, "Key %dA not found in attack", i); @@ -1113,14 +1111,14 @@ void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker) { deactivated = true; } else { // If the key B is marked as found and matches the searching key, invalidate it - uint8_t found_key[6]; - memcpy(found_key, data->block[i].value + 10, 6); + MfClassicSectorTrailer* sec_trailer = + mf_classic_get_sector_trailer_by_sector(data, i); uint8_t current_key[6]; - memcpy(current_key, &key, 6); + nfc_util_num2bytes(key, 6, current_key); if(mf_classic_is_key_found(data, i, MfClassicKeyB) && - found_key == current_key) { + memcmp(sec_trailer->key_b, current_key, 6) == 0) { mf_classic_set_key_not_found(data, i, MfClassicKeyB); is_key_b_found = false; FURI_LOG_D(TAG, "Key %dB not found in attack", i); @@ -1136,7 +1134,7 @@ void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker) { } if(nfc_worker->state != NfcWorkerStateMfClassicDictAttack) break; } - memcpy(&prev_key, &key, sizeof(key)); + prev_key = key; } if(nfc_worker->state != NfcWorkerStateMfClassicDictAttack) break; mf_classic_read_sector(&tx_rx, data, i); @@ -1165,7 +1163,7 @@ void nfc_worker_emulate_mf_classic(NfcWorker* nfc_worker) { furi_hal_nfc_listen_start(nfc_data); while(nfc_worker->state == NfcWorkerStateMfClassicEmulate) { //-V1044 if(furi_hal_nfc_listen_rx(&tx_rx, 300)) { - mf_classic_emulator(&emulator, &tx_rx); + mf_classic_emulator(&emulator, &tx_rx, false); } } if(emulator.data_changed) { @@ -1450,7 +1448,7 @@ void nfc_worker_analyze_reader(NfcWorker* nfc_worker) { NfcProtocol protocol = reader_analyzer_guess_protocol(reader_analyzer, tx_rx.rx_data, tx_rx.rx_bits / 8); if(protocol == NfcDeviceProtocolMifareClassic) { - mf_classic_emulator(&emulator, &tx_rx); + mf_classic_emulator(&emulator, &tx_rx, true); } } else { reader_no_data_received_cnt++; diff --git a/lib/nfc/nfc_worker.h b/lib/nfc/nfc_worker.h index 89ce91186c..a175c212e1 100644 --- a/lib/nfc/nfc_worker.h +++ b/lib/nfc/nfc_worker.h @@ -44,8 +44,8 @@ typedef enum { NfcWorkerEventReadMfClassicDone, NfcWorkerEventReadMfClassicLoadKeyCache, NfcWorkerEventReadMfClassicDictAttackRequired, - NfcWorkerEventReadNfcV, NfcWorkerEventReadBankCard, + NfcWorkerEventReadNfcV, // Nfc worker common events NfcWorkerEventSuccess, diff --git a/lib/nfc/protocols/felica.c b/lib/nfc/protocols/felica.c index 8271875649..182893527b 100644 --- a/lib/nfc/protocols/felica.c +++ b/lib/nfc/protocols/felica.c @@ -1,4 +1,5 @@ #include +#include #include #include "felica.h" #include "nfc_util.h" @@ -151,6 +152,215 @@ FelicaICType felica_get_ic_type(uint8_t* PMm) { return FelicaICType2K; } +// static void felica_lite_diversify_key(uint8_t* id_block, uint8_t* master_key, uint8_t* card_key) { +// uint8_t ZERO[8] = {0}; +// uint8_t L[8]; +// mbedtls_des3_context ctx; +// mbedtls_des3_init(&ctx); +// mbedtls_des3_set3key_enc(&ctx, master_key); +// mbedtls_des3_crypt_ecb(&ctx, ZERO, L); +// mbedtls_des3_free(&ctx); + +// uint8_t K1[8]; +// for(int i = 0; i < 8; i++) { +// K1[i] = L[i] << 1; +// if(i < 7) { +// K1[i] |= (L[i + 1] >> 7); +// } +// } + +// if((L[0] ^ 0x80) == 0) { +// K1[7] ^= 0x1B; +// } + +// uint8_t M1[8]; +// uint8_t M2[8]; +// memcpy(M1, id_block, 8); +// memcpy(M2, id_block + 8, 8); +// for(int i = 0; i < 8; i++) { +// M2[i] ^= K1[i]; +// } + +// uint8_t C1[8]; +// mbedtls_des3_init(&ctx); +// mbedtls_des3_set3key_enc(&ctx, master_key); +// mbedtls_des3_crypt_ecb(&ctx, M1, C1); +// for(int i = 0; i < 8; i++) { +// C1[i] ^= M2[i]; +// } + +// mbedtls_des3_crypt_ecb(&ctx, C1, card_key); // T + +// M1[0] ^= 0x80; // M'1 +// mbedtls_des3_crypt_ecb(&ctx, M1, C1); // C'1 +// for(int i = 0; i < 8; i++) { +// C1[i] ^= M2[i]; +// } + +// mbedtls_des3_crypt_ecb(&ctx, C1, card_key + 8); // T' +// mbedtls_des3_free(&ctx); +// } + +// static void felica_lite_generate_session_key( +// uint8_t* random_challenge, +// uint8_t* card_key, +// uint8_t* session_key) { +// uint8_t RC1[8]; +// uint8_t RC2[8]; +// uint8_t CK[16]; + +// for(int i = 0; i < 8; i++) { +// RC1[i] = random_challenge[7 - i]; +// RC2[i] = random_challenge[i]; +// CK[i] = card_key[7 - i]; +// CK[i + 8] = card_key[15 - i]; +// } + +// mbedtls_des3_context ctx; + +// uint8_t SK1[8]; +// mbedtls_des3_init(&ctx); +// mbedtls_des3_set2key_enc(&ctx, CK); +// mbedtls_des3_crypt_ecb(&ctx, RC1, SK1); + +// uint8_t SK2[8]; +// for(int i = 0; i < 8; i++) { +// RC2[i] ^= SK1[i]; +// } +// mbedtls_des3_crypt_ecb(&ctx, RC2, SK2); +// mbedtls_des3_free(&ctx); + +// for(int i = 0; i < 8; i++) { +// session_key[i] = SK1[7 - i]; +// session_key[i + 8] = SK2[7 - i]; +// } +// } + +// static void felica_lite_calculate_mac( +// uint8_t* random_challenge, +// uint8_t* session_key, +// uint8_t* block_data, +// size_t block_count, +// uint8_t* MAC) { +// uint8_t SK[16]; + +// for(int i = 0; i < 8; i++) { +// MAC[i] = random_challenge[7 - i]; +// SK[i] = session_key[7 - i]; +// SK[i + 8] = session_key[15 - i]; +// } + +// mbedtls_des3_context ctx; +// mbedtls_des3_init(&ctx); +// mbedtls_des3_set3key_enc(&ctx, SK); + +// for(size_t block_num = 0; block_num < block_count; block_num++) { +// for(int i = 0; i < 8; i++) { +// MAC[i] ^= block_data[block_num * FELICA_BLOCK_SIZE + 7 - i]; +// } + +// uint8_t intermediate[8]; +// mbedtls_des3_crypt_ecb(&ctx, MAC, intermediate); +// for(int i = 0; i < 8; i++) { +// intermediate[i] ^= block_data[block_num * FELICA_BLOCK_SIZE + 15 - i]; +// } + +// mbedtls_des3_crypt_ecb(&ctx, intermediate, MAC); +// } + +// mbedtls_des3_free(&ctx); +// } + +// static void felica_lite_calculate_mac_a( +// uint8_t* random_challenge, +// uint8_t* session_key, +// uint8_t* iv, +// uint8_t* block_data, +// size_t block_count, +// uint8_t* MAC_A) { +// uint8_t SK[16]; +// uint8_t intermediate_a[8]; +// uint8_t intermediate_b[8]; + +// for(int i = 0; i < 8; i++) { +// intermediate_a[i] = iv[7 - 1] ^ random_challenge[7 - i]; +// SK[i] = session_key[7 - i]; +// SK[i + 8] = session_key[15 - i]; +// } + +// mbedtls_des3_context ctx; +// mbedtls_des3_init(&ctx); +// mbedtls_des3_set3key_enc(&ctx, SK); +// mbedtls_des3_crypt_ecb(&ctx, intermediate_a, intermediate_b); + +// for(size_t block_num = 0; block_num < block_count; block_num++) { +// for(int i = 0; i < 8; i++) { +// intermediate_b[i] ^= block_data[block_num * FELICA_BLOCK_SIZE + 7 - i]; +// } + +// mbedtls_des3_crypt_ecb(&ctx, intermediate_b, intermediate_a); +// for(int i = 0; i < 8; i++) { +// intermediate_a[i] ^= block_data[block_num * FELICA_BLOCK_SIZE + 7 - i]; +// } + +// mbedtls_des3_crypt_ecb(&ctx, intermediate_a, intermediate_b); +// } + +// for(int i = 0; i < 8; i++) { +// MAC_A[i] = intermediate_b[7 - 1]; +// } +// } + +// static void felica_lite_calculate_mac_a_for_write( +// uint8_t* random_challenge, +// uint8_t* session_key, +// uint32_t write_count, +// uint8_t block_number, +// uint8_t* block_data, +// uint8_t* MAC_A) { +// uint8_t iv[8]; +// nfc_util_num2bytes(write_count, 3, iv); +// iv[3] = 0x00; +// iv[4] = block_number; +// iv[5] = 0x00; +// iv[6] = 0x91; +// iv[7] = 0x00; + +// uint8_t SK[16]; +// for(int i = 0; i < 8; i++) { +// SK[i] = session_key[i + 8]; +// SK[i + 8] = session_key[i]; +// } + +// felica_lite_calculate_mac_a(random_challenge, SK, iv, block_data, 1, MAC_A); +// } + +// static void felica_lite_calculate_mac_a_for_read( +// uint8_t* random_challenge, +// uint8_t* session_key, +// uint8_t* block_list, +// uint8_t block_list_count, +// uint8_t* block_data, +// uint8_t block_count, +// uint8_t* MAC_A) { +// uint8_t iv[8] = {0}; + +// uint8_t block_list_to_write = MIN(block_list_count, 4); +// for(int i = 0; i < block_list_to_write; i++) { +// iv[i * 2] = block_list[i]; +// } +// if(block_list_to_write < 4) { +// iv[6] = 0xFF; +// iv[7] = 0xFF; +// } +// if(block_list_to_write < 3) { +// iv[4] = 0xFF; +// iv[5] = 0xFF; +// } + +// felica_lite_calculate_mac_a(random_challenge, session_key, iv, block_data, block_count, MAC_A); +// } + /** Parse common FeliCa response headers. * * This parses and validates the most commonly occurring response header types. @@ -165,7 +375,7 @@ FelicaICType felica_get_ic_type(uint8_t* PMm) { * @param always_succeed When set to true, skip status flags (sf1 and sf2) parsing. * @return The number of bytes parsed, or 0 when response is invalid or status flags are set. */ -static uint8_t felica_consume_header( +static uint8_t felica_consume_unencrypted_header( uint8_t* buf, uint8_t len, FelicaReader* reader, @@ -261,7 +471,8 @@ uint16_t felica_parse_unencrypted_read( FelicaReader* reader, uint8_t* out, uint16_t out_len) { - uint8_t consumed = felica_consume_header(buf, len, reader, FELICA_UNENCRYPTED_READ_RES, false); + uint8_t consumed = + felica_consume_unencrypted_header(buf, len, reader, FELICA_UNENCRYPTED_READ_RES, false); if(!consumed) { return 0; } @@ -345,7 +556,7 @@ uint8_t felica_lite_prepare_unencrypted_write( bool felica_parse_unencrypted_write(uint8_t* buf, uint8_t len, FelicaReader* reader) { uint8_t consumed = - felica_consume_header(buf, len, reader, FELICA_UNENCRYPTED_WRITE_RES, false); + felica_consume_unencrypted_header(buf, len, reader, FELICA_UNENCRYPTED_WRITE_RES, false); if(!consumed) { return false; } @@ -364,7 +575,7 @@ bool felica_parse_request_system_code( FelicaReader* reader, FelicaSystemArray_t* systems) { uint8_t consumed = - felica_consume_header(buf, len, reader, FELICA_REQUEST_SYSTEM_CODE_RES, true); + felica_consume_unencrypted_header(buf, len, reader, FELICA_REQUEST_SYSTEM_CODE_RES, true); if(consumed == 0) { return false; } @@ -588,6 +799,7 @@ bool felica_lite_dump_data( } } if(data->type == FelicaICTypeLiteS) { + lite_info->is_lite_s = true; const uint8_t fixed_s_blocks[] = { CARD_KEY_LITE_BLOCK, MAC_A_LITE_BLOCK, @@ -728,4 +940,4 @@ void felica_clear(FelicaData* data) { } } FelicaSystemArray_clear(data->systems); -} \ No newline at end of file +} diff --git a/lib/nfc/protocols/felica.h b/lib/nfc/protocols/felica.h index f8e504102e..876596bb56 100644 --- a/lib/nfc/protocols/felica.h +++ b/lib/nfc/protocols/felica.h @@ -206,6 +206,7 @@ typedef struct { uint16_t card_key_version; uint8_t memory_config[FELICA_BLOCK_SIZE]; + bool is_lite_s; // Lite-S only uint8_t MAC_A[8]; uint32_t write_count; @@ -294,8 +295,6 @@ uint8_t felica_lite_prepare_unencrypted_write( const uint8_t* block_data); bool felica_parse_unencrypted_write(uint8_t* buf, uint8_t len, FelicaReader* reader); -bool felica_lite_can_read_without_mac(uint8_t* mc_r_restr, uint8_t block_number); - void felica_define_normal_block(FelicaService* service, uint16_t number, uint8_t* data); void felica_push_normal_block(FelicaService* service, uint8_t* data); diff --git a/lib/nfc/protocols/felica_util.c b/lib/nfc/protocols/felica_util.c index 481ea5ed7d..47f66fcc56 100644 --- a/lib/nfc/protocols/felica_util.c +++ b/lib/nfc/protocols/felica_util.c @@ -11,6 +11,20 @@ uint_least32_t felica_estimate_timing_us(uint_least8_t timing, uint_least8_t uni return TIME_CONSTANT_US * scale * (base_cost_factor + unit_cost_factor * units); } +bool felica_lite_is_issued(FelicaLiteInfo* lite_info) { + // System blocks aren't writable? + if(lite_info->memory_config[2] == 0x00) { + return false; + } + + // MC is not writable? + if(lite_info->memory_config[1] & 0x80) { + return false; + } + + return true; +} + FuriString* felica_get_system_name(FelicaSystem* system) { uint16_t code = system->code; diff --git a/lib/nfc/protocols/felica_util.h b/lib/nfc/protocols/felica_util.h index e53d668054..de7ff884a8 100644 --- a/lib/nfc/protocols/felica_util.h +++ b/lib/nfc/protocols/felica_util.h @@ -1,5 +1,6 @@ #include "./felica.h" uint_least32_t felica_estimate_timing_us(uint_least8_t timing, uint_least8_t units); +bool felica_lite_is_issued(FelicaLiteInfo* lite_info); FuriString* felica_get_system_name(FelicaSystem* system); FuriString* felica_get_service_name(FelicaService* service); \ No newline at end of file diff --git a/lib/nfc/protocols/mifare_classic.c b/lib/nfc/protocols/mifare_classic.c index 799a734631..f11d81f37b 100644 --- a/lib/nfc/protocols/mifare_classic.c +++ b/lib/nfc/protocols/mifare_classic.c @@ -850,7 +850,10 @@ uint8_t mf_classic_update_card(FuriHalNfcTxRxContext* tx_rx, MfClassicData* data return sectors_read; } -bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_rx) { +bool mf_classic_emulator( + MfClassicEmulator* emulator, + FuriHalNfcTxRxContext* tx_rx, + bool is_reader_analyzer) { furi_assert(emulator); furi_assert(tx_rx); bool command_processed = false; @@ -897,11 +900,27 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ MfClassicSectorTrailer* sector_trailer = (MfClassicSectorTrailer*)emulator->data.block[sector_trailer_block].value; if(cmd == MF_CLASSIC_AUTH_KEY_A_CMD) { - key = nfc_util_bytes2num(sector_trailer->key_a, 6); - access_key = MfClassicKeyA; + if(mf_classic_is_key_found( + &emulator->data, mf_classic_get_sector_by_block(block), MfClassicKeyA) || + is_reader_analyzer) { + key = nfc_util_bytes2num(sector_trailer->key_a, 6); + access_key = MfClassicKeyA; + } else { + FURI_LOG_D(TAG, "Key not known"); + command_processed = true; + break; + } } else { - key = nfc_util_bytes2num(sector_trailer->key_b, 6); - access_key = MfClassicKeyB; + if(mf_classic_is_key_found( + &emulator->data, mf_classic_get_sector_by_block(block), MfClassicKeyB) || + is_reader_analyzer) { + key = nfc_util_bytes2num(sector_trailer->key_b, 6); + access_key = MfClassicKeyB; + } else { + FURI_LOG_D(TAG, "Key not known"); + command_processed = true; + break; + } } uint32_t nonce = prng_successor(DWT->CYCCNT, 32) ^ 0xAA; diff --git a/lib/nfc/protocols/mifare_classic.h b/lib/nfc/protocols/mifare_classic.h index f39c2ca68f..4e35ac6717 100644 --- a/lib/nfc/protocols/mifare_classic.h +++ b/lib/nfc/protocols/mifare_classic.h @@ -199,7 +199,10 @@ uint8_t mf_classic_read_card( uint8_t mf_classic_update_card(FuriHalNfcTxRxContext* tx_rx, MfClassicData* data); -bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_rx); +bool mf_classic_emulator( + MfClassicEmulator* emulator, + FuriHalNfcTxRxContext* tx_rx, + bool is_reader_analyzer); void mf_classic_halt(FuriHalNfcTxRxContext* tx_rx, Crypto1* crypto); diff --git a/lib/nfc/protocols/nfcv.c b/lib/nfc/protocols/nfcv.c index f9848ae065..017b06cae0 100644 --- a/lib/nfc/protocols/nfcv.c +++ b/lib/nfc/protocols/nfcv.c @@ -52,7 +52,7 @@ ReturnCode nfcv_read_blocks(NfcVReader* reader, NfcVData* nfcv_data) { uint16_t received = 0; for(size_t block = 0; block < nfcv_data->block_num; block++) { uint8_t rxBuf[32]; - //FURI_LOG_D(TAG, "Reading block %d/%d", block, (nfcv_data->block_num - 1)); + FURI_LOG_D(TAG, "Reading block %d/%d", block, (nfcv_data->block_num - 1)); ReturnCode ret = ERR_NONE; for(int tries = 0; tries < NFCV_COMMAND_RETRIES; tries++) { @@ -64,18 +64,18 @@ ReturnCode nfcv_read_blocks(NfcVReader* reader, NfcVData* nfcv_data) { } } if(ret != ERR_NONE) { - //FURI_LOG_D(TAG, "failed to read: %d", ret); + FURI_LOG_D(TAG, "failed to read: %d", ret); return ret; } memcpy( &(nfcv_data->data[block * nfcv_data->block_size]), &rxBuf[1], nfcv_data->block_size); - /*FURI_LOG_D( + FURI_LOG_D( TAG, " %02X %02X %02X %02X", nfcv_data->data[block * nfcv_data->block_size + 0], nfcv_data->data[block * nfcv_data->block_size + 1], nfcv_data->data[block * nfcv_data->block_size + 2], - nfcv_data->data[block * nfcv_data->block_size + 3]);*/ + nfcv_data->data[block * nfcv_data->block_size + 3]); } return ERR_NONE; @@ -86,7 +86,7 @@ ReturnCode nfcv_read_sysinfo(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data) { uint16_t received = 0; ReturnCode ret = ERR_NONE; - //FURI_LOG_D(TAG, "Read SYSTEM INFORMATION..."); + FURI_LOG_D(TAG, "Read SYSTEM INFORMATION..."); for(int tries = 0; tries < NFCV_COMMAND_RETRIES; tries++) { /* TODO: needs proper abstraction via fury_hal(_ll)_* */ @@ -110,7 +110,7 @@ ReturnCode nfcv_read_sysinfo(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data) { nfcv_data->block_num = rxBuf[NFCV_UID_LENGTH + 4] + 1; nfcv_data->block_size = rxBuf[NFCV_UID_LENGTH + 5] + 1; nfcv_data->ic_ref = rxBuf[NFCV_UID_LENGTH + 6]; - /*FURI_LOG_D( + FURI_LOG_D( TAG, " UID: %02X %02X %02X %02X %02X %02X %02X %02X", nfc_data->uid[0], @@ -128,10 +128,10 @@ ReturnCode nfcv_read_sysinfo(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data) { nfcv_data->afi, nfcv_data->block_num, nfcv_data->block_size, - nfcv_data->ic_ref);*/ + nfcv_data->ic_ref); return ret; } - //FURI_LOG_D(TAG, "Failed: %d", ret); + FURI_LOG_D(TAG, "Failed: %d", ret); return ret; } @@ -149,12 +149,18 @@ bool nfcv_read_card(NfcVReader* reader, FuriHalNfcDevData* nfc_data, NfcVData* n return false; } + /* clear all know sub type data before reading them */ + memset(&nfcv_data->sub_data, 0x00, sizeof(nfcv_data->sub_data)); + if(slix_check_card_type(nfc_data)) { FURI_LOG_I(TAG, "NXP SLIX detected"); nfcv_data->sub_type = NfcVTypeSlix; } else if(slix2_check_card_type(nfc_data)) { FURI_LOG_I(TAG, "NXP SLIX2 detected"); nfcv_data->sub_type = NfcVTypeSlix2; + if(slix2_read_custom(nfc_data, nfcv_data) != ERR_NONE) { + return false; + } } else if(slix_s_check_card_type(nfc_data)) { FURI_LOG_I(TAG, "NXP SLIX-S detected"); nfcv_data->sub_type = NfcVTypeSlixS; @@ -438,7 +444,7 @@ void nfcv_emu_send( furi_assert(nfcv); /* picked default value (0) to match the most common format */ - if(!flags) { + if(flags == NfcVSendFlagsNormal) { flags = NfcVSendFlagsSof | NfcVSendFlagsCrc | NfcVSendFlagsEof | NfcVSendFlagsOneSubcarrier | NfcVSendFlagsHighRate; } @@ -612,9 +618,34 @@ void nfcv_emu_handle_packet( if(ctx->flags & NFCV_REQ_FLAG_AFI) { uint8_t afi = nfcv_data->frame[ctx->payload_offset]; - if(afi == nfcv_data->afi) { - respond = true; + + uint8_t family = (afi & 0xF0); + uint8_t subfamily = (afi & 0x0F); + + if(family) { + if(subfamily) { + /* selected family and subfamily only */ + if(afi == nfcv_data->afi) { + respond = true; + } + } else { + /* selected family, any subfamily */ + if(family == (nfcv_data->afi & 0xf0)) { + respond = true; + } + } + } else { + if(subfamily) { + /* proprietary subfamily only */ + if(afi == nfcv_data->afi) { + respond = true; + } + } else { + /* all families and subfamilies */ + respond = true; + } } + } else { respond = true; } @@ -740,13 +771,19 @@ void nfcv_emu_handle_packet( case NFCV_CMD_READ_MULTI_BLOCK: case NFCV_CMD_READ_BLOCK: { uint8_t block = nfcv_data->frame[ctx->payload_offset]; - uint8_t blocks = 1; + int blocks = 1; if(ctx->command == NFCV_CMD_READ_MULTI_BLOCK) { blocks = nfcv_data->frame[ctx->payload_offset + 1] + 1; } - if(block + blocks <= nfcv_data->block_num) { + /* limit the maximum block count, underflow accepted */ + if(block + blocks > nfcv_data->block_num) { + blocks = nfcv_data->block_num - block; + } + + /* only respond with the valid blocks, if there are any */ + if(blocks > 0) { uint8_t buffer_pos = 0; ctx->response_buffer[buffer_pos++] = NFCV_NOERROR; @@ -773,10 +810,13 @@ void nfcv_emu_handle_packet( ctx->response_flags, ctx->send_time); } else { - ctx->response_buffer[0] = NFCV_RES_FLAG_ERROR; - ctx->response_buffer[1] = NFCV_ERROR_GENERIC; - nfcv_emu_send( - tx_rx, nfcv_data, ctx->response_buffer, 2, ctx->response_flags, ctx->send_time); + /* reply with an error only in addressed or selected mode */ + if(ctx->addressed || ctx->selected) { + ctx->response_buffer[0] = NFCV_RES_FLAG_ERROR; + ctx->response_buffer[1] = NFCV_ERROR_GENERIC; + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 2, ctx->response_flags, ctx->send_time); + } } snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "READ BLOCK %d", block); @@ -1084,9 +1124,9 @@ void nfcv_emu_sniff_packet( break; } - /*if(strlen(nfcv_data->last_command) > 0) { + if(strlen(nfcv_data->last_command) > 0) { FURI_LOG_D(TAG, "Received command %s", nfcv_data->last_command); - }*/ + } } void nfcv_emu_init(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data) { @@ -1140,7 +1180,7 @@ void nfcv_emu_init(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data) { } } - /*FURI_LOG_D(TAG, "Starting NfcV emulation"); + FURI_LOG_D(TAG, "Starting NfcV emulation"); FURI_LOG_D( TAG, " UID: %02X %02X %02X %02X %02X %02X %02X %02X", @@ -1151,7 +1191,7 @@ void nfcv_emu_init(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data) { nfc_data->uid[4], nfc_data->uid[5], nfc_data->uid[6], - nfc_data->uid[7]);*/ + nfc_data->uid[7]); switch(nfcv_data->sub_type) { case NfcVTypeSlixL: @@ -1326,7 +1366,7 @@ bool nfcv_emu_loop( bits_received += 2; if(periods == 1) { - byte_value |= 0x00 << 6; + byte_value |= 0x00 << 6; // -V684 periods_previous = 6; } else if(periods == 3) { byte_value |= 0x01 << 6; diff --git a/lib/nfc/protocols/nfcv.h b/lib/nfc/protocols/nfcv.h index 87a6967375..e4139de998 100644 --- a/lib/nfc/protocols/nfcv.h +++ b/lib/nfc/protocols/nfcv.h @@ -139,8 +139,10 @@ typedef enum { } NfcVErrorcodes; typedef enum { - NfcVLockBitDsfid = 1, - NfcVLockBitAfi = 2, + NfcVLockBitDsfid = 1 << 0, + NfcVLockBitAfi = 1 << 1, + NfcVLockBitEas = 1 << 2, + NfcVLockBitPpl = 1 << 3, } NfcVLockBits; typedef enum { @@ -168,14 +170,55 @@ typedef enum { NfcVSendFlagsHighRate = 1 << 4 } NfcVSendFlags; +/* SLIX specific config flags */ +typedef enum { + NfcVSlixDataFlagsNone = 0, + NfcVSlixDataFlagsHasKeyRead = 1 << 0, + NfcVSlixDataFlagsHasKeyWrite = 1 << 1, + NfcVSlixDataFlagsHasKeyPrivacy = 1 << 2, + NfcVSlixDataFlagsHasKeyDestroy = 1 << 3, + NfcVSlixDataFlagsHasKeyEas = 1 << 4, + NfcVSlixDataFlagsValidKeyRead = 1 << 8, + NfcVSlixDataFlagsValidKeyWrite = 1 << 9, + NfcVSlixDataFlagsValidKeyPrivacy = 1 << 10, + NfcVSlixDataFlagsValidKeyDestroy = 1 << 11, + NfcVSlixDataFlagsValidKeyEas = 1 << 12, + NfcVSlixDataFlagsPrivacy = 1 << 16, + NfcVSlixDataFlagsDestroyed = 1 << 17 +} NfcVSlixDataFlags; + +/* abstract the file read/write operations for all SLIX types to reduce duplicated code */ +typedef enum { + SlixFeatureRead = 1 << 0, + SlixFeatureWrite = 1 << 1, + SlixFeaturePrivacy = 1 << 2, + SlixFeatureDestroy = 1 << 3, + SlixFeatureEas = 1 << 4, + SlixFeatureSignature = 1 << 5, + SlixFeatureProtection = 1 << 6, + + SlixFeatureSlix = SlixFeatureEas, + SlixFeatureSlixS = + (SlixFeatureRead | SlixFeatureWrite | SlixFeaturePrivacy | SlixFeatureDestroy | + SlixFeatureEas), + SlixFeatureSlixL = (SlixFeaturePrivacy | SlixFeatureDestroy | SlixFeatureEas), + SlixFeatureSlix2 = + (SlixFeatureRead | SlixFeatureWrite | SlixFeaturePrivacy | SlixFeatureDestroy | + SlixFeatureEas | SlixFeatureSignature | SlixFeatureProtection), +} SlixTypeFeatures; + typedef struct { + uint32_t flags; uint8_t key_read[4]; uint8_t key_write[4]; uint8_t key_privacy[4]; uint8_t key_destroy[4]; uint8_t key_eas[4]; uint8_t rand[2]; - bool privacy; + uint8_t signature[32]; + /* SLIX2 options */ + uint8_t pp_pointer; + uint8_t pp_condition; } NfcVSlixData; typedef union { diff --git a/lib/nfc/protocols/slix.c b/lib/nfc/protocols/slix.c index ec3afc248b..68937d161a 100644 --- a/lib/nfc/protocols/slix.c +++ b/lib/nfc/protocols/slix.c @@ -9,6 +9,120 @@ #define TAG "SLIX" +ReturnCode slix2_read_nxp_sysinfo(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data) { + furi_assert(nfc_data); + furi_assert(nfcv_data); + + uint8_t rxBuf[32]; + uint16_t received = 0; + ReturnCode ret = ERR_NONE; + + FURI_LOG_D(TAG, "Read NXP SYSTEM INFORMATION..."); + + for(int tries = 0; tries < NFCV_COMMAND_RETRIES; tries++) { + uint8_t cmd[] = {}; + uint8_t uid[NFCV_UID_LENGTH]; + + /* UID is stored reversed in requests */ + for(int pos = 0; pos < nfc_data->uid_len; pos++) { + uid[pos] = nfc_data->uid[nfc_data->uid_len - 1 - pos]; + } + + ReturnCode ret = rfalNfcvPollerTransceiveReq( + NFCV_CMD_NXP_GET_NXP_SYSTEM_INFORMATION, + RFAL_NFCV_REQ_FLAG_DEFAULT, + NFCV_MANUFACTURER_NXP, + uid, + cmd, + sizeof(cmd), + rxBuf, + sizeof(rxBuf), + &received); + + if(ret == ERR_NONE) { + break; + } + } + + if(ret != ERR_NONE || received != 8) { //-V560 + FURI_LOG_D(TAG, "Failed: %d, %d", ret, received); + return ret; + } + FURI_LOG_D(TAG, "Success..."); + + NfcVSlixData* slix = &nfcv_data->sub_data.slix; + slix->pp_pointer = rxBuf[1]; + slix->pp_condition = rxBuf[2]; + + /* convert NXP's to our internal lock bits format */ + nfcv_data->security_status[0] = 0; + nfcv_data->security_status[0] |= (rxBuf[3] & SlixLockBitDsfid) ? NfcVLockBitDsfid : 0; + nfcv_data->security_status[0] |= (rxBuf[3] & SlixLockBitAfi) ? NfcVLockBitAfi : 0; + nfcv_data->security_status[0] |= (rxBuf[3] & SlixLockBitEas) ? NfcVLockBitEas : 0; + nfcv_data->security_status[0] |= (rxBuf[3] & SlixLockBitPpl) ? NfcVLockBitPpl : 0; + + return ERR_NONE; +} + +ReturnCode slix2_read_signature(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data) { + furi_assert(nfc_data); + furi_assert(nfcv_data); + + uint8_t rxBuf[64]; + uint16_t received = 0; + ReturnCode ret = ERR_NONE; + + FURI_LOG_D(TAG, "Read SIGNATURE..."); + + for(int tries = 0; tries < NFCV_COMMAND_RETRIES; tries++) { + uint8_t cmd[] = {}; + uint8_t uid[NFCV_UID_LENGTH]; + + /* UID is stored reversed in requests */ + for(int pos = 0; pos < nfc_data->uid_len; pos++) { + uid[pos] = nfc_data->uid[nfc_data->uid_len - 1 - pos]; + } + + ReturnCode ret = rfalNfcvPollerTransceiveReq( + NFCV_CMD_NXP_READ_SIGNATURE, + RFAL_NFCV_REQ_FLAG_DEFAULT, + NFCV_MANUFACTURER_NXP, + uid, + cmd, + sizeof(cmd), + rxBuf, + sizeof(rxBuf), + &received); + + if(ret == ERR_NONE) { + break; + } + } + + if(ret != ERR_NONE || received != 33) { //-V560 + FURI_LOG_D(TAG, "Failed: %d, %d", ret, received); + return ret; + } + FURI_LOG_D(TAG, "Success..."); + + NfcVSlixData* slix = &nfcv_data->sub_data.slix; + memcpy(slix->signature, &rxBuf[1], 32); + + return ERR_NONE; +} + +ReturnCode slix2_read_custom(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data) { + ReturnCode ret = ERR_NONE; + + ret = slix2_read_nxp_sysinfo(nfc_data, nfcv_data); + if(ret != ERR_NONE) { + return ret; + } + ret = slix2_read_signature(nfc_data, nfcv_data); + + return ret; +} + static uint32_t slix_read_be(uint8_t* data, uint32_t length) { uint32_t value = 0; @@ -137,6 +251,43 @@ ReturnCode slix_unlock(NfcVData* data, uint32_t password_id) { return ret; } +static void slix_generic_pass_infos( + uint8_t password_id, + NfcVSlixData* slix, + uint8_t** password, + uint32_t* flag_valid, + uint32_t* flag_set) { + switch(password_id) { + case SLIX_PASS_READ: + *password = slix->key_read; + *flag_valid = NfcVSlixDataFlagsValidKeyRead; + *flag_set = NfcVSlixDataFlagsHasKeyRead; + break; + case SLIX_PASS_WRITE: + *password = slix->key_write; + *flag_valid = NfcVSlixDataFlagsValidKeyWrite; + *flag_set = NfcVSlixDataFlagsHasKeyWrite; + break; + case SLIX_PASS_PRIVACY: + *password = slix->key_privacy; + *flag_valid = NfcVSlixDataFlagsValidKeyPrivacy; + *flag_set = NfcVSlixDataFlagsHasKeyPrivacy; + break; + case SLIX_PASS_DESTROY: + *password = slix->key_destroy; + *flag_valid = NfcVSlixDataFlagsValidKeyDestroy; + *flag_set = NfcVSlixDataFlagsHasKeyDestroy; + break; + case SLIX_PASS_EASAFI: + *password = slix->key_eas; + *flag_valid = NfcVSlixDataFlagsValidKeyEas; + *flag_set = NfcVSlixDataFlagsHasKeyEas; + break; + default: + break; + } +} + bool slix_generic_protocol_filter( FuriHalNfcTxRxContext* tx_rx, FuriHalNfcDevData* nfc_data, @@ -150,7 +301,8 @@ bool slix_generic_protocol_filter( NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx; NfcVSlixData* slix = &nfcv_data->sub_data.slix; - if(slix->privacy && ctx->command != NFCV_CMD_NXP_GET_RANDOM_NUMBER && + if((slix->flags & NfcVSlixDataFlagsPrivacy) && + ctx->command != NFCV_CMD_NXP_GET_RANDOM_NUMBER && ctx->command != NFCV_CMD_NXP_SET_PASSWORD) { snprintf( nfcv_data->last_command, @@ -186,66 +338,73 @@ bool slix_generic_protocol_filter( } case NFCV_CMD_NXP_SET_PASSWORD: { + /* the password to be set is the first parameter */ uint8_t password_id = nfcv_data->frame[ctx->payload_offset]; + /* right after that is the XORed password */ + uint8_t* password_xored = &nfcv_data->frame[ctx->payload_offset + 1]; + /* only handle if the password type is supported */ if(!(password_id & password_supported)) { break; } - uint8_t* password_xored = &nfcv_data->frame[ctx->payload_offset + 1]; + /* fetch the last RAND value */ uint8_t* rand = slix->rand; - uint8_t* password = NULL; - uint8_t password_rcv[4]; - switch(password_id) { - case SLIX_PASS_READ: - password = slix->key_read; - break; - case SLIX_PASS_WRITE: - password = slix->key_write; - break; - case SLIX_PASS_PRIVACY: - password = slix->key_privacy; - break; - case SLIX_PASS_DESTROY: - password = slix->key_destroy; - break; - case SLIX_PASS_EASAFI: - password = slix->key_eas; - break; - default: - break; + /* first calc the password that has been sent */ + uint8_t password_rcv[4]; + for(int pos = 0; pos < 4; pos++) { + password_rcv[pos] = password_xored[3 - pos] ^ rand[pos % 2]; } + uint32_t pass_received = slix_read_be(password_rcv, 4); + /* then determine the password type (or even update if not set yet) */ + uint8_t* password = NULL; + uint32_t flag_valid = 0; + uint32_t flag_set = 0; + + slix_generic_pass_infos(password_id, slix, &password, &flag_valid, &flag_set); + + /* when the password is not supported, return silently */ if(!password) { break; } - for(int pos = 0; pos < 4; pos++) { - password_rcv[pos] = password_xored[3 - pos] ^ rand[pos % 2]; + /* check if the password is known */ + bool pass_valid = false; + uint32_t pass_expect = 0; + + if(slix->flags & flag_set) { + /* if so, fetch the stored password and compare */ + pass_expect = slix_read_be(password, 4); + pass_valid = (pass_expect == pass_received); + } else { + /* if not known, just accept it and store that password */ + memcpy(password, password_rcv, 4); + nfcv_data->modified = true; + slix->flags |= flag_set; + + pass_valid = true; } - uint32_t pass_expect = slix_read_be(password, 4); - uint32_t pass_received = slix_read_be(password_rcv, 4); - /* if the password is all-zeroes, just accept any password*/ - if(!pass_expect || pass_expect == pass_received) { + /* if the pass was valid or accepted for other reasons, continue */ + if(pass_valid) { + slix->flags |= flag_valid; + + /* handle actions when a correct password was given, aside of setting the flag */ switch(password_id) { - case SLIX_PASS_READ: - break; - case SLIX_PASS_WRITE: - break; case SLIX_PASS_PRIVACY: - slix->privacy = false; + slix->flags &= ~NfcVSlixDataFlagsPrivacy; nfcv_data->modified = true; break; case SLIX_PASS_DESTROY: + slix->flags |= NfcVSlixDataFlagsDestroyed; FURI_LOG_D(TAG, "Pooof! Got destroyed"); break; - case SLIX_PASS_EASAFI: - break; default: break; } + ctx->response_buffer[0] = NFCV_NOERROR; nfcv_emu_send( tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); @@ -268,6 +427,49 @@ bool slix_generic_protocol_filter( break; } + case NFCV_CMD_NXP_WRITE_PASSWORD: { + uint8_t password_id = nfcv_data->frame[ctx->payload_offset]; + + if(!(password_id & password_supported)) { + break; + } + + uint8_t* new_password = &nfcv_data->frame[ctx->payload_offset + 1]; + uint8_t* password = NULL; + uint32_t flag_valid = 0; + uint32_t flag_set = 0; + + slix_generic_pass_infos(password_id, slix, &password, &flag_valid, &flag_set); + + /* when the password is not supported, return silently */ + if(!password) { + break; + } + + bool pass_valid = (slix->flags & flag_valid); + if(!(slix->flags & flag_set)) { + pass_valid = true; + } + + if(pass_valid) { + slix->flags |= flag_valid; + slix->flags |= flag_set; + + memcpy(password, new_password, 4); + + ctx->response_buffer[0] = NFCV_NOERROR; + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); + snprintf( + nfcv_data->last_command, sizeof(nfcv_data->last_command), "WRITE_PASSWORD OK"); + } else { + snprintf( + nfcv_data->last_command, sizeof(nfcv_data->last_command), "WRITE_PASSWORD FAIL"); + } + handled = true; + break; + } + case NFCV_CMD_NXP_ENABLE_PRIVACY: { ctx->response_buffer[0] = NFCV_NOERROR; @@ -278,7 +480,7 @@ bool slix_generic_protocol_filter( sizeof(nfcv_data->last_command), "NFCV_CMD_NXP_ENABLE_PRIVACY"); - slix->privacy = true; + slix->flags |= NfcVSlixDataFlagsPrivacy; handled = true; break; } @@ -315,7 +517,10 @@ void slix_l_prepare(NfcVData* nfcv_data) { FURI_LOG_D( TAG, " Destroy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_destroy, 4)); FURI_LOG_D(TAG, " EAS pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_eas, 4)); - FURI_LOG_D(TAG, " Privacy mode: %s", nfcv_data->sub_data.slix.privacy ? "ON" : "OFF"); + FURI_LOG_D( + TAG, + " Privacy mode: %s", + (nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsPrivacy) ? "ON" : "OFF"); NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx; ctx->emu_protocol_filter = &slix_l_protocol_filter; @@ -345,7 +550,10 @@ void slix_s_prepare(NfcVData* nfcv_data) { FURI_LOG_D( TAG, " Destroy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_destroy, 4)); FURI_LOG_D(TAG, " EAS pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_eas, 4)); - FURI_LOG_D(TAG, " Privacy mode: %s", nfcv_data->sub_data.slix.privacy ? "ON" : "OFF"); + FURI_LOG_D( + TAG, + " Privacy mode: %s", + (nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsPrivacy) ? "ON" : "OFF"); NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx; ctx->emu_protocol_filter = &slix_s_protocol_filter; @@ -375,13 +583,16 @@ void slix_prepare(NfcVData* nfcv_data) { FURI_LOG_D( TAG, " Destroy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_destroy, 4)); FURI_LOG_D(TAG, " EAS pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_eas, 4)); - FURI_LOG_D(TAG, " Privacy mode: %s", nfcv_data->sub_data.slix.privacy ? "ON" : "OFF"); + FURI_LOG_D( + TAG, + " Privacy mode: %s", + (nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsPrivacy) ? "ON" : "OFF"); NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx; ctx->emu_protocol_filter = &slix_protocol_filter; } -bool slix2_protocol_filter( +bool slix2_protocol_filter( // -V524 FuriHalNfcTxRxContext* tx_rx, FuriHalNfcDevData* nfc_data, void* nfcv_data_in) { @@ -389,6 +600,10 @@ bool slix2_protocol_filter( furi_assert(nfc_data); furi_assert(nfcv_data_in); + NfcVData* nfcv_data = (NfcVData*)nfcv_data_in; + NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx; + NfcVSlixData* slix = &nfcv_data->sub_data.slix; + bool handled = false; /* many SLIX share some of the functions, place that in a generic handler */ @@ -396,6 +611,160 @@ bool slix2_protocol_filter( return true; } + switch(ctx->command) { + /* override WRITE BLOCK for block 79 (16 bit counter) */ + case NFCV_CMD_WRITE_BLOCK: + case NFCV_CMD_WRITE_MULTI_BLOCK: { + uint8_t resp_len = 1; + uint8_t blocks = 1; + uint8_t block = nfcv_data->frame[ctx->payload_offset]; + uint8_t data_pos = ctx->payload_offset + 1; + + if(ctx->command == NFCV_CMD_WRITE_MULTI_BLOCK) { + blocks = nfcv_data->frame[data_pos] + 1; + data_pos++; + } + + uint8_t* data = &nfcv_data->frame[data_pos]; + uint32_t data_len = nfcv_data->block_size * blocks; + + if((block + blocks) <= nfcv_data->block_num && + (data_pos + data_len + 2) == nfcv_data->frame_length) { + ctx->response_buffer[0] = NFCV_NOERROR; + + for(int block_num = block; block_num < block + blocks; block_num++) { + /* special case, 16-bit counter */ + if(block_num == 79) { + uint32_t dest; + uint32_t ctr_old; + + memcpy(&dest, &nfcv_data->frame[data_pos], 4); + memcpy(&ctr_old, &nfcv_data->data[nfcv_data->block_size * block_num], 4); + + uint32_t ctr_new = ctr_old; + bool allowed = true; + + /* increment counter */ + if(dest == 1) { + ctr_new = (ctr_old & 0xFFFF0000) | ((ctr_old + 1) & 0xFFFF); + + /* protection flag set? */ + if(ctr_old & 0x01000000) { //-V1051 + allowed = nfcv_data->sub_data.slix.flags & + NfcVSlixDataFlagsValidKeyRead; + } + } else { + ctr_new = dest; + allowed = nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsValidKeyWrite; + } + + if(allowed) { + memcpy( //-V1086 + &nfcv_data->data[nfcv_data->block_size * block_num], + &ctr_new, + 4); + } else { + /* incorrect read or write password */ + ctx->response_buffer[0] = NFCV_RES_FLAG_ERROR; + ctx->response_buffer[1] = NFCV_ERROR_GENERIC; + resp_len = 2; + } + } else { + memcpy( + &nfcv_data->data[nfcv_data->block_size * block_num], + &nfcv_data->frame[data_pos], + nfcv_data->block_size); + } + data_pos += nfcv_data->block_size; + } + nfcv_data->modified = true; + + } else { + ctx->response_buffer[0] = NFCV_RES_FLAG_ERROR; + ctx->response_buffer[1] = NFCV_ERROR_GENERIC; + resp_len = 2; + } + + bool respond = (ctx->response_buffer[0] == NFCV_NOERROR) || + (ctx->addressed || ctx->selected); + + if(respond) { + nfcv_emu_send( + tx_rx, + nfcv_data, + ctx->response_buffer, + resp_len, + ctx->response_flags, + ctx->send_time); + } + + if(ctx->command == NFCV_CMD_WRITE_MULTI_BLOCK) { + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "WRITE MULTI BLOCK %d, %d blocks", + block, + blocks); + } else { + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "WRITE BLOCK %d <- %02X %02X %02X %02X", + block, + data[0], + data[1], + data[2], + data[3]); + } + handled = true; + break; + } + + case NFCV_CMD_NXP_READ_SIGNATURE: { + uint32_t len = 0; + ctx->response_buffer[len++] = NFCV_NOERROR; + memcpy(&ctx->response_buffer[len], slix->signature, sizeof(slix->signature)); + len += sizeof(slix->signature); + + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, len, ctx->response_flags, ctx->send_time); + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "READ_SIGNATURE"); + + handled = true; + break; + } + + case NFCV_CMD_NXP_GET_NXP_SYSTEM_INFORMATION: { + uint32_t len = 0; + uint8_t lock_bits = 0; + + /* convert our internal lock bits format into NXP's */ + lock_bits |= (nfcv_data->security_status[0] & NfcVLockBitDsfid) ? SlixLockBitDsfid : 0; + lock_bits |= (nfcv_data->security_status[0] & NfcVLockBitAfi) ? SlixLockBitAfi : 0; + lock_bits |= (nfcv_data->security_status[0] & NfcVLockBitEas) ? SlixLockBitEas : 0; + lock_bits |= (nfcv_data->security_status[0] & NfcVLockBitPpl) ? SlixLockBitPpl : 0; + + ctx->response_buffer[len++] = NFCV_NOERROR; + ctx->response_buffer[len++] = nfcv_data->sub_data.slix.pp_pointer; + ctx->response_buffer[len++] = nfcv_data->sub_data.slix.pp_condition; + ctx->response_buffer[len++] = lock_bits; + ctx->response_buffer[len++] = 0x7F; /* features LSB */ + ctx->response_buffer[len++] = 0x35; /* features */ + ctx->response_buffer[len++] = 0; /* features */ + ctx->response_buffer[len++] = 0; /* features MSB */ + + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, len, ctx->response_flags, ctx->send_time); + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "GET_NXP_SYSTEM_INFORMATION"); + + handled = true; + break; + } + } + return handled; } @@ -405,7 +774,10 @@ void slix2_prepare(NfcVData* nfcv_data) { FURI_LOG_D( TAG, " Destroy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_destroy, 4)); FURI_LOG_D(TAG, " EAS pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_eas, 4)); - FURI_LOG_D(TAG, " Privacy mode: %s", nfcv_data->sub_data.slix.privacy ? "ON" : "OFF"); + FURI_LOG_D( + TAG, + " Privacy mode: %s", + (nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsPrivacy) ? "ON" : "OFF"); NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx; ctx->emu_protocol_filter = &slix2_protocol_filter; diff --git a/lib/nfc/protocols/slix.h b/lib/nfc/protocols/slix.h index 701fa2f820..67f09e46d6 100644 --- a/lib/nfc/protocols/slix.h +++ b/lib/nfc/protocols/slix.h @@ -8,19 +8,35 @@ #define NFCV_MANUFACTURER_NXP 0x04 /* ISO15693-3 CUSTOM NXP COMMANDS */ -#define NFCV_CMD_NXP_SET_EAS 0xA2 -#define NFCV_CMD_NXP_RESET_EAS 0xA3 -#define NFCV_CMD_NXP_LOCK_EAS 0xA4 -#define NFCV_CMD_NXP_EAS_ALARM 0xA5 -#define NFCV_CMD_NXP_PASSWORD_PROTECT_EAS_AFI 0xA6 -#define NFCV_CMD_NXP_WRITE_EAS_ID 0xA7 -#define NFCV_CMD_NXP_INVENTORY_PAGE_READ 0xB0 -#define NFCV_CMD_NXP_INVENTORY_PAGE_READ_FAST 0xB1 -#define NFCV_CMD_NXP_GET_RANDOM_NUMBER 0xB2 -#define NFCV_CMD_NXP_SET_PASSWORD 0xB3 -#define NFCV_CMD_NXP_WRITE_PASSWORD 0xB4 -#define NFCV_CMD_NXP_DESTROY 0xB9 -#define NFCV_CMD_NXP_ENABLE_PRIVACY 0xBA +typedef enum { + NFCV_CMD_NXP_SET_EAS = 0xA2, + NFCV_CMD_NXP_RESET_EAS = 0xA3, + NFCV_CMD_NXP_LOCK_EAS = 0xA4, + NFCV_CMD_NXP_EAS_ALARM = 0xA5, + NFCV_CMD_NXP_PASSWORD_PROTECT_EAS_AFI = 0xA6, + NFCV_CMD_NXP_WRITE_EAS_ID = 0xA7, + NFCV_CMD_NXP_GET_NXP_SYSTEM_INFORMATION = 0xAB, + NFCV_CMD_NXP_INVENTORY_PAGE_READ = 0xB0, + NFCV_CMD_NXP_INVENTORY_PAGE_READ_FAST = 0xB1, + NFCV_CMD_NXP_GET_RANDOM_NUMBER = 0xB2, + NFCV_CMD_NXP_SET_PASSWORD = 0xB3, + NFCV_CMD_NXP_WRITE_PASSWORD = 0xB4, + NFCV_CMD_NXP_64_BIT_PASSWORD_PROTECTION = 0xB5, + NFCV_CMD_NXP_PROTECT_PAGE = 0xB6, + NFCV_CMD_NXP_LOCK_PAGE_PROTECTION_CONDITION = 0xB7, + NFCV_CMD_NXP_DESTROY = 0xB9, + NFCV_CMD_NXP_ENABLE_PRIVACY = 0xBA, + NFCV_CMD_NXP_STAY_QUIET_PERSISTENT = 0xBC, + NFCV_CMD_NXP_READ_SIGNATURE = 0xBD +} SlixCommands; + +/* lock bit bits used in SLIX's NXP SYSTEM INFORMATION response */ +typedef enum { + SlixLockBitAfi = 1 << 0, + SlixLockBitEas = 1 << 1, + SlixLockBitDsfid = 1 << 2, + SlixLockBitPpl = 1 << 3, +} SlixLockBits; /* available passwords */ #define SLIX_PASS_READ 0x01 @@ -37,6 +53,10 @@ bool slix2_check_card_type(FuriHalNfcDevData* nfc_data); bool slix_s_check_card_type(FuriHalNfcDevData* nfc_data); bool slix_l_check_card_type(FuriHalNfcDevData* nfc_data); +ReturnCode slix2_read_custom(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data); +ReturnCode slix2_read_signature(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data); +ReturnCode slix2_read_nxp_sysinfo(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data); + ReturnCode slix_get_random(NfcVData* data); ReturnCode slix_unlock(NfcVData* data, uint32_t password_id); diff --git a/lib/stm32wb_copro b/lib/stm32wb_copro index d685979c28..6c9c54f056 160000 --- a/lib/stm32wb_copro +++ b/lib/stm32wb_copro @@ -1 +1 @@ -Subproject commit d685979c282c9f38d561dd1ea8e6fdbd735d7362 +Subproject commit 6c9c54f05669b2c4d436df58bb691d3b0d7c86df diff --git a/lib/subghz/SConscript b/lib/subghz/SConscript index 9b96c73067..40e5a9b631 100644 --- a/lib/subghz/SConscript +++ b/lib/subghz/SConscript @@ -7,11 +7,10 @@ env.Append( SDK_HEADERS=[ File("environment.h"), File("receiver.h"), + File("registry.h"), File("subghz_worker.h"), File("subghz_tx_rx_worker.h"), File("transmitter.h"), - File("registry.h"), - File("protocols/protocol_items.h"), File("protocols/raw.h"), File("blocks/const.h"), File("blocks/custom_btn.h"), @@ -20,6 +19,7 @@ env.Append( File("blocks/generic.h"), File("blocks/math.h"), File("subghz_setting.h"), + File("subghz_protocol_registry.h"), ], ) diff --git a/lib/subghz/environment.c b/lib/subghz/environment.c index bcada87522..b69494e669 100644 --- a/lib/subghz/environment.c +++ b/lib/subghz/environment.c @@ -96,16 +96,17 @@ const char* void subghz_environment_set_protocol_registry( SubGhzEnvironment* instance, - void* protocol_registry_items) { + const SubGhzProtocolRegistry* protocol_registry_items) { furi_assert(instance); const SubGhzProtocolRegistry* protocol_registry = protocol_registry_items; instance->protocol_registry = protocol_registry; } -void* subghz_environment_get_protocol_registry(SubGhzEnvironment* instance) { +const SubGhzProtocolRegistry* + subghz_environment_get_protocol_registry(SubGhzEnvironment* instance) { furi_assert(instance); furi_assert(instance->protocol_registry); - return (void*)instance->protocol_registry; + return instance->protocol_registry; } const char* diff --git a/lib/subghz/environment.h b/lib/subghz/environment.h index 67de48df9c..ff694acb86 100644 --- a/lib/subghz/environment.h +++ b/lib/subghz/environment.h @@ -1,6 +1,7 @@ #pragma once #include +#include "registry.h" #include "subghz_keystore.h" @@ -9,6 +10,7 @@ extern "C" { #endif typedef struct SubGhzEnvironment SubGhzEnvironment; +typedef struct SubGhzProtocolRegistry SubGhzProtocolRegistry; /** * Allocate SubGhzEnvironment. @@ -93,14 +95,15 @@ const char* */ void subghz_environment_set_protocol_registry( SubGhzEnvironment* instance, - void* protocol_registry_items); + const SubGhzProtocolRegistry* protocol_registry_items); /** * Get list of protocols to work. * @param instance Pointer to a SubGhzEnvironment instance * @return Pointer to a SubGhzProtocolRegistry */ -void* subghz_environment_get_protocol_registry(SubGhzEnvironment* instance); +const SubGhzProtocolRegistry* + subghz_environment_get_protocol_registry(SubGhzEnvironment* instance); /** * Get list of protocols names. diff --git a/lib/subghz/protocols/kia.c b/lib/subghz/protocols/kia.c index 1d134f7bab..2d2ceaa1d4 100644 --- a/lib/subghz/protocols/kia.c +++ b/lib/subghz/protocols/kia.c @@ -63,7 +63,8 @@ const SubGhzProtocolEncoder subghz_protocol_kia_encoder = { const SubGhzProtocol subghz_protocol_kia = { .name = SUBGHZ_PROTOCOL_KIA_NAME, .type = SubGhzProtocolTypeDynamic, - .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_FM | SubGhzProtocolFlag_Decodable, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_FM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_AutoAlarms, .decoder = &subghz_protocol_kia_decoder, .encoder = &subghz_protocol_kia_encoder, diff --git a/lib/subghz/protocols/magellan.c b/lib/subghz/protocols/magellan.c index 2d02a866c2..be819ff318 100644 --- a/lib/subghz/protocols/magellan.c +++ b/lib/subghz/protocols/magellan.c @@ -64,7 +64,8 @@ const SubGhzProtocol subghz_protocol_magellan = { .name = SUBGHZ_PROTOCOL_MAGELLAN_NAME, .type = SubGhzProtocolTypeStatic, .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | - SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send | + SubGhzProtocolFlag_Magelan, .decoder = &subghz_protocol_magellan_decoder, .encoder = &subghz_protocol_magellan_encoder, diff --git a/lib/subghz/protocols/protocol_items.h b/lib/subghz/protocols/protocol_items.h index b7e082bf55..f1a28ac9b5 100644 --- a/lib/subghz/protocols/protocol_items.h +++ b/lib/subghz/protocols/protocol_items.h @@ -1,5 +1,6 @@ #pragma once #include "../registry.h" +#include "../subghz_protocol_registry.h" #include "princeton.h" #include "keeloq.h" @@ -43,13 +44,3 @@ #include "alutech_at_4n.h" #include "kinggates_stylo_4k.h" #include "bin_raw.h" - -#ifdef __cplusplus -extern "C" { -#endif - -extern const SubGhzProtocolRegistry subghz_protocol_registry; - -#ifdef __cplusplus -} -#endif \ No newline at end of file diff --git a/lib/subghz/protocols/scher_khan.c b/lib/subghz/protocols/scher_khan.c index c2fa77b2f1..53b7935d69 100644 --- a/lib/subghz/protocols/scher_khan.c +++ b/lib/subghz/protocols/scher_khan.c @@ -70,7 +70,7 @@ const SubGhzProtocol subghz_protocol_scher_khan = { .name = SUBGHZ_PROTOCOL_SCHER_KHAN_NAME, .type = SubGhzProtocolTypeDynamic, .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_FM | SubGhzProtocolFlag_Decodable | - SubGhzProtocolFlag_Save, + SubGhzProtocolFlag_Save | SubGhzProtocolFlag_AutoAlarms, .decoder = &subghz_protocol_scher_khan_decoder, .encoder = &subghz_protocol_scher_khan_encoder, diff --git a/lib/subghz/protocols/star_line.c b/lib/subghz/protocols/star_line.c index 05afd80a67..bf338b35d0 100644 --- a/lib/subghz/protocols/star_line.c +++ b/lib/subghz/protocols/star_line.c @@ -79,7 +79,8 @@ const SubGhzProtocol subghz_protocol_star_line = { .name = SUBGHZ_PROTOCOL_STAR_LINE_NAME, .type = SubGhzProtocolTypeDynamic, .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | - SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send | + SubGhzProtocolFlag_StarLine, .decoder = &subghz_protocol_star_line_decoder, .encoder = &subghz_protocol_star_line_encoder, diff --git a/lib/subghz/registry.h b/lib/subghz/registry.h index 91027807e8..8529c10970 100644 --- a/lib/subghz/registry.h +++ b/lib/subghz/registry.h @@ -9,6 +9,7 @@ extern "C" { typedef struct SubGhzEnvironment SubGhzEnvironment; typedef struct SubGhzProtocolRegistry SubGhzProtocolRegistry; +typedef struct SubGhzProtocol SubGhzProtocol; struct SubGhzProtocolRegistry { const SubGhzProtocol** items; diff --git a/lib/subghz/subghz_protocol_registry.h b/lib/subghz/subghz_protocol_registry.h new file mode 100644 index 0000000000..6a27da9925 --- /dev/null +++ b/lib/subghz/subghz_protocol_registry.h @@ -0,0 +1,13 @@ +#pragma once + +#include "registry.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern const SubGhzProtocolRegistry subghz_protocol_registry; + +#ifdef __cplusplus +} +#endif diff --git a/lib/subghz/subghz_setting.c b/lib/subghz/subghz_setting.c index d32484c452..5e936b33b5 100644 --- a/lib/subghz/subghz_setting.c +++ b/lib/subghz/subghz_setting.c @@ -41,6 +41,9 @@ static const uint32_t subghz_frequency_list[] = { 387000000, 390000000, 418000000, + 430000000, + 431000000, + 431500000, 433075000, /* LPD433 first */ 433220000, 433420000, @@ -471,3 +474,35 @@ uint32_t subghz_setting_get_default_frequency(SubGhzSetting* instance) { return subghz_setting_get_frequency( instance, subghz_setting_get_frequency_default_index(instance)); } + +uint8_t subghz_setting_customs_presets_to_log(SubGhzSetting* instance) { + furi_assert(instance); +#ifndef FURI_DEBUG + FURI_LOG_I(TAG, "Logging loaded presets allow only Debug build"); +#else + uint8_t count = 0; + FuriString* temp = furi_string_alloc(); + + FURI_LOG_I(TAG, "Loaded presets"); + for + M_EACH(item, instance->preset->data, SubGhzSettingCustomPresetItemArray_t) { + furi_string_reset(temp); + + for(uint8_t i = 0; i < item->custom_preset_data_size; i++) { + furi_string_cat_printf(temp, "%02u ", item->custom_preset_data[i]); + } + + FURI_LOG_I( + TAG, "%u - %s", count + 1, furi_string_get_cstr(item->custom_preset_name)); + FURI_LOG_I(TAG, " Size: %u", item->custom_preset_data_size); + FURI_LOG_I(TAG, " Data: %s", furi_string_get_cstr(temp)); + + count++; + } + + furi_string_free(temp); + + return count; +#endif + return 0; +} diff --git a/lib/subghz/subghz_setting.h b/lib/subghz/subghz_setting.h index 60b751e35e..7ca1574c73 100644 --- a/lib/subghz/subghz_setting.h +++ b/lib/subghz/subghz_setting.h @@ -61,6 +61,8 @@ uint32_t subghz_setting_get_default_frequency(SubGhzSetting* instance); void subghz_setting_set_default_frequency(SubGhzSetting* instance, uint32_t frequency_to_setup); +uint8_t subghz_setting_customs_presets_to_log(SubGhzSetting* instance); + #ifdef __cplusplus } #endif diff --git a/lib/subghz/types.h b/lib/subghz/types.h index e0ed956541..70a57d3f9e 100644 --- a/lib/subghz/types.h +++ b/lib/subghz/types.h @@ -21,6 +21,9 @@ #define SUBGHZ_RAW_FILE_VERSION 1 #define SUBGHZ_RAW_FILE_TYPE "Flipper SubGhz RAW File" +typedef struct SubGhzProtocolRegistry SubGhzProtocolRegistry; +typedef struct SubGhzEnvironment SubGhzEnvironment; + // Radio Preset typedef struct { FuriString* name; @@ -114,13 +117,16 @@ typedef enum { SubGhzProtocolFlag_Load = (1 << 8), SubGhzProtocolFlag_Send = (1 << 9), SubGhzProtocolFlag_BinRAW = (1 << 10), + SubGhzProtocolFlag_StarLine = (1 << 11), + SubGhzProtocolFlag_AutoAlarms = (1 << 12), + SubGhzProtocolFlag_Magelan = (1 << 13), } SubGhzProtocolFlag; -typedef struct { +struct SubGhzProtocol { const char* name; SubGhzProtocolType type; SubGhzProtocolFlag flag; const SubGhzProtocolEncoder* encoder; const SubGhzProtocolDecoder* decoder; -} SubGhzProtocol; +}; diff --git a/lib/toolbox/dir_walk.c b/lib/toolbox/dir_walk.c index 509ceb5b42..144a3528f9 100644 --- a/lib/toolbox/dir_walk.c +++ b/lib/toolbox/dir_walk.c @@ -1,6 +1,8 @@ #include "dir_walk.h" #include +#define MAX_NAME_LEN 254 + LIST_DEF(DirIndexList, uint32_t); struct DirWalk { @@ -11,6 +13,8 @@ struct DirWalk { bool recursive; DirWalkFilterCb filter_cb; void* filter_context; + const char** recurse_filter; + size_t recurse_filter_count; }; DirWalk* dir_walk_alloc(Storage* storage) { @@ -20,6 +24,8 @@ DirWalk* dir_walk_alloc(Storage* storage) { DirIndexList_init(dir_walk->index_list); dir_walk->recursive = true; dir_walk->filter_cb = NULL; + dir_walk->recurse_filter = NULL; + dir_walk->recurse_filter_count = 0; return dir_walk; } @@ -39,6 +45,11 @@ void dir_walk_set_filter_cb(DirWalk* dir_walk, DirWalkFilterCb cb, void* context dir_walk->filter_context = context; } +void dir_walk_set_recurse_filter(DirWalk* dir_walk, const char** array, size_t count) { + dir_walk->recurse_filter = array; + dir_walk->recurse_filter_count = count; +} + bool dir_walk_open(DirWalk* dir_walk, const char* path) { furi_string_set(dir_walk->path, path); dir_walk->current_index = 0; @@ -56,12 +67,12 @@ static bool dir_walk_filter(DirWalk* dir_walk, const char* name, FileInfo* filei static DirWalkResult dir_walk_iter(DirWalk* dir_walk, FuriString* return_path, FileInfo* fileinfo) { DirWalkResult result = DirWalkError; - char* name = malloc(256); // FIXME: remove magic number + char* name = malloc(MAX_NAME_LEN); FileInfo info; bool end = false; while(!end) { - storage_dir_read(dir_walk->file, &info, name, 255); + storage_dir_read(dir_walk->file, &info, name, MAX_NAME_LEN); if(storage_file_get_error(dir_walk->file) == FSE_OK) { result = DirWalkOK; @@ -84,13 +95,30 @@ static DirWalkResult } if(file_info_is_dir(&info) && dir_walk->recursive) { - // step into - DirIndexList_push_back(dir_walk->index_list, dir_walk->current_index); - dir_walk->current_index = 0; - storage_dir_close(dir_walk->file); - furi_string_cat_printf(dir_walk->path, "/%s", name); - storage_dir_open(dir_walk->file, furi_string_get_cstr(dir_walk->path)); + + bool filter = false; + for(size_t i = 0; i < dir_walk->recurse_filter_count; i++) { + if(furi_string_equal_str(dir_walk->path, dir_walk->recurse_filter[i])) { + filter = true; + break; + } + } + + if(filter) { + // reset path + size_t last_char = furi_string_search_rchar(dir_walk->path, '/'); + if(last_char != FURI_STRING_FAILURE) { + furi_string_left(dir_walk->path, last_char); + } + + } else { + // step into + DirIndexList_push_back(dir_walk->index_list, dir_walk->current_index); + dir_walk->current_index = 0; + storage_dir_close(dir_walk->file); + storage_dir_open(dir_walk->file, furi_string_get_cstr(dir_walk->path)); + } } } else if(storage_file_get_error(dir_walk->file) == FSE_NOT_EXIST) { if(DirIndexList_size(dir_walk->index_list) == 0) { @@ -119,7 +147,7 @@ static DirWalkResult break; } - if(!storage_dir_read(dir_walk->file, &info, name, 255)) { + if(!storage_dir_read(dir_walk->file, &info, name, MAX_NAME_LEN)) { result = DirWalkError; end = true; break; diff --git a/lib/toolbox/dir_walk.h b/lib/toolbox/dir_walk.h index 535237fc6e..67c364b471 100644 --- a/lib/toolbox/dir_walk.h +++ b/lib/toolbox/dir_walk.h @@ -43,6 +43,14 @@ void dir_walk_set_recursive(DirWalk* dir_walk, bool recursive); */ void dir_walk_set_filter_cb(DirWalk* dir_walk, DirWalkFilterCb cb, void* context); +/** + * Set recurse filtered paths + * @param dir_walk + * @param array + * @param count + */ +void dir_walk_set_recurse_filter(DirWalk* dir_walk, const char** array, size_t count); + /** * Open directory * @param dir_walk @@ -76,4 +84,4 @@ void dir_walk_close(DirWalk* dir_walk); #ifdef __cplusplus } -#endif \ No newline at end of file +#endif diff --git a/lib/toolbox/m_cstr_dup.h b/lib/toolbox/m_cstr_dup.h index 0555f72c65..11b7fe35ad 100644 --- a/lib/toolbox/m_cstr_dup.h +++ b/lib/toolbox/m_cstr_dup.h @@ -2,15 +2,16 @@ #include #define M_INIT_DUP(a) ((a) = strdup("")) -#define M_SET_DUP(a, b) (M_CHECK_DEFAULT_TYPE(a), free((void*)a), (a) = strdup(b)) +#define M_INIT_SET_DUP(a, b) ((a) = strdup(b)) +#define M_SET_DUP(a, b) (free((void*)a), (a) = strdup(b)) #define M_CLEAR_DUP(a) (free((void*)a)) -#define M_CSTR_DUP_OPLIST \ - (INIT(M_INIT_DUP), \ - INIT_SET(M_SET_DUP), \ - SET(M_SET_DUP), \ - CLEAR(M_CLEAR_DUP), \ - HASH(m_core_cstr_hash), \ - EQUAL(M_CSTR_EQUAL), \ - CMP(strcmp), \ +#define M_CSTR_DUP_OPLIST \ + (INIT(M_INIT_DUP), \ + INIT_SET(M_INIT_SET_DUP), \ + SET(M_SET_DUP), \ + CLEAR(M_CLEAR_DUP), \ + HASH(m_core_cstr_hash), \ + EQUAL(M_CSTR_EQUAL), \ + CMP(strcmp), \ TYPE(const char*)) diff --git a/lib/toolbox/path.c b/lib/toolbox/path.c index 3d161a1963..78a9ea8e97 100644 --- a/lib/toolbox/path.c +++ b/lib/toolbox/path.c @@ -34,6 +34,18 @@ void path_extract_filename(FuriString* path, FuriString* name, bool trim_ext) { } } +void path_extract_ext_str(FuriString* path, FuriString* ext) { + size_t dot = furi_string_search_rchar(path, '.'); + size_t filename_start = furi_string_search_rchar(path, '/'); + + if(dot != FURI_STRING_FAILURE && filename_start != FURI_STRING_FAILURE && + filename_start < dot) { + furi_string_set_n(ext, path, dot, furi_string_size(path) - dot); + } else { + furi_string_reset(ext); + } +} + void path_extract_extension(FuriString* path, char* ext, size_t ext_len_max) { size_t dot = furi_string_search_rchar(path, '.'); size_t filename_start = furi_string_search_rchar(path, '/'); diff --git a/lib/toolbox/path.h b/lib/toolbox/path.h index e6145b7156..6296cfb3fa 100644 --- a/lib/toolbox/path.h +++ b/lib/toolbox/path.h @@ -22,6 +22,14 @@ void path_extract_filename_no_ext(const char* path, FuriString* filename); */ void path_extract_filename(FuriString* path, FuriString* filename, bool trim_ext); +/** + * @brief Extract file extension string from path. + * + * @param path path string + * @param ext output extension furi string + */ +void path_extract_ext_str(FuriString* path, FuriString* ext); + /** * @brief Extract file extension from path. * diff --git a/lib/toolbox/tar/tar_archive.c b/lib/toolbox/tar/tar_archive.c index fcfc22a703..c8abbd6d47 100644 --- a/lib/toolbox/tar/tar_archive.c +++ b/lib/toolbox/tar/tar_archive.c @@ -6,7 +6,7 @@ #include #define TAG "TarArch" -#define MAX_NAME_LEN 255 +#define MAX_NAME_LEN 254 #define FILE_BLOCK_SIZE 512 #define FILE_OPEN_NTRIES 10 diff --git a/lib/toolbox/value_index.c b/lib/toolbox/value_index.c index 87bf990f37..5ec0fb9628 100644 --- a/lib/toolbox/value_index.c +++ b/lib/toolbox/value_index.c @@ -1,12 +1,14 @@ #include "value_index.h" uint8_t value_index_int32(const int32_t value, const int32_t values[], uint8_t values_count) { + int64_t last_value = INT64_MIN; uint8_t index = 0; for(uint8_t i = 0; i < values_count; i++) { - if(value == values[i]) { + if((value >= last_value) && (value <= values[i])) { index = i; break; } + last_value = values[i]; } return index; } diff --git a/lib/toolbox/value_index.h b/lib/toolbox/value_index.h index 23d53ca215..5aa768e3d1 100644 --- a/lib/toolbox/value_index.h +++ b/lib/toolbox/value_index.h @@ -7,7 +7,7 @@ extern "C" { #endif -/** Get the index of a int32_t array element which is equal to the given value. +/** Get the index of a int32_t array element which is closest to the given value. * * Returned index corresponds to the first element found. * If no suitable elements were found, the function returns 0. diff --git a/lib/u8g2/u8g2_glue.c b/lib/u8g2/u8g2_glue.c index 17a702b50f..0142e3e2fd 100644 --- a/lib/u8g2/u8g2_glue.c +++ b/lib/u8g2/u8g2_glue.c @@ -2,6 +2,9 @@ #include +#define CONTRAST_ERC 31 +#define CONTRAST_MGG 31 + uint8_t u8g2_gpio_and_delay_stm32(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr) { UNUSED(u8x8); UNUSED(arg_ptr); @@ -207,6 +210,19 @@ void u8x8_d_st756x_init(u8x8_t* u8x8, uint8_t contrast, uint8_t regulation_ratio u8x8_cad_EndTransfer(u8x8); } +void u8x8_d_st756x_set_contrast(u8x8_t* u8x8, int8_t contrast_offset) { + uint8_t contrast = (furi_hal_version_get_hw_display() == FuriHalVersionDisplayMgg) ? + CONTRAST_MGG : + CONTRAST_ERC; + contrast += contrast_offset; + contrast = contrast & 0b00111111; + + u8x8_cad_StartTransfer(u8x8); + u8x8_cad_SendCmd(u8x8, ST756X_CMD_SET_EV); + u8x8_cad_SendArg(u8x8, contrast); + u8x8_cad_EndTransfer(u8x8); +} + uint8_t u8x8_d_st756x_flipper(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr) { /* call common procedure first and handle messages there */ if(u8x8_d_st756x_common(u8x8, msg, arg_int, arg_ptr) == 0) { @@ -225,7 +241,7 @@ uint8_t u8x8_d_st756x_flipper(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* * RR = 10 / ((1 - (63 - 32) / 162) * 2.1) ~= 5.88 is 6 (0b110) * Bias = 1/9 (false) */ - u8x8_d_st756x_init(u8x8, 31, 0b110, false); + u8x8_d_st756x_init(u8x8, CONTRAST_MGG, 0b110, false); } else { /* ERC v1(ST7565) and v2(ST7567) * EV = 33 @@ -233,7 +249,7 @@ uint8_t u8x8_d_st756x_flipper(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* * RR = 9.3 / ((1 - (63 - 32) / 162) * 2.1) ~= 5.47 is 5.5 (0b101) * Bias = 1/9 (false) */ - u8x8_d_st756x_init(u8x8, 32, 0b101, false); + u8x8_d_st756x_init(u8x8, CONTRAST_ERC, 0b101, false); } break; case U8X8_MSG_DISPLAY_SET_FLIP_MODE: diff --git a/lib/u8g2/u8g2_glue.h b/lib/u8g2/u8g2_glue.h index 91ba2980a5..af236279ec 100644 --- a/lib/u8g2/u8g2_glue.h +++ b/lib/u8g2/u8g2_glue.h @@ -14,3 +14,5 @@ void u8g2_Setup_st756x_flipper( u8x8_msg_cb gpio_and_delay_cb); void u8x8_d_st756x_init(u8x8_t* u8x8, uint8_t contrast, uint8_t regulation_ratio, bool bias); + +void u8x8_d_st756x_set_contrast(u8x8_t* u8x8, int8_t contrast_offset); diff --git a/lib/update_util/update_operation.h b/lib/update_util/update_operation.h index 65abf8e150..270d607608 100644 --- a/lib/update_util/update_operation.h +++ b/lib/update_util/update_operation.h @@ -8,7 +8,7 @@ extern "C" { #endif #define UPDATE_OPERATION_ROOT_DIR_PACKAGE_MAGIC 0 -#define UPDATE_OPERATION_MAX_MANIFEST_PATH_LEN 255u +#define UPDATE_OPERATION_MAX_MANIFEST_PATH_LEN 254u #define UPDATE_OPERATION_MIN_MANIFEST_VERSION 2 /* diff --git a/lib/xtreme/assets.c b/lib/xtreme/assets.c index e291d60744..59df1206d9 100644 --- a/lib/xtreme/assets.c +++ b/lib/xtreme/assets.c @@ -105,6 +105,7 @@ void XTREME_ASSETS_LOAD() { if(!furi_hal_is_normal_boot()) return; const char* pack = XTREME_SETTINGS()->asset_pack; + XTREME_SETTINGS()->is_nsfw = !strncmp(pack, "NSFW", strlen("NSFW")); if(pack[0] == '\0') return; Storage* storage = furi_record_open(RECORD_STORAGE); diff --git a/lib/xtreme/settings.c b/lib/xtreme/settings.c index f87e30f5f4..57e5670e0f 100644 --- a/lib/xtreme/settings.c +++ b/lib/xtreme/settings.c @@ -6,7 +6,7 @@ #define TAG "XtremeSettings" XtremeSettings xtreme_settings = { - .asset_pack = "", + .asset_pack = "", // SFW .anim_speed = 100, // 100% .cycle_anims = 0, // Meta.txt .unlock_anims = false, // OFF @@ -14,23 +14,27 @@ XtremeSettings xtreme_settings = { .wii_menu = true, // ON .lock_on_boot = false, // OFF .bad_pins_format = false, // OFF + .pin_unlock_from_app = false, // OFF .lockscreen_time = true, // ON .lockscreen_seconds = false, // OFF .lockscreen_date = true, // ON .lockscreen_statusbar = true, // ON .lockscreen_prompt = true, // ON .battery_icon = BatteryIconBarPercent, // Bar % + .statusbar_clock = false, // OFF .status_icons = true, // ON .bar_borders = true, // ON .bar_background = false, // OFF .sort_dirs_first = true, // ON - .dark_mode = false, // OFF + .show_hidden_files = false, // OFF + .show_internal_tab = false, // OFF .favorite_timeout = 0, // OFF .bad_bt = false, // USB .bad_bt_remember = false, // OFF + .dark_mode = false, // OFF + .rgb_backlight = false, // OFF .butthurt_timer = 21600, // 6 H .charge_cap = 100, // 100% - .rgb_backlight = false, // OFF }; void XTREME_SETTINGS_LOAD() { @@ -43,7 +47,6 @@ void XTREME_SETTINGS_LOAD() { FuriString* string = furi_string_alloc(); if(flipper_format_read_string(file, "asset_pack", string)) { strlcpy(x->asset_pack, furi_string_get_cstr(string), XTREME_ASSETS_PACK_NAME_LEN); - x->is_nsfw = strncmp(x->asset_pack, "NSFW", strlen("NSFW")) == 0; } furi_string_free(string); uint32_t u; @@ -74,6 +77,10 @@ void XTREME_SETTINGS_LOAD() { x->bad_pins_format = b; } flipper_format_rewind(file); + if(flipper_format_read_bool(file, "pin_unlock_from_app", &b, 1)) { + x->pin_unlock_from_app = b; + } + flipper_format_rewind(file); if(flipper_format_read_bool(file, "lock_on_boot", &b, 1)) { x->lock_on_boot = b; } @@ -102,6 +109,10 @@ void XTREME_SETTINGS_LOAD() { x->battery_icon = CLAMP(u, BatteryIconCount - 1U, 0U); } flipper_format_rewind(file); + if(flipper_format_read_bool(file, "statusbar_clock", &b, 1)) { + x->statusbar_clock = b; + } + flipper_format_rewind(file); if(flipper_format_read_bool(file, "status_icons", &b, 1)) { x->status_icons = b; } @@ -118,10 +129,12 @@ void XTREME_SETTINGS_LOAD() { x->sort_dirs_first = b; } flipper_format_rewind(file); - if(flipper_format_read_bool(file, "dark_mode", &b, 1)) { - { - x->dark_mode = b; - } + if(flipper_format_read_bool(file, "show_hidden_files", &b, 1)) { + x->show_hidden_files = b; + } + flipper_format_rewind(file); + if(flipper_format_read_bool(file, "show_internal_tab", &b, 1)) { + x->show_internal_tab = b; } flipper_format_rewind(file); if(flipper_format_read_uint32(file, "favorite_timeout", &u, 1)) { @@ -136,6 +149,14 @@ void XTREME_SETTINGS_LOAD() { x->bad_bt_remember = b; } flipper_format_rewind(file); + if(flipper_format_read_bool(file, "dark_mode", &b, 1)) { + x->dark_mode = b; + } + flipper_format_rewind(file); + if(flipper_format_read_bool(file, "rgb_backlight", &b, 1)) { + x->rgb_backlight = b; + } + flipper_format_rewind(file); if(flipper_format_read_uint32(file, "butthurt_timer", &u, 1)) { x->butthurt_timer = CLAMP(u, 172800U, 0U); } @@ -143,10 +164,6 @@ void XTREME_SETTINGS_LOAD() { if(flipper_format_read_uint32(file, "charge_cap", &u, 1)) { x->charge_cap = CLAMP(u, 100U, 5U); } - flipper_format_rewind(file); - if(flipper_format_read_bool(file, "rgb_backlight", &b, 1)) { - x->rgb_backlight = b; - } } flipper_format_free(file); furi_record_close(RECORD_STORAGE); @@ -166,24 +183,28 @@ void XTREME_SETTINGS_SAVE() { flipper_format_write_bool(file, "fallback_anim", &x->fallback_anim, 1); flipper_format_write_bool(file, "wii_menu", &x->wii_menu, 1); flipper_format_write_bool(file, "bad_pins_format", &x->bad_pins_format, 1); + flipper_format_write_bool(file, "pin_unlock_from_app", &x->pin_unlock_from_app, 1); flipper_format_write_bool(file, "lock_on_boot", &x->lock_on_boot, 1); flipper_format_write_bool(file, "lockscreen_time", &x->lockscreen_time, 1); flipper_format_write_bool(file, "lockscreen_seconds", &x->lockscreen_seconds, 1); flipper_format_write_bool(file, "lockscreen_date", &x->lockscreen_date, 1); flipper_format_write_bool(file, "lockscreen_statusbar", &x->lockscreen_statusbar, 1); flipper_format_write_bool(file, "lockscreen_prompt", &x->lockscreen_prompt, 1); - flipper_format_write_uint32(file, "battery_icon", (uint32_t*)&x->battery_icon, 1); + flipper_format_write_uint32(file, "battery_icon", &x->battery_icon, 1); + flipper_format_write_bool(file, "statusbar_clock", &x->statusbar_clock, 1); flipper_format_write_bool(file, "status_icons", &x->status_icons, 1); flipper_format_write_bool(file, "bar_borders", &x->bar_borders, 1); flipper_format_write_bool(file, "bar_background", &x->bar_background, 1); flipper_format_write_bool(file, "sort_dirs_first", &x->sort_dirs_first, 1); - flipper_format_write_bool(file, "dark_mode", &x->dark_mode, 1); + flipper_format_write_bool(file, "show_hidden_files", &x->show_hidden_files, 1); + flipper_format_write_bool(file, "show_internal_tab", &x->show_internal_tab, 1); flipper_format_write_uint32(file, "favorite_timeout", &x->favorite_timeout, 1); flipper_format_write_bool(file, "bad_bt", &x->bad_bt, 1); flipper_format_write_bool(file, "bad_bt_remember", &x->bad_bt_remember, 1); + flipper_format_write_bool(file, "dark_mode", &x->dark_mode, 1); + flipper_format_write_bool(file, "rgb_backlight", &x->rgb_backlight, 1); flipper_format_write_uint32(file, "butthurt_timer", &x->butthurt_timer, 1); flipper_format_write_uint32(file, "charge_cap", &x->charge_cap, 1); - flipper_format_write_bool(file, "rgb_backlight", &x->rgb_backlight, 1); } flipper_format_free(file); furi_record_close(RECORD_STORAGE); diff --git a/lib/xtreme/xtreme.h b/lib/xtreme/xtreme.h index a827730f87..134f737e12 100644 --- a/lib/xtreme/xtreme.h +++ b/lib/xtreme/xtreme.h @@ -13,8 +13,9 @@ extern "C" { #define XTREME_ASSETS_PACK_NAME_LEN 32 typedef struct { - char asset_pack[XTREME_ASSETS_PACK_NAME_LEN]; bool is_nsfw; // TODO: replace with packs text support + + char asset_pack[XTREME_ASSETS_PACK_NAME_LEN]; uint32_t anim_speed; int32_t cycle_anims; bool unlock_anims; @@ -22,23 +23,27 @@ typedef struct { bool wii_menu; bool lock_on_boot; bool bad_pins_format; + bool pin_unlock_from_app; bool lockscreen_time; bool lockscreen_seconds; bool lockscreen_date; bool lockscreen_statusbar; bool lockscreen_prompt; - BatteryIcon battery_icon; + uint32_t battery_icon; + bool statusbar_clock; bool status_icons; bool bar_borders; bool bar_background; bool sort_dirs_first; - bool dark_mode; + bool show_hidden_files; + bool show_internal_tab; uint32_t favorite_timeout; bool bad_bt; bool bad_bt_remember; + bool dark_mode; + bool rgb_backlight; uint32_t butthurt_timer; uint32_t charge_cap; - bool rgb_backlight; } XtremeSettings; void XTREME_SETTINGS_SAVE(); diff --git a/scripts/debug/flipperapps.py b/scripts/debug/flipperapps.py index 608c30412f..6dba89a564 100644 --- a/scripts/debug/flipperapps.py +++ b/scripts/debug/flipperapps.py @@ -188,6 +188,7 @@ def attach_to_fw(self) -> None: ) self.app_type_ptr = gdb.lookup_type("FlipperApplication").pointer() self.app_list_entry_type = gdb.lookup_type("struct FlipperApplicationList_s") + self._sync_apps() def handle_stop(self, event) -> None: self._sync_apps() diff --git a/scripts/distfap.py b/scripts/distfap.py old mode 100644 new mode 100755 index 74601fdca0..b1c5587906 --- a/scripts/distfap.py +++ b/scripts/distfap.py @@ -52,7 +52,7 @@ def install(self): if not self.args.launch_app: return 0 - storage.send_and_wait_eol(f'loader open "Apps" {fap_dst_path}\r') + storage.send_and_wait_eol(f"loader open {fap_dst_path}\r") if len(result := storage.read.until(storage.CLI_EOL)): self.logger.error(f"Unexpected response: {result.decode('ascii')}") diff --git a/scripts/fastfap.py b/scripts/fastfap.py new file mode 100755 index 0000000000..95e32c37be --- /dev/null +++ b/scripts/fastfap.py @@ -0,0 +1,169 @@ +#!/usr/bin/env python3 +import hashlib +import os +import struct +import subprocess +import tempfile +from collections import defaultdict +from dataclasses import dataclass + +from elftools.elf.elffile import ELFFile +from elftools.elf.relocation import RelocationSection +from elftools.elf.sections import SymbolTableSection +from fbt.sdk.hashes import gnu_sym_hash +from flipper.app import App + +VERSION = 1 + + +@dataclass +class RelData: + section: int + section_value: int + type: int + offset: int + name: str + + +@dataclass(frozen=True) +class UniqueRelData: + section: int + section_value: int + type: int + name: str + + +@dataclass +class RelSection: + name: str + oringinal_name: str + data: dict[UniqueRelData, list[int]] + + +def serialize_relsection_data(data: dict[UniqueRelData, list[int]]) -> bytes: + result = struct.pack(" 0: + result += struct.pack("> 8) & 0xFF, (offset >> 16) & 0xFF + ) + + return result + + +class Main(App): + def init(self): + self.parser.add_argument("fap_src_path", help="App file to upload") + self.parser.add_argument("objcopy_path", help="Objcopy path") + self.parser.set_defaults(func=self.process) + + def process(self): + fap_path = self.args.fap_src_path + objcopy_path = self.args.objcopy_path + + sections: list[RelSection] = [] + + with open(fap_path, "rb") as f: + elf_file = ELFFile(f) + + relocation_sections: list[RelocationSection] = [] + symtab_section: SymbolTableSection | None = None + + for section in elf_file.iter_sections(): + if isinstance(section, RelocationSection): + relocation_sections.append(section) + + if isinstance(section, SymbolTableSection): + symtab_section = section + + if not symtab_section: + self.logger.error("No symbol table found") + return 1 + + if not relocation_sections: + self.logger.info("No relocation sections found") + return 0 + + for section in relocation_sections: + section_relocations: list[RelData] = [] + + for relocation in section.iter_relocations(): + symbol_id: int = relocation.entry["r_info_sym"] + offset: int = relocation.entry["r_offset"] + type: int = relocation.entry["r_info_type"] + symbol = symtab_section.get_symbol(symbol_id) + section_index: int = symbol["st_shndx"] + section_value: int = symbol["st_value"] + if section_index == "SHN_UNDEF": + section_index = 0 + + section_relocations.append( + RelData(section_index, section_value, type, offset, symbol.name) + ) + + unique_relocations: dict[UniqueRelData, list[int]] = defaultdict(list) + for relocation in section_relocations: + unique = UniqueRelData( + relocation.section, + relocation.section_value, + relocation.type, + relocation.name, + ) + + unique_relocations[unique].append(relocation.offset) + + section_name = section.name + if section_name.startswith(".rel"): + section_name = ".fast.rel" + section_name[4:] + else: + self.logger.error( + "Unknown relocation section name: %s", section_name + ) + return 1 + + sections.append( + RelSection(section_name, section.name, unique_relocations) + ) + + with tempfile.TemporaryDirectory() as temp_dir: + for section in sections: + data = serialize_relsection_data(section.data) + hash_name = hashlib.md5(section.name.encode()).hexdigest() + filename = f"{temp_dir}/{hash_name}.bin" + + if os.path.isfile(filename): + self.logger.error(f"File {filename} already exists") + return 1 + + with open(filename, "wb") as f: + f.write(data) + + exit_code = subprocess.run( + [ + objcopy_path, + "--add-section", + f"{section.name}={filename}", + fap_path, + ], + check=True, + ) + + if exit_code.returncode != 0: + self.logger.error("objcopy failed") + return 1 + + return 0 + + +if __name__ == "__main__": + Main()() diff --git a/scripts/fbt/appmanifest.py b/scripts/fbt/appmanifest.py index 32070b55af..f570491ea7 100644 --- a/scripts/fbt/appmanifest.py +++ b/scripts/fbt/appmanifest.py @@ -1,7 +1,8 @@ import os +import re from dataclasses import dataclass, field from enum import Enum -from typing import Callable, List, Optional, Tuple, Union +from typing import Callable, ClassVar, List, Optional, Tuple, Union class FlipperManifestException(Exception): @@ -12,6 +13,7 @@ class FlipperAppType(Enum): SERVICE = "Service" SYSTEM = "System" APP = "App" + FAPP = "Fapp" DEBUG = "Debug" ARCHIVE = "Archive" SETTINGS = "Settings" @@ -19,11 +21,12 @@ class FlipperAppType(Enum): EXTERNAL = "External" METAPACKAGE = "Package" PLUGIN = "Plugin" - EXTMAINAPP = "ExtMainApp" @dataclass class FlipperApplication: + APP_ID_REGEX: ClassVar[re.Pattern] = re.compile(r"^[a-z0-9_]+$") + @dataclass class ExternallyBuiltFile: path: str @@ -42,7 +45,6 @@ class Library: appid: str apptype: FlipperAppType - preload: Optional[bool] = False name: Optional[str] = "" entry_point: Optional[str] = None flags: List[str] = field(default_factory=lambda: ["Default"]) @@ -86,6 +88,10 @@ def is_default_deployable(self): def __post_init__(self): if self.apptype == FlipperAppType.PLUGIN: self.stack_size = 0 + if not self.APP_ID_REGEX.match(self.appid): + raise FlipperManifestException( + f"Invalid appid '{self.appid}'. Must match regex '{self.APP_ID_REGEX}'" + ) if isinstance(self.fap_version, str): try: self.fap_version = tuple(int(v) for v in self.fap_version.split(".")) @@ -124,11 +130,6 @@ def _validate_app_params(self, *args, **kw): raise FlipperManifestException( f"Plugin {kw.get('appid')} must have 'requires' in manifest" ) - # Harmless - cdefines for external apps are meaningless - # if apptype == FlipperAppType.EXTERNAL and kw.get("cdefines"): - # raise FlipperManifestException( - # f"External app {kw.get('appid')} must not have 'cdefines' in manifest" - # ) def load_manifest(self, app_manifest_path: str, app_dir_node: object): if not os.path.exists(app_manifest_path): @@ -349,12 +350,18 @@ def get_builtin_app_folders(self): class ApplicationsCGenerator: APP_TYPE_MAP = { - FlipperAppType.SERVICE: ("FlipperApplication", "FLIPPER_SERVICES"), - FlipperAppType.SYSTEM: ("FlipperApplication", "FLIPPER_SYSTEM_APPS"), - FlipperAppType.APP: ("FlipperApplication", "FLIPPER_APPS"), - FlipperAppType.DEBUG: ("FlipperApplication", "FLIPPER_DEBUG_APPS"), - FlipperAppType.SETTINGS: ("FlipperApplication", "FLIPPER_SETTINGS_APPS"), - FlipperAppType.STARTUP: ("FlipperOnStartHook", "FLIPPER_ON_SYSTEM_START"), + FlipperAppType.SERVICE: ("FlipperInternalApplication", "FLIPPER_SERVICES"), + FlipperAppType.SYSTEM: ("FlipperInternalApplication", "FLIPPER_SYSTEM_APPS"), + FlipperAppType.APP: ("FlipperInternalApplication", "FLIPPER_APPS"), + FlipperAppType.DEBUG: ("FlipperInternalApplication", "FLIPPER_DEBUG_APPS"), + FlipperAppType.SETTINGS: ( + "FlipperInternalApplication", + "FLIPPER_SETTINGS_APPS", + ), + FlipperAppType.STARTUP: ( + "FlipperInternalOnStartHook", + "FLIPPER_ON_SYSTEM_START", + ), } def __init__(self, buildset: AppBuildset, autorun_app: str = ""): @@ -369,21 +376,21 @@ def get_app_ep_forward(self, app: FlipperApplication): def get_app_descr(self, app: FlipperApplication): if app.apptype == FlipperAppType.STARTUP: return app.entry_point - if app.apptype == FlipperAppType.EXTMAINAPP: + if app.apptype == FlipperAppType.FAPP: return f""" {{.app = NULL, .name = "{app.name}", - .appid = "/ext/apps/.Main/{app.appid}.fap", - .stack_size = {1 if app.preload else 0}, + .appid = "/ext/apps/assets/{app.appid}.fap", + .stack_size = 0, .icon = {f"&{app.icon}" if app.icon else "NULL"}, - .flags = {'|'.join(f"FlipperApplicationFlag{flag}" for flag in app.flags)}}}""" + .flags = {'|'.join(f"FlipperInternalApplicationFlag{flag}" for flag in app.flags)}}}""" return f""" {{.app = {app.entry_point}, .name = "{app.name}", .appid = "{app.appid}", .stack_size = {app.stack_size}, .icon = {f"&{app.icon}" if app.icon else "NULL"}, - .flags = {'|'.join(f"FlipperApplicationFlag{flag}" for flag in app.flags)}}}""" + .flags = {'|'.join(f"FlipperInternalApplicationFlag{flag}" for flag in app.flags)}}}""" def generate(self): contents = [ @@ -396,10 +403,10 @@ def generate(self): map(self.get_app_ep_forward, self.buildset.get_apps_of_type(apptype)) ) entry_type, entry_block = self.APP_TYPE_MAP[apptype] - contents.append(f"{entry_type} {entry_block}[] = {{") + contents.append(f"const {entry_type} {entry_block}[] = {{") apps = self.buildset.get_apps_of_type(apptype) if apptype is FlipperAppType.APP: - apps += self.buildset.get_apps_of_type(FlipperAppType.EXTMAINAPP) + apps += self.buildset.get_apps_of_type(FlipperAppType.FAPP) apps.sort(key=lambda app: app.order) contents.append(",\n".join(map(self.get_app_descr, apps))) contents.append("};") @@ -412,7 +419,7 @@ def generate(self): contents.extend( [ self.get_app_ep_forward(archive_app[0]), - f"const FlipperApplication FLIPPER_ARCHIVE = {self.get_app_descr(archive_app[0])};", + f"const FlipperInternalApplication FLIPPER_ARCHIVE = {self.get_app_descr(archive_app[0])};", ] ) diff --git a/scripts/fbt/sdk/collector.py b/scripts/fbt/sdk/collector.py index 578a8c7a62..1dd3bc4ebb 100644 --- a/scripts/fbt/sdk/collector.py +++ b/scripts/fbt/sdk/collector.py @@ -1,4 +1,5 @@ from typing import List +from .hashes import gnu_sym_hash from cxxheaderparser.parser import CxxParser from . import ( @@ -72,13 +73,6 @@ def add_header(self, header: str): self.api.headers.add(ApiHeader(header)) -def gnu_sym_hash(name: str): - h = 0x1505 - for c in name: - h = (h << 5) + h + ord(c) - return str(hex(h))[-8:] - - class SdkCollector: def __init__(self): self.symbol_manager = SymbolManager() diff --git a/scripts/fbt/sdk/hashes.py b/scripts/fbt/sdk/hashes.py new file mode 100644 index 0000000000..fef88ddb5e --- /dev/null +++ b/scripts/fbt/sdk/hashes.py @@ -0,0 +1,5 @@ +def gnu_sym_hash(name: str) -> int: + h = 0x1505 + for c in name: + h = ((h << 5) + h + ord(c)) & 0xFFFFFFFF + return h diff --git a/scripts/fbt_tools/fbt_dist.py b/scripts/fbt_tools/fbt_dist.py index 48de8e868e..105f501aaa 100644 --- a/scripts/fbt_tools/fbt_dist.py +++ b/scripts/fbt_tools/fbt_dist.py @@ -132,7 +132,7 @@ def generate(env): "UsbInstall": Builder( action=[ Action( - '${PYTHON3} "${SELFUPDATE_SCRIPT}" ${UPDATE_BUNDLE_DIR}/update.fuf' + '${PYTHON3} "${SELFUPDATE_SCRIPT}" -p ${FLIP_PORT} ${UPDATE_BUNDLE_DIR}/update.fuf' ), Touch("${TARGET}"), ] diff --git a/scripts/fbt_tools/fbt_extapps.py b/scripts/fbt_tools/fbt_extapps.py index 79788ff0c0..083b3e9720 100644 --- a/scripts/fbt_tools/fbt_extapps.py +++ b/scripts/fbt_tools/fbt_extapps.py @@ -179,7 +179,7 @@ def _build_app(self): deployable = False app_artifacts.dist_entries.append((deployable, fal_path)) else: - fap_path = f"apps/{'.Main' if self.app.apptype == FlipperAppType.EXTMAINAPP else self.app.fap_category}/{app_artifacts.compact.name}" + fap_path = f"apps/{'assets' if self.app.apptype == FlipperAppType.FAPP else self.app.fap_category}/{app_artifacts.compact.name}" app_artifacts.dist_entries.append( (self.app.is_default_deployable, fap_path) ) @@ -387,10 +387,16 @@ def generate_embed_app_metadata_actions(source, target, env, for_signature): "${SOURCES} ${TARGET}" ) - actions.append( - Action( - objcopy_str, - "$APPMETAEMBED_COMSTR", + actions.extend( + ( + Action( + objcopy_str, + "$APPMETAEMBED_COMSTR", + ), + Action( + "${PYTHON3} ${FBT_SCRIPT_DIR}/fastfap.py ${TARGET} ${OBJCOPY}", + "$FASTFAP_COMSTR", + ), ) ) @@ -434,7 +440,7 @@ def _add_host_app_to_targets(host_app): # print(deploy_sources, flipp_dist_paths) env.PhonyTarget( launch_target_name, - '${PYTHON3} "${APP_RUN_SCRIPT}" ${EXTRA_ARGS} -s ${SOURCES} -t ${FLIPPER_FILE_TARGETS}', + '${PYTHON3} "${APP_RUN_SCRIPT}" -p ${FLIP_PORT} ${EXTRA_ARGS} -s ${SOURCES} -t ${FLIPPER_FILE_TARGETS}', source=deploy_sources, FLIPPER_FILE_TARGETS=flipp_dist_paths, EXTRA_ARGS=run_script_extra_ars, @@ -446,7 +452,6 @@ def generate(env, **kw): env.SetDefault( EXT_APPS_WORK_DIR="${FBT_FAP_DEBUG_ELF_ROOT}", APP_RUN_SCRIPT="${FBT_SCRIPT_DIR}/runfap.py", - STORAGE_SCRIPT="${FBT_SCRIPT_DIR}/storage.py", ) if not env["VERBOSE"]: env.SetDefault( @@ -454,6 +459,7 @@ def generate(env, **kw): APPMETA_COMSTR="\tAPPMETA\t${TARGET}", APPFILE_COMSTR="\tAPPFILE\t${TARGET}", APPMETAEMBED_COMSTR="\tFAP\t${TARGET}", + FASTFAP_COMSTR="\tFASTFAP\t${TARGET}", APPCHECK_COMSTR="\tAPPCHK\t${SOURCE}", ) diff --git a/scripts/flipper/assets/copro.py b/scripts/flipper/assets/copro.py index f176e3b2e8..25c072899e 100644 --- a/scripts/flipper/assets/copro.py +++ b/scripts/flipper/assets/copro.py @@ -58,14 +58,23 @@ def loadCubeInfo(self, cube_dir, reference_cube_version): def _getFileName(self, name): return posixpath.join(self.COPRO_TAR_DIR, name) + def _addFileReadPermission(self, tarinfo): + tarinfo.mode = 0o644 + return tarinfo + def addFile(self, array, filename, **kwargs): source_file = os.path.join(self.mcu_copro, filename) - self.output_tar.add(source_file, arcname=self._getFileName(filename)) + self.output_tar.add( + source_file, + arcname=self._getFileName(filename), + filter=self._addFileReadPermission, + ) array.append({"name": filename, "sha256": file_sha256(source_file), **kwargs}) def bundle(self, output_file, stack_file_name, stack_type, stack_addr=None): self.output_tar = tarfile.open(output_file, "w:gz", format=tarfile.USTAR_FORMAT) fw_directory = tarfile.TarInfo(self.COPRO_TAR_DIR) + fw_directory.mode = 0o755 fw_directory.type = tarfile.DIRTYPE self.output_tar.addfile(fw_directory) diff --git a/scripts/flipper/assets/coprobin.py b/scripts/flipper/assets/coprobin.py index 75bf76d766..84f52fbb3e 100644 --- a/scripts/flipper/assets/coprobin.py +++ b/scripts/flipper/assets/coprobin.py @@ -46,7 +46,10 @@ class CoproFooterBase: _SIG_BIN_COMMON_SIZE = 2 * 4 def get_version(self): - return f"Version {self.version_major}.{self.version_minor}.{self.version_sub}, branch {self.version_branch}, build {self.version_build} (magic {self.magic:X})" + return ( + f"Version {self.version_major}.{self.version_minor}.{self.version_sub}, " + f"branch {self.version_branch}, build {self.version_build} (magic {self.magic:X})" + ) def get_details(self): raise CoproException("Not implemented") diff --git a/scripts/flipper/storage.py b/scripts/flipper/storage.py index f4d622bfe4..2c9c043d5a 100644 --- a/scripts/flipper/storage.py +++ b/scripts/flipper/storage.py @@ -257,12 +257,12 @@ def send_file(self, filename_from: str, filename_to: str): self.read.until(self.CLI_PROMPT) ftell = file.tell() - percent = str(math.ceil(ftell / filesize * 100)) - total_chunks = str(math.ceil(filesize / buffer_size)) - current_chunk = str(math.ceil(ftell / buffer_size)) + percent = math.ceil(ftell / filesize * 100) + total_chunks = math.ceil(filesize / buffer_size) + current_chunk = math.ceil(ftell / buffer_size) approx_speed = ftell / (time.time() - start_time + 0.0001) sys.stdout.write( - f"\r{percent}%, chunk {current_chunk} of {total_chunks} @ {approx_speed/1024:.2f} kb/s" + f"\r<{percent:3d}%, chunk {current_chunk:2d} of {total_chunks:2d} @ {approx_speed/1024:.2f} kb/s" ) sys.stdout.flush() print() @@ -270,6 +270,7 @@ def send_file(self, filename_from: str, filename_to: str): def read_file(self, filename: str): """Receive file from Flipper, and get filedata (bytes)""" buffer_size = self.chunk_size + start_time = time.time() self.send_and_wait_eol( 'storage read_chunks "' + filename + '" ' + str(buffer_size) + "\r" ) @@ -290,10 +291,13 @@ def read_file(self, filename: str): filedata.extend(self.port.read(chunk_size)) read_size = read_size + chunk_size - percent = str(math.ceil(read_size / size * 100)) - total_chunks = str(math.ceil(size / buffer_size)) - current_chunk = str(math.ceil(read_size / buffer_size)) - sys.stdout.write(f"\r{percent}%, chunk {current_chunk} of {total_chunks}") + percent = math.ceil(read_size / size * 100) + total_chunks = math.ceil(size / buffer_size) + current_chunk = math.ceil(read_size / buffer_size) + approx_speed = read_size / (time.time() - start_time + 0.0001) + sys.stdout.write( + f"\r>{percent:3d}%, chunk {current_chunk:2d} of {total_chunks:2d} @ {approx_speed/1024:.2f} kb/s" + ) sys.stdout.flush() print() self.read.until(self.CLI_PROMPT) diff --git a/scripts/flipper/utils/cdc.py b/scripts/flipper/utils/cdc.py index 7c73516702..9564088598 100644 --- a/scripts/flipper/utils/cdc.py +++ b/scripts/flipper/utils/cdc.py @@ -6,7 +6,7 @@ def resolve_port(logger, portname: str = "auto"): if portname != "auto": return portname # Try guessing - flippers = list(list_ports.grep("flip")) + flippers = list(list_ports.grep("flip_")) if len(flippers) == 1: flipper = flippers[0] logger.info(f"Using {flipper.serial_number} on {flipper.device}") diff --git a/scripts/fwsize.py b/scripts/fwsize.py old mode 100644 new mode 100755 diff --git a/scripts/get_env.py b/scripts/get_env.py old mode 100644 new mode 100755 diff --git a/scripts/map_mariadb_insert.py b/scripts/map_mariadb_insert.py new file mode 100755 index 0000000000..a4c9ed5c78 --- /dev/null +++ b/scripts/map_mariadb_insert.py @@ -0,0 +1,139 @@ +#!/usr/bin/env python3 + +# Requiremets: +# mariadb==1.1.6 + +from datetime import datetime +import argparse +import mariadb +import sys +import os + + +def parseArgs(): + parser = argparse.ArgumentParser() + parser.add_argument("db_user", help="MariaDB user") + parser.add_argument("db_pass", help="MariaDB password") + parser.add_argument("db_host", help="MariaDB hostname") + parser.add_argument("db_port", type=int, help="MariaDB port") + parser.add_argument("db_name", help="MariaDB database") + parser.add_argument("report_file", help="Report file(.map.all)") + args = parser.parse_args() + return args + + +def mariadbConnect(args): + try: + conn = mariadb.connect( + user=args.db_user, + password=args.db_pass, + host=args.db_host, + port=args.db_port, + database=args.db_name, + ) + except mariadb.Error as e: + print(f"Error connecting to MariaDB: {e}") + sys.exit(1) + return conn + + +def parseEnv(): + outArr = [] + outArr.append(datetime.now().strftime("%Y-%m-%d %H:%M:%S")) + outArr.append(os.getenv("COMMIT_HASH", default=None)) + outArr.append(os.getenv("COMMIT_MSG", default=None)) + outArr.append(os.getenv("BRANCH_NAME", default=None)) + outArr.append(os.getenv("BSS_SIZE", default=None)) + outArr.append(os.getenv("TEXT_SIZE", default=None)) + outArr.append(os.getenv("RODATA_SIZE", default=None)) + outArr.append(os.getenv("DATA_SIZE", default=None)) + outArr.append(os.getenv("FREE_FLASH_SIZE", default=None)) + outArr.append(os.getenv("PULL_ID", default=None)) + outArr.append(os.getenv("PULL_NAME", default=None)) + return outArr + + +def createTables(cur, conn): + headerTable = "CREATE TABLE IF NOT EXISTS `header` ( \ + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, \ + `datetime` datetime NOT NULL, \ + `commit` varchar(40) NOT NULL, \ + `commit_msg` text NOT NULL, \ + `branch_name` text NOT NULL, \ + `bss_size` int(10) unsigned NOT NULL, \ + `text_size` int(10) unsigned NOT NULL, \ + `rodata_size` int(10) unsigned NOT NULL, \ + `data_size` int(10) unsigned NOT NULL, \ + `free_flash_size` int(10) unsigned NOT NULL, \ + `pullrequest_id` int(10) unsigned DEFAULT NULL, \ + `pullrequest_name` text DEFAULT NULL, \ + PRIMARY KEY (`id`), \ + KEY `header_id_index` (`id`) )" + dataTable = "CREATE TABLE IF NOT EXISTS `data` ( \ + `header_id` int(10) unsigned NOT NULL, \ + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, \ + `section` text NOT NULL, \ + `address` text NOT NULL, \ + `size` int(10) unsigned NOT NULL, \ + `name` text NOT NULL, \ + `lib` text NOT NULL, \ + `obj_name` text NOT NULL, \ + PRIMARY KEY (`id`), \ + KEY `data_id_index` (`id`), \ + KEY `data_header_id_index` (`header_id`), \ + CONSTRAINT `data_header_id_foreign` FOREIGN KEY (`header_id`) REFERENCES `header` (`id`) )" + cur.execute(headerTable) + cur.execute(dataTable) + conn.commit() + + +def insertHeader(data, cur, conn): + query = "INSERT INTO `header` ( \ + datetime, commit, commit_msg, branch_name, bss_size, text_size, \ + rodata_size, data_size, free_flash_size, pullrequest_id, pullrequest_name) \ + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" + cur.execute(query, data) + conn.commit() + return cur.lastrowid + + +def parseFile(fileObj, headerID): + arr = [] + fileLines = fileObj.readlines() + for line in fileLines: + lineArr = [] + tempLineArr = line.split("\t") + lineArr.append(headerID) + lineArr.append(tempLineArr[0]) # section + lineArr.append(int(tempLineArr[2], 16)) # address hex + lineArr.append(int(tempLineArr[3])) # size + lineArr.append(tempLineArr[4]) # name + lineArr.append(tempLineArr[5]) # lib + lineArr.append(tempLineArr[6]) # obj_name + arr.append(tuple(lineArr)) + return arr + + +def insertData(data, cur, conn): + query = "INSERT INTO `data` ( \ + header_id, section, address, size, \ + name, lib, obj_name) \ + VALUES (?, ?, ?, ?, ? ,?, ?)" + cur.executemany(query, data) + conn.commit() + + +def main(): + args = parseArgs() + dbConn = mariadbConnect(args) + reportFile = open(args.report_file) + dbCurs = dbConn.cursor() + createTables(dbCurs, dbConn) + headerID = insertHeader(parseEnv(), dbCurs, dbConn) + insertData(parseFile(reportFile, headerID), dbCurs, dbConn) + reportFile.close() + dbCurs.close() + + +if __name__ == "__main__": + main() diff --git a/scripts/map_parser.py b/scripts/map_parser.py new file mode 100755 index 0000000000..1efc4fe82f --- /dev/null +++ b/scripts/map_parser.py @@ -0,0 +1,274 @@ +#!/usr/bin/env python3 + +# Requiremets: +# cxxfilt==0.3.0 + +# Most part of this code written by Lars-Dominik Braun https://github.com/PromyLOPh/linkermapviz +# and distributes under MIT licence + +# Copyright (c) 2017 Lars-Dominik Braun +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +import sys +import re +import os +from typing import TextIO +from cxxfilt import demangle + + +class Objectfile: + def __init__(self, section: str, offset: int, size: int, comment: str): + self.section = section.strip() + self.offset = offset + self.size = size + self.path = (None, None) + self.basepath = None + + if comment: + self.path = re.match(r"^(.+?)(?:\(([^\)]+)\))?$", comment).groups() + self.basepath = os.path.basename(self.path[0]) + + self.children = [] + + def __repr__(self) -> str: + return f"" + + +def update_children_size(children: list[list], subsection_size: int) -> list: + # set subsection size to an only child + if len(children) == 1: + children[0][1] = subsection_size + return children + + rest_size = subsection_size + + for index in range(1, len(children)): + if rest_size > 0: + # current size = current address - previous child address + child_size = children[index][0] - children[index - 1][0] + rest_size -= child_size + children[index - 1][1] = child_size + + # if there is rest size, set it to the last child element + if rest_size > 0: + children[-1][1] = rest_size + + return children + + +def parse_sections(file_name: str) -> list: + """ + Quick&Dirty parsing for GNU ld’s linker map output, needs LANG=C, because + some messages are localized. + """ + + sections = [] + with open(file_name, "r") as file: + # skip until memory map is found + found = False + + while True: + line = file.readline() + if not line: + break + if line.strip() == "Memory Configuration": + found = True + break + + if not found: + raise Exception(f"Memory configuration is not found in the{input_file}") + + # long section names result in a linebreak afterwards + sectionre = re.compile( + "(?P
.+?|.{14,}\n)[ ]+0x(?P[0-9a-f]+)[ ]+0x(?P[0-9a-f]+)(?:[ ]+(?P.+))?\n+", + re.I, + ) + subsectionre = re.compile( + "[ ]{16}0x(?P[0-9a-f]+)[ ]+(?P.+)\n+", re.I + ) + s = file.read() + pos = 0 + + while True: + m = sectionre.match(s, pos) + if not m: + # skip that line + try: + nextpos = s.index("\n", pos) + 1 + pos = nextpos + continue + except ValueError: + break + + pos = m.end() + section = m.group("section") + v = m.group("offset") + offset = int(v, 16) if v is not None else None + v = m.group("size") + size = int(v, 16) if v is not None else None + comment = m.group("comment") + + if section != "*default*" and size > 0: + of = Objectfile(section, offset, size, comment) + + if section.startswith(" "): + children = [] + sections[-1].children.append(of) + + while True: + m = subsectionre.match(s, pos) + if not m: + break + pos = m.end() + offset, function = m.groups() + offset = int(offset, 16) + if sections and sections[-1].children: + children.append([offset, 0, function]) + + if children: + children = update_children_size( + children=children, subsection_size=of.size + ) + + sections[-1].children[-1].children.extend(children) + + else: + sections.append(of) + + return sections + + +def get_subsection_name(section_name: str, subsection: Objectfile) -> str: + subsection_split_names = subsection.section.split(".") + if subsection.section.startswith("."): + subsection_split_names = subsection_split_names[1:] + + return ( + f".{subsection_split_names[1]}" + if len(subsection_split_names) > 2 + else section_name + ) + + +def write_subsection( + section_name: str, + subsection_name: str, + address: str, + size: int, + demangled_name: str, + module_name: str, + file_name: str, + mangled_name: str, + write_file_object: TextIO, +) -> None: + write_file_object.write( + f"{section_name}\t" + f"{subsection_name}\t" + f"{address}\t" + f"{size}\t" + f"{demangled_name}\t" + f"{module_name}\t" + f"{file_name}\t" + f"{mangled_name}\n" + ) + + +def save_subsection( + section_name: str, subsection: Objectfile, write_file_object: TextIO +) -> None: + subsection_name = get_subsection_name(section_name, subsection) + module_name = subsection.path[0] + file_name = subsection.path[1] + + if not file_name: + file_name, module_name = module_name, "" + + if not subsection.children: + address = f"{subsection.offset:x}" + size = subsection.size + mangled_name = ( + "" + if subsection.section == section_name + else subsection.section.split(".")[-1] + ) + demangled_name = demangle(mangled_name) if mangled_name else mangled_name + + write_subsection( + section_name=section_name, + subsection_name=subsection_name, + address=address, + size=size, + demangled_name=demangled_name, + module_name=module_name, + file_name=file_name, + mangled_name=mangled_name, + write_file_object=write_file_object, + ) + return + + for subsection_child in subsection.children: + address = f"{subsection_child[0]:x}" + size = subsection_child[1] + mangled_name = subsection_child[2] + demangled_name = demangle(mangled_name) + + write_subsection( + section_name=section_name, + subsection_name=subsection_name, + address=address, + size=size, + demangled_name=demangled_name, + module_name=module_name, + file_name=file_name, + mangled_name=mangled_name, + write_file_object=write_file_object, + ) + + +def save_section(section: Objectfile, write_file_object: TextIO) -> None: + section_name = section.section + for subsection in section.children: + save_subsection( + section_name=section_name, + subsection=subsection, + write_file_object=write_file_object, + ) + + +def save_parsed_data(parsed_data: list[Objectfile], output_file_name: str) -> None: + with open(output_file_name, "w") as write_file_object: + for section in parsed_data: + if section.children: + save_section(section=section, write_file_object=write_file_object) + + +if __name__ == "__main__": + if len(sys.argv) < 3: + raise Exception(f"Usage: {sys.argv[0]} ") + + input_file = sys.argv[1] + output_file = sys.argv[2] + + parsed_sections = parse_sections(input_file) + + if parsed_sections is None: + raise Exception(f"Memory configuration is not {input_file}") + + save_parsed_data(parsed_sections, output_file) diff --git a/scripts/ob.data b/scripts/ob.data index 5276a5103b..605faccbfc 100644 --- a/scripts/ob.data +++ b/scripts/ob.data @@ -14,7 +14,7 @@ IWDGSTOP:0x1:rw IWDGSW:0x1:rw IPCCDBA:0x0:rw ESE:0x1:r -SFSA:0xD7:r +SFSA:0xD5:r FSD:0x0:r DDS:0x1:r C2OPT:0x1:r @@ -22,7 +22,7 @@ NBRSD:0x0:r SNBRSA:0xD:r BRSD:0x0:r SBRSA:0x12:r -SBRV:0x35C00:r +SBRV:0x35400:r PCROP1A_STRT:0x1FF:r PCROP1A_END:0x0:r PCROP_RDP:0x1:rw diff --git a/scripts/runfap.py b/scripts/runfap.py old mode 100644 new mode 100755 index 206c54cc10..42141acff6 --- a/scripts/runfap.py +++ b/scripts/runfap.py @@ -63,7 +63,7 @@ def install(self): storage_ops.recursive_send(fap_dst_path, fap_local_path, False) fap_host_app = self.args.targets[0] - startup_command = f'"Apps" {fap_host_app}' + startup_command = f"{fap_host_app}" if self.args.host_app: startup_command = self.args.host_app diff --git a/scripts/sconsdist.py b/scripts/sconsdist.py old mode 100644 new mode 100755 index 39c7d10f06..b1830ab099 --- a/scripts/sconsdist.py +++ b/scripts/sconsdist.py @@ -84,17 +84,6 @@ def copy_single_project(self, project: ProjectDir) -> None: if exists(sdk_folder := join(obj_directory, foldertype)): self.note_dist_component(foldertype, "dir", sdk_folder) - # TODO: remove this after everyone migrates to new uFBT - self.create_zip_stub("lib") - - def create_zip_stub(self, foldertype): - with zipfile.ZipFile( - self.get_dist_path(self.get_dist_file_name(foldertype, "zip")), - "w", - zipfile.ZIP_DEFLATED, - ) as _: - pass - def copy(self) -> int: self._dist_components: dict[str, str] = dict() self.projects: dict[str, ProjectDir] = dict( diff --git a/scripts/selfupdate.py b/scripts/selfupdate.py old mode 100644 new mode 100755 diff --git a/scripts/serial_cli.py b/scripts/serial_cli.py index 2fa37d7512..8e35d57fac 100644 --- a/scripts/serial_cli.py +++ b/scripts/serial_cli.py @@ -1,3 +1,4 @@ +import argparse import logging import os import subprocess @@ -8,8 +9,11 @@ def main(): logger = logging.getLogger() - if not (port := resolve_port(logger, "auto")): - logger.error("Is Flipper connected over USB and is it not in DFU mode?") + parser = argparse.ArgumentParser() + parser.add_argument("-p", "--port", help="CDC Port", default="auto") + args = parser.parse_args() + if not (port := resolve_port(logger, args.port)): + logger.error("Is Flipper connected via USB and not in DFU mode?") return 1 subprocess.call( [ diff --git a/scripts/slideshow.py b/scripts/slideshow.py old mode 100644 new mode 100755 diff --git a/scripts/testing/await_flipper.py b/scripts/testing/await_flipper.py index 2b4c8b4c39..ea07d6be7f 100755 --- a/scripts/testing/await_flipper.py +++ b/scripts/testing/await_flipper.py @@ -8,6 +8,7 @@ def flp_serial_by_name(flp_name): if sys.platform == "darwin": # MacOS flp_serial = "/dev/cu.usbmodemflip_" + flp_name + "1" + logging.info(f"Darwin, looking for {flp_serial}") elif sys.platform == "linux": # Linux flp_serial = ( "/dev/serial/by-id/usb-Flipper_Devices_Inc._Flipper_" @@ -16,10 +17,12 @@ def flp_serial_by_name(flp_name): + flp_name + "-if00" ) + logging.info(f"linux, looking for {flp_serial}") if os.path.exists(flp_serial): return flp_serial else: + logging.info(f"Couldn't find {logging.info} on this attempt.") if os.path.exists(flp_name): return flp_name else: @@ -38,7 +41,7 @@ def main(): level=logging.INFO, datefmt="%Y-%m-%d %H:%M:%S", ) - logging.info("Waiting for Flipper to be ready...") + logging.info(f"Waiting for Flipper {flipper_name} to be ready...") while flipper == "" and elapsed < UPDATE_TIMEOUT: elapsed += 1 diff --git a/scripts/testing/units.py b/scripts/testing/units.py index 5083bcd435..fd8e29a733 100755 --- a/scripts/testing/units.py +++ b/scripts/testing/units.py @@ -20,13 +20,13 @@ def main(): logging.error("Flipper not found!") sys.exit(1) - with serial.Serial(flp_serial, timeout=1) as flipper: + with serial.Serial(flp_serial, timeout=10) as flipper: logging.info(f"Found Flipper at {flp_serial}") flipper.baudrate = 230400 flipper.flushOutput() flipper.flushInput() - flipper.timeout = 180 + flipper.timeout = 300 flipper.read_until(b">: ").decode("utf-8") flipper.write(b"unit_tests\r") diff --git a/scripts/toolchain/fbtenv.cmd b/scripts/toolchain/fbtenv.cmd index 9d45b7e9d7..4ae04e2a2c 100644 --- a/scripts/toolchain/fbtenv.cmd +++ b/scripts/toolchain/fbtenv.cmd @@ -13,7 +13,7 @@ if not ["%FBT_NOENV%"] == [""] ( exit /b 0 ) -set "FLIPPER_TOOLCHAIN_VERSION=21" +set "FLIPPER_TOOLCHAIN_VERSION=22" if ["%FBT_TOOLCHAIN_PATH%"] == [""] ( set "FBT_TOOLCHAIN_PATH=%FBT_ROOT%" diff --git a/scripts/toolchain/fbtenv.sh b/scripts/toolchain/fbtenv.sh index d911c2334d..81df6c0af9 100755 --- a/scripts/toolchain/fbtenv.sh +++ b/scripts/toolchain/fbtenv.sh @@ -4,7 +4,7 @@ # public variables DEFAULT_SCRIPT_PATH="$(pwd -P)"; -FBT_TOOLCHAIN_VERSION="${FBT_TOOLCHAIN_VERSION:-"21"}"; +FBT_TOOLCHAIN_VERSION="${FBT_TOOLCHAIN_VERSION:-"22"}"; if [ -z ${FBT_TOOLCHAIN_PATH+x} ] ; then FBT_TOOLCHAIN_PATH_WAS_SET=0; diff --git a/scripts/ufbt/SConstruct b/scripts/ufbt/SConstruct index d72de380c3..8812a4e559 100644 --- a/scripts/ufbt/SConstruct +++ b/scripts/ufbt/SConstruct @@ -75,7 +75,7 @@ from fbt.util import ( wrap_tempfile, path_as_posix, ) -from fbt.appmanifest import FlipperAppType +from fbt.appmanifest import FlipperAppType, FlipperApplication from fbt.sdk.cache import SdkCache # Base environment with all tools loaded from SDK @@ -342,7 +342,7 @@ else: appenv.PhonyTarget( "cli", - '${PYTHON3} "${FBT_SCRIPT_DIR}/serial_cli.py"', + '${PYTHON3} "${FBT_SCRIPT_DIR}/serial_cli.py" -p ${FLIP_PORT}', ) # Linter @@ -410,6 +410,12 @@ dist_env.Alias("vscode_dist", vscode_dist) # Creating app from base template dist_env.SetDefault(FBT_APPID=appenv.subst("$APPID") or "template") +if fbt_appid := dist_env.subst("$FBT_APPID"): + if not FlipperApplication.APP_ID_REGEX.match(fbt_appid): + raise UserError( + f"Invalid app id '{fbt_appid}'. App id must match {FlipperApplication.APP_ID_REGEX.pattern}" + ) + app_template_dir = project_template_dir.Dir("app_template") app_template_dist = [] for template_file in app_template_dir.glob("*"): @@ -463,7 +469,7 @@ if dolphin_src_dir.exists(): ) dist_env.PhonyTarget( "dolphin_ext", - '${PYTHON3} ${FBT_SCRIPT_DIR}/storage.py send "${SOURCE}" /ext/dolphin', + '${PYTHON3} ${FBT_SCRIPT_DIR}/storage.py -p ${FLIP_PORT} send "${SOURCE}" /ext/dolphin', source=ufbt_build_dir.Dir("dolphin"), ) else: diff --git a/scripts/ufbt/commandline.scons b/scripts/ufbt/commandline.scons index a9b91bbca9..349b4ef252 100644 --- a/scripts/ufbt/commandline.scons +++ b/scripts/ufbt/commandline.scons @@ -71,6 +71,11 @@ vars.AddVariables( validator=PathVariable.PathIsDir, default="", ), + ( + "FLIP_PORT", + "CDC Port of Flipper to use, if multiple are connected", + "auto", + ), ) Return("vars") diff --git a/scripts/version.py b/scripts/version.py old mode 100644 new mode 100755 index 80299037fa..987fd9b96a --- a/scripts/version.py +++ b/scripts/version.py @@ -1,5 +1,5 @@ #!/usb/bin/env python3 -VERSION = "XFW-0048" +VERSION = "XFW-0049" import json import os @@ -45,27 +45,33 @@ def get_version_info(self): if force_no_dirty != "": dirty = False + if "SOURCE_DATE_EPOCH" in os.environ: + commit_date = datetime.utcfromtimestamp( + int(os.environ["SOURCE_DATE_EPOCH"]) + ) + else: + commit_date = datetime.strptime( + self._exec_git("log -1 --format=%cd").strip(), + "%a %b %d %H:%M:%S %Y %z", + ) + return { "GIT_COMMIT": commit, "GIT_BRANCH": branch, "VERSION": version, "BUILD_DIRTY": dirty and 1 or 0, - "GIT_ORIGIN": ",".join(self._get_git_origins()), + "GIT_ORIGIN": self._get_git_origin(), + "GIT_COMMIT_DATE": commit_date, } - def _get_git_origins(self): + def _get_git_origin(self): try: - remotes = self._exec_git("remote -v") + branch = self._exec_git("branch --show-current") + remote = self._exec_git(f"config branch.{branch}.remote") + origin = self._exec_git(f"remote get-url {remote}") + return origin except subprocess.CalledProcessError: - return set() - origins = set() - for line in remotes.split("\n"): - if not line: - continue - _, destination = line.split("\t") - url, _ = destination.split(" ") - origins.add(url) - return origins + return "" def _exec_git(self, args): cmd = ["git"] @@ -107,10 +113,11 @@ def init(self): def generate(self): current_info = GitVersion(self.args.sourcedir).get_version_info() - if "SOURCE_DATE_EPOCH" in os.environ: - build_date = datetime.utcfromtimestamp(int(os.environ["SOURCE_DATE_EPOCH"])) - else: - build_date = date.today() + build_date = ( + date.today() + if current_info["BUILD_DIRTY"] + else current_info["GIT_COMMIT_DATE"] + ) current_info.update( { @@ -120,6 +127,8 @@ def generate(self): } ) + del current_info["GIT_COMMIT_DATE"] + version_values = [] for key in current_info: val = current_info[key] diff --git a/scripts/wifi_board.py b/scripts/wifi_board.py new file mode 100755 index 0000000000..3f89ebdc65 --- /dev/null +++ b/scripts/wifi_board.py @@ -0,0 +1,240 @@ +#!/usr/bin/env python3 + +from flipper.app import App +from serial.tools.list_ports_common import ListPortInfo + +import logging +import os +import tempfile +import subprocess +import serial.tools.list_ports as list_ports +import json +import requests +import tarfile + + +class UpdateDownloader: + UPDATE_SERVER = "https://update.flipperzero.one" + UPDATE_PROJECT = "/blackmagic-firmware" + UPDATE_INDEX = UPDATE_SERVER + UPDATE_PROJECT + "/directory.json" + UPDATE_TYPE = "full_tgz" + + CHANNEL_ID_ALIAS = { + "dev": "development", + "rc": "release-candidate", + "r": "release", + "rel": "release", + } + + def __init__(self): + self.logger = logging.getLogger() + + def download(self, channel_id: str, dir: str) -> bool: + # Aliases + if channel_id in self.CHANNEL_ID_ALIAS: + channel_id = self.CHANNEL_ID_ALIAS[channel_id] + + # Make directory + if not os.path.exists(dir): + self.logger.info(f"Creating directory {dir}") + os.makedirs(dir) + + # Download json index + self.logger.info(f"Downloading {self.UPDATE_INDEX}") + response = requests.get(self.UPDATE_INDEX) + if response.status_code != 200: + self.logger.error(f"Failed to download {self.UPDATE_INDEX}") + return False + + # Parse json index + try: + index = json.loads(response.content) + except Exception as e: + self.logger.error(f"Failed to parse json index: {e}") + return False + + # Find channel + channel = None + for channel_candidate in index["channels"]: + if channel_candidate["id"] == channel_id: + channel = channel_candidate + break + + # Check if channel found + if channel is None: + self.logger.error( + f"Channel '{channel_id}' not found. Valid channels: {', '.join([c['id'] for c in index['channels']])}" + ) + return False + + self.logger.info(f"Using channel '{channel_id}'") + + # Get latest version + try: + version = channel["versions"][0] + except Exception as e: + self.logger.error(f"Failed to get version: {e}") + return False + + self.logger.info(f"Using version '{version['version']}'") + + # Get changelog + changelog = None + try: + changelog = version["changelog"] + except Exception as e: + self.logger.error(f"Failed to get changelog: {e}") + + # print changelog + if changelog is not None: + self.logger.info(f"Changelog:") + for line in changelog.split("\n"): + if line.strip() == "": + continue + self.logger.info(f" {line}") + + # Find file + file_url = None + for file_candidate in version["files"]: + if file_candidate["type"] == self.UPDATE_TYPE: + file_url = file_candidate["url"] + break + + if file_url is None: + self.logger.error(f"File not found") + return False + + # Make file path + file_name = file_url.split("/")[-1] + file_path = os.path.join(dir, file_name) + + # Download file + self.logger.info(f"Downloading {file_url} to {file_path}") + with open(file_path, "wb") as f: + response = requests.get(file_url) + f.write(response.content) + + # Unzip tgz + self.logger.info(f"Unzipping {file_path}") + with tarfile.open(file_path, "r") as tar: + tar.extractall(dir) + + return True + + +class Main(App): + def init(self): + self.parser.add_argument("-p", "--port", help="CDC Port", default="auto") + self.parser.add_argument( + "-c", "--channel", help="Channel name", default="release" + ) + self.parser.set_defaults(func=self.update) + + # logging + self.logger = logging.getLogger() + + def find_wifi_board(self) -> bool: + # idk why, but python thinks that list_ports.grep returns tuple[str, str, str] + blackmagics: list[ListPortInfo] = list(list_ports.grep("blackmagic")) # type: ignore + daps: list[ListPortInfo] = list(list_ports.grep("CMSIS-DAP")) # type: ignore + + return len(blackmagics) > 0 or len(daps) > 0 + + def find_wifi_board_bootloader(self): + # idk why, but python thinks that list_ports.grep returns tuple[str, str, str] + ports: list[ListPortInfo] = list(list_ports.grep("ESP32-S2")) # type: ignore + + if len(ports) == 0: + # Blackmagic probe serial port not found, will be handled later + pass + elif len(ports) > 1: + raise Exception("More than one WiFi board found") + else: + port = ports[0] + if os.name == "nt": + port.device = f"\\\\.\\{port.device}" + return port.device + + def update(self): + try: + port = self.find_wifi_board_bootloader() + except Exception as e: + self.logger.error(f"{e}") + return 1 + + if self.args.port != "auto": + port = self.args.port + + available_ports = [p[0] for p in list(list_ports.comports())] + if port not in available_ports: + self.logger.error(f"Port {port} not found") + return 1 + + if port is None: + if self.find_wifi_board(): + self.logger.error("WiFi board found, but not in bootloader mode.") + self.logger.info("Please hold down BOOT button and press RESET button") + else: + self.logger.error("WiFi board not found") + self.logger.info( + "Please connect WiFi board to your computer, hold down BOOT button and press RESET button" + ) + return 1 + + # get temporary dir + with tempfile.TemporaryDirectory() as temp_dir: + downloader = UpdateDownloader() + + # download latest channel update + try: + if not downloader.download(self.args.channel, temp_dir): + self.logger.error(f"Cannot download update") + return 1 + except Exception as e: + self.logger.error(f"Cannot download update: {e}") + return 1 + + with open(os.path.join(temp_dir, "flash.command"), "r") as f: + flash_command = f.read() + + flash_command = flash_command.replace("\n", "").replace("\r", "") + flash_command = flash_command.replace("(PORT)", port) + + # We can't reset the board after flashing via usb + flash_command = flash_command.replace( + "--after hard_reset", "--after no_reset_stub" + ) + + args = flash_command.split(" ")[0:] + args = list(filter(None, args)) + + esptool_params = [] + esptool_params.extend(args) + + self.logger.info(f'Running command: "{" ".join(args)}" in "{temp_dir}"') + + process = subprocess.Popen( + esptool_params, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + cwd=temp_dir, + bufsize=1, + universal_newlines=True, + ) + + while process.poll() is None: + if process.stdout is not None: + for line in process.stdout: + self.logger.debug(f"{line.strip()}") + + if process.returncode != 0: + self.logger.error(f"Failed to flash WiFi board") + else: + self.logger.info("WiFi board flashed successfully") + self.logger.info("Press RESET button on WiFi board to start it") + + return process.returncode + + +if __name__ == "__main__": + Main()() diff --git a/site_scons/commandline.scons b/site_scons/commandline.scons index f6deeb4208..8ea43ca718 100644 --- a/site_scons/commandline.scons +++ b/site_scons/commandline.scons @@ -248,6 +248,11 @@ vars.AddVariables( " app can check what version it is being built for.", "Official", ), + ( + "FLIP_PORT", + "Full port name of Flipper to use, if multiple Flippers are connected", + "auto", + ), ) Return("vars") diff --git a/site_scons/extapps.scons b/site_scons/extapps.scons index 9468448702..1005ab1a67 100644 --- a/site_scons/extapps.scons +++ b/site_scons/extapps.scons @@ -65,9 +65,9 @@ class FlipperExtAppBuildArtifacts: apps_to_build_as_faps = [ + FlipperAppType.FAPP, FlipperAppType.PLUGIN, FlipperAppType.EXTERNAL, - FlipperAppType.EXTMAINAPP, FlipperAppType.DEBUG, ]