diff --git a/.gitignore b/.gitignore index e33c10125..d6a2dc470 100644 --- a/.gitignore +++ b/.gitignore @@ -30,7 +30,7 @@ bindings/ Brewfile.lock.json # Visual Studio Code -.vscode/ +/.vscode/ # Visual Studio .vs/ diff --git a/.pvsoptions b/.pvsoptions index 6b22aed76..ecb333dec 100644 --- a/.pvsoptions +++ b/.pvsoptions @@ -1 +1 @@ ---ignore-ccache -C gccarm --rules-config .pvsconfig -e lib/fatfs -e lib/fnv1a-hash -e lib/FreeRTOS-Kernel -e lib/heatshrink -e lib/libusb_stm32 -e lib/littlefs -e lib/mbedtls -e lib/micro-ecc -e lib/microtar -e lib/mlib -e lib/qrcode -e lib/ST25RFAL002 -e lib/STM32CubeWB -e lib/u8g2 -e lib/nanopb -e */arm-none-eabi/* -e applications/external/dap_link/lib/free-dap +--ignore-ccache -C gccarm --rules-config .pvsconfig -e lib/fatfs -e lib/fnv1a-hash -e lib/FreeRTOS-Kernel -e lib/heatshrink -e lib/libusb_stm32 -e lib/littlefs -e lib/mbedtls -e lib/micro-ecc -e lib/microtar -e lib/mlib -e lib/qrcode -e lib/ST25RFAL002 -e lib/STM32CubeWB -e lib/u8g2 -e lib/xtreme -e lib/nanopb -e */arm-none-eabi/* -e applications/external/dap_link/lib/free-dap diff --git a/ReadMe.md b/ReadMe.md index 87e6df32e..6934b45c1 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -32,18 +32,23 @@ Note, the below mentioned changes are only a few things we did. For a full list We wrote a powerful yet easy-to-use application specifically for our Firmware, that gives you easy-access to all the fancy things we implemented: - + -Misc: -All the other options that don't fit elsewhere. Change your Flipper's name, change xp level, and manage settings for RGB backlight. + + -
+- Interface: Customize every bit of your Flipper, from the desktop animations, to the main menu apps, lockscreen style etc. + +- Protocols: Here you can toggle between USB & Bluetooth mode for BadKB, and manage custom Subghz frequencies. + +- Misc: All the other options that don't fit elsewhere. Change your Flipper's name, xp level, and configure the RGB backlight. + +
-----
diff --git a/applications/debug/accessor/accessor_app.cpp b/applications/debug/accessor/accessor_app.cpp index 2e40b3c35..05a248efa 100644 --- a/applications/debug/accessor/accessor_app.cpp +++ b/applications/debug/accessor/accessor_app.cpp @@ -23,7 +23,7 @@ void AccessorApp::run(void) { exit = switch_to_previous_scene(); } } - }; + } scenes[current_scene]->on_exit(this); diff --git a/applications/debug/battery_test_app/views/battery_info.c b/applications/debug/battery_test_app/views/battery_info.c index 5353a2e2a..e32d1b461 100644 --- a/applications/debug/battery_test_app/views/battery_info.c +++ b/applications/debug/battery_test_app/views/battery_info.c @@ -17,7 +17,7 @@ static void draw_stat(Canvas* canvas, int x, int y, const Icon* icon, char* val) canvas_draw_box(canvas, x - 4, y + 16, 24, 6); canvas_set_color(canvas, ColorBlack); canvas_draw_str_aligned(canvas, x + 8, y + 22, AlignCenter, AlignBottom, val); -}; +} static void draw_battery(Canvas* canvas, BatteryInfoModel* data, int x, int y) { char emote[20] = {}; @@ -85,7 +85,7 @@ static void draw_battery(Canvas* canvas, BatteryInfoModel* data, int x, int y) { canvas_draw_str_aligned(canvas, 92, y + 3, AlignCenter, AlignCenter, emote); canvas_draw_str_aligned(canvas, 92, y + 15, AlignCenter, AlignCenter, header); canvas_draw_str_aligned(canvas, 92, y + 27, AlignCenter, AlignCenter, value); -}; +} static void battery_info_draw_callback(Canvas* canvas, void* context) { furi_assert(context); diff --git a/applications/debug/uart_echo/application.fam b/applications/debug/uart_echo/application.fam index c4079c6c1..ecdc847cd 100644 --- a/applications/debug/uart_echo/application.fam +++ b/applications/debug/uart_echo/application.fam @@ -1,12 +1,12 @@ App( appid="UART_Echo", name="[GPIO] UART Echo", - apptype=FlipperAppType.EXTERNAL, + apptype=FlipperAppType.DEBUG, entry_point="uart_echo_app", cdefines=["APP_UART_ECHO"], requires=["gui"], stack_size=2 * 1024, order=70, fap_icon="uart_10px.png", - fap_category="GPIO", + fap_category="Debug", ) diff --git a/applications/debug/unit_tests/nfc/nfc_test.c b/applications/debug/unit_tests/nfc/nfc_test.c index 54bdd5909..3b3a44431 100644 --- a/applications/debug/unit_tests/nfc/nfc_test.c +++ b/applications/debug/unit_tests/nfc/nfc_test.c @@ -1,10 +1,12 @@ #include #include +#include #include #include #include #include #include +#include #include #include @@ -179,6 +181,153 @@ MU_TEST(nfc_digital_signal_test) { "NFC long digital signal test failed\r\n"); } +static bool nfc_test_pulse_reader_toggle( + uint32_t usec_low, + uint32_t usec_high, + uint32_t period_count, + uint32_t tolerance) { + furi_assert(nfc_test); + + bool success = false; + uint32_t pulses = 0; + const GpioPin* gpio_in = &gpio_ext_pa6; + const GpioPin* gpio_out = &gpio_ext_pa7; + PulseReader* reader = NULL; + + do { + reader = pulse_reader_alloc(gpio_in, 512); + + if(!reader) { + FURI_LOG_E(TAG, "failed to allocate pulse reader"); + break; + } + + /* use TIM1 to create a specific number of pulses with defined duty cycle + but first set the IO to high, so the low/high pulse can get detected */ + furi_hal_gpio_init(gpio_out, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + furi_hal_gpio_write(gpio_out, true); + + LL_TIM_DeInit(TIM1); + + LL_TIM_SetCounterMode(TIM1, LL_TIM_COUNTERMODE_UP); + LL_TIM_SetRepetitionCounter(TIM1, 0); + LL_TIM_SetClockDivision(TIM1, LL_TIM_CLOCKDIVISION_DIV1); + LL_TIM_SetClockSource(TIM1, LL_TIM_CLOCKSOURCE_INTERNAL); + LL_TIM_DisableARRPreload(TIM1); + + LL_TIM_OC_DisablePreload(TIM1, LL_TIM_CHANNEL_CH1); + LL_TIM_OC_SetMode(TIM1, LL_TIM_CHANNEL_CH1, LL_TIM_OCMODE_PWM2); + LL_TIM_OC_SetPolarity(TIM1, LL_TIM_CHANNEL_CH1N, LL_TIM_OCPOLARITY_HIGH); + LL_TIM_OC_DisableFast(TIM1, LL_TIM_CHANNEL_CH1); + LL_TIM_CC_EnableChannel(TIM1, LL_TIM_CHANNEL_CH1N); + + LL_TIM_EnableAllOutputs(TIM1); + + /* now calculate the TIM1 period and compare values */ + uint32_t freq_div = 64 * (usec_low + usec_high); + uint32_t prescaler = freq_div / 0x10000LU; + uint32_t period = freq_div / (prescaler + 1); + uint32_t compare = 64 * usec_low / (prescaler + 1); + + LL_TIM_SetPrescaler(TIM1, prescaler); + LL_TIM_SetAutoReload(TIM1, period - 1); + LL_TIM_SetCounter(TIM1, period - 1); + LL_TIM_OC_SetCompareCH1(TIM1, compare); + + /* timer is ready to launch, now start the pulse reader */ + pulse_reader_set_timebase(reader, PulseReaderUnitMicrosecond); + pulse_reader_start(reader); + + /* and quickly enable and switch over the GPIO to the generated signal */ + LL_TIM_EnableCounter(TIM1); + furi_hal_gpio_init_ex( + gpio_out, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedVeryHigh, GpioAltFn1TIM1); + + /* now it's time to parse the pulses received by the reader */ + uint32_t timer_pulses = period_count; + uint32_t prev_cnt = 0; + + while(timer_pulses > 0) { + /* whenever the counter gets reset, we went through a full period */ + uint32_t cur_cnt = LL_TIM_GetCounter(TIM1); + if(cur_cnt < prev_cnt) { + timer_pulses--; + } + prev_cnt = cur_cnt; + } + /* quickly halt the counter to keep a static signal */ + LL_TIM_DisableCounter(TIM1); + + do { + /* as all edges were sampled asynchronously, the timeout can be zero */ + uint32_t length = pulse_reader_receive(reader, 0); + + /* in the last pulse, we expect a "no edge" return value. if seen that, test succeeded. */ + if(pulses > period_count * 2) { + if(length != PULSE_READER_NO_EDGE) { + FURI_LOG_E( + TAG, + "last pulse expected to be PULSE_READER_NO_EDGE, but was %lu.", + length); + break; + } + success = true; + break; + } + + /* else we shall never see "no edge" or "lost edge" */ + if(length == PULSE_READER_NO_EDGE) { + FURI_LOG_E(TAG, "%lu. pulse not expected to be PULSE_READER_NO_EDGE", pulses); + break; + } + if(length == PULSE_READER_LOST_EDGE) { + FURI_LOG_E(TAG, "%lu. pulse not expected to be PULSE_READER_LOST_EDGE", pulses); + break; + } + + if(pulses > 0) { + /* throw away the first pulse, which is the 1->0 from the first start and will be irrelevant for our test */ + bool phase = ((pulses - 1) % 2) == 1; + uint32_t expected = phase ? usec_high : usec_low; + uint32_t deviation = abs((int32_t)length - (int32_t)expected); + + if(deviation > tolerance) { + FURI_LOG_E( + TAG, + "%lu. pulse expected %lu, but pulse was %lu.", + pulses, + expected, + length); + break; + } + } + pulses++; + } while(true); + } while(false); + + if(reader != NULL) { + pulse_reader_stop(reader); + pulse_reader_free(reader); + } + + LL_TIM_DeInit(TIM1); + furi_hal_gpio_init_simple(gpio_in, GpioModeAnalog); + furi_hal_gpio_init_simple(gpio_out, GpioModeAnalog); + + return success; +} + +MU_TEST(nfc_pulse_reader_test) { + mu_assert(nfc_test_pulse_reader_toggle(1500, 2500, 50, 10), "1 ms signal failed\r\n"); + mu_assert(nfc_test_pulse_reader_toggle(10000, 10000, 10, 10), "10 ms signal failed\r\n"); + mu_assert(nfc_test_pulse_reader_toggle(100000, 100000, 5, 50), "100 ms signal failed\r\n"); + mu_assert(nfc_test_pulse_reader_toggle(100, 900, 50, 10), "1 ms asymmetric signal failed\r\n"); + mu_assert( + nfc_test_pulse_reader_toggle(3333, 6666, 10, 10), "10 ms asymmetric signal failed\r\n"); + mu_assert( + nfc_test_pulse_reader_toggle(25000, 75000, 5, 10), "100 ms asymmetric signal failed\r\n"); +} + MU_TEST(mf_classic_dict_test) { MfClassicDict* instance = NULL; uint64_t key = 0; @@ -513,6 +662,7 @@ MU_TEST(mf_classic_4k_7b_file_test) { MU_TEST_SUITE(nfc) { nfc_test_alloc(); + MU_RUN_TEST(nfc_pulse_reader_test); MU_RUN_TEST(nfca_file_test); MU_RUN_TEST(mf_mini_file_test); MU_RUN_TEST(mf_classic_1k_4b_file_test); diff --git a/applications/debug/unit_tests/rpc/rpc_test.c b/applications/debug/unit_tests/rpc/rpc_test.c index 329f3b741..167266a84 100644 --- a/applications/debug/unit_tests/rpc/rpc_test.c +++ b/applications/debug/unit_tests/rpc/rpc_test.c @@ -84,7 +84,7 @@ static void test_rpc_setup(void) { rpc = furi_record_open(RECORD_RPC); for(int i = 0; !(rpc_session[0].session) && (i < 10000); ++i) { - rpc_session[0].session = rpc_session_open(rpc); + rpc_session[0].session = rpc_session_open(rpc, RpcOwnerUnknown); furi_delay_tick(1); } furi_check(rpc_session[0].session); @@ -104,7 +104,7 @@ static void test_rpc_setup_second_session(void) { furi_check(!(rpc_session[1].session)); for(int i = 0; !(rpc_session[1].session) && (i < 10000); ++i) { - rpc_session[1].session = rpc_session_open(rpc); + rpc_session[1].session = rpc_session_open(rpc, RpcOwnerUnknown); furi_delay_tick(1); } furi_check(rpc_session[1].session); diff --git a/applications/external/airmouse/tracking/util/matrix_4x4.h b/applications/external/airmouse/tracking/util/matrix_4x4.h index 9934f6be0..83f151fc4 100644 --- a/applications/external/airmouse/tracking/util/matrix_4x4.h +++ b/applications/external/airmouse/tracking/util/matrix_4x4.h @@ -34,4 +34,4 @@ private: } // namespace cardboard -#endif // CARDBOARD_SDK_UTIL_MATRIX4X4_H_ +#endif // CARDBOARD_SDK_UTIL_MATRIX_4X4_H_ diff --git a/applications/external/asteroids/application.fam b/applications/external/asteroids/application.fam index 5f70a0e1c..5eb43a6e5 100644 --- a/applications/external/asteroids/application.fam +++ b/applications/external/asteroids/application.fam @@ -8,8 +8,8 @@ App( stack_size=8 * 1024, order=50, fap_icon="appicon.png", - fap_icon_assets="assets", # Image assets to compile for this application 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", diff --git a/applications/external/avr_isp_programmer/helpers/avr_isp.c b/applications/external/avr_isp_programmer/helpers/avr_isp.c index 51b4f8846..1b9e2fd1f 100644 --- a/applications/external/avr_isp_programmer/helpers/avr_isp.c +++ b/applications/external/avr_isp_programmer/helpers/avr_isp.c @@ -152,7 +152,12 @@ bool avr_isp_auto_set_spi_speed_start_pmode(AvrIsp* instance) { } } } - if(instance->spi) avr_isp_spi_sw_free(instance->spi); + + if(instance->spi) { + avr_isp_spi_sw_free(instance->spi); + instance->spi = NULL; + } + return false; } @@ -169,7 +174,7 @@ static void avr_isp_commit(AvrIsp* instance, uint16_t addr, uint8_t data) { while((furi_get_tick() - starttime) < 30) { if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FLASH_HI(addr)) != 0xFF) { break; - }; + } } } } @@ -352,7 +357,7 @@ uint8_t avr_isp_read_lock_byte(AvrIsp* instance) { data = avr_isp_spi_transaction(instance, AVR_ISP_READ_LOCK_BYTE); if(avr_isp_spi_transaction(instance, AVR_ISP_READ_LOCK_BYTE) == data) { break; - }; + } data = 0x00; } return data; @@ -372,7 +377,7 @@ bool avr_isp_write_lock_byte(AvrIsp* instance, uint8_t lock) { if(avr_isp_spi_transaction(instance, AVR_ISP_READ_LOCK_BYTE) == lock) { ret = true; break; - }; + } } } return ret; @@ -387,7 +392,7 @@ uint8_t avr_isp_read_fuse_low(AvrIsp* instance) { data = avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_LOW); if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_LOW) == data) { break; - }; + } data = 0x00; } return data; @@ -407,7 +412,7 @@ bool avr_isp_write_fuse_low(AvrIsp* instance, uint8_t lfuse) { if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_LOW) == lfuse) { ret = true; break; - }; + } } } return ret; @@ -422,7 +427,7 @@ uint8_t avr_isp_read_fuse_high(AvrIsp* instance) { data = avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_HIGH); if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_HIGH) == data) { break; - }; + } data = 0x00; } return data; @@ -442,7 +447,7 @@ bool avr_isp_write_fuse_high(AvrIsp* instance, uint8_t hfuse) { if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_HIGH) == hfuse) { ret = true; break; - }; + } } } return ret; @@ -457,7 +462,7 @@ uint8_t avr_isp_read_fuse_extended(AvrIsp* instance) { data = avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_EXTENDED); if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_EXTENDED) == data) { break; - }; + } data = 0x00; } return data; @@ -477,7 +482,7 @@ bool avr_isp_write_fuse_extended(AvrIsp* instance, uint8_t efuse) { if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_EXTENDED) == efuse) { ret = true; break; - }; + } } } return ret; diff --git a/applications/external/avr_isp_programmer/lib/driver/avr_isp_prog.c b/applications/external/avr_isp_programmer/lib/driver/avr_isp_prog.c index b3c81f3b1..051d97e9c 100644 --- a/applications/external/avr_isp_programmer/lib/driver/avr_isp_prog.c +++ b/applications/external/avr_isp_programmer/lib/driver/avr_isp_prog.c @@ -111,7 +111,7 @@ static uint8_t avr_isp_prog_getch(AvrIspProg* instance) { uint8_t data[1] = {0}; while(furi_stream_buffer_receive(instance->stream_rx, &data, sizeof(int8_t), 30) == 0) { if(instance->exit) break; - }; + } return data[0]; } @@ -196,7 +196,7 @@ static void avr_isp_prog_set_cfg(AvrIspProg* instance) { instance->cfg->lockbytes = instance->buff[6]; instance->cfg->fusebytes = instance->buff[7]; instance->cfg->flashpoll = instance->buff[8]; - // ignore (instance->buff[9] == instance->buff[8]) //FLASH polling value. Same as “flashpoll” + // ignore (instance->buff[9] == instance->buff[8]) //FLASH polling value. Same as οΏ½flashpollοΏ½ instance->cfg->eeprompoll = instance->buff[10] << 8 | instance->buff[11]; instance->cfg->pagesize = instance->buff[12] << 8 | instance->buff[13]; instance->cfg->eepromsize = instance->buff[14] << 8 | instance->buff[15]; @@ -317,7 +317,12 @@ static bool avr_isp_prog_auto_set_spi_speed_start_pmode(AvrIspProg* instance) { } } } - if(instance->spi) avr_isp_spi_sw_free(instance->spi); + + if(instance->spi) { + avr_isp_spi_sw_free(instance->spi); + instance->spi = NULL; + } + return false; } @@ -343,7 +348,7 @@ static void avr_isp_prog_commit(AvrIspProg* instance, uint16_t addr, uint8_t dat while((furi_get_tick() - starttime) < 30) { if(avr_isp_prog_spi_transaction(instance, AVR_ISP_READ_FLASH_HI(addr)) != 0xFF) { break; - }; + } } } } diff --git a/applications/external/barcode_gen/barcode_utils.c b/applications/external/barcode_gen/barcode_utils.c index 0a4770045..502014d85 100644 --- a/applications/external/barcode_gen/barcode_utils.c +++ b/applications/external/barcode_gen/barcode_utils.c @@ -98,7 +98,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 +121,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/bomberduck/LICENSE b/applications/external/bomberduck/LICENSE new file mode 100644 index 000000000..4624b249c --- /dev/null +++ b/applications/external/bomberduck/LICENSE @@ -0,0 +1,22 @@ +MIT License + +Copyright (c) 2023 лСнь + +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/bomberduck/application.fam b/applications/external/bomberduck/application.fam new file mode 100644 index 000000000..d2ee5564b --- /dev/null +++ b/applications/external/bomberduck/application.fam @@ -0,0 +1,14 @@ +App( + appid="bomberduck", + name="Bomberduck", + apptype=FlipperAppType.EXTERNAL, + entry_point="bomberduck_app", + requires=[ + "gui", + ], + stack_size=1 * 1024, + order=90, + fap_icon="bomb.png", + fap_category="Games", + fap_icon_assets="assets", +) diff --git a/applications/external/bomberduck/assets/bomb0.png b/applications/external/bomberduck/assets/bomb0.png new file mode 100644 index 000000000..3fdc3a3c1 Binary files /dev/null and b/applications/external/bomberduck/assets/bomb0.png differ diff --git a/applications/external/bomberduck/assets/bomb1.png b/applications/external/bomberduck/assets/bomb1.png new file mode 100644 index 000000000..11d05b9b7 Binary files /dev/null and b/applications/external/bomberduck/assets/bomb1.png differ diff --git a/applications/external/bomberduck/assets/bomb2.png b/applications/external/bomberduck/assets/bomb2.png new file mode 100644 index 000000000..38ce7c732 Binary files /dev/null and b/applications/external/bomberduck/assets/bomb2.png differ diff --git a/applications/external/bomberduck/assets/box.png b/applications/external/bomberduck/assets/box.png new file mode 100644 index 000000000..bbd352b6f Binary files /dev/null and b/applications/external/bomberduck/assets/box.png differ diff --git a/applications/external/bomberduck/assets/end.png b/applications/external/bomberduck/assets/end.png new file mode 100644 index 000000000..d634933b7 Binary files /dev/null and b/applications/external/bomberduck/assets/end.png differ diff --git a/applications/external/bomberduck/assets/enemy1.png b/applications/external/bomberduck/assets/enemy1.png new file mode 100644 index 000000000..7ee7cb27f Binary files /dev/null and b/applications/external/bomberduck/assets/enemy1.png differ diff --git a/applications/external/bomberduck/assets/enemyleft.png b/applications/external/bomberduck/assets/enemyleft.png new file mode 100644 index 000000000..bb85dfbb2 Binary files /dev/null and b/applications/external/bomberduck/assets/enemyleft.png differ diff --git a/applications/external/bomberduck/assets/enemyright.png b/applications/external/bomberduck/assets/enemyright.png new file mode 100644 index 000000000..45e6a861a Binary files /dev/null and b/applications/external/bomberduck/assets/enemyright.png differ diff --git a/applications/external/bomberduck/assets/explore.png b/applications/external/bomberduck/assets/explore.png new file mode 100644 index 000000000..5eb50b669 Binary files /dev/null and b/applications/external/bomberduck/assets/explore.png differ diff --git a/applications/external/bomberduck/assets/playerleft.png b/applications/external/bomberduck/assets/playerleft.png new file mode 100644 index 000000000..86997a985 Binary files /dev/null and b/applications/external/bomberduck/assets/playerleft.png differ diff --git a/applications/external/bomberduck/assets/playerright.png b/applications/external/bomberduck/assets/playerright.png new file mode 100644 index 000000000..1a6283d9c Binary files /dev/null and b/applications/external/bomberduck/assets/playerright.png differ diff --git a/applications/external/bomberduck/assets/unbreakbox.png b/applications/external/bomberduck/assets/unbreakbox.png new file mode 100644 index 000000000..5e65912d5 Binary files /dev/null and b/applications/external/bomberduck/assets/unbreakbox.png differ diff --git a/applications/external/bomberduck/bomb.png b/applications/external/bomberduck/bomb.png new file mode 100644 index 000000000..44b9bfdea Binary files /dev/null and b/applications/external/bomberduck/bomb.png differ diff --git a/applications/external/bomberduck/bomberduck.c b/applications/external/bomberduck/bomberduck.c new file mode 100644 index 000000000..8c38b1a9c --- /dev/null +++ b/applications/external/bomberduck/bomberduck.c @@ -0,0 +1,643 @@ +#include +#include + +#include +#include +#include +#include +#include +#include "bomberduck_icons.h" + +int max(int a, int b) { + return (a > b) ? a : b; +} + +int min(int a, int b) { + return (a < b) ? a : b; +} + +#define WorldSizeX 12 +#define WorldSizeY 6 +#define BombRange 1 + +typedef struct { + FuriMutex* mutex; +} BomberState; + +typedef struct { + int row; + int col; +} Cell; + +typedef struct { + Cell cells[WorldSizeY * WorldSizeX]; + int front; + int rear; +} Queue; + +void enqueue(Queue* q, Cell c) { + q->cells[q->rear] = c; + q->rear++; +} + +Cell dequeue(Queue* q) { + Cell c = q->cells[q->front]; + q->front++; + + return c; +} + +bool is_empty(Queue* q) { + return q->front == q->rear; +} + +typedef struct { + int x; + int y; + int planted; +} Bomb; + +typedef struct { + int x; + int y; + bool side; +} Player; + +typedef struct { + int x; + int y; + int last; + bool side; + int level; +} Enemy; + +typedef struct { + int matrix[WorldSizeY][WorldSizeX]; + Player* player; + bool running; + int level; + + Enemy enemies[10]; + int enemies_count; + + Bomb bombs[100]; + int bombs_count; + + int endx; + int endy; +} World; + +Player player = {0, 0, 1}; +World world = {{{0}}, &player, 1, 0, {}, 0, {}, 0, 0, 0}; +bool vibration = false; + +void init() { + player.x = 1; + player.y = 1; + + world.endx = 4 + rand() % 8; + world.endy = rand() % 6; + for(int i = 0; i < WorldSizeY; i++) { + for(int j = 0; j < WorldSizeX; j++) { + world.matrix[i][j] = rand() % 3; + } + } + world.running = 1; + world.bombs_count = 0; + vibration = false; + for(int j = max(0, player.y - BombRange); j < min(WorldSizeY, player.y + BombRange + 1); j++) { + world.matrix[j][player.x] = 0; + } + + for(int j = max(0, player.x - BombRange); j < min(WorldSizeX, player.x + BombRange + 1); j++) { + world.matrix[player.y][j] = 0; + } + + world.enemies_count = 0; + for(int j = 0; j < rand() % 4 + world.level / 5; j++) { + Enemy enemy; + enemy.x = 4 + rand() % 7; + enemy.y = rand() % 6; + enemy.last = 0; + enemy.side = 1; + enemy.level = 0; + + world.enemies[j] = enemy; + world.enemies_count++; + + for(int m = max(0, world.enemies[j].y - BombRange); + m < min(WorldSizeY, world.enemies[j].y + BombRange + 1); + m++) { + world.matrix[m][world.enemies[j].x] = 0; + } + + for(int m = max(0, world.enemies[j].x - BombRange); + m < min(WorldSizeX, world.enemies[j].x + BombRange + 1); + m++) { + world.matrix[world.enemies[j].y][m] = 0; + } + } + world.matrix[world.endy][world.endx] = 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, +}; + +static const NotificationSequence bomb2 = { + &message_vibro_on, + &message_delay_25, + &message_vibro_off, + NULL, +}; + +static const NotificationSequence bomb_explore = { + &message_vibro_on, + &message_delay_50, + &message_vibro_off, + NULL, +}; + +static const NotificationSequence vibr1 = { + &message_vibro_on, + &message_delay_10, + &message_vibro_off, + &message_delay_10, + &message_vibro_on, + &message_delay_10, + &message_vibro_off, + &message_delay_10, + + 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; + } +} + +bool BFS() { + // Initialize visited array and queue + int visited[WorldSizeY][WorldSizeX] = {0}; + Queue q = {.front = 0, .rear = 0}; + // Mark the starting cell as visited and enqueue it + visited[world.player->y][world.player->x] = 1; + Cell startCell = {.row = world.player->y, .col = world.player->x}; + enqueue(&q, startCell); + // Traverse the field + while(!is_empty(&q)) { + // Dequeue a cell from the queue + Cell currentCell = dequeue(&q); + // Check if the current cell is the destination cell + if(currentCell.row == world.endy && currentCell.col == world.endx) { + return true; + } + // Check the neighboring cells + for(int rowOffset = -1; rowOffset <= 1; rowOffset++) { + for(int colOffset = -1; colOffset <= 1; colOffset++) { + // Skip diagonals and the current cell + if(rowOffset == 0 && colOffset == 0) { + continue; + } + if(rowOffset != 0 && colOffset != 0) { + continue; + } + // Calculate the row and column of the neighboring cell + int neighborRow = currentCell.row + rowOffset; + int neighborCol = currentCell.col + colOffset; + // Skip out-of-bounds cells and already visited cells + if(neighborRow < 0 || neighborRow >= WorldSizeY || neighborCol < 0 || + neighborCol >= WorldSizeX) { + continue; + } + if(visited[neighborRow][neighborCol]) { + continue; + } + // Mark the neighboring cell as visited and enqueue it + if(world.matrix[neighborRow][neighborCol] != 2) { + visited[neighborRow][neighborCol] = 1; + Cell neighborCell = {.row = neighborRow, .col = neighborCol}; + enqueue(&q, neighborCell); + } + } + } + } + return false; +} + +static void draw_callback(Canvas* canvas, void* ctx) { + furi_assert(ctx); + const BomberState* bomber_state = ctx; + + furi_mutex_acquire(bomber_state->mutex, FuriWaitForever); + if(!BFS()) { + init(); + } + canvas_clear(canvas); + + canvas_draw_icon(canvas, world.endx * 10 + 4, world.endy * 10 + 2, &I_end); + + if(world.running) { + for(size_t i = 0; i < WorldSizeY; i++) { + for(size_t j = 0; j < WorldSizeX; j++) { + switch(world.matrix[i][j]) { + case 0: + break; + case 1: + canvas_draw_icon(canvas, j * 10 + 4, i * 10 + 2, &I_box); + break; + case 2: + canvas_draw_icon(canvas, j * 10 + 4, i * 10 + 2, &I_unbreakbox); + break; + case 3: + canvas_draw_icon(canvas, j * 10 + 4, i * 10 + 2, &I_bomb0); + break; + case 4: + canvas_draw_icon(canvas, j * 10 + 4, i * 10 + 2, &I_bomb1); + break; + case 5: + canvas_draw_icon(canvas, j * 10 + 4, i * 10 + 2, &I_bomb2); + break; + case 6: + canvas_draw_icon(canvas, j * 10 + 4, i * 10 + 2, &I_explore); + world.matrix[i][j] = 0; + break; + } + } + } + + if(world.player->side) { + canvas_draw_icon( + canvas, world.player->x * 10 + 4, world.player->y * 10 + 2, &I_playerright); + } else { + canvas_draw_icon( + canvas, world.player->x * 10 + 4, world.player->y * 10 + 2, &I_playerleft); + } + + for(int i = 0; i < world.enemies_count; i++) { + if(world.enemies[i].level > 0) { + canvas_draw_icon( + canvas, world.enemies[i].x * 10 + 4, world.enemies[i].y * 10 + 2, &I_enemy1); + } else { + if(world.enemies[i].side) { + canvas_draw_icon( + canvas, + world.enemies[i].x * 10 + 4, + world.enemies[i].y * 10 + 2, + &I_enemyright); + } else { + canvas_draw_icon( + canvas, + world.enemies[i].x * 10 + 4, + world.enemies[i].y * 10 + 2, + &I_enemyleft); + } + } + } + } else { + canvas_set_font(canvas, FontPrimary); + if(world.player->x == world.endx && world.player->y == world.endy) { + if(world.level == 20) { + canvas_draw_str(canvas, 30, 35, "You win!"); + } else { + canvas_draw_str(canvas, 30, 35, "Next level!"); + char str[20]; + intToStr(world.level, str); + canvas_draw_str(canvas, 90, 35, str); + } + + } else { + canvas_draw_str(canvas, 30, 35, "You died :("); + } + } + + furi_mutex_release(bomber_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 bomberduck_app(void* p) { + UNUSED(p); + + // Π’Π΅ΠΊΡƒΡ‰Π΅Π΅ событиС Ρ‚ΠΈΠΏΠ° InputEvent + InputEvent event; + // ΠžΡ‡Π΅Ρ€Π΅Π΄ΡŒ событий Π½Π° 8 элСмСнтов Ρ€Π°Π·ΠΌΠ΅Ρ€Π° InputEvent + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); + + BomberState* bomber_state = malloc(sizeof(BomberState)); + + bomber_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); // Alloc Mutex + if(!bomber_state->mutex) { + FURI_LOG_E("BomberDuck", "cannot create mutex\r\n"); + furi_message_queue_free(event_queue); + free(bomber_state); + return 255; + } + + // Π‘ΠΎΠ·Π΄Π°Π΅ΠΌ Π½ΠΎΠ²Ρ‹ΠΉ view port + ViewPort* view_port = view_port_alloc(); + // Π‘ΠΎΠ·Π΄Π°Π΅ΠΌ callback отрисовки, Π±Π΅Π· контСкста + view_port_draw_callback_set(view_port, draw_callback, bomber_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); + + init(); + + // БСсконСчный Ρ†ΠΈΠΊΠ» ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠΈ ΠΎΡ‡Π΅Ρ€Π΅Π΄ΠΈ событий + while(1) { + if(furi_message_queue_get(event_queue, &event, 100) == FuriStatusOk) { + furi_mutex_acquire(bomber_state->mutex, FuriWaitForever); + // Если Π½Π°ΠΆΠ°Ρ‚Π° ΠΊΠ½ΠΎΠΏΠΊΠ° "Π½Π°Π·Π°Π΄", Ρ‚ΠΎ Π²Ρ‹Ρ…ΠΎΠ΄ΠΈΠΌ ΠΈΠ· Ρ†ΠΈΠΊΠ»Π°, Π° ΡΠ»Π΅Π΄ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΠ½ΠΎ ΠΈ ΠΈΠ· прилоТСния + + if(event.type == InputTypePress) { + if(event.key == InputKeyOk) { + if(world.running) { + if(world.matrix[world.player->y][world.player->x] == 0 && + world.bombs_count < 2) { + notification_message(notification, &bomb2); + world.matrix[world.player->y][world.player->x] = 3; + Bomb bomb = {world.player->x, world.player->y, furi_get_tick()}; + world.bombs[world.bombs_count] = bomb; + world.bombs_count++; + } + } else { + init(); + } + } + if(world.running) { + if(event.key == InputKeyUp) { + if(world.player->y > 0 && + world.matrix[world.player->y - 1][world.player->x] == 0) + world.player->y--; + } + if(event.key == InputKeyDown) { + if(world.player->y < WorldSizeY - 1 && + world.matrix[world.player->y + 1][world.player->x] == 0) + world.player->y++; + } + if(event.key == InputKeyLeft) { + world.player->side = 0; + if(world.player->x > 0 && + world.matrix[world.player->y][world.player->x - 1] == 0) + world.player->x--; + } + if(event.key == InputKeyRight) { + world.player->side = 1; + if(world.player->x < WorldSizeX - 1 && + world.matrix[world.player->y][world.player->x + 1] == 0) + world.player->x++; + } + } + } else if(event.type == InputTypeLong) { + if(event.key == InputKeyBack) { + break; + } + } + } + if(world.running) { + if(world.player->x == world.endx && world.player->y == world.endy) { + notification_message(notification, &end); + world.running = 0; + world.level += 1; + } + for(int i = 0; i < world.bombs_count; i++) { + if(furi_get_tick() - world.bombs[i].planted > + (unsigned long)max((3000 - world.level * 150), 1000)) { + vibration = false; + world.matrix[world.bombs[i].y][world.bombs[i].x] = 6; + notification_message(notification, &bomb_explore); + + for(int j = max(0, world.bombs[i].y - BombRange); + j < min(WorldSizeY, world.bombs[i].y + BombRange + 1); + j++) { + if(world.matrix[j][world.bombs[i].x] != 2) { + world.matrix[j][world.bombs[i].x] = 6; + if(j == world.player->y && world.bombs[i].x == world.player->x) { + notification_message(notification, &end); + world.running = 0; + } + for(int e = 0; e < world.enemies_count; e++) { + if(j == world.enemies[e].y && + world.bombs[i].x == world.enemies[e].x) { + if(world.enemies[e].level > 0) { + world.enemies[e].level--; + } else { + for(int l = e; l < world.enemies_count - 1; l++) { + world.enemies[l] = world.enemies[l + 1]; + } + world.enemies_count--; + } + } + } + } + } + + for(int j = max(0, world.bombs[i].x - BombRange); + j < min(WorldSizeX, world.bombs[i].x + BombRange + 1); + j++) { + if(world.matrix[world.bombs[i].y][j] != 2) { + world.matrix[world.bombs[i].y][j] = 6; + if(world.bombs[i].y == world.player->y && j == world.player->x) { + notification_message(notification, &end); + world.running = 0; + } + for(int e = 0; e < world.enemies_count; e++) { + if(world.bombs[i].y == world.enemies[e].y && + j == world.enemies[e].x) { + if(world.enemies[e].level > 0) { + world.enemies[e].level--; + } else { + for(int l = e; l < world.enemies_count - 1; l++) { + world.enemies[l] = world.enemies[l + 1]; + } + world.enemies_count--; + } + } + } + } + } + + for(int j = i; j < world.bombs_count - 1; j++) { + world.bombs[j] = world.bombs[j + 1]; + } + world.bombs_count--; + } else if( + furi_get_tick() - world.bombs[i].planted > + (unsigned long)max((3000 - world.level * 150) * 2 / 3, 666) && + world.matrix[world.bombs[i].y][world.bombs[i].x] != 5) { + world.matrix[world.bombs[i].y][world.bombs[i].x] = 5; + vibration = true; + + } else if( + furi_get_tick() - world.bombs[i].planted > + (unsigned long)max((3000 - world.level * 150) / 3, 333) && + world.matrix[world.bombs[i].y][world.bombs[i].x] != 4) { + world.matrix[world.bombs[i].y][world.bombs[i].x] = 4; + } + } + for(int e = 0; e < world.enemies_count; e++) { + if(world.player->y == world.enemies[e].y && + world.player->x == world.enemies[e].x) { + notification_message(notification, &end); + world.running = 0; + } + } + + for(int e = 0; e < world.enemies_count; e++) { + if(world.enemies[e].level > 0) { + if(furi_get_tick() - world.enemies[e].last > + (unsigned long)max((2000 - world.level * 100), 1000)) { + world.enemies[e].last = furi_get_tick(); + int move = rand() % 4; + switch(move) { + case 0: + if(world.enemies[e].y > 0 && + world.matrix[world.enemies[e].y - 1][world.enemies[e].x] != 2) + world.enemies[e].y--; + break; + case 1: + if(world.enemies[e].y < WorldSizeY - 1 && + world.matrix[world.enemies[e].y + 1][world.enemies[e].x] != 2) + world.enemies[e].y++; + break; + case 2: + world.enemies[e].side = 0; + if(world.enemies[e].x > 0 && + world.matrix[world.enemies[e].y][world.enemies[e].x - 1] != 2) + world.enemies[e].x--; + break; + case 3: + world.enemies[e].side = 1; + if(world.enemies[e].x < WorldSizeX - 1 && + world.matrix[world.enemies[e].y][world.enemies[e].x + 1] != 2) + world.enemies[e].x++; + default: + break; + } + } + } else { + if(furi_get_tick() - world.enemies[e].last > + (unsigned long)max((1000 - world.level * 50), 500)) { + world.enemies[e].last = furi_get_tick(); + int move = rand() % 4; + switch(move) { + case 0: + if(world.enemies[e].y > 0 && + world.matrix[world.enemies[e].y - 1][world.enemies[e].x] == 0) + world.enemies[e].y--; + break; + case 1: + if(world.enemies[e].y < WorldSizeY - 1 && + world.matrix[world.enemies[e].y + 1][world.enemies[e].x] == 0) + world.enemies[e].y++; + break; + case 2: + world.enemies[e].side = 0; + if(world.enemies[e].x > 0 && + world.matrix[world.enemies[e].y][world.enemies[e].x - 1] == 0) + world.enemies[e].x--; + break; + case 3: + world.enemies[e].side = 1; + if(world.enemies[e].x < WorldSizeX - 1 && + world.matrix[world.enemies[e].y][world.enemies[e].x + 1] == 0) + world.enemies[e].x++; + default: + break; + } + } + } + } + for(int e = 0; e < world.enemies_count; e++) { + for(int h = e + 1; h < world.enemies_count; h++) { + if(world.enemies[e].y == world.enemies[h].y && + world.enemies[e].x == world.enemies[h].x) { + world.enemies[h].level++; + for(int l = e; l < world.enemies_count - 1; l++) { + world.enemies[l] = world.enemies[l + 1]; + } + world.enemies_count--; + } + } + } + if(vibration) { + notification_message(notification, &vibr1); + } + } + + view_port_update(view_port); + furi_mutex_release(bomber_state->mutex); + } + + // Return to normal backlight settings + 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(bomber_state->mutex); + furi_record_close(RECORD_GUI); + free(bomber_state); + + return 0; +} diff --git a/applications/external/bpmtapper/img/screenshot.png b/applications/external/bpmtapper/img/screenshot.png deleted file mode 100644 index fbba2aad9..000000000 Binary files a/applications/external/bpmtapper/img/screenshot.png and /dev/null differ diff --git a/applications/external/brainfuck/worker.c b/applications/external/brainfuck/worker.c index 584bb9fb8..8fed94b40 100644 --- a/applications/external/brainfuck/worker.c +++ b/applications/external/brainfuck/worker.c @@ -111,7 +111,7 @@ void rShift() { memset((tmp + stackSize) - BF_STACK_STEP_SIZE, 0x00, BF_STACK_STEP_SIZE); bfStack = (uint8_t*)tmp; - }; + } if(stackPtr > stackSizeReal) { stackSizeReal = stackPtr; } diff --git a/applications/external/calculator/calculator.c b/applications/external/calculator/calculator.c index 1ca1d3a86..cc12cb7d1 100644 --- a/applications/external/calculator/calculator.c +++ b/applications/external/calculator/calculator.c @@ -199,7 +199,7 @@ void generate_calculator_layout(Canvas* canvas) { canvas_draw_str(canvas, 19, 118, " 0"); canvas_draw_str(canvas, 35, 118, " ."); canvas_draw_str(canvas, 51, 118, " ="); -}; +} void calculator_draw_callback(Canvas* canvas, void* ctx) { furi_assert(ctx); diff --git a/applications/external/cli_bridge/internal_defs.h b/applications/external/cli_bridge/internal_defs.h index 9840d008b..09fe6169c 100644 --- a/applications/external/cli_bridge/internal_defs.h +++ b/applications/external/cli_bridge/internal_defs.h @@ -79,8 +79,8 @@ typedef struct { void* view_dispatcher; void* primary_menu; - void* plugins_menu; - void* debug_menu; + // void* plugins_menu; + // void* debug_menu; void* settings_menu; volatile uint8_t lock_count; diff --git a/applications/external/doom/assets/screenshot1.png b/applications/external/doom/assets/screenshot1.png deleted file mode 100644 index 1ecb073a6..000000000 Binary files a/applications/external/doom/assets/screenshot1.png and /dev/null differ diff --git a/applications/external/doom/assets/screenshot2.png b/applications/external/doom/assets/screenshot2.png deleted file mode 100644 index 216b699de..000000000 Binary files a/applications/external/doom/assets/screenshot2.png and /dev/null differ diff --git a/applications/external/doom/assets/screenshot3.png b/applications/external/doom/assets/screenshot3.png deleted file mode 100644 index b5aec03fd..000000000 Binary files a/applications/external/doom/assets/screenshot3.png and /dev/null differ diff --git a/applications/external/doom/doom_music_player_worker.c b/applications/external/doom/doom_music_player_worker.c index e81549625..c8b9bb9db 100644 --- a/applications/external/doom/doom_music_player_worker.c +++ b/applications/external/doom/doom_music_player_worker.c @@ -393,7 +393,7 @@ bool music_player_worker_load_rtttl_from_file(MusicPlayerWorker* instance, const if(!storage_file_open(file, file_path, FSAM_READ, FSOM_OPEN_EXISTING)) { FURI_LOG_E(TAG, "Unable to open file"); break; - }; + } uint16_t ret = 0; do { diff --git a/applications/external/flipfrid/application.fam b/applications/external/flipfrid/application.fam index e2482e0d1..f66cc7b73 100644 --- a/applications/external/flipfrid/application.fam +++ b/applications/external/flipfrid/application.fam @@ -4,7 +4,7 @@ App( apptype=FlipperAppType.EXTERNAL, entry_point="flipfrid_start", requires=["gui", "storage", "dialogs", "input", "notification"], - stack_size=1 * 1024, + stack_size=2 * 1024, order=180, fap_icon="rfid_10px.png", fap_category="Tools", diff --git a/applications/external/flipfrid/flipfrid.c b/applications/external/flipfrid/flipfrid.c index ff52ab160..172a98ef3 100644 --- a/applications/external/flipfrid/flipfrid.c +++ b/applications/external/flipfrid/flipfrid.c @@ -61,6 +61,16 @@ FlipFridState* flipfrid_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; @@ -103,8 +113,13 @@ void flipfrid_free(FlipFridState* flipfrid) { furi_string_free(flipfrid->proto_name); furi_string_free(flipfrid->data_str); - free(flipfrid->data); - free(flipfrid->payload); + 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); diff --git a/applications/external/flipfrid/flipfrid.h b/applications/external/flipfrid/flipfrid.h index e4122054b..0ee8aa320 100644 --- a/applications/external/flipfrid/flipfrid.h +++ b/applications/external/flipfrid/flipfrid.h @@ -75,6 +75,8 @@ typedef struct { FlipFridProtos proto; FuriString* attack_name; FuriString* proto_name; + FuriString* main_menu_items[4]; + FuriString* main_menu_proto_items[4]; DialogsApp* dialogs; FuriString* notification_msg; diff --git a/applications/external/flipfrid/scene/flipfrid_scene_entrypoint.c b/applications/external/flipfrid/scene/flipfrid_scene_entrypoint.c index 24c19dc4c..f4b39aa66 100644 --- a/applications/external/flipfrid/scene/flipfrid_scene_entrypoint.c +++ b/applications/external/flipfrid/scene/flipfrid_scene_entrypoint.c @@ -1,8 +1,5 @@ #include "flipfrid_scene_entrypoint.h" -FuriString* main_menu_items[4]; -FuriString* main_menu_proto_items[4]; - void flipfrid_scene_entrypoint_menu_callback( FlipFridState* context, uint32_t index, @@ -68,31 +65,14 @@ void flipfrid_scene_entrypoint_on_enter(FlipFridState* context) { menu_items[i] = furi_string_alloc(); }*/ - main_menu_items[0] = furi_string_alloc_set("Default Values"); - main_menu_items[1] = furi_string_alloc_set("BF Customer ID"); - main_menu_items[2] = furi_string_alloc_set("Load File"); - main_menu_items[3] = furi_string_alloc_set("Load UIDs from file"); - context->menu_proto_index = 0; /*for(uint32_t i = 0; i < 4; i++) { menu_proto_items[i] = furi_string_alloc(); }*/ - - main_menu_proto_items[0] = furi_string_alloc_set("EM4100"); - main_menu_proto_items[1] = furi_string_alloc_set("HIDProx"); - main_menu_proto_items[2] = furi_string_alloc_set("PAC/Stanley"); - main_menu_proto_items[3] = furi_string_alloc_set("H10301"); } void flipfrid_scene_entrypoint_on_exit(FlipFridState* context) { UNUSED(context); - for(uint32_t i = 0; i < 4; i++) { - furi_string_free(main_menu_items[i]); - } - - for(uint32_t i = 0; i < 4; i++) { - furi_string_free(main_menu_proto_items[i]); - } } void flipfrid_scene_entrypoint_on_tick(FlipFridState* context) { @@ -145,73 +125,77 @@ void flipfrid_scene_entrypoint_on_draw(Canvas* canvas, FlipFridState* context) { canvas_clear(canvas); canvas_set_color(canvas, ColorBlack); - if(main_menu_items[context->menu_index] != NULL) { - if(context->menu_index > FlipFridAttackDefaultValues) { - canvas_set_font(canvas, FontSecondary); + 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, - 24, + 36, AlignCenter, AlignTop, - furi_string_get_cstr(main_menu_items[context->menu_index - 1])); - } + furi_string_get_cstr(context->main_menu_items[context->menu_index])); - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned( - canvas, - 64, - 36, - AlignCenter, - AlignTop, - furi_string_get_cstr(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_index < FlipFridAttackLoadFileCustomUids) { - canvas_set_font(canvas, FontSecondary); + 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, - 48, + 4, AlignCenter, AlignTop, - furi_string_get_cstr(main_menu_items[context->menu_index + 1])); - } + furi_string_get_cstr(context->main_menu_proto_items[context->menu_proto_index])); - if(context->menu_proto_index > EM4100) { - canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned( - canvas, - 64, - -12, - AlignCenter, - AlignTop, - furi_string_get_cstr(main_menu_proto_items[context->menu_proto_index - 1])); - } + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 101, 4, AlignCenter, AlignTop, ">"); - 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(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(main_menu_proto_items[context->menu_proto_index + 1])); + 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_run_attack.c b/applications/external/flipfrid/scene/flipfrid_scene_run_attack.c index 6c726832a..225752559 100644 --- a/applications/external/flipfrid/scene/flipfrid_scene_run_attack.c +++ b/applications/external/flipfrid/scene/flipfrid_scene_run_attack.c @@ -356,7 +356,7 @@ void flipfrid_scene_run_attack_on_tick(FlipFridState* context) { 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; @@ -370,7 +370,7 @@ void flipfrid_scene_run_attack_on_tick(FlipFridState* context) { 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++) { @@ -394,7 +394,7 @@ void flipfrid_scene_run_attack_on_tick(FlipFridState* context) { 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; @@ -408,7 +408,7 @@ void flipfrid_scene_run_attack_on_tick(FlipFridState* context) { 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++) { diff --git a/applications/external/geiger/flipper_geiger.c b/applications/external/geiger/flipper_geiger.c index a5503eb90..9c3d0d3fc 100644 --- a/applications/external/geiger/flipper_geiger.c +++ b/applications/external/geiger/flipper_geiger.c @@ -39,33 +39,53 @@ typedef struct { static void draw_callback(Canvas* canvas, void* ctx) { furi_assert(ctx); - mutexStruct displayStruct; - mutexStruct* geigerMutex = ctx; - furi_mutex_acquire(geigerMutex->mutex, FuriWaitForever); - memcpy(&displayStruct, geigerMutex, sizeof(mutexStruct)); - furi_mutex_release(geigerMutex->mutex); + mutexStruct* mutexVal = ctx; + mutexStruct mutexDraw; + furi_mutex_acquire(mutexVal->mutex, FuriWaitForever); + memcpy(&mutexDraw, mutexVal, sizeof(mutexStruct)); + furi_mutex_release(mutexVal->mutex); char buffer[32]; - if(displayStruct.data == 0) - snprintf( - buffer, sizeof(buffer), "%ld cps - %ld cpm", displayStruct.cps, displayStruct.cpm); - else if(displayStruct.data == 1) + if(mutexDraw.data == 0) + snprintf(buffer, sizeof(buffer), "%ld cps - %ld cpm", mutexDraw.cps, mutexDraw.cpm); + else if(mutexDraw.data == 1) snprintf( buffer, sizeof(buffer), "%ld cps - %.2f uSv/h", - displayStruct.cps, - ((double)displayStruct.cpm * (double)CONVERSION_FACTOR)); - else + mutexDraw.cps, + ((double)mutexDraw.cpm * (double)CONVERSION_FACTOR)); + else if(mutexDraw.data == 2) snprintf( buffer, sizeof(buffer), "%ld cps - %.2f mSv/y", - displayStruct.cps, - (((double)displayStruct.cpm * (double)CONVERSION_FACTOR)) * (double)8.76); + mutexDraw.cps, + (((double)mutexDraw.cpm * (double)CONVERSION_FACTOR)) * (double)8.76); + else if(mutexDraw.data == 3) + snprintf( + buffer, + sizeof(buffer), + "%ld cps - %.4f Rad/h", + mutexDraw.cps, + ((double)mutexDraw.cpm * (double)CONVERSION_FACTOR) / (double)10000); + else if(mutexDraw.data == 4) + snprintf( + buffer, + sizeof(buffer), + "%ld cps - %.2f mR/h", + mutexDraw.cps, + ((double)mutexDraw.cpm * (double)CONVERSION_FACTOR) / (double)10); + else + snprintf( + buffer, + sizeof(buffer), + "%ld cps - %.2f uR/h", + mutexDraw.cps, + ((double)mutexDraw.cpm * (double)CONVERSION_FACTOR) * (double)100); for(int i = 0; i < SCREEN_SIZE_X; i += 2) { - float Y = SCREEN_SIZE_Y - (displayStruct.line[i / 2] * displayStruct.coef); + float Y = SCREEN_SIZE_Y - (mutexDraw.line[i / 2] * mutexDraw.coef); canvas_draw_line(canvas, i, Y, i, SCREEN_SIZE_Y); canvas_draw_line(canvas, i + 1, Y, i + 1, SCREEN_SIZE_Y); @@ -103,8 +123,7 @@ static void gpiocallback(void* ctx) { furi_message_queue_put(queue, &event, 0); } -int32_t flipper_geiger_app(void* p) { - UNUSED(p); +int32_t flipper_geiger_app() { EventApp event; FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(EventApp)); @@ -127,7 +146,7 @@ int32_t flipper_geiger_app(void* p) { } ViewPort* view_port = view_port_alloc(); - view_port_draw_callback_set(view_port, draw_callback, &mutexVal); + view_port_draw_callback_set(view_port, draw_callback, &mutexVal.mutex); view_port_input_callback_set(view_port, input_callback, event_queue); furi_hal_gpio_add_int_callback(&gpio_ext_pa7, gpiocallback, event_queue); @@ -167,7 +186,7 @@ int32_t flipper_geiger_app(void* p) { if(mutexVal.data != 0) mutexVal.data--; else - mutexVal.data = 2; + mutexVal.data = 5; screenRefresh = 1; furi_mutex_release(mutexVal.mutex); @@ -175,7 +194,7 @@ int32_t flipper_geiger_app(void* p) { event.input.type == InputTypeShort)) { furi_mutex_acquire(mutexVal.mutex, FuriWaitForever); - if(mutexVal.data != 2) + if(mutexVal.data != 5) mutexVal.data++; else mutexVal.data = 0; diff --git a/applications/external/gpio_reader_a/GPIO_reader.c b/applications/external/gpioreader_a/GPIO_reader.c similarity index 100% rename from applications/external/gpio_reader_a/GPIO_reader.c rename to applications/external/gpioreader_a/GPIO_reader.c diff --git a/applications/external/gpio_reader_a/GPIO_reader_item.c b/applications/external/gpioreader_a/GPIO_reader_item.c similarity index 100% rename from applications/external/gpio_reader_a/GPIO_reader_item.c rename to applications/external/gpioreader_a/GPIO_reader_item.c diff --git a/applications/external/gpio_reader_a/GPIO_reader_item.h b/applications/external/gpioreader_a/GPIO_reader_item.h similarity index 100% rename from applications/external/gpio_reader_a/GPIO_reader_item.h rename to applications/external/gpioreader_a/GPIO_reader_item.h diff --git a/applications/external/gpio_reader_a/application.fam b/applications/external/gpioreader_a/application.fam similarity index 100% rename from applications/external/gpio_reader_a/application.fam rename to applications/external/gpioreader_a/application.fam diff --git a/applications/external/gpio_reader_a/icon.png b/applications/external/gpioreader_a/icon.png similarity index 100% rename from applications/external/gpio_reader_a/icon.png rename to applications/external/gpioreader_a/icon.png diff --git a/applications/external/gpio_reader_b/LICENSE b/applications/external/gpioreader_b/LICENSE similarity index 100% rename from applications/external/gpio_reader_b/LICENSE rename to applications/external/gpioreader_b/LICENSE diff --git a/applications/external/gpio_reader_b/application.fam b/applications/external/gpioreader_b/application.fam similarity index 100% rename from applications/external/gpio_reader_b/application.fam rename to applications/external/gpioreader_b/application.fam diff --git a/applications/external/gpio_reader_b/gpio_app.c b/applications/external/gpioreader_b/gpio_app.c similarity index 100% rename from applications/external/gpio_reader_b/gpio_app.c rename to applications/external/gpioreader_b/gpio_app.c diff --git a/applications/external/gpio_reader_b/gpio_app.h b/applications/external/gpioreader_b/gpio_app.h similarity index 100% rename from applications/external/gpio_reader_b/gpio_app.h rename to applications/external/gpioreader_b/gpio_app.h diff --git a/applications/external/gpio_reader_b/gpio_app_i.h b/applications/external/gpioreader_b/gpio_app_i.h similarity index 100% rename from applications/external/gpio_reader_b/gpio_app_i.h rename to applications/external/gpioreader_b/gpio_app_i.h diff --git a/applications/external/gpio_reader_b/gpio_custom_event.h b/applications/external/gpioreader_b/gpio_custom_event.h similarity index 100% rename from applications/external/gpio_reader_b/gpio_custom_event.h rename to applications/external/gpioreader_b/gpio_custom_event.h diff --git a/applications/external/gpio_reader_b/gpio_item.c b/applications/external/gpioreader_b/gpio_item.c similarity index 100% rename from applications/external/gpio_reader_b/gpio_item.c rename to applications/external/gpioreader_b/gpio_item.c diff --git a/applications/external/gpio_reader_b/gpio_item.h b/applications/external/gpioreader_b/gpio_item.h similarity index 100% rename from applications/external/gpio_reader_b/gpio_item.h rename to applications/external/gpioreader_b/gpio_item.h diff --git a/applications/external/gpio_reader_b/icon.png b/applications/external/gpioreader_b/icon.png similarity index 100% rename from applications/external/gpio_reader_b/icon.png rename to applications/external/gpioreader_b/icon.png diff --git a/applications/external/gpio_reader_b/scenes/gpio_scene.c b/applications/external/gpioreader_b/scenes/gpio_scene.c similarity index 100% rename from applications/external/gpio_reader_b/scenes/gpio_scene.c rename to applications/external/gpioreader_b/scenes/gpio_scene.c diff --git a/applications/external/gpio_reader_b/scenes/gpio_scene.h b/applications/external/gpioreader_b/scenes/gpio_scene.h similarity index 100% rename from applications/external/gpio_reader_b/scenes/gpio_scene.h rename to applications/external/gpioreader_b/scenes/gpio_scene.h diff --git a/applications/external/gpio_reader_b/scenes/gpio_scene_config.h b/applications/external/gpioreader_b/scenes/gpio_scene_config.h similarity index 100% rename from applications/external/gpio_reader_b/scenes/gpio_scene_config.h rename to applications/external/gpioreader_b/scenes/gpio_scene_config.h diff --git a/applications/external/gpio_reader_b/scenes/gpio_scene_reader.c b/applications/external/gpioreader_b/scenes/gpio_scene_reader.c similarity index 100% rename from applications/external/gpio_reader_b/scenes/gpio_scene_reader.c rename to applications/external/gpioreader_b/scenes/gpio_scene_reader.c diff --git a/applications/external/gpio_reader_b/scenes/gpio_scene_start.c b/applications/external/gpioreader_b/scenes/gpio_scene_start.c similarity index 100% rename from applications/external/gpio_reader_b/scenes/gpio_scene_start.c rename to applications/external/gpioreader_b/scenes/gpio_scene_start.c diff --git a/applications/external/gpio_reader_b/scenes/gpio_scene_test.c b/applications/external/gpioreader_b/scenes/gpio_scene_test.c similarity index 100% rename from applications/external/gpio_reader_b/scenes/gpio_scene_test.c rename to applications/external/gpioreader_b/scenes/gpio_scene_test.c diff --git a/applications/external/gpio_reader_b/scenes/gpio_scene_usb_uart.c b/applications/external/gpioreader_b/scenes/gpio_scene_usb_uart.c similarity index 100% rename from applications/external/gpio_reader_b/scenes/gpio_scene_usb_uart.c rename to applications/external/gpioreader_b/scenes/gpio_scene_usb_uart.c diff --git a/applications/external/gpio_reader_b/scenes/gpio_scene_usb_uart_close_rpc.c b/applications/external/gpioreader_b/scenes/gpio_scene_usb_uart_close_rpc.c similarity index 100% rename from applications/external/gpio_reader_b/scenes/gpio_scene_usb_uart_close_rpc.c rename to applications/external/gpioreader_b/scenes/gpio_scene_usb_uart_close_rpc.c diff --git a/applications/external/gpio_reader_b/scenes/gpio_scene_usb_uart_config.c b/applications/external/gpioreader_b/scenes/gpio_scene_usb_uart_config.c similarity index 100% rename from applications/external/gpio_reader_b/scenes/gpio_scene_usb_uart_config.c rename to applications/external/gpioreader_b/scenes/gpio_scene_usb_uart_config.c diff --git a/applications/external/gpio_reader_b/usb_uart_bridge.c b/applications/external/gpioreader_b/usb_uart_bridge.c similarity index 100% rename from applications/external/gpio_reader_b/usb_uart_bridge.c rename to applications/external/gpioreader_b/usb_uart_bridge.c diff --git a/applications/external/gpio_reader_b/usb_uart_bridge.h b/applications/external/gpioreader_b/usb_uart_bridge.h similarity index 100% rename from applications/external/gpio_reader_b/usb_uart_bridge.h rename to applications/external/gpioreader_b/usb_uart_bridge.h diff --git a/applications/external/gpio_reader_b/views/gpio_reader.c b/applications/external/gpioreader_b/views/gpio_reader.c similarity index 100% rename from applications/external/gpio_reader_b/views/gpio_reader.c rename to applications/external/gpioreader_b/views/gpio_reader.c diff --git a/applications/external/gpio_reader_b/views/gpio_reader.h b/applications/external/gpioreader_b/views/gpio_reader.h similarity index 100% rename from applications/external/gpio_reader_b/views/gpio_reader.h rename to applications/external/gpioreader_b/views/gpio_reader.h diff --git a/applications/external/gpio_reader_b/views/gpio_test.c b/applications/external/gpioreader_b/views/gpio_test.c similarity index 100% rename from applications/external/gpio_reader_b/views/gpio_test.c rename to applications/external/gpioreader_b/views/gpio_test.c diff --git a/applications/external/gpio_reader_b/views/gpio_test.h b/applications/external/gpioreader_b/views/gpio_test.h similarity index 100% rename from applications/external/gpio_reader_b/views/gpio_test.h rename to applications/external/gpioreader_b/views/gpio_test.h diff --git a/applications/external/gpio_reader_b/views/gpio_usb_uart.c b/applications/external/gpioreader_b/views/gpio_usb_uart.c similarity index 98% rename from applications/external/gpio_reader_b/views/gpio_usb_uart.c rename to applications/external/gpioreader_b/views/gpio_usb_uart.c index f71dcccab..14f8c12fe 100644 --- a/applications/external/gpio_reader_b/views/gpio_usb_uart.c +++ b/applications/external/gpioreader_b/views/gpio_usb_uart.c @@ -82,7 +82,7 @@ static void gpio_usb_uart_draw_callback(Canvas* canvas, void* _model) { if(model->rx_active) canvas_draw_icon_ex(canvas, 48, 34, &I_ArrowUpFilled_14x15, IconRotation180); else - canvas_draw_icon_ex(canvas, 48, 34, &I_ArrowUpFilled_14x15, IconRotation180); + canvas_draw_icon_ex(canvas, 48, 34, &I_ArrowUpEmpty_14x15, IconRotation180); } static bool gpio_usb_uart_input_callback(InputEvent* event, void* context) { diff --git a/applications/external/gpio_reader_b/views/gpio_usb_uart.h b/applications/external/gpioreader_b/views/gpio_usb_uart.h similarity index 100% rename from applications/external/gpio_reader_b/views/gpio_usb_uart.h rename to applications/external/gpioreader_b/views/gpio_usb_uart.h diff --git a/applications/external/gps_nmea_uart/gps.c b/applications/external/gps_nmea_uart/gps.c index 0f4a7a1d5..a0c6ead03 100644 --- a/applications/external/gps_nmea_uart/gps.c +++ b/applications/external/gps_nmea_uart/gps.c @@ -139,20 +139,14 @@ int32_t gps_app(void* p) { switch(event.input.key) { case InputKeyUp: gps_uart_deinit_thread(gps_uart); - switch(gps_uart->baudrate) { - case GPS_BAUDRATE_9k: - gps_uart->baudrate = GPS_BAUDRATE_57k; - break; - case GPS_BAUDRATE_57k: - gps_uart->baudrate = GPS_BAUDRATE_115k; - break; - case GPS_BAUDRATE_115k: - gps_uart->baudrate = GPS_BAUDRATE_9k; - break; - - default: - break; + const int baudrate_length = + sizeof(gps_baudrates) / sizeof(gps_baudrates[0]); + current_gps_baudrate++; + if(current_gps_baudrate >= baudrate_length) { + current_gps_baudrate = 0; } + gps_uart->baudrate = gps_baudrates[current_gps_baudrate]; + gps_uart_init_thread(gps_uart); gps_uart->changing_baudrate = true; view_port_update(view_port); diff --git a/applications/external/gps_nmea_uart/gps_uart.c b/applications/external/gps_nmea_uart/gps_uart.c index 39538b74b..4e66aa284 100644 --- a/applications/external/gps_nmea_uart/gps_uart.c +++ b/applications/external/gps_nmea_uart/gps_uart.c @@ -169,7 +169,7 @@ GpsUart* gps_uart_enable() { gps_uart->notifications = furi_record_open(RECORD_NOTIFICATION); - gps_uart->baudrate = GPS_BAUDRATE_57k; + gps_uart->baudrate = gps_baudrates[current_gps_baudrate]; gps_uart_init_thread(gps_uart); diff --git a/applications/external/gps_nmea_uart/gps_uart.h b/applications/external/gps_nmea_uart/gps_uart.h index 5a42b9c58..152f4cd7f 100644 --- a/applications/external/gps_nmea_uart/gps_uart.h +++ b/applications/external/gps_nmea_uart/gps_uart.h @@ -3,11 +3,11 @@ #include #include -#define GPS_BAUDRATE_9k 9600 -#define GPS_BAUDRATE_57k 57600 -#define GPS_BAUDRATE_115k 115200 #define RX_BUF_SIZE 1024 +static const int gps_baudrates[5] = {9600, 19200, 38400, 57600, 115200}; +static int current_gps_baudrate = 3; + typedef struct { bool valid; float latitude; diff --git a/applications/external/hex_viewer/hex_viewer.c b/applications/external/hex_viewer/hex_viewer.c index 50c34d634..bd4afa1a8 100644 --- a/applications/external/hex_viewer/hex_viewer.c +++ b/applications/external/hex_viewer/hex_viewer.c @@ -163,7 +163,7 @@ static bool hex_viewer_open_file(HexViewer* hex_viewer, const char* file_path) { FURI_LOG_E(TAG, "Unable to open stream: %s", file_path); isOk = false; break; - }; + } hex_viewer->model->file_size = stream_size(hex_viewer->model->stream); } while(false); diff --git a/applications/external/hid_app/assets/OutCircles.png b/applications/external/hid_app/assets/OutCircles.png new file mode 100644 index 000000000..f34d2687a Binary files /dev/null and b/applications/external/hid_app/assets/OutCircles.png differ diff --git a/applications/external/hid_app/assets/Pause_icon_9x9.png b/applications/external/hid_app/assets/Pause_icon_9x9.png new file mode 100644 index 000000000..fe16dc03e Binary files /dev/null and b/applications/external/hid_app/assets/Pause_icon_9x9.png differ diff --git a/applications/external/hid_app/assets/Pin_back_arrow_10x10.png b/applications/external/hid_app/assets/Pin_back_arrow_10x10.png new file mode 100644 index 000000000..e7510fd5d Binary files /dev/null and b/applications/external/hid_app/assets/Pin_back_arrow_10x10.png differ diff --git a/applications/external/hid_app/assets/Pressed_Button_19x19.png b/applications/external/hid_app/assets/Pressed_Button_19x19.png new file mode 100644 index 000000000..42a7dbdcc Binary files /dev/null and b/applications/external/hid_app/assets/Pressed_Button_19x19.png differ diff --git a/applications/external/hid_app/assets/S_DOWN.png b/applications/external/hid_app/assets/S_DOWN.png new file mode 100644 index 000000000..eac667aa0 Binary files /dev/null and b/applications/external/hid_app/assets/S_DOWN.png differ diff --git a/applications/external/hid_app/assets/S_LEFT.png b/applications/external/hid_app/assets/S_LEFT.png new file mode 100644 index 000000000..13c9f51b4 Binary files /dev/null and b/applications/external/hid_app/assets/S_LEFT.png differ diff --git a/applications/external/hid_app/assets/S_RIGHT.png b/applications/external/hid_app/assets/S_RIGHT.png new file mode 100644 index 000000000..e0ba2afd1 Binary files /dev/null and b/applications/external/hid_app/assets/S_RIGHT.png differ diff --git a/applications/external/hid_app/assets/S_UP.png b/applications/external/hid_app/assets/S_UP.png new file mode 100644 index 000000000..e55a03624 Binary files /dev/null and b/applications/external/hid_app/assets/S_UP.png differ diff --git a/applications/external/hid_app/assets/Voldwn_6x6.png b/applications/external/hid_app/assets/Voldwn_6x6.png index d7a82a2df..ce487b546 100644 Binary files a/applications/external/hid_app/assets/Voldwn_6x6.png and b/applications/external/hid_app/assets/Voldwn_6x6.png differ diff --git a/applications/external/hid_app/assets/Volup_8x6.png b/applications/external/hid_app/assets/Volup_8x6.png index 4b7ec66d6..ee9045f7f 100644 Binary files a/applications/external/hid_app/assets/Volup_8x6.png and b/applications/external/hid_app/assets/Volup_8x6.png differ diff --git a/applications/external/hid_app/hid.c b/applications/external/hid_app/hid.c index 949ff63b3..f6b853f9c 100644 --- a/applications/external/hid_app/hid.c +++ b/applications/external/hid_app/hid.c @@ -7,10 +7,13 @@ enum HidDebugSubmenuIndex { HidSubmenuIndexKeynote, + HidSubmenuIndexKeynoteVertical, HidSubmenuIndexKeyboard, HidSubmenuIndexMedia, HidSubmenuIndexTikTok, + HidSubmenuIndexYTShorts, HidSubmenuIndexMouse, + HidSubmenuIndexMouseClicker, HidSubmenuIndexMouseJiggler, }; @@ -20,6 +23,9 @@ static void hid_submenu_callback(void* context, uint32_t index) { if(index == HidSubmenuIndexKeynote) { app->view_id = HidViewKeynote; 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); } else if(index == HidSubmenuIndexKeyboard) { app->view_id = HidViewKeyboard; view_dispatcher_switch_to_view(app->view_dispatcher, HidViewKeyboard); @@ -32,6 +38,12 @@ static void hid_submenu_callback(void* context, uint32_t index) { } else if(index == HidSubmenuIndexTikTok) { app->view_id = BtHidViewTikTok; view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewTikTok); + } else if(index == HidSubmenuIndexYTShorts) { + app->view_id = BtHidViewYTShorts; + view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewYTShorts); + } else if(index == HidSubmenuIndexMouseClicker) { + app->view_id = HidViewMouseClicker; + view_dispatcher_switch_to_view(app->view_dispatcher, HidViewMouseClicker); } else if(index == HidSubmenuIndexMouseJiggler) { app->view_id = HidViewMouseJiggler; view_dispatcher_switch_to_view(app->view_dispatcher, HidViewMouseJiggler); @@ -50,11 +62,14 @@ 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_media_set_connected_status(hid->hid_media, connected); hid_mouse_set_connected_status(hid->hid_mouse, connected); + hid_mouse_clicker_set_connected_status(hid->hid_mouse_clicker, connected); hid_mouse_jiggler_set_connected_status(hid->hid_mouse_jiggler, connected); hid_tiktok_set_connected_status(hid->hid_tiktok, connected); + hid_ytshorts_set_connected_status(hid->hid_ytshorts, connected); } static void hid_dialog_callback(DialogExResult result, void* context) { @@ -100,6 +115,12 @@ Hid* hid_alloc(HidTransport transport) { app->device_type_submenu = submenu_alloc(); submenu_add_item( app->device_type_submenu, "Keynote", HidSubmenuIndexKeynote, hid_submenu_callback, app); + submenu_add_item( + app->device_type_submenu, + "Keynote Vertical", + HidSubmenuIndexKeynoteVertical, + hid_submenu_callback, + app); submenu_add_item( app->device_type_submenu, "Keyboard", HidSubmenuIndexKeyboard, hid_submenu_callback, app); submenu_add_item( @@ -113,7 +134,19 @@ Hid* hid_alloc(HidTransport transport) { HidSubmenuIndexTikTok, hid_submenu_callback, app); + submenu_add_item( + app->device_type_submenu, + "YT Shorts Controller", + HidSubmenuIndexYTShorts, + hid_submenu_callback, + app); } + submenu_add_item( + app->device_type_submenu, + "Mouse Clicker", + HidSubmenuIndexMouseClicker, + hid_submenu_callback, + app); submenu_add_item( app->device_type_submenu, "Mouse Jiggler", @@ -148,6 +181,15 @@ 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); @@ -166,12 +208,26 @@ Hid* hid_app_alloc_view(void* context) { view_dispatcher_add_view( app->view_dispatcher, BtHidViewTikTok, hid_tiktok_get_view(app->hid_tiktok)); + // YTShorts view + app->hid_ytshorts = hid_ytshorts_alloc(app); + view_set_previous_callback(hid_ytshorts_get_view(app->hid_ytshorts), hid_exit_confirm_view); + view_dispatcher_add_view( + app->view_dispatcher, BtHidViewYTShorts, hid_ytshorts_get_view(app->hid_ytshorts)); + // Mouse view app->hid_mouse = hid_mouse_alloc(app); view_set_previous_callback(hid_mouse_get_view(app->hid_mouse), hid_exit_confirm_view); view_dispatcher_add_view( app->view_dispatcher, HidViewMouse, hid_mouse_get_view(app->hid_mouse)); + // Mouse clicker view + app->hid_mouse_clicker = hid_mouse_clicker_alloc(app); + view_set_previous_callback( + hid_mouse_clicker_get_view(app->hid_mouse_clicker), hid_exit_confirm_view); + view_dispatcher_add_view( + app->view_dispatcher, + HidViewMouseClicker, + hid_mouse_clicker_get_view(app->hid_mouse_clicker)); // Mouse jiggler view app->hid_mouse_jiggler = hid_mouse_jiggler_alloc(app); view_set_previous_callback( @@ -199,16 +255,22 @@ 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, HidViewMedia); hid_media_free(app->hid_media); view_dispatcher_remove_view(app->view_dispatcher, HidViewMouse); hid_mouse_free(app->hid_mouse); + view_dispatcher_remove_view(app->view_dispatcher, HidViewMouseClicker); + hid_mouse_clicker_free(app->hid_mouse_clicker); view_dispatcher_remove_view(app->view_dispatcher, HidViewMouseJiggler); hid_mouse_jiggler_free(app->hid_mouse_jiggler); view_dispatcher_remove_view(app->view_dispatcher, BtHidViewTikTok); hid_tiktok_free(app->hid_tiktok); + view_dispatcher_remove_view(app->view_dispatcher, BtHidViewYTShorts); + hid_ytshorts_free(app->hid_ytshorts); view_dispatcher_free(app->view_dispatcher); // Close records diff --git a/applications/external/hid_app/hid.h b/applications/external/hid_app/hid.h index 8ed1664a3..6fe7d381c 100644 --- a/applications/external/hid_app/hid.h +++ b/applications/external/hid_app/hid.h @@ -17,11 +17,14 @@ #include #include #include "views/hid_keynote.h" +#include "views/hid_keynote_vertical.h" #include "views/hid_keyboard.h" #include "views/hid_media.h" #include "views/hid_mouse.h" #include "views/hid_mouse_jiggler.h" #include "views/hid_tiktok.h" +#include "views/hid_ytshorts.h" +#include "views/hid_mouse_clicker.h" #define HID_BT_KEYS_STORAGE_NAME ".bt_hid.keys" @@ -40,11 +43,14 @@ struct Hid { Submenu* device_type_submenu; DialogEx* dialog; HidKeynote* hid_keynote; + HidKeynoteVertical* hid_keynote_vertical; HidKeyboard* hid_keyboard; HidMedia* hid_media; HidMouse* hid_mouse; + HidMouseClicker* hid_mouse_clicker; HidMouseJiggler* hid_mouse_jiggler; HidTikTok* hid_tiktok; + HidYTShorts* hid_ytshorts; HidTransport transport; uint32_t view_id; @@ -62,4 +68,4 @@ void hid_hal_mouse_move(Hid* instance, int8_t dx, int8_t dy); void hid_hal_mouse_scroll(Hid* instance, int8_t delta); void hid_hal_mouse_press(Hid* instance, uint16_t event); void hid_hal_mouse_release(Hid* instance, uint16_t event); -void hid_hal_mouse_release_all(Hid* instance); \ No newline at end of file +void hid_hal_mouse_release_all(Hid* instance); diff --git a/applications/external/hid_app/views.h b/applications/external/hid_app/views.h index 2a44832e1..297fc7bc2 100644 --- a/applications/external/hid_app/views.h +++ b/applications/external/hid_app/views.h @@ -1,10 +1,13 @@ typedef enum { HidViewSubmenu, HidViewKeynote, + HidViewKeynoteVertical, HidViewKeyboard, HidViewMedia, HidViewMouse, + HidViewMouseClicker, HidViewMouseJiggler, BtHidViewTikTok, + BtHidViewYTShorts, HidViewExitConfirm, -} HidView; \ No newline at end of file +} HidView; diff --git a/applications/external/hid_app/views/hid_keynote_vertical.c b/applications/external/hid_app/views/hid_keynote_vertical.c new file mode 100644 index 000000000..7d2303813 --- /dev/null +++ b/applications/external/hid_app/views/hid_keynote_vertical.c @@ -0,0 +1,228 @@ +#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 new file mode 100644 index 000000000..bb7134adb --- /dev/null +++ b/applications/external/hid_app/views/hid_keynote_vertical.h @@ -0,0 +1,16 @@ +#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 468529d56..0028ac596 100644 --- a/applications/external/hid_app/views/hid_media.c +++ b/applications/external/hid_app/views/hid_media.c @@ -21,6 +21,7 @@ typedef struct { bool down_pressed; bool ok_pressed; bool connected; + bool back_pressed; HidTransport transport; } HidMediaModel; @@ -55,61 +56,72 @@ static void hid_media_draw_callback(Canvas* canvas, void* context) { canvas_set_font(canvas, FontSecondary); // Keypad circles - canvas_draw_icon(canvas, 76, 8, &I_Circles_47x47); + canvas_draw_icon(canvas, 58, 3, &I_OutCircles); // Up if(model->up_pressed) { canvas_set_bitmap_mode(canvas, 1); - canvas_draw_icon(canvas, 93, 9, &I_Pressed_Button_13x13); + canvas_draw_icon(canvas, 68, 6, &I_S_UP); canvas_set_bitmap_mode(canvas, 0); canvas_set_color(canvas, ColorWhite); } - canvas_draw_icon(canvas, 96, 12, &I_Volup_8x6); + canvas_draw_icon(canvas, 79, 9, &I_Volup_8x6); canvas_set_color(canvas, ColorBlack); // Down if(model->down_pressed) { canvas_set_bitmap_mode(canvas, 1); - canvas_draw_icon(canvas, 93, 41, &I_Pressed_Button_13x13); + canvas_draw_icon(canvas, 68, 36, &I_S_DOWN); canvas_set_bitmap_mode(canvas, 0); canvas_set_color(canvas, ColorWhite); } - canvas_draw_icon(canvas, 96, 45, &I_Voldwn_6x6); + canvas_draw_icon(canvas, 80, 41, &I_Voldwn_6x6); canvas_set_color(canvas, ColorBlack); // Left if(model->left_pressed) { canvas_set_bitmap_mode(canvas, 1); - canvas_draw_icon(canvas, 77, 25, &I_Pressed_Button_13x13); + canvas_draw_icon(canvas, 61, 13, &I_S_LEFT); canvas_set_bitmap_mode(canvas, 0); canvas_set_color(canvas, ColorWhite); } - hid_media_draw_arrow(canvas, 82, 31, CanvasDirectionRightToLeft); - hid_media_draw_arrow(canvas, 86, 31, CanvasDirectionRightToLeft); + hid_media_draw_arrow(canvas, 65, 28, CanvasDirectionRightToLeft); + hid_media_draw_arrow(canvas, 70, 28, CanvasDirectionRightToLeft); canvas_set_color(canvas, ColorBlack); // Right if(model->right_pressed) { canvas_set_bitmap_mode(canvas, 1); - canvas_draw_icon(canvas, 109, 25, &I_Pressed_Button_13x13); + canvas_draw_icon(canvas, 91, 13, &I_S_RIGHT); canvas_set_bitmap_mode(canvas, 0); canvas_set_color(canvas, ColorWhite); } - hid_media_draw_arrow(canvas, 112, 31, CanvasDirectionLeftToRight); - hid_media_draw_arrow(canvas, 116, 31, CanvasDirectionLeftToRight); + hid_media_draw_arrow(canvas, 96, 28, CanvasDirectionLeftToRight); + hid_media_draw_arrow(canvas, 101, 28, CanvasDirectionLeftToRight); canvas_set_color(canvas, ColorBlack); // Ok if(model->ok_pressed) { - canvas_draw_icon(canvas, 93, 25, &I_Pressed_Button_13x13); + canvas_set_bitmap_mode(canvas, 1); + canvas_draw_icon(canvas, 74, 19, &I_Pressed_Button_19x19); + canvas_set_bitmap_mode(canvas, 0); canvas_set_color(canvas, ColorWhite); } - hid_media_draw_arrow(canvas, 96, 31, CanvasDirectionLeftToRight); - canvas_draw_line(canvas, 100, 29, 100, 33); - canvas_draw_line(canvas, 102, 29, 102, 33); + hid_media_draw_arrow(canvas, 80, 28, CanvasDirectionLeftToRight); + canvas_draw_line(canvas, 84, 26, 84, 30); + canvas_draw_line(canvas, 86, 26, 86, 30); canvas_set_color(canvas, ColorBlack); // Exit + if(model->back_pressed) { + canvas_set_bitmap_mode(canvas, 1); + canvas_draw_icon(canvas, 107, 33, &I_Pressed_Button_19x19); + canvas_set_bitmap_mode(canvas, 0); + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_icon(canvas, 111, 38, &I_Pin_back_arrow_10x10); + canvas_set_color(canvas, ColorBlack); + canvas_draw_icon(canvas, 0, 54, &I_Pin_back_arrow_10x8); canvas_set_font(canvas, FontSecondary); elements_multiline_text_aligned(canvas, 13, 62, AlignLeft, AlignBottom, "Hold to exit"); @@ -135,6 +147,8 @@ static void hid_media_process_press(HidMedia* hid_media, InputEvent* event) { } else if(event->key == InputKeyOk) { model->ok_pressed = true; hid_hal_consumer_key_press(hid_media->hid, HID_CONSUMER_PLAY_PAUSE); + } else if(event->key == InputKeyBack) { + model->back_pressed = true; } }, true); @@ -160,6 +174,8 @@ static void hid_media_process_release(HidMedia* hid_media, InputEvent* event) { } else if(event->key == InputKeyOk) { model->ok_pressed = false; hid_hal_consumer_key_release(hid_media->hid, HID_CONSUMER_PLAY_PAUSE); + } else if(event->key == InputKeyBack) { + model->back_pressed = false; } }, true); @@ -176,12 +192,7 @@ static bool hid_media_input_callback(InputEvent* event, void* context) { } else if(event->type == InputTypeRelease) { hid_media_process_release(hid_media, event); consumed = true; - } else if(event->type == InputTypeShort) { - if(event->key == InputKeyBack) { - hid_hal_consumer_key_release_all(hid_media->hid); - } } - return consumed; } diff --git a/applications/external/hid_app/views/hid_mouse.c b/applications/external/hid_app/views/hid_mouse.c index 30a9d9d06..75df53dd1 100644 --- a/applications/external/hid_app/views/hid_mouse.c +++ b/applications/external/hid_app/views/hid_mouse.c @@ -49,61 +49,67 @@ static void hid_mouse_draw_callback(Canvas* canvas, void* context) { } // Keypad circles - canvas_draw_icon(canvas, 64, 8, &I_Circles_47x47); + canvas_draw_icon(canvas, 58, 3, &I_OutCircles); // Up if(model->up_pressed) { canvas_set_bitmap_mode(canvas, 1); - canvas_draw_icon(canvas, 81, 9, &I_Pressed_Button_13x13); + canvas_draw_icon(canvas, 68, 6, &I_S_UP); canvas_set_bitmap_mode(canvas, 0); canvas_set_color(canvas, ColorWhite); } - canvas_draw_icon(canvas, 84, 10, &I_Pin_arrow_up_7x9); + canvas_draw_icon(canvas, 80, 8, &I_Pin_arrow_up_7x9); canvas_set_color(canvas, ColorBlack); // Down if(model->down_pressed) { canvas_set_bitmap_mode(canvas, 1); - canvas_draw_icon(canvas, 81, 41, &I_Pressed_Button_13x13); + canvas_draw_icon(canvas, 68, 36, &I_S_DOWN); canvas_set_bitmap_mode(canvas, 0); canvas_set_color(canvas, ColorWhite); } - canvas_draw_icon(canvas, 84, 43, &I_Pin_arrow_down_7x9); + canvas_draw_icon(canvas, 80, 40, &I_Pin_arrow_down_7x9); canvas_set_color(canvas, ColorBlack); // Left if(model->left_pressed) { canvas_set_bitmap_mode(canvas, 1); - canvas_draw_icon(canvas, 65, 25, &I_Pressed_Button_13x13); + canvas_draw_icon(canvas, 61, 13, &I_S_LEFT); canvas_set_bitmap_mode(canvas, 0); canvas_set_color(canvas, ColorWhite); } - canvas_draw_icon(canvas, 67, 28, &I_Pin_arrow_left_9x7); + canvas_draw_icon(canvas, 63, 25, &I_Pin_arrow_left_9x7); canvas_set_color(canvas, ColorBlack); // Right if(model->right_pressed) { canvas_set_bitmap_mode(canvas, 1); - canvas_draw_icon(canvas, 97, 25, &I_Pressed_Button_13x13); + canvas_draw_icon(canvas, 91, 13, &I_S_RIGHT); canvas_set_bitmap_mode(canvas, 0); canvas_set_color(canvas, ColorWhite); } - canvas_draw_icon(canvas, 99, 28, &I_Pin_arrow_right_9x7); + canvas_draw_icon(canvas, 95, 25, &I_Pin_arrow_right_9x7); canvas_set_color(canvas, ColorBlack); // Ok if(model->left_mouse_pressed) { - canvas_draw_icon(canvas, 81, 25, &I_Ok_btn_pressed_13x13); - } else { - canvas_draw_icon(canvas, 83, 27, &I_Left_mouse_icon_9x9); + canvas_set_bitmap_mode(canvas, 1); + canvas_draw_icon(canvas, 74, 19, &I_Pressed_Button_19x19); + canvas_set_bitmap_mode(canvas, 0); + canvas_set_color(canvas, ColorWhite); } + canvas_draw_icon(canvas, 79, 24, &I_Left_mouse_icon_9x9); + canvas_set_color(canvas, ColorBlack); // Back if(model->right_mouse_pressed) { - canvas_draw_icon(canvas, 108, 48, &I_Ok_btn_pressed_13x13); - } else { - canvas_draw_icon(canvas, 110, 50, &I_Right_mouse_icon_9x9); + canvas_set_bitmap_mode(canvas, 1); + canvas_draw_icon(canvas, 107, 33, &I_Pressed_Button_19x19); + canvas_set_bitmap_mode(canvas, 0); + canvas_set_color(canvas, ColorWhite); } + canvas_draw_icon(canvas, 112, 38, &I_Right_mouse_icon_9x9); + canvas_set_color(canvas, ColorBlack); } static void hid_mouse_process(HidMouse* hid_mouse, InputEvent* event) { diff --git a/applications/external/hid_app/views/hid_mouse_clicker.c b/applications/external/hid_app/views/hid_mouse_clicker.c new file mode 100644 index 000000000..efaca190a --- /dev/null +++ b/applications/external/hid_app/views/hid_mouse_clicker.c @@ -0,0 +1,214 @@ +#include "hid_mouse_clicker.h" +#include +#include "../hid.h" + +#include "hid_icons.h" + +#define TAG "HidMouseClicker" +#define DEFAULT_CLICK_RATE 1 +#define MAXIMUM_CLICK_RATE 60 + +struct HidMouseClicker { + View* view; + Hid* hid; + FuriTimer* timer; +}; + +typedef struct { + bool connected; + bool running; + int rate; + HidTransport transport; +} HidMouseClickerModel; + +static void hid_mouse_clicker_start_or_restart_timer(void* context) { + furi_assert(context); + HidMouseClicker* hid_mouse_clicker = context; + + if(furi_timer_is_running(hid_mouse_clicker->timer)) { + furi_timer_stop(hid_mouse_clicker->timer); + } + + with_view_model( + hid_mouse_clicker->view, + HidMouseClickerModel * model, + { + furi_timer_start( + hid_mouse_clicker->timer, furi_kernel_get_tick_frequency() / model->rate); + }, + true); +} + +static void hid_mouse_clicker_draw_callback(Canvas* canvas, void* context) { + furi_assert(context); + HidMouseClickerModel* 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, "Mouse Clicker"); + + // Ok + canvas_draw_icon(canvas, 63, 25, &I_Space_65x18); + if(model->running) { + canvas_set_font(canvas, FontPrimary); + + FuriString* rate_label = furi_string_alloc(); + furi_string_printf(rate_label, "%d clicks/s\n\nUp / Down", model->rate); + elements_multiline_text(canvas, AlignLeft, 35, furi_string_get_cstr(rate_label)); + canvas_set_font(canvas, FontSecondary); + furi_string_free(rate_label); + + elements_slightly_rounded_box(canvas, 66, 27, 60, 13); + canvas_set_color(canvas, ColorWhite); + } else { + canvas_set_font(canvas, FontPrimary); + elements_multiline_text(canvas, AlignLeft, 35, "Press Start\nto start\nclicking"); + canvas_set_font(canvas, FontSecondary); + } + canvas_draw_icon(canvas, 74, 29, &I_Ok_btn_9x9); + if(model->running) { + elements_multiline_text_aligned(canvas, 91, 36, AlignLeft, AlignBottom, "Stop"); + } else { + elements_multiline_text_aligned(canvas, 91, 36, AlignLeft, AlignBottom, "Start"); + } + canvas_set_color(canvas, ColorBlack); + + // Back + canvas_draw_icon(canvas, 74, 49, &I_Pin_back_arrow_10x8); + elements_multiline_text_aligned(canvas, 91, 57, AlignLeft, AlignBottom, "Quit"); +} + +static void hid_mouse_clicker_timer_callback(void* context) { + furi_assert(context); + HidMouseClicker* hid_mouse_clicker = context; + with_view_model( + hid_mouse_clicker->view, + HidMouseClickerModel * model, + { + if(model->running) { + hid_hal_mouse_press(hid_mouse_clicker->hid, HID_MOUSE_BTN_LEFT); + hid_hal_mouse_release(hid_mouse_clicker->hid, HID_MOUSE_BTN_LEFT); + } + }, + false); +} + +static void hid_mouse_clicker_enter_callback(void* context) { + hid_mouse_clicker_start_or_restart_timer(context); +} + +static void hid_mouse_clicker_exit_callback(void* context) { + furi_assert(context); + HidMouseClicker* hid_mouse_clicker = context; + furi_timer_stop(hid_mouse_clicker->timer); +} + +static bool hid_mouse_clicker_input_callback(InputEvent* event, void* context) { + furi_assert(context); + HidMouseClicker* hid_mouse_clicker = context; + + bool consumed = false; + bool rate_changed = false; + + if(event->type != InputTypeRelease) { + return false; + } + + with_view_model( + hid_mouse_clicker->view, + HidMouseClickerModel * model, + { + switch(event->key) { + case InputKeyOk: + model->running = !model->running; + consumed = true; + break; + case InputKeyUp: + if(model->rate < MAXIMUM_CLICK_RATE) { + model->rate++; + } + rate_changed = true; + consumed = true; + break; + case InputKeyDown: + if(model->rate > 1) { + model->rate--; + } + rate_changed = true; + consumed = true; + break; + default: + consumed = true; + break; + } + }, + true); + + if(rate_changed) { + hid_mouse_clicker_start_or_restart_timer(context); + } + + return consumed; +} + +HidMouseClicker* hid_mouse_clicker_alloc(Hid* hid) { + HidMouseClicker* hid_mouse_clicker = malloc(sizeof(HidMouseClicker)); + + hid_mouse_clicker->view = view_alloc(); + view_set_context(hid_mouse_clicker->view, hid_mouse_clicker); + view_allocate_model( + hid_mouse_clicker->view, ViewModelTypeLocking, sizeof(HidMouseClickerModel)); + view_set_draw_callback(hid_mouse_clicker->view, hid_mouse_clicker_draw_callback); + view_set_input_callback(hid_mouse_clicker->view, hid_mouse_clicker_input_callback); + view_set_enter_callback(hid_mouse_clicker->view, hid_mouse_clicker_enter_callback); + view_set_exit_callback(hid_mouse_clicker->view, hid_mouse_clicker_exit_callback); + + hid_mouse_clicker->hid = hid; + + hid_mouse_clicker->timer = furi_timer_alloc( + hid_mouse_clicker_timer_callback, FuriTimerTypePeriodic, hid_mouse_clicker); + + with_view_model( + hid_mouse_clicker->view, + HidMouseClickerModel * model, + { + model->transport = hid->transport; + model->rate = DEFAULT_CLICK_RATE; + }, + true); + + return hid_mouse_clicker; +} + +void hid_mouse_clicker_free(HidMouseClicker* hid_mouse_clicker) { + furi_assert(hid_mouse_clicker); + + furi_timer_stop(hid_mouse_clicker->timer); + furi_timer_free(hid_mouse_clicker->timer); + + view_free(hid_mouse_clicker->view); + + free(hid_mouse_clicker); +} + +View* hid_mouse_clicker_get_view(HidMouseClicker* hid_mouse_clicker) { + furi_assert(hid_mouse_clicker); + return hid_mouse_clicker->view; +} + +void hid_mouse_clicker_set_connected_status(HidMouseClicker* hid_mouse_clicker, bool connected) { + furi_assert(hid_mouse_clicker); + with_view_model( + hid_mouse_clicker->view, + HidMouseClickerModel * model, + { model->connected = connected; }, + true); +} diff --git a/applications/external/hid_app/views/hid_mouse_clicker.h b/applications/external/hid_app/views/hid_mouse_clicker.h new file mode 100644 index 000000000..d72847baa --- /dev/null +++ b/applications/external/hid_app/views/hid_mouse_clicker.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +typedef struct Hid Hid; +typedef struct HidMouseClicker HidMouseClicker; + +HidMouseClicker* hid_mouse_clicker_alloc(Hid* bt_hid); + +void hid_mouse_clicker_free(HidMouseClicker* hid_mouse_clicker); + +View* hid_mouse_clicker_get_view(HidMouseClicker* hid_mouse_clicker); + +void hid_mouse_clicker_set_connected_status(HidMouseClicker* hid_mouse_clicker, bool connected); diff --git a/applications/external/hid_app/views/hid_tiktok.c b/applications/external/hid_app/views/hid_tiktok.c index e1f9f4bed..4dfbde4eb 100644 --- a/applications/external/hid_app/views/hid_tiktok.c +++ b/applications/external/hid_app/views/hid_tiktok.c @@ -19,6 +19,7 @@ typedef struct { bool ok_pressed; bool connected; bool is_cursor_set; + bool back_mouse_pressed; HidTransport transport; } HidTikTokModel; @@ -40,54 +41,68 @@ static void hid_tiktok_draw_callback(Canvas* canvas, void* context) { canvas_set_font(canvas, FontSecondary); // Keypad circles - canvas_draw_icon(canvas, 76, 8, &I_Circles_47x47); + canvas_draw_icon(canvas, 58, 3, &I_OutCircles); + + // Pause + if(model->back_mouse_pressed) { + canvas_set_bitmap_mode(canvas, 1); + canvas_draw_icon(canvas, 107, 33, &I_Pressed_Button_19x19); + canvas_set_bitmap_mode(canvas, 0); + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_icon(canvas, 113, 37, &I_Pause_icon_9x9); + canvas_set_color(canvas, ColorBlack); // Up if(model->up_pressed) { canvas_set_bitmap_mode(canvas, 1); - canvas_draw_icon(canvas, 93, 9, &I_Pressed_Button_13x13); + canvas_draw_icon(canvas, 68, 6, &I_S_UP); canvas_set_bitmap_mode(canvas, 0); canvas_set_color(canvas, ColorWhite); } - canvas_draw_icon(canvas, 96, 11, &I_Arr_up_7x9); + canvas_draw_icon(canvas, 80, 8, &I_Arr_up_7x9); canvas_set_color(canvas, ColorBlack); // Down if(model->down_pressed) { canvas_set_bitmap_mode(canvas, 1); - canvas_draw_icon(canvas, 93, 41, &I_Pressed_Button_13x13); + canvas_draw_icon(canvas, 68, 36, &I_S_DOWN); canvas_set_bitmap_mode(canvas, 0); canvas_set_color(canvas, ColorWhite); } - canvas_draw_icon(canvas, 96, 44, &I_Arr_dwn_7x9); + canvas_draw_icon(canvas, 80, 40, &I_Arr_dwn_7x9); canvas_set_color(canvas, ColorBlack); // Left if(model->left_pressed) { canvas_set_bitmap_mode(canvas, 1); - canvas_draw_icon(canvas, 77, 25, &I_Pressed_Button_13x13); + canvas_draw_icon(canvas, 61, 13, &I_S_LEFT); canvas_set_bitmap_mode(canvas, 0); canvas_set_color(canvas, ColorWhite); } - canvas_draw_icon(canvas, 81, 29, &I_Voldwn_6x6); + canvas_draw_icon(canvas, 64, 25, &I_Voldwn_6x6); canvas_set_color(canvas, ColorBlack); // Right if(model->right_pressed) { canvas_set_bitmap_mode(canvas, 1); - canvas_draw_icon(canvas, 109, 25, &I_Pressed_Button_13x13); + canvas_draw_icon(canvas, 91, 13, &I_S_RIGHT); canvas_set_bitmap_mode(canvas, 0); canvas_set_color(canvas, ColorWhite); } - canvas_draw_icon(canvas, 111, 29, &I_Volup_8x6); + canvas_draw_icon(canvas, 95, 25, &I_Volup_8x6); canvas_set_color(canvas, ColorBlack); // Ok if(model->ok_pressed) { - canvas_draw_icon(canvas, 91, 23, &I_Like_pressed_17x17); - } else { - canvas_draw_icon(canvas, 94, 27, &I_Like_def_11x9); + canvas_set_bitmap_mode(canvas, 1); + canvas_draw_icon(canvas, 74, 19, &I_Pressed_Button_19x19); + canvas_set_bitmap_mode(canvas, 0); + canvas_set_color(canvas, ColorWhite); } + canvas_draw_icon(canvas, 78, 25, &I_Like_def_11x9); + canvas_set_color(canvas, ColorBlack); + // Exit canvas_draw_icon(canvas, 0, 54, &I_Pin_back_arrow_10x8); canvas_set_font(canvas, FontSecondary); @@ -102,7 +117,8 @@ static void hid_tiktok_reset_cursor(HidTikTok* hid_tiktok) { furi_delay_ms(50); } // Move cursor from the corner - hid_hal_mouse_move(hid_tiktok->hid, 20, 120); + hid_hal_mouse_move(hid_tiktok->hid, 40, 120); + hid_hal_mouse_move(hid_tiktok->hid, 0, 120); furi_delay_ms(50); } @@ -120,6 +136,8 @@ static void hid_hal_consumer_key_press(hid_tiktok->hid, HID_CONSUMER_VOLUME_INCREMENT); } else if(event->key == InputKeyOk) { model->ok_pressed = true; + } else if(event->key == InputKeyBack) { + model->back_mouse_pressed = true; } } @@ -137,6 +155,8 @@ static void hid_hal_consumer_key_release(hid_tiktok->hid, HID_CONSUMER_VOLUME_INCREMENT); } else if(event->key == InputKeyOk) { model->ok_pressed = false; + } else if(event->key == InputKeyBack) { + model->back_mouse_pressed = false; } } @@ -162,32 +182,27 @@ static bool hid_tiktok_input_callback(InputEvent* event, void* context) { } else if(event->type == InputTypeShort) { if(event->key == InputKeyOk) { hid_hal_mouse_press(hid_tiktok->hid, HID_MOUSE_BTN_LEFT); - furi_delay_ms(50); + furi_delay_ms(25); hid_hal_mouse_release(hid_tiktok->hid, HID_MOUSE_BTN_LEFT); - furi_delay_ms(50); + furi_delay_ms(100); + hid_hal_mouse_press(hid_tiktok->hid, HID_MOUSE_BTN_LEFT); + furi_delay_ms(25); + hid_hal_mouse_release(hid_tiktok->hid, HID_MOUSE_BTN_LEFT); + consumed = true; + } else if(event->key == InputKeyDown) { + // Swipe to previous video + hid_hal_mouse_scroll(hid_tiktok->hid, 19); + consumed = true; + } else if(event->key == InputKeyUp) { + // Swipe to new video + hid_hal_mouse_scroll(hid_tiktok->hid, -19); + consumed = true; + } else if(event->key == InputKeyBack) { + // Pause hid_hal_mouse_press(hid_tiktok->hid, HID_MOUSE_BTN_LEFT); furi_delay_ms(50); hid_hal_mouse_release(hid_tiktok->hid, HID_MOUSE_BTN_LEFT); consumed = true; - } else if(event->key == InputKeyUp) { - // Emulate up swipe - hid_hal_mouse_scroll(hid_tiktok->hid, -6); - hid_hal_mouse_scroll(hid_tiktok->hid, -12); - hid_hal_mouse_scroll(hid_tiktok->hid, -19); - hid_hal_mouse_scroll(hid_tiktok->hid, -12); - hid_hal_mouse_scroll(hid_tiktok->hid, -6); - consumed = true; - } else if(event->key == InputKeyDown) { - // Emulate down swipe - hid_hal_mouse_scroll(hid_tiktok->hid, 6); - hid_hal_mouse_scroll(hid_tiktok->hid, 12); - hid_hal_mouse_scroll(hid_tiktok->hid, 19); - hid_hal_mouse_scroll(hid_tiktok->hid, 12); - hid_hal_mouse_scroll(hid_tiktok->hid, 6); - consumed = true; - } else if(event->key == InputKeyBack) { - hid_hal_consumer_key_release_all(hid_tiktok->hid); - consumed = true; } } else if(event->type == InputTypeLong) { if(event->key == InputKeyBack) { diff --git a/applications/external/hid_app/views/hid_ytshorts.c b/applications/external/hid_app/views/hid_ytshorts.c new file mode 100644 index 000000000..359091640 --- /dev/null +++ b/applications/external/hid_app/views/hid_ytshorts.c @@ -0,0 +1,272 @@ +#include "hid_ytshorts.h" +#include "../hid.h" +#include + +#include "hid_icons.h" + +#define TAG "HidYTShorts" + +struct HidYTShorts { + View* view; + Hid* hid; +}; + +typedef struct { + bool left_pressed; + bool up_pressed; + bool right_pressed; + bool down_pressed; + bool ok_pressed; + bool connected; + bool is_cursor_set; + bool back_mouse_pressed; + HidTransport transport; +} HidYTShortsModel; + +static void hid_ytshorts_draw_callback(Canvas* canvas, void* context) { + furi_assert(context); + HidYTShortsModel* 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, "Shorts"); + canvas_set_font(canvas, FontSecondary); + + // Keypad circles + canvas_draw_icon(canvas, 58, 3, &I_OutCircles); + + // Pause + if(model->back_mouse_pressed) { + canvas_set_bitmap_mode(canvas, 1); + canvas_draw_icon(canvas, 107, 33, &I_Pressed_Button_19x19); + canvas_set_bitmap_mode(canvas, 0); + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_icon(canvas, 113, 37, &I_Pause_icon_9x9); + canvas_set_color(canvas, ColorBlack); + + // Up + if(model->up_pressed) { + canvas_set_bitmap_mode(canvas, 1); + canvas_draw_icon(canvas, 68, 6, &I_S_UP); + canvas_set_bitmap_mode(canvas, 0); + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_icon(canvas, 80, 8, &I_Arr_up_7x9); + canvas_set_color(canvas, ColorBlack); + + // Down + if(model->down_pressed) { + canvas_set_bitmap_mode(canvas, 1); + canvas_draw_icon(canvas, 68, 36, &I_S_DOWN); + canvas_set_bitmap_mode(canvas, 0); + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_icon(canvas, 80, 40, &I_Arr_dwn_7x9); + canvas_set_color(canvas, ColorBlack); + + // Left + if(model->left_pressed) { + canvas_set_bitmap_mode(canvas, 1); + canvas_draw_icon(canvas, 61, 13, &I_S_LEFT); + canvas_set_bitmap_mode(canvas, 0); + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_icon(canvas, 64, 25, &I_Voldwn_6x6); + canvas_set_color(canvas, ColorBlack); + + // Right + if(model->right_pressed) { + canvas_set_bitmap_mode(canvas, 1); + canvas_draw_icon(canvas, 91, 13, &I_S_RIGHT); + canvas_set_bitmap_mode(canvas, 0); + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_icon(canvas, 95, 25, &I_Volup_8x6); + canvas_set_color(canvas, ColorBlack); + + // Ok + if(model->ok_pressed) { + canvas_set_bitmap_mode(canvas, 1); + canvas_draw_icon(canvas, 74, 19, &I_Pressed_Button_19x19); + canvas_set_bitmap_mode(canvas, 0); + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_icon(canvas, 78, 25, &I_Like_def_11x9); + canvas_set_color(canvas, ColorBlack); + + // Exit + canvas_draw_icon(canvas, 0, 54, &I_Pin_back_arrow_10x8); + canvas_set_font(canvas, FontSecondary); + elements_multiline_text_aligned(canvas, 13, 62, AlignLeft, AlignBottom, "Hold to exit"); +} + +static void hid_ytshorts_reset_cursor(HidYTShorts* hid_ytshorts) { + // Set cursor to the phone's left up corner + // Delays to guarantee one packet per connection interval + for(size_t i = 0; i < 8; i++) { + hid_hal_mouse_move(hid_ytshorts->hid, -127, -127); + furi_delay_ms(50); + } + // Move cursor from the corner + hid_hal_mouse_move(hid_ytshorts->hid, 40, 120); + hid_hal_mouse_move(hid_ytshorts->hid, 0, 120); + furi_delay_ms(50); +} + +static void hid_ytshorts_process_press( + HidYTShorts* hid_ytshorts, + HidYTShortsModel* model, + InputEvent* event) { + if(event->key == InputKeyUp) { + model->up_pressed = true; + } else if(event->key == InputKeyDown) { + model->down_pressed = true; + } else if(event->key == InputKeyLeft) { + model->left_pressed = true; + hid_hal_consumer_key_press(hid_ytshorts->hid, HID_CONSUMER_VOLUME_DECREMENT); + } else if(event->key == InputKeyRight) { + model->right_pressed = true; + hid_hal_consumer_key_press(hid_ytshorts->hid, HID_CONSUMER_VOLUME_INCREMENT); + } else if(event->key == InputKeyOk) { + model->ok_pressed = true; + } else if(event->key == InputKeyBack) { + model->back_mouse_pressed = true; + } +} + +static void hid_ytshorts_process_release( + HidYTShorts* hid_ytshorts, + HidYTShortsModel* model, + InputEvent* event) { + if(event->key == InputKeyUp) { + model->up_pressed = false; + } else if(event->key == InputKeyDown) { + model->down_pressed = false; + } else if(event->key == InputKeyLeft) { + model->left_pressed = false; + hid_hal_consumer_key_release(hid_ytshorts->hid, HID_CONSUMER_VOLUME_DECREMENT); + } else if(event->key == InputKeyRight) { + model->right_pressed = false; + hid_hal_consumer_key_release(hid_ytshorts->hid, HID_CONSUMER_VOLUME_INCREMENT); + } else if(event->key == InputKeyOk) { + model->ok_pressed = false; + } else if(event->key == InputKeyBack) { + model->back_mouse_pressed = false; + } +} + +static bool hid_ytshorts_input_callback(InputEvent* event, void* context) { + furi_assert(context); + HidYTShorts* hid_ytshorts = context; + bool consumed = false; + + with_view_model( + hid_ytshorts->view, + HidYTShortsModel * model, + { + if(event->type == InputTypePress) { + hid_ytshorts_process_press(hid_ytshorts, model, event); + if(model->connected && !model->is_cursor_set) { + hid_ytshorts_reset_cursor(hid_ytshorts); + model->is_cursor_set = true; + } + consumed = true; + } else if(event->type == InputTypeRelease) { + hid_ytshorts_process_release(hid_ytshorts, model, event); + consumed = true; + } else if(event->type == InputTypeShort) { + if(event->key == InputKeyOk) { + hid_hal_mouse_press(hid_ytshorts->hid, HID_MOUSE_BTN_LEFT); + furi_delay_ms(50); + hid_hal_mouse_release(hid_ytshorts->hid, HID_MOUSE_BTN_LEFT); + furi_delay_ms(50); + hid_hal_mouse_press(hid_ytshorts->hid, HID_MOUSE_BTN_LEFT); + furi_delay_ms(50); + hid_hal_mouse_release(hid_ytshorts->hid, HID_MOUSE_BTN_LEFT); + consumed = true; + } else if(event->key == InputKeyDown) { + // Swipe to new video + hid_hal_mouse_scroll(hid_ytshorts->hid, 6); + hid_hal_mouse_scroll(hid_ytshorts->hid, 8); + hid_hal_mouse_scroll(hid_ytshorts->hid, 10); + hid_hal_mouse_scroll(hid_ytshorts->hid, 8); + hid_hal_mouse_scroll(hid_ytshorts->hid, 6); + consumed = true; + } else if(event->key == InputKeyUp) { + // Swipe to previous video + hid_hal_mouse_scroll(hid_ytshorts->hid, -6); + hid_hal_mouse_scroll(hid_ytshorts->hid, -8); + hid_hal_mouse_scroll(hid_ytshorts->hid, -10); + hid_hal_mouse_scroll(hid_ytshorts->hid, -8); + hid_hal_mouse_scroll(hid_ytshorts->hid, -6); + consumed = true; + } else if(event->key == InputKeyBack) { + // Pause + hid_hal_mouse_press(hid_ytshorts->hid, HID_MOUSE_BTN_LEFT); + furi_delay_ms(50); + hid_hal_mouse_release(hid_ytshorts->hid, HID_MOUSE_BTN_LEFT); + furi_delay_ms(50); + consumed = true; + } + } else if(event->type == InputTypeLong) { + if(event->key == InputKeyBack) { + hid_hal_consumer_key_release_all(hid_ytshorts->hid); + model->is_cursor_set = false; + consumed = false; + } + } + }, + true); + + return consumed; +} + +HidYTShorts* hid_ytshorts_alloc(Hid* bt_hid) { + HidYTShorts* hid_ytshorts = malloc(sizeof(HidYTShorts)); + hid_ytshorts->hid = bt_hid; + hid_ytshorts->view = view_alloc(); + view_set_context(hid_ytshorts->view, hid_ytshorts); + view_allocate_model(hid_ytshorts->view, ViewModelTypeLocking, sizeof(HidYTShortsModel)); + view_set_draw_callback(hid_ytshorts->view, hid_ytshorts_draw_callback); + view_set_input_callback(hid_ytshorts->view, hid_ytshorts_input_callback); + + with_view_model( + hid_ytshorts->view, + HidYTShortsModel * model, + { model->transport = bt_hid->transport; }, + true); + + return hid_ytshorts; +} + +void hid_ytshorts_free(HidYTShorts* hid_ytshorts) { + furi_assert(hid_ytshorts); + view_free(hid_ytshorts->view); + free(hid_ytshorts); +} + +View* hid_ytshorts_get_view(HidYTShorts* hid_ytshorts) { + furi_assert(hid_ytshorts); + return hid_ytshorts->view; +} + +void hid_ytshorts_set_connected_status(HidYTShorts* hid_ytshorts, bool connected) { + furi_assert(hid_ytshorts); + with_view_model( + hid_ytshorts->view, + HidYTShortsModel * model, + { + model->connected = connected; + model->is_cursor_set = false; + }, + true); +} diff --git a/applications/external/hid_app/views/hid_ytshorts.h b/applications/external/hid_app/views/hid_ytshorts.h new file mode 100644 index 000000000..03264dd36 --- /dev/null +++ b/applications/external/hid_app/views/hid_ytshorts.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +typedef struct Hid Hid; +typedef struct HidYTShorts HidYTShorts; + +HidYTShorts* hid_ytshorts_alloc(Hid* bt_hid); + +void hid_ytshorts_free(HidYTShorts* hid_ytshorts); + +View* hid_ytshorts_get_view(HidYTShorts* hid_ytshorts); + +void hid_ytshorts_set_connected_status(HidYTShorts* hid_ytshorts, bool connected); diff --git a/applications/external/ibtn_fuzzer/ibtnfuzzer.c b/applications/external/ibtn_fuzzer/ibtnfuzzer.c index 04d18886f..9e6239b69 100644 --- a/applications/external/ibtn_fuzzer/ibtnfuzzer.c +++ b/applications/external/ibtn_fuzzer/ibtnfuzzer.c @@ -59,6 +59,14 @@ iBtnFuzzerState* ibtnfuzzer_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; @@ -105,8 +113,13 @@ void ibtnfuzzer_free(iBtnFuzzerState* ibtnfuzzer) { furi_string_free(ibtnfuzzer->proto_name); furi_string_free(ibtnfuzzer->data_str); - free(ibtnfuzzer->data); - free(ibtnfuzzer->payload); + 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); diff --git a/applications/external/ibtn_fuzzer/ibtnfuzzer.h b/applications/external/ibtn_fuzzer/ibtnfuzzer.h index ed42cc541..3a3a1d21f 100644 --- a/applications/external/ibtn_fuzzer/ibtnfuzzer.h +++ b/applications/external/ibtn_fuzzer/ibtnfuzzer.h @@ -73,6 +73,8 @@ typedef struct { iBtnFuzzerProtos proto; FuriString* attack_name; FuriString* proto_name; + FuriString* main_menu_items[3]; + FuriString* main_menu_proto_items[3]; DialogsApp* dialogs; FuriString* notification_msg; diff --git a/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_entrypoint.c b/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_entrypoint.c index 3ea7e49e6..1dd239c3b 100644 --- a/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_entrypoint.c +++ b/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_entrypoint.c @@ -1,8 +1,5 @@ #include "ibtnfuzzer_scene_entrypoint.h" -FuriString* main_menu_items[3]; -FuriString* main_menu_proto_items[3]; - void ibtnfuzzer_scene_entrypoint_menu_callback( iBtnFuzzerState* context, uint32_t index, @@ -61,30 +58,14 @@ void ibtnfuzzer_scene_entrypoint_on_enter(iBtnFuzzerState* context) { menu_items[i] = furi_string_alloc(); }*/ - main_menu_items[0] = furi_string_alloc_set("Default Values"); - main_menu_items[1] = furi_string_alloc_set("Load File"); - main_menu_items[2] = furi_string_alloc_set("Load UIDs from file"); - context->menu_proto_index = 0; /*for(uint32_t i = 0; i < 4; i++) { menu_proto_items[i] = furi_string_alloc(); }*/ - - main_menu_proto_items[0] = furi_string_alloc_set("DS1990"); - main_menu_proto_items[1] = furi_string_alloc_set("Metakom"); - main_menu_proto_items[2] = furi_string_alloc_set("Cyfral"); } void ibtnfuzzer_scene_entrypoint_on_exit(iBtnFuzzerState* context) { context->enter_rerun = false; - - for(uint32_t i = 0; i < 3; i++) { - furi_string_free(main_menu_items[i]); - } - - for(uint32_t i = 0; i < 3; i++) { - furi_string_free(main_menu_proto_items[i]); - } } void ibtnfuzzer_scene_entrypoint_on_tick(iBtnFuzzerState* context) { @@ -142,74 +123,79 @@ void ibtnfuzzer_scene_entrypoint_on_draw(Canvas* canvas, iBtnFuzzerState* contex canvas_clear(canvas); canvas_set_color(canvas, ColorBlack); - if(main_menu_items[context->menu_index] != NULL) { - if(context->menu_index > iBtnFuzzerAttackDefaultValues) { - canvas_set_font(canvas, FontSecondary); + 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, - 24, + 36, AlignCenter, AlignTop, - furi_string_get_cstr(main_menu_items[context->menu_index - 1])); - } + furi_string_get_cstr(context->main_menu_items[context->menu_index])); - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned( - canvas, - 64, - 36, - AlignCenter, - AlignTop, - furi_string_get_cstr(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_index < iBtnFuzzerAttackLoadFileCustomUids) { - canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned( - canvas, - 64, - 48, - AlignCenter, - AlignTop, - furi_string_get_cstr(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])); + } - if(context->menu_proto_index > DS1990) { - canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned( - canvas, - 64, - -12, - AlignCenter, - AlignTop, - furi_string_get_cstr(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, 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, ">"); - canvas_set_font(canvas, FontPrimary); - if(main_menu_proto_items[context->menu_proto_index] != NULL) { - canvas_draw_str_aligned( - canvas, - 64, - 4, - AlignCenter, - AlignTop, - furi_string_get_cstr(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(main_menu_proto_items[context->menu_proto_index + 1])); + 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_run_attack.c b/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.c index 1cab8b04e..53f2f694f 100644 --- a/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.c +++ b/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.c @@ -3,7 +3,7 @@ uint8_t counter = 0; -uint8_t id_list_ds1990[25][8] = { +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}, //- ΠΏΡ€ΠΎΠ²Π΅Ρ€Π΅Π½ Ρ€Π°Π±ΠΎΡ‚Π°Π΅Ρ‚ @@ -19,16 +19,9 @@ uint8_t id_list_ds1990[25][8] = { {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, 0xFF}, // Only FF - {0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11}, // Only 11 - {0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22}, // Only 22 - {0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33}, // Only 33 - {0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44}, // Only 44 - {0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55}, // Only 55 - {0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66}, // Only 66 - {0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77}, // Only 77 - {0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88}, // Only 88 - {0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99}, // Only 99 + {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] = { @@ -51,7 +44,7 @@ uint8_t id_list_metakom[17][4] = { {0xCA, 0xCA, 0xCA, 0xCA}, // ?? }; -uint8_t id_list_cyfral[14][2] = { +uint8_t id_list_cyfral[16][2] = { {0x00, 0x00}, // Null bytes {0xFF, 0xFF}, // Only FF {0x11, 0x11}, // Only 11 @@ -66,6 +59,8 @@ uint8_t id_list_cyfral[14][2] = { {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) { @@ -130,7 +125,7 @@ void ibtnfuzzer_scene_run_attack_on_tick(iBtnFuzzerState* context) { context->payload[6] = id_list_ds1990[context->attack_step][6]; context->payload[7] = id_list_ds1990[context->attack_step][7]; - if(context->attack_step == 24) { + if(context->attack_step == 17) { context->attack_step = 0; counter = 0; context->is_attacking = false; @@ -160,7 +155,7 @@ void ibtnfuzzer_scene_run_attack_on_tick(iBtnFuzzerState* context) { context->payload[0] = id_list_cyfral[context->attack_step][0]; context->payload[1] = id_list_cyfral[context->attack_step][1]; - if(context->attack_step == 13) { + if(context->attack_step == 15) { context->attack_step = 0; counter = 0; context->is_attacking = false; @@ -247,7 +242,7 @@ void ibtnfuzzer_scene_run_attack_on_tick(iBtnFuzzerState* context) { 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; @@ -261,7 +256,7 @@ void ibtnfuzzer_scene_run_attack_on_tick(iBtnFuzzerState* context) { 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++) { @@ -285,7 +280,7 @@ void ibtnfuzzer_scene_run_attack_on_tick(iBtnFuzzerState* context) { 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; @@ -299,7 +294,7 @@ void ibtnfuzzer_scene_run_attack_on_tick(iBtnFuzzerState* context) { 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++) { @@ -323,7 +318,7 @@ void ibtnfuzzer_scene_run_attack_on_tick(iBtnFuzzerState* context) { 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; @@ -337,7 +332,7 @@ void ibtnfuzzer_scene_run_attack_on_tick(iBtnFuzzerState* context) { 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++) { diff --git a/applications/external/ifttt/ifttt_virtual_button.c b/applications/external/ifttt/ifttt_virtual_button.c index e23b8715d..1d2d06416 100644 --- a/applications/external/ifttt/ifttt_virtual_button.c +++ b/applications/external/ifttt/ifttt_virtual_button.c @@ -38,6 +38,11 @@ void save_settings_file(FlipperFormat* file, Settings* settings) { Settings* load_settings() { Settings* settings = malloc(sizeof(Settings)); + settings->save_ssid = ""; + settings->save_password = ""; + settings->save_key = ""; + settings->save_event = ""; + Storage* storage = furi_record_open(RECORD_STORAGE); FlipperFormat* file = flipper_format_file_alloc(storage); @@ -53,29 +58,14 @@ Settings* load_settings() { text_event_value = furi_string_alloc(); if(storage_common_stat(storage, CONFIG_FILE_PATH, NULL) != FSE_OK) { - if(!flipper_format_file_open_new(file, CONFIG_FILE_PATH)) { - flipper_format_file_close(file); - } else { - settings->save_ssid = malloc(1); - settings->save_password = malloc(1); - settings->save_key = malloc(1); - settings->save_event = malloc(1); - - settings->save_ssid[0] = '\0'; - settings->save_password[0] = '\0'; - settings->save_key[0] = '\0'; - settings->save_event[0] = '\0'; - + if(flipper_format_file_open_new(file, CONFIG_FILE_PATH)) { save_settings_file(file, settings); - flipper_format_file_close(file); } + flipper_format_file_close(file); } else { - if(!flipper_format_file_open_existing(file, CONFIG_FILE_PATH)) { - flipper_format_file_close(file); - } else { + if(flipper_format_file_open_existing(file, CONFIG_FILE_PATH)) { uint32_t value; - if(!flipper_format_read_header(file, string_value, &value)) { - } else { + if(flipper_format_read_header(file, string_value, &value)) { if(flipper_format_read_string(file, CONF_SSID, text_ssid_value)) { settings->save_ssid = malloc(furi_string_size(text_ssid_value) + 1); strcpy(settings->save_ssid, furi_string_get_cstr(text_ssid_value)); @@ -93,8 +83,8 @@ Settings* load_settings() { strcpy(settings->save_event, furi_string_get_cstr(text_event_value)); } } - flipper_format_file_close(file); } + flipper_format_file_close(file); } furi_string_free(text_ssid_value); @@ -139,7 +129,7 @@ void send_serial_command_config(ESerialCommand command, Settings* settings) { break; default: return; - }; + } furi_hal_uart_tx(FuriHalUartIdUSART1, data, 1); } @@ -248,4 +238,4 @@ int32_t ifttt_virtual_button_app(void* p) { view_dispatcher_run(app->view_dispatcher); ifttt_virtual_button_app_free(app); return 0; -} \ No newline at end of file +} diff --git a/applications/external/ifttt/views/send_view.c b/applications/external/ifttt/views/send_view.c index 6046c39e3..e1638e7a7 100644 --- a/applications/external/ifttt/views/send_view.c +++ b/applications/external/ifttt/views/send_view.c @@ -38,7 +38,7 @@ void send_serial_command_send(ESerialCommand command) { break; default: return; - }; + } furi_hal_uart_tx(FuriHalUartIdUSART1, data, 1); } diff --git a/applications/external/metronome/img/screenshot.png b/applications/external/metronome/img/screenshot.png deleted file mode 100644 index 7b6916e81..000000000 Binary files a/applications/external/metronome/img/screenshot.png and /dev/null differ diff --git a/applications/external/metronome/metronome.c b/applications/external/metronome/metronome.c index a01f4418d..46231d66d 100644 --- a/applications/external/metronome/metronome.c +++ b/applications/external/metronome/metronome.c @@ -178,7 +178,7 @@ static void timer_callback(void* ctx) { case Silent: break; } - }; + } // this is a bit of a kludge... if we are on vibro and unpronounced, stop vibro after half the usual duration switch(metronome_state->output_mode) { diff --git a/applications/external/minesweeper/img/screenshot.png b/applications/external/minesweeper/img/screenshot.png deleted file mode 100644 index 65b307c55..000000000 Binary files a/applications/external/minesweeper/img/screenshot.png and /dev/null differ diff --git a/applications/external/music_beeper/music_beeper_worker.c b/applications/external/music_beeper/music_beeper_worker.c index e06e77447..c95fe8d3a 100644 --- a/applications/external/music_beeper/music_beeper_worker.c +++ b/applications/external/music_beeper/music_beeper_worker.c @@ -399,7 +399,7 @@ bool music_beeper_worker_load_rtttl_from_file(MusicBeeperWorker* instance, const if(!storage_file_open(file, file_path, FSAM_READ, FSOM_OPEN_EXISTING)) { FURI_LOG_E(TAG, "Unable to open file"); break; - }; + } uint16_t ret = 0; do { diff --git a/applications/external/music_player/music_player_worker.c b/applications/external/music_player/music_player_worker.c index ee350ee80..6a712d3e3 100644 --- a/applications/external/music_player/music_player_worker.c +++ b/applications/external/music_player/music_player_worker.c @@ -397,7 +397,7 @@ bool music_player_worker_load_rtttl_from_file(MusicPlayerWorker* instance, const if(!storage_file_open(file, file_path, FSAM_READ, FSOM_OPEN_EXISTING)) { FURI_LOG_E(TAG, "Unable to open file"); break; - }; + } uint16_t ret = 0; do { diff --git a/applications/external/musictracker/application.fam b/applications/external/music_tracker/application.fam similarity index 100% rename from applications/external/musictracker/application.fam rename to applications/external/music_tracker/application.fam diff --git a/applications/external/musictracker/tracker_engine/speaker_hal.c b/applications/external/music_tracker/tracker_engine/speaker_hal.c similarity index 99% rename from applications/external/musictracker/tracker_engine/speaker_hal.c rename to applications/external/music_tracker/tracker_engine/speaker_hal.c index 94489f1b6..0a506a424 100644 --- a/applications/external/musictracker/tracker_engine/speaker_hal.c +++ b/applications/external/music_tracker/tracker_engine/speaker_hal.c @@ -104,4 +104,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/musictracker/tracker_engine/speaker_hal.h b/applications/external/music_tracker/tracker_engine/speaker_hal.h similarity index 100% rename from applications/external/musictracker/tracker_engine/speaker_hal.h rename to applications/external/music_tracker/tracker_engine/speaker_hal.h diff --git a/applications/external/musictracker/tracker_engine/tracker.c b/applications/external/music_tracker/tracker_engine/tracker.c similarity index 100% rename from applications/external/musictracker/tracker_engine/tracker.c rename to applications/external/music_tracker/tracker_engine/tracker.c diff --git a/applications/external/musictracker/tracker_engine/tracker.h b/applications/external/music_tracker/tracker_engine/tracker.h similarity index 100% rename from applications/external/musictracker/tracker_engine/tracker.h rename to applications/external/music_tracker/tracker_engine/tracker.h diff --git a/applications/external/musictracker/tracker_engine/tracker_notes.h b/applications/external/music_tracker/tracker_engine/tracker_notes.h similarity index 100% rename from applications/external/musictracker/tracker_engine/tracker_notes.h rename to applications/external/music_tracker/tracker_engine/tracker_notes.h diff --git a/applications/external/musictracker/tracker_engine/tracker_song.h b/applications/external/music_tracker/tracker_engine/tracker_song.h similarity index 100% rename from applications/external/musictracker/tracker_engine/tracker_song.h rename to applications/external/music_tracker/tracker_engine/tracker_song.h diff --git a/applications/external/musictracker/view/tracker_view.c b/applications/external/music_tracker/view/tracker_view.c similarity index 100% rename from applications/external/musictracker/view/tracker_view.c rename to applications/external/music_tracker/view/tracker_view.c diff --git a/applications/external/musictracker/view/tracker_view.h b/applications/external/music_tracker/view/tracker_view.h similarity index 100% rename from applications/external/musictracker/view/tracker_view.h rename to applications/external/music_tracker/view/tracker_view.h diff --git a/applications/external/musictracker/zero_tracker.c b/applications/external/music_tracker/zero_tracker.c similarity index 100% rename from applications/external/musictracker/zero_tracker.c rename to applications/external/music_tracker/zero_tracker.c diff --git a/applications/external/musictracker/zero_tracker.h b/applications/external/music_tracker/zero_tracker.h similarity index 100% rename from applications/external/musictracker/zero_tracker.h rename to applications/external/music_tracker/zero_tracker.h diff --git a/applications/external/musictracker/zero_tracker.png b/applications/external/music_tracker/zero_tracker.png similarity index 100% rename from applications/external/musictracker/zero_tracker.png rename to applications/external/music_tracker/zero_tracker.png diff --git a/applications/external/nightstand_clock/application.fam b/applications/external/nightstand/application.fam similarity index 100% rename from applications/external/nightstand_clock/application.fam rename to applications/external/nightstand/application.fam diff --git a/applications/external/nightstand_clock/clock.png b/applications/external/nightstand/clock.png similarity index 100% rename from applications/external/nightstand_clock/clock.png rename to applications/external/nightstand/clock.png diff --git a/applications/external/nightstand_clock/clock_app.c b/applications/external/nightstand/clock_app.c similarity index 100% rename from applications/external/nightstand_clock/clock_app.c rename to applications/external/nightstand/clock_app.c diff --git a/applications/external/nightstand_clock/clock_app.h b/applications/external/nightstand/clock_app.h similarity index 100% rename from applications/external/nightstand_clock/clock_app.h rename to applications/external/nightstand/clock_app.h diff --git a/applications/external/picopass/picopass_worker.c b/applications/external/picopass/picopass_worker.c index 06d361fb5..5e85e6eda 100644 --- a/applications/external/picopass/picopass_worker.c +++ b/applications/external/picopass/picopass_worker.c @@ -570,7 +570,7 @@ void picopass_worker_elite_dict_attack(PicopassWorker* picopass_worker) { picopass_worker->callback(PicopassWorkerEventFail, picopass_worker->context); break; } - picopass_worker->callback(PicopassWorkerEventSuccess, picopass_worker->context); + picopass_worker->callback(PicopassWorkerEventAborted, picopass_worker->context); break; } @@ -596,6 +596,9 @@ int32_t picopass_worker_task(void* context) { picopass_worker_write_key(picopass_worker); } else if(picopass_worker->state == PicopassWorkerStateEliteDictAttack) { picopass_worker_elite_dict_attack(picopass_worker); + } else if(picopass_worker->state == PicopassWorkerStateStop) { + FURI_LOG_D(TAG, "Worker state stop"); + // no-op } else { FURI_LOG_W(TAG, "Unknown state %d", picopass_worker->state); } diff --git a/applications/external/picopass/rfal_picopass.c b/applications/external/picopass/rfal_picopass.c index ac66cb92d..e8ca64403 100644 --- a/applications/external/picopass/rfal_picopass.c +++ b/applications/external/picopass/rfal_picopass.c @@ -48,7 +48,7 @@ FuriHalNfcReturn rfalPicoPassPollerInitialize(void) { FuriHalNfcModePollPicopass, FuriHalNfcBitrate26p48, FuriHalNfcBitrate26p48); if(ret != FuriHalNfcReturnOk) { return ret; - }; + } furi_hal_nfc_ll_set_error_handling(FuriHalNfcErrorHandlingNfc); furi_hal_nfc_ll_set_guard_time(FURI_HAL_NFC_LL_GT_PICOPASS); diff --git a/applications/external/picopass/scenes/picopass_scene_elite_dict_attack.c b/applications/external/picopass/scenes/picopass_scene_elite_dict_attack.c index c76a8ffae..e6191d5ba 100644 --- a/applications/external/picopass/scenes/picopass_scene_elite_dict_attack.c +++ b/applications/external/picopass/scenes/picopass_scene_elite_dict_attack.c @@ -116,8 +116,7 @@ bool picopass_scene_elite_dict_attack_on_event(void* context, SceneManagerEvent uint32_t state = scene_manager_get_scene_state(picopass->scene_manager, PicopassSceneEliteDictAttack); if(event.type == SceneManagerEventTypeCustom) { - if(event.event == PicopassWorkerEventSuccess || - event.event == PicopassWorkerEventAborted) { + if(event.event == PicopassWorkerEventSuccess) { if(state == DictAttackStateUserDictInProgress || state == DictAttackStateStandardDictInProgress) { picopass_worker_stop(picopass->worker); @@ -127,6 +126,9 @@ bool picopass_scene_elite_dict_attack_on_event(void* context, SceneManagerEvent scene_manager_next_scene(picopass->scene_manager, PicopassSceneReadCardSuccess); consumed = true; } + } else if(event.event == PicopassWorkerEventAborted) { + scene_manager_next_scene(picopass->scene_manager, PicopassSceneReadCardSuccess); + consumed = true; } else if(event.event == PicopassWorkerEventCardDetected) { dict_attack_set_card_detected(picopass->dict_attack); consumed = true; 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 198b21d98..cc18ac066 100644 --- a/applications/external/picopass/scenes/picopass_scene_read_card_success.c +++ b/applications/external/picopass/scenes/picopass_scene_read_card_success.c @@ -34,7 +34,7 @@ void picopass_scene_read_card_success_on_enter(void* context) { 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++) { - furi_string_cat_printf(csn_str, "%02X ", csn[i]); + furi_string_cat_printf(csn_str, "%02X", csn[i]); } bool no_key = picopass_is_memset(pacs->key, 0x00, PICOPASS_BLOCK_LEN); diff --git a/applications/external/pocsag_pager/pocsag_pager_app.c b/applications/external/pocsag_pager/pocsag_pager_app.c index d4b12c466..b52b2f37f 100644 --- a/applications/external/pocsag_pager/pocsag_pager_app.c +++ b/applications/external/pocsag_pager/pocsag_pager_app.c @@ -128,7 +128,8 @@ POCSAGPagerApp* pocsag_pager_app_alloc() { // Auto switch to internal radio if external radio is not available furi_delay_ms(15); if(!furi_hal_subghz_check_radio()) { - furi_hal_subghz_set_radio_type(SubGhzRadioInternal); + furi_hal_subghz_select_radio_type(SubGhzRadioInternal); + furi_hal_subghz_init_radio_type(SubGhzRadioInternal); } furi_hal_power_suppress_charge_enter(); @@ -146,6 +147,8 @@ void pocsag_pager_app_free(POCSAGPagerApp* app) { // 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); // Submenu view_dispatcher_remove_view(app->view_dispatcher, POCSAGPagerViewSubmenu); diff --git a/applications/external/pocsag_pager/pocsag_pager_app_i.c b/applications/external/pocsag_pager/pocsag_pager_app_i.c index ff73ab50e..85e2a4bcf 100644 --- a/applications/external/pocsag_pager/pocsag_pager_app_i.c +++ b/applications/external/pocsag_pager/pocsag_pager_app_i.c @@ -131,7 +131,7 @@ void pcsg_hopper_update(POCSAGPagerApp* app) { if(app->txrx->txrx_state == PCSGTxRxStateRx) { pcsg_rx_end(app); - }; + } if(app->txrx->txrx_state == PCSGTxRxStateIDLE) { subghz_receiver_reset(app->txrx->receiver); app->txrx->preset->frequency = diff --git a/applications/external/pocsag_pager/scenes/pocsag_pager_receiver.c b/applications/external/pocsag_pager/scenes/pocsag_pager_receiver.c index 658b70fea..60dca01c9 100644 --- a/applications/external/pocsag_pager/scenes/pocsag_pager_receiver.c +++ b/applications/external/pocsag_pager/scenes/pocsag_pager_receiver.c @@ -133,7 +133,7 @@ void pocsag_pager_scene_receiver_on_enter(void* context) { if(app->txrx->txrx_state == PCSGTxRxStateRx) { pcsg_rx_end(app); - }; + } if((app->txrx->txrx_state == PCSGTxRxStateIDLE) || (app->txrx->txrx_state == PCSGTxRxStateSleep)) { pcsg_begin( @@ -158,7 +158,7 @@ bool pocsag_pager_scene_receiver_on_event(void* context, SceneManagerEvent event if(app->txrx->txrx_state == PCSGTxRxStateRx) { pcsg_rx_end(app); pcsg_sleep(app); - }; + } app->txrx->hopper_state = PCSGHopperStateOFF; app->txrx->idx_menu_chosen = 0; subghz_receiver_set_rx_callback(app->txrx->receiver, NULL, app); diff --git a/applications/external/pomodoro/flipp_pomodoro_app.c b/applications/external/pomodoro/flipp_pomodoro_app.c index 5adca1edb..3ee3b0277 100644 --- a/applications/external/pomodoro/flipp_pomodoro_app.c +++ b/applications/external/pomodoro/flipp_pomodoro_app.c @@ -9,14 +9,14 @@ static bool flipp_pomodoro_app_back_event_callback(void* ctx) { furi_assert(ctx); FlippPomodoroApp* app = ctx; return scene_manager_handle_back_event(app->scene_manager); -}; +} static void flipp_pomodoro_app_tick_event_callback(void* ctx) { furi_assert(ctx); FlippPomodoroApp* app = ctx; scene_manager_handle_custom_event(app->scene_manager, FlippPomodoroAppCustomEventTimerTick); -}; +} static bool flipp_pomodoro_app_custom_event_callback(void* ctx, uint32_t event) { furi_assert(ctx); @@ -40,7 +40,7 @@ static bool flipp_pomodoro_app_custom_event_callback(void* ctx, uint32_t event) break; } return scene_manager_handle_custom_event(app->scene_manager, event); -}; +} FlippPomodoroApp* flipp_pomodoro_app_alloc() { FlippPomodoroApp* app = malloc(sizeof(FlippPomodoroApp)); @@ -71,17 +71,18 @@ FlippPomodoroApp* flipp_pomodoro_app_alloc() { scene_manager_next_scene(app->scene_manager, FlippPomodoroSceneTimer); return app; -}; +} void flipp_pomodoro_app_free(FlippPomodoroApp* app) { view_dispatcher_remove_view(app->view_dispatcher, FlippPomodoroAppViewTimer); view_dispatcher_free(app->view_dispatcher); scene_manager_free(app->scene_manager); flipp_pomodoro_view_timer_free(app->timer_view); + flipp_pomodoro__destroy(app->state); free(app); furi_record_close(RECORD_GUI); furi_record_close(RECORD_NOTIFICATION); -}; +} int32_t flipp_pomodoro_app(void* p) { UNUSED(p); @@ -92,4 +93,4 @@ int32_t flipp_pomodoro_app(void* p) { flipp_pomodoro_app_free(app); return 0; -}; +} diff --git a/applications/external/pomodoro/helpers/time.c b/applications/external/pomodoro/helpers/time.c index 7fb0d13c2..02540a939 100644 --- a/applications/external/pomodoro/helpers/time.c +++ b/applications/external/pomodoro/helpers/time.c @@ -7,7 +7,7 @@ const int TIME_MINUTES_IN_HOUR = 60; uint32_t time_now() { return furi_hal_rtc_get_timestamp(); -}; +} TimeDifference time_difference_seconds(uint32_t begin, uint32_t end) { const uint32_t duration_seconds = end - begin; @@ -17,4 +17,4 @@ TimeDifference time_difference_seconds(uint32_t begin, uint32_t end) { return ( TimeDifference){.total_seconds = duration_seconds, .minutes = minutes, .seconds = seconds}; -}; +} diff --git a/applications/external/pomodoro/modules/flipp_pomodoro.c b/applications/external/pomodoro/modules/flipp_pomodoro.c index 161e862f8..cd417f791 100644 --- a/applications/external/pomodoro/modules/flipp_pomodoro.c +++ b/applications/external/pomodoro/modules/flipp_pomodoro.c @@ -38,21 +38,26 @@ void flipp_pomodoro__toggle_stage(FlippPomodoroState* state) { furi_assert(state); state->current_stage_index = state->current_stage_index + 1; state->started_at_timestamp = time_now(); -}; +} PomodoroStage flipp_pomodoro__get_stage(FlippPomodoroState* state) { furi_assert(state); return flipp_pomodoro__stage_by_index(state->current_stage_index); -}; +} char* flipp_pomodoro__current_stage_label(FlippPomodoroState* state) { furi_assert(state); return current_stage_label[flipp_pomodoro__get_stage(state)]; -}; +} char* flipp_pomodoro__next_stage_label(FlippPomodoroState* state) { furi_assert(state); return next_stage_label[flipp_pomodoro__stage_by_index(state->current_stage_index + 1)]; +} + +void flipp_pomodoro__destroy(FlippPomodoroState* state) { + furi_assert(state); + free(state); }; uint32_t flipp_pomodoro__current_stage_total_duration(FlippPomodoroState* state) { @@ -63,22 +68,22 @@ uint32_t flipp_pomodoro__current_stage_total_duration(FlippPomodoroState* state) }; return stage_duration_seconds_map[flipp_pomodoro__get_stage(state)]; -}; +} uint32_t flipp_pomodoro__stage_expires_timestamp(FlippPomodoroState* state) { return state->started_at_timestamp + flipp_pomodoro__current_stage_total_duration(state); -}; +} TimeDifference flipp_pomodoro__stage_remaining_duration(FlippPomodoroState* state) { const uint32_t stage_ends_at = flipp_pomodoro__stage_expires_timestamp(state); return time_difference_seconds(time_now(), stage_ends_at); -}; +} bool flipp_pomodoro__is_stage_expired(FlippPomodoroState* state) { const uint32_t expired_by = flipp_pomodoro__stage_expires_timestamp(state); const uint8_t seamless_change_span_seconds = 1; return (time_now() - seamless_change_span_seconds) >= expired_by; -}; +} FlippPomodoroState* flipp_pomodoro__new() { FlippPomodoroState* state = malloc(sizeof(FlippPomodoroState)); @@ -86,4 +91,4 @@ FlippPomodoroState* flipp_pomodoro__new() { state->started_at_timestamp = now; state->current_stage_index = 0; return state; -}; \ No newline at end of file +} \ No newline at end of file diff --git a/applications/external/pomodoro/scenes/flipp_pomodoro_scene_timer.c b/applications/external/pomodoro/scenes/flipp_pomodoro_scene_timer.c index 2190dbdb7..8ed5dd5e7 100644 --- a/applications/external/pomodoro/scenes/flipp_pomodoro_scene_timer.c +++ b/applications/external/pomodoro/scenes/flipp_pomodoro_scene_timer.c @@ -15,7 +15,7 @@ void flipp_pomodoro_scene_timer_sync_view_state(void* ctx) { flipp_pomodoro_view_timer_set_state( flipp_pomodoro_view_timer_get_view(app->timer_view), app->state); -}; +} void flipp_pomodoro_scene_timer_on_next_stage(void* ctx) { furi_assert(ctx); @@ -23,7 +23,7 @@ void flipp_pomodoro_scene_timer_on_next_stage(void* ctx) { FlippPomodoroApp* app = ctx; view_dispatcher_send_custom_event(app->view_dispatcher, FlippPomodoroAppCustomEventStageSkip); -}; +} void flipp_pomodoro_scene_timer_on_enter(void* ctx) { furi_assert(ctx); @@ -34,7 +34,7 @@ void flipp_pomodoro_scene_timer_on_enter(void* ctx) { flipp_pomodoro_scene_timer_sync_view_state(app); flipp_pomodoro_view_timer_set_on_right_cb( app->timer_view, flipp_pomodoro_scene_timer_on_next_stage, app); -}; +} void flipp_pomodoro_scene_timer_handle_custom_event( FlippPomodoroApp* app, @@ -48,7 +48,7 @@ void flipp_pomodoro_scene_timer_handle_custom_event( if(custom_event == FlippPomodoroAppCustomEventStateUpdated) { flipp_pomodoro_scene_timer_sync_view_state(app); } -}; +} bool flipp_pomodoro_scene_timer_on_event(void* ctx, SceneManagerEvent event) { furi_assert(ctx); @@ -62,10 +62,10 @@ bool flipp_pomodoro_scene_timer_on_event(void* ctx, SceneManagerEvent event) { return ExitSignal; default: break; - }; + } return SceneEventNotConusmed; -}; +} void flipp_pomodoro_scene_timer_on_exit(void* ctx) { UNUSED(ctx); -}; \ No newline at end of file +} \ No newline at end of file diff --git a/applications/external/pomodoro/views/flipp_pomodoro_timer_view.c b/applications/external/pomodoro/views/flipp_pomodoro_timer_view.c index e8e0383b7..302380ddd 100644 --- a/applications/external/pomodoro/views/flipp_pomodoro_timer_view.c +++ b/applications/external/pomodoro/views/flipp_pomodoro_timer_view.c @@ -58,7 +58,7 @@ static void remaining_stage_time_string); furi_string_free(timer_string); -}; +} static void draw_str_with_drop_shadow( Canvas* canvas, @@ -92,7 +92,7 @@ static void static void flipp_pomodoro_view_timer_draw_callback(Canvas* canvas, void* _model) { if(!_model) { return; - }; + } FlippPomodoroTimerViewModel* model = _model; @@ -109,7 +109,7 @@ static void flipp_pomodoro_view_timer_draw_callback(Canvas* canvas, void* _model canvas_set_font(canvas, FontSecondary); elements_button_right(canvas, flipp_pomodoro__next_stage_label(model->state)); -}; +} bool flipp_pomodoro_view_timer_input_callback(InputEvent* event, void* ctx) { furi_assert(ctx); @@ -125,15 +125,15 @@ bool flipp_pomodoro_view_timer_input_callback(InputEvent* event, void* ctx) { furi_assert(timer->right_cb_ctx); timer->right_cb(timer->right_cb_ctx); return ViewInputConsumed; - }; + } return ViewInputNotConusmed; -}; +} View* flipp_pomodoro_view_timer_get_view(FlippPomodoroTimerView* timer) { furi_assert(timer); return timer->view; -}; +} void flipp_pomodoro_view_timer_assign_animation(View* view) { with_view_model( @@ -162,7 +162,7 @@ FlippPomodoroTimerView* flipp_pomodoro_view_timer_alloc() { view_set_input_callback(timer->view, flipp_pomodoro_view_timer_input_callback); return timer; -}; +} void flipp_pomodoro_view_timer_set_on_right_cb( FlippPomodoroTimerView* timer, @@ -172,7 +172,7 @@ void flipp_pomodoro_view_timer_set_on_right_cb( furi_assert(right_cb_ctx); timer->right_cb = right_cb; timer->right_cb_ctx = right_cb_ctx; -}; +} void flipp_pomodoro_view_timer_set_state(View* view, FlippPomodoroState* state) { furi_assert(view); @@ -180,7 +180,7 @@ void flipp_pomodoro_view_timer_set_state(View* view, FlippPomodoroState* state) with_view_model( view, FlippPomodoroTimerViewModel * model, { model->state = state; }, false); flipp_pomodoro_view_timer_assign_animation(view); -}; +} void flipp_pomodoro_view_timer_free(FlippPomodoroTimerView* timer) { furi_assert(timer); @@ -192,4 +192,4 @@ void flipp_pomodoro_view_timer_free(FlippPomodoroTimerView* timer) { view_free(timer->view); free(timer); -}; \ No newline at end of file +} \ No newline at end of file diff --git a/applications/external/pong/flipper_pong.c b/applications/external/pong/flipper_pong.c index 55b371ad5..53c6a7e27 100644 --- a/applications/external/pong/flipper_pong.c +++ b/applications/external/pong/flipper_pong.c @@ -15,8 +15,7 @@ #define PAD_SIZE_X 3 #define PAD_SIZE_Y 8 -#define PLAYER1_PAD_SPEED 4 - +#define PLAYER1_PAD_SPEED 2 #define PLAYER2_PAD_SPEED 2 #define BALL_SIZE 4 @@ -39,29 +38,22 @@ typedef struct Players { static void draw_callback(Canvas* canvas, void* ctx) { furi_assert(ctx); - Players* playersMutex = ctx; - furi_mutex_acquire(playersMutex->mutex, FuriWaitForever); + Players* players = ctx; + furi_mutex_acquire(players->mutex, FuriWaitForever); canvas_draw_frame(canvas, 0, 0, 128, 64); - 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_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_set_font(canvas, FontPrimary); canvas_set_font_direction(canvas, CanvasDirectionBottomToTop); char buffer[16]; - snprintf( - buffer, - sizeof(buffer), - "%u - %u", - playersMutex->player1_score, - playersMutex->player2_score); + snprintf(buffer, sizeof(buffer), "%u - %u", players->player1_score, players->player2_score); canvas_draw_str_aligned( canvas, SCREEN_SIZE_X / 2 + 15, SCREEN_SIZE_Y / 2 + 2, AlignCenter, AlignTop, buffer); - furi_mutex_release(playersMutex->mutex); + furi_mutex_release(players->mutex); } static void input_callback(InputEvent* input_event, void* ctx) { @@ -101,8 +93,7 @@ uint8_t changeDirection() { return randomuint8[0]; } -int32_t flipper_pong_app(void* p) { - UNUSED(p); +int32_t flipper_pong_app() { EventApp event; FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(EventApp)); @@ -129,7 +120,7 @@ int32_t flipper_pong_app(void* p) { } ViewPort* view_port = view_port_alloc(); - view_port_draw_callback_set(view_port, draw_callback, &players); + view_port_draw_callback_set(view_port, draw_callback, &players.mutex); view_port_input_callback_set(view_port, input_callback, event_queue); Gui* gui = furi_record_open(RECORD_GUI); @@ -152,7 +143,6 @@ int32_t flipper_pong_app(void* p) { 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 2d3acccac..70205a0a4 100644 --- a/applications/external/protoview/app.c +++ b/applications/external/protoview/app.c @@ -173,7 +173,8 @@ ProtoViewApp* protoview_app_alloc() { // Auto switch to internal radio if external radio is not available furi_delay_ms(15); if(!furi_hal_subghz_check_radio()) { - furi_hal_subghz_set_radio_type(SubGhzRadioInternal); + furi_hal_subghz_select_radio_type(SubGhzRadioInternal); + furi_hal_subghz_init_radio_type(SubGhzRadioInternal); } furi_hal_power_suppress_charge_enter(); @@ -193,6 +194,8 @@ void protoview_app_free(ProtoViewApp* app) { // 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); // View related. view_port_enabled_set(app->view_port, false); diff --git a/applications/external/rc2014_coleco/coleco.c b/applications/external/rc2014_coleco/coleco.c index 311b0ceac..f0a4c6188 100644 --- a/applications/external/rc2014_coleco/coleco.c +++ b/applications/external/rc2014_coleco/coleco.c @@ -40,15 +40,14 @@ typedef struct { } PluginEvent; typedef struct { - FuriMutex* mutex; bool dpad; int row; int column; + FuriMutex* mutex; } Coleco; static void render_callback(Canvas* const canvas, void* context) { - furi_assert(context); - Coleco* coleco = context; + Coleco* coleco = (Coleco*)context; furi_mutex_acquire(coleco->mutex, FuriWaitForever); if(coleco->dpad) { @@ -175,12 +174,20 @@ static Coleco* coleco_alloc() { coleco->row = 0; coleco->column = 1; + coleco->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + if(!coleco->mutex) { + FURI_LOG_E("Coleco", "cannot create mutex\r\n"); + free(coleco); + return NULL; + } + return coleco; } static void coleco_free(Coleco* coleco) { furi_assert(coleco); + furi_mutex_free(coleco->mutex); free(coleco); } @@ -190,11 +197,7 @@ int32_t coleco_app(void* p) { FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent)); Coleco* coleco = coleco_alloc(); - - coleco->mutex = furi_mutex_alloc(FuriMutexTypeNormal); - if(!coleco->mutex) { - FURI_LOG_E("Coleco", "cannot create mutex\r\n"); - coleco_free(coleco); + if(coleco == NULL) { return 255; } @@ -346,6 +349,8 @@ int32_t coleco_app(void* p) { view_port_update(view_port); } + } else { + FURI_LOG_D("Coleco", "FuriMessageQueue: event timeout"); } furi_mutex_release(coleco->mutex); @@ -358,7 +363,6 @@ int32_t coleco_app(void* p) { furi_record_close("gui"); view_port_free(view_port); furi_message_queue_free(event_queue); - furi_mutex_free(coleco->mutex); coleco_free(coleco); return 0; } diff --git a/applications/external/rubiks_cube_scrambler/rubiks_cube_scrambler.c b/applications/external/rubiks_cube_scrambler/rubiks_cube_scrambler.c index 4c845b883..c4ceae4ab 100644 --- a/applications/external/rubiks_cube_scrambler/rubiks_cube_scrambler.c +++ b/applications/external/rubiks_cube_scrambler/rubiks_cube_scrambler.c @@ -8,11 +8,11 @@ #include "scrambler.h" #include "furi_hal_random.h" -int scrambleStarted = 0; +bool scrambleStarted = false; char scramble_str[100] = {0}; char scramble_start[100] = {0}; char scramble_end[100] = {0}; -int notifications_enabled = 0; +bool notifications_enabled = false; static void success_vibration() { furi_hal_vibro_on(false); @@ -22,12 +22,12 @@ static void success_vibration() { return; } void split_array(char original[], int size, char first[], char second[]) { - int mid = size / 2; + int32_t mid = size / 2; if(size % 2 != 0) { mid++; } - int first_index = 0, second_index = 0; - for(int i = 0; i < size; i++) { + int32_t first_index = 0, second_index = 0; + for(int32_t i = 0; i < size; i++) { if(i < mid) { first[first_index++] = original[i]; } else { @@ -40,30 +40,24 @@ void split_array(char original[], int size, char first[], char second[]) { first[first_index] = '\0'; second[second_index] = '\0'; } +void genScramble() { + scrambleReplace(); + strcpy(scramble_str, printData()); + split_array(scramble_str, strlen(scramble_str), scramble_start, scramble_end); +} static void draw_callback(Canvas* canvas, void* ctx) { UNUSED(ctx); canvas_clear(canvas); canvas_set_font(canvas, FontPrimary); canvas_draw_str(canvas, 4, 13, "Rubik's Cube Scrambler"); - - if(scrambleStarted) { - genScramble(); - scrambleReplace(); - strcpy(scramble_str, printData()); - if(notifications_enabled) { - success_vibration(); - } - split_array(scramble_str, strlen(scramble_str), scramble_start, scramble_end); - scrambleStarted = 0; - } canvas_set_font(canvas, FontSecondary); canvas_draw_str_aligned(canvas, 64, 28, AlignCenter, AlignCenter, scramble_start); canvas_draw_str_aligned(canvas, 64, 38, AlignCenter, AlignCenter, scramble_end); elements_button_center(canvas, "New"); elements_button_left(canvas, notifications_enabled ? "On" : "Off"); -}; +} static void input_callback(InputEvent* input_event, void* ctx) { furi_assert(ctx); @@ -90,13 +84,16 @@ int32_t rubiks_cube_scrambler_main(void* p) { furi_check(furi_message_queue_get(event_queue, &event, FuriWaitForever) == FuriStatusOk); if(event.key == InputKeyOk && event.type == InputTypeShort) { - scrambleStarted = 1; + genScramble(); + if(notifications_enabled) { + success_vibration(); + } } if(event.key == InputKeyLeft && event.type == InputTypeShort) { if(notifications_enabled) { - notifications_enabled = 0; + notifications_enabled = false; } else { - notifications_enabled = 1; + notifications_enabled = true; success_vibration(); } } diff --git a/applications/external/rubiks_cube_scrambler/scrambler.c b/applications/external/rubiks_cube_scrambler/scrambler.c index ea5291940..b97a67400 100644 --- a/applications/external/rubiks_cube_scrambler/scrambler.c +++ b/applications/external/rubiks_cube_scrambler/scrambler.c @@ -12,66 +12,37 @@ Authors: Tanish Bhongade and RaZe // 6 moves along with direction char moves[6] = {'R', 'U', 'F', 'B', 'L', 'D'}; -char dir[4] = {' ', '\'', '2'}; -const int SLEN = 20; +char dir[4] = {'\'', '2'}; +const int32_t SLEN = 20; #define RESULT_SIZE 100 -// Structure which holds main scramble + struct GetScramble { char mainScramble[25][3]; }; -struct GetScramble a; // Its object - -// Function prototypes to avoid bugs -void scrambleReplace(); -void genScramble(); -void valid(); -int getRand(int upr, int lwr); -char* printData(); -void writeToFile(); - -// Main function -/* int main(){ - genScramble ();//Calling genScramble - scrambleReplace();//Calling scrambleReplace - valid();//Calling valid to validate the scramble - printData ();//Printing the final scramble - //writeToFile();//If you want to write to a file, please uncomment this - - return 0; -} */ - -void genScramble() { - // Stage 1 - for(int i = 0; i < SLEN; i++) { - strcpy(a.mainScramble[i], "00"); - } - // This makes array like this 00 00 00....... -} +struct GetScramble a; void scrambleReplace() { - // Stage 2 - // Actual process begins here - // Initialize the mainScramble array with all the possible moves - for(int i = 0; i < SLEN; i++) { + for(int32_t i = 0; i < SLEN; i++) { a.mainScramble[i][0] = moves[furi_hal_random_get() % 6]; a.mainScramble[i][1] = dir[furi_hal_random_get() % 3]; } - // Perform the Fisher-Yates shuffle - for(int i = 6 - 1; i > 0; i--) { - int j = rand() % (i + 1); + /* // Perform the Fisher-Yates shuffle + for (int32_t i = 6 - 1; i > 0; i--) + { + int32_t j = rand() % (i + 1); char temp[3]; strcpy(temp, a.mainScramble[i]); strcpy(a.mainScramble[i], a.mainScramble[j]); strcpy(a.mainScramble[j], temp); - } + } */ - // Select the first 10 elements as the scramble, using only the first three elements of the dir array - for(int i = 0; i < SLEN; i++) { + // Select the first 10 elements as the scramble, using only the first two elements of the dir array + for(int32_t i = 0; i < SLEN; i++) { a.mainScramble[i][1] = dir[furi_hal_random_get() % 3]; } - for(int i = 1; i < SLEN; i++) { + for(int32_t i = 1; i < SLEN; i++) { while(a.mainScramble[i][0] == a.mainScramble[i - 2][0] || a.mainScramble[i][0] == a.mainScramble[i - 1][0]) { a.mainScramble[i][0] = moves[furi_hal_random_get() % 5]; @@ -79,24 +50,11 @@ void scrambleReplace() { } } -// Let this function be here for now till I find out what is causing the extra space bug in the scrambles -void remove_double_spaces(char* str) { - int i, j; - int len = strlen(str); - for(i = 0, j = 0; i < len; i++, j++) { - if(str[i] == ' ' && str[i + 1] == ' ') { - i++; - } - str[j] = str[i]; - } - str[j] = '\0'; -} char* printData() { static char result[RESULT_SIZE]; - int offset = 0; - for(int loop = 0; loop < SLEN; loop++) { + int32_t offset = 0; + for(int32_t loop = 0; loop < SLEN; loop++) { offset += snprintf(result + offset, RESULT_SIZE - offset, "%s ", a.mainScramble[loop]); } - remove_double_spaces(result); return result; -} +} \ No newline at end of file diff --git a/applications/external/rubiks_cube_scrambler/scrambler.h b/applications/external/rubiks_cube_scrambler/scrambler.h index 4b56c565d..557ef20ae 100644 --- a/applications/external/rubiks_cube_scrambler/scrambler.h +++ b/applications/external/rubiks_cube_scrambler/scrambler.h @@ -1,3 +1,2 @@ void scrambleReplace(); -void genScramble(); char* printData(); diff --git a/applications/external/sam/stm32_sam.cpp b/applications/external/sam/stm32_sam.cpp index 16f6fcaab..1ab73a66d 100644 --- a/applications/external/sam/stm32_sam.cpp +++ b/applications/external/sam/stm32_sam.cpp @@ -3945,7 +3945,7 @@ void STM32SAM::Code41240() { 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: diff --git a/applications/external/spectrum_analyzer/spectrum_analyzer.c b/applications/external/spectrum_analyzer/spectrum_analyzer.c index 7148ad92b..6250ac039 100644 --- a/applications/external/spectrum_analyzer/spectrum_analyzer.c +++ b/applications/external/spectrum_analyzer/spectrum_analyzer.c @@ -396,6 +396,8 @@ void spectrum_analyzer_free(SpectrumAnalyzer* instance) { // 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); } int32_t spectrum_analyzer_app(void* p) { @@ -410,7 +412,8 @@ int32_t spectrum_analyzer_app(void* p) { // Auto switch to internal radio if external radio is not available furi_delay_ms(15); if(!furi_hal_subghz_check_radio()) { - furi_hal_subghz_set_radio_type(SubGhzRadioInternal); + furi_hal_subghz_select_radio_type(SubGhzRadioInternal); + furi_hal_subghz_init_radio_type(SubGhzRadioInternal); } furi_hal_power_suppress_charge_enter(); diff --git a/applications/external/subghz_bruteforcer/subbrute.c b/applications/external/subghz_bruteforcer/subbrute.c index 59d2c1ccb..99334c05e 100644 --- a/applications/external/subghz_bruteforcer/subbrute.c +++ b/applications/external/subghz_bruteforcer/subbrute.c @@ -185,7 +185,8 @@ int32_t subbrute_app(void* p) { // Auto switch to internal radio if external radio is not available furi_delay_ms(15); if(!furi_hal_subghz_check_radio()) { - furi_hal_subghz_set_radio_type(SubGhzRadioInternal); + furi_hal_subghz_select_radio_type(SubGhzRadioInternal); + furi_hal_subghz_init_radio_type(SubGhzRadioInternal); } furi_hal_power_suppress_charge_enter(); @@ -195,6 +196,8 @@ int32_t subbrute_app(void* p) { furi_hal_power_suppress_charge_exit(); // 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); subbrute_free(instance); diff --git a/applications/external/subghz_bruteforcer/subbrute_device.c b/applications/external/subghz_bruteforcer/subbrute_device.c index 0971c380e..192b8bfa0 100644 --- a/applications/external/subghz_bruteforcer/subbrute_device.c +++ b/applications/external/subghz_bruteforcer/subbrute_device.c @@ -104,7 +104,6 @@ bool subbrute_device_save_file(SubBruteDevice* instance, const char* dev_file_na instance->current_step, instance->file_protocol_info->bits, instance->file_protocol_info->te, - instance->file_protocol_info->repeat, instance->bit_index, instance->key_from_file, instance->two_bytes); @@ -116,8 +115,7 @@ bool subbrute_device_save_file(SubBruteDevice* instance, const char* dev_file_na instance->protocol_info->file, instance->current_step, instance->protocol_info->bits, - instance->protocol_info->te, - instance->protocol_info->repeat); + instance->protocol_info->te); } result = true; diff --git a/applications/external/subghz_bruteforcer/subbrute_protocols.c b/applications/external/subghz_bruteforcer/subbrute_protocols.c index 6c6781b78..1f3e45129 100644 --- a/applications/external/subghz_bruteforcer/subbrute_protocols.c +++ b/applications/external/subghz_bruteforcer/subbrute_protocols.c @@ -522,9 +522,9 @@ static const char* subbrute_protocol_file_types[] = { * Values to not use less memory for packet parse operations */ static const char* subbrute_key_file_start_no_tail = - "Filetype: Flipper SubGhz Key File\nVersion: 1\nFrequency: %u\nPreset: %s\nProtocol: %s\nBit: %d\nKey: %s\nRepeat: %d\n"; + "Filetype: Flipper SubGhz Key File\nVersion: 1\nFrequency: %u\nPreset: %s\nProtocol: %s\nBit: %d\nKey: %s\n"; static const char* subbrute_key_file_start_with_tail = - "Filetype: Flipper SubGhz Key File\nVersion: 1\nFrequency: %u\nPreset: %s\nProtocol: %s\nBit: %d\nKey: %s\nTE: %d\nRepeat: %d\n"; + "Filetype: Flipper SubGhz Key File\nVersion: 1\nFrequency: %u\nPreset: %s\nProtocol: %s\nBit: %d\nKey: %s\nTE: %d\n"; static const char* subbrute_key_small_no_tail = "Bit: %d\nKey: %s\nRepeat: %d\n"; //static const char* subbrute_key_small_raw = // "Filetype: Flipper SubGhz Key File\nVersion: 1\nFrequency: %u\nPreset: %s\nProtocol: %s\nBit: %d\n"; @@ -771,8 +771,7 @@ void subbrute_protocol_default_generate_file( SubBruteFileProtocol file, uint64_t step, uint8_t bits, - uint32_t te, - uint8_t repeat) { + uint32_t te) { FuriString* candidate = furi_string_alloc(); subbrute_protocol_create_candidate_for_default(candidate, file, step); @@ -790,8 +789,7 @@ void subbrute_protocol_default_generate_file( subbrute_protocol_file(file), bits, furi_string_get_cstr(candidate), - te, - repeat); + te); } else { stream_write_format( stream, @@ -800,8 +798,7 @@ void subbrute_protocol_default_generate_file( subbrute_protocol_preset(preset), subbrute_protocol_file(file), bits, - furi_string_get_cstr(candidate), - repeat); + furi_string_get_cstr(candidate)); } furi_string_free(candidate); @@ -815,7 +812,6 @@ void subbrute_protocol_file_generate_file( uint64_t step, uint8_t bits, uint32_t te, - uint8_t repeat, uint8_t bit_index, uint64_t file_key, bool two_bytes) { @@ -826,6 +822,7 @@ void subbrute_protocol_file_generate_file( candidate, step, bit_index, file_key, two_bytes); stream_clean(stream); + if(te) { stream_write_format( stream, @@ -835,8 +832,7 @@ void subbrute_protocol_file_generate_file( subbrute_protocol_file(file), bits, furi_string_get_cstr(candidate), - te, - repeat); + te); } else { stream_write_format( stream, @@ -845,8 +841,7 @@ void subbrute_protocol_file_generate_file( subbrute_protocol_preset(preset), subbrute_protocol_file(file), bits, - furi_string_get_cstr(candidate), - repeat); + furi_string_get_cstr(candidate)); } furi_string_free(candidate); diff --git a/applications/external/subghz_bruteforcer/subbrute_protocols.h b/applications/external/subghz_bruteforcer/subbrute_protocols.h index 2f41b185b..066040b10 100644 --- a/applications/external/subghz_bruteforcer/subbrute_protocols.h +++ b/applications/external/subghz_bruteforcer/subbrute_protocols.h @@ -110,8 +110,7 @@ void subbrute_protocol_default_generate_file( SubBruteFileProtocol file, uint64_t step, uint8_t bits, - uint32_t te, - uint8_t repeat); + uint32_t te); void subbrute_protocol_file_generate_file( Stream* stream, uint32_t frequency, @@ -120,7 +119,6 @@ void subbrute_protocol_file_generate_file( uint64_t step, uint8_t bits, uint32_t te, - uint8_t repeat, uint8_t bit_index, uint64_t file_key, bool two_bytes); diff --git a/applications/external/subghz_playlist/playlist.c b/applications/external/subghz_playlist/playlist.c index 345b19927..906df0725 100644 --- a/applications/external/subghz_playlist/playlist.c +++ b/applications/external/subghz_playlist/playlist.c @@ -718,7 +718,8 @@ int32_t playlist_app(void* p) { // Auto switch to internal radio if external radio is not available furi_delay_ms(15); if(!furi_hal_subghz_check_radio()) { - furi_hal_subghz_set_radio_type(SubGhzRadioInternal); + furi_hal_subghz_select_radio_type(SubGhzRadioInternal); + furi_hal_subghz_init_radio_type(SubGhzRadioInternal); } furi_hal_power_suppress_charge_enter(); @@ -811,6 +812,8 @@ exit_cleanup: furi_hal_power_suppress_charge_exit(); // 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); if(app->worker != NULL) { if(playlist_worker_running(app->worker)) { diff --git a/applications/external/subghz_remote/subghz_remote_app.c b/applications/external/subghz_remote/subghz_remote_app.c index 940968592..015d57dac 100644 --- a/applications/external/subghz_remote/subghz_remote_app.c +++ b/applications/external/subghz_remote/subghz_remote_app.c @@ -136,7 +136,7 @@ static void cfg_read_file_path( *is_enabled = 0; } else { *text_file_label = extract_filename(furi_string_get_cstr(text_file_path), 16); - FURI_LOG_D(TAG, "%s file: %s", read_key, furi_string_get_cstr(text_file_path)); + //FURI_LOG_D(TAG, "%s file: %s", read_key, furi_string_get_cstr(text_file_path)); *is_enabled = 1; } flipper_format_rewind(fff_file); @@ -155,7 +155,7 @@ static void cfg_read_file_label( if(is_enabled == 1) { *text_file_label = char_to_str((char*)furi_string_get_cstr(temp_label), 16); } - FURI_LOG_D(TAG, "%s label: %s", read_key, *text_file_label); + //FURI_LOG_D(TAG, "%s label: %s", read_key, *text_file_label); } flipper_format_rewind(fff_file); furi_string_free(temp_label); @@ -355,6 +355,10 @@ bool subghz_remote_key_load( bool res = false; + subghz_custom_btn_set(0); + keeloq_reset_original_btn(); + subghz_custom_btns_reset(); + do { // load frequency from file if(!flipper_format_read_uint32(fff_file, "Frequency", &preset->frequency, 1)) { @@ -386,6 +390,10 @@ bool subghz_remote_key_load( FURI_LOG_E(TAG, "Could not read Protocol."); break; } + if(!flipper_format_rewind(fff_data)) { + FURI_LOG_E(TAG, "Rewind error"); + return false; + } if(!furi_string_cmp_str(preset->protocol, "RAW")) { subghz_protocol_raw_gen_fff_data(fff_data, path); @@ -394,10 +402,7 @@ bool subghz_remote_key_load( FURI_LOG_E(TAG, "Unable to insert or update Repeat"); break; } - if(!flipper_format_rewind(fff_data)) { - FURI_LOG_E(TAG, "Rewind error"); - return false; - } + } else { stream_copy_full( flipper_format_get_raw_stream(fff_file), flipper_format_get_raw_stream(fff_data)); @@ -406,10 +411,6 @@ bool subghz_remote_key_load( FURI_LOG_E(TAG, "Unable to insert or update Repeat"); break; } - if(!flipper_format_rewind(fff_data)) { - FURI_LOG_E(TAG, "Rewind error"); - return false; - } } if(!flipper_format_rewind(fff_file)) { @@ -467,7 +468,7 @@ bool subghz_remote_save_protocol_to_file(FlipperFormat* fff_file, const char* de stream_save_to_file(flipper_format_stream, storage, dev_file_name, FSOM_CREATE_ALWAYS); saved = true; - FURI_LOG_D(TAG, "(save) OK Save"); + //FURI_LOG_D(TAG, "(save) OK Save"); } while(0); furi_string_free(file_dir); furi_record_close(RECORD_STORAGE); @@ -527,7 +528,8 @@ static bool subghz_remote_send_sub(SubGHzRemote* app, FlipperFormat* fff_data) { bool res = false; do { if(!furi_hal_subghz_is_tx_allowed(app->txpreset->frequency)) { - printf( + FURI_LOG_E( + TAG, "In your settings, only reception on this frequency (%lu) is allowed,\r\n" "the actual operation of the subghz remote app is not possible\r\n ", app->txpreset->frequency); @@ -544,7 +546,11 @@ static bool subghz_remote_send_sub(SubGHzRemote* app, FlipperFormat* fff_data) { break; } - subghz_transmitter_deserialize(app->tx_transmitter, fff_data); + if(subghz_transmitter_deserialize(app->tx_transmitter, fff_data) != + SubGhzProtocolStatusOk) { + FURI_LOG_E(TAG, "Deserialize error!"); + break; + } furi_hal_subghz_reset(); furi_hal_subghz_idle(); @@ -575,7 +581,7 @@ static bool subghz_remote_send_sub(SubGHzRemote* app, FlipperFormat* fff_data) { } static void subghz_remote_send_signal(SubGHzRemote* app, Storage* storage, const char* path) { - FURI_LOG_D(TAG, "Sending: %s", path); + //FURI_LOG_D(TAG, "Sending: %s", path); app->tx_file_path = path; @@ -615,7 +621,7 @@ static void subghz_remote_send_signal(SubGHzRemote* app, Storage* storage, const static void subghz_remote_process_signal(SubGHzRemote* app, FuriString* signal) { view_port_update(app->view_port); - FURI_LOG_D(TAG, "signal = %s", furi_string_get_cstr(signal)); + //FURI_LOG_D(TAG, "signal = %s", furi_string_get_cstr(signal)); if(strlen(furi_string_get_cstr(signal)) > 12) { Storage* storage = furi_record_open(RECORD_STORAGE); @@ -745,6 +751,8 @@ void subghz_remote_subghz_alloc(SubGHzRemote* app) { app->environment, EXT_PATH("subghz/assets/came_atomo")); subghz_environment_set_nice_flor_s_rainbow_table_file_name( app->environment, EXT_PATH("subghz/assets/nice_flor_s")); + subghz_environment_set_alutech_at_4n_rainbow_table_file_name( + app->environment, EXT_PATH("subghz/assets/alutech_at_4n")); subghz_environment_set_protocol_registry(app->environment, (void*)&subghz_protocol_registry); app->subghz_receiver = subghz_receiver_alloc_init(app->environment); @@ -758,7 +766,8 @@ SubGHzRemote* subghz_remote_alloc(void) { // Auto switch to internal radio if external radio is not available furi_delay_ms(15); if(!furi_hal_subghz_check_radio()) { - furi_hal_subghz_set_radio_type(SubGhzRadioInternal); + furi_hal_subghz_select_radio_type(SubGhzRadioInternal); + furi_hal_subghz_init_radio_type(SubGhzRadioInternal); } furi_hal_power_suppress_charge_enter(); @@ -785,6 +794,8 @@ void subghz_remote_free(SubGHzRemote* app, bool with_subghz) { // 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(app->up_file); furi_string_free(app->down_file); @@ -867,14 +878,14 @@ int32_t subghz_remote_app(void* p) { bool exit_loop = false; if(app->file_result == 2) { - FURI_LOG_D( - TAG, - "U: %s - D: %s - L: %s - R: %s - O: %s ", - furi_string_get_cstr(app->up_file), - furi_string_get_cstr(app->down_file), - furi_string_get_cstr(app->left_file), - furi_string_get_cstr(app->right_file), - furi_string_get_cstr(app->ok_file)); + //FURI_LOG_D( + //TAG, + //"U: %s - D: %s - L: %s - R: %s - O: %s ", + //furi_string_get_cstr(app->up_file), + //furi_string_get_cstr(app->down_file), + //furi_string_get_cstr(app->left_file), + //furi_string_get_cstr(app->right_file), + //furi_string_get_cstr(app->ok_file)); //variables to control multiple button presses and status updates app->send_status = "Idle"; @@ -892,11 +903,11 @@ int32_t subghz_remote_app(void* p) { while(1) { furi_check( furi_message_queue_get(app->input_queue, &input, FuriWaitForever) == FuriStatusOk); - FURI_LOG_D( - TAG, - "key: %s type: %s", - input_get_key_name(input.key), - input_get_type_name(input.type)); + //FURI_LOG_D( + //TAG, + //"key: %s type: %s", + //input_get_key_name(input.key), + //input_get_type_name(input.type)); switch(input.key) { case InputKeyUp: @@ -998,12 +1009,12 @@ int32_t subghz_remote_app(void* p) { } if(app->processing == 0) { - FURI_LOG_D(TAG, "processing 0"); + //FURI_LOG_D(TAG, "processing 0"); app->send_status = "Idle"; app->send_status_c = 0; app->button = 0; } else if(app->processing == 1) { - FURI_LOG_D(TAG, "processing 1"); + //FURI_LOG_D(TAG, "processing 1"); app->send_status = "Send"; @@ -1046,11 +1057,11 @@ int32_t subghz_remote_app(void* p) { while(1) { furi_check( furi_message_queue_get(app->input_queue, &input, FuriWaitForever) == FuriStatusOk); - FURI_LOG_D( - TAG, - "key: %s type: %s", - input_get_key_name(input.key), - input_get_type_name(input.type)); + //FURI_LOG_D( + //TAG, + //"key: %s type: %s", + //input_get_key_name(input.key), + //input_get_type_name(input.type)); switch(input.key) { case InputKeyRight: diff --git a/applications/external/swd_probe/swd_probe_app.c b/applications/external/swd_probe/swd_probe_app.c index beaa871dc..ae835fe61 100644 --- a/applications/external/swd_probe/swd_probe_app.c +++ b/applications/external/swd_probe/swd_probe_app.c @@ -3164,6 +3164,11 @@ int32_t swd_probe_app_main(void* p) { furi_message_queue_free(app->event_queue); furi_mutex_free(app->gui_mutex); furi_mutex_free(app->swd_mutex); + + // Reset GPIO pins to default state + for(int io = 0; io < 8; io++) { + furi_hal_gpio_init(gpios[io], GpioModeAnalog, GpioPullNo, GpioSpeedLow); + } free(app); furi_record_close(RECORD_GUI); diff --git a/applications/external/text_viewer/text_viewer.c b/applications/external/text_viewer/text_viewer.c index 59923adb9..b5ccb6ef3 100644 --- a/applications/external/text_viewer/text_viewer.c +++ b/applications/external/text_viewer/text_viewer.c @@ -157,7 +157,7 @@ static bool text_viewer_open_file(TextViewer* text_viewer, const char* file_path FURI_LOG_E(TAG, "Unable to open stream: %s", file_path); isOk = false; break; - }; + } text_viewer->model->file_size = stream_size(text_viewer->model->stream); } while(false); diff --git a/applications/external/timelapse/icons/ButtonDownHollow_7x4.png b/applications/external/timelapse/icons/ButtonDownHollow_7x4.png new file mode 100644 index 000000000..2b87c4364 Binary files /dev/null and b/applications/external/timelapse/icons/ButtonDownHollow_7x4.png differ diff --git a/applications/external/timelapse/icons/ButtonLeftHollow_4x7.png b/applications/external/timelapse/icons/ButtonLeftHollow_4x7.png new file mode 100644 index 000000000..374dc7d1a Binary files /dev/null and b/applications/external/timelapse/icons/ButtonLeftHollow_4x7.png differ diff --git a/applications/external/timelapse/icons/ButtonRightHollow_4x7.png b/applications/external/timelapse/icons/ButtonRightHollow_4x7.png new file mode 100644 index 000000000..acbb08592 Binary files /dev/null and b/applications/external/timelapse/icons/ButtonRightHollow_4x7.png differ diff --git a/applications/external/timelapse/icons/ButtonUpHollow_7x4.png b/applications/external/timelapse/icons/ButtonUpHollow_7x4.png new file mode 100644 index 000000000..e88ee3322 Binary files /dev/null and b/applications/external/timelapse/icons/ButtonUpHollow_7x4.png differ diff --git a/applications/external/timelapse/zeitraffer.c b/applications/external/timelapse/zeitraffer.c index cbae4fa44..73ea4424a 100644 --- a/applications/external/timelapse/zeitraffer.c +++ b/applications/external/timelapse/zeitraffer.c @@ -69,16 +69,18 @@ static void draw_callback(Canvas* canvas, void* ctx) { canvas_draw_str(canvas, 13, 55, "AUTO"); } - //canvas_draw_icon(canvas, 90, 17, &I_ButtonUp_7x4); - //canvas_draw_icon(canvas, 100, 17, &I_ButtonDown_7x4); - //canvas_draw_icon(canvas, 27, 17, &I_ButtonLeftSmall_3x5); - //canvas_draw_icon(canvas, 37, 17, &I_ButtonRightSmall_3x5); - //canvas_draw_icon(canvas, 3, 48, &I_Pin_star_7x7); + if(Work) { + canvas_draw_icon(canvas, 85, 41, &I_ButtonUpHollow_7x4); + canvas_draw_icon(canvas, 85, 57, &I_ButtonDownHollow_7x4); + canvas_draw_icon(canvas, 59, 48, &I_ButtonLeftHollow_4x7); + canvas_draw_icon(canvas, 72, 48, &I_ButtonRightHollow_4x7); + } else { + canvas_draw_icon(canvas, 85, 41, &I_ButtonUp_7x4); + canvas_draw_icon(canvas, 85, 57, &I_ButtonDown_7x4); + canvas_draw_icon(canvas, 59, 48, &I_ButtonLeft_4x7); + canvas_draw_icon(canvas, 72, 48, &I_ButtonRight_4x7); + } - canvas_draw_icon(canvas, 85, 41, &I_ButtonUp_7x4); - canvas_draw_icon(canvas, 85, 57, &I_ButtonDown_7x4); - canvas_draw_icon(canvas, 59, 48, &I_ButtonLeft_4x7); - canvas_draw_icon(canvas, 72, 48, &I_ButtonRight_4x7); canvas_draw_icon(canvas, 3, 48, &I_Pin_star_7x7); canvas_set_font(canvas, FontPrimary); @@ -87,8 +89,8 @@ static void draw_callback(Canvas* canvas, void* ctx) { canvas_set_font(canvas, FontPrimary); canvas_draw_str(canvas, 85, 55, "S"); - canvas_draw_icon(canvas, 59, 48, &I_ButtonLeft_4x7); - canvas_draw_icon(canvas, 72, 48, &I_ButtonRight_4x7); + //canvas_draw_icon(canvas, 59, 48, &I_ButtonLeft_4x7); + //canvas_draw_icon(canvas, 72, 48, &I_ButtonRight_4x7); if(Work) { canvas_draw_icon(canvas, 106, 46, &I_loading_10px); @@ -151,6 +153,10 @@ 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; @@ -247,6 +253,8 @@ int32_t zeitraffer_app(void* p) { if(WorkTime == 0) WorkTime = Delay; + if(Count == 1) WorkTime = Time; + if(Count == 0) { InfiniteShot = true; WorkCount = 1; @@ -390,7 +398,7 @@ int32_t zeitraffer_app(void* p) { } if(!flipper_format_write_comment_cstr( save, - "Zeitraffer app settings: n of frames, interval time, backlight type, Delay")) { + "Zeitraffer app settings: β„– of frames, interval time, backlight type, Delay")) { notification_message(notifications, &sequence_error); break; } diff --git a/applications/external/totp/application.fam b/applications/external/totp/application.fam index 8e56bbe76..94e85aae2 100644 --- a/applications/external/totp/application.fam +++ b/applications/external/totp/application.fam @@ -20,7 +20,6 @@ App( Lib( name="base64", ), - Lib(name="linked_list"), Lib( name="timezone_utils", ), diff --git a/applications/external/totp/cli/cli.c b/applications/external/totp/cli/cli.c index 30876c7e4..c860b5a36 100644 --- a/applications/external/totp/cli/cli.c +++ b/applications/external/totp/cli/cli.c @@ -63,7 +63,7 @@ static void totp_cli_handler(Cli* cli, FuriString* args, void* context) { } else if(furi_string_cmp_str(cmd, TOTP_CLI_COMMAND_AUTOMATION) == 0) { totp_cli_command_automation_handle(plugin_state, args, cli); } else if(furi_string_cmp_str(cmd, TOTP_CLI_COMMAND_RESET) == 0) { - totp_cli_command_reset_handle(cli, cli_context->event_queue); + totp_cli_command_reset_handle(plugin_state, cli, cli_context->event_queue); } else if(furi_string_cmp_str(cmd, TOTP_CLI_COMMAND_UPDATE) == 0) { totp_cli_command_update_handle(plugin_state, args, cli); } else if( diff --git a/applications/external/totp/cli/cli_helpers.c b/applications/external/totp/cli/cli_helpers.c index 36b98cf65..226917237 100644 --- a/applications/external/totp/cli/cli_helpers.c +++ b/applications/external/totp/cli/cli_helpers.c @@ -3,6 +3,11 @@ #include #include "../types/plugin_event.h" +const char* TOTP_CLI_COLOR_ERROR = "91m"; +const char* TOTP_CLI_COLOR_WARNING = "93m"; +const char* TOTP_CLI_COLOR_SUCCESS = "92m"; +const char* TOTP_CLI_COLOR_INFO = "96m"; + bool totp_cli_ensure_authenticated(const PluginState* plugin_state, Cli* cli) { if(plugin_state->current_scene == TotpSceneAuthentication) { TOTP_CLI_PRINTF("Pleases enter PIN on your flipper device\r\n"); @@ -13,10 +18,11 @@ bool totp_cli_ensure_authenticated(const PluginState* plugin_state, Cli* cli) { furi_delay_ms(100); } - TOTP_CLI_DELETE_LAST_LINE(); + totp_cli_delete_last_line(); if(plugin_state->current_scene == TotpSceneAuthentication || //-V560 plugin_state->current_scene == TotpSceneNone) { //-V560 + TOTP_CLI_PRINTF_INFO("Cancelled by user\r\n"); return false; } } @@ -54,7 +60,7 @@ bool totp_cli_read_line(Cli* cli, FuriString* out_str, bool mask_user_input) { } else if(c == CliSymbolAsciiBackspace || c == CliSymbolAsciiDel) { size_t out_str_size = furi_string_size(out_str); if(out_str_size > 0) { - TOTP_CLI_DELETE_LAST_CHAR(); + totp_cli_delete_last_char(); furi_string_left(out_str, out_str_size - 1); } } else if(c == CliSymbolAsciiCR) { @@ -83,3 +89,35 @@ void furi_string_secure_free(FuriString* str) { furi_string_free(str); } + +void totp_cli_print_invalid_arguments() { + TOTP_CLI_PRINTF_ERROR( + "Invalid command arguments. use \"help\" command to get list of available commands"); +} + +void totp_cli_print_error_updating_config_file() { + TOTP_CLI_PRINTF_ERROR("An error has occurred during updating config file\r\n"); +} + +void totp_cli_print_error_loading_token_info() { + TOTP_CLI_PRINTF_ERROR("An error has occurred during loading token information\r\n"); +} + +void totp_cli_print_processing() { + TOTP_CLI_PRINTF("Processing, please wait...\r\n"); +} + +void totp_cli_delete_last_char() { + TOTP_CLI_PRINTF("\b \b"); + fflush(stdout); +} + +void totp_cli_delete_current_line() { + TOTP_CLI_PRINTF("\33[2K\r"); + fflush(stdout); +} + +void totp_cli_delete_last_line() { + TOTP_CLI_PRINTF("\033[A\33[2K\r"); + fflush(stdout); +} diff --git a/applications/external/totp/cli/cli_helpers.h b/applications/external/totp/cli/cli_helpers.h index dd5a282d4..0f8b24364 100644 --- a/applications/external/totp/cli/cli_helpers.h +++ b/applications/external/totp/cli/cli_helpers.h @@ -14,6 +14,11 @@ #define DOCOPT_OPTIONS "[options]" #define DOCOPT_DEFAULT(val) "[default: " val "]" +extern const char* TOTP_CLI_COLOR_ERROR; +extern const char* TOTP_CLI_COLOR_WARNING; +extern const char* TOTP_CLI_COLOR_SUCCESS; +extern const char* TOTP_CLI_COLOR_INFO; + #define TOTP_CLI_PRINTF(format, ...) printf(format, ##__VA_ARGS__) #define TOTP_CLI_PRINTF_COLORFUL(color, format, ...) \ @@ -22,11 +27,6 @@ printf("\e[0m"); \ fflush(stdout) -#define TOTP_CLI_COLOR_ERROR "91m" -#define TOTP_CLI_COLOR_WARNING "93m" -#define TOTP_CLI_COLOR_SUCCESS "92m" -#define TOTP_CLI_COLOR_INFO "96m" - #define TOTP_CLI_PRINTF_ERROR(format, ...) \ TOTP_CLI_PRINTF_COLORFUL(TOTP_CLI_COLOR_ERROR, format, ##__VA_ARGS__) #define TOTP_CLI_PRINTF_WARNING(format, ...) \ @@ -36,24 +36,12 @@ #define TOTP_CLI_PRINTF_INFO(format, ...) \ TOTP_CLI_PRINTF_COLORFUL(TOTP_CLI_COLOR_INFO, format, ##__VA_ARGS__) -#define TOTP_CLI_DELETE_LAST_LINE() \ - TOTP_CLI_PRINTF("\033[A\33[2K\r"); \ - fflush(stdout) +#define TOTP_CLI_LOCK_UI(plugin_state) \ + Scene __previous_scene = plugin_state->current_scene; \ + totp_scene_director_activate_scene(plugin_state, TotpSceneStandby) -#define TOTP_CLI_DELETE_CURRENT_LINE() \ - TOTP_CLI_PRINTF("\33[2K\r"); \ - fflush(stdout) - -#define TOTP_CLI_DELETE_LAST_CHAR() \ - TOTP_CLI_PRINTF("\b \b"); \ - fflush(stdout) - -#define TOTP_CLI_PRINT_INVALID_ARGUMENTS() \ - TOTP_CLI_PRINTF_ERROR( \ - "Invalid command arguments. use \"help\" command to get list of available commands") - -#define TOTP_CLI_PRINT_ERROR_UPDATING_CONFIG_FILE() \ - TOTP_CLI_PRINTF_ERROR("An error has occurred during updating config file\r\n") +#define TOTP_CLI_UNLOCK_UI(plugin_state) \ + totp_scene_director_activate_scene(plugin_state, __previous_scene) /** * @brief Checks whether user is authenticated and entered correct PIN. @@ -92,3 +80,38 @@ bool args_read_uint8_and_trim(FuriString* args, uint8_t* value); * @param str instance to free */ void furi_string_secure_free(FuriString* str); + +/** + * @brief Deletes last printed line in console + */ +void totp_cli_delete_last_line(); + +/** + * @brief Deletes current printed line in console + */ +void totp_cli_delete_current_line(); + +/** + * @brief Deletes last printed char in console + */ +void totp_cli_delete_last_char(); + +/** + * @brief Prints error message about invalid command arguments + */ +void totp_cli_print_invalid_arguments(); + +/** + * @brief Prints error message about config file update error + */ +void totp_cli_print_error_updating_config_file(); + +/** + * @brief Prints error message about config file loading error + */ +void totp_cli_print_error_loading_token_info(); + +/** + * @brief Prints message to let user knwo that command is processing now + */ +void totp_cli_print_processing(); \ No newline at end of file diff --git a/applications/external/totp/cli/commands/add/add.c b/applications/external/totp/cli/commands/add/add.c index 3549e785b..b2cd01352 100644 --- a/applications/external/totp/cli/commands/add/add.c +++ b/applications/external/totp/cli/commands/add/add.c @@ -1,7 +1,6 @@ #include "add.h" #include #include -#include #include "../../../types/token_info.h" #include "../../../services/config/config.h" #include "../../../services/convert/convert.h" @@ -9,6 +8,77 @@ #include "../../../ui/scene_director.h" #include "../../common_command_arguments.h" +struct TotpAddContext { + FuriString* args; + Cli* cli; + uint8_t* iv; +}; + +enum TotpIteratorUpdateTokenResultsEx { + TotpIteratorUpdateTokenResultInvalidSecret = 1, + TotpIteratorUpdateTokenResultCancelled = 2, + TotpIteratorUpdateTokenResultInvalidArguments = 3 +}; + +static TotpIteratorUpdateTokenResult + add_token_handler(TokenInfo* token_info, const void* context) { + const struct TotpAddContext* context_t = context; + + // Reading token name + if(!args_read_probably_quoted_string_and_trim(context_t->args, token_info->name)) { + return TotpIteratorUpdateTokenResultInvalidArguments; + } + + FuriString* temp_str = furi_string_alloc(); + + // Read optional arguments + bool mask_user_input = true; + PlainTokenSecretEncoding token_secret_encoding = PlainTokenSecretEncodingBase32; + while(args_read_string_and_trim(context_t->args, temp_str)) { + bool parsed = false; + if(!totp_cli_try_read_algo(token_info, temp_str, context_t->args, &parsed) && + !totp_cli_try_read_digits(token_info, temp_str, context_t->args, &parsed) && + !totp_cli_try_read_duration(token_info, temp_str, context_t->args, &parsed) && + !totp_cli_try_read_unsecure_flag(temp_str, &parsed, &mask_user_input) && + !totp_cli_try_read_automation_features(token_info, temp_str, context_t->args, &parsed) && + !totp_cli_try_read_plain_token_secret_encoding( + temp_str, context_t->args, &parsed, &token_secret_encoding)) { + totp_cli_printf_unknown_argument(temp_str); + } + + if(!parsed) { + furi_string_free(temp_str); + return TotpIteratorUpdateTokenResultInvalidArguments; + } + } + + // Reading token secret + furi_string_reset(temp_str); + TOTP_CLI_PRINTF("Enter token secret and confirm with [ENTER]\r\n"); + if(!totp_cli_read_line(context_t->cli, temp_str, mask_user_input)) { + totp_cli_delete_last_line(); + furi_string_secure_free(temp_str); + return TotpIteratorUpdateTokenResultCancelled; + } + + totp_cli_delete_last_line(); + + bool secret_set = token_info_set_secret( + token_info, + furi_string_get_cstr(temp_str), + furi_string_size(temp_str), + token_secret_encoding, + context_t->iv); + + furi_string_secure_free(temp_str); + + if(!secret_set) { + return TotpIteratorUpdateTokenResultInvalidSecret; + } + + return TotpIteratorUpdateTokenResultSuccess; +} + void totp_cli_command_add_docopt_commands() { TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_ADD ", " TOTP_CLI_COMMAND_ADD_ALT ", " TOTP_CLI_COMMAND_ADD_ALT2 " Add new token\r\n"); @@ -75,90 +145,33 @@ void totp_cli_command_add_docopt_options() { } void totp_cli_command_add_handle(PluginState* plugin_state, FuriString* args, Cli* cli) { - FuriString* temp_str = furi_string_alloc(); - TokenInfo* token_info = token_info_alloc(); - - // Reading token name - if(!args_read_probably_quoted_string_and_trim(args, temp_str)) { - TOTP_CLI_PRINT_INVALID_ARGUMENTS(); - furi_string_free(temp_str); - token_info_free(token_info); + if(!totp_cli_ensure_authenticated(plugin_state, cli)) { return; } - size_t temp_cstr_len = furi_string_size(temp_str); - token_info->name = malloc(temp_cstr_len + 1); - furi_check(token_info->name != NULL); - strlcpy(token_info->name, furi_string_get_cstr(temp_str), temp_cstr_len + 1); + TokenInfoIteratorContext* iterator_context = + totp_config_get_token_iterator_context(plugin_state); - // Read optional arguments - bool mask_user_input = true; - PlainTokenSecretEncoding token_secret_encoding = PLAIN_TOKEN_ENCODING_BASE32; - while(args_read_string_and_trim(args, temp_str)) { - bool parsed = false; - if(!totp_cli_try_read_algo(token_info, temp_str, args, &parsed) && - !totp_cli_try_read_digits(token_info, temp_str, args, &parsed) && - !totp_cli_try_read_duration(token_info, temp_str, args, &parsed) && - !totp_cli_try_read_unsecure_flag(temp_str, &parsed, &mask_user_input) && - !totp_cli_try_read_automation_features(token_info, temp_str, args, &parsed) && - !totp_cli_try_read_plain_token_secret_encoding( - temp_str, args, &parsed, &token_secret_encoding)) { - totp_cli_printf_unknown_argument(temp_str); - } + TOTP_CLI_LOCK_UI(plugin_state); - if(!parsed) { - TOTP_CLI_PRINT_INVALID_ARGUMENTS(); - furi_string_free(temp_str); - token_info_free(token_info); - return; - } - } + struct TotpAddContext add_context = {.args = args, .cli = cli, .iv = &plugin_state->iv[0]}; + TotpIteratorUpdateTokenResult add_result = + totp_token_info_iterator_add_new_token(iterator_context, &add_token_handler, &add_context); - // Reading token secret - furi_string_reset(temp_str); - TOTP_CLI_PRINTF("Enter token secret and confirm with [ENTER]\r\n"); - if(!totp_cli_read_line(cli, temp_str, mask_user_input) || - !totp_cli_ensure_authenticated(plugin_state, cli)) { - TOTP_CLI_DELETE_LAST_LINE(); + if(add_result == TotpIteratorUpdateTokenResultSuccess) { + TOTP_CLI_PRINTF_SUCCESS( + "Token \"%s\" has been successfully added\r\n", + furi_string_get_cstr( + totp_token_info_iterator_get_current_token(iterator_context)->name)); + } else if(add_result == TotpIteratorUpdateTokenResultCancelled) { TOTP_CLI_PRINTF_INFO("Cancelled by user\r\n"); - furi_string_secure_free(temp_str); - token_info_free(token_info); - return; - } - - TOTP_CLI_DELETE_LAST_LINE(); - - bool load_generate_token_scene = false; - if(plugin_state->current_scene == TotpSceneGenerateToken) { - totp_scene_director_activate_scene(plugin_state, TotpSceneNone, NULL); - load_generate_token_scene = true; - } - - bool secret_set = token_info_set_secret( - token_info, - furi_string_get_cstr(temp_str), - furi_string_size(temp_str), - token_secret_encoding, - plugin_state->iv); - - furi_string_secure_free(temp_str); - - if(secret_set) { - TOTP_LIST_INIT_OR_ADD(plugin_state->tokens_list, token_info, furi_check); - plugin_state->tokens_count++; - - if(totp_config_file_save_new_token(token_info) == TotpConfigFileUpdateSuccess) { - TOTP_CLI_PRINTF_SUCCESS( - "Token \"%s\" has been successfully added\r\n", token_info->name); - } else { - TOTP_CLI_PRINT_ERROR_UPDATING_CONFIG_FILE(); - } - } else { - token_info_free(token_info); + } else if(add_result == TotpIteratorUpdateTokenResultInvalidArguments) { + totp_cli_print_invalid_arguments(); + } else if(add_result == TotpIteratorUpdateTokenResultInvalidSecret) { TOTP_CLI_PRINTF_ERROR("Token secret seems to be invalid and can not be parsed\r\n"); + } else if(add_result == TotpIteratorUpdateTokenResultFileUpdateFailed) { + totp_cli_print_error_updating_config_file(); } - if(load_generate_token_scene) { - totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL); - } + TOTP_CLI_UNLOCK_UI(plugin_state); } \ No newline at end of file diff --git a/applications/external/totp/cli/commands/automation/automation.c b/applications/external/totp/cli/commands/automation/automation.c index 1af2e5dd7..c9f6ac34b 100644 --- a/applications/external/totp/cli/commands/automation/automation.c +++ b/applications/external/totp/cli/commands/automation/automation.c @@ -31,7 +31,7 @@ void totp_cli_command_automation_docopt_arguments() { "\r\n"); } -static void totp_cli_command_automation_print_method(AutomationMethod method, char* color) { +static void totp_cli_command_automation_print_method(AutomationMethod method, const char* color) { #ifdef TOTP_BADBT_TYPE_ENABLED bool has_previous_method = false; #endif @@ -88,26 +88,20 @@ void totp_cli_command_automation_handle(PluginState* plugin_state, FuriString* a do { if(!args_valid) { - TOTP_CLI_PRINT_INVALID_ARGUMENTS(); + totp_cli_print_invalid_arguments(); break; } if(new_method_provided) { - Scene previous_scene = TotpSceneNone; - if(plugin_state->current_scene == TotpSceneGenerateToken || - plugin_state->current_scene == TotpSceneAppSettings) { - previous_scene = plugin_state->current_scene; - totp_scene_director_activate_scene(plugin_state, TotpSceneNone, NULL); - } + TOTP_CLI_LOCK_UI(plugin_state); plugin_state->automation_method = new_method; - if(totp_config_file_update_automation_method(new_method) == - TotpConfigFileUpdateSuccess) { + if(totp_config_file_update_automation_method(plugin_state)) { TOTP_CLI_PRINTF_SUCCESS("Automation method is set to "); totp_cli_command_automation_print_method(new_method, TOTP_CLI_COLOR_SUCCESS); cli_nl(); } else { - TOTP_CLI_PRINT_ERROR_UPDATING_CONFIG_FILE(); + totp_cli_print_error_updating_config_file(); } #ifdef TOTP_BADBT_TYPE_ENABLED @@ -118,9 +112,7 @@ void totp_cli_command_automation_handle(PluginState* plugin_state, FuriString* a } #endif - if(previous_scene != TotpSceneNone) { - totp_scene_director_activate_scene(plugin_state, previous_scene, NULL); - } + TOTP_CLI_UNLOCK_UI(plugin_state); } else { TOTP_CLI_PRINTF_INFO("Current automation method is "); totp_cli_command_automation_print_method( diff --git a/applications/external/totp/cli/commands/delete/delete.c b/applications/external/totp/cli/commands/delete/delete.c index a45525e4b..9a084b23f 100644 --- a/applications/external/totp/cli/commands/delete/delete.c +++ b/applications/external/totp/cli/commands/delete/delete.c @@ -3,7 +3,6 @@ #include #include #include -#include #include "../../../services/config/config.h" #include "../../cli_helpers.h" #include "../../../ui/scene_director.h" @@ -37,10 +36,13 @@ void totp_cli_command_delete_handle(PluginState* plugin_state, FuriString* args, return; } + TokenInfoIteratorContext* iterator_context = + totp_config_get_token_iterator_context(plugin_state); + int token_number; if(!args_read_int_and_trim(args, &token_number) || token_number <= 0 || - token_number > plugin_state->tokens_count) { - TOTP_CLI_PRINT_INVALID_ARGUMENTS(); + (size_t)token_number > totp_token_info_iterator_get_total_count(iterator_context)) { + totp_cli_print_invalid_arguments(); return; } @@ -51,23 +53,27 @@ void totp_cli_command_delete_handle(PluginState* plugin_state, FuriString* args, confirm_needed = false; } else { totp_cli_printf_unknown_argument(temp_str); - TOTP_CLI_PRINT_INVALID_ARGUMENTS(); + totp_cli_print_invalid_arguments(); furi_string_free(temp_str); return; } } furi_string_free(temp_str); - ListNode* list_node = list_element_at(plugin_state->tokens_list, token_number - 1); + TOTP_CLI_LOCK_UI(plugin_state); - TokenInfo* token_info = list_node->data; + size_t original_token_index = + totp_token_info_iterator_get_current_token_index(iterator_context); + totp_token_info_iterator_go_to(iterator_context, token_number - 1); + const TokenInfo* token_info = totp_token_info_iterator_get_current_token(iterator_context); + const char* token_info_name = furi_string_get_cstr(token_info->name); bool confirmed = !confirm_needed; if(confirm_needed) { TOTP_CLI_PRINTF_WARNING("WARNING!\r\n"); TOTP_CLI_PRINTF_WARNING( "TOKEN \"%s\" WILL BE PERMANENTLY DELETED WITHOUT ABILITY TO RECOVER IT.\r\n", - token_info->name); + token_info_name); TOTP_CLI_PRINTF_WARNING("Confirm? [y/n]\r\n"); fflush(stdout); char user_pick; @@ -80,32 +86,21 @@ void totp_cli_command_delete_handle(PluginState* plugin_state, FuriString* args, } if(confirmed) { - if(!totp_cli_ensure_authenticated(plugin_state, cli)) { - return; - } - - bool activate_generate_token_scene = false; - if(plugin_state->current_scene != TotpSceneAuthentication) { - totp_scene_director_activate_scene(plugin_state, TotpSceneNone, NULL); - activate_generate_token_scene = true; - } - - plugin_state->tokens_list = list_remove(plugin_state->tokens_list, list_node); - plugin_state->tokens_count--; - - if(totp_full_save_config_file(plugin_state) == TotpConfigFileUpdateSuccess) { + totp_cli_print_processing(); + if(totp_token_info_iterator_remove_current_token_info(iterator_context)) { + totp_cli_delete_last_line(); TOTP_CLI_PRINTF_SUCCESS( - "Token \"%s\" has been successfully deleted\r\n", token_info->name); + "Token \"%s\" has been successfully deleted\r\n", token_info_name); + totp_token_info_iterator_go_to(iterator_context, 0); } else { - TOTP_CLI_PRINT_ERROR_UPDATING_CONFIG_FILE(); - } - - token_info_free(token_info); - - if(activate_generate_token_scene) { - totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL); + totp_cli_delete_last_line(); + totp_cli_print_error_updating_config_file(); + totp_token_info_iterator_go_to(iterator_context, original_token_index); } } else { TOTP_CLI_PRINTF_INFO("User has not confirmed\r\n"); + totp_token_info_iterator_go_to(iterator_context, original_token_index); } + + TOTP_CLI_UNLOCK_UI(plugin_state); } \ No newline at end of file diff --git a/applications/external/totp/cli/commands/details/details.c b/applications/external/totp/cli/commands/details/details.c index 1b9289454..1677121b1 100644 --- a/applications/external/totp/cli/commands/details/details.c +++ b/applications/external/totp/cli/commands/details/details.c @@ -1,9 +1,10 @@ #include "details.h" #include #include -#include #include "../../../types/token_info.h" #include "../../../services/config/constants.h" +#include "../../../services/config/config.h" +#include "../../../ui/scene_director.h" #include "../../cli_helpers.h" #include "../../common_command_arguments.h" @@ -17,21 +18,21 @@ } while(false) static void print_automation_features(const TokenInfo* token_info) { - if(token_info->automation_features == TOKEN_AUTOMATION_FEATURE_NONE) { + if(token_info->automation_features == TokenAutomationFeatureNone) { TOTP_CLI_PRINTF("| %-20s | %-28.28s |\r\n", "Automation features", "None"); return; } bool header_printed = false; - if(token_info->automation_features & TOKEN_AUTOMATION_FEATURE_ENTER_AT_THE_END) { + if(token_info->automation_features & TokenAutomationFeatureEnterAtTheEnd) { TOTP_CLI_PRINTF_AUTOMATION_FEATURE("Type key at the end", header_printed); } - if(token_info->automation_features & TOKEN_AUTOMATION_FEATURE_TAB_AT_THE_END) { + if(token_info->automation_features & TokenAutomationFeatureTabAtTheEnd) { TOTP_CLI_PRINTF_AUTOMATION_FEATURE("Type key at the end", header_printed); } - if(token_info->automation_features & TOKEN_AUTOMATION_FEATURE_TYPE_SLOWER) { + if(token_info->automation_features & TokenAutomationFeatureTypeSlower) { TOTP_CLI_PRINTF_AUTOMATION_FEATURE("Type slower", header_printed); } } @@ -53,26 +54,39 @@ void totp_cli_command_details_handle(PluginState* plugin_state, FuriString* args } int token_number; + TokenInfoIteratorContext* iterator_context = + totp_config_get_token_iterator_context(plugin_state); if(!args_read_int_and_trim(args, &token_number) || token_number <= 0 || - token_number > plugin_state->tokens_count) { - TOTP_CLI_PRINT_INVALID_ARGUMENTS(); + (size_t)token_number > totp_token_info_iterator_get_total_count(iterator_context)) { + totp_cli_print_invalid_arguments(); return; } - ListNode* list_node = list_element_at(plugin_state->tokens_list, token_number - 1); + TOTP_CLI_LOCK_UI(plugin_state); - TokenInfo* token_info = list_node->data; + size_t original_token_index = + totp_token_info_iterator_get_current_token_index(iterator_context); + if(totp_token_info_iterator_go_to(iterator_context, token_number - 1)) { + const TokenInfo* token_info = totp_token_info_iterator_get_current_token(iterator_context); - TOTP_CLI_PRINTF("+----------------------+------------------------------+\r\n"); - TOTP_CLI_PRINTF("| %-20s | %-28s |\r\n", "Property", "Value"); - TOTP_CLI_PRINTF("+----------------------+------------------------------+\r\n"); - TOTP_CLI_PRINTF("| %-20s | %-28d |\r\n", "Index", token_number); - TOTP_CLI_PRINTF("| %-20s | %-28.28s |\r\n", "Name", token_info->name); - TOTP_CLI_PRINTF( - "| %-20s | %-28s |\r\n", "Hashing algorithm", token_info_get_algo_as_cstr(token_info)); - TOTP_CLI_PRINTF("| %-20s | %-28" PRIu8 " |\r\n", "Number of digits", token_info->digits); - TOTP_CLI_PRINTF( - "| %-20s | %" PRIu8 " sec.%-21s |\r\n", "Token lifetime", token_info->duration, " "); - print_automation_features(token_info); - TOTP_CLI_PRINTF("+----------------------+------------------------------+\r\n"); + TOTP_CLI_PRINTF("+----------------------+------------------------------+\r\n"); + TOTP_CLI_PRINTF("| %-20s | %-28s |\r\n", "Property", "Value"); + TOTP_CLI_PRINTF("+----------------------+------------------------------+\r\n"); + TOTP_CLI_PRINTF("| %-20s | %-28d |\r\n", "Index", token_number); + TOTP_CLI_PRINTF( + "| %-20s | %-28.28s |\r\n", "Name", furi_string_get_cstr(token_info->name)); + TOTP_CLI_PRINTF( + "| %-20s | %-28s |\r\n", "Hashing algorithm", token_info_get_algo_as_cstr(token_info)); + TOTP_CLI_PRINTF("| %-20s | %-28" PRIu8 " |\r\n", "Number of digits", token_info->digits); + TOTP_CLI_PRINTF( + "| %-20s | %" PRIu8 " sec.%-21s |\r\n", "Token lifetime", token_info->duration, " "); + print_automation_features(token_info); + TOTP_CLI_PRINTF("+----------------------+------------------------------+\r\n"); + } else { + totp_cli_print_error_loading_token_info(); + } + + totp_token_info_iterator_go_to(iterator_context, original_token_index); + + TOTP_CLI_UNLOCK_UI(plugin_state); } \ No newline at end of file diff --git a/applications/external/totp/cli/commands/list/list.c b/applications/external/totp/cli/commands/list/list.c index 951c102a0..bf5a8da52 100644 --- a/applications/external/totp/cli/commands/list/list.c +++ b/applications/external/totp/cli/commands/list/list.c @@ -1,8 +1,9 @@ #include "list.h" #include -#include #include "../../../types/token_info.h" #include "../../../services/config/constants.h" +#include "../../../services/config/config.h" +#include "../../../ui/scene_director.h" #include "../../cli_helpers.h" void totp_cli_command_list_docopt_commands() { @@ -20,25 +21,36 @@ void totp_cli_command_list_handle(PluginState* plugin_state, Cli* cli) { return; } - if(plugin_state->tokens_list == NULL) { + TokenInfoIteratorContext* iterator_context = + totp_config_get_token_iterator_context(plugin_state); + size_t total_count = totp_token_info_iterator_get_total_count(iterator_context); + if(total_count <= 0) { TOTP_CLI_PRINTF("There are no tokens"); return; } + TOTP_CLI_LOCK_UI(plugin_state); + + size_t original_index = totp_token_info_iterator_get_current_token_index(iterator_context); + TOTP_CLI_PRINTF("+-----+---------------------------+--------+----+-----+\r\n"); TOTP_CLI_PRINTF("| %-3s | %-25s | %-6s | %-s | %-s |\r\n", "#", "Name", "Algo", "Ln", "Dur"); TOTP_CLI_PRINTF("+-----+---------------------------+--------+----+-----+\r\n"); - uint16_t index = 1; - TOTP_LIST_FOREACH(plugin_state->tokens_list, node, { - TokenInfo* token_info = (TokenInfo*)node->data; + for(size_t i = 0; i < total_count; i++) { + totp_token_info_iterator_go_to(iterator_context, i); + const TokenInfo* token_info = totp_token_info_iterator_get_current_token(iterator_context); TOTP_CLI_PRINTF( "| %-3" PRIu16 " | %-25.25s | %-6s | %-2" PRIu8 " | %-3" PRIu8 " |\r\n", - index, - token_info->name, + i + 1, + furi_string_get_cstr(token_info->name), token_info_get_algo_as_cstr(token_info), token_info->digits, token_info->duration); - index++; - }); + } + TOTP_CLI_PRINTF("+-----+---------------------------+--------+----+-----+\r\n"); + + totp_token_info_iterator_go_to(iterator_context, original_index); + + TOTP_CLI_UNLOCK_UI(plugin_state); } \ No newline at end of file diff --git a/applications/external/totp/cli/commands/move/move.c b/applications/external/totp/cli/commands/move/move.c index 5c4bdfcd6..f26cda46e 100644 --- a/applications/external/totp/cli/commands/move/move.c +++ b/applications/external/totp/cli/commands/move/move.c @@ -2,7 +2,6 @@ #include #include -#include #include "../../../types/token_info.h" #include "../../../services/config/config.h" #include "../../cli_helpers.h" @@ -33,42 +32,52 @@ void totp_cli_command_move_handle(PluginState* plugin_state, FuriString* args, C return; } - int token_index; - if(!args_read_int_and_trim(args, &token_index) || token_index < 1 || - token_index > plugin_state->tokens_count) { - TOTP_CLI_PRINT_INVALID_ARGUMENTS(); + int token_number; + TokenInfoIteratorContext* iterator_context = + totp_config_get_token_iterator_context(plugin_state); + size_t total_count = totp_token_info_iterator_get_total_count(iterator_context); + if(!args_read_int_and_trim(args, &token_number) || token_number < 1 || + (size_t)token_number > total_count) { + totp_cli_print_invalid_arguments(); return; } - int new_token_index = 0; + int new_token_number = 0; - if(!args_read_int_and_trim(args, &new_token_index) || new_token_index < 1 || - new_token_index > plugin_state->tokens_count) { - TOTP_CLI_PRINT_INVALID_ARGUMENTS(); + if(!args_read_int_and_trim(args, &new_token_number) || new_token_number < 1 || + (size_t)new_token_number > total_count) { + totp_cli_print_invalid_arguments(); return; } - bool activate_generate_token_scene = false; - if(plugin_state->current_scene != TotpSceneAuthentication) { - totp_scene_director_activate_scene(plugin_state, TotpSceneNone, NULL); - activate_generate_token_scene = true; + if(token_number == new_token_number) { + TOTP_CLI_PRINTF_ERROR("New token number matches current token number\r\n"); + return; } - TokenInfo* token_info = NULL; - plugin_state->tokens_list = - list_remove_at(plugin_state->tokens_list, token_index - 1, (void**)&token_info); - furi_check(token_info != NULL); - plugin_state->tokens_list = - list_insert_at(plugin_state->tokens_list, new_token_index - 1, token_info); + TOTP_CLI_LOCK_UI(plugin_state); - if(totp_full_save_config_file(plugin_state) == TotpConfigFileUpdateSuccess) { + size_t token_index = token_number - 1; + size_t new_token_index = new_token_number - 1; + + size_t original_token_index = + totp_token_info_iterator_get_current_token_index(iterator_context); + + totp_cli_print_processing(); + + if(totp_token_info_iterator_go_to(iterator_context, token_index) && + totp_token_info_iterator_move_current_token_info(iterator_context, new_token_index)) { + totp_cli_delete_last_line(); TOTP_CLI_PRINTF_SUCCESS( - "Token \"%s\" has been successfully updated\r\n", token_info->name); + "Token \"%s\" has been successfully updated\r\n", + furi_string_get_cstr( + totp_token_info_iterator_get_current_token(iterator_context)->name)); } else { - TOTP_CLI_PRINT_ERROR_UPDATING_CONFIG_FILE(); + totp_cli_delete_last_line(); + totp_cli_print_error_updating_config_file(); } - if(activate_generate_token_scene) { - totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL); - } + totp_token_info_iterator_go_to(iterator_context, original_token_index); + + TOTP_CLI_UNLOCK_UI(plugin_state); } \ No newline at end of file diff --git a/applications/external/totp/cli/commands/notification/notification.c b/applications/external/totp/cli/commands/notification/notification.c index b81b7371a..f91b1b982 100644 --- a/applications/external/totp/cli/commands/notification/notification.c +++ b/applications/external/totp/cli/commands/notification/notification.c @@ -28,7 +28,8 @@ void totp_cli_command_notification_docopt_arguments() { ", " TOTP_CLI_COMMAND_NOTIFICATION_METHOD_VIBRO "\r\n"); } -static void totp_cli_command_notification_print_method(NotificationMethod method, char* color) { +static void + totp_cli_command_notification_print_method(NotificationMethod method, const char* color) { bool has_previous_method = false; if(method & NotificationMethodSound) { TOTP_CLI_PRINTF_COLORFUL(color, "\"" TOTP_CLI_COMMAND_NOTIFICATION_METHOD_SOUND "\""); @@ -73,31 +74,23 @@ void totp_cli_command_notification_handle(PluginState* plugin_state, FuriString* do { if(!args_valid) { - TOTP_CLI_PRINT_INVALID_ARGUMENTS(); + totp_cli_print_invalid_arguments(); break; } if(new_method_provided) { - Scene previous_scene = TotpSceneNone; - if(plugin_state->current_scene == TotpSceneGenerateToken || - plugin_state->current_scene == TotpSceneAppSettings) { - previous_scene = plugin_state->current_scene; - totp_scene_director_activate_scene(plugin_state, TotpSceneNone, NULL); - } + TOTP_CLI_LOCK_UI(plugin_state); plugin_state->notification_method = new_method; - if(totp_config_file_update_notification_method(new_method) == - TotpConfigFileUpdateSuccess) { + if(totp_config_file_update_notification_method(plugin_state)) { TOTP_CLI_PRINTF_SUCCESS("Notification method is set to "); totp_cli_command_notification_print_method(new_method, TOTP_CLI_COLOR_SUCCESS); cli_nl(); } else { - TOTP_CLI_PRINT_ERROR_UPDATING_CONFIG_FILE(); + totp_cli_print_error_updating_config_file(); } - if(previous_scene != TotpSceneNone) { - totp_scene_director_activate_scene(plugin_state, previous_scene, NULL); - } + TOTP_CLI_UNLOCK_UI(plugin_state); } else { TOTP_CLI_PRINTF_INFO("Current notification method is "); totp_cli_command_notification_print_method( diff --git a/applications/external/totp/cli/commands/pin/pin.c b/applications/external/totp/cli/commands/pin/pin.c index 9b9038ae7..62531b96a 100644 --- a/applications/external/totp/cli/commands/pin/pin.c +++ b/applications/external/totp/cli/commands/pin/pin.c @@ -2,7 +2,6 @@ #include #include -#include #include "../../../types/token_info.h" #include "../../../types/user_pin_codes.h" #include "../../../services/config/config.h" @@ -65,14 +64,14 @@ static bool totp_cli_read_pin(Cli* cli, uint8_t* pin, uint8_t* pin_length) { } } } else if(c == CliSymbolAsciiETX) { - TOTP_CLI_DELETE_CURRENT_LINE(); + totp_cli_delete_current_line(); TOTP_CLI_PRINTF_INFO("Cancelled by user\r\n"); return false; } else if(c == CliSymbolAsciiBackspace || c == CliSymbolAsciiDel) { if(*pin_length > 0) { *pin_length = *pin_length - 1; pin[*pin_length] = 0; - TOTP_CLI_DELETE_LAST_CHAR(); + totp_cli_delete_last_char(); } } else if(c == CliSymbolAsciiCR) { cli_nl(); @@ -80,7 +79,7 @@ static bool totp_cli_read_pin(Cli* cli, uint8_t* pin, uint8_t* pin_length) { } } - TOTP_CLI_DELETE_LAST_LINE(); + totp_cli_delete_last_line(); return true; } @@ -97,22 +96,22 @@ void totp_cli_command_pin_handle(PluginState* plugin_state, FuriString* args, Cl } else if(furi_string_cmpi_str(temp_str, TOTP_CLI_COMMAND_PIN_COMMAND_REMOVE) == 0) { do_remove = true; } else { - TOTP_CLI_PRINT_INVALID_ARGUMENTS(); + totp_cli_print_invalid_arguments(); } } else { - TOTP_CLI_PRINT_INVALID_ARGUMENTS(); + totp_cli_print_invalid_arguments(); } if((do_change || do_remove) && totp_cli_ensure_authenticated(plugin_state, cli)) { - bool load_generate_token_scene = false; + TOTP_CLI_LOCK_UI(plugin_state); do { uint8_t old_iv[TOTP_IV_SIZE]; memcpy(&old_iv[0], &plugin_state->iv[0], TOTP_IV_SIZE); uint8_t new_pin[TOTP_IV_SIZE]; + memset(&new_pin[0], 0, TOTP_IV_SIZE); uint8_t new_pin_length = 0; if(do_change) { - if(!totp_cli_read_pin(cli, &new_pin[0], &new_pin_length) || - !totp_cli_ensure_authenticated(plugin_state, cli)) { + if(!totp_cli_read_pin(cli, &new_pin[0], &new_pin_length)) { memset_s(&new_pin[0], TOTP_IV_SIZE, 0, TOTP_IV_SIZE); break; } @@ -121,7 +120,7 @@ void totp_cli_command_pin_handle(PluginState* plugin_state, FuriString* args, Cl memset(&new_pin[0], 0, TOTP_IV_SIZE); } - char* backup_path = totp_config_file_backup(); + char* backup_path = totp_config_file_backup(plugin_state); if(backup_path != NULL) { TOTP_CLI_PRINTF_WARNING("Backup conf file %s has been created\r\n", backup_path); TOTP_CLI_PRINTF_WARNING( @@ -134,61 +133,28 @@ void totp_cli_command_pin_handle(PluginState* plugin_state, FuriString* args, Cl break; } - if(plugin_state->current_scene == TotpSceneGenerateToken) { - totp_scene_director_activate_scene(plugin_state, TotpSceneNone, NULL); - load_generate_token_scene = true; - } + TOTP_CLI_PRINTF("Encrypting...\r\n"); - TOTP_CLI_PRINTF("Encrypting, please wait...\r\n"); - - memset(&plugin_state->iv[0], 0, TOTP_IV_SIZE); - memset(&plugin_state->base_iv[0], 0, TOTP_IV_SIZE); - if(plugin_state->crypto_verify_data != NULL) { - free(plugin_state->crypto_verify_data); - plugin_state->crypto_verify_data = NULL; - } - - if(!totp_crypto_seed_iv( - plugin_state, new_pin_length > 0 ? &new_pin[0] : NULL, new_pin_length)) { - memset_s(&new_pin[0], TOTP_IV_SIZE, 0, TOTP_IV_SIZE); - TOTP_CLI_PRINT_ERROR_UPDATING_CONFIG_FILE(); - break; - } + bool update_result = + totp_config_file_update_encryption(plugin_state, new_pin, new_pin_length); memset_s(&new_pin[0], TOTP_IV_SIZE, 0, TOTP_IV_SIZE); - TOTP_LIST_FOREACH(plugin_state->tokens_list, node, { - TokenInfo* token_info = node->data; - size_t plain_token_length; - uint8_t* plain_token = totp_crypto_decrypt( - token_info->token, token_info->token_length, &old_iv[0], &plain_token_length); - free(token_info->token); - token_info->token = totp_crypto_encrypt( - plain_token, - plain_token_length, - &plugin_state->iv[0], - &token_info->token_length); - memset_s(plain_token, plain_token_length, 0, plain_token_length); - free(plain_token); - }); + totp_cli_delete_last_line(); - TOTP_CLI_DELETE_LAST_LINE(); - - if(totp_full_save_config_file(plugin_state) == TotpConfigFileUpdateSuccess) { + if(update_result) { if(do_change) { TOTP_CLI_PRINTF_SUCCESS("PIN has been successfully changed\r\n"); } else if(do_remove) { TOTP_CLI_PRINTF_SUCCESS("PIN has been successfully removed\r\n"); } } else { - TOTP_CLI_PRINT_ERROR_UPDATING_CONFIG_FILE(); + totp_cli_print_error_updating_config_file(); } } while(false); - if(load_generate_token_scene) { - totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL); - } + TOTP_CLI_UNLOCK_UI(plugin_state); } furi_string_free(temp_str); diff --git a/applications/external/totp/cli/commands/reset/reset.c b/applications/external/totp/cli/commands/reset/reset.c index c7928db31..96b2cc56a 100644 --- a/applications/external/totp/cli/commands/reset/reset.c +++ b/applications/external/totp/cli/commands/reset/reset.c @@ -1,8 +1,9 @@ #include "reset.h" #include -#include +#include #include "../../cli_helpers.h" +#include "../../../ui/scene_director.h" #include "../../../services/config/config.h" #define TOTP_CLI_RESET_CONFIRMATION_KEYWORD "YES" @@ -16,7 +17,11 @@ void totp_cli_command_reset_docopt_usage() { TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_NAME " " TOTP_CLI_COMMAND_RESET "\r\n"); } -void totp_cli_command_reset_handle(Cli* cli, FuriMessageQueue* event_queue) { +void totp_cli_command_reset_handle( + PluginState* plugin_state, + Cli* cli, + FuriMessageQueue* event_queue) { + TOTP_CLI_LOCK_UI(plugin_state); TOTP_CLI_PRINTF_WARNING( "As a result of reset all the settings and tokens will be permanently lost.\r\n"); TOTP_CLI_PRINTF_WARNING("Do you really want to reset application?\r\n"); @@ -27,11 +32,12 @@ void totp_cli_command_reset_handle(Cli* cli, FuriMessageQueue* event_queue) { furi_string_cmpi_str(temp_str, TOTP_CLI_RESET_CONFIRMATION_KEYWORD) == 0; furi_string_free(temp_str); if(is_confirmed) { - totp_config_file_reset(); + totp_config_file_reset(plugin_state); TOTP_CLI_PRINTF_SUCCESS("Application has been successfully reset to default.\r\n"); TOTP_CLI_PRINTF_SUCCESS("Now application will be closed to apply all the changes.\r\n"); totp_cli_force_close_app(event_queue); } else { TOTP_CLI_PRINTF_INFO("Action was not confirmed by user\r\n"); + TOTP_CLI_UNLOCK_UI(plugin_state); } } \ No newline at end of file diff --git a/applications/external/totp/cli/commands/reset/reset.h b/applications/external/totp/cli/commands/reset/reset.h index 7c879e13e..3a4675587 100644 --- a/applications/external/totp/cli/commands/reset/reset.h +++ b/applications/external/totp/cli/commands/reset/reset.h @@ -5,6 +5,9 @@ #define TOTP_CLI_COMMAND_RESET "reset" -void totp_cli_command_reset_handle(Cli* cli, FuriMessageQueue* event_queue); +void totp_cli_command_reset_handle( + PluginState* plugin_state, + Cli* cli, + FuriMessageQueue* event_queue); void totp_cli_command_reset_docopt_commands(); void totp_cli_command_reset_docopt_usage(); \ No newline at end of file diff --git a/applications/external/totp/cli/commands/timezone/timezone.c b/applications/external/totp/cli/commands/timezone/timezone.c index 61e4fa065..0f6bc5a76 100644 --- a/applications/external/totp/cli/commands/timezone/timezone.c +++ b/applications/external/totp/cli/commands/timezone/timezone.c @@ -33,19 +33,14 @@ void totp_cli_command_timezone_handle(PluginState* plugin_state, FuriString* arg char* strtof_endptr; float tz = strtof(furi_string_get_cstr(temp_str), &strtof_endptr); if(*strtof_endptr == 0 && tz >= -12.75f && tz <= 12.75f) { + TOTP_CLI_LOCK_UI(plugin_state); plugin_state->timezone_offset = tz; - if(totp_config_file_update_timezone_offset(tz) == TotpConfigFileUpdateSuccess) { + if(totp_config_file_update_timezone_offset(plugin_state)) { TOTP_CLI_PRINTF_SUCCESS("Timezone is set to %f\r\n", (double)tz); } else { - TOTP_CLI_PRINT_ERROR_UPDATING_CONFIG_FILE(); - } - if(plugin_state->current_scene == TotpSceneGenerateToken) { - totp_scene_director_activate_scene(plugin_state, TotpSceneNone, NULL); - totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL); - } else if(plugin_state->current_scene == TotpSceneAppSettings) { - totp_scene_director_activate_scene(plugin_state, TotpSceneNone, NULL); - totp_scene_director_activate_scene(plugin_state, TotpSceneAppSettings, NULL); + totp_cli_print_error_updating_config_file(); } + TOTP_CLI_UNLOCK_UI(plugin_state); } else { TOTP_CLI_PRINTF_ERROR("Invalid timezone offset\r\n"); } diff --git a/applications/external/totp/cli/commands/update/update.c b/applications/external/totp/cli/commands/update/update.c index bba7cad35..49beb7b6d 100644 --- a/applications/external/totp/cli/commands/update/update.c +++ b/applications/external/totp/cli/commands/update/update.c @@ -1,7 +1,6 @@ #include "update.h" #include #include -#include #include "../../../types/token_info.h" #include "../../../services/config/config.h" #include "../../../services/convert/convert.h" @@ -11,6 +10,103 @@ #define TOTP_CLI_COMMAND_UPDATE_ARG_SECRET_PREFIX "-s" +struct TotpUpdateContext { + FuriString* args; + Cli* cli; + uint8_t* iv; +}; + +enum TotpIteratorUpdateTokenResultsEx { + TotpIteratorUpdateTokenResultInvalidSecret = 1, + TotpIteratorUpdateTokenResultCancelled = 2, + TotpIteratorUpdateTokenResultInvalidArguments = 3 +}; + +static bool totp_cli_try_read_name( + TokenInfo* token_info, + const FuriString* arg, + FuriString* args, + bool* parsed) { + if(furi_string_cmpi_str(arg, TOTP_CLI_COMMAND_ARG_NAME_PREFIX) == 0) { + if(!args_read_probably_quoted_string_and_trim(args, token_info->name) || + furi_string_empty(token_info->name)) { + totp_cli_printf_missed_argument_value(TOTP_CLI_COMMAND_ARG_NAME_PREFIX); + } else { + *parsed = true; + } + + return true; + } + + return false; +} + +static bool totp_cli_try_read_change_secret_flag(const FuriString* arg, bool* parsed, bool* flag) { + if(furi_string_cmpi_str(arg, TOTP_CLI_COMMAND_UPDATE_ARG_SECRET_PREFIX) == 0) { + *flag = true; + *parsed = true; + return true; + } + + return false; +} + +static TotpIteratorUpdateTokenResult + update_token_handler(TokenInfo* token_info, const void* context) { + const struct TotpUpdateContext* context_t = context; + + // Read optional arguments + FuriString* temp_str = furi_string_alloc(); + bool mask_user_input = true; + bool update_token_secret = false; + PlainTokenSecretEncoding token_secret_encoding = PlainTokenSecretEncodingBase32; + while(args_read_string_and_trim(context_t->args, temp_str)) { + bool parsed = false; + if(!totp_cli_try_read_name(token_info, temp_str, context_t->args, &parsed) && + !totp_cli_try_read_algo(token_info, temp_str, context_t->args, &parsed) && + !totp_cli_try_read_digits(token_info, temp_str, context_t->args, &parsed) && + !totp_cli_try_read_duration(token_info, temp_str, context_t->args, &parsed) && + !totp_cli_try_read_unsecure_flag(temp_str, &parsed, &mask_user_input) && + !totp_cli_try_read_change_secret_flag(temp_str, &parsed, &update_token_secret) && + !totp_cli_try_read_automation_features(token_info, temp_str, context_t->args, &parsed) && + !totp_cli_try_read_plain_token_secret_encoding( + temp_str, context_t->args, &parsed, &token_secret_encoding)) { + totp_cli_printf_unknown_argument(temp_str); + } + + if(!parsed) { + furi_string_free(temp_str); + return TotpIteratorUpdateTokenResultInvalidArguments; + } + } + + if(update_token_secret) { + // Reading token secret + furi_string_reset(temp_str); + TOTP_CLI_PRINTF("Enter token secret and confirm with [ENTER]\r\n"); + bool token_secret_read = totp_cli_read_line(context_t->cli, temp_str, mask_user_input); + totp_cli_delete_last_line(); + if(!token_secret_read) { + furi_string_secure_free(temp_str); + return TotpIteratorUpdateTokenResultCancelled; + } + + if(!token_info_set_secret( + token_info, + furi_string_get_cstr(temp_str), + furi_string_size(temp_str), + token_secret_encoding, + context_t->iv)) { + furi_string_secure_free(temp_str); + return TotpIteratorUpdateTokenResultInvalidSecret; + } + } + + furi_string_secure_free(temp_str); + + return TotpIteratorUpdateTokenResultSuccess; +} + void totp_cli_command_update_docopt_commands() { TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_UPDATE " Update existing token\r\n"); } @@ -34,136 +130,46 @@ void totp_cli_command_update_docopt_options() { TOTP_CLI_COMMAND_UPDATE_ARG_SECRET_PREFIX) " Update token secret\r\n"); } -static bool - totp_cli_try_read_name(TokenInfo* token_info, FuriString* arg, FuriString* args, bool* parsed) { - if(furi_string_cmpi_str(arg, TOTP_CLI_COMMAND_ARG_NAME_PREFIX) == 0) { - if(!args_read_probably_quoted_string_and_trim(args, arg) || furi_string_empty(arg)) { - totp_cli_printf_missed_argument_value(TOTP_CLI_COMMAND_ARG_NAME_PREFIX); - } else { - if(token_info->name != NULL) { - free(token_info->name); - } - - size_t temp_cstr_len = furi_string_size(arg); - token_info->name = malloc(temp_cstr_len + 1); - furi_check(token_info->name != NULL); - strlcpy(token_info->name, furi_string_get_cstr(arg), temp_cstr_len + 1); - *parsed = true; - } - - return true; - } - - return false; -} - -static bool totp_cli_try_read_change_secret_flag(const FuriString* arg, bool* parsed, bool* flag) { - if(furi_string_cmpi_str(arg, TOTP_CLI_COMMAND_UPDATE_ARG_SECRET_PREFIX) == 0) { - *flag = true; - *parsed = true; - return true; - } - - return false; -} - void totp_cli_command_update_handle(PluginState* plugin_state, FuriString* args, Cli* cli) { - FuriString* temp_str = furi_string_alloc(); - if(!totp_cli_ensure_authenticated(plugin_state, cli)) { return; } + TokenInfoIteratorContext* iterator_context = + totp_config_get_token_iterator_context(plugin_state); + int token_number; if(!args_read_int_and_trim(args, &token_number) || token_number <= 0 || - token_number > plugin_state->tokens_count) { - TOTP_CLI_PRINT_INVALID_ARGUMENTS(); + (size_t)token_number > totp_token_info_iterator_get_total_count(iterator_context)) { + totp_cli_print_invalid_arguments(); return; } - ListNode* list_item = list_element_at(plugin_state->tokens_list, token_number - 1); - TokenInfo* existing_token_info = list_item->data; - TokenInfo* token_info = token_info_clone(existing_token_info); + TOTP_CLI_LOCK_UI(plugin_state); - // Read optional arguments - bool mask_user_input = true; - bool update_token_secret = false; - PlainTokenSecretEncoding token_secret_encoding = PLAIN_TOKEN_ENCODING_BASE32; - while(args_read_string_and_trim(args, temp_str)) { - bool parsed = false; - if(!totp_cli_try_read_name(token_info, temp_str, args, &parsed) && - !totp_cli_try_read_algo(token_info, temp_str, args, &parsed) && - !totp_cli_try_read_digits(token_info, temp_str, args, &parsed) && - !totp_cli_try_read_duration(token_info, temp_str, args, &parsed) && - !totp_cli_try_read_unsecure_flag(temp_str, &parsed, &mask_user_input) && - !totp_cli_try_read_change_secret_flag(temp_str, &parsed, &update_token_secret) && - !totp_cli_try_read_automation_features(token_info, temp_str, args, &parsed) && - !totp_cli_try_read_plain_token_secret_encoding( - temp_str, args, &parsed, &token_secret_encoding)) { - totp_cli_printf_unknown_argument(temp_str); - } + size_t previous_index = totp_token_info_iterator_get_current_token_index(iterator_context); + totp_token_info_iterator_go_to(iterator_context, token_number - 1); - if(!parsed) { - TOTP_CLI_PRINT_INVALID_ARGUMENTS(); - furi_string_free(temp_str); - token_info_free(token_info); - return; - } + struct TotpUpdateContext update_context = { + .args = args, .cli = cli, .iv = &plugin_state->iv[0]}; + TotpIteratorUpdateTokenResult update_result = totp_token_info_iterator_update_current_token( + iterator_context, &update_token_handler, &update_context); + + if(update_result == TotpIteratorUpdateTokenResultSuccess) { + TOTP_CLI_PRINTF_SUCCESS( + "Token \"%s\" has been successfully updated\r\n", + furi_string_get_cstr( + totp_token_info_iterator_get_current_token(iterator_context)->name)); + } else if(update_result == TotpIteratorUpdateTokenResultInvalidArguments) { + totp_cli_print_invalid_arguments(); + } else if(update_result == TotpIteratorUpdateTokenResultCancelled) { + TOTP_CLI_PRINTF_INFO("Cancelled by user\r\n"); + } else if(update_result == TotpIteratorUpdateTokenResultInvalidSecret) { + TOTP_CLI_PRINTF_ERROR("Token secret seems to be invalid and can not be parsed\r\n"); + } else if(update_result == TotpIteratorUpdateTokenResultFileUpdateFailed) { + totp_cli_print_error_updating_config_file(); } - bool token_secret_read = false; - if(update_token_secret) { - // Reading token secret - furi_string_reset(temp_str); - TOTP_CLI_PRINTF("Enter token secret and confirm with [ENTER]\r\n"); - token_secret_read = totp_cli_read_line(cli, temp_str, mask_user_input); - TOTP_CLI_DELETE_LAST_LINE(); - if(!token_secret_read) { - TOTP_CLI_PRINTF_INFO("Cancelled by user\r\n"); - } - } - - bool load_generate_token_scene = false; - if(plugin_state->current_scene == TotpSceneGenerateToken) { - totp_scene_director_activate_scene(plugin_state, TotpSceneNone, NULL); - load_generate_token_scene = true; - } - - bool token_secret_set = false; - if(update_token_secret && token_secret_read) { - if(token_info->token != NULL) { - free(token_info->token); - } - token_secret_set = token_info_set_secret( - token_info, - furi_string_get_cstr(temp_str), - furi_string_size(temp_str), - token_secret_encoding, - plugin_state->iv); - if(!token_secret_set) { - TOTP_CLI_PRINTF_ERROR("Token secret seems to be invalid and can not be parsed\r\n"); - } - } - - furi_string_secure_free(temp_str); - - if(!update_token_secret || (token_secret_read && token_secret_set)) { - list_item->data = token_info; - - if(totp_full_save_config_file(plugin_state) == TotpConfigFileUpdateSuccess) { - TOTP_CLI_PRINTF_SUCCESS( - "Token \"%s\" has been successfully updated\r\n", token_info->name); - token_info_free(existing_token_info); - } else { - TOTP_CLI_PRINT_ERROR_UPDATING_CONFIG_FILE(); - list_item->data = existing_token_info; - token_info_free(token_info); - } - } else { - token_info_free(token_info); - } - - if(load_generate_token_scene) { - totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL); - } + totp_token_info_iterator_go_to(iterator_context, previous_index); + TOTP_CLI_UNLOCK_UI(plugin_state); } \ No newline at end of file diff --git a/applications/external/totp/cli/common_command_arguments.c b/applications/external/totp/cli/common_command_arguments.c index 9ed9f0126..c3129a157 100644 --- a/applications/external/totp/cli/common_command_arguments.c +++ b/applications/external/totp/cli/common_command_arguments.c @@ -1,11 +1,11 @@ #include "common_command_arguments.h" #include -inline void totp_cli_printf_missed_argument_value(char* arg) { +void totp_cli_printf_missed_argument_value(char* arg) { TOTP_CLI_PRINTF_ERROR("Missed or incorrect value for argument \"%s\"\r\n", arg); } -inline void totp_cli_printf_unknown_argument(const FuriString* arg) { +void totp_cli_printf_unknown_argument(const FuriString* arg) { TOTP_CLI_PRINTF("Unknown argument \"%s\"\r\n", furi_string_get_cstr(arg)); } @@ -121,10 +121,10 @@ bool totp_cli_try_read_plain_token_secret_encoding( totp_cli_printf_missed_argument_value(TOTP_CLI_COMMAND_ARG_SECRET_ENCODING_PREFIX); } else { if(furi_string_cmpi_str(arg, PLAIN_TOKEN_ENCODING_BASE32_NAME) == 0) { - *secret_encoding = PLAIN_TOKEN_ENCODING_BASE32; + *secret_encoding = PlainTokenSecretEncodingBase32; *parsed = true; } else if(furi_string_cmpi_str(arg, PLAIN_TOKEN_ENCODING_BASE64_NAME) == 0) { - *secret_encoding = PLAIN_TOKEN_ENCODING_BASE64; + *secret_encoding = PlainTokenSecretEncodingBase64; *parsed = true; } else { TOTP_CLI_PRINTF_ERROR( diff --git a/applications/external/totp/cli/common_command_arguments.h b/applications/external/totp/cli/common_command_arguments.h index be01c216d..be1c0bdfe 100644 --- a/applications/external/totp/cli/common_command_arguments.h +++ b/applications/external/totp/cli/common_command_arguments.h @@ -19,23 +19,29 @@ #define TOTP_CLI_COMMAND_ARG_SECRET_ENCODING "encoding" void totp_cli_printf_unknown_argument(const FuriString* arg); + void totp_cli_printf_missed_argument_value(char* arg); + bool totp_cli_try_read_algo(TokenInfo* token_info, FuriString* arg, FuriString* args, bool* parsed); + bool totp_cli_try_read_digits( TokenInfo* token_info, const FuriString* arg, FuriString* args, bool* parsed); + bool totp_cli_try_read_duration( TokenInfo* token_info, const FuriString* arg, FuriString* args, bool* parsed); + bool totp_cli_try_read_automation_features( TokenInfo* token_info, FuriString* arg, FuriString* args, bool* parsed); + bool totp_cli_try_read_unsecure_flag(const FuriString* arg, bool* parsed, bool* unsecure_flag); bool totp_cli_try_read_plain_token_secret_encoding( diff --git a/applications/external/totp/lib/base64/base64.c b/applications/external/totp/lib/base64/base64.c index dd0a12b5e..2cb0d2ffc 100644 --- a/applications/external/totp/lib/base64/base64.c +++ b/applications/external/totp/lib/base64/base64.c @@ -1,6 +1,7 @@ /* * Base64 encoding/decoding (RFC1341) * Copyright (c) 2005, Jouni Malinen + * Modified and optimized for Flipepr Zero device purposes by Alex Kopachov (@akopachov) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as diff --git a/applications/external/totp/lib/linked_list/linked_list.c b/applications/external/totp/lib/linked_list/linked_list.c deleted file mode 100644 index 23d10c548..000000000 --- a/applications/external/totp/lib/linked_list/linked_list.c +++ /dev/null @@ -1,136 +0,0 @@ -#include "linked_list.h" - -ListNode* list_init_head(void* data) { - ListNode* new = malloc(sizeof(ListNode)); - if(new == NULL) return NULL; - new->data = data; - new->next = NULL; - return new; -} - -ListNode* list_add(ListNode* head, void* data) { - ListNode* new = malloc(sizeof(ListNode)); - if(new == NULL) return NULL; - new->data = data; - new->next = NULL; - - if(head == NULL) - head = new; - else { - ListNode* it; - - for(it = head; it->next != NULL; it = it->next) - ; - - it->next = new; - } - - return head; -} - -ListNode* list_find(ListNode* head, const void* data) { - ListNode* it = NULL; - - for(it = head; it != NULL; it = it->next) - if(it->data == data) break; - - return it; -} - -ListNode* list_element_at(ListNode* head, uint16_t index) { - ListNode* it; - uint16_t i; - for(it = head, i = 0; it != NULL && i < index; it = it->next, i++) - ; - return it; -} - -ListNode* list_remove(ListNode* head, ListNode* ep) { - if(head == NULL) { - return NULL; - } - - if(head == ep) { - ListNode* new_head = head->next; - free(head); - return new_head; - } - - ListNode* it; - - for(it = head; it->next != ep; it = it->next) - ; - - it->next = ep->next; - free(ep); - - return head; -} - -ListNode* list_remove_at(ListNode* head, uint16_t index, void** removed_node_data) { - if(head == NULL) { - return NULL; - } - - ListNode* it; - ListNode* prev = NULL; - - uint16_t i; - - for(it = head, i = 0; it != NULL && i < index; prev = it, it = it->next, i++) - ; - - if(it == NULL) return head; - - ListNode* new_head = head; - if(prev == NULL) { - new_head = it->next; - } else { - prev->next = it->next; - } - - if(removed_node_data != NULL) { - *removed_node_data = it->data; - } - - free(it); - - return new_head; -} - -ListNode* list_insert_at(ListNode* head, uint16_t index, void* data) { - if(index == 0 || head == NULL) { - ListNode* new_head = list_init_head(data); - if(new_head != NULL) { - new_head->next = head; - } - return new_head; - } - - ListNode* it; - ListNode* prev = NULL; - - uint16_t i; - - for(it = head, i = 0; it != NULL && i < index; prev = it, it = it->next, i++) - ; - - ListNode* new = malloc(sizeof(ListNode)); - if(new == NULL) return NULL; - new->data = data; - new->next = it; - prev->next = new; - - return head; -} - -void list_free(ListNode* head) { - ListNode* it = head; - ListNode* tmp; - - while(it != NULL) { - tmp = it; - it = it->next; - free(tmp); - } -} diff --git a/applications/external/totp/lib/linked_list/linked_list.h b/applications/external/totp/lib/linked_list/linked_list.h deleted file mode 100644 index 3c938e59a..000000000 --- a/applications/external/totp/lib/linked_list/linked_list.h +++ /dev/null @@ -1,98 +0,0 @@ -#pragma once - -#include -#include - -/** - * @brief Single linked list node - */ -typedef struct ListNode { - /** - * @brief Pointer to the data assigned to the current list node - */ - void* data; - - /** - * @brief Pointer to the next list node - */ - struct ListNode* next; -} ListNode; - -/** - * @brief Initializes a new list node head - * @param data data to be assigned to the head list node - * @return Head list node - */ -ListNode* list_init_head(void* data); - -/** - * @brief Adds new list node to the end of the list - * @param head head list node - * @param data data to be assigned to the newly added list node - * @return Head list node - */ -ListNode* list_add( - ListNode* head, - void* data); /* adds element with specified data to the end of the list and returns new head node. */ - -/** - * @brief Searches list node with the given assigned \p data in the list - * @param head head list node - * @param data data to be searched - * @return List node containing \p data if there is such a node in the list; \c NULL otherwise - */ -ListNode* list_find(ListNode* head, const void* data); - -/** - * @brief Searches list node with the given \p index in the list - * @param head head list node - * @param index desired list node index - * @return List node with the given \p index in the list if there is such a list node; \c NULL otherwise - */ -ListNode* list_element_at(ListNode* head, uint16_t index); - -/** - * @brief Removes list node from the list - * @param head head list node - * @param ep list node to be removed - * @return Head list node - */ -ListNode* list_remove(ListNode* head, ListNode* ep); - -/** - * @brief Removes list node with the given \p index in the list from the list - * @param head head list node - * @param index index of the node to be removed - * @param[out] removed_node_data data which was assigned to the removed list node - * @return Head list node - */ -ListNode* list_remove_at(ListNode* head, uint16_t index, void** removed_node_data); - -/** - * @brief Inserts new list node at the given index - * @param head head list node - * @param index index in the list where the new list node should be inserted - * @param data data to be assgned to the new list node - * @return Head list node - */ -ListNode* list_insert_at(ListNode* head, uint16_t index, void* data); - -/** - * @brief Disposes all the list nodes in the list - * @param head head list node - */ -void list_free(ListNode* head); - -#define TOTP_LIST_INIT_OR_ADD(head, item, assert) \ - if(head == NULL) { \ - head = list_init_head(item); \ - assert(head != NULL); \ - } else { \ - assert(list_add(head, item) != NULL); \ - } - -#define TOTP_LIST_FOREACH(head, node, action) \ - ListNode* node = head; \ - while(node != NULL) { \ - action node = node->next; \ - } diff --git a/applications/external/totp/lib/polyfills/strnlen.h b/applications/external/totp/lib/polyfills/strnlen.h index 7dcef3a18..4fe0d540c 100644 --- a/applications/external/totp/lib/polyfills/strnlen.h +++ b/applications/external/totp/lib/polyfills/strnlen.h @@ -1,3 +1,6 @@ +#pragma once +#pragma weak strnlen + #include size_t strnlen(const char* s, size_t maxlen); \ No newline at end of file diff --git a/applications/external/totp/lib/roll_value/roll_value.c b/applications/external/totp/lib/roll_value/roll_value.c index b8f30e078..563429d0d 100644 --- a/applications/external/totp/lib/roll_value/roll_value.c +++ b/applications/external/totp/lib/roll_value/roll_value.c @@ -25,4 +25,4 @@ TOTP_ROLL_VALUE_FN(int8_t, int8_t) TOTP_ROLL_VALUE_FN(uint8_t, int8_t) -TOTP_ROLL_VALUE_FN(uint16_t, int16_t); \ No newline at end of file +TOTP_ROLL_VALUE_FN(size_t, int16_t) \ No newline at end of file diff --git a/applications/external/totp/lib/roll_value/roll_value.h b/applications/external/totp/lib/roll_value/roll_value.h index 3c270be9a..0c1894fd6 100644 --- a/applications/external/totp/lib/roll_value/roll_value.h +++ b/applications/external/totp/lib/roll_value/roll_value.h @@ -1,6 +1,7 @@ #pragma once #include +#include typedef uint8_t TotpRollValueOverflowBehavior; @@ -47,7 +48,7 @@ TOTP_ROLL_VALUE_FN_HEADER(int8_t, int8_t); TOTP_ROLL_VALUE_FN_HEADER(uint8_t, int8_t); /** - * @brief Rolls \c uint16_t \p value using \p min and \p max as an value constraints with \p step step. + * @brief Rolls \c size_t \p value using \p min and \p max as an value constraints with \p step step. * When value reaches constraint value \p overflow_behavior defines what to do next. * @param[in,out] value value to roll * @param step step to be used to change value @@ -55,4 +56,4 @@ TOTP_ROLL_VALUE_FN_HEADER(uint8_t, int8_t); * @param max maximum possible value * @param overflow_behavior defines what to do when value reaches constraint value */ -TOTP_ROLL_VALUE_FN_HEADER(uint16_t, int16_t); \ No newline at end of file +TOTP_ROLL_VALUE_FN_HEADER(size_t, int16_t); \ No newline at end of file diff --git a/applications/external/totp/services/config/config.c b/applications/external/totp/services/config/config.c index 6d5838b28..b40750134 100644 --- a/applications/external/totp/services/config/config.c +++ b/applications/external/totp/services/config/config.c @@ -1,19 +1,41 @@ #include "config.h" #include #include -#include +#include +#include +#include +#include +#include #include "../../types/common.h" #include "../../types/token_info.h" #include "../../features_config.h" +#include "../crypto/crypto.h" #include "migrations/common_migration.h" -#define CONFIG_FILE_DIRECTORY_PATH EXT_PATH("apps_data/authenticator") #define CONFIG_FILE_PATH CONFIG_FILE_DIRECTORY_PATH "/totp.conf" -#define CONFIG_FILE_BACKUP_BASE_PATH CONFIG_FILE_PATH ".backup" +#define CONFIG_FILE_BACKUP_DIR CONFIG_FILE_DIRECTORY_PATH "/backups" +#define CONFIG_FILE_BACKUP_BASE_PATH CONFIG_FILE_BACKUP_DIR "/totp.conf" #define CONFIG_FILE_TEMP_PATH CONFIG_FILE_PATH ".tmp" #define CONFIG_FILE_ORIG_PATH CONFIG_FILE_PATH ".orig" #define CONFIG_FILE_PATH_PREVIOUS EXT_PATH("apps/Misc") "/totp.conf" +struct ConfigFileContext { + /** + * @brief Config file reference + */ + FlipperFormat* config_file; + + /** + * @brief Storage reference + */ + Storage* storage; + + /** + * @brief Token list iterator context + */ + TokenInfoIteratorContext* token_info_iterator_context; +}; + /** * @brief Opens storage record * @return Storage record @@ -45,19 +67,34 @@ static void totp_close_config_file(FlipperFormat* file) { * @return backup path if backup successfully taken; \c NULL otherwise */ static char* totp_config_file_backup_i(Storage* storage) { - uint8_t backup_path_size = sizeof(CONFIG_FILE_BACKUP_BASE_PATH) + 5; + if(!storage_dir_exists(storage, CONFIG_FILE_BACKUP_DIR) && + !storage_simply_mkdir(storage, CONFIG_FILE_BACKUP_DIR)) { + return NULL; + } + + FuriHalRtcDateTime current_datetime; + furi_hal_rtc_get_datetime(¤t_datetime); + + uint8_t backup_path_size = sizeof(CONFIG_FILE_BACKUP_BASE_PATH) + 14; char* backup_path = malloc(backup_path_size); furi_check(backup_path != NULL); memcpy(backup_path, CONFIG_FILE_BACKUP_BASE_PATH, sizeof(CONFIG_FILE_BACKUP_BASE_PATH)); uint16_t i = 1; bool backup_file_exists; - while((backup_file_exists = storage_common_exists(storage, backup_path)) && i <= 9999) { - snprintf(backup_path, backup_path_size, CONFIG_FILE_BACKUP_BASE_PATH ".%" PRIu16, i); + do { + snprintf( + backup_path, + backup_path_size, + CONFIG_FILE_BACKUP_BASE_PATH ".%4" PRIu16 "%02" PRIu8 "%02" PRIu8 "-%" PRIu16, + current_datetime.year, + current_datetime.month, + current_datetime.day, + i); i++; - } + } while((backup_file_exists = storage_common_exists(storage, backup_path)) && i <= 9999); if(backup_file_exists || - !storage_common_copy(storage, CONFIG_FILE_PATH, backup_path) == FSE_OK) { + storage_common_copy(storage, CONFIG_FILE_PATH, backup_path) != FSE_OK) { FURI_LOG_E(LOGGING_TAG, "Unable to take a backup"); free(backup_path); return NULL; @@ -73,7 +110,7 @@ static char* totp_config_file_backup_i(Storage* storage) { * @param[out] file opened config file * @return Config file open result */ -static TotpConfigFileOpenResult totp_open_config_file(Storage* storage, FlipperFormat** file) { +static bool totp_open_config_file(Storage* storage, FlipperFormat** file) { FlipperFormat* fff_data_file = flipper_format_file_alloc(storage); if(storage_common_stat(storage, CONFIG_FILE_PATH, NULL) == FSE_OK) { @@ -81,7 +118,7 @@ static TotpConfigFileOpenResult totp_open_config_file(Storage* storage, FlipperF if(!flipper_format_file_open_existing(fff_data_file, CONFIG_FILE_PATH)) { FURI_LOG_E(LOGGING_TAG, "Error opening existing file %s", CONFIG_FILE_PATH); totp_close_config_file(fff_data_file); - return TotpConfigFileOpenError; + return false; } } else if(storage_common_stat(storage, CONFIG_FILE_PATH_PREVIOUS, NULL) == FSE_OK) { FURI_LOG_D(LOGGING_TAG, "Old config file %s found", CONFIG_FILE_PATH_PREVIOUS); @@ -93,13 +130,13 @@ static TotpConfigFileOpenResult totp_open_config_file(Storage* storage, FlipperF if(!storage_simply_mkdir(storage, CONFIG_FILE_DIRECTORY_PATH)) { FURI_LOG_E(LOGGING_TAG, "Error creating directory %s", CONFIG_FILE_DIRECTORY_PATH); totp_close_config_file(fff_data_file); - return TotpConfigFileOpenError; + return false; } } if(storage_common_rename(storage, CONFIG_FILE_PATH_PREVIOUS, CONFIG_FILE_PATH) != FSE_OK) { FURI_LOG_E(LOGGING_TAG, "Error moving config to %s", CONFIG_FILE_PATH); totp_close_config_file(fff_data_file); - return TotpConfigFileOpenError; + return false; } FURI_LOG_I(LOGGING_TAG, "Applied config file path migration"); return totp_open_config_file(storage, file); @@ -112,421 +149,155 @@ static TotpConfigFileOpenResult totp_open_config_file(Storage* storage, FlipperF CONFIG_FILE_DIRECTORY_PATH); if(!storage_simply_mkdir(storage, CONFIG_FILE_DIRECTORY_PATH)) { FURI_LOG_E(LOGGING_TAG, "Error creating directory %s", CONFIG_FILE_DIRECTORY_PATH); - return TotpConfigFileOpenError; + return false; } } if(!flipper_format_file_open_new(fff_data_file, CONFIG_FILE_PATH)) { totp_close_config_file(fff_data_file); FURI_LOG_E(LOGGING_TAG, "Error creating new file %s", CONFIG_FILE_PATH); - return TotpConfigFileOpenError; + return false; } flipper_format_write_header_cstr( fff_data_file, CONFIG_FILE_HEADER, CONFIG_FILE_ACTUAL_VERSION); - float tmp_tz = 0; - flipper_format_write_comment_cstr(fff_data_file, " "); + flipper_format_write_comment_cstr( fff_data_file, - "Timezone offset in hours. Important note: do not put '+' sign for positive values"); + "Config file format specification can be found here: https://github.com/akopachov/flipper-zero_authenticator/blob/master/docs/conf-file_description.md"); + + float tmp_tz = 0; flipper_format_write_float(fff_data_file, TOTP_CONFIG_KEY_TIMEZONE, &tmp_tz, 1); uint32_t tmp_uint32 = NotificationMethodSound | NotificationMethodVibro; - flipper_format_write_comment_cstr(fff_data_file, " "); - flipper_format_write_comment_cstr( - fff_data_file, - "How to notify user when new token is generated or badusb mode is activated (possible values: 0 - do not notify, 1 - sound, 2 - vibro, 3 sound and vibro)"); flipper_format_write_uint32( fff_data_file, TOTP_CONFIG_KEY_NOTIFICATION_METHOD, &tmp_uint32, 1); tmp_uint32 = AutomationMethodBadUsb; - flipper_format_write_comment_cstr(fff_data_file, " "); - flipper_format_write_comment_cstr( - fff_data_file, - "Automation method (0 - None, 1 - BadUSB, 2 - BadBT, 3 - BadUSB and BadBT)"); flipper_format_write_uint32( fff_data_file, TOTP_CONFIG_KEY_AUTOMATION_METHOD, &tmp_uint32, 1); - FuriString* temp_str = furi_string_alloc(); - - flipper_format_write_comment_cstr(fff_data_file, " "); - flipper_format_write_comment_cstr(fff_data_file, "=== TOKEN SAMPLE BEGIN ==="); - flipper_format_write_comment_cstr(fff_data_file, " "); - flipper_format_write_comment_cstr( - fff_data_file, "# Token name which will be visible in the UI."); - furi_string_printf(temp_str, "%s: Sample token name", TOTP_CONFIG_KEY_TOKEN_NAME); - flipper_format_write_comment(fff_data_file, temp_str); - flipper_format_write_comment_cstr(fff_data_file, " "); - - flipper_format_write_comment_cstr( - fff_data_file, - "# Plain token secret without spaces, dashes and etc, just pure alpha-numeric characters. Important note: plain token will be encrypted and replaced by TOTP app"); - furi_string_printf(temp_str, "%s: plaintokensecret", TOTP_CONFIG_KEY_TOKEN_SECRET); - flipper_format_write_comment(fff_data_file, temp_str); - flipper_format_write_comment_cstr(fff_data_file, " "); - - furi_string_printf( - temp_str, - " # Token hashing algorithm to use during code generation. Supported options are %s, %s, %s, and %s. If you are not use which one to use - use %s", - TOTP_TOKEN_ALGO_SHA1_NAME, - TOTP_TOKEN_ALGO_SHA256_NAME, - TOTP_TOKEN_ALGO_SHA512_NAME, - TOTP_TOKEN_ALGO_STEAM_NAME, - TOTP_TOKEN_ALGO_SHA1_NAME); - flipper_format_write_comment(fff_data_file, temp_str); - furi_string_printf( - temp_str, "%s: %s", TOTP_CONFIG_KEY_TOKEN_ALGO, TOTP_TOKEN_ALGO_SHA1_NAME); - flipper_format_write_comment(fff_data_file, temp_str); - flipper_format_write_comment_cstr(fff_data_file, " "); - - flipper_format_write_comment_cstr( - fff_data_file, - "# How many digits there should be in generated code. Available options are 5, 6 and 8. Majority websites requires 6 digits code, however some rare websites wants to get 8 digits code. If you are not sure which one to use - use 6"); - furi_string_printf(temp_str, "%s: 6", TOTP_CONFIG_KEY_TOKEN_DIGITS); - flipper_format_write_comment(fff_data_file, temp_str); - flipper_format_write_comment_cstr(fff_data_file, " "); - - flipper_format_write_comment_cstr( - fff_data_file, - "# Token lifetime duration in seconds. Should be between 15 and 255. Majority websites requires 30, however some rare websites may require custom lifetime. If you are not sure which one to use - use 30"); - furi_string_printf(temp_str, "%s: 30", TOTP_CONFIG_KEY_TOKEN_DURATION); - flipper_format_write_comment(fff_data_file, temp_str); - flipper_format_write_comment_cstr(fff_data_file, " "); - - flipper_format_write_comment_cstr( - fff_data_file, - "# Token input automation features (0 - None, 1 - press \"Enter\" key at the end of automation)"); - furi_string_printf(temp_str, "%s: 0", TOTP_CONFIG_KEY_TOKEN_AUTOMATION_FEATURES); - flipper_format_write_comment(fff_data_file, temp_str); - flipper_format_write_comment_cstr(fff_data_file, " "); - - flipper_format_write_comment_cstr(fff_data_file, "=== TOKEN SAMPLE END ==="); - flipper_format_write_comment_cstr(fff_data_file, " "); - - furi_string_free(temp_str); if(!flipper_format_rewind(fff_data_file)) { totp_close_config_file(fff_data_file); FURI_LOG_E(LOGGING_TAG, "Rewind error"); - return TotpConfigFileOpenError; + return false; } } *file = fff_data_file; - return TotpConfigFileOpenSuccess; + return true; } -static TotpConfigFileUpdateResult - totp_config_file_save_new_token_i(FlipperFormat* file, const TokenInfo* token_info) { - TotpConfigFileUpdateResult update_result; +char* totp_config_file_backup(const PluginState* plugin_state) { + if(plugin_state->config_file_context == NULL) return NULL; + + totp_close_config_file(plugin_state->config_file_context->config_file); + + char* result = totp_config_file_backup_i(plugin_state->config_file_context->storage); + + totp_open_config_file( + plugin_state->config_file_context->storage, + &plugin_state->config_file_context->config_file); + + totp_token_info_iterator_attach_to_config_file( + plugin_state->config_file_context->token_info_iterator_context, + plugin_state->config_file_context->config_file); + + return result; +} + +bool totp_config_file_update_timezone_offset(const PluginState* plugin_state) { + FlipperFormat* file = plugin_state->config_file_context->config_file; + flipper_format_rewind(file); + bool update_result = false; + do { - if(!flipper_format_seek_to_end(file)) { - update_result = TotpConfigFileUpdateError; + if(!flipper_format_insert_or_update_float( + file, TOTP_CONFIG_KEY_TIMEZONE, &plugin_state->timezone_offset, 1)) { break; } - if(!flipper_format_write_string_cstr(file, TOTP_CONFIG_KEY_TOKEN_NAME, token_info->name)) { - update_result = TotpConfigFileUpdateError; - break; - } - - bool token_is_valid = token_info->token != NULL && token_info->token_length > 0; - if(!token_is_valid && - !flipper_format_write_comment_cstr(file, "!!! WARNING BEGIN: INVALID TOKEN !!!")) { - update_result = TotpConfigFileUpdateError; - break; - } - - if(!flipper_format_write_hex( - file, TOTP_CONFIG_KEY_TOKEN_SECRET, token_info->token, token_info->token_length)) { - update_result = TotpConfigFileUpdateError; - break; - } - - if(!token_is_valid && !flipper_format_write_comment_cstr(file, "!!! WARNING END !!!")) { - update_result = TotpConfigFileUpdateError; - break; - } - - if(!flipper_format_write_string_cstr( - file, TOTP_CONFIG_KEY_TOKEN_ALGO, token_info_get_algo_as_cstr(token_info))) { - update_result = TotpConfigFileUpdateError; - break; - } - - uint32_t tmp_uint32 = token_info->digits; - if(!flipper_format_write_uint32(file, TOTP_CONFIG_KEY_TOKEN_DIGITS, &tmp_uint32, 1)) { - update_result = TotpConfigFileUpdateError; - break; - } - - tmp_uint32 = token_info->duration; - if(!flipper_format_write_uint32(file, TOTP_CONFIG_KEY_TOKEN_DURATION, &tmp_uint32, 1)) { - update_result = TotpConfigFileUpdateError; - break; - } - - tmp_uint32 = token_info->automation_features; - if(!flipper_format_write_uint32( - file, TOTP_CONFIG_KEY_TOKEN_AUTOMATION_FEATURES, &tmp_uint32, 1)) { - update_result = TotpConfigFileUpdateError; - break; - } - - update_result = TotpConfigFileUpdateSuccess; + update_result = true; } while(false); return update_result; } -char* totp_config_file_backup() { - Storage* storage = totp_open_storage(); - char* result = totp_config_file_backup_i(storage); - totp_close_storage(); - return result; -} - -TotpConfigFileUpdateResult totp_config_file_save_new_token(const TokenInfo* token_info) { - Storage* cfg_storage = totp_open_storage(); - FlipperFormat* file; - TotpConfigFileUpdateResult update_result; - - if(totp_open_config_file(cfg_storage, &file) == TotpConfigFileOpenSuccess) { - do { - if(totp_config_file_save_new_token_i(file, token_info) != - TotpConfigFileUpdateSuccess) { - update_result = TotpConfigFileUpdateError; - break; - } - - update_result = TotpConfigFileUpdateSuccess; - } while(false); - - totp_close_config_file(file); - } else { - update_result = TotpConfigFileUpdateError; - } - - totp_close_storage(); - return update_result; -} - -TotpConfigFileUpdateResult totp_config_file_update_timezone_offset(float new_timezone_offset) { - Storage* cfg_storage = totp_open_storage(); - FlipperFormat* file; - TotpConfigFileUpdateResult update_result; - - if(totp_open_config_file(cfg_storage, &file) == TotpConfigFileOpenSuccess) { - do { - if(!flipper_format_insert_or_update_float( - file, TOTP_CONFIG_KEY_TIMEZONE, &new_timezone_offset, 1)) { - update_result = TotpConfigFileUpdateError; - break; - } - - update_result = TotpConfigFileUpdateSuccess; - } while(false); - - totp_close_config_file(file); - } else { - update_result = TotpConfigFileUpdateError; - } - - totp_close_storage(); - return update_result; -} - -TotpConfigFileUpdateResult - totp_config_file_update_notification_method(NotificationMethod new_notification_method) { - Storage* cfg_storage = totp_open_storage(); - FlipperFormat* file; - TotpConfigFileUpdateResult update_result; - - if(totp_open_config_file(cfg_storage, &file) == TotpConfigFileOpenSuccess) { - do { - uint32_t tmp_uint32 = new_notification_method; - if(!flipper_format_insert_or_update_uint32( - file, TOTP_CONFIG_KEY_NOTIFICATION_METHOD, &tmp_uint32, 1)) { - update_result = TotpConfigFileUpdateError; - break; - } - - update_result = TotpConfigFileUpdateSuccess; - } while(false); - - totp_close_config_file(file); - } else { - update_result = TotpConfigFileUpdateError; - } - - totp_close_storage(); - return update_result; -} - -TotpConfigFileUpdateResult - totp_config_file_update_automation_method(AutomationMethod new_automation_method) { - Storage* cfg_storage = totp_open_storage(); - FlipperFormat* file; - TotpConfigFileUpdateResult update_result; - - if(totp_open_config_file(cfg_storage, &file) == TotpConfigFileOpenSuccess) { - do { - uint32_t tmp_uint32 = new_automation_method; - if(!flipper_format_insert_or_update_uint32( - file, TOTP_CONFIG_KEY_AUTOMATION_METHOD, &tmp_uint32, 1)) { - update_result = TotpConfigFileUpdateError; - break; - } - - update_result = TotpConfigFileUpdateSuccess; - } while(false); - - totp_close_config_file(file); - } else { - update_result = TotpConfigFileUpdateError; - } - - totp_close_storage(); - return update_result; -} - -TotpConfigFileUpdateResult totp_config_file_update_user_settings(const PluginState* plugin_state) { - Storage* cfg_storage = totp_open_storage(); - FlipperFormat* file; - TotpConfigFileUpdateResult update_result; - if(totp_open_config_file(cfg_storage, &file) == TotpConfigFileOpenSuccess) { - do { - if(!flipper_format_insert_or_update_float( - file, TOTP_CONFIG_KEY_TIMEZONE, &plugin_state->timezone_offset, 1)) { - update_result = TotpConfigFileUpdateError; - break; - } - uint32_t tmp_uint32 = plugin_state->notification_method; - if(!flipper_format_insert_or_update_uint32( - file, TOTP_CONFIG_KEY_NOTIFICATION_METHOD, &tmp_uint32, 1)) { - update_result = TotpConfigFileUpdateError; - break; - } - - tmp_uint32 = plugin_state->automation_method; - if(!flipper_format_insert_or_update_uint32( - file, TOTP_CONFIG_KEY_AUTOMATION_METHOD, &tmp_uint32, 1)) { - update_result = TotpConfigFileUpdateError; - break; - } - - update_result = TotpConfigFileUpdateSuccess; - } while(false); - - totp_close_config_file(file); - } else { - update_result = TotpConfigFileUpdateError; - } - - totp_close_storage(); - return update_result; -} - -TotpConfigFileUpdateResult totp_full_save_config_file(const PluginState* const plugin_state) { - Storage* storage = totp_open_storage(); - FlipperFormat* fff_data_file = flipper_format_file_alloc(storage); - TotpConfigFileUpdateResult result = TotpConfigFileUpdateSuccess; +bool totp_config_file_update_notification_method(const PluginState* plugin_state) { + FlipperFormat* file = plugin_state->config_file_context->config_file; + flipper_format_rewind(file); + bool update_result = false; do { - if(!flipper_format_file_open_always(fff_data_file, CONFIG_FILE_TEMP_PATH)) { - result = TotpConfigFileUpdateError; + uint32_t tmp_uint32 = plugin_state->notification_method; + if(!flipper_format_insert_or_update_uint32( + file, TOTP_CONFIG_KEY_NOTIFICATION_METHOD, &tmp_uint32, 1)) { break; } - if(!flipper_format_write_header_cstr( - fff_data_file, CONFIG_FILE_HEADER, CONFIG_FILE_ACTUAL_VERSION)) { - result = TotpConfigFileUpdateError; + update_result = true; + } while(false); + + return update_result; +} + +bool totp_config_file_update_automation_method(const PluginState* plugin_state) { + FlipperFormat* file = plugin_state->config_file_context->config_file; + flipper_format_rewind(file); + bool update_result = false; + + do { + uint32_t tmp_uint32 = plugin_state->automation_method; + if(!flipper_format_insert_or_update_uint32( + file, TOTP_CONFIG_KEY_AUTOMATION_METHOD, &tmp_uint32, 1)) { break; } - if(!flipper_format_write_hex( - fff_data_file, TOTP_CONFIG_KEY_BASE_IV, &plugin_state->base_iv[0], TOTP_IV_SIZE)) { - result = TotpConfigFileUpdateError; - break; - } + update_result = true; + } while(false); - if(!flipper_format_write_hex( - fff_data_file, - TOTP_CONFIG_KEY_CRYPTO_VERIFY, - plugin_state->crypto_verify_data, - plugin_state->crypto_verify_data_length)) { - result = TotpConfigFileUpdateError; - break; - } + return update_result; +} - if(!flipper_format_write_float( - fff_data_file, TOTP_CONFIG_KEY_TIMEZONE, &plugin_state->timezone_offset, 1)) { - result = TotpConfigFileUpdateError; - break; - } - - if(!flipper_format_write_bool( - fff_data_file, TOTP_CONFIG_KEY_PINSET, &plugin_state->pin_set, 1)) { - result = TotpConfigFileUpdateError; +bool totp_config_file_update_user_settings(const PluginState* plugin_state) { + FlipperFormat* file = plugin_state->config_file_context->config_file; + flipper_format_rewind(file); + bool update_result = false; + do { + if(!flipper_format_insert_or_update_float( + file, TOTP_CONFIG_KEY_TIMEZONE, &plugin_state->timezone_offset, 1)) { break; } uint32_t tmp_uint32 = plugin_state->notification_method; - if(!flipper_format_write_uint32( - fff_data_file, TOTP_CONFIG_KEY_NOTIFICATION_METHOD, &tmp_uint32, 1)) { - result = TotpConfigFileUpdateError; + if(!flipper_format_insert_or_update_uint32( + file, TOTP_CONFIG_KEY_NOTIFICATION_METHOD, &tmp_uint32, 1)) { break; } tmp_uint32 = plugin_state->automation_method; - if(!flipper_format_write_uint32( - fff_data_file, TOTP_CONFIG_KEY_AUTOMATION_METHOD, &tmp_uint32, 1)) { - result = TotpConfigFileUpdateError; + if(!flipper_format_insert_or_update_uint32( + file, TOTP_CONFIG_KEY_AUTOMATION_METHOD, &tmp_uint32, 1)) { break; } - bool tokens_written = true; - TOTP_LIST_FOREACH(plugin_state->tokens_list, node, { - const TokenInfo* token_info = node->data; - tokens_written = tokens_written && - totp_config_file_save_new_token_i(fff_data_file, token_info) == - TotpConfigFileUpdateSuccess; - }); - - if(!tokens_written) { - result = TotpConfigFileUpdateError; - break; - } + update_result = true; } while(false); - totp_close_config_file(fff_data_file); - - if(result == TotpConfigFileUpdateSuccess) { - if(storage_file_exists(storage, CONFIG_FILE_ORIG_PATH)) { - storage_simply_remove(storage, CONFIG_FILE_ORIG_PATH); - } - - if(storage_common_rename(storage, CONFIG_FILE_PATH, CONFIG_FILE_ORIG_PATH) != FSE_OK) { - result = TotpConfigFileUpdateError; - } else if(storage_common_rename(storage, CONFIG_FILE_TEMP_PATH, CONFIG_FILE_PATH) != FSE_OK) { - result = TotpConfigFileUpdateError; - } else if(!storage_simply_remove(storage, CONFIG_FILE_ORIG_PATH)) { - result = TotpConfigFileUpdateError; - } - } - - totp_close_storage(); - return result; + return update_result; } -TotpConfigFileOpenResult totp_config_file_load_base(PluginState* const plugin_state) { +bool totp_config_file_load(PluginState* const plugin_state) { Storage* storage = totp_open_storage(); FlipperFormat* fff_data_file; - - TotpConfigFileOpenResult result; - if((result = totp_open_config_file(storage, &fff_data_file)) != TotpConfigFileOpenSuccess) { + if(!totp_open_config_file(storage, &fff_data_file)) { totp_close_storage(); - return result; + return false; } + flipper_format_rewind(fff_data_file); + + bool result = false; + plugin_state->timezone_offset = 0; FuriString* temp_str = furi_string_alloc(); @@ -535,7 +306,6 @@ TotpConfigFileOpenResult totp_config_file_load_base(PluginState* const plugin_st uint32_t file_version; if(!flipper_format_read_header(fff_data_file, temp_str, &file_version)) { FURI_LOG_E(LOGGING_TAG, "Missing or incorrect header"); - result = TotpConfigFileOpenError; break; } @@ -551,8 +321,7 @@ TotpConfigFileOpenResult totp_config_file_load_base(PluginState* const plugin_st char* backup_path = totp_config_file_backup_i(storage); if(backup_path != NULL) { - if(totp_open_config_file(storage, &fff_data_file) != TotpConfigFileOpenSuccess) { - result = TotpConfigFileOpenError; + if(totp_open_config_file(storage, &fff_data_file) != true) { break; } @@ -560,7 +329,6 @@ TotpConfigFileOpenResult totp_config_file_load_base(PluginState* const plugin_st if(!flipper_format_file_open_existing(fff_backup_data_file, backup_path)) { flipper_format_file_close(fff_backup_data_file); flipper_format_free(fff_backup_data_file); - result = TotpConfigFileOpenError; break; } @@ -575,7 +343,6 @@ TotpConfigFileOpenResult totp_config_file_load_base(PluginState* const plugin_st LOGGING_TAG, "An error occurred during migration to version %" PRId16, CONFIG_FILE_ACTUAL_VERSION); - result = TotpConfigFileOpenError; break; } @@ -588,7 +355,6 @@ TotpConfigFileOpenResult totp_config_file_load_base(PluginState* const plugin_st LOGGING_TAG, "An error occurred during taking backup of %s before migration", CONFIG_FILE_PATH); - result = TotpConfigFileOpenError; break; } } @@ -599,7 +365,6 @@ TotpConfigFileOpenResult totp_config_file_load_base(PluginState* const plugin_st } if(!flipper_format_rewind(fff_data_file)) { - result = TotpConfigFileOpenError; break; } @@ -626,7 +391,6 @@ TotpConfigFileOpenResult totp_config_file_load_base(PluginState* const plugin_st } if(!flipper_format_rewind(fff_data_file)) { - result = TotpConfigFileOpenError; break; } @@ -637,7 +401,6 @@ TotpConfigFileOpenResult totp_config_file_load_base(PluginState* const plugin_st } if(!flipper_format_rewind(fff_data_file)) { - result = TotpConfigFileOpenError; break; } @@ -664,186 +427,176 @@ TotpConfigFileOpenResult totp_config_file_load_base(PluginState* const plugin_st } plugin_state->automation_method = tmp_uint32; + + plugin_state->config_file_context = malloc(sizeof(ConfigFileContext)); + furi_check(plugin_state->config_file_context != NULL); + plugin_state->config_file_context->storage = storage; + plugin_state->config_file_context->config_file = fff_data_file; + plugin_state->config_file_context->token_info_iterator_context = + totp_token_info_iterator_alloc( + storage, plugin_state->config_file_context->config_file, plugin_state->iv); + result = true; } while(false); furi_string_free(temp_str); - totp_close_config_file(fff_data_file); - totp_close_storage(); return result; } -TokenLoadingResult totp_config_file_load_tokens(PluginState* const plugin_state) { - Storage* storage = totp_open_storage(); - FlipperFormat* fff_data_file; - if(totp_open_config_file(storage, &fff_data_file) != TotpConfigFileOpenSuccess) { - totp_close_storage(); - return TokenLoadingResultError; - } - - FuriString* temp_str = furi_string_alloc(); - uint32_t temp_data32; - - if(!flipper_format_read_header(fff_data_file, temp_str, &temp_data32)) { - FURI_LOG_E(LOGGING_TAG, "Missing or incorrect header"); - totp_close_storage(); - furi_string_free(temp_str); - return TokenLoadingResultError; - } - - TokenLoadingResult result = TokenLoadingResultSuccess; - uint16_t index = 0; - bool has_any_plain_secret = false; - - while(true) { - if(!flipper_format_read_string(fff_data_file, TOTP_CONFIG_KEY_TOKEN_NAME, temp_str)) { +bool totp_config_file_update_crypto_signatures(const PluginState* plugin_state) { + FlipperFormat* config_file = plugin_state->config_file_context->config_file; + flipper_format_rewind(config_file); + bool update_result = false; + do { + if(!flipper_format_insert_or_update_hex( + config_file, TOTP_CONFIG_KEY_BASE_IV, plugin_state->base_iv, TOTP_IV_SIZE)) { break; } - TokenInfo* tokenInfo = token_info_alloc(); - - size_t temp_cstr_len = furi_string_size(temp_str); - tokenInfo->name = malloc(temp_cstr_len + 1); - furi_check(tokenInfo->name != NULL); - strlcpy(tokenInfo->name, furi_string_get_cstr(temp_str), temp_cstr_len + 1); - - uint32_t secret_bytes_count; - if(!flipper_format_get_value_count( - fff_data_file, TOTP_CONFIG_KEY_TOKEN_SECRET, &secret_bytes_count)) { - secret_bytes_count = 0; + if(!flipper_format_insert_or_update_hex( + config_file, + TOTP_CONFIG_KEY_CRYPTO_VERIFY, + plugin_state->crypto_verify_data, + plugin_state->crypto_verify_data_length)) { + break; } - if(secret_bytes_count == 1) { // Plain secret key - if(flipper_format_read_string(fff_data_file, TOTP_CONFIG_KEY_TOKEN_SECRET, temp_str)) { - if(token_info_set_secret( - tokenInfo, - furi_string_get_cstr(temp_str), - furi_string_size(temp_str), - PLAIN_TOKEN_ENCODING_BASE32, - &plugin_state->iv[0])) { - FURI_LOG_W(LOGGING_TAG, "Token \"%s\" has plain secret", tokenInfo->name); - } else { - tokenInfo->token = NULL; - tokenInfo->token_length = 0; - FURI_LOG_W(LOGGING_TAG, "Token \"%s\" has invalid secret", tokenInfo->name); - result = TokenLoadingResultWarning; - } - } else { - tokenInfo->token = NULL; - tokenInfo->token_length = 0; - result = TokenLoadingResultWarning; - } - - has_any_plain_secret = true; - } else { // encrypted - tokenInfo->token_length = secret_bytes_count; - if(secret_bytes_count > 0) { - tokenInfo->token = malloc(tokenInfo->token_length); - furi_check(tokenInfo->token != NULL); - if(!flipper_format_read_hex( - fff_data_file, - TOTP_CONFIG_KEY_TOKEN_SECRET, - tokenInfo->token, - tokenInfo->token_length)) { - free(tokenInfo->token); - tokenInfo->token = NULL; - tokenInfo->token_length = 0; - result = TokenLoadingResultWarning; - } - } else { - tokenInfo->token = NULL; - result = TokenLoadingResultWarning; - } + if(!flipper_format_insert_or_update_bool( + config_file, TOTP_CONFIG_KEY_PINSET, &plugin_state->pin_set, 1)) { + break; } - if(!flipper_format_read_string(fff_data_file, TOTP_CONFIG_KEY_TOKEN_ALGO, temp_str) || - !token_info_set_algo_from_str(tokenInfo, temp_str)) { - tokenInfo->algo = SHA1; - } + update_result = true; + } while(false); - if(!flipper_format_read_uint32( - fff_data_file, TOTP_CONFIG_KEY_TOKEN_DIGITS, &temp_data32, 1) || - !token_info_set_digits_from_int(tokenInfo, temp_data32)) { - tokenInfo->digits = TOTP_6_DIGITS; - } - - if(!flipper_format_read_uint32( - fff_data_file, TOTP_CONFIG_KEY_TOKEN_DURATION, &temp_data32, 1) || - !token_info_set_duration_from_int(tokenInfo, temp_data32)) { - tokenInfo->duration = TOTP_TOKEN_DURATION_DEFAULT; - } - - if(flipper_format_read_uint32( - fff_data_file, TOTP_CONFIG_KEY_TOKEN_AUTOMATION_FEATURES, &temp_data32, 1)) { - tokenInfo->automation_features = temp_data32; - } else { - tokenInfo->automation_features = TOKEN_AUTOMATION_FEATURE_NONE; - } - - FURI_LOG_D(LOGGING_TAG, "Found token \"%s\"", tokenInfo->name); - - TOTP_LIST_INIT_OR_ADD(plugin_state->tokens_list, tokenInfo, furi_check); - - index++; - } - - plugin_state->tokens_count = index; - plugin_state->token_list_loaded = true; - - FURI_LOG_D(LOGGING_TAG, "Found %" PRIu16 " tokens", index); - - furi_string_free(temp_str); - totp_close_config_file(fff_data_file); - totp_close_storage(); - - if(has_any_plain_secret) { - totp_full_save_config_file(plugin_state); - } - - return result; -} - -TotpConfigFileUpdateResult - totp_config_file_update_crypto_signatures(const PluginState* plugin_state) { - Storage* storage = totp_open_storage(); - FlipperFormat* config_file; - TotpConfigFileUpdateResult update_result; - if(totp_open_config_file(storage, &config_file) == TotpConfigFileOpenSuccess) { - do { - if(!flipper_format_insert_or_update_hex( - config_file, TOTP_CONFIG_KEY_BASE_IV, plugin_state->base_iv, TOTP_IV_SIZE)) { - update_result = TotpConfigFileUpdateError; - break; - } - - if(!flipper_format_insert_or_update_hex( - config_file, - TOTP_CONFIG_KEY_CRYPTO_VERIFY, - plugin_state->crypto_verify_data, - plugin_state->crypto_verify_data_length)) { - update_result = TotpConfigFileUpdateError; - break; - } - - if(!flipper_format_insert_or_update_bool( - config_file, TOTP_CONFIG_KEY_PINSET, &plugin_state->pin_set, 1)) { - update_result = TotpConfigFileUpdateError; - break; - } - - update_result = TotpConfigFileUpdateSuccess; - } while(false); - - totp_close_config_file(config_file); - } else { - update_result = TotpConfigFileUpdateError; - } - - totp_close_storage(); return update_result; } -void totp_config_file_reset() { +void totp_config_file_close(PluginState* const plugin_state) { + if(plugin_state->config_file_context == NULL) return; + totp_token_info_iterator_free(plugin_state->config_file_context->token_info_iterator_context); + totp_close_config_file(plugin_state->config_file_context->config_file); + free(plugin_state->config_file_context); + plugin_state->config_file_context = NULL; + totp_close_storage(); +} + +void totp_config_file_reset(PluginState* const plugin_state) { + totp_config_file_close(plugin_state); Storage* storage = totp_open_storage(); storage_simply_remove(storage, CONFIG_FILE_PATH); totp_close_storage(); } + +bool totp_config_file_update_encryption( + PluginState* plugin_state, + const uint8_t* new_pin, + uint8_t new_pin_length) { + FlipperFormat* config_file = plugin_state->config_file_context->config_file; + Stream* stream = flipper_format_get_raw_stream(config_file); + size_t original_offset = stream_tell(stream); + if(!stream_rewind(stream)) { + return false; + } + + uint8_t old_iv[TOTP_IV_SIZE]; + memcpy(&old_iv[0], &plugin_state->iv[0], TOTP_IV_SIZE); + + memset(&plugin_state->iv[0], 0, TOTP_IV_SIZE); + memset(&plugin_state->base_iv[0], 0, TOTP_IV_SIZE); + if(plugin_state->crypto_verify_data != NULL) { + free(plugin_state->crypto_verify_data); + plugin_state->crypto_verify_data = NULL; + } + + CryptoSeedIVResult seed_result = + totp_crypto_seed_iv(plugin_state, new_pin_length > 0 ? new_pin : NULL, new_pin_length); + if(seed_result & CryptoSeedIVResultFlagSuccess && + seed_result & CryptoSeedIVResultFlagNewCryptoVerifyData) { + if(!totp_config_file_update_crypto_signatures(plugin_state)) { + return false; + } + } else if(seed_result == CryptoSeedIVResultFailed) { + return false; + } + + char buffer[sizeof(TOTP_CONFIG_KEY_TOKEN_SECRET) + 1]; + bool result = true; + + while(true) { + if(!stream_seek_to_char(stream, '\n', StreamDirectionForward)) { + break; + } + + size_t buffer_read_size; + if((buffer_read_size = stream_read(stream, (uint8_t*)&buffer[0], sizeof(buffer))) == 0) { + break; + } + + if(!stream_seek(stream, -(int32_t)buffer_read_size, StreamOffsetFromCurrent)) { + result = false; + break; + } + + if(strncmp(buffer, "\n" TOTP_CONFIG_KEY_TOKEN_SECRET ":", sizeof(buffer)) == 0) { + uint32_t secret_bytes_count; + if(!flipper_format_get_value_count( + config_file, TOTP_CONFIG_KEY_TOKEN_SECRET, &secret_bytes_count)) { + secret_bytes_count = 0; + } + + if(secret_bytes_count > 1) { + size_t secret_token_start = stream_tell(stream) + 1; + uint8_t* encrypted_token = malloc(secret_bytes_count); + furi_check(encrypted_token != NULL); + + if(!flipper_format_read_hex( + config_file, + TOTP_CONFIG_KEY_TOKEN_SECRET, + encrypted_token, + secret_bytes_count)) { + result = false; + free(encrypted_token); + break; + } + + size_t plain_token_length; + uint8_t* plain_token = totp_crypto_decrypt( + encrypted_token, secret_bytes_count, &old_iv[0], &plain_token_length); + + free(encrypted_token); + size_t encrypted_token_length; + encrypted_token = totp_crypto_encrypt( + plain_token, plain_token_length, &plugin_state->iv[0], &encrypted_token_length); + + memset_s(plain_token, plain_token_length, 0, plain_token_length); + free(plain_token); + + if(!stream_seek(stream, secret_token_start, StreamOffsetFromStart)) { + result = false; + free(encrypted_token); + break; + } + + if(!flipper_format_write_hex( + config_file, + TOTP_CONFIG_KEY_TOKEN_SECRET, + encrypted_token, + encrypted_token_length)) { + free(encrypted_token); + result = false; + break; + } + + free(encrypted_token); + } + } + } + + stream_seek(stream, original_offset, StreamOffsetFromStart); + + return result; +} + +TokenInfoIteratorContext* totp_config_get_token_iterator_context(const PluginState* plugin_state) { + return plugin_state->config_file_context->token_info_iterator_context; +} diff --git a/applications/external/totp/services/config/config.h b/applications/external/totp/services/config/config.h index dabeb373a..d2fe957c6 100644 --- a/applications/external/totp/services/config/config.h +++ b/applications/external/totp/services/config/config.h @@ -1,138 +1,90 @@ #pragma once -#include -#include #include "../../types/plugin_state.h" #include "../../types/token_info.h" +#include "config_file_context.h" #include "constants.h" +#include "token_info_iterator.h" -typedef uint8_t TokenLoadingResult; typedef uint8_t TotpConfigFileOpenResult; typedef uint8_t TotpConfigFileUpdateResult; -/** - * @brief Token loading results - */ -enum TokenLoadingResults { - /** - * @brief All the tokens loaded successfully - */ - TokenLoadingResultSuccess, - - /** - * @brief All the tokens loaded, but there are some warnings - */ - TokenLoadingResultWarning, - - /** - * @brief Tokens not loaded because of error(s) - */ - TokenLoadingResultError -}; - -/** - * @brief Config file opening result - */ -enum TotpConfigFileOpenResults { - /** - * @brief Config file opened successfully - */ - TotpConfigFileOpenSuccess = 0, - - /** - * @brief An error has occurred during opening config file - */ - TotpConfigFileOpenError = 1 -}; - -/** - * @brief Config file updating result - */ -enum TotpConfigFileUpdateResults { - /** - * @brief Config file updated successfully - */ - TotpConfigFileUpdateSuccess, - - /** - * @brief An error has occurred during updating config file - */ - TotpConfigFileUpdateError -}; - /** * @brief Tries to take a config file backup + * @param plugin_state application state * @return backup path if backup successfully taken; \c NULL otherwise */ -char* totp_config_file_backup(); - -/** - * @brief Saves all the settings and tokens to an application config file - * @param plugin_state application state - * @return Config file update result - */ -TotpConfigFileUpdateResult totp_full_save_config_file(const PluginState* const plugin_state); +char* totp_config_file_backup(const PluginState* plugin_state); /** * @brief Loads basic information from an application config file into application state without loading all the tokens * @param plugin_state application state * @return Config file open result */ -TotpConfigFileOpenResult totp_config_file_load_base(PluginState* const plugin_state); - -/** - * @brief Loads tokens from an application config file into application state - * @param plugin_state application state - * @return Results of the loading - */ -TokenLoadingResult totp_config_file_load_tokens(PluginState* const plugin_state); - -/** - * @brief Add new token to the end of the application config file - * @param token_info token information to be saved - * @return Config file update result - */ -TotpConfigFileUpdateResult totp_config_file_save_new_token(const TokenInfo* token_info); +bool totp_config_file_load(PluginState* const plugin_state); /** * @brief Updates timezone offset in an application config file - * @param new_timezone_offset new timezone offset to be set + * @param plugin_state application state * @return Config file update result */ -TotpConfigFileUpdateResult totp_config_file_update_timezone_offset(float new_timezone_offset); +bool totp_config_file_update_timezone_offset(const PluginState* plugin_state); /** * @brief Updates notification method in an application config file - * @param new_notification_method new notification method to be set + * @param plugin_state application state * @return Config file update result */ -TotpConfigFileUpdateResult - totp_config_file_update_notification_method(NotificationMethod new_notification_method); +bool totp_config_file_update_notification_method(const PluginState* plugin_state); /** * @brief Updates automation method in an application config file - * @param new_automation_method new automation method to be set + * @param plugin_state application state * @return Config file update result */ -TotpConfigFileUpdateResult - totp_config_file_update_automation_method(AutomationMethod new_automation_method); +bool totp_config_file_update_automation_method(const PluginState* plugin_state); /** * @brief Updates application user settings * @param plugin_state application state * @return Config file update result */ -TotpConfigFileUpdateResult totp_config_file_update_user_settings(const PluginState* plugin_state); +bool totp_config_file_update_user_settings(const PluginState* plugin_state); /** * @brief Updates crypto signatures information * @param plugin_state application state * @return Config file update result */ -TotpConfigFileUpdateResult - totp_config_file_update_crypto_signatures(const PluginState* plugin_state); +bool totp_config_file_update_crypto_signatures(const PluginState* plugin_state); /** * @brief Reset all the settings to default + * @param plugin_state application state */ -void totp_config_file_reset(); \ No newline at end of file +void totp_config_file_reset(PluginState* const plugin_state); + +/** + * @brief Closes config file and releases all the resources + * @param plugin_state application state + */ +void totp_config_file_close(PluginState* const plugin_state); + +/** + * @brief Updates config file encryption by re-encrypting it using new user's PIN and new randomly generated IV + * @param plugin_state application state + * @param new_pin new user's PIN + * @param new_pin_length new user's PIN length + * @return \c true if config file encryption successfully updated; \c false otherwise + */ +bool totp_config_file_update_encryption( + PluginState* plugin_state, + const uint8_t* new_pin, + uint8_t new_pin_length); + +/** + * @brief Gets token info iterator context + * @param plugin_state application state + * @return token info iterator context + */ +TokenInfoIteratorContext* totp_config_get_token_iterator_context(const PluginState* plugin_state); \ No newline at end of file diff --git a/applications/external/totp/services/config/config_file_context.h b/applications/external/totp/services/config/config_file_context.h new file mode 100644 index 000000000..98badbcbb --- /dev/null +++ b/applications/external/totp/services/config/config_file_context.h @@ -0,0 +1,3 @@ +#pragma once + +typedef struct ConfigFileContext ConfigFileContext; \ No newline at end of file diff --git a/applications/external/totp/services/config/constants.h b/applications/external/totp/services/config/constants.h index 7137e2374..3a33c80b3 100644 --- a/applications/external/totp/services/config/constants.h +++ b/applications/external/totp/services/config/constants.h @@ -1,7 +1,10 @@ #pragma once +#include + +#define CONFIG_FILE_DIRECTORY_PATH EXT_PATH("authenticator") #define CONFIG_FILE_HEADER "Flipper TOTP plugin config file" -#define CONFIG_FILE_ACTUAL_VERSION (4) +#define CONFIG_FILE_ACTUAL_VERSION (5) #define TOTP_CONFIG_KEY_TIMEZONE "Timezone" #define TOTP_CONFIG_KEY_TOKEN_NAME "TokenName" diff --git a/applications/external/totp/services/config/migrations/common_migration.c b/applications/external/totp/services/config/migrations/common_migration.c index 073eaab12..07026fb1f 100644 --- a/applications/external/totp/services/config/migrations/common_migration.c +++ b/applications/external/totp/services/config/migrations/common_migration.c @@ -1,6 +1,7 @@ #include "common_migration.h" #include "../constants.h" #include "../../../types/token_info.h" +#include bool totp_config_migrate_to_latest( FlipperFormat* fff_data_file, @@ -57,18 +58,12 @@ bool totp_config_migrate_to_latest( flipper_format_rewind(fff_backup_data_file); - FuriString* comment_str = furi_string_alloc(); - while(true) { if(!flipper_format_read_string( fff_backup_data_file, TOTP_CONFIG_KEY_TOKEN_NAME, temp_str)) { break; } - furi_string_printf( - comment_str, "=== BEGIN \"%s\" ===", furi_string_get_cstr(temp_str)); - flipper_format_write_comment(fff_data_file, comment_str); - furi_string_printf(comment_str, "=== END \"%s\" ===", furi_string_get_cstr(temp_str)); flipper_format_write_string(fff_data_file, TOTP_CONFIG_KEY_TOKEN_NAME, temp_str); flipper_format_read_string( @@ -78,15 +73,32 @@ bool totp_config_migrate_to_latest( if(current_version > 1) { flipper_format_read_string( fff_backup_data_file, TOTP_CONFIG_KEY_TOKEN_ALGO, temp_str); - flipper_format_write_string(fff_data_file, TOTP_CONFIG_KEY_TOKEN_ALGO, temp_str); + + if(current_version < 5) { + uint32_t algo_as_uint32t = SHA1; + if(furi_string_cmpi_str(temp_str, TOTP_TOKEN_ALGO_SHA256_NAME) == 0) { + algo_as_uint32t = SHA256; + } else if(furi_string_cmpi_str(temp_str, TOTP_TOKEN_ALGO_SHA512_NAME) == 0) { + algo_as_uint32t = SHA512; + } else if(furi_string_cmpi_str(temp_str, TOTP_TOKEN_ALGO_STEAM_NAME) == 0) { + algo_as_uint32t = STEAM; + } + + flipper_format_write_uint32( + fff_data_file, TOTP_CONFIG_KEY_TOKEN_ALGO, &algo_as_uint32t, 1); + } else { + flipper_format_write_string( + fff_data_file, TOTP_CONFIG_KEY_TOKEN_ALGO, temp_str); + } flipper_format_read_string( fff_backup_data_file, TOTP_CONFIG_KEY_TOKEN_DIGITS, temp_str); flipper_format_write_string(fff_data_file, TOTP_CONFIG_KEY_TOKEN_DIGITS, temp_str); } else { - flipper_format_write_string_cstr( - fff_data_file, TOTP_CONFIG_KEY_TOKEN_ALGO, TOTP_TOKEN_ALGO_SHA1_NAME); - const uint32_t default_digits = TOTP_6_DIGITS; + const uint32_t default_algo = SHA1; + flipper_format_write_uint32( + fff_data_file, TOTP_CONFIG_KEY_TOKEN_ALGO, &default_algo, 1); + const uint32_t default_digits = TotpSixDigitsCount; flipper_format_write_uint32( fff_data_file, TOTP_CONFIG_KEY_TOKEN_DIGITS, &default_digits, 1); } @@ -108,18 +120,21 @@ bool totp_config_migrate_to_latest( flipper_format_write_string( fff_data_file, TOTP_CONFIG_KEY_TOKEN_AUTOMATION_FEATURES, temp_str); } else { - const uint32_t default_automation_features = TOKEN_AUTOMATION_FEATURE_NONE; + const uint32_t default_automation_features = TokenAutomationFeatureNone; flipper_format_write_uint32( fff_data_file, TOTP_CONFIG_KEY_TOKEN_AUTOMATION_FEATURES, &default_automation_features, 1); } - - flipper_format_write_comment(fff_data_file, comment_str); } - furi_string_free(comment_str); + Stream* stream = flipper_format_get_raw_stream(fff_data_file); + size_t current_pos = stream_tell(stream); + size_t total_size = stream_size(stream); + if(current_pos < total_size) { + stream_delete(stream, total_size - current_pos); + } result = true; } while(false); diff --git a/applications/external/totp/services/config/migrations/common_migration.h b/applications/external/totp/services/config/migrations/common_migration.h index 71defc384..326277f14 100644 --- a/applications/external/totp/services/config/migrations/common_migration.h +++ b/applications/external/totp/services/config/migrations/common_migration.h @@ -2,6 +2,12 @@ #include +/** + * @brief Migrates config file to the latest version + * @param fff_data_file original config file to be migrated + * @param fff_backup_data_file backup copy of original config file + * @return \c true if operation succeeded; \c false otherwise + */ bool totp_config_migrate_to_latest( FlipperFormat* fff_data_file, - FlipperFormat* fff_backup_data_file); \ No newline at end of file + FlipperFormat* fff_backup_data_file); diff --git a/applications/external/totp/services/config/token_info_iterator.c b/applications/external/totp/services/config/token_info_iterator.c new file mode 100644 index 000000000..f8cd3c64e --- /dev/null +++ b/applications/external/totp/services/config/token_info_iterator.c @@ -0,0 +1,547 @@ +#include "token_info_iterator.h" + +#include +#include +#include +#include "../../types/common.h" + +#define CONFIG_FILE_PART_FILE_PATH CONFIG_FILE_DIRECTORY_PATH "/totp.conf.part" +#define STREAM_COPY_BUFFER_SIZE 128 + +struct TokenInfoIteratorContext { + size_t total_count; + size_t current_index; + size_t last_seek_offset; + size_t last_seek_index; + TokenInfo* current_token; + FlipperFormat* config_file; + uint8_t* iv; + Storage* storage; +}; + +static bool + flipper_format_seek_to_siblinig_token_start(Stream* stream, StreamDirection direction) { + char buffer[sizeof(TOTP_CONFIG_KEY_TOKEN_NAME) + 1]; + bool found = false; + while(!found) { + if(!stream_seek_to_char(stream, '\n', direction)) { + break; + } + + size_t buffer_read_size; + if((buffer_read_size = stream_read(stream, (uint8_t*)&buffer[0], sizeof(buffer))) == 0) { + break; + } + + if(!stream_seek(stream, -(int32_t)buffer_read_size, StreamOffsetFromCurrent)) { + break; + } + + if(strncmp(buffer, "\n" TOTP_CONFIG_KEY_TOKEN_NAME ":", sizeof(buffer)) == 0) { + found = true; + } + } + + return found; +} + +static bool seek_to_token(size_t token_index, TokenInfoIteratorContext* context) { + furi_check(context != NULL && context->config_file != NULL); + if(token_index >= context->total_count) { + return false; + } + + Stream* stream = flipper_format_get_raw_stream(context->config_file); + long token_index_diff = (long)token_index - (long)context->last_seek_index; + size_t token_index_diff_weight = (size_t)labs(token_index_diff); + StreamDirection direction = token_index_diff >= 0 ? StreamDirectionForward : + StreamDirectionBackward; + if(token_index_diff_weight > token_index || context->last_seek_offset == 0) { + context->last_seek_offset = 0; + context->last_seek_index = 0; + token_index_diff = token_index + 1; + direction = StreamDirectionForward; + } else if(token_index_diff_weight > (context->total_count - token_index - 1)) { + context->last_seek_offset = stream_size(stream); + context->last_seek_index = context->total_count - 1; + token_index_diff = -(long)(context->total_count - token_index); + direction = StreamDirectionBackward; + } + + if(!stream_seek(stream, context->last_seek_offset, StreamOffsetFromStart)) { + return false; + } + + if(token_index_diff != 0) { + long i = 0; + long i_inc = token_index_diff >= 0 ? 1 : -1; + do { + if(!flipper_format_seek_to_siblinig_token_start(stream, direction)) { + break; + } + + i += i_inc; + } while((i_inc > 0 && i < token_index_diff) || (i_inc < 0 && i > token_index_diff)); + + if((i_inc > 0 && i < token_index_diff) || (i_inc < 0 && i > token_index_diff)) { + context->last_seek_offset = 0; + FURI_LOG_D(LOGGING_TAG, "Was not able to move"); + return false; + } + + context->last_seek_offset = stream_tell(stream); + context->last_seek_index = token_index; + } + + return true; +} + +static bool stream_insert_stream(Stream* dst, Stream* src) { + uint8_t buffer[STREAM_COPY_BUFFER_SIZE]; + size_t buffer_read_size; + while((buffer_read_size = stream_read(src, buffer, sizeof(buffer))) != 0) { + if(!stream_insert(dst, buffer, buffer_read_size)) { + return false; + } + } + + return true; +} + +static bool ensure_stream_ends_with_lf(Stream* stream) { + uint8_t last_char; + size_t original_pos = stream_tell(stream); + if(!stream_seek(stream, -1, StreamOffsetFromEnd) || stream_read(stream, &last_char, 1) < 1) { + return false; + } + + const uint8_t lf = '\n'; + if(last_char != lf && !stream_write(stream, &lf, 1)) { + return false; + } + + if(!stream_seek(stream, original_pos, StreamOffsetFromStart)) { + return false; + } + + return true; +} + +static bool + totp_token_info_iterator_save_current_token_info_changes(TokenInfoIteratorContext* context) { + bool is_new_token = context->current_index >= context->total_count; + Stream* stream = flipper_format_get_raw_stream(context->config_file); + if(is_new_token) { + if(!ensure_stream_ends_with_lf(stream) || + !flipper_format_seek_to_end(context->config_file)) { + return false; + } + } else { + if(!seek_to_token(context->current_index, context)) { + return false; + } + } + + size_t offset_start = stream_tell(stream); + + size_t offset_end; + if(is_new_token) { + offset_end = offset_start; + } else if(context->current_index + 1 >= context->total_count) { + offset_end = stream_size(stream); + } else if(seek_to_token(context->current_index + 1, context)) { + offset_end = stream_tell(stream); + } else { + return false; + } + + FlipperFormat* temp_ff = flipper_format_file_alloc(context->storage); + if(!flipper_format_file_open_always(temp_ff, CONFIG_FILE_PART_FILE_PATH)) { + flipper_format_free(temp_ff); + return false; + } + + TokenInfo* token_info = context->current_token; + bool result = false; + + do { + if(!flipper_format_write_string(temp_ff, TOTP_CONFIG_KEY_TOKEN_NAME, token_info->name)) { + break; + } + + if(!flipper_format_write_hex( + temp_ff, + TOTP_CONFIG_KEY_TOKEN_SECRET, + token_info->token, + token_info->token_length)) { + break; + } + + uint32_t tmp_uint32 = token_info->algo; + if(!flipper_format_write_uint32(temp_ff, TOTP_CONFIG_KEY_TOKEN_ALGO, &tmp_uint32, 1)) { + break; + } + + tmp_uint32 = token_info->digits; + if(!flipper_format_write_uint32(temp_ff, TOTP_CONFIG_KEY_TOKEN_DIGITS, &tmp_uint32, 1)) { + break; + } + + tmp_uint32 = token_info->duration; + if(!flipper_format_write_uint32(temp_ff, TOTP_CONFIG_KEY_TOKEN_DURATION, &tmp_uint32, 1)) { + break; + } + + tmp_uint32 = token_info->automation_features; + if(!flipper_format_write_uint32( + temp_ff, TOTP_CONFIG_KEY_TOKEN_AUTOMATION_FEATURES, &tmp_uint32, 1)) { + break; + } + + Stream* temp_stream = flipper_format_get_raw_stream(temp_ff); + + if(!stream_rewind(temp_stream)) { + break; + } + + if(!stream_seek(stream, offset_start, StreamOffsetFromStart)) { + break; + } + + if(offset_end != offset_start && !stream_delete(stream, offset_end - offset_start)) { + break; + } + + if(!is_new_token && !stream_write_char(stream, '\n')) { + break; + } + + if(!stream_insert_stream(stream, temp_stream)) { + break; + } + + if(is_new_token) { + context->total_count++; + } + + result = true; + } while(false); + + flipper_format_free(temp_ff); + storage_common_remove(context->storage, CONFIG_FILE_PART_FILE_PATH); + + stream_seek(stream, offset_start, StreamOffsetFromStart); + context->last_seek_offset = offset_start; + context->last_seek_index = context->current_index; + + return result; +} + +TokenInfoIteratorContext* + totp_token_info_iterator_alloc(Storage* storage, FlipperFormat* config_file, uint8_t* iv) { + Stream* stream = flipper_format_get_raw_stream(config_file); + stream_rewind(stream); + size_t tokens_count = 0; + while(true) { + if(!flipper_format_seek_to_siblinig_token_start(stream, StreamDirectionForward)) { + break; + } + + tokens_count++; + } + + TokenInfoIteratorContext* context = malloc(sizeof(TokenInfoIteratorContext)); + furi_check(context != NULL); + + context->total_count = tokens_count; + context->current_token = token_info_alloc(); + context->config_file = config_file; + context->iv = iv; + context->storage = storage; + return context; +} + +void totp_token_info_iterator_free(TokenInfoIteratorContext* context) { + if(context == NULL) return; + token_info_free(context->current_token); + free(context); +} + +bool totp_token_info_iterator_remove_current_token_info(TokenInfoIteratorContext* context) { + if(!seek_to_token(context->current_index, context)) { + return false; + } + + Stream* stream = flipper_format_get_raw_stream(context->config_file); + size_t begin_offset = stream_tell(stream); + size_t end_offset; + if(!ensure_stream_ends_with_lf(stream)) { + return false; + } + + if(context->current_index >= context->total_count - 1) { + end_offset = stream_size(stream) - 1; + } else if(seek_to_token(context->current_index + 1, context)) { + end_offset = stream_tell(stream); + } else { + return false; + } + + if(!stream_seek(stream, begin_offset, StreamOffsetFromStart) || + !stream_delete(stream, end_offset - begin_offset)) { + return false; + } + + context->total_count--; + if(context->current_index >= context->total_count) { + context->current_index = context->total_count - 1; + } + + return true; +} + +bool totp_token_info_iterator_move_current_token_info( + TokenInfoIteratorContext* context, + size_t new_index) { + if(context->current_index == new_index) return true; + + Stream* stream = flipper_format_get_raw_stream(context->config_file); + + if(!ensure_stream_ends_with_lf(stream)) { + return false; + } + + if(!seek_to_token(context->current_index, context)) { + return false; + } + + size_t begin_offset = stream_tell(stream); + size_t end_offset; + if(context->current_index >= context->total_count - 1) { + end_offset = stream_size(stream) - 1; + } else if(seek_to_token(context->current_index + 1, context)) { + end_offset = stream_tell(stream); + } else { + return false; + } + + Stream* temp_stream = file_stream_alloc(context->storage); + if(!file_stream_open( + temp_stream, CONFIG_FILE_PART_FILE_PATH, FSAM_READ_WRITE, FSOM_CREATE_ALWAYS)) { + stream_free(temp_stream); + return false; + } + + size_t moving_size = end_offset - begin_offset; + + bool result = false; + do { + if(!stream_seek(stream, begin_offset, StreamOffsetFromStart)) { + break; + } + + if(stream_copy(stream, temp_stream, moving_size) < moving_size) { + break; + } + + if(!stream_rewind(temp_stream)) { + break; + } + + if(!stream_seek(stream, begin_offset, StreamOffsetFromStart)) { + break; + } + + if(!stream_delete(stream, moving_size)) { + break; + } + + context->last_seek_offset = 0; + context->last_seek_index = 0; + if(new_index >= context->total_count - 1) { + if(!stream_seek(stream, stream_size(stream) - 1, StreamOffsetFromStart)) { + break; + } + } else if(!seek_to_token(new_index, context)) { + break; + } + + result = stream_insert_stream(stream, temp_stream); + } while(false); + + stream_free(temp_stream); + storage_common_remove(context->storage, CONFIG_FILE_PART_FILE_PATH); + + context->last_seek_offset = 0; + context->last_seek_index = 0; + + return result; +} + +TotpIteratorUpdateTokenResult totp_token_info_iterator_update_current_token( + TokenInfoIteratorContext* context, + TOTP_ITERATOR_UPDATE_TOKEN_ACTION update, + const void* update_context) { + TotpIteratorUpdateTokenResult result = update(context->current_token, update_context); + if(result == TotpIteratorUpdateTokenResultSuccess) { + if(!totp_token_info_iterator_save_current_token_info_changes(context)) { + result = TotpIteratorUpdateTokenResultFileUpdateFailed; + } + + return result; + } + + totp_token_info_iterator_go_to(context, context->current_index); + return result; +} + +TotpIteratorUpdateTokenResult totp_token_info_iterator_add_new_token( + TokenInfoIteratorContext* context, + TOTP_ITERATOR_UPDATE_TOKEN_ACTION update, + const void* update_context) { + size_t previous_index = context->current_index; + context->current_index = context->total_count; + token_info_set_defaults(context->current_token); + TotpIteratorUpdateTokenResult result = update(context->current_token, update_context); + if(result == TotpIteratorUpdateTokenResultSuccess && + !totp_token_info_iterator_save_current_token_info_changes(context)) { + result = TotpIteratorUpdateTokenResultFileUpdateFailed; + } + + if(result != TotpIteratorUpdateTokenResultSuccess) { + totp_token_info_iterator_go_to(context, previous_index); + } + + return result; +} + +bool totp_token_info_iterator_go_to(TokenInfoIteratorContext* context, size_t token_index) { + furi_check(context != NULL); + context->current_index = token_index; + if(!seek_to_token(context->current_index, context)) { + return false; + } + + Stream* stream = flipper_format_get_raw_stream(context->config_file); + size_t original_offset = stream_tell(stream); + + if(!flipper_format_read_string( + context->config_file, TOTP_CONFIG_KEY_TOKEN_NAME, context->current_token->name)) { + stream_seek(stream, original_offset, StreamOffsetFromStart); + return false; + } + + uint32_t secret_bytes_count; + if(!flipper_format_get_value_count( + context->config_file, TOTP_CONFIG_KEY_TOKEN_SECRET, &secret_bytes_count)) { + secret_bytes_count = 0; + } + TokenInfo* tokenInfo = context->current_token; + bool token_update_needed = false; + if(tokenInfo->token != NULL) { + free(tokenInfo->token); + tokenInfo->token_length = 0; + } + + if(secret_bytes_count == 1) { // Plain secret key + FuriString* temp_str = furi_string_alloc(); + + if(flipper_format_read_string( + context->config_file, TOTP_CONFIG_KEY_TOKEN_SECRET, temp_str)) { + if(token_info_set_secret( + tokenInfo, + furi_string_get_cstr(temp_str), + furi_string_size(temp_str), + PlainTokenSecretEncodingBase32, + context->iv)) { + FURI_LOG_W( + LOGGING_TAG, + "Token \"%s\" has plain secret", + furi_string_get_cstr(tokenInfo->name)); + token_update_needed = true; + } else { + tokenInfo->token = NULL; + tokenInfo->token_length = 0; + FURI_LOG_W( + LOGGING_TAG, + "Token \"%s\" has invalid secret", + furi_string_get_cstr(tokenInfo->name)); + } + } else { + tokenInfo->token = NULL; + tokenInfo->token_length = 0; + } + + furi_string_free(temp_str); + } else { // encrypted + tokenInfo->token_length = secret_bytes_count; + if(secret_bytes_count > 0) { + tokenInfo->token = malloc(tokenInfo->token_length); + furi_check(tokenInfo->token != NULL); + if(!flipper_format_read_hex( + context->config_file, + TOTP_CONFIG_KEY_TOKEN_SECRET, + tokenInfo->token, + tokenInfo->token_length)) { + free(tokenInfo->token); + tokenInfo->token = NULL; + tokenInfo->token_length = 0; + } + } else { + tokenInfo->token = NULL; + } + } + + uint32_t temp_data32; + if(!flipper_format_read_uint32( + context->config_file, TOTP_CONFIG_KEY_TOKEN_ALGO, &temp_data32, 1) || + !token_info_set_algo_from_int(tokenInfo, temp_data32)) { + tokenInfo->algo = SHA1; + } + + if(!flipper_format_read_uint32( + context->config_file, TOTP_CONFIG_KEY_TOKEN_DIGITS, &temp_data32, 1) || + !token_info_set_digits_from_int(tokenInfo, temp_data32)) { + tokenInfo->digits = TotpSixDigitsCount; + } + + if(!flipper_format_read_uint32( + context->config_file, TOTP_CONFIG_KEY_TOKEN_DURATION, &temp_data32, 1) || + !token_info_set_duration_from_int(tokenInfo, temp_data32)) { + tokenInfo->duration = TOTP_TOKEN_DURATION_DEFAULT; + } + + if(flipper_format_read_uint32( + context->config_file, TOTP_CONFIG_KEY_TOKEN_AUTOMATION_FEATURES, &temp_data32, 1)) { + tokenInfo->automation_features = temp_data32; + } else { + tokenInfo->automation_features = TokenAutomationFeatureNone; + } + + stream_seek(stream, original_offset, StreamOffsetFromStart); + + if(token_update_needed && !totp_token_info_iterator_save_current_token_info_changes(context)) { + return false; + } + + return true; +} + +const TokenInfo* + totp_token_info_iterator_get_current_token(const TokenInfoIteratorContext* context) { + return context->current_token; +} + +size_t totp_token_info_iterator_get_current_token_index(const TokenInfoIteratorContext* context) { + return context->current_index; +} + +size_t totp_token_info_iterator_get_total_count(const TokenInfoIteratorContext* context) { + return context->total_count; +} + +void totp_token_info_iterator_attach_to_config_file( + TokenInfoIteratorContext* context, + FlipperFormat* config_file) { + context->config_file = config_file; +} \ No newline at end of file diff --git a/applications/external/totp/services/config/token_info_iterator.h b/applications/external/totp/services/config/token_info_iterator.h new file mode 100644 index 000000000..7e9a65853 --- /dev/null +++ b/applications/external/totp/services/config/token_info_iterator.h @@ -0,0 +1,121 @@ +#pragma once + +#include "../../types/token_info.h" +#include +#include "constants.h" + +typedef int TotpIteratorUpdateTokenResult; + +typedef TotpIteratorUpdateTokenResult ( + *TOTP_ITERATOR_UPDATE_TOKEN_ACTION)(TokenInfo* const token_info, const void* context); + +typedef struct TokenInfoIteratorContext TokenInfoIteratorContext; + +enum TotpIteratorUpdateTokenResults { + + /** + * @brief Token successfully updated + */ + TotpIteratorUpdateTokenResultSuccess = 0, + + /** + * @brief An error ocurred during updating config file + */ + TotpIteratorUpdateTokenResultFileUpdateFailed = -1 +}; + +/** + * @brief Initializes a new token info iterator + * @param storage storage reference + * @param config_file config file to use + * @param iv initialization vector (IV) to be used for encryption\decryption + * @return Token info iterator context + */ +TokenInfoIteratorContext* + totp_token_info_iterator_alloc(Storage* storage, FlipperFormat* config_file, uint8_t* iv); + +/** + * @brief Navigates iterator to the token with given index + * @param context token info iterator context + * @param token_index token index to navigate to + * @return \c true if navigation succeeded; \c false otherwise + */ +bool totp_token_info_iterator_go_to(TokenInfoIteratorContext* context, size_t token_index); + +/** + * @brief Moves current token to a given new index + * @param context token info iterator context + * @param new_index new token index to move current token to + * @return \c true if operation succeeded; \c false otherwise + */ +bool totp_token_info_iterator_move_current_token_info( + TokenInfoIteratorContext* context, + size_t new_index); + +/** + * @brief Updates current token info using given update action + * @param context token info iterator context + * @param update action which is responsible to make all the necessary updates to token info + * @param update_context update action context + * @return \c true if operation succeeded; \c false otherwise + */ +TotpIteratorUpdateTokenResult totp_token_info_iterator_update_current_token( + TokenInfoIteratorContext* context, + TOTP_ITERATOR_UPDATE_TOKEN_ACTION update, + const void* update_context); + +/** + * @brief Adds new token info to the end of the list using given update action + * @param context token info iterator context + * @param update action which is responsible to make all the necessary updates to token info + * @param update_context update action context + * @return \c true if operation succeeded; \c false otherwise + */ +TotpIteratorUpdateTokenResult totp_token_info_iterator_add_new_token( + TokenInfoIteratorContext* context, + TOTP_ITERATOR_UPDATE_TOKEN_ACTION update, + const void* update_context); + +/** + * @brief Remvoves current token info + * @param context token info iterator context + * @return \c true if operation succeeded; \c false otherwise + */ +bool totp_token_info_iterator_remove_current_token_info(TokenInfoIteratorContext* context); + +/** + * @brief Disposes token info iterator and releases all the resources + * @param context token info iterator context + */ +void totp_token_info_iterator_free(TokenInfoIteratorContext* context); + +/** + * @brief Gets current token info + * @param context token info iterator context + * @return current token info + */ +const TokenInfo* + totp_token_info_iterator_get_current_token(const TokenInfoIteratorContext* context); + +/** + * @brief Gets current token info index + * @param context token info iterator context + * @return current token info index + */ +size_t totp_token_info_iterator_get_current_token_index(const TokenInfoIteratorContext* context); + +/** + * @brief Gets total amount of token infos found + * @param context token info iterator context + * @return amount of token infos found + */ +size_t totp_token_info_iterator_get_total_count(const TokenInfoIteratorContext* context); + +/** + * @brief Attaches token info iterator to another config file + * @param context token info iterator context + * @param config_file config file reference to attach token info iterator to + */ +void totp_token_info_iterator_attach_to_config_file( + TokenInfoIteratorContext* context, + FlipperFormat* config_file); diff --git a/applications/external/totp/services/crypto/crypto.c b/applications/external/totp/services/crypto/crypto.c index 55fb1df6a..03d9c9d51 100644 --- a/applications/external/totp/services/crypto/crypto.c +++ b/applications/external/totp/services/crypto/crypto.c @@ -1,7 +1,7 @@ #include "crypto.h" -#include -#include -#include "../config/config.h" +#include +#include +#include #include "../../types/common.h" #include "memset_s.h" @@ -61,9 +61,11 @@ uint8_t* totp_crypto_decrypt( return decrypted_data; } -bool totp_crypto_seed_iv(PluginState* plugin_state, const uint8_t* pin, uint8_t pin_length) { +CryptoSeedIVResult + totp_crypto_seed_iv(PluginState* plugin_state, const uint8_t* pin, uint8_t pin_length) { + CryptoSeedIVResult result; if(plugin_state->crypto_verify_data == NULL) { - FURI_LOG_D(LOGGING_TAG, "Generating new IV"); + FURI_LOG_I(LOGGING_TAG, "Generating new IV"); furi_hal_random_fill_buf(&plugin_state->base_iv[0], TOTP_IV_SIZE); } @@ -94,9 +96,9 @@ bool totp_crypto_seed_iv(PluginState* plugin_state, const uint8_t* pin, uint8_t } } - bool result = true; + result = CryptoSeedIVResultFlagSuccess; if(plugin_state->crypto_verify_data == NULL) { - FURI_LOG_D(LOGGING_TAG, "Generating crypto verify data"); + FURI_LOG_I(LOGGING_TAG, "Generating crypto verify data"); plugin_state->crypto_verify_data = malloc(CRYPTO_VERIFY_KEY_LENGTH); furi_check(plugin_state->crypto_verify_data != NULL); plugin_state->crypto_verify_data_length = CRYPTO_VERIFY_KEY_LENGTH; @@ -109,8 +111,7 @@ bool totp_crypto_seed_iv(PluginState* plugin_state, const uint8_t* pin, uint8_t plugin_state->pin_set = pin != NULL && pin_length > 0; - result = totp_config_file_update_crypto_signatures(plugin_state) == - TotpConfigFileUpdateSuccess; + result |= CryptoSeedIVResultFlagNewCryptoVerifyData; } return result; diff --git a/applications/external/totp/services/crypto/crypto.h b/applications/external/totp/services/crypto/crypto.h index 3442b9a6e..ab27191a8 100644 --- a/applications/external/totp/services/crypto/crypto.h +++ b/applications/external/totp/services/crypto/crypto.h @@ -2,6 +2,26 @@ #include "../../types/plugin_state.h" +typedef uint8_t CryptoSeedIVResult; + +enum CryptoSeedIVResults { + + /** + * @brief IV seeding operation failed + */ + CryptoSeedIVResultFailed = 0b00, + + /** + * @brief IV seeding operation succeeded + */ + CryptoSeedIVResultFlagSuccess = 0b01, + + /** + * @brief As a part of IV seeding operation new crypto verify data has been generated + */ + CryptoSeedIVResultFlagNewCryptoVerifyData = 0b10 +}; + /** * @brief Encrypts plain data using built-in certificate and given initialization vector (IV) * @param plain_data plain data to be encrypted @@ -35,9 +55,10 @@ uint8_t* totp_crypto_decrypt( * @param plugin_state application state * @param pin user's PIN * @param pin_length user's PIN length - * @return \c true on success; \c false otherwise + * @return Results of seeding IV */ -bool totp_crypto_seed_iv(PluginState* plugin_state, const uint8_t* pin, uint8_t pin_length); +CryptoSeedIVResult + totp_crypto_seed_iv(PluginState* plugin_state, const uint8_t* pin, uint8_t pin_length); /** * @brief Verifies whether cryptographic information (certificate + IV) is valid and can be used for encryption and decryption diff --git a/applications/external/totp/services/hmac/hmac_common.h b/applications/external/totp/services/hmac/hmac_common.h index 0cd56ed99..3499cb800 100644 --- a/applications/external/totp/services/hmac/hmac_common.h +++ b/applications/external/totp/services/hmac/hmac_common.h @@ -1,5 +1,4 @@ #include -#include "sha256.h" #include "memxor.h" #define IPAD 0x36 diff --git a/applications/external/totp/services/hmac/hmac_sha256.c b/applications/external/totp/services/hmac/hmac_sha256.c index c51f24b4d..00ac2a177 100644 --- a/applications/external/totp/services/hmac/hmac_sha256.c +++ b/applications/external/totp/services/hmac/hmac_sha256.c @@ -15,6 +15,7 @@ along with this program. If not, see . */ #include "hmac_sha256.h" +#include "sha256.h" #define GL_HMAC_NAME 256 #define GL_HMAC_BLOCKSIZE 64 diff --git a/applications/external/totp/services/hmac/sha1.c b/applications/external/totp/services/hmac/sha1.c index ecf22fc97..29f22e3c3 100644 --- a/applications/external/totp/services/hmac/sha1.c +++ b/applications/external/totp/services/hmac/sha1.c @@ -27,6 +27,8 @@ #include #include +#include "sha_pad_buffer.h" + #ifdef WORDS_BIGENDIAN #define SWAP(n) (n) #else @@ -34,10 +36,6 @@ #define SWAP(n) swap_uint32(n) #endif -/* This array contains the bytes used to pad the buffer to the next - 64-byte boundary. (RFC 1321, 3.1: Step 1) */ -static const unsigned char fillbuf[64] = {0x80, 0 /* , 0, 0, ... */}; - /* Take a pointer to a 160 bit block of data (five 32 bit ints) and initialize it to the start constants of the SHA1 algorithm. This must be called before using hash in the call to sha1_hash. */ @@ -87,7 +85,7 @@ void* sha1_finish_ctx(struct sha1_ctx* ctx, void* resbuf) { ctx->buffer[size - 2] = SWAP((ctx->total[1] << 3) | (ctx->total[0] >> 29)); ctx->buffer[size - 1] = SWAP(ctx->total[0] << 3); - memcpy(&((char*)ctx->buffer)[bytes], fillbuf, (size - 2) * 4 - bytes); + sha_pad_buffer(&((uint8_t*)ctx->buffer)[bytes], (size - 2) * 4 - bytes); /* Process last bytes. */ sha1_process_block(ctx->buffer, size * 4, ctx); diff --git a/applications/external/totp/services/hmac/sha256.c b/applications/external/totp/services/hmac/sha256.c index 89ca67c2b..09ba272e7 100644 --- a/applications/external/totp/services/hmac/sha256.c +++ b/applications/external/totp/services/hmac/sha256.c @@ -25,6 +25,7 @@ #include #include +#include "sha_pad_buffer.h" #ifdef WORDS_BIGENDIAN #define SWAP(n) (n) @@ -33,10 +34,6 @@ #define SWAP(n) swap_uint32(n) #endif -/* This array contains the bytes used to pad the buffer to the next - 64-byte boundary. */ -static const unsigned char fillbuf[64] = {0x80, 0 /* , 0, 0, ... */}; - /* Takes a pointer to a 256 bit block of data (eight 32 bit ints) and initializes it to the start constants of the SHA256 algorithm. This @@ -91,7 +88,7 @@ static void sha256_conclude_ctx(struct sha256_ctx* ctx) { set_uint32((char*)&ctx->buffer[size - 2], SWAP((ctx->total[1] << 3) | (ctx->total[0] >> 29))); set_uint32((char*)&ctx->buffer[size - 1], SWAP(ctx->total[0] << 3)); - memcpy(&((char*)ctx->buffer)[bytes], fillbuf, (size - 2) * 4 - bytes); + sha_pad_buffer(&((uint8_t*)ctx->buffer)[bytes], (size - 2) * 4 - bytes); /* Process last bytes. */ sha256_process_block(ctx->buffer, size * 4, ctx); diff --git a/applications/external/totp/services/hmac/sha512.c b/applications/external/totp/services/hmac/sha512.c index b56dd0f2e..ffe2864fb 100644 --- a/applications/external/totp/services/hmac/sha512.c +++ b/applications/external/totp/services/hmac/sha512.c @@ -27,13 +27,10 @@ #include #include "byteswap.h" +#include "sha_pad_buffer.h" #define SWAP(n) swap_uint64(n) -/* This array contains the bytes used to pad the buffer to the next - 128-byte boundary. */ -static const unsigned char fillbuf[128] = {0x80, 0 /* , 0, 0, ... */}; - /* Takes a pointer to a 512 bit block of data (eight 64 bit ints) and initializes it to the start constants of the SHA512 algorithm. This @@ -90,7 +87,7 @@ static void sha512_conclude_ctx(struct sha512_ctx* ctx) { SWAP(u64or(u64shl(ctx->total[1], 3), u64shr(ctx->total[0], 61)))); set_uint64((char*)&ctx->buffer[size - 1], SWAP(u64shl(ctx->total[0], 3))); - memcpy(&((char*)ctx->buffer)[bytes], fillbuf, (size - 2) * 8 - bytes); + sha_pad_buffer(&((uint8_t*)ctx->buffer)[bytes], (size - 2) * 8 - bytes); /* Process last bytes. */ sha512_process_block(ctx->buffer, size * 8, ctx); diff --git a/applications/external/totp/services/hmac/sha_pad_buffer.c b/applications/external/totp/services/hmac/sha_pad_buffer.c new file mode 100644 index 000000000..badedbcc7 --- /dev/null +++ b/applications/external/totp/services/hmac/sha_pad_buffer.c @@ -0,0 +1,11 @@ +#include "sha_pad_buffer.h" +#include + +void sha_pad_buffer(uint8_t* buffer, size_t size) { + if(size > 0) { + buffer[0] = 0x80; + if(size > 1) { + memset(&buffer[1], 0, size - 1); + } + } +} \ No newline at end of file diff --git a/applications/external/totp/services/hmac/sha_pad_buffer.h b/applications/external/totp/services/hmac/sha_pad_buffer.h new file mode 100644 index 000000000..7dba40fa9 --- /dev/null +++ b/applications/external/totp/services/hmac/sha_pad_buffer.h @@ -0,0 +1,4 @@ +#include +#include + +void sha_pad_buffer(uint8_t* buffer, size_t size); \ No newline at end of file diff --git a/applications/external/totp/services/totp/totp.c b/applications/external/totp/services/totp/totp.c index f6e0401e6..45a283b06 100644 --- a/applications/external/totp/services/totp/totp.c +++ b/applications/external/totp/services/totp/totp.c @@ -1,15 +1,13 @@ #include "totp.h" -#include #include #include -#include #include +#include #include "../hmac/hmac_sha1.h" #include "../hmac/hmac_sha256.h" #include "../hmac/hmac_sha512.h" #include "../hmac/byteswap.h" -#include "../../lib/timezone_utils/timezone_utils.h" #define HMAC_MAX_RESULT_SIZE HMAC_SHA512_RESULT_SIZE diff --git a/applications/external/totp/totp_app.c b/applications/external/totp/totp_app.c index 8a014628d..4e0bbbd25 100644 --- a/applications/external/totp/totp_app.c +++ b/applications/external/totp/totp_app.c @@ -1,10 +1,7 @@ -#include -#include #include #include #include #include -#include #include #include #include "features_config.h" @@ -53,23 +50,39 @@ static bool totp_activate_initial_scene(PluginState* const plugin_state) { dialog_message_show(plugin_state->dialogs_app, message); dialog_message_free(message); if(dialog_result == DialogMessageButtonRight) { - totp_scene_director_activate_scene(plugin_state, TotpSceneAuthentication, NULL); + totp_scene_director_activate_scene(plugin_state, TotpSceneAuthentication); } else { - if(!totp_crypto_seed_iv(plugin_state, NULL, 0)) { + CryptoSeedIVResult seed_result = totp_crypto_seed_iv(plugin_state, NULL, 0); + if(seed_result & CryptoSeedIVResultFlagSuccess && + seed_result & CryptoSeedIVResultFlagNewCryptoVerifyData) { + if(!totp_config_file_update_crypto_signatures(plugin_state)) { + totp_dialogs_config_loading_error(plugin_state); + return false; + } + } else if(seed_result == CryptoSeedIVResultFailed) { totp_dialogs_config_loading_error(plugin_state); return false; } - totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL); + + totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken); } } else if(plugin_state->pin_set) { - totp_scene_director_activate_scene(plugin_state, TotpSceneAuthentication, NULL); + totp_scene_director_activate_scene(plugin_state, TotpSceneAuthentication); } else { - if(!totp_crypto_seed_iv(plugin_state, NULL, 0)) { + CryptoSeedIVResult seed_result = totp_crypto_seed_iv(plugin_state, NULL, 0); + if(seed_result & CryptoSeedIVResultFlagSuccess && + seed_result & CryptoSeedIVResultFlagNewCryptoVerifyData) { + if(!totp_config_file_update_crypto_signatures(plugin_state)) { + totp_dialogs_config_loading_error(plugin_state); + return false; + } + } else if(seed_result == CryptoSeedIVResultFailed) { totp_dialogs_config_loading_error(plugin_state); return false; } + if(totp_crypto_verify_key(plugin_state)) { - totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL); + totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken); } else { FURI_LOG_E( LOGGING_TAG, @@ -98,16 +111,12 @@ static bool totp_plugin_state_init(PluginState* const plugin_state) { plugin_state->dialogs_app = furi_record_open(RECORD_DIALOGS); memset(&plugin_state->iv[0], 0, TOTP_IV_SIZE); - if(totp_config_file_load_base(plugin_state) != TotpConfigFileOpenSuccess) { + if(!totp_config_file_load(plugin_state)) { totp_dialogs_config_loading_error(plugin_state); return false; } plugin_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); - if(plugin_state->mutex == NULL) { - FURI_LOG_E(LOGGING_TAG, "Cannot create mutex\r\n"); - return false; - } #ifdef TOTP_BADBT_TYPE_ENABLED if(plugin_state->automation_method & AutomationMethodBadBt) { @@ -125,15 +134,7 @@ static void totp_plugin_state_free(PluginState* plugin_state) { furi_record_close(RECORD_NOTIFICATION); furi_record_close(RECORD_DIALOGS); - ListNode* node = plugin_state->tokens_list; - ListNode* tmp; - while(node != NULL) { - tmp = node->next; - TokenInfo* tokenInfo = node->data; - token_info_free(tokenInfo); - free(node); - node = tmp; - } + totp_config_file_close(plugin_state); if(plugin_state->crypto_verify_data != NULL) { free(plugin_state->crypto_verify_data); @@ -196,8 +197,9 @@ int32_t totp_app() { } } 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, NULL); + totp_scene_director_activate_scene(plugin_state, TotpSceneAuthentication); } view_port_update(view_port); diff --git a/applications/external/totp/types/common.c b/applications/external/totp/types/common.c new file mode 100644 index 000000000..ec5eb3ebd --- /dev/null +++ b/applications/external/totp/types/common.c @@ -0,0 +1,3 @@ +#include "common.h" + +const char* LOGGING_TAG = "TOTP APP"; \ No newline at end of file diff --git a/applications/external/totp/types/common.h b/applications/external/totp/types/common.h index 2c6d6b293..737adb82d 100644 --- a/applications/external/totp/types/common.h +++ b/applications/external/totp/types/common.h @@ -1,3 +1,3 @@ #pragma once -#define LOGGING_TAG "TOTP APP" +extern const char* LOGGING_TAG; diff --git a/applications/external/totp/types/nullable.h b/applications/external/totp/types/nullable.h deleted file mode 100644 index 4f9b7bc01..000000000 --- a/applications/external/totp/types/nullable.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include -#include - -#define TOTP_NULLABLE_STRUCT(value_type) \ - typedef struct TotpNullable_##value_type { \ - bool is_null; \ - value_type value; \ - } TotpNullable_##value_type - -#define TOTP_NULLABLE_NULL(s) s.is_null = true -#define TOTP_NULLABLE_VALUE(s, v) \ - s.is_null = false; \ - s.value = v - -TOTP_NULLABLE_STRUCT(uint16_t); diff --git a/applications/external/totp/types/plugin_state.h b/applications/external/totp/types/plugin_state.h index cacf68426..c20594f37 100644 --- a/applications/external/totp/types/plugin_state.h +++ b/applications/external/totp/types/plugin_state.h @@ -4,8 +4,8 @@ #include #include #include "../features_config.h" -#include #include "../ui/totp_scenes_enum.h" +#include "../services/config/config_file_context.h" #include "notification_method.h" #include "automation_method.h" #ifdef TOTP_BADBT_TYPE_ENABLED @@ -48,20 +48,7 @@ typedef struct { */ float timezone_offset; - /** - * @brief Token list head node - */ - ListNode* tokens_list; - - /** - * @brief Whether token list is loaded or not - */ - bool token_list_loaded; - - /** - * @brief Tokens list length - */ - uint16_t tokens_count; + ConfigFileContext* config_file_context; /** * @brief Encrypted well-known string data diff --git a/applications/external/totp/types/token_info.c b/applications/external/totp/types/token_info.c index b8196c56b..6810d0211 100644 --- a/applications/external/totp/types/token_info.c +++ b/applications/external/totp/types/token_info.c @@ -1,26 +1,22 @@ #include "token_info.h" -#include #include #include #include -#include #include "common.h" #include "../services/crypto/crypto.h" TokenInfo* token_info_alloc() { TokenInfo* tokenInfo = malloc(sizeof(TokenInfo)); furi_check(tokenInfo != NULL); - tokenInfo->algo = SHA1; - tokenInfo->digits = TOTP_6_DIGITS; - tokenInfo->duration = TOTP_TOKEN_DURATION_DEFAULT; - tokenInfo->automation_features = TOKEN_AUTOMATION_FEATURE_NONE; + tokenInfo->name = furi_string_alloc(); + token_info_set_defaults(tokenInfo); return tokenInfo; } void token_info_free(TokenInfo* token_info) { if(token_info == NULL) return; - free(token_info->name); free(token_info->token); + furi_string_free(token_info->name); free(token_info); } @@ -34,13 +30,13 @@ bool token_info_set_secret( uint8_t* plain_secret; size_t plain_secret_length; size_t plain_secret_size; - if(plain_token_secret_encoding == PLAIN_TOKEN_ENCODING_BASE32) { + if(plain_token_secret_encoding == PlainTokenSecretEncodingBase32) { plain_secret_size = token_secret_length; plain_secret = malloc(plain_secret_size); furi_check(plain_secret != NULL); plain_secret_length = base32_decode((const uint8_t*)plain_token_secret, plain_secret, plain_secret_size); - } else if(plain_token_secret_encoding == PLAIN_TOKEN_ENCODING_BASE64) { + } else if(plain_token_secret_encoding == PlainTokenSecretEncodingBase64) { plain_secret_length = 0; plain_secret = base64_decode( (const uint8_t*)plain_token_secret, @@ -54,6 +50,10 @@ bool token_info_set_secret( bool result; if(plain_secret_length > 0) { + if(token_info->token != NULL) { + free(token_info->token); + } + token_info->token = totp_crypto_encrypt(plain_secret, plain_secret_length, iv, &token_info->token_length); result = true; @@ -69,13 +69,13 @@ bool token_info_set_secret( bool token_info_set_digits_from_int(TokenInfo* token_info, uint8_t digits) { switch(digits) { case 5: - token_info->digits = TOTP_5_DIGITS; + token_info->digits = TotpFiveDigitsCount; return true; case 6: - token_info->digits = TOTP_6_DIGITS; + token_info->digits = TotpSixDigitsCount; return true; case 8: - token_info->digits = TOTP_8_DIGITS; + token_info->digits = TotpEightDigitsCount; return true; default: break; @@ -117,6 +117,27 @@ bool token_info_set_algo_from_str(TokenInfo* token_info, const FuriString* str) return false; } +bool token_info_set_algo_from_int(TokenInfo* token_info, uint8_t algo_code) { + switch(algo_code) { + case SHA1: + token_info->algo = SHA1; + break; + case SHA256: + token_info->algo = SHA256; + break; + case SHA512: + token_info->algo = SHA512; + break; + case STEAM: + token_info->algo = STEAM; + break; + default: + return false; + } + + return true; +} + char* token_info_get_algo_as_cstr(const TokenInfo* token_info) { switch(token_info->algo) { case SHA1: @@ -136,22 +157,22 @@ char* token_info_get_algo_as_cstr(const TokenInfo* token_info) { bool token_info_set_automation_feature_from_str(TokenInfo* token_info, const FuriString* str) { if(furi_string_cmpi_str(str, TOTP_TOKEN_AUTOMATION_FEATURE_ENTER_AT_THE_END_NAME) == 0) { - token_info->automation_features |= TOKEN_AUTOMATION_FEATURE_ENTER_AT_THE_END; + token_info->automation_features |= TokenAutomationFeatureEnterAtTheEnd; return true; } if(furi_string_cmpi_str(str, TOTP_TOKEN_AUTOMATION_FEATURE_TAB_AT_THE_END_NAME) == 0) { - token_info->automation_features |= TOKEN_AUTOMATION_FEATURE_TAB_AT_THE_END; + token_info->automation_features |= TokenAutomationFeatureTabAtTheEnd; return true; } if(furi_string_cmpi_str(str, TOTP_TOKEN_AUTOMATION_FEATURE_TYPE_SLOWER_NAME) == 0) { - token_info->automation_features |= TOKEN_AUTOMATION_FEATURE_TYPE_SLOWER; + token_info->automation_features |= TokenAutomationFeatureTypeSlower; return true; } if(furi_string_cmpi_str(str, TOTP_TOKEN_AUTOMATION_FEATURE_NONE_NAME) == 0) { - token_info->automation_features = TOKEN_AUTOMATION_FEATURE_NONE; + token_info->automation_features = TokenAutomationFeatureNone; return true; } @@ -166,10 +187,17 @@ TokenInfo* token_info_clone(const TokenInfo* src) { furi_check(clone->token != NULL); memcpy(clone->token, src->token, src->token_length); - int name_length = strnlen(src->name, TOTP_TOKEN_MAX_LENGTH); - clone->name = malloc(name_length + 1); - furi_check(clone->name != NULL); - strlcpy(clone->name, src->name, name_length + 1); + clone->name = furi_string_alloc(); + furi_string_set(clone->name, src->name); return clone; +} + +void token_info_set_defaults(TokenInfo* token_info) { + furi_check(token_info != NULL); + token_info->algo = SHA1; + token_info->digits = TotpSixDigitsCount; + token_info->duration = TOTP_TOKEN_DURATION_DEFAULT; + token_info->automation_features = TokenAutomationFeatureNone; + furi_string_reset(token_info->name); } \ No newline at end of file diff --git a/applications/external/totp/types/token_info.h b/applications/external/totp/types/token_info.h index 9ca3528dc..0d73dd061 100644 --- a/applications/external/totp/types/token_info.h +++ b/applications/external/totp/types/token_info.h @@ -2,7 +2,7 @@ #include #include -#include +#include #define TOTP_TOKEN_DURATION_DEFAULT (30) @@ -20,6 +20,8 @@ #define TOTP_TOKEN_AUTOMATION_FEATURE_TAB_AT_THE_END_NAME "tab" #define TOTP_TOKEN_AUTOMATION_FEATURE_TYPE_SLOWER_NAME "slower" +#define TOTP_TOKEN_DIGITS_MAX_COUNT (8) + typedef uint8_t TokenHashAlgo; typedef uint8_t TokenDigitsCount; typedef uint8_t TokenAutomationFeature; @@ -32,22 +34,22 @@ enum TokenHashAlgos { /** * @brief SHA1 hashing algorithm */ - SHA1, + SHA1 = 0, /** * @brief SHA256 hashing algorithm */ - SHA256, + SHA256 = 1, /** * @brief SHA512 hashing algorithm */ - SHA512, + SHA512 = 2, /** * @brief Algorithm used by Steam (Valve) */ - STEAM + STEAM = 3 }; /** @@ -55,19 +57,19 @@ enum TokenHashAlgos { */ enum TokenDigitsCounts { /** - * @brief 6 digits + * @brief 5 digits */ - TOTP_5_DIGITS = 5, + TotpFiveDigitsCount = 5, /** * @brief 6 digits */ - TOTP_6_DIGITS = 6, + TotpSixDigitsCount = 6, /** * @brief 8 digits */ - TOTP_8_DIGITS = 8 + TotpEightDigitsCount = 8 }; /** @@ -77,22 +79,22 @@ enum TokenAutomationFeatures { /** * @brief No features enabled */ - TOKEN_AUTOMATION_FEATURE_NONE = 0b000, + TokenAutomationFeatureNone = 0b000, /** * @brief Press "Enter" key at the end as a part of token input automation */ - TOKEN_AUTOMATION_FEATURE_ENTER_AT_THE_END = 0b001, + TokenAutomationFeatureEnterAtTheEnd = 0b001, /** * @brief Press "Tab" key at the end as a part of token input automation */ - TOKEN_AUTOMATION_FEATURE_TAB_AT_THE_END = 0b010, + TokenAutomationFeatureTabAtTheEnd = 0b010, /** * @brief Press keys slower and wait longer between keystrokes */ - TOKEN_AUTOMATION_FEATURE_TYPE_SLOWER = 0b100 + TokenAutomationFeatureTypeSlower = 0b100 }; /** @@ -103,16 +105,14 @@ enum PlainTokenSecretEncodings { /** * @brief Base32 encoding */ - PLAIN_TOKEN_ENCODING_BASE32 = 0, + PlainTokenSecretEncodingBase32 = 0, /** * @brief Base64 encoding */ - PLAIN_TOKEN_ENCODING_BASE64 = 1 + PlainTokenSecretEncodingBase64 = 1 }; -#define TOTP_TOKEN_DIGITS_MAX_COUNT (8) - /** * @brief TOTP token information */ @@ -130,7 +130,7 @@ typedef struct { /** * @brief User-friendly token name */ - char* name; + FuriString* name; /** * @brief Hashing algorithm @@ -168,7 +168,7 @@ void token_info_free(TokenInfo* token_info); /** * @brief Encrypts & sets plain token secret to the given instance of \c TokenInfo * @param token_info instance where secret should be updated - * @param base32_token_secret plain token secret in Base32 format + * @param plain_token_secret plain token secret * @param token_secret_length plain token secret length * @param plain_token_secret_encoding plain token secret encoding * @param iv initialization vecor (IV) to be used for encryption @@ -201,10 +201,18 @@ bool token_info_set_duration_from_int(TokenInfo* token_info, uint8_t duration); * @brief Sets token hashing algorithm from \c str value * @param token_info instance whichs token hashing algorithm should be updated * @param str desired token algorithm - * @return \c true if token hahsing algorithm has been updated; \c false otherwise + * @return \c true if token hashing algorithm has been updated; \c false otherwise */ bool token_info_set_algo_from_str(TokenInfo* token_info, const FuriString* str); +/** + * @brief Sets token hashing algorithm from \c algo_code code + * @param token_info instance whichs token hashing algorithm should be updated + * @param algo_code desired token algorithm code + * @return \c true if token hashing algorithm has been updated; \c false otherwise + */ +bool token_info_set_algo_from_int(TokenInfo* token_info, uint8_t algo_code); + /** * @brief Gets token hahsing algorithm name as C-string * @param token_info instance which token hahsing algorithm name should be returned @@ -225,4 +233,10 @@ bool token_info_set_automation_feature_from_str(TokenInfo* token_info, const Fur * @param src instance to clone * @return cloned instance */ -TokenInfo* token_info_clone(const TokenInfo* src); \ No newline at end of file +TokenInfo* token_info_clone(const TokenInfo* src); + +/** + * @brief Sets default values to all the properties of \c token_info + * @param token_info instance to set defaults to + */ +void token_info_set_defaults(TokenInfo* token_info); diff --git a/applications/external/totp/ui/scene_director.c b/applications/external/totp/ui/scene_director.c index c6f709006..657762a94 100644 --- a/applications/external/totp/ui/scene_director.c +++ b/applications/external/totp/ui/scene_director.c @@ -5,29 +5,28 @@ #include "scenes/add_new_token/totp_scene_add_new_token.h" #include "scenes/token_menu/totp_scene_token_menu.h" #include "scenes/app_settings/totp_app_settings.h" +#include "scenes/standby/standby.h" -void totp_scene_director_activate_scene( - PluginState* const plugin_state, - Scene scene, - const void* context) { +void totp_scene_director_activate_scene(PluginState* const plugin_state, Scene scene) { totp_scene_director_deactivate_active_scene(plugin_state); switch(scene) { case TotpSceneGenerateToken: - totp_scene_generate_token_activate(plugin_state, context); + totp_scene_generate_token_activate(plugin_state); break; case TotpSceneAuthentication: totp_scene_authenticate_activate(plugin_state); break; case TotpSceneAddNewToken: - totp_scene_add_new_token_activate(plugin_state, context); + totp_scene_add_new_token_activate(plugin_state); break; case TotpSceneTokenMenu: - totp_scene_token_menu_activate(plugin_state, context); + totp_scene_token_menu_activate(plugin_state); break; case TotpSceneAppSettings: - totp_scene_app_settings_activate(plugin_state, context); + totp_scene_app_settings_activate(plugin_state); break; case TotpSceneNone: + case TotpSceneStandby: break; default: break; @@ -56,6 +55,7 @@ void totp_scene_director_deactivate_active_scene(PluginState* const plugin_state totp_scene_app_settings_deactivate(plugin_state); break; case TotpSceneNone: + case TotpSceneStandby: break; default: break; @@ -81,6 +81,9 @@ void totp_scene_director_render(Canvas* const canvas, PluginState* const plugin_ break; case TotpSceneNone: break; + case TotpSceneStandby: + totp_scene_standby_render(canvas); + break; default: break; } @@ -105,6 +108,7 @@ bool totp_scene_director_handle_event(PluginEvent* const event, PluginState* con processing = totp_scene_app_settings_handle_event(event, plugin_state); break; case TotpSceneNone: + case TotpSceneStandby: break; default: break; diff --git a/applications/external/totp/ui/scene_director.h b/applications/external/totp/ui/scene_director.h index 71709978f..e45223997 100644 --- a/applications/external/totp/ui/scene_director.h +++ b/applications/external/totp/ui/scene_director.h @@ -11,10 +11,7 @@ * @param scene scene to be activated * @param context scene context to be passed to the scene activation method */ -void totp_scene_director_activate_scene( - PluginState* const plugin_state, - Scene scene, - const void* context); +void totp_scene_director_activate_scene(PluginState* const plugin_state, Scene scene); /** * @brief Deactivate current scene diff --git a/applications/external/totp/ui/scenes/add_new_token/totp_input_text.c b/applications/external/totp/ui/scenes/add_new_token/totp_input_text.c index 6956ec1ad..bbe0b7726 100644 --- a/applications/external/totp/ui/scenes/add_new_token/totp_input_text.c +++ b/applications/external/totp/ui/scenes/add_new_token/totp_input_text.c @@ -1,6 +1,5 @@ #include "totp_input_text.h" #include -#include "../../../lib/polyfills/strnlen.h" void view_draw(View* view, Canvas* canvas) { furi_assert(view); diff --git a/applications/external/totp/ui/scenes/add_new_token/totp_scene_add_new_token.c b/applications/external/totp/ui/scenes/add_new_token/totp_scene_add_new_token.c index 3f8e4fd93..d525e3399 100644 --- a/applications/external/totp/ui/scenes/add_new_token/totp_scene_add_new_token.c +++ b/applications/external/totp/ui/scenes/add_new_token/totp_scene_add_new_token.c @@ -4,17 +4,18 @@ #include "../../scene_director.h" #include "totp_input_text.h" #include "../../../types/token_info.h" -#include #include "../../../services/config/config.h" #include "../../ui_controls.h" #include "../../common_dialogs.h" #include -#include "../../../types/nullable.h" #include "../generate_token/totp_scene_generate_token.h" char* TOKEN_ALGO_LIST[] = {"SHA1", "SHA256", "SHA512", "Steam"}; char* TOKEN_DIGITS_TEXT_LIST[] = {"5 digits", "6 digits", "8 digits"}; -TokenDigitsCount TOKEN_DIGITS_VALUE_LIST[] = {TOTP_5_DIGITS, TOTP_6_DIGITS, TOTP_8_DIGITS}; +TokenDigitsCount TOKEN_DIGITS_VALUE_LIST[] = { + TotpFiveDigitsCount, + TotpSixDigitsCount, + TotpEightDigitsCount}; typedef enum { TokenNameTextBox, @@ -36,7 +37,6 @@ typedef struct { InputTextSceneContext* token_secret_input_context; InputTextSceneState* input_state; uint32_t input_started_at; - TotpNullable_uint16_t current_token_index; int16_t screen_y_offset; TokenHashAlgo algo; uint8_t digits_count_index; @@ -44,6 +44,13 @@ typedef struct { FuriString* duration_text; } SceneState; +struct TotpAddContext { + SceneState* scene_state; + uint8_t* iv; +}; + +enum TotpIteratorUpdateTokenResultsEx { TotpIteratorUpdateTokenResultInvalidSecret = 1 }; + static void on_token_name_user_comitted(InputTextSceneCallbackResult* result) { SceneState* scene_state = result->callback_data; free(scene_state->token_name); @@ -66,9 +73,29 @@ static void update_duration_text(SceneState* scene_state) { furi_string_printf(scene_state->duration_text, "%d sec.", scene_state->duration); } -void totp_scene_add_new_token_activate( - PluginState* plugin_state, - const TokenAddEditSceneContext* context) { +static TotpIteratorUpdateTokenResult add_token_handler(TokenInfo* tokenInfo, const void* context) { + const struct TotpAddContext* context_t = context; + if(!token_info_set_secret( + tokenInfo, + context_t->scene_state->token_secret, + context_t->scene_state->token_secret_length, + PlainTokenSecretEncodingBase32, + context_t->iv)) { + return TotpIteratorUpdateTokenResultInvalidSecret; + } + + furi_string_set_strn( + tokenInfo->name, + context_t->scene_state->token_name, + context_t->scene_state->token_name_length + 1); + tokenInfo->algo = context_t->scene_state->algo; + tokenInfo->digits = TOKEN_DIGITS_VALUE_LIST[context_t->scene_state->digits_count_index]; + tokenInfo->duration = context_t->scene_state->duration; + + return TotpIteratorUpdateTokenResultSuccess; +} + +void totp_scene_add_new_token_activate(PluginState* plugin_state) { SceneState* scene_state = malloc(sizeof(SceneState)); furi_check(scene_state != NULL); plugin_state->current_scene_state = scene_state; @@ -97,12 +124,6 @@ void totp_scene_add_new_token_activate( scene_state->duration = TOTP_TOKEN_DURATION_DEFAULT; scene_state->duration_text = furi_string_alloc(); update_duration_text(scene_state); - - if(context == NULL) { - TOTP_NULLABLE_NULL(scene_state->current_token_index); - } else { - TOTP_NULLABLE_VALUE(scene_state->current_token_index, context->current_token_index); - } } void totp_scene_add_new_token_render(Canvas* const canvas, PluginState* plugin_state) { @@ -260,38 +281,16 @@ bool totp_scene_add_new_token_handle_event(PluginEvent* const event, PluginState case TokenDurationSelect: break; case ConfirmButton: { - TokenInfo* tokenInfo = token_info_alloc(); - bool token_secret_set = token_info_set_secret( - tokenInfo, - scene_state->token_secret, - scene_state->token_secret_length, - PLAIN_TOKEN_ENCODING_BASE32, - &plugin_state->iv[0]); + struct TotpAddContext add_context = { + .iv = plugin_state->iv, .scene_state = scene_state}; + TokenInfoIteratorContext* iterator_context = + totp_config_get_token_iterator_context(plugin_state); + TotpIteratorUpdateTokenResult add_result = totp_token_info_iterator_add_new_token( + iterator_context, &add_token_handler, &add_context); - if(token_secret_set) { - tokenInfo->name = malloc(scene_state->token_name_length + 1); - furi_check(tokenInfo->name != NULL); - strlcpy( - tokenInfo->name, scene_state->token_name, scene_state->token_name_length + 1); - tokenInfo->algo = scene_state->algo; - tokenInfo->digits = TOKEN_DIGITS_VALUE_LIST[scene_state->digits_count_index]; - tokenInfo->duration = scene_state->duration; - - TOTP_LIST_INIT_OR_ADD(plugin_state->tokens_list, tokenInfo, furi_check); - plugin_state->tokens_count++; - - if(totp_config_file_save_new_token(tokenInfo) != TotpConfigFileUpdateSuccess) { - token_info_free(tokenInfo); - totp_dialogs_config_updating_error(plugin_state); - return false; - } - - GenerateTokenSceneContext generate_scene_context = { - .current_token_index = plugin_state->tokens_count - 1}; - totp_scene_director_activate_scene( - plugin_state, TotpSceneGenerateToken, &generate_scene_context); - } else { - token_info_free(tokenInfo); + if(add_result == TotpIteratorUpdateTokenResultSuccess) { + totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken); + } else if(add_result == TotpIteratorUpdateTokenResultInvalidSecret) { DialogMessage* message = dialog_message_alloc(); dialog_message_set_buttons(message, "Back", NULL, NULL); dialog_message_set_text( @@ -305,7 +304,10 @@ bool totp_scene_add_new_token_handle_event(PluginEvent* const event, PluginState dialog_message_free(message); scene_state->selected_control = TokenSecretTextBox; update_screen_y_offset(scene_state); + } else if(add_result == TotpIteratorUpdateTokenResultFileUpdateFailed) { + totp_dialogs_config_updating_error(plugin_state); } + break; } default: @@ -313,14 +315,7 @@ bool totp_scene_add_new_token_handle_event(PluginEvent* const event, PluginState } break; case InputKeyBack: - if(!scene_state->current_token_index.is_null) { - GenerateTokenSceneContext generate_scene_context = { - .current_token_index = scene_state->current_token_index.value}; - totp_scene_director_activate_scene( - plugin_state, TotpSceneGenerateToken, &generate_scene_context); - } else { - totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL); - } + totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken); break; default: break; diff --git a/applications/external/totp/ui/scenes/add_new_token/totp_scene_add_new_token.h b/applications/external/totp/ui/scenes/add_new_token/totp_scene_add_new_token.h index 07098111a..dd6b32994 100644 --- a/applications/external/totp/ui/scenes/add_new_token/totp_scene_add_new_token.h +++ b/applications/external/totp/ui/scenes/add_new_token/totp_scene_add_new_token.h @@ -1,18 +1,10 @@ #pragma once #include -#include -#include #include "../../../types/plugin_state.h" #include "../../../types/plugin_event.h" -typedef struct { - uint16_t current_token_index; -} TokenAddEditSceneContext; - -void totp_scene_add_new_token_activate( - PluginState* plugin_state, - const TokenAddEditSceneContext* context); +void totp_scene_add_new_token_activate(PluginState* plugin_state); void totp_scene_add_new_token_render(Canvas* const canvas, PluginState* plugin_state); bool totp_scene_add_new_token_handle_event(PluginEvent* const event, PluginState* plugin_state); void totp_scene_add_new_token_deactivate(PluginState* plugin_state); 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 d2cf629d2..6dcf0dbc9 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 @@ -9,7 +9,6 @@ #include "../../../services/config/config.h" #include "../../../services/convert/convert.h" #include -#include "../../../types/nullable.h" #include "../../../features_config.h" #ifdef TOTP_BADBT_TYPE_ENABLED #include "../../../workers/bt_type_code/bt_type_code.h" @@ -40,21 +39,13 @@ typedef struct { bool badbt_enabled; #endif uint8_t y_offset; - TotpNullable_uint16_t current_token_index; Control selected_control; } SceneState; -void totp_scene_app_settings_activate( - PluginState* plugin_state, - const AppSettingsSceneContext* context) { +void totp_scene_app_settings_activate(PluginState* plugin_state) { SceneState* scene_state = malloc(sizeof(SceneState)); furi_check(scene_state != NULL); plugin_state->current_scene_state = scene_state; - if(context != NULL) { - TOTP_NULLABLE_VALUE(scene_state->current_token_index, context->current_token_index); - } else { - TOTP_NULLABLE_NULL(scene_state->current_token_index); - } float off_int; float off_dec = modff(plugin_state->timezone_offset, &off_int); @@ -281,8 +272,7 @@ bool totp_scene_app_settings_handle_event( AutomationMethodNone; #endif - if(totp_config_file_update_user_settings(plugin_state) != - TotpConfigFileUpdateSuccess) { + if(!totp_config_file_update_user_settings(plugin_state)) { totp_dialogs_config_updating_error(plugin_state); return false; } @@ -294,25 +284,11 @@ bool totp_scene_app_settings_handle_event( } #endif - if(!scene_state->current_token_index.is_null) { - TokenMenuSceneContext generate_scene_context = { - .current_token_index = scene_state->current_token_index.value}; - totp_scene_director_activate_scene( - plugin_state, TotpSceneTokenMenu, &generate_scene_context); - } else { - totp_scene_director_activate_scene(plugin_state, TotpSceneTokenMenu, NULL); - } + totp_scene_director_activate_scene(plugin_state, TotpSceneTokenMenu); } break; case InputKeyBack: { - if(!scene_state->current_token_index.is_null) { - TokenMenuSceneContext generate_scene_context = { - .current_token_index = scene_state->current_token_index.value}; - totp_scene_director_activate_scene( - plugin_state, TotpSceneTokenMenu, &generate_scene_context); - } else { - totp_scene_director_activate_scene(plugin_state, TotpSceneTokenMenu, NULL); - } + totp_scene_director_activate_scene(plugin_state, TotpSceneTokenMenu); break; } default: diff --git a/applications/external/totp/ui/scenes/app_settings/totp_app_settings.h b/applications/external/totp/ui/scenes/app_settings/totp_app_settings.h index a0e408b00..e54aab87b 100644 --- a/applications/external/totp/ui/scenes/app_settings/totp_app_settings.h +++ b/applications/external/totp/ui/scenes/app_settings/totp_app_settings.h @@ -4,13 +4,7 @@ #include "../../../types/plugin_state.h" #include "../../../types/plugin_event.h" -typedef struct { - uint16_t current_token_index; -} AppSettingsSceneContext; - -void totp_scene_app_settings_activate( - PluginState* plugin_state, - const AppSettingsSceneContext* context); +void totp_scene_app_settings_activate(PluginState* plugin_state); void totp_scene_app_settings_render(Canvas* const canvas, const PluginState* plugin_state); bool totp_scene_app_settings_handle_event( const PluginEvent* const event, 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 c0a0b5744..86e1e8e2b 100644 --- a/applications/external/totp/ui/scenes/authenticate/totp_scene_authenticate.c +++ b/applications/external/totp/ui/scenes/authenticate/totp_scene_authenticate.c @@ -114,12 +114,18 @@ bool totp_scene_authenticate_handle_event( scene_state->code_length++; } break; - case InputKeyOk: - totp_crypto_seed_iv(plugin_state, &scene_state->code_input[0], scene_state->code_length); + case InputKeyOk: { + CryptoSeedIVResult seed_result = totp_crypto_seed_iv( + plugin_state, &scene_state->code_input[0], scene_state->code_length); + + if(seed_result & CryptoSeedIVResultFlagSuccess && + seed_result & CryptoSeedIVResultFlagNewCryptoVerifyData) { + totp_config_file_update_crypto_signatures(plugin_state); + } if(totp_crypto_verify_key(plugin_state)) { FURI_LOG_D(LOGGING_TAG, "PIN is valid"); - totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL); + totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken); } else { FURI_LOG_D(LOGGING_TAG, "PIN is NOT valid"); memset(&scene_state->code_input[0], 0, MAX_CODE_LENGTH); @@ -140,6 +146,7 @@ bool totp_scene_authenticate_handle_event( dialog_message_free(message); } break; + } case InputKeyBack: if(scene_state->code_length > 0) { scene_state->code_input[scene_state->code_length - 1] = 0; 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 22e6ebc33..a316e9955 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,19 +2,16 @@ #include #include #include +#include #include "totp_scene_generate_token.h" #include "../../../types/token_info.h" #include "../../../types/common.h" #include "../../constants.h" -#include "../../../services/totp/totp.h" #include "../../../services/config/config.h" -#include "../../../services/crypto/crypto.h" -#include "../../../services/convert/convert.h" -#include "../../../lib/polyfills/memset_s.h" -#include "../../../lib/roll_value/roll_value.h" #include "../../scene_director.h" #include "../token_menu/totp_scene_token_menu.h" #include "../../../features_config.h" +#include "../../../workers/generate_totp_code/generate_totp_code.h" #include "../../../workers/usb_type_code/usb_type_code.h" #ifdef TOTP_BADBT_TYPE_ENABLED #include "../../../workers/bt_type_code/bt_type_code.h" @@ -23,18 +20,24 @@ #define PROGRESS_BAR_MARGIN (3) #define PROGRESS_BAR_HEIGHT (4) -static const char* STEAM_ALGO_ALPHABET = "23456789BCDFGHJKMNPQRTVWXY"; typedef struct { - uint16_t current_token_index; + uint8_t progress_bar_x; + uint8_t progress_bar_width; + uint8_t code_total_length; + uint8_t code_offset_x; + uint8_t code_offset_x_inc; + uint8_t code_offset_y; +} UiPrecalculatedDimensions; + +typedef struct { char last_code[TOTP_TOKEN_DIGITS_MAX_COUNT + 1]; - bool need_token_update; - TokenInfo* current_token; - uint32_t last_token_gen_time; TotpUsbTypeCodeWorkerContext* usb_type_code_worker_context; NotificationMessage const** notification_sequence_new_token; - NotificationMessage const** notification_sequence_badusb; + NotificationMessage const** notification_sequence_automation; FuriMutex* last_code_update_sync; + TotpGenerateCodeWorkerContext* generate_code_worker_context; + UiPrecalculatedDimensions ui_precalculated_dimensions; } SceneState; static const NotificationSequence* @@ -80,7 +83,7 @@ static const NotificationSequence* static const NotificationSequence* get_notification_sequence_automation(const PluginState* plugin_state, SceneState* scene_state) { - if(scene_state->notification_sequence_badusb == NULL) { + if(scene_state->notification_sequence_automation == NULL) { uint8_t i = 0; uint8_t length = 3; if(plugin_state->notification_method & NotificationMethodVibro) { @@ -91,155 +94,123 @@ static const NotificationSequence* length += 6; } - scene_state->notification_sequence_badusb = malloc(sizeof(void*) * length); - furi_check(scene_state->notification_sequence_badusb != NULL); + scene_state->notification_sequence_automation = malloc(sizeof(void*) * length); + furi_check(scene_state->notification_sequence_automation != NULL); - scene_state->notification_sequence_badusb[i++] = &message_blue_255; + scene_state->notification_sequence_automation[i++] = &message_blue_255; if(plugin_state->notification_method & NotificationMethodVibro) { - scene_state->notification_sequence_badusb[i++] = &message_vibro_on; + scene_state->notification_sequence_automation[i++] = &message_vibro_on; } if(plugin_state->notification_method & NotificationMethodSound) { - scene_state->notification_sequence_badusb[i++] = &message_note_d5; //-V525 - scene_state->notification_sequence_badusb[i++] = &message_delay_50; - scene_state->notification_sequence_badusb[i++] = &message_note_e4; - scene_state->notification_sequence_badusb[i++] = &message_delay_50; - scene_state->notification_sequence_badusb[i++] = &message_note_f3; + scene_state->notification_sequence_automation[i++] = &message_note_d5; //-V525 + scene_state->notification_sequence_automation[i++] = &message_delay_50; + scene_state->notification_sequence_automation[i++] = &message_note_e4; + scene_state->notification_sequence_automation[i++] = &message_delay_50; + scene_state->notification_sequence_automation[i++] = &message_note_f3; } - scene_state->notification_sequence_badusb[i++] = &message_delay_50; + scene_state->notification_sequence_automation[i++] = &message_delay_50; if(plugin_state->notification_method & NotificationMethodVibro) { - scene_state->notification_sequence_badusb[i++] = &message_vibro_off; + scene_state->notification_sequence_automation[i++] = &message_vibro_off; } if(plugin_state->notification_method & NotificationMethodSound) { - scene_state->notification_sequence_badusb[i++] = &message_sound_off; + scene_state->notification_sequence_automation[i++] = &message_sound_off; } - scene_state->notification_sequence_badusb[i++] = NULL; + scene_state->notification_sequence_automation[i++] = NULL; } - return (NotificationSequence*)scene_state->notification_sequence_badusb; + return (NotificationSequence*)scene_state->notification_sequence_automation; } -static void - int_token_to_str(uint64_t i_token_code, char* str, TokenDigitsCount len, TokenHashAlgo algo) { - if(i_token_code == OTP_ERROR) { - memset(&str[0], '-', len); - } else { - if(algo == STEAM) { - for(uint8_t i = 0; i < len; i++) { - str[i] = STEAM_ALGO_ALPHABET[i_token_code % 26]; - i_token_code = i_token_code / 26; - } - } else { - for(int8_t i = len - 1; i >= 0; i--) { - str[i] = CONVERT_DIGIT_TO_CHAR(i_token_code % 10); - i_token_code = i_token_code / 10; - } - } - } - - str[len] = '\0'; -} - -static TOTP_ALGO get_totp_algo_impl(TokenHashAlgo algo) { - switch(algo) { - case SHA1: - case STEAM: - return TOTP_ALGO_SHA1; - case SHA256: - return TOTP_ALGO_SHA256; - case SHA512: - return TOTP_ALGO_SHA512; - default: - break; - } - - return NULL; -} - -static void update_totp_params(PluginState* const plugin_state) { +static void update_totp_params(PluginState* const plugin_state, size_t token_index) { SceneState* scene_state = (SceneState*)plugin_state->current_scene_state; - - if(scene_state->current_token_index < plugin_state->tokens_count) { - TokenInfo* tokenInfo = - list_element_at(plugin_state->tokens_list, scene_state->current_token_index)->data; - - scene_state->need_token_update = true; - scene_state->current_token = tokenInfo; + TokenInfoIteratorContext* iterator_context = + totp_config_get_token_iterator_context(plugin_state); + if(totp_token_info_iterator_go_to(iterator_context, token_index)) { + totp_generate_code_worker_notify( + scene_state->generate_code_worker_context, TotpGenerateCodeWorkerEventForceUpdate); } } -static void draw_totp_code(Canvas* const canvas, const SceneState* const scene_state) { - uint8_t code_length = scene_state->current_token->digits; +static void draw_totp_code(Canvas* const canvas, const PluginState* const plugin_state) { + const SceneState* scene_state = plugin_state->current_scene_state; + const TokenInfoIteratorContext* iterator_context = + totp_config_get_token_iterator_context(plugin_state); + uint8_t code_length = totp_token_info_iterator_get_current_token(iterator_context)->digits; + uint8_t offset_x = scene_state->ui_precalculated_dimensions.code_offset_x; uint8_t char_width = modeNine_15ptFontInfo.charInfo[0].width; - uint8_t total_length = code_length * (char_width + modeNine_15ptFontInfo.spacePixels); - uint8_t offset_x = (SCREEN_WIDTH - total_length) >> 1; - uint8_t offset_x_inc = char_width + modeNine_15ptFontInfo.spacePixels; - uint8_t offset_y = SCREEN_HEIGHT_CENTER - (modeNine_15ptFontInfo.height >> 1); + uint8_t offset_x_inc = scene_state->ui_precalculated_dimensions.code_offset_x_inc; for(uint8_t i = 0; i < code_length; i++) { char ch = scene_state->last_code[i]; - uint8_t char_index = ch - modeNine_15ptFontInfo.startChar; - canvas_draw_xbm( - canvas, - offset_x, - offset_y, - char_width, - modeNine_15ptFontInfo.height, - &modeNine_15ptFontInfo.data[modeNine_15ptFontInfo.charInfo[char_index].offset]); + if(ch >= modeNine_15ptFontInfo.startChar && ch <= modeNine_15ptFontInfo.endChar) { + uint8_t char_index = ch - modeNine_15ptFontInfo.startChar; + canvas_draw_xbm( + canvas, + offset_x, + scene_state->ui_precalculated_dimensions.code_offset_y, + char_width, + modeNine_15ptFontInfo.height, + &modeNine_15ptFontInfo.data[modeNine_15ptFontInfo.charInfo[char_index].offset]); + } offset_x += offset_x_inc; } } -void totp_scene_generate_token_activate( - PluginState* plugin_state, - const GenerateTokenSceneContext* context) { - if(!plugin_state->token_list_loaded) { - TokenLoadingResult token_load_result = totp_config_file_load_tokens(plugin_state); - if(token_load_result != TokenLoadingResultSuccess) { - DialogMessage* message = dialog_message_alloc(); - dialog_message_set_buttons(message, NULL, "Okay", NULL); - if(token_load_result == TokenLoadingResultWarning) { - dialog_message_set_text( - message, - "Unable to load some tokens\nPlease review conf file", - SCREEN_WIDTH_CENTER, - SCREEN_HEIGHT_CENTER, - AlignCenter, - AlignCenter); - } else if(token_load_result == TokenLoadingResultError) { - dialog_message_set_text( - message, - "Unable to load tokens\nPlease review conf file", - SCREEN_WIDTH_CENTER, - SCREEN_HEIGHT_CENTER, - AlignCenter, - AlignCenter); - } - - dialog_message_show(plugin_state->dialogs_app, message); - dialog_message_free(message); - } +static void on_new_token_code_generated(bool time_left, void* context) { + const PluginState* plugin_state = context; + const TokenInfoIteratorContext* iterator_context = + totp_config_get_token_iterator_context(plugin_state); + if(totp_token_info_iterator_get_total_count(iterator_context) == 0) { + return; } + + SceneState* scene_state = plugin_state->current_scene_state; + const TokenInfo* current_token = totp_token_info_iterator_get_current_token(iterator_context); + + uint8_t char_width = modeNine_15ptFontInfo.charInfo[0].width; + scene_state->ui_precalculated_dimensions.code_total_length = + current_token->digits * (char_width + modeNine_15ptFontInfo.spacePixels); + scene_state->ui_precalculated_dimensions.code_offset_x = + (SCREEN_WIDTH - scene_state->ui_precalculated_dimensions.code_total_length) >> 1; + scene_state->ui_precalculated_dimensions.code_offset_x_inc = + char_width + modeNine_15ptFontInfo.spacePixels; + scene_state->ui_precalculated_dimensions.code_offset_y = + SCREEN_HEIGHT_CENTER - (modeNine_15ptFontInfo.height >> 1); + + if(time_left) { + notification_message( + plugin_state->notification_app, + get_notification_sequence_new_token(plugin_state, plugin_state->current_scene_state)); + } +} + +static void on_code_lifetime_updated_generated(float code_lifetime_percent, void* context) { + SceneState* scene_state = context; + scene_state->ui_precalculated_dimensions.progress_bar_width = + (uint8_t)((float)(SCREEN_WIDTH - (PROGRESS_BAR_MARGIN << 1)) * code_lifetime_percent); + scene_state->ui_precalculated_dimensions.progress_bar_x = + ((SCREEN_WIDTH - (PROGRESS_BAR_MARGIN << 1) - + scene_state->ui_precalculated_dimensions.progress_bar_width) >> + 1) + + PROGRESS_BAR_MARGIN; +} + +void totp_scene_generate_token_activate(PluginState* plugin_state) { SceneState* scene_state = malloc(sizeof(SceneState)); furi_check(scene_state != NULL); - if(context == NULL || context->current_token_index > plugin_state->tokens_count) { - scene_state->current_token_index = 0; - } else { - scene_state->current_token_index = context->current_token_index; - } - scene_state->need_token_update = true; + plugin_state->current_scene_state = scene_state; FURI_LOG_D(LOGGING_TAG, "Timezone set to: %f", (double)plugin_state->timezone_offset); - update_totp_params(plugin_state); scene_state->last_code_update_sync = furi_mutex_alloc(FuriMutexTypeNormal); if(plugin_state->automation_method & AutomationMethodBadUsb) { scene_state->usb_type_code_worker_context = totp_usb_type_code_worker_start( - &scene_state->last_code[0], + scene_state->last_code, TOTP_TOKEN_DIGITS_MAX_COUNT + 1, scene_state->last_code_update_sync); } @@ -252,15 +223,36 @@ void totp_scene_generate_token_activate( } totp_bt_type_code_worker_start( plugin_state->bt_type_code_worker_context, - &scene_state->last_code[0], + scene_state->last_code, TOTP_TOKEN_DIGITS_MAX_COUNT + 1, scene_state->last_code_update_sync); } #endif + const TokenInfoIteratorContext* iterator_context = + totp_config_get_token_iterator_context(plugin_state); + scene_state->generate_code_worker_context = totp_generate_code_worker_start( + scene_state->last_code, + totp_token_info_iterator_get_current_token(iterator_context), + scene_state->last_code_update_sync, + plugin_state->timezone_offset, + plugin_state->iv); + + totp_generate_code_worker_set_code_generated_handler( + scene_state->generate_code_worker_context, &on_new_token_code_generated, plugin_state); + + totp_generate_code_worker_set_lifetime_changed_handler( + scene_state->generate_code_worker_context, + &on_code_lifetime_updated_generated, + scene_state); + + update_totp_params( + plugin_state, totp_token_info_iterator_get_current_token_index(iterator_context)); } void totp_scene_generate_token_render(Canvas* const canvas, PluginState* plugin_state) { - if(plugin_state->tokens_count == 0) { + const TokenInfoIteratorContext* iterator_context = + totp_config_get_token_iterator_context(plugin_state); + if(totp_token_info_iterator_get_total_count(iterator_context) == 0) { canvas_draw_str_aligned( canvas, SCREEN_WIDTH_CENTER, @@ -278,57 +270,12 @@ void totp_scene_generate_token_render(Canvas* const canvas, PluginState* plugin_ return; } - SceneState* scene_state = (SceneState*)plugin_state->current_scene_state; - FuriHalRtcDateTime curr_dt; - furi_hal_rtc_get_datetime(&curr_dt); - uint32_t curr_ts = furi_hal_rtc_datetime_to_timestamp(&curr_dt); - - bool is_new_token_time = curr_ts % scene_state->current_token->duration == 0; - if(is_new_token_time && scene_state->last_token_gen_time != curr_ts) { - scene_state->need_token_update = true; - } - - if(scene_state->need_token_update) { - scene_state->need_token_update = false; - scene_state->last_token_gen_time = curr_ts; - - const TokenInfo* tokenInfo = scene_state->current_token; - - if(tokenInfo->token != NULL && tokenInfo->token_length > 0) { - furi_mutex_acquire(scene_state->last_code_update_sync, FuriWaitForever); - size_t key_length; - uint8_t* key = totp_crypto_decrypt( - tokenInfo->token, tokenInfo->token_length, &plugin_state->iv[0], &key_length); - - int_token_to_str( - totp_at( - get_totp_algo_impl(tokenInfo->algo), - key, - key_length, - curr_ts, - plugin_state->timezone_offset, - tokenInfo->duration), - scene_state->last_code, - tokenInfo->digits, - tokenInfo->algo); - memset_s(key, key_length, 0, key_length); - free(key); - } else { - furi_mutex_acquire(scene_state->last_code_update_sync, FuriWaitForever); - int_token_to_str(0, scene_state->last_code, tokenInfo->digits, tokenInfo->algo); - } - - furi_mutex_release(scene_state->last_code_update_sync); - - if(is_new_token_time) { - notification_message( - plugin_state->notification_app, - get_notification_sequence_new_token(plugin_state, scene_state)); - } - } + const SceneState* scene_state = (SceneState*)plugin_state->current_scene_state; canvas_set_font(canvas, FontPrimary); - uint16_t token_name_width = canvas_string_width(canvas, scene_state->current_token->name); + const char* token_name_cstr = + furi_string_get_cstr(totp_token_info_iterator_get_current_token(iterator_context)->name); + uint16_t token_name_width = canvas_string_width(canvas, token_name_cstr); if(SCREEN_WIDTH - token_name_width > 18) { canvas_draw_str_aligned( canvas, @@ -336,40 +283,28 @@ void totp_scene_generate_token_render(Canvas* const canvas, PluginState* plugin_ SCREEN_HEIGHT_CENTER - 20, AlignCenter, AlignCenter, - scene_state->current_token->name); + token_name_cstr); } else { canvas_draw_str_aligned( - canvas, - 9, - SCREEN_HEIGHT_CENTER - 20, - AlignLeft, - AlignCenter, - scene_state->current_token->name); + canvas, 9, SCREEN_HEIGHT_CENTER - 20, AlignLeft, AlignCenter, token_name_cstr); canvas_set_color(canvas, ColorWhite); canvas_draw_box(canvas, 0, SCREEN_HEIGHT_CENTER - 24, 9, 9); canvas_draw_box(canvas, SCREEN_WIDTH - 10, SCREEN_HEIGHT_CENTER - 24, 9, 9); canvas_set_color(canvas, ColorBlack); } - draw_totp_code(canvas, scene_state); - - const uint8_t TOKEN_LIFETIME = scene_state->current_token->duration; - float percentDone = (float)(TOKEN_LIFETIME - curr_ts % TOKEN_LIFETIME) / (float)TOKEN_LIFETIME; - uint8_t barWidth = (uint8_t)((float)(SCREEN_WIDTH - (PROGRESS_BAR_MARGIN << 1)) * percentDone); - uint8_t barX = - ((SCREEN_WIDTH - (PROGRESS_BAR_MARGIN << 1) - barWidth) >> 1) + PROGRESS_BAR_MARGIN; + draw_totp_code(canvas, plugin_state); canvas_draw_box( canvas, - barX, + scene_state->ui_precalculated_dimensions.progress_bar_x, SCREEN_HEIGHT - PROGRESS_BAR_MARGIN - PROGRESS_BAR_HEIGHT, - barWidth, + scene_state->ui_precalculated_dimensions.progress_bar_width, PROGRESS_BAR_HEIGHT); - - if(plugin_state->tokens_count > 1) { + if(totp_token_info_iterator_get_total_count(iterator_context) > 1) { canvas_draw_icon(canvas, 0, SCREEN_HEIGHT_CENTER - 24, &I_totp_arrow_left_8x9); canvas_draw_icon( - canvas, SCREEN_WIDTH - 9, SCREEN_HEIGHT_CENTER - 24, &I_totp_arrow_right_8x9); + canvas, SCREEN_WIDTH - 8, SCREEN_HEIGHT_CENTER - 24, &I_totp_arrow_right_8x9); } #ifdef TOTP_AUTOMATION_ICONS_ENABLED @@ -390,7 +325,7 @@ void totp_scene_generate_token_render(Canvas* const canvas, PluginState* plugin_ #ifdef TOTP_BADBT_TYPE_ENABLED if(plugin_state->automation_method & AutomationMethodBadBt && plugin_state->bt_type_code_worker_context != NULL && - plugin_state->bt_type_code_worker_context->is_advertising) { + totp_bt_type_code_worker_is_advertising(plugin_state->bt_type_code_worker_context)) { canvas_draw_icon( canvas, SCREEN_WIDTH_CENTER + @@ -418,10 +353,12 @@ bool totp_scene_generate_token_handle_event( if(event->input.key == InputKeyDown && plugin_state->automation_method & AutomationMethodBadUsb) { scene_state = (SceneState*)plugin_state->current_scene_state; + const TokenInfoIteratorContext* iterator_context = + totp_config_get_token_iterator_context(plugin_state); totp_usb_type_code_worker_notify( scene_state->usb_type_code_worker_context, TotpUsbTypeCodeWorkerEventType, - scene_state->current_token->automation_features); + totp_token_info_iterator_get_current_token(iterator_context)->automation_features); notification_message( plugin_state->notification_app, get_notification_sequence_automation(plugin_state, scene_state)); @@ -432,10 +369,12 @@ bool totp_scene_generate_token_handle_event( event->input.key == InputKeyUp && plugin_state->automation_method & AutomationMethodBadBt) { scene_state = (SceneState*)plugin_state->current_scene_state; + const TokenInfoIteratorContext* iterator_context = + totp_config_get_token_iterator_context(plugin_state); totp_bt_type_code_worker_notify( plugin_state->bt_type_code_worker_context, TotpBtTypeCodeWorkerEventType, - scene_state->current_token->automation_features); + totp_token_info_iterator_get_current_token(iterator_context)->automation_features); notification_message( plugin_state->notification_app, get_notification_sequence_automation(plugin_state, scene_state)); @@ -448,37 +387,43 @@ bool totp_scene_generate_token_handle_event( return true; } - scene_state = (SceneState*)plugin_state->current_scene_state; switch(event->input.key) { case InputKeyUp: break; case InputKeyDown: break; - case InputKeyRight: - totp_roll_value_uint16_t( - &scene_state->current_token_index, + case InputKeyRight: { + const TokenInfoIteratorContext* iterator_context = + totp_config_get_token_iterator_context(plugin_state); + size_t current_token_index = + totp_token_info_iterator_get_current_token_index(iterator_context); + totp_roll_value_size_t( + ¤t_token_index, 1, 0, - plugin_state->tokens_count - 1, + totp_token_info_iterator_get_total_count(iterator_context) - 1, RollOverflowBehaviorRoll); - update_totp_params(plugin_state); + + update_totp_params(plugin_state, current_token_index); break; - case InputKeyLeft: - totp_roll_value_uint16_t( - &scene_state->current_token_index, + } + case InputKeyLeft: { + const TokenInfoIteratorContext* iterator_context = + totp_config_get_token_iterator_context(plugin_state); + size_t current_token_index = + totp_token_info_iterator_get_current_token_index(iterator_context); + totp_roll_value_size_t( + ¤t_token_index, -1, 0, - plugin_state->tokens_count - 1, + totp_token_info_iterator_get_total_count(iterator_context) - 1, RollOverflowBehaviorRoll); - update_totp_params(plugin_state); + + update_totp_params(plugin_state, current_token_index); break; + } case InputKeyOk: - if(plugin_state->tokens_count == 0) { - totp_scene_director_activate_scene(plugin_state, TotpSceneTokenMenu, NULL); - } else { - TokenMenuSceneContext ctx = {.current_token_index = scene_state->current_token_index}; - totp_scene_director_activate_scene(plugin_state, TotpSceneTokenMenu, &ctx); - } + totp_scene_director_activate_scene(plugin_state, TotpSceneTokenMenu); break; case InputKeyBack: break; @@ -493,6 +438,8 @@ void totp_scene_generate_token_deactivate(PluginState* plugin_state) { if(plugin_state->current_scene_state == NULL) return; SceneState* scene_state = (SceneState*)plugin_state->current_scene_state; + totp_generate_code_worker_stop(scene_state->generate_code_worker_context); + if(plugin_state->automation_method & AutomationMethodBadUsb) { totp_usb_type_code_worker_stop(scene_state->usb_type_code_worker_context); } @@ -506,8 +453,8 @@ void totp_scene_generate_token_deactivate(PluginState* plugin_state) { free(scene_state->notification_sequence_new_token); } - if(scene_state->notification_sequence_badusb != NULL) { - free(scene_state->notification_sequence_badusb); + if(scene_state->notification_sequence_automation != NULL) { + free(scene_state->notification_sequence_automation); } furi_mutex_free(scene_state->last_code_update_sync); diff --git a/applications/external/totp/ui/scenes/generate_token/totp_scene_generate_token.h b/applications/external/totp/ui/scenes/generate_token/totp_scene_generate_token.h index e183f53d2..3f7bc0408 100644 --- a/applications/external/totp/ui/scenes/generate_token/totp_scene_generate_token.h +++ b/applications/external/totp/ui/scenes/generate_token/totp_scene_generate_token.h @@ -4,13 +4,7 @@ #include "../../../types/plugin_state.h" #include "../../../types/plugin_event.h" -typedef struct { - uint16_t current_token_index; -} GenerateTokenSceneContext; - -void totp_scene_generate_token_activate( - PluginState* plugin_state, - const GenerateTokenSceneContext* context); +void totp_scene_generate_token_activate(PluginState* plugin_state); void totp_scene_generate_token_render(Canvas* const canvas, PluginState* plugin_state); bool totp_scene_generate_token_handle_event( const PluginEvent* const event, diff --git a/applications/external/totp/ui/scenes/standby/standby.c b/applications/external/totp/ui/scenes/standby/standby.c new file mode 100644 index 000000000..5cd6bae6a --- /dev/null +++ b/applications/external/totp/ui/scenes/standby/standby.c @@ -0,0 +1,12 @@ +#include "standby.h" +#include +#include "../../constants.h" + +void totp_scene_standby_render(Canvas* const canvas) { + canvas_draw_icon(canvas, SCREEN_WIDTH - 56, SCREEN_HEIGHT - 48, &I_DolphinCommon_56x48); + + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 5, 10, AlignLeft, AlignTop, "CLI command"); + + canvas_draw_str_aligned(canvas, 5, 24, AlignLeft, AlignTop, "is running now"); +} \ No newline at end of file diff --git a/applications/external/totp/ui/scenes/standby/standby.h b/applications/external/totp/ui/scenes/standby/standby.h new file mode 100644 index 000000000..78e2b0915 --- /dev/null +++ b/applications/external/totp/ui/scenes/standby/standby.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +void totp_scene_standby_render(Canvas* const canvas); \ No newline at end of file diff --git a/applications/external/totp/ui/scenes/token_menu/totp_scene_token_menu.c b/applications/external/totp/ui/scenes/token_menu/totp_scene_token_menu.c index 7b00f0a1b..a8c8de28a 100644 --- a/applications/external/totp/ui/scenes/token_menu/totp_scene_token_menu.c +++ b/applications/external/totp/ui/scenes/token_menu/totp_scene_token_menu.c @@ -6,12 +6,10 @@ #include "../../constants.h" #include "../../scene_director.h" #include "../../../services/config/config.h" -#include #include "../../../types/token_info.h" #include "../generate_token/totp_scene_generate_token.h" #include "../add_new_token/totp_scene_add_new_token.h" #include "../app_settings/totp_app_settings.h" -#include "../../../types/nullable.h" #include #define SCREEN_HEIGHT_THIRD (SCREEN_HEIGHT / 3) @@ -21,25 +19,19 @@ typedef enum { AddNewToken, DeleteToken, AppSettings } Control; typedef struct { Control selected_control; - TotpNullable_uint16_t current_token_index; } SceneState; -void totp_scene_token_menu_activate( - PluginState* plugin_state, - const TokenMenuSceneContext* context) { +void totp_scene_token_menu_activate(PluginState* plugin_state) { SceneState* scene_state = malloc(sizeof(SceneState)); furi_check(scene_state != NULL); plugin_state->current_scene_state = scene_state; - if(context != NULL) { - TOTP_NULLABLE_VALUE(scene_state->current_token_index, context->current_token_index); - } else { - TOTP_NULLABLE_NULL(scene_state->current_token_index); - } } void totp_scene_token_menu_render(Canvas* const canvas, PluginState* plugin_state) { const SceneState* scene_state = (SceneState*)plugin_state->current_scene_state; - if(scene_state->current_token_index.is_null) { + const TokenInfoIteratorContext* iterator_context = + totp_config_get_token_iterator_context(plugin_state); + if(totp_token_info_iterator_get_total_count(iterator_context) == 0) { ui_control_button_render( canvas, SCREEN_WIDTH_CENTER - 36, @@ -95,22 +87,28 @@ bool totp_scene_token_menu_handle_event(const PluginEvent* const event, PluginSt } switch(event->input.key) { - case InputKeyUp: + case InputKeyUp: { + const TokenInfoIteratorContext* iterator_context = + totp_config_get_token_iterator_context(plugin_state); totp_roll_value_uint8_t( &scene_state->selected_control, -1, AddNewToken, AppSettings, RollOverflowBehaviorRoll); if(scene_state->selected_control == DeleteToken && - scene_state->current_token_index.is_null) { + totp_token_info_iterator_get_total_count(iterator_context) == 0) { scene_state->selected_control--; } break; - case InputKeyDown: + } + case InputKeyDown: { + const TokenInfoIteratorContext* iterator_context = + totp_config_get_token_iterator_context(plugin_state); totp_roll_value_uint8_t( &scene_state->selected_control, 1, AddNewToken, AppSettings, RollOverflowBehaviorRoll); if(scene_state->selected_control == DeleteToken && - scene_state->current_token_index.is_null) { + totp_token_info_iterator_get_total_count(iterator_context) == 0) { scene_state->selected_control++; } break; + } case InputKeyRight: break; case InputKeyLeft: @@ -118,14 +116,7 @@ bool totp_scene_token_menu_handle_event(const PluginEvent* const event, PluginSt case InputKeyOk: switch(scene_state->selected_control) { case AddNewToken: { - if(scene_state->current_token_index.is_null) { - totp_scene_director_activate_scene(plugin_state, TotpSceneAddNewToken, NULL); - } else { - TokenAddEditSceneContext add_new_token_scene_context = { - .current_token_index = scene_state->current_token_index.value}; - totp_scene_director_activate_scene( - plugin_state, TotpSceneAddNewToken, &add_new_token_scene_context); - } + totp_scene_director_activate_scene(plugin_state, TotpSceneAddNewToken); break; } case DeleteToken: { @@ -142,34 +133,21 @@ bool totp_scene_token_menu_handle_event(const PluginEvent* const event, PluginSt DialogMessageButton dialog_result = dialog_message_show(plugin_state->dialogs_app, message); dialog_message_free(message); + TokenInfoIteratorContext* iterator_context = + totp_config_get_token_iterator_context(plugin_state); if(dialog_result == DialogMessageButtonRight && - !scene_state->current_token_index.is_null) { - TokenInfo* tokenInfo = NULL; - plugin_state->tokens_list = list_remove_at( - plugin_state->tokens_list, - scene_state->current_token_index.value, - (void**)&tokenInfo); - plugin_state->tokens_count--; - furi_check(tokenInfo != NULL); - token_info_free(tokenInfo); - - if(totp_full_save_config_file(plugin_state) != TotpConfigFileUpdateSuccess) { + totp_token_info_iterator_get_total_count(iterator_context) > 0) { + if(!totp_token_info_iterator_remove_current_token_info(iterator_context)) { totp_dialogs_config_updating_error(plugin_state); return false; } - totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL); + + totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken); } break; } case AppSettings: { - if(!scene_state->current_token_index.is_null) { - AppSettingsSceneContext app_settings_context = { - .current_token_index = scene_state->current_token_index.value}; - totp_scene_director_activate_scene( - plugin_state, TotpSceneAppSettings, &app_settings_context); - } else { - totp_scene_director_activate_scene(plugin_state, TotpSceneAppSettings, NULL); - } + totp_scene_director_activate_scene(plugin_state, TotpSceneAppSettings); break; } default: @@ -177,14 +155,7 @@ bool totp_scene_token_menu_handle_event(const PluginEvent* const event, PluginSt } break; case InputKeyBack: { - if(!scene_state->current_token_index.is_null) { - GenerateTokenSceneContext generate_scene_context = { - .current_token_index = scene_state->current_token_index.value}; - totp_scene_director_activate_scene( - plugin_state, TotpSceneGenerateToken, &generate_scene_context); - } else { - totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL); - } + totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken); break; } default: diff --git a/applications/external/totp/ui/scenes/token_menu/totp_scene_token_menu.h b/applications/external/totp/ui/scenes/token_menu/totp_scene_token_menu.h index f9d4b4cbf..a715c9748 100644 --- a/applications/external/totp/ui/scenes/token_menu/totp_scene_token_menu.h +++ b/applications/external/totp/ui/scenes/token_menu/totp_scene_token_menu.h @@ -4,13 +4,7 @@ #include "../../../types/plugin_state.h" #include "../../../types/plugin_event.h" -typedef struct { - uint16_t current_token_index; -} TokenMenuSceneContext; - -void totp_scene_token_menu_activate( - PluginState* plugin_state, - const TokenMenuSceneContext* context); +void totp_scene_token_menu_activate(PluginState* plugin_state); void totp_scene_token_menu_render(Canvas* const canvas, PluginState* plugin_state); bool totp_scene_token_menu_handle_event(const PluginEvent* const event, PluginState* plugin_state); void totp_scene_token_menu_deactivate(PluginState* plugin_state); diff --git a/applications/external/totp/ui/totp_scenes_enum.h b/applications/external/totp/ui/totp_scenes_enum.h index 0c73af772..4624eddd9 100644 --- a/applications/external/totp/ui/totp_scenes_enum.h +++ b/applications/external/totp/ui/totp_scenes_enum.h @@ -34,5 +34,10 @@ enum Scenes { /** * @brief Scene where user can change application settings */ - TotpSceneAppSettings + TotpSceneAppSettings, + + /** + * @brief Scene which informs user that CLI command is running + */ + TotpSceneStandby }; diff --git a/applications/external/totp/workers/bt_type_code/bt_type_code.c b/applications/external/totp/workers/bt_type_code/bt_type_code.c index ec4c1619d..bf55dba6a 100644 --- a/applications/external/totp/workers/bt_type_code/bt_type_code.c +++ b/applications/external/totp/workers/bt_type_code/bt_type_code.c @@ -1,13 +1,40 @@ #include "bt_type_code.h" #include +#include #include +#include +#include +#include +#include +#include #include #include "../../types/common.h" #include "../../types/token_info.h" -#include "../common.h" +#include "../type_code_common.h" +#include "../../features_config.h" + +#if TOTP_TARGET_FIRMWARE == TOTP_FIRMWARE_XTREME +#define TOTP_BT_WORKER_BT_ADV_NAME_MAX_LEN FURI_HAL_BT_ADV_NAME_LENGTH +#define TOTP_BT_WORKER_BT_MAC_ADDRESS_LEN GAP_MAC_ADDR_SIZE +#endif #define HID_BT_KEYS_STORAGE_PATH EXT_PATH("authenticator/.bt_hid.keys") +struct TotpBtTypeCodeWorkerContext { + char* code_buffer; + uint8_t code_buffer_size; + uint8_t flags; + FuriThread* thread; + FuriMutex* code_buffer_sync; + Bt* bt; + bool is_advertising; + bool is_connected; +#if TOTP_TARGET_FIRMWARE == TOTP_FIRMWARE_XTREME + char previous_bt_name[TOTP_BT_WORKER_BT_ADV_NAME_MAX_LEN]; + uint8_t previous_bt_mac[TOTP_BT_WORKER_BT_MAC_ADDRESS_LEN]; +#endif +}; + static inline bool totp_type_code_worker_stop_requested() { return furi_thread_flags_get() & TotpBtTypeCodeWorkerEventStop; } @@ -16,15 +43,15 @@ static inline bool totp_type_code_worker_stop_requested() { static void totp_type_code_worker_bt_set_app_mac(uint8_t* mac) { uint8_t max_i; size_t uid_size = furi_hal_version_uid_size(); - if(uid_size < 6) { + if(uid_size < TOTP_BT_WORKER_BT_MAC_ADDRESS_LEN) { max_i = uid_size; } else { - max_i = 6; + max_i = TOTP_BT_WORKER_BT_MAC_ADDRESS_LEN; } const uint8_t* uid = furi_hal_version_uid(); memcpy(mac, uid, max_i); - for(uint8_t i = max_i; i < 6; i++) { + for(uint8_t i = max_i; i < TOTP_BT_WORKER_BT_MAC_ADDRESS_LEN; i++) { mac[i] = 0; } @@ -39,23 +66,21 @@ static void totp_type_code_worker_type_code(TotpBtTypeCodeWorkerContext* context i++; } while(!context->is_connected && i < 100 && !totp_type_code_worker_stop_requested()); - if(context->is_connected && furi_mutex_acquire(context->string_sync, 500) == FuriStatusOk) { + if(context->is_connected && + furi_mutex_acquire(context->code_buffer_sync, 500) == FuriStatusOk) { totp_type_code_worker_execute_automation( &furi_hal_bt_hid_kb_press, &furi_hal_bt_hid_kb_release, - context->string, - context->string_length, + context->code_buffer, + context->code_buffer_size, context->flags); - furi_mutex_release(context->string_sync); + furi_mutex_release(context->code_buffer_sync); } } static int32_t totp_type_code_worker_callback(void* context) { furi_check(context); FuriMutex* context_mutex = furi_mutex_alloc(FuriMutexTypeNormal); - if(context_mutex == NULL) { - return 251; - } TotpBtTypeCodeWorkerContext* bt_context = context; @@ -92,13 +117,13 @@ static void connection_status_changed_callback(BtStatus status, void* context) { void totp_bt_type_code_worker_start( TotpBtTypeCodeWorkerContext* context, - char* code_buf, - uint8_t code_buf_length, - FuriMutex* code_buf_update_sync) { + char* code_buffer, + uint8_t code_buffer_size, + FuriMutex* code_buffer_sync) { furi_check(context != NULL); - context->string = code_buf; - context->string_length = code_buf_length; - context->string_sync = code_buf_update_sync; + context->code_buffer = code_buffer; + context->code_buffer_size = code_buffer_size; + context->code_buffer_sync = code_buffer_sync; context->thread = furi_thread_alloc(); furi_thread_set_name(context->thread, "TOTPBtHidWorker"); furi_thread_set_stack_size(context->thread, 1024); @@ -137,7 +162,6 @@ TotpBtTypeCodeWorkerContext* totp_bt_type_code_worker_init() { bt_keys_storage_set_storage_path(context->bt, HID_BT_KEYS_STORAGE_PATH); #if TOTP_TARGET_FIRMWARE == TOTP_FIRMWARE_XTREME - totp_type_code_worker_bt_set_app_mac(&context->bt_mac[0]); memcpy( &context->previous_bt_name[0], furi_hal_bt_get_profile_adv_name(FuriHalBtProfileHidKeyboard), @@ -148,8 +172,10 @@ TotpBtTypeCodeWorkerContext* totp_bt_type_code_worker_init() { TOTP_BT_WORKER_BT_MAC_ADDRESS_LEN); char new_name[TOTP_BT_WORKER_BT_ADV_NAME_MAX_LEN]; snprintf(new_name, sizeof(new_name), "%s TOTP Auth", furi_hal_version_get_name_ptr()); + uint8_t new_bt_mac[TOTP_BT_WORKER_BT_MAC_ADDRESS_LEN]; + totp_type_code_worker_bt_set_app_mac(new_bt_mac); furi_hal_bt_set_profile_adv_name(FuriHalBtProfileHidKeyboard, new_name); - furi_hal_bt_set_profile_mac_addr(FuriHalBtProfileHidKeyboard, context->bt_mac); + furi_hal_bt_set_profile_mac_addr(FuriHalBtProfileHidKeyboard, new_bt_mac); #endif if(!bt_set_profile(context->bt, BtProfileHidKeyboard)) { @@ -197,4 +223,8 @@ void totp_bt_type_code_worker_free(TotpBtTypeCodeWorkerContext* context) { context->bt = NULL; free(context); +} + +bool totp_bt_type_code_worker_is_advertising(const TotpBtTypeCodeWorkerContext* context) { + return context->is_advertising; } \ No newline at end of file diff --git a/applications/external/totp/workers/bt_type_code/bt_type_code.h b/applications/external/totp/workers/bt_type_code/bt_type_code.h index 1c59ea3e9..85016592e 100644 --- a/applications/external/totp/workers/bt_type_code/bt_type_code.h +++ b/applications/external/totp/workers/bt_type_code/bt_type_code.h @@ -1,49 +1,79 @@ #pragma once -#include -#include -#include -#include -#include "../../features_config.h" - -#if TOTP_TARGET_FIRMWARE == TOTP_FIRMWARE_XTREME -#define TOTP_BT_WORKER_BT_ADV_NAME_MAX_LEN FURI_HAL_BT_ADV_NAME_LENGTH -#define TOTP_BT_WORKER_BT_MAC_ADDRESS_LEN GAP_MAC_ADDR_SIZE -#endif +#include +#include +#include typedef uint8_t TotpBtTypeCodeWorkerEvent; -typedef struct { - char* string; - uint8_t string_length; - uint8_t flags; - FuriThread* thread; - FuriMutex* string_sync; - Bt* bt; - bool is_advertising; - bool is_connected; -#if TOTP_TARGET_FIRMWARE == TOTP_FIRMWARE_XTREME - uint8_t bt_mac[TOTP_BT_WORKER_BT_MAC_ADDRESS_LEN]; - char previous_bt_name[TOTP_BT_WORKER_BT_ADV_NAME_MAX_LEN]; - uint8_t previous_bt_mac[TOTP_BT_WORKER_BT_MAC_ADDRESS_LEN]; -#endif -} TotpBtTypeCodeWorkerContext; +typedef struct TotpBtTypeCodeWorkerContext TotpBtTypeCodeWorkerContext; +/** + * @brief Bluetooth token input automation worker events + */ enum TotpBtTypeCodeWorkerEvents { - TotpBtTypeCodeWorkerEventReserved = 0b0000, - TotpBtTypeCodeWorkerEventStop = 0b0100, - TotpBtTypeCodeWorkerEventType = 0b1000 + + /** + * @brief Reserved, should not be used anywhere + */ + TotpBtTypeCodeWorkerEventReserved = 0b00, + + /** + * @brief Stop worker + */ + TotpBtTypeCodeWorkerEventStop = 0b01, + + /** + * @brief Trigger token input automation + */ + TotpBtTypeCodeWorkerEventType = 0b10 }; +/** + * @brief Initializes bluetooth token input automation worker + * @return worker context + */ TotpBtTypeCodeWorkerContext* totp_bt_type_code_worker_init(); + +/** + * @brief Disposes bluetooth token input automation worker and releases all the allocated resources + * @param context worker context + */ void totp_bt_type_code_worker_free(TotpBtTypeCodeWorkerContext* context); + +/** + * @brief Starts bluetooth token input automation worker + * @param context worker context + * @param code_buffer code buffer to be used to automate + * @param code_buffer_size code buffer size + * @param code_buffer_sync code buffer synchronization primitive + */ void totp_bt_type_code_worker_start( TotpBtTypeCodeWorkerContext* context, - char* code_buf, - uint8_t code_buf_length, - FuriMutex* code_buf_update_sync); + char* code_buffer, + uint8_t code_buffer_size, + FuriMutex* code_buffer_sync); + +/** + * @brief Stops bluetooth token input automation worker + * @param context worker context + */ void totp_bt_type_code_worker_stop(TotpBtTypeCodeWorkerContext* context); + +/** + * @brief Notifies bluetooth token input automation worker with a given event + * @param context worker context + * @param event event to notify worker with + * @param flags event flags + */ void totp_bt_type_code_worker_notify( TotpBtTypeCodeWorkerContext* context, TotpBtTypeCodeWorkerEvent event, uint8_t flags); + +/** + * @brief Gets information whether Bluetooth is advertising now or not + * @param context worker context + * @return \c true if Bluetooth is advertising now; \c false otherwise + */ +bool totp_bt_type_code_worker_is_advertising(const TotpBtTypeCodeWorkerContext* context); diff --git a/applications/external/totp/workers/common.h b/applications/external/totp/workers/common.h deleted file mode 100644 index 5e3a2006e..000000000 --- a/applications/external/totp/workers/common.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once -#include -#include "../types/token_info.h" - -typedef bool (*TOTP_AUTOMATION_KEY_HANDLER)(uint16_t key); - -void totp_type_code_worker_execute_automation( - TOTP_AUTOMATION_KEY_HANDLER key_press_fn, - TOTP_AUTOMATION_KEY_HANDLER key_release_fn, - const char* string, - uint8_t string_length, - TokenAutomationFeature features); \ No newline at end of file diff --git a/applications/external/totp/workers/generate_totp_code/generate_totp_code.c b/applications/external/totp/workers/generate_totp_code/generate_totp_code.c new file mode 100644 index 000000000..7e9356c45 --- /dev/null +++ b/applications/external/totp/workers/generate_totp_code/generate_totp_code.c @@ -0,0 +1,195 @@ +#include "generate_totp_code.h" +#include +#include "../../services/crypto/crypto.h" +#include "../../services/totp/totp.h" +#include "../../services/convert/convert.h" +#include +#include + +#define ONE_SEC_MS (1000) + +struct TotpGenerateCodeWorkerContext { + char* code_buffer; + FuriThread* thread; + FuriMutex* code_buffer_sync; + const TokenInfo* token_info; + float timezone_offset; + uint8_t* iv; + TOTP_NEW_CODE_GENERATED_HANDLER on_new_code_generated_handler; + void* on_new_code_generated_handler_context; + TOTP_CODE_LIFETIME_CHANGED_HANDLER on_code_lifetime_changed_handler; + void* on_code_lifetime_changed_handler_context; +}; + +static const char* STEAM_ALGO_ALPHABET = "23456789BCDFGHJKMNPQRTVWXY"; + +static void + int_token_to_str(uint64_t i_token_code, char* str, TokenDigitsCount len, TokenHashAlgo algo) { + str[len] = '\0'; + if(i_token_code == OTP_ERROR) { + memset(&str[0], '-', len); + } else { + if(algo == STEAM) { + for(uint8_t i = 0; i < len; i++) { + str[i] = STEAM_ALGO_ALPHABET[i_token_code % 26]; + i_token_code = i_token_code / 26; + } + } else { + for(int8_t i = len - 1; i >= 0; i--) { + str[i] = CONVERT_DIGIT_TO_CHAR(i_token_code % 10); + i_token_code = i_token_code / 10; + } + } + } +} + +static TOTP_ALGO get_totp_algo_impl(TokenHashAlgo algo) { + switch(algo) { + case SHA1: + case STEAM: + return TOTP_ALGO_SHA1; + case SHA256: + return TOTP_ALGO_SHA256; + case SHA512: + return TOTP_ALGO_SHA512; + default: + break; + } + + return NULL; +} + +static void generate_totp_code( + TotpGenerateCodeWorkerContext* context, + const TokenInfo* token_info, + uint32_t current_ts) { + if(token_info->token != NULL && token_info->token_length > 0) { + size_t key_length; + uint8_t* key = totp_crypto_decrypt( + token_info->token, token_info->token_length, context->iv, &key_length); + + int_token_to_str( + totp_at( + get_totp_algo_impl(token_info->algo), + key, + key_length, + current_ts, + context->timezone_offset, + token_info->duration), + context->code_buffer, + token_info->digits, + token_info->algo); + memset_s(key, key_length, 0, key_length); + free(key); + } else { + int_token_to_str(0, context->code_buffer, token_info->digits, token_info->algo); + } +} + +static int32_t totp_generate_worker_callback(void* context) { + furi_check(context); + + TotpGenerateCodeWorkerContext* t_context = context; + + while(true) { + uint32_t flags = furi_thread_flags_wait( + TotpGenerateCodeWorkerEventStop | TotpGenerateCodeWorkerEventForceUpdate, + FuriFlagWaitAny, + ONE_SEC_MS); + + if(flags == + (uint32_t) + FuriFlagErrorTimeout) { // If timeout, consider as no error, as we expect this and can handle gracefully + flags = 0; + } + + furi_check((flags & FuriFlagError) == 0); //-V562 + + if(flags & TotpGenerateCodeWorkerEventStop) break; + + const TokenInfo* token_info = t_context->token_info; + if(token_info == NULL) { + continue; + } + + uint32_t curr_ts = furi_hal_rtc_get_timestamp(); + + bool time_left = false; + if(flags & TotpGenerateCodeWorkerEventForceUpdate || + (time_left = (curr_ts % token_info->duration) == 0)) { + if(furi_mutex_acquire(t_context->code_buffer_sync, FuriWaitForever) == FuriStatusOk) { + generate_totp_code(t_context, token_info, curr_ts); + curr_ts = furi_hal_rtc_get_timestamp(); + furi_mutex_release(t_context->code_buffer_sync); + if(t_context->on_new_code_generated_handler != NULL) { + (*(t_context->on_new_code_generated_handler))( + time_left, t_context->on_new_code_generated_handler_context); + } + } + } + + if(t_context->on_code_lifetime_changed_handler != NULL) { + (*(t_context->on_code_lifetime_changed_handler))( + (float)(token_info->duration - curr_ts % token_info->duration) / + (float)token_info->duration, + t_context->on_code_lifetime_changed_handler_context); + } + } + + return 0; +} + +TotpGenerateCodeWorkerContext* totp_generate_code_worker_start( + char* code_buffer, + const TokenInfo* token_info, + FuriMutex* code_buffer_sync, + float timezone_offset, + uint8_t* iv) { + TotpGenerateCodeWorkerContext* context = malloc(sizeof(TotpGenerateCodeWorkerContext)); + furi_check(context != NULL); + context->code_buffer = code_buffer; + context->token_info = token_info; + context->code_buffer_sync = code_buffer_sync; + context->timezone_offset = timezone_offset; + context->iv = iv; + context->thread = furi_thread_alloc(); + furi_thread_set_name(context->thread, "TOTPGenerateWorker"); + furi_thread_set_stack_size(context->thread, 2048); + furi_thread_set_context(context->thread, context); + furi_thread_set_callback(context->thread, totp_generate_worker_callback); + furi_thread_start(context->thread); + return context; +} + +void totp_generate_code_worker_stop(TotpGenerateCodeWorkerContext* context) { + furi_check(context != NULL); + furi_thread_flags_set(furi_thread_get_id(context->thread), TotpGenerateCodeWorkerEventStop); + furi_thread_join(context->thread); + furi_thread_free(context->thread); + free(context); +} + +void totp_generate_code_worker_notify( + TotpGenerateCodeWorkerContext* context, + TotpGenerateCodeWorkerEvent event) { + furi_check(context != NULL); + furi_thread_flags_set(furi_thread_get_id(context->thread), event); +} + +void totp_generate_code_worker_set_code_generated_handler( + TotpGenerateCodeWorkerContext* context, + TOTP_NEW_CODE_GENERATED_HANDLER on_new_code_generated_handler, + void* on_new_code_generated_handler_context) { + furi_check(context != NULL); + context->on_new_code_generated_handler = on_new_code_generated_handler; + context->on_new_code_generated_handler_context = on_new_code_generated_handler_context; +} + +void totp_generate_code_worker_set_lifetime_changed_handler( + TotpGenerateCodeWorkerContext* context, + TOTP_CODE_LIFETIME_CHANGED_HANDLER on_code_lifetime_changed_handler, + void* on_code_lifetime_changed_handler_context) { + furi_check(context != NULL); + context->on_code_lifetime_changed_handler = on_code_lifetime_changed_handler; + context->on_code_lifetime_changed_handler_context = on_code_lifetime_changed_handler_context; +} \ No newline at end of file diff --git a/applications/external/totp/workers/generate_totp_code/generate_totp_code.h b/applications/external/totp/workers/generate_totp_code/generate_totp_code.h new file mode 100644 index 000000000..f351ffa68 --- /dev/null +++ b/applications/external/totp/workers/generate_totp_code/generate_totp_code.h @@ -0,0 +1,86 @@ +#pragma once + +#include +#include +#include "../../types/token_info.h" + +typedef uint8_t TotpGenerateCodeWorkerEvent; + +typedef void (*TOTP_NEW_CODE_GENERATED_HANDLER)(bool time_left, void* context); +typedef void (*TOTP_CODE_LIFETIME_CHANGED_HANDLER)(float code_lifetime_percent, void* context); + +typedef struct TotpGenerateCodeWorkerContext TotpGenerateCodeWorkerContext; + +/** + * @brief Generate token worker events + */ +enum TotGenerateCodeWorkerEvents { + + /** + * @brief Reserved, should not be used anywhere + */ + TotpGenerateCodeWorkerEventReserved = 0b00, + + /** + * @brief Stop worker + */ + TotpGenerateCodeWorkerEventStop = 0b01, + + /** + * @brief Trigger token input automation + */ + TotpGenerateCodeWorkerEventForceUpdate = 0b10 +}; + +/** + * @brief Starts generate code worker + * @param code_buffer code buffer to generate code to + * @param token_info token info to be used to generate code + * @param code_buffer_sync code buffer synchronization primitive + * @param timezone_offset timezone offset to be used to generate code + * @param iv initialization vector (IV) to be used to decrypt token secret + * @return worker context + */ +TotpGenerateCodeWorkerContext* totp_generate_code_worker_start( + char* code_buffer, + const TokenInfo* token_info, + FuriMutex* code_buffer_sync, + float timezone_offset, + uint8_t* iv); + +/** + * @brief Stops generate code worker + * @param context worker context + */ +void totp_generate_code_worker_stop(TotpGenerateCodeWorkerContext* context); + +/** + * @brief Notifies generate code worker with a given event + * @param context worker context + * @param event event to notify worker with + */ +void totp_generate_code_worker_notify( + TotpGenerateCodeWorkerContext* context, + TotpGenerateCodeWorkerEvent event); + +/** + * @brief Sets new handler for "on new code generated" event + * @param context worker context + * @param on_new_code_generated_handler handler + * @param on_new_code_generated_handler_context handler context + */ +void totp_generate_code_worker_set_code_generated_handler( + TotpGenerateCodeWorkerContext* context, + TOTP_NEW_CODE_GENERATED_HANDLER on_new_code_generated_handler, + void* on_new_code_generated_handler_context); + +/** + * @brief Sets new handler for "on code lifetime changed" event + * @param context worker context + * @param on_code_lifetime_changed_handler handler + * @param on_code_lifetime_changed_handler_context handler context + */ +void totp_generate_code_worker_set_lifetime_changed_handler( + TotpGenerateCodeWorkerContext* context, + TOTP_CODE_LIFETIME_CHANGED_HANDLER on_code_lifetime_changed_handler, + void* on_code_lifetime_changed_handler_context); \ No newline at end of file diff --git a/applications/external/totp/workers/common.c b/applications/external/totp/workers/type_code_common.c similarity index 71% rename from applications/external/totp/workers/common.c rename to applications/external/totp/workers/type_code_common.c index 8ad0c2b46..bf5818ab2 100644 --- a/applications/external/totp/workers/common.c +++ b/applications/external/totp/workers/type_code_common.c @@ -1,6 +1,6 @@ -#include "common.h" -#include -#include +#include "type_code_common.h" +#include +#include #include "../../services/convert/convert.h" static const uint8_t hid_number_keys[] = { @@ -14,7 +14,7 @@ static const uint8_t hid_number_keys[] = { HID_KEYBOARD_Z}; static uint32_t get_keystroke_delay(TokenAutomationFeature features) { - if(features & TOKEN_AUTOMATION_FEATURE_TYPE_SLOWER) { + if(features & TokenAutomationFeatureTypeSlower) { return 100; } @@ -22,7 +22,7 @@ static uint32_t get_keystroke_delay(TokenAutomationFeature features) { } static uint32_t get_keypress_delay(TokenAutomationFeature features) { - if(features & TOKEN_AUTOMATION_FEATURE_TYPE_SLOWER) { + if(features & TokenAutomationFeatureTypeSlower) { return 60; } @@ -30,7 +30,7 @@ static uint32_t get_keypress_delay(TokenAutomationFeature features) { } static void totp_type_code_worker_press_key( - uint8_t key, + uint16_t key, TOTP_AUTOMATION_KEY_HANDLER key_press_fn, TOTP_AUTOMATION_KEY_HANDLER key_release_fn, TokenAutomationFeature features) { @@ -42,39 +42,38 @@ static void totp_type_code_worker_press_key( void totp_type_code_worker_execute_automation( TOTP_AUTOMATION_KEY_HANDLER key_press_fn, TOTP_AUTOMATION_KEY_HANDLER key_release_fn, - const char* string, - uint8_t string_length, + const char* code_buffer, + uint8_t code_buffer_size, TokenAutomationFeature features) { furi_delay_ms(500); uint8_t i = 0; - totp_type_code_worker_press_key( - HID_KEYBOARD_CAPS_LOCK, key_press_fn, key_release_fn, features); - while(i < string_length && string[i] != 0) { - uint8_t char_index = CONVERT_CHAR_TO_DIGIT(string[i]); + while(i < code_buffer_size && code_buffer[i] != 0) { + uint8_t char_index = CONVERT_CHAR_TO_DIGIT(code_buffer[i]); if(char_index > 9) { - char_index = string[i] - 0x41 + 10; + char_index = code_buffer[i] - 0x41 + 10; } if(char_index > 35) break; - uint8_t hid_kb_key = hid_number_keys[char_index]; + uint16_t hid_kb_key = hid_number_keys[char_index]; + if(char_index > 9) { + hid_kb_key |= KEY_MOD_LEFT_SHIFT; + } + totp_type_code_worker_press_key(hid_kb_key, key_press_fn, key_release_fn, features); furi_delay_ms(get_keystroke_delay(features)); i++; } - if(features & TOKEN_AUTOMATION_FEATURE_ENTER_AT_THE_END) { + if(features & TokenAutomationFeatureEnterAtTheEnd) { furi_delay_ms(get_keystroke_delay(features)); totp_type_code_worker_press_key( HID_KEYBOARD_RETURN, key_press_fn, key_release_fn, features); } - if(features & TOKEN_AUTOMATION_FEATURE_TAB_AT_THE_END) { + if(features & TokenAutomationFeatureTabAtTheEnd) { furi_delay_ms(get_keystroke_delay(features)); totp_type_code_worker_press_key(HID_KEYBOARD_TAB, key_press_fn, key_release_fn, features); } - - totp_type_code_worker_press_key( - HID_KEYBOARD_CAPS_LOCK, key_press_fn, key_release_fn, features); } \ No newline at end of file diff --git a/applications/external/totp/workers/type_code_common.h b/applications/external/totp/workers/type_code_common.h new file mode 100644 index 000000000..db357329a --- /dev/null +++ b/applications/external/totp/workers/type_code_common.h @@ -0,0 +1,20 @@ +#pragma once +#include +#include "../types/token_info.h" + +typedef bool (*TOTP_AUTOMATION_KEY_HANDLER)(uint16_t key); + +/** + * @brief Executes token input automation using given key press\release handlers + * @param key_press_fn key press handler + * @param key_release_fn key release handler + * @param code_buffer code buffer to be typed + * @param code_buffer_size code buffer size + * @param features automation features + */ +void totp_type_code_worker_execute_automation( + TOTP_AUTOMATION_KEY_HANDLER key_press_fn, + TOTP_AUTOMATION_KEY_HANDLER key_release_fn, + const char* code_buffer, + uint8_t code_buffer_size, + TokenAutomationFeature features); \ No newline at end of file diff --git a/applications/external/totp/workers/usb_type_code/usb_type_code.c b/applications/external/totp/workers/usb_type_code/usb_type_code.c index 5f7ccddf8..a391bdf82 100644 --- a/applications/external/totp/workers/usb_type_code/usb_type_code.c +++ b/applications/external/totp/workers/usb_type_code/usb_type_code.c @@ -1,7 +1,21 @@ #include "usb_type_code.h" +#include +#include +#include +#include +#include #include "../../services/convert/convert.h" #include "../../types/token_info.h" -#include "../common.h" +#include "../type_code_common.h" + +struct TotpUsbTypeCodeWorkerContext { + char* code_buffer; + uint8_t code_buffer_size; + uint8_t flags; + FuriThread* thread; + FuriMutex* code_buffer_sync; + FuriHalUsbInterface* usb_mode_prev; +}; static void totp_type_code_worker_restore_usb_mode(TotpUsbTypeCodeWorkerContext* context) { if(context->usb_mode_prev != NULL) { @@ -25,14 +39,14 @@ static void totp_type_code_worker_type_code(TotpUsbTypeCodeWorkerContext* contex } while(!furi_hal_hid_is_connected() && i < 100 && !totp_type_code_worker_stop_requested()); if(furi_hal_hid_is_connected() && - furi_mutex_acquire(context->string_sync, 500) == FuriStatusOk) { + furi_mutex_acquire(context->code_buffer_sync, 500) == FuriStatusOk) { totp_type_code_worker_execute_automation( &furi_hal_hid_kb_press, &furi_hal_hid_kb_release, - context->string, - context->string_length, + context->code_buffer, + context->code_buffer_size, context->flags); - furi_mutex_release(context->string_sync); + furi_mutex_release(context->code_buffer_sync); furi_delay_ms(100); } @@ -43,9 +57,6 @@ static void totp_type_code_worker_type_code(TotpUsbTypeCodeWorkerContext* contex static int32_t totp_type_code_worker_callback(void* context) { furi_check(context); FuriMutex* context_mutex = furi_mutex_alloc(FuriMutexTypeNormal); - if(context_mutex == NULL) { - return 251; - } while(true) { uint32_t flags = furi_thread_flags_wait( @@ -70,14 +81,14 @@ static int32_t totp_type_code_worker_callback(void* context) { } TotpUsbTypeCodeWorkerContext* totp_usb_type_code_worker_start( - char* code_buf, - uint8_t code_buf_length, - FuriMutex* code_buf_update_sync) { + char* code_buffer, + uint8_t code_buffer_size, + FuriMutex* code_buffer_sync) { TotpUsbTypeCodeWorkerContext* context = malloc(sizeof(TotpUsbTypeCodeWorkerContext)); furi_check(context != NULL); - context->string = code_buf; - context->string_length = code_buf_length; - context->string_sync = code_buf_update_sync; + context->code_buffer = code_buffer; + context->code_buffer_size = code_buffer_size; + context->code_buffer_sync = code_buffer_sync; context->thread = furi_thread_alloc(); context->usb_mode_prev = NULL; furi_thread_set_name(context->thread, "TOTPUsbHidWorker"); diff --git a/applications/external/totp/workers/usb_type_code/usb_type_code.h b/applications/external/totp/workers/usb_type_code/usb_type_code.h index d0ea600ce..0a700e7fe 100644 --- a/applications/external/totp/workers/usb_type_code/usb_type_code.h +++ b/applications/external/totp/workers/usb_type_code/usb_type_code.h @@ -1,32 +1,59 @@ #pragma once -#include -#include -#include +#include +#include +#include typedef uint8_t TotpUsbTypeCodeWorkerEvent; -typedef struct { - char* string; - uint8_t string_length; - uint8_t flags; - FuriThread* thread; - FuriMutex* string_sync; - FuriHalUsbInterface* usb_mode_prev; -} TotpUsbTypeCodeWorkerContext; +typedef struct TotpUsbTypeCodeWorkerContext TotpUsbTypeCodeWorkerContext; +/** + * @brief USB token input automation worker events + */ enum TotpUsbTypeCodeWorkerEvents { + + /** + * @brief Reserved, should not be used anywhere + */ TotpUsbTypeCodeWorkerEventReserved = 0b00, + + /** + * @brief Stop worker + */ TotpUsbTypeCodeWorkerEventStop = 0b01, + + /** + * @brief Trigger token input automation + */ TotpUsbTypeCodeWorkerEventType = 0b10 }; +/** + * @brief Starts USB token input automation worker + * @param code_buffer code buffer to be used to automate + * @param code_buffer_size code buffer size + * @param code_buffer_sync code buffer synchronization primitive + * @return worker context + */ TotpUsbTypeCodeWorkerContext* totp_usb_type_code_worker_start( - char* code_buf, - uint8_t code_buf_length, - FuriMutex* code_buf_update_sync); + char* code_buffer, + uint8_t code_buffer_size, + FuriMutex* code_buffer_sync); + +/** + * @brief Stops USB token input automation worker + * @param context worker context + */ void totp_usb_type_code_worker_stop(TotpUsbTypeCodeWorkerContext* context); + +/** + * @brief Notifies USB token input automation worker with a given event + * @param context worker context + * @param event event to notify worker with + * @param flags event flags + */ void totp_usb_type_code_worker_notify( TotpUsbTypeCodeWorkerContext* context, TotpUsbTypeCodeWorkerEvent event, - uint8_t flags); \ No newline at end of file + uint8_t flags); diff --git a/applications/external/uart_terminal/scenes/uart_terminal_scene_console_output.c b/applications/external/uart_terminal/scenes/uart_terminal_scene_console_output.c index 38a5a20e4..6988c42de 100644 --- a/applications/external/uart_terminal/scenes/uart_terminal_scene_console_output.c +++ b/applications/external/uart_terminal/scenes/uart_terminal_scene_console_output.c @@ -114,13 +114,9 @@ void uart_terminal_scene_console_output_on_enter(void* context) { // Send command with CR+LF or newline '\n' if(app->is_command && app->selected_tx_string) { if(app->TERMINAL_MODE == 1) { - // char buffer[240]; - // snprintf(buffer, 240, "%s\r\n", (app->selected_tx_string)); - // uart_terminal_uart_tx((unsigned char *)buffer, strlen(buffer)); uart_terminal_uart_tx( (uint8_t*)(app->selected_tx_string), strlen(app->selected_tx_string)); - uart_terminal_uart_tx((uint8_t*)("\r"), 1); - uart_terminal_uart_tx((uint8_t*)("\n"), 1); + uart_terminal_uart_tx((uint8_t*)("\r\n"), 2); } else { uart_terminal_uart_tx( (uint8_t*)(app->selected_tx_string), strlen(app->selected_tx_string)); @@ -149,9 +145,4 @@ void uart_terminal_scene_console_output_on_exit(void* context) { // Unregister rx callback uart_terminal_uart_set_handle_rx_data_cb(app->uart, NULL); - - // Automatically logut when exiting view - //if(app->is_command) { - // uart_terminal_uart_tx((uint8_t*)("exit\n"), strlen("exit\n")); - //} } \ No newline at end of file diff --git a/applications/external/usb_hid_autofire/CHANGELOG.md b/applications/external/usb_hid_autofire/CHANGELOG.md deleted file mode 100644 index d0924edd3..000000000 --- a/applications/external/usb_hid_autofire/CHANGELOG.md +++ /dev/null @@ -1,13 +0,0 @@ -# Changelog - -## 0.4 -- Show active/inactive state in primary font (bold) - -## 0.3 -- Add a delay between key-presses (with left/right buttons) - -## 0.2 -- Update icon - -## 0.1 -- Initial release of the USB HID Autofire application diff --git a/applications/external/usb_hid_autofire/LICENSE b/applications/external/usb_hid_autofire/LICENSE deleted file mode 100644 index f288702d2..000000000 --- a/applications/external/usb_hid_autofire/LICENSE +++ /dev/null @@ -1,674 +0,0 @@ - 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/usb_hid_autofire/application.fam b/applications/external/usb_hid_autofire/application.fam deleted file mode 100644 index 9e7b9378c..000000000 --- a/applications/external/usb_hid_autofire/application.fam +++ /dev/null @@ -1,13 +0,0 @@ -App( - appid="usb_hid_autofire", - name="USB HID Autofire", - apptype=FlipperAppType.EXTERNAL, - entry_point="usb_hid_autofire_app", - cdefines=["APP_USB_HID_AUTOFIRE"], - requires=[ - "gui", - ], - stack_size=1 * 1024, - fap_icon="usb_hid_autofire.png", - fap_category="Misc", -) diff --git a/applications/external/usb_hid_autofire/tools.c b/applications/external/usb_hid_autofire/tools.c deleted file mode 100644 index 566d00564..000000000 --- a/applications/external/usb_hid_autofire/tools.c +++ /dev/null @@ -1,50 +0,0 @@ -// -// Tools for USB HID Autofire -// - -void strrev(char* arr, int start, int end) { - char temp; - - if(start >= end) return; - - temp = *(arr + start); - *(arr + start) = *(arr + end); - *(arr + end) = temp; - - start++; - end--; - strrev(arr, start, end); -} - -char* itoa(int number, char* arr, int base) { - int i = 0, r, negative = 0; - - if(number == 0) { - arr[i] = '0'; - arr[i + 1] = '\0'; - return arr; - } - - if(number < 0 && base == 10) { - number *= -1; - negative = 1; - } - - while(number != 0) { - r = number % base; - arr[i] = (r > 9) ? (r - 10) + 'a' : r + '0'; - i++; - number /= base; - } - - if(negative) { - arr[i] = '-'; - i++; - } - - strrev(arr, 0, i - 1); - - arr[i] = '\0'; - - return arr; -} diff --git a/applications/external/usb_hid_autofire/tools.h b/applications/external/usb_hid_autofire/tools.h deleted file mode 100644 index 7e5226514..000000000 --- a/applications/external/usb_hid_autofire/tools.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef FLIPPERZERO_FIRMWARE_TOOLS_H -#define FLIPPERZERO_FIRMWARE_TOOLS_H - -void strrev(char* arr, int start, int end); -char* itoa(int number, char* arr, int base); - -#endif //FLIPPERZERO_FIRMWARE_TOOLS_H diff --git a/applications/external/usb_hid_autofire/usb_hid_autofire.c b/applications/external/usb_hid_autofire/usb_hid_autofire.c deleted file mode 100644 index cf8077ae7..000000000 --- a/applications/external/usb_hid_autofire/usb_hid_autofire.c +++ /dev/null @@ -1,127 +0,0 @@ -#include -#include -#include -#include -#include -#include "version.h" -#include "tools.h" - -// Uncomment to be able to make a screenshot -//#define USB_HID_AUTOFIRE_SCREENSHOT - -typedef enum { - EventTypeInput, -} EventType; - -typedef struct { - union { - InputEvent input; - }; - EventType type; -} UsbMouseEvent; - -bool btn_left_autofire = false; -uint32_t autofire_delay = 10; - -static void usb_hid_autofire_render_callback(Canvas* canvas, void* ctx) { - UNUSED(ctx); - char autofire_delay_str[12]; - //std::string pi = "pi is " + std::to_string(3.1415926); - itoa(autofire_delay, autofire_delay_str, 10); - //sprintf(autofire_delay_str, "%lu", autofire_delay); - - canvas_clear(canvas); - - canvas_set_font(canvas, FontPrimary); - canvas_draw_str(canvas, 0, 10, "USB HID Autofire"); - canvas_draw_str(canvas, 0, 34, btn_left_autofire ? "" : ""); - - canvas_set_font(canvas, FontSecondary); - canvas_draw_str(canvas, 90, 10, "v"); - canvas_draw_str(canvas, 96, 10, VERSION); - canvas_draw_str(canvas, 0, 22, "Press [ok] for auto left clicking"); - canvas_draw_str(canvas, 0, 46, "delay [ms]:"); - canvas_draw_str(canvas, 50, 46, autofire_delay_str); - canvas_draw_str(canvas, 0, 63, "Press [back] to exit"); -} - -static void usb_hid_autofire_input_callback(InputEvent* input_event, void* ctx) { - FuriMessageQueue* event_queue = ctx; - - UsbMouseEvent event; - event.type = EventTypeInput; - event.input = *input_event; - furi_message_queue_put(event_queue, &event, FuriWaitForever); -} - -int32_t usb_hid_autofire_app(void* p) { - UNUSED(p); - FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(UsbMouseEvent)); - furi_check(event_queue); - ViewPort* view_port = view_port_alloc(); - - FuriHalUsbInterface* usb_mode_prev = furi_hal_usb_get_config(); -#ifndef USB_HID_AUTOFIRE_SCREENSHOT - furi_hal_usb_unlock(); - furi_check(furi_hal_usb_set_config(&usb_hid, NULL) == true); -#endif - - view_port_draw_callback_set(view_port, usb_hid_autofire_render_callback, NULL); - view_port_input_callback_set(view_port, usb_hid_autofire_input_callback, event_queue); - - // Open GUI and register view_port - Gui* gui = furi_record_open(RECORD_GUI); - gui_add_view_port(gui, view_port, GuiLayerFullscreen); - - UsbMouseEvent event; - while(1) { - FuriStatus event_status = furi_message_queue_get(event_queue, &event, 50); - - if(event_status == FuriStatusOk) { - if(event.type == EventTypeInput) { - if(event.input.key == InputKeyBack) { - break; - } - - if(event.input.type != InputTypeRelease) { - continue; - } - - switch(event.input.key) { - case InputKeyOk: - btn_left_autofire = !btn_left_autofire; - break; - case InputKeyLeft: - if(autofire_delay > 0) { - autofire_delay -= 10; - } - break; - case InputKeyRight: - autofire_delay += 10; - break; - default: - break; - } - } - } - - if(btn_left_autofire) { - furi_hal_hid_mouse_press(HID_MOUSE_BTN_LEFT); - // TODO: Don't wait, but use the timer directly to just don't send the release event (see furi_hal_cortex_delay_us) - furi_delay_us(autofire_delay * 500); - furi_hal_hid_mouse_release(HID_MOUSE_BTN_LEFT); - furi_delay_us(autofire_delay * 500); - } - - view_port_update(view_port); - } - - furi_hal_usb_set_config(usb_mode_prev, NULL); - - // remove & free all stuff created by app - gui_remove_view_port(gui, view_port); - view_port_free(view_port); - furi_message_queue_free(event_queue); - - return 0; -} diff --git a/applications/external/usb_hid_autofire/usb_hid_autofire.kra b/applications/external/usb_hid_autofire/usb_hid_autofire.kra deleted file mode 100644 index 21d416548..000000000 Binary files a/applications/external/usb_hid_autofire/usb_hid_autofire.kra and /dev/null differ diff --git a/applications/external/usb_hid_autofire/usb_hid_autofire.png b/applications/external/usb_hid_autofire/usb_hid_autofire.png deleted file mode 100644 index 369bff022..000000000 Binary files a/applications/external/usb_hid_autofire/usb_hid_autofire.png and /dev/null differ diff --git a/applications/external/usb_hid_autofire/usb_hid_autofire.svg b/applications/external/usb_hid_autofire/usb_hid_autofire.svg deleted file mode 100644 index ed66f3cfd..000000000 --- a/applications/external/usb_hid_autofire/usb_hid_autofire.svg +++ /dev/null @@ -1,75 +0,0 @@ - - - - - - - - - A - - diff --git a/applications/external/usb_hid_autofire/version.h b/applications/external/usb_hid_autofire/version.h deleted file mode 100644 index ac1f5d0fa..000000000 --- a/applications/external/usb_hid_autofire/version.h +++ /dev/null @@ -1 +0,0 @@ -#define VERSION "0.4" diff --git a/applications/external/wav_player/wav_parser.c b/applications/external/wav_player/wav_parser.c index 1f534bacb..8c1f22b19 100644 --- a/applications/external/wav_player/wav_parser.c +++ b/applications/external/wav_player/wav_parser.c @@ -11,7 +11,7 @@ const char* format_text(FormatTag tag) { default: return "Unknown"; } -}; +} struct WavParser { WavHeaderChunk header; diff --git a/applications/external/weather_station/protocols/protocol_items.c b/applications/external/weather_station/protocols/protocol_items.c index 2c9d751c7..cd4bae76d 100644 --- a/applications/external/weather_station/protocols/protocol_items.c +++ b/applications/external/weather_station/protocols/protocol_items.c @@ -16,6 +16,7 @@ const SubGhzProtocol* weather_station_protocol_registry_items[] = { &ws_protocol_auriol_th, &ws_protocol_oregon_v1, &ws_protocol_tx_8300, + &ws_protocol_wendox_w6726, }; const SubGhzProtocolRegistry weather_station_protocol_registry = { diff --git a/applications/external/weather_station/protocols/protocol_items.h b/applications/external/weather_station/protocols/protocol_items.h index f9e443abc..0398c11f2 100644 --- a/applications/external/weather_station/protocols/protocol_items.h +++ b/applications/external/weather_station/protocols/protocol_items.h @@ -16,5 +16,6 @@ #include "auriol_hg0601a.h" #include "oregon_v1.h" #include "tx_8300.h" +#include "wendox_w6726.h" extern const SubGhzProtocolRegistry weather_station_protocol_registry; diff --git a/applications/external/weather_station/protocols/wendox_w6726.c b/applications/external/weather_station/protocols/wendox_w6726.c new file mode 100644 index 000000000..265c77f70 --- /dev/null +++ b/applications/external/weather_station/protocols/wendox_w6726.c @@ -0,0 +1,299 @@ +#include "wendox_w6726.h" + +#define TAG "WSProtocolWendoxW6726" + +/* + * Wendox W6726 + * + * Temperature -50°Ρ to +70°Ρ + * _ _ _ __ _ + * _| |___| |___| |___ ... | |_| |__...._______________ + * preamble data guard time + * + * 3 reps every 3 minutes + * in the first message 11 bytes of the preamble in the rest by 7 + * + * bit 0: 1955-hi, 5865-lo + * bit 1: 5865-hi, 1955-lo + * guard time: 12*1955+(lo last bit) + * data: 29 bit + * + * IIIII | ZTTTTTTTTT | uuuuuuuBuu | CCCC + * + * I: identification; + * Z: temperature sign; + * T: temperature sign dependent +12 Ρ°; + * B: battery low; flag to indicate low battery voltage; + * C: CRC4 (polynomial = 0x9, start_data = 0xD); + * u: unknown; + */ + +static const SubGhzBlockConst ws_protocol_wendox_w6726_const = { + .te_short = 1955, + .te_long = 5865, + .te_delta = 300, + .min_count_bit_for_found = 29, +}; + +struct WSProtocolDecoderWendoxW6726 { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + WSBlockGeneric generic; + + uint16_t header_count; +}; + +struct WSProtocolEncoderWendoxW6726 { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + WSBlockGeneric generic; +}; + +typedef enum { + WendoxW6726DecoderStepReset = 0, + WendoxW6726DecoderStepCheckPreambule, + WendoxW6726DecoderStepSaveDuration, + WendoxW6726DecoderStepCheckDuration, +} WendoxW6726DecoderStep; + +const SubGhzProtocolDecoder ws_protocol_wendox_w6726_decoder = { + .alloc = ws_protocol_decoder_wendox_w6726_alloc, + .free = ws_protocol_decoder_wendox_w6726_free, + + .feed = ws_protocol_decoder_wendox_w6726_feed, + .reset = ws_protocol_decoder_wendox_w6726_reset, + + .get_hash_data = ws_protocol_decoder_wendox_w6726_get_hash_data, + .serialize = ws_protocol_decoder_wendox_w6726_serialize, + .deserialize = ws_protocol_decoder_wendox_w6726_deserialize, + .get_string = ws_protocol_decoder_wendox_w6726_get_string, +}; + +const SubGhzProtocolEncoder ws_protocol_wendox_w6726_encoder = { + .alloc = NULL, + .free = NULL, + + .deserialize = NULL, + .stop = NULL, + .yield = NULL, +}; + +const SubGhzProtocol ws_protocol_wendox_w6726 = { + .name = WS_PROTOCOL_WENDOX_W6726_NAME, + .type = SubGhzProtocolWeatherStation, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | + SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, + + .decoder = &ws_protocol_wendox_w6726_decoder, + .encoder = &ws_protocol_wendox_w6726_encoder, +}; + +void* ws_protocol_decoder_wendox_w6726_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + WSProtocolDecoderWendoxW6726* instance = malloc(sizeof(WSProtocolDecoderWendoxW6726)); + instance->base.protocol = &ws_protocol_wendox_w6726; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void ws_protocol_decoder_wendox_w6726_free(void* context) { + furi_assert(context); + WSProtocolDecoderWendoxW6726* instance = context; + free(instance); +} + +void ws_protocol_decoder_wendox_w6726_reset(void* context) { + furi_assert(context); + WSProtocolDecoderWendoxW6726* instance = context; + instance->decoder.parser_step = WendoxW6726DecoderStepReset; +} + +static bool ws_protocol_wendox_w6726_check(WSProtocolDecoderWendoxW6726* instance) { + if(!instance->decoder.decode_data) return false; + uint8_t msg[] = { + instance->decoder.decode_data >> 28, + instance->decoder.decode_data >> 20, + instance->decoder.decode_data >> 12, + instance->decoder.decode_data >> 4}; + + uint8_t crc = subghz_protocol_blocks_crc4(msg, 4, 0x9, 0xD); + return (crc == (instance->decoder.decode_data & 0x0F)); +} + +/** + * Analysis of received data + * @param instance Pointer to a WSBlockGeneric* instance + */ +static void ws_protocol_wendox_w6726_remote_controller(WSBlockGeneric* instance) { + instance->id = (instance->data >> 24) & 0xFF; + instance->battery_low = (instance->data >> 6) & 1; + instance->channel = WS_NO_CHANNEL; + + if(((instance->data >> 23) & 1)) { + instance->temp = (float)(((instance->data >> 14) & 0x1FF) + 12) / 10.0f; + } else { + instance->temp = (float)((~(instance->data >> 14) & 0x1FF) + 1 - 12) / -10.0f; + } + + if(instance->temp < -50.0f) { + instance->temp = -50.0f; + } else if(instance->temp > 70.0f) { + instance->temp = 70.0f; + } + + instance->btn = WS_NO_BTN; + instance->humidity = WS_NO_HUMIDITY; +} + +void ws_protocol_decoder_wendox_w6726_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + WSProtocolDecoderWendoxW6726* instance = context; + + switch(instance->decoder.parser_step) { + case WendoxW6726DecoderStepReset: + if((level) && (DURATION_DIFF(duration, ws_protocol_wendox_w6726_const.te_short) < + ws_protocol_wendox_w6726_const.te_delta)) { + instance->decoder.parser_step = WendoxW6726DecoderStepCheckPreambule; + instance->decoder.te_last = duration; + instance->header_count = 0; + } + break; + + case WendoxW6726DecoderStepCheckPreambule: + if(level) { + instance->decoder.te_last = duration; + } else { + if((DURATION_DIFF(instance->decoder.te_last, ws_protocol_wendox_w6726_const.te_short) < + ws_protocol_wendox_w6726_const.te_delta * 1) && + (DURATION_DIFF(duration, ws_protocol_wendox_w6726_const.te_long) < + ws_protocol_wendox_w6726_const.te_delta * 2)) { + instance->header_count++; + } else if((instance->header_count > 4) && (instance->header_count < 12)) { + if((DURATION_DIFF( + instance->decoder.te_last, ws_protocol_wendox_w6726_const.te_long) < + ws_protocol_wendox_w6726_const.te_delta * 2) && + (DURATION_DIFF(duration, ws_protocol_wendox_w6726_const.te_short) < + ws_protocol_wendox_w6726_const.te_delta)) { + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = WendoxW6726DecoderStepSaveDuration; + } else { + instance->decoder.parser_step = WendoxW6726DecoderStepReset; + } + + } else { + instance->decoder.parser_step = WendoxW6726DecoderStepReset; + } + } + break; + + case WendoxW6726DecoderStepSaveDuration: + if(level) { + instance->decoder.te_last = duration; + instance->decoder.parser_step = WendoxW6726DecoderStepCheckDuration; + } else { + instance->decoder.parser_step = WendoxW6726DecoderStepReset; + } + break; + + case WendoxW6726DecoderStepCheckDuration: + if(!level) { + if(duration > + ws_protocol_wendox_w6726_const.te_short + ws_protocol_wendox_w6726_const.te_long) { + if(DURATION_DIFF( + instance->decoder.te_last, ws_protocol_wendox_w6726_const.te_short) < + ws_protocol_wendox_w6726_const.te_delta) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = WendoxW6726DecoderStepSaveDuration; + } else if( + DURATION_DIFF( + instance->decoder.te_last, ws_protocol_wendox_w6726_const.te_long) < + ws_protocol_wendox_w6726_const.te_delta * 2) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = WendoxW6726DecoderStepSaveDuration; + } else { + instance->decoder.parser_step = WendoxW6726DecoderStepReset; + } + if((instance->decoder.decode_count_bit == + ws_protocol_wendox_w6726_const.min_count_bit_for_found) && + ws_protocol_wendox_w6726_check(instance)) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + ws_protocol_wendox_w6726_remote_controller(&instance->generic); + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + + instance->decoder.parser_step = WendoxW6726DecoderStepReset; + } else if( + (DURATION_DIFF(instance->decoder.te_last, ws_protocol_wendox_w6726_const.te_short) < + ws_protocol_wendox_w6726_const.te_delta) && + (DURATION_DIFF(duration, ws_protocol_wendox_w6726_const.te_long) < + ws_protocol_wendox_w6726_const.te_delta * 3)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = WendoxW6726DecoderStepSaveDuration; + } else if( + (DURATION_DIFF(instance->decoder.te_last, ws_protocol_wendox_w6726_const.te_long) < + ws_protocol_wendox_w6726_const.te_delta * 2) && + (DURATION_DIFF(duration, ws_protocol_wendox_w6726_const.te_short) < + ws_protocol_wendox_w6726_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = WendoxW6726DecoderStepSaveDuration; + } else { + instance->decoder.parser_step = WendoxW6726DecoderStepReset; + } + } else { + instance->decoder.parser_step = WendoxW6726DecoderStepReset; + } + break; + } +} + +uint8_t ws_protocol_decoder_wendox_w6726_get_hash_data(void* context) { + furi_assert(context); + WSProtocolDecoderWendoxW6726* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +SubGhzProtocolStatus ws_protocol_decoder_wendox_w6726_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + WSProtocolDecoderWendoxW6726* instance = context; + return ws_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +SubGhzProtocolStatus + ws_protocol_decoder_wendox_w6726_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + WSProtocolDecoderWendoxW6726* instance = context; + return ws_block_generic_deserialize_check_count_bit( + &instance->generic, + flipper_format, + ws_protocol_wendox_w6726_const.min_count_bit_for_found); +} + +void ws_protocol_decoder_wendox_w6726_get_string(void* context, FuriString* output) { + furi_assert(context); + WSProtocolDecoderWendoxW6726* instance = context; + furi_string_printf( + output, + "%s %dbit\r\n" + "Key:0x%lX%08lX\r\n" + "Sn:0x%lX Ch:%d Bat:%d\r\n" + "Temp:%3.1f C Hum:%d%%", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data >> 32), + (uint32_t)(instance->generic.data), + instance->generic.id, + instance->generic.channel, + instance->generic.battery_low, + (double)instance->generic.temp, + instance->generic.humidity); +} diff --git a/applications/external/weather_station/protocols/wendox_w6726.h b/applications/external/weather_station/protocols/wendox_w6726.h new file mode 100644 index 000000000..236777a1c --- /dev/null +++ b/applications/external/weather_station/protocols/wendox_w6726.h @@ -0,0 +1,80 @@ +#pragma once + +#include + +#include +#include +#include +#include "ws_generic.h" +#include + +#define WS_PROTOCOL_WENDOX_W6726_NAME "Wendox W6726" + +typedef struct WSProtocolDecoderWendoxW6726 WSProtocolDecoderWendoxW6726; +typedef struct WSProtocolEncoderWendoxW6726 WSProtocolEncoderWendoxW6726; + +extern const SubGhzProtocolDecoder ws_protocol_wendox_w6726_decoder; +extern const SubGhzProtocolEncoder ws_protocol_wendox_w6726_encoder; +extern const SubGhzProtocol ws_protocol_wendox_w6726; + +/** + * Allocate WSProtocolDecoderWendoxW6726. + * @param environment Pointer to a SubGhzEnvironment instance + * @return WSProtocolDecoderWendoxW6726* pointer to a WSProtocolDecoderWendoxW6726 instance + */ +void* ws_protocol_decoder_wendox_w6726_alloc(SubGhzEnvironment* environment); + +/** + * Free WSProtocolDecoderWendoxW6726. + * @param context Pointer to a WSProtocolDecoderWendoxW6726 instance + */ +void ws_protocol_decoder_wendox_w6726_free(void* context); + +/** + * Reset decoder WSProtocolDecoderWendoxW6726. + * @param context Pointer to a WSProtocolDecoderWendoxW6726 instance + */ +void ws_protocol_decoder_wendox_w6726_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a WSProtocolDecoderWendoxW6726 instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void ws_protocol_decoder_wendox_w6726_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a WSProtocolDecoderWendoxW6726 instance + * @return hash Hash sum + */ +uint8_t ws_protocol_decoder_wendox_w6726_get_hash_data(void* context); + +/** + * Serialize data WSProtocolDecoderWendoxW6726. + * @param context Pointer to a WSProtocolDecoderWendoxW6726 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return status + */ +SubGhzProtocolStatus ws_protocol_decoder_wendox_w6726_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data WSProtocolDecoderWendoxW6726. + * @param context Pointer to a WSProtocolDecoderWendoxW6726 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return status + */ +SubGhzProtocolStatus + ws_protocol_decoder_wendox_w6726_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a WSProtocolDecoderWendoxW6726 instance + * @param output Resulting text + */ +void ws_protocol_decoder_wendox_w6726_get_string(void* context, FuriString* output); diff --git a/applications/external/weather_station/scenes/weather_station_receiver.c b/applications/external/weather_station/scenes/weather_station_receiver.c index e76810430..79d01f13d 100644 --- a/applications/external/weather_station/scenes/weather_station_receiver.c +++ b/applications/external/weather_station/scenes/weather_station_receiver.c @@ -133,7 +133,7 @@ void weather_station_scene_receiver_on_enter(void* context) { if(app->txrx->txrx_state == WSTxRxStateRx) { ws_rx_end(app); - }; + } if((app->txrx->txrx_state == WSTxRxStateIDLE) || (app->txrx->txrx_state == WSTxRxStateSleep)) { ws_begin( app, @@ -157,7 +157,7 @@ bool weather_station_scene_receiver_on_event(void* context, SceneManagerEvent ev if(app->txrx->txrx_state == WSTxRxStateRx) { ws_rx_end(app); ws_sleep(app); - }; + } app->txrx->hopper_state = WSHopperStateOFF; app->txrx->idx_menu_chosen = 0; subghz_receiver_set_rx_callback(app->txrx->receiver, NULL, app); diff --git a/applications/external/weather_station/weather_station_app.c b/applications/external/weather_station/weather_station_app.c index a3135a6b0..8bea4961d 100644 --- a/applications/external/weather_station/weather_station_app.c +++ b/applications/external/weather_station/weather_station_app.c @@ -110,7 +110,8 @@ WeatherStationApp* weather_station_app_alloc() { // Auto switch to internal radio if external radio is not available furi_delay_ms(15); if(!furi_hal_subghz_check_radio()) { - furi_hal_subghz_set_radio_type(SubGhzRadioInternal); + furi_hal_subghz_select_radio_type(SubGhzRadioInternal); + furi_hal_subghz_init_radio_type(SubGhzRadioInternal); } furi_hal_power_suppress_charge_enter(); @@ -128,6 +129,8 @@ void weather_station_app_free(WeatherStationApp* app) { // 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); // Submenu view_dispatcher_remove_view(app->view_dispatcher, WeatherStationViewSubmenu); diff --git a/applications/external/weather_station/weather_station_app_i.c b/applications/external/weather_station/weather_station_app_i.c index 7236b6625..2e83a6e30 100644 --- a/applications/external/weather_station/weather_station_app_i.c +++ b/applications/external/weather_station/weather_station_app_i.c @@ -146,7 +146,7 @@ void ws_hopper_update(WeatherStationApp* app) { if(app->txrx->txrx_state == WSTxRxStateRx) { ws_rx_end(app); - }; + } if(app->txrx->txrx_state == WSTxRxStateIDLE) { subghz_receiver_reset(app->txrx->receiver); app->txrx->preset->frequency = diff --git a/applications/external/wifi_marauder_companion/application.fam b/applications/external/wifi_marauder_companion/application.fam index 5fe303b00..746819b99 100644 --- a/applications/external/wifi_marauder_companion/application.fam +++ b/applications/external/wifi_marauder_companion/application.fam @@ -8,4 +8,5 @@ App( order=90, fap_icon="wifi_10px.png", fap_category="WiFi", + fap_icon_assets="assets", ) diff --git a/applications/external/wifi_marauder_companion/assets/DolphinCommon_56x48.png b/applications/external/wifi_marauder_companion/assets/DolphinCommon_56x48.png new file mode 100644 index 000000000..089aaed83 Binary files /dev/null and b/applications/external/wifi_marauder_companion/assets/DolphinCommon_56x48.png differ diff --git a/applications/external/wifi_marauder_companion/assets/KeyBackspaceSelected_16x9.png b/applications/external/wifi_marauder_companion/assets/KeyBackspaceSelected_16x9.png new file mode 100644 index 000000000..7cc0759a8 Binary files /dev/null and b/applications/external/wifi_marauder_companion/assets/KeyBackspaceSelected_16x9.png differ diff --git a/applications/external/wifi_marauder_companion/assets/KeyBackspace_16x9.png b/applications/external/wifi_marauder_companion/assets/KeyBackspace_16x9.png new file mode 100644 index 000000000..9946232d9 Binary files /dev/null and b/applications/external/wifi_marauder_companion/assets/KeyBackspace_16x9.png differ diff --git a/applications/external/wifi_marauder_companion/assets/KeySaveSelected_24x11.png b/applications/external/wifi_marauder_companion/assets/KeySaveSelected_24x11.png new file mode 100644 index 000000000..eeb3569d3 Binary files /dev/null and b/applications/external/wifi_marauder_companion/assets/KeySaveSelected_24x11.png differ diff --git a/applications/external/wifi_marauder_companion/assets/KeySave_24x11.png b/applications/external/wifi_marauder_companion/assets/KeySave_24x11.png new file mode 100644 index 000000000..e7dba987a Binary files /dev/null and b/applications/external/wifi_marauder_companion/assets/KeySave_24x11.png differ diff --git a/applications/external/wifi_marauder_companion/assets/Text_10x10.png b/applications/external/wifi_marauder_companion/assets/Text_10x10.png new file mode 100644 index 000000000..8e8a6183d Binary files /dev/null and b/applications/external/wifi_marauder_companion/assets/Text_10x10.png differ diff --git a/applications/external/wifi_marauder_companion/assets/WarningDolphin_45x42.png b/applications/external/wifi_marauder_companion/assets/WarningDolphin_45x42.png new file mode 100644 index 000000000..d766ffbb4 Binary files /dev/null and b/applications/external/wifi_marauder_companion/assets/WarningDolphin_45x42.png differ diff --git a/applications/external/wifi_marauder_companion/file/sequential_file.c b/applications/external/wifi_marauder_companion/file/sequential_file.c new file mode 100644 index 000000000..d780deb12 --- /dev/null +++ b/applications/external/wifi_marauder_companion/file/sequential_file.c @@ -0,0 +1,46 @@ +#include "sequential_file.h" + +char* sequential_file_resolve_path( + Storage* storage, + const char* dir, + const char* prefix, + const char* extension) { + if(storage == NULL || dir == NULL || prefix == NULL || extension == NULL) { + return NULL; + } + + char file_path[256]; + int file_index = 0; + + do { + if(snprintf( + file_path, sizeof(file_path), "%s/%s_%d.%s", dir, prefix, file_index, extension) < + 0) { + return NULL; + } + file_index++; + } while(storage_file_exists(storage, file_path)); + + return strdup(file_path); +} + +bool sequential_file_open( + Storage* storage, + File* file, + const char* dir, + const char* prefix, + const char* extension) { + if(storage == NULL || file == NULL || dir == NULL || prefix == NULL || extension == NULL) { + return false; + } + + char* file_path = sequential_file_resolve_path(storage, dir, prefix, extension); + if(file_path == NULL) { + return false; + } + + bool success = storage_file_open(file, file_path, FSAM_WRITE, FSOM_CREATE_ALWAYS); + free(file_path); + + return success; +} \ No newline at end of file diff --git a/applications/external/wifi_marauder_companion/file/sequential_file.h b/applications/external/wifi_marauder_companion/file/sequential_file.h new file mode 100644 index 000000000..4fd4794c2 --- /dev/null +++ b/applications/external/wifi_marauder_companion/file/sequential_file.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +char* sequential_file_resolve_path( + Storage* storage, + const char* dir, + const char* prefix, + const char* extension); +bool sequential_file_open( + Storage* storage, + File* file, + const char* dir, + const char* prefix, + const char* extension); \ No newline at end of file diff --git a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_config.h b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_config.h index 715897d17..ae976c6bf 100644 --- a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_config.h +++ b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_config.h @@ -3,3 +3,12 @@ ADD_SCENE(wifi_marauder, console_output, ConsoleOutput) ADD_SCENE(wifi_marauder, text_input, TextInput) ADD_SCENE(wifi_marauder, settings_init, SettingsInit) ADD_SCENE(wifi_marauder, log_viewer, LogViewer) +ADD_SCENE(wifi_marauder, user_input, UserInput) +ADD_SCENE(wifi_marauder, script_select, ScriptSelect) +ADD_SCENE(wifi_marauder, script_options, ScriptOptions) +ADD_SCENE(wifi_marauder, script_edit, ScriptEdit) +ADD_SCENE(wifi_marauder, script_settings, ScriptSettings) +ADD_SCENE(wifi_marauder, script_confirm_delete, ScriptConfirmDelete) +ADD_SCENE(wifi_marauder, script_stage_edit, ScriptStageEdit) +ADD_SCENE(wifi_marauder, script_stage_add, ScriptStageAdd) +ADD_SCENE(wifi_marauder, script_stage_edit_list, ScriptStageEditList) diff --git a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_console_output.c b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_console_output.c index 0729500eb..05d94fe80 100644 --- a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_console_output.c +++ b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_console_output.c @@ -1,5 +1,34 @@ #include "../wifi_marauder_app_i.h" +char* _wifi_marauder_get_prefix_from_cmd(const char* command) { + int end = strcspn(command, " "); + char* prefix = (char*)malloc(sizeof(char) * (end + 1)); + strncpy(prefix, command, end); + prefix[end] = '\0'; + return prefix; +} + +bool _wifi_marauder_is_save_pcaps_enabled(WifiMarauderApp* app) { + if(!app->ok_to_save_pcaps) { + return false; + } + // If it is a script that contains a sniff function + if(app->script != NULL) { + return wifi_marauder_script_has_stage(app->script, WifiMarauderScriptStageTypeSniffRaw) || + wifi_marauder_script_has_stage( + app->script, WifiMarauderScriptStageTypeSniffBeacon) || + wifi_marauder_script_has_stage( + app->script, WifiMarauderScriptStageTypeSniffDeauth) || + wifi_marauder_script_has_stage(app->script, WifiMarauderScriptStageTypeSniffEsp) || + wifi_marauder_script_has_stage( + app->script, WifiMarauderScriptStageTypeSniffPmkid) || + wifi_marauder_script_has_stage(app->script, WifiMarauderScriptStageTypeSniffPwn); + } + // If it is a sniff function + return app->is_command && app->selected_tx_string && + strncmp("sniff", app->selected_tx_string, strlen("sniff")) == 0; +} + void wifi_marauder_console_output_handle_rx_data_cb(uint8_t* buf, size_t len, void* context) { furi_assert(context); WifiMarauderApp* app = context; @@ -34,23 +63,29 @@ void wifi_marauder_console_output_handle_rx_packets_cb(uint8_t* buf, size_t len, void wifi_marauder_scene_console_output_on_enter(void* context) { WifiMarauderApp* app = context; + // Reset text box and set font TextBox* text_box = app->text_box; - text_box_reset(app->text_box); + text_box_reset(text_box); text_box_set_font(text_box, TextBoxFontText); + + // Set focus on start or end if(app->focus_console_start) { text_box_set_focus(text_box, TextBoxFocusStart); } else { text_box_set_focus(text_box, TextBoxFocusEnd); } + + // Set command-related messages if(app->is_command) { furi_string_reset(app->text_box_store); app->text_box_store_strlen = 0; + // Help message if(0 == strncmp("help", app->selected_tx_string, strlen("help"))) { const char* help_msg = "Marauder companion " WIFI_MARAUDER_APP_VERSION "\n"; furi_string_cat_str(app->text_box_store, help_msg); app->text_box_store_strlen += strlen(help_msg); } - + // Stopscan message if(app->show_stopscan_tip) { const char* help_msg = "Press BACK to send stopscan\n"; furi_string_cat_str(app->text_box_store, help_msg); @@ -58,13 +93,14 @@ void wifi_marauder_scene_console_output_on_enter(void* context) { } } - // Set starting text - for "View Log from end", this will just be what was already in the text box store + // Set starting text text_box_set_text(app->text_box, furi_string_get_cstr(app->text_box_store)); + // Set scene state and switch view scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneConsoleOutput, 0); view_dispatcher_switch_to_view(app->view_dispatcher, WifiMarauderAppViewConsoleOutput); - // Register callback to receive data + // Register callbacks to receive data wifi_marauder_uart_set_handle_rx_data_cb( app->uart, wifi_marauder_console_output_handle_rx_data_cb); // setup callback for general log rx thread @@ -73,25 +109,53 @@ void wifi_marauder_scene_console_output_on_enter(void* context) { wifi_marauder_console_output_handle_rx_packets_cb); // setup callback for packets rx thread // Get ready to send command - if(app->is_command && app->selected_tx_string) { + if((app->is_command && app->selected_tx_string) || app->script) { + const char* prefix = + strlen(app->selected_tx_string) > 0 ? + _wifi_marauder_get_prefix_from_cmd(app->selected_tx_string) : // Function name + app->script->name; // Script name + // Create files *before* sending command // (it takes time to iterate through the directory) if(app->ok_to_save_logs) { - app->is_writing_log = true; - wifi_marauder_create_log_file(app); + strcpy( + app->log_file_path, + sequential_file_resolve_path( + app->storage, MARAUDER_APP_FOLDER_LOGS, prefix, "log")); + if(app->log_file_path != NULL) { + if(storage_file_open( + app->log_file, app->log_file_path, FSAM_WRITE, FSOM_CREATE_ALWAYS)) { + app->is_writing_log = true; + } else { + dialog_message_show_storage_error(app->dialogs, "Cannot open log file"); + } + } else { + dialog_message_show_storage_error(app->dialogs, "Cannot resolve log path"); + } } - // If it is a sniff function, open the pcap file for recording - if(app->ok_to_save_pcaps && - strncmp("sniff", app->selected_tx_string, strlen("sniff")) == 0) { - app->is_writing_pcap = true; - wifi_marauder_create_pcap_file(app); + // If it is a sniff function or script, open the pcap file for recording + if(_wifi_marauder_is_save_pcaps_enabled(app)) { + if(sequential_file_open( + app->storage, app->capture_file, MARAUDER_APP_FOLDER_PCAPS, prefix, "pcap")) { + app->is_writing_pcap = true; + } else { + dialog_message_show_storage_error(app->dialogs, "Cannot open pcap file"); + } } // Send command with newline '\n' - wifi_marauder_uart_tx( - (uint8_t*)(app->selected_tx_string), strlen(app->selected_tx_string)); - wifi_marauder_uart_tx((uint8_t*)("\n"), 1); + if(app->selected_tx_string) { + wifi_marauder_uart_tx( + (uint8_t*)(app->selected_tx_string), strlen(app->selected_tx_string)); + wifi_marauder_uart_tx((uint8_t*)("\n"), 1); + } + + // Run the script if the file with the script has been opened + if(app->script != NULL) { + app->script_worker = wifi_marauder_script_worker_alloc(); + wifi_marauder_script_worker_start(app->script_worker, app->script); + } } } @@ -113,14 +177,18 @@ bool wifi_marauder_scene_console_output_on_event(void* context, SceneManagerEven void wifi_marauder_scene_console_output_on_exit(void* context) { WifiMarauderApp* app = context; + // Automatically stop the scan when exiting view + if(app->is_command) { + wifi_marauder_uart_tx((uint8_t*)("stopscan\n"), strlen("stopscan\n")); + furi_delay_ms(50); + } + // Unregister rx callback wifi_marauder_uart_set_handle_rx_data_cb(app->uart, NULL); wifi_marauder_uart_set_handle_rx_data_cb(app->lp_uart, NULL); - // Automatically stop the scan when exiting view - if(app->is_command) { - wifi_marauder_uart_tx((uint8_t*)("stopscan\n"), strlen("stopscan\n")); - } + wifi_marauder_script_worker_free(app->script_worker); + app->script_worker = NULL; app->is_writing_pcap = false; if(app->capture_file && storage_file_is_open(app->capture_file)) { diff --git a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_log_viewer.c b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_log_viewer.c index f4e84ccc8..6edb4a49d 100644 --- a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_log_viewer.c +++ b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_log_viewer.c @@ -148,8 +148,10 @@ bool wifi_marauder_scene_log_viewer_on_event(void* context, SceneManagerEvent ev // Browse FuriString* predefined_filepath = furi_string_alloc_set_str(MARAUDER_APP_FOLDER_LOGS); FuriString* selected_filepath = furi_string_alloc(); + DialogsFileBrowserOptions browser_options; + dialog_file_browser_set_basic_options(&browser_options, ".log", &I_Text_10x10); if(dialog_file_browser_show( - app->dialogs, selected_filepath, predefined_filepath, NULL)) { + app->dialogs, selected_filepath, predefined_filepath, &browser_options)) { strncpy( app->log_file_path, furi_string_get_cstr(selected_filepath), diff --git a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_confirm_delete.c b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_confirm_delete.c new file mode 100644 index 000000000..8e436fe2b --- /dev/null +++ b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_confirm_delete.c @@ -0,0 +1,83 @@ +#include "../wifi_marauder_app_i.h" + +void wifi_marauder_scene_script_confirm_delete_widget_callback( + GuiButtonType result, + InputType type, + void* context) { + WifiMarauderApp* app = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(app->view_dispatcher, result); + } +} + +void wifi_marauder_scene_script_confirm_delete_on_enter(void* context) { + WifiMarauderApp* app = context; + + widget_add_button_element( + app->widget, + GuiButtonTypeLeft, + "No", + wifi_marauder_scene_script_confirm_delete_widget_callback, + app); + widget_add_button_element( + app->widget, + GuiButtonTypeRight, + "Yes", + wifi_marauder_scene_script_confirm_delete_widget_callback, + app); + + widget_add_string_element( + app->widget, 0, 0, AlignLeft, AlignTop, FontPrimary, "Are you sure?"); + widget_add_text_box_element( + app->widget, + 0, + 12, + 128, + 38, + AlignCenter, + AlignCenter, + "The script will be\npermanently deleted", + false); + + view_dispatcher_switch_to_view(app->view_dispatcher, WifiMarauderAppViewWidget); +} + +bool wifi_marauder_scene_script_confirm_delete_on_event(void* context, SceneManagerEvent event) { + WifiMarauderApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + // get which button press: "Yes" or "No" + if(event.event == GuiButtonTypeRight) { + // Yes + if(app->script != NULL) { + char script_path[256]; + snprintf( + script_path, + sizeof(script_path), + "%s/%s.json", + MARAUDER_APP_FOLDER_SCRIPTS, + app->script->name); + storage_simply_remove(app->storage, script_path); + wifi_marauder_script_free(app->script); + app->script = NULL; + + DialogMessage* message = dialog_message_alloc(); + dialog_message_set_text(message, "Deleted!", 88, 32, AlignCenter, AlignCenter); + dialog_message_set_icon(message, &I_DolphinCommon_56x48, 5, 6); + dialog_message_set_buttons(message, NULL, "Ok", NULL); + dialog_message_show(app->dialogs, message); + dialog_message_free(message); + } + } + scene_manager_previous_scene(app->scene_manager); + consumed = true; + } + + return consumed; +} + +void wifi_marauder_scene_script_confirm_delete_on_exit(void* context) { + WifiMarauderApp* app = context; + widget_reset(app->widget); +} diff --git a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_edit.c b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_edit.c new file mode 100644 index 000000000..697daba4f --- /dev/null +++ b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_edit.c @@ -0,0 +1,125 @@ +#include "../wifi_marauder_app_i.h" + +static void wifi_marauder_scene_script_edit_callback(void* context, uint32_t index) { + WifiMarauderApp* app = context; + WifiMarauderScriptStage* current_stage = app->script->first_stage; + uint32_t stage_index = 0; + + while(current_stage != NULL && stage_index < index) { + current_stage = current_stage->next_stage; + stage_index++; + } + app->script_edit_selected_stage = current_stage; + + if(app->script_edit_selected_stage != NULL) { + scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptEdit, index); + scene_manager_next_scene(app->scene_manager, WifiMarauderSceneScriptStageEdit); + } +} + +static void wifi_marauder_scene_script_edit_add_callback(void* context, uint32_t index) { + WifiMarauderApp* app = context; + scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptEdit, index); + scene_manager_next_scene(app->scene_manager, WifiMarauderSceneScriptStageAdd); +} + +void wifi_marauder_scene_script_edit_on_enter(void* context) { + WifiMarauderApp* app = context; + Submenu* submenu = app->submenu; + WifiMarauderScript* script = app->script; + submenu_set_header(submenu, script->name); + + WifiMarauderScriptStage* current_stage = script->first_stage; + int stage_index = 0; + while(current_stage != NULL) { + switch(current_stage->type) { + case WifiMarauderScriptStageTypeScan: + submenu_add_item( + submenu, "Scan", stage_index, wifi_marauder_scene_script_edit_callback, app); + break; + case WifiMarauderScriptStageTypeSelect: + submenu_add_item( + submenu, "Select", stage_index, wifi_marauder_scene_script_edit_callback, app); + break; + case WifiMarauderScriptStageTypeDeauth: + submenu_add_item( + submenu, "Deauth", stage_index, wifi_marauder_scene_script_edit_callback, app); + break; + case WifiMarauderScriptStageTypeProbe: + submenu_add_item( + submenu, "Probe", stage_index, wifi_marauder_scene_script_edit_callback, app); + break; + case WifiMarauderScriptStageTypeSniffRaw: + submenu_add_item( + submenu, "Sniff raw", stage_index, wifi_marauder_scene_script_edit_callback, app); + break; + case WifiMarauderScriptStageTypeSniffBeacon: + submenu_add_item( + submenu, + "Sniff beacon", + stage_index, + wifi_marauder_scene_script_edit_callback, + app); + break; + case WifiMarauderScriptStageTypeSniffDeauth: + submenu_add_item( + submenu, + "Sniff deauth", + stage_index, + wifi_marauder_scene_script_edit_callback, + app); + break; + case WifiMarauderScriptStageTypeSniffEsp: + submenu_add_item( + submenu, "Sniff esp", stage_index, wifi_marauder_scene_script_edit_callback, app); + break; + case WifiMarauderScriptStageTypeSniffPmkid: + submenu_add_item( + submenu, "Sniff PMKID", stage_index, wifi_marauder_scene_script_edit_callback, app); + break; + case WifiMarauderScriptStageTypeSniffPwn: + submenu_add_item( + submenu, "Sniff pwn", stage_index, wifi_marauder_scene_script_edit_callback, app); + break; + case WifiMarauderScriptStageTypeBeaconList: + submenu_add_item( + submenu, "Beacon list", stage_index, wifi_marauder_scene_script_edit_callback, app); + break; + case WifiMarauderScriptStageTypeBeaconAp: + submenu_add_item( + submenu, "Beacon AP", stage_index, wifi_marauder_scene_script_edit_callback, app); + break; + case WifiMarauderScriptStageTypeExec: + submenu_add_item( + submenu, + "Custom command", + stage_index, + wifi_marauder_scene_script_edit_callback, + app); + break; + case WifiMarauderScriptStageTypeDelay: + submenu_add_item( + submenu, "Delay", stage_index, wifi_marauder_scene_script_edit_callback, app); + break; + } + current_stage = current_stage->next_stage; + stage_index++; + } + + submenu_add_item( + submenu, "[+] ADD STAGE", stage_index++, wifi_marauder_scene_script_edit_add_callback, app); + submenu_set_selected_item( + submenu, scene_manager_get_scene_state(app->scene_manager, WifiMarauderSceneScriptEdit)); + view_dispatcher_switch_to_view(app->view_dispatcher, WifiMarauderAppViewSubmenu); +} + +bool wifi_marauder_scene_script_edit_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void wifi_marauder_scene_script_edit_on_exit(void* context) { + WifiMarauderApp* app = context; + submenu_reset(app->submenu); +} diff --git a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_edit_list.c b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_edit_list.c new file mode 100644 index 000000000..7a3284caf --- /dev/null +++ b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_edit_list.c @@ -0,0 +1,188 @@ +#include "../wifi_marauder_app_i.h" + +static void + wifi_marauder_scene_script_stage_edit_list_add_callback(void* context, uint32_t index) { + WifiMarauderApp* app = context; + + // Creates new item + WifiMarauderScriptStageListItem* new_item = + (WifiMarauderScriptStageListItem*)malloc(sizeof(WifiMarauderScriptStageListItem)); + new_item->value = malloc(64); + new_item->next_item = NULL; + + if(app->script_stage_edit_first_item == NULL) { + app->script_stage_edit_first_item = new_item; + } else { + WifiMarauderScriptStageListItem* last_item = app->script_stage_edit_first_item; + while(last_item->next_item != NULL) { + last_item = last_item->next_item; + } + last_item->next_item = new_item; + } + + scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptStageEditList, index); + app->user_input_type = WifiMarauderUserInputTypeString; + app->user_input_string_reference = &new_item->value; + scene_manager_next_scene(app->scene_manager, WifiMarauderSceneUserInput); +} + +static void wifi_marauder_scene_script_stage_edit_list_deallocate_items(WifiMarauderApp* app) { + WifiMarauderScriptStageListItem* current_item = app->script_stage_edit_first_item; + while(current_item != NULL) { + WifiMarauderScriptStageListItem* next_item = current_item->next_item; + free(current_item->value); + free(current_item); + current_item = next_item; + } + app->script_stage_edit_first_item = NULL; +} + +static void wifi_marauder_scene_script_stage_edit_list_save_strings(WifiMarauderApp* app) { + WifiMarauderScriptStageListItem* current_item = app->script_stage_edit_first_item; + int array_size = 0; + + // Calculates the required array size + while(current_item != NULL) { + array_size++; + current_item = current_item->next_item; + } + + // Reallocate the array of strings if necessary + if(*app->script_stage_edit_string_count_reference < array_size) { + *app->script_stage_edit_strings_reference = + realloc(*app->script_stage_edit_strings_reference, array_size * sizeof(char*)); + } + + // Fills the array of strings + current_item = app->script_stage_edit_first_item; + int i = 0; + while(current_item != NULL) { + char* current_str = malloc(strlen(current_item->value) + 1); + strncpy(current_str, current_item->value, strlen(current_item->value) + 1); + (*app->script_stage_edit_strings_reference)[i] = current_str; + current_item = current_item->next_item; + i++; + } + + *app->script_stage_edit_string_count_reference = array_size; +} + +static void wifi_marauder_scene_script_stage_edit_list_save_numbers(WifiMarauderApp* app) { + WifiMarauderScriptStageListItem* current_item = app->script_stage_edit_first_item; + int array_size = 0; + + // Calculates the required array size + while(current_item != NULL) { + array_size++; + current_item = current_item->next_item; + } + + // Reallocate the array of integers if necessary + if(*app->script_stage_edit_number_count_reference < array_size) { + *app->script_stage_edit_numbers_reference = + realloc(*app->script_stage_edit_numbers_reference, array_size * sizeof(int)); + } + + // Fills the array of integers + current_item = app->script_stage_edit_first_item; + int i = 0; + while(current_item != NULL) { + (*app->script_stage_edit_numbers_reference)[i] = atoi(current_item->value); + current_item = current_item->next_item; + i++; + } + + *app->script_stage_edit_number_count_reference = array_size; +} + +static void + wifi_marauder_scene_script_stage_edit_list_save_callback(void* context, uint32_t index) { + UNUSED(index); + WifiMarauderApp* app = context; + + if(app->script_stage_edit_strings_reference != NULL && + app->script_stage_edit_string_count_reference != NULL) { + wifi_marauder_scene_script_stage_edit_list_save_strings(app); + } + + if(app->script_stage_edit_numbers_reference != NULL && + app->script_stage_edit_number_count_reference != NULL) { + wifi_marauder_scene_script_stage_edit_list_save_numbers(app); + } + + wifi_marauder_scene_script_stage_edit_list_deallocate_items(app); + scene_manager_previous_scene(app->scene_manager); +} + +static void + wifi_marauder_scene_script_stage_edit_list_clear_callback(void* context, uint32_t index) { + UNUSED(index); + WifiMarauderApp* app = context; + + wifi_marauder_scene_script_stage_edit_list_deallocate_items(app); + + submenu_reset(app->submenu); + submenu_add_item( + app->submenu, + "[+] ADD ITEM", + 99, + wifi_marauder_scene_script_stage_edit_list_add_callback, + app); + submenu_add_item( + app->submenu, + "[*] SAVE ITEMS", + 99, + wifi_marauder_scene_script_stage_edit_list_save_callback, + app); + submenu_add_item( + app->submenu, + "[-] CLEAR LIST", + 99, + wifi_marauder_scene_script_stage_edit_list_clear_callback, + app); +} + +void wifi_marauder_scene_script_stage_edit_list_on_enter(void* context) { + WifiMarauderApp* app = context; + int item_index = 0; + WifiMarauderScriptStageListItem* current_item = app->script_stage_edit_first_item; + + while(current_item != NULL) { + submenu_add_item(app->submenu, current_item->value, item_index++, NULL, app); + current_item = current_item->next_item; + } + submenu_add_item( + app->submenu, + "[+] ADD ITEM", + 99, + wifi_marauder_scene_script_stage_edit_list_add_callback, + app); + submenu_add_item( + app->submenu, + "[*] SAVE ITEMS", + 99, + wifi_marauder_scene_script_stage_edit_list_save_callback, + app); + submenu_add_item( + app->submenu, + "[-] CLEAR LIST", + 99, + wifi_marauder_scene_script_stage_edit_list_clear_callback, + app); + + submenu_set_selected_item( + app->submenu, + scene_manager_get_scene_state(app->scene_manager, WifiMarauderSceneScriptStageEditList)); + view_dispatcher_switch_to_view(app->view_dispatcher, WifiMarauderAppViewSubmenu); +} + +bool wifi_marauder_scene_script_stage_edit_list_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void wifi_marauder_scene_script_stage_edit_list_on_exit(void* context) { + WifiMarauderApp* app = context; + submenu_reset(app->submenu); +} diff --git a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_options.c b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_options.c new file mode 100644 index 000000000..35b374f61 --- /dev/null +++ b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_options.c @@ -0,0 +1,111 @@ +#include "../wifi_marauder_app_i.h" + +enum SubmenuIndex { + SubmenuIndexRun, + SubmenuIndexSettings, + SubmenuIndexEditStages, + SubmenuIndexSave, + SubmenuIndexDelete +}; + +void wifi_marauder_scene_script_options_save_script(WifiMarauderApp* app) { + char script_path[256]; + snprintf( + script_path, + sizeof(script_path), + "%s/%s.json", + MARAUDER_APP_FOLDER_SCRIPTS, + app->script->name); + wifi_marauder_script_save_json(app->storage, script_path, app->script); + + DialogMessage* message = dialog_message_alloc(); + dialog_message_set_text(message, "Saved!", 88, 32, AlignCenter, AlignCenter); + dialog_message_set_icon(message, &I_DolphinCommon_56x48, 5, 6); + dialog_message_set_buttons(message, NULL, "Ok", NULL); + dialog_message_show(app->dialogs, message); + dialog_message_free(message); +} + +static void wifi_marauder_scene_script_options_callback(void* context, uint32_t index) { + WifiMarauderApp* app = context; + + switch(index) { + case SubmenuIndexRun: + scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptOptions, index); + scene_manager_next_scene(app->scene_manager, WifiMarauderSceneConsoleOutput); + break; + case SubmenuIndexSettings: + scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptOptions, index); + scene_manager_next_scene(app->scene_manager, WifiMarauderSceneScriptSettings); + break; + case SubmenuIndexEditStages: + scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptOptions, index); + scene_manager_next_scene(app->scene_manager, WifiMarauderSceneScriptEdit); + break; + case SubmenuIndexSave: + wifi_marauder_scene_script_options_save_script(app); + break; + case SubmenuIndexDelete: + scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptOptions, index); + scene_manager_next_scene(app->scene_manager, WifiMarauderSceneScriptConfirmDelete); + break; + } +} + +void wifi_marauder_scene_script_options_on_enter(void* context) { + WifiMarauderApp* app = context; + + // If returning after confirming script deletion + if(app->script == NULL) { + scene_manager_previous_scene(app->scene_manager); + return; + } + + Submenu* submenu = app->submenu; + + submenu_set_header(submenu, app->script->name); + submenu_add_item( + submenu, "[>] RUN", SubmenuIndexRun, wifi_marauder_scene_script_options_callback, app); + submenu_add_item( + submenu, + "[S] SETTINGS", + SubmenuIndexSettings, + wifi_marauder_scene_script_options_callback, + app); + submenu_add_item( + submenu, + "[+] EDIT STAGES", + SubmenuIndexEditStages, + wifi_marauder_scene_script_options_callback, + app); + submenu_add_item( + submenu, "[*] SAVE", SubmenuIndexSave, wifi_marauder_scene_script_options_callback, app); + submenu_add_item( + submenu, + "[X] DELETE", + SubmenuIndexDelete, + wifi_marauder_scene_script_options_callback, + app); + + submenu_set_selected_item( + submenu, + scene_manager_get_scene_state(app->scene_manager, WifiMarauderSceneScriptOptions)); + view_dispatcher_switch_to_view(app->view_dispatcher, WifiMarauderAppViewSubmenu); +} + +bool wifi_marauder_scene_script_options_on_event(void* context, SceneManagerEvent event) { + WifiMarauderApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeBack) { + wifi_marauder_script_free(app->script); + app->script = NULL; + } + + return consumed; +} + +void wifi_marauder_scene_script_options_on_exit(void* context) { + WifiMarauderApp* app = context; + submenu_reset(app->submenu); +} 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 new file mode 100644 index 000000000..bc0746858 --- /dev/null +++ b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_select.c @@ -0,0 +1,90 @@ +#include "../wifi_marauder_app_i.h" + +static void wifi_marauder_scene_script_select_callback(void* context, uint32_t index) { + WifiMarauderApp* app = context; + + char script_path[256]; + snprintf( + script_path, + sizeof(script_path), + "%s/%s.json", + MARAUDER_APP_FOLDER_SCRIPTS, + furi_string_get_cstr(app->script_list[index])); + + app->script = wifi_marauder_script_parse_json(app->storage, script_path); + if(app->script) { + scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptSelect, index); + scene_manager_next_scene(app->scene_manager, WifiMarauderSceneScriptOptions); + } +} + +static void wifi_marauder_scene_script_select_add_callback(void* context, uint32_t index) { + WifiMarauderApp* app = context; + scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptSelect, index); + + app->user_input_type = WifiMarauderUserInputTypeFileName; + app->user_input_file_dir = strdup(MARAUDER_APP_FOLDER_SCRIPTS); + app->user_input_file_extension = strdup("json"); + scene_manager_next_scene(app->scene_manager, WifiMarauderSceneUserInput); +} + +void wifi_marauder_scene_script_select_on_enter(void* context) { + WifiMarauderApp* app = context; + Submenu* submenu = app->submenu; + + 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]; + 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)) { + app->script_list_count++; + } + if(app->script_list_count > 0) { + submenu_set_header(submenu, "Select a script:"); + app->script_list = malloc(app->script_list_count * sizeof(FuriString*)); + storage_dir_close(dir_scripts); + 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)) { + app->script_list[script_index] = furi_string_alloc(); + path_extract_filename_no_ext(file_path, app->script_list[script_index]); + submenu_add_item( + submenu, + furi_string_get_cstr(app->script_list[script_index]), + script_index, + wifi_marauder_scene_script_select_callback, + app); + script_index++; + } + } else { + submenu_set_header(submenu, "No script found"); + } + submenu_add_item( + submenu, "[+] ADD SCRIPT", 99, wifi_marauder_scene_script_select_add_callback, app); + storage_dir_close(dir_scripts); + } + storage_file_free(dir_scripts); + + submenu_set_selected_item( + submenu, scene_manager_get_scene_state(app->scene_manager, WifiMarauderSceneScriptSelect)); + view_dispatcher_switch_to_view(app->view_dispatcher, WifiMarauderAppViewSubmenu); +} + +bool wifi_marauder_scene_script_select_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void wifi_marauder_scene_script_select_on_exit(void* context) { + WifiMarauderApp* app = context; + submenu_reset(app->submenu); + + for(int i = 0; i < app->script_list_count; i++) { + furi_string_free(app->script_list[i]); + } + free(app->script_list); +} diff --git a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_settings.c b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_settings.c new file mode 100644 index 000000000..b4903af05 --- /dev/null +++ b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_settings.c @@ -0,0 +1,87 @@ +#include "../wifi_marauder_app_i.h" + +enum ScriptSettingsOption { + ScriptSettingsOptionRepeat, + ScriptSettingsOptionSavePcap, + ScriptSettingsOptionEnableLed +}; + +const char* option_values[3] = {"No", "Yes", "Default"}; + +static void wifi_marauder_scene_script_settings_enter_callback(void* context, uint32_t index) { + WifiMarauderApp* app = context; + // Accept script repeat value + if(index == ScriptSettingsOptionRepeat) { + scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptSettings, index); + app->user_input_type = WifiMarauderUserInputTypeNumber; + app->user_input_number_reference = &app->script->repeat; + scene_manager_next_scene(app->scene_manager, WifiMarauderSceneUserInput); + } +} + +static void wifi_marauder_scene_script_settings_change_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + + uint8_t current_option = variable_item_list_get_selected_item_index(app->var_item_list); + uint8_t option_value_index = variable_item_get_current_value_index(item); + + switch(current_option) { + case ScriptSettingsOptionSavePcap: + variable_item_set_current_value_text(item, option_values[option_value_index]); + app->script->save_pcap = option_value_index; + break; + case ScriptSettingsOptionEnableLed: + variable_item_set_current_value_text(item, option_values[option_value_index]); + app->script->enable_led = option_value_index; + break; + } +} + +void wifi_marauder_scene_script_settings_on_enter(void* context) { + WifiMarauderApp* app = context; + VariableItemList* var_item_list = app->var_item_list; + variable_item_list_set_enter_callback( + app->var_item_list, wifi_marauder_scene_script_settings_enter_callback, app); + + // Script repeat option + VariableItem* repeat_item = variable_item_list_add(app->var_item_list, "Repeat", 1, NULL, app); + char repeat_str[32]; + snprintf(repeat_str, sizeof(repeat_str), "%d", app->script->repeat); + variable_item_set_current_value_text(repeat_item, repeat_str); + + // Save PCAP option + VariableItem* save_pcap_item = variable_item_list_add( + app->var_item_list, + "Save PCAP", + 3, + wifi_marauder_scene_script_settings_change_callback, + app); + variable_item_set_current_value_index(save_pcap_item, app->script->save_pcap); + variable_item_set_current_value_text(save_pcap_item, option_values[app->script->save_pcap]); + + // Enable board LED option + VariableItem* enable_led_item = variable_item_list_add( + app->var_item_list, + "Enable LED", + 3, + wifi_marauder_scene_script_settings_change_callback, + app); + variable_item_set_current_value_index(enable_led_item, app->script->enable_led); + variable_item_set_current_value_text(enable_led_item, option_values[app->script->enable_led]); + + variable_item_list_set_selected_item( + var_item_list, + scene_manager_get_scene_state(app->scene_manager, WifiMarauderSceneScriptSettings)); + view_dispatcher_switch_to_view(app->view_dispatcher, WifiMarauderAppViewVarItemList); +} + +bool wifi_marauder_scene_script_settings_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void wifi_marauder_scene_script_settings_on_exit(void* context) { + WifiMarauderApp* app = context; + variable_item_list_reset(app->var_item_list); +} diff --git a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_stage_add.c b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_stage_add.c new file mode 100644 index 000000000..33f1a2f03 --- /dev/null +++ b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_stage_add.c @@ -0,0 +1,297 @@ +#include "../wifi_marauder_app_i.h" + +// Scan +static void wifi_marauder_scene_script_stage_add_scan_callback(void* context, uint32_t index) { + UNUSED(index); + WifiMarauderApp* app = context; + + WifiMarauderScriptStageScan* stage = + (WifiMarauderScriptStageScan*)malloc(sizeof(WifiMarauderScriptStageScan)); + stage->type = WifiMarauderScriptScanTypeAp; + stage->timeout = WIFI_MARAUDER_DEFAULT_TIMEOUT_SCAN; + + wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeScan, stage); + scene_manager_previous_scene(app->scene_manager); +} + +// Select +static void wifi_marauder_scene_script_stage_add_select_callback(void* context, uint32_t index) { + UNUSED(index); + WifiMarauderApp* app = context; + + WifiMarauderScriptStageSelect* stage = + (WifiMarauderScriptStageSelect*)malloc(sizeof(WifiMarauderScriptStageSelect)); + stage->type = WifiMarauderScriptSelectTypeAp; + stage->filter = strdup("all"); + + wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeSelect, stage); + scene_manager_previous_scene(app->scene_manager); +} + +// Deauth +static void wifi_marauder_scene_script_stage_add_deauth_callback(void* context, uint32_t index) { + UNUSED(index); + WifiMarauderApp* app = context; + + WifiMarauderScriptStageDeauth* stage = + (WifiMarauderScriptStageDeauth*)malloc(sizeof(WifiMarauderScriptStageDeauth)); + stage->timeout = WIFI_MARAUDER_DEFAULT_TIMEOUT_DEAUTH; + + wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeDeauth, stage); + scene_manager_previous_scene(app->scene_manager); +} + +// Probe +static void wifi_marauder_scene_script_stage_add_probe_callback(void* context, uint32_t index) { + UNUSED(index); + WifiMarauderApp* app = context; + + WifiMarauderScriptStageProbe* stage = + (WifiMarauderScriptStageProbe*)malloc(sizeof(WifiMarauderScriptStageProbe)); + stage->timeout = WIFI_MARAUDER_DEFAULT_TIMEOUT_PROBE; + + wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeProbe, stage); + scene_manager_previous_scene(app->scene_manager); +} + +// Sniff RAW +static void wifi_marauder_scene_script_stage_add_sniffraw_callback(void* context, uint32_t index) { + UNUSED(index); + WifiMarauderApp* app = context; + + WifiMarauderScriptStageSniffRaw* stage = + (WifiMarauderScriptStageSniffRaw*)malloc(sizeof(WifiMarauderScriptStageSniffRaw)); + stage->timeout = WIFI_MARAUDER_DEFAULT_TIMEOUT_SNIFF; + + wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeSniffRaw, stage); + scene_manager_previous_scene(app->scene_manager); +} + +// Sniff Beacon +static void + wifi_marauder_scene_script_stage_add_sniffbeacon_callback(void* context, uint32_t index) { + UNUSED(index); + WifiMarauderApp* app = context; + + WifiMarauderScriptStageSniffBeacon* stage = + (WifiMarauderScriptStageSniffBeacon*)malloc(sizeof(WifiMarauderScriptStageSniffBeacon)); + stage->timeout = WIFI_MARAUDER_DEFAULT_TIMEOUT_SNIFF; + + wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeSniffBeacon, stage); + scene_manager_previous_scene(app->scene_manager); +} + +// Sniff Deauth +static void + wifi_marauder_scene_script_stage_add_sniffdeauth_callback(void* context, uint32_t index) { + UNUSED(index); + WifiMarauderApp* app = context; + + WifiMarauderScriptStageSniffDeauth* stage = + (WifiMarauderScriptStageSniffDeauth*)malloc(sizeof(WifiMarauderScriptStageSniffDeauth)); + stage->timeout = WIFI_MARAUDER_DEFAULT_TIMEOUT_SNIFF; + + wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeSniffDeauth, stage); + scene_manager_previous_scene(app->scene_manager); +} + +// Sniff Esp +static void wifi_marauder_scene_script_stage_add_sniffesp_callback(void* context, uint32_t index) { + UNUSED(index); + WifiMarauderApp* app = context; + + WifiMarauderScriptStageSniffEsp* stage = + (WifiMarauderScriptStageSniffEsp*)malloc(sizeof(WifiMarauderScriptStageSniffEsp)); + stage->timeout = WIFI_MARAUDER_DEFAULT_TIMEOUT_SNIFF; + + wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeSniffEsp, stage); + scene_manager_previous_scene(app->scene_manager); +} + +// Sniff PMKID +static void + wifi_marauder_scene_script_stage_add_sniffpmkid_callback(void* context, uint32_t index) { + UNUSED(index); + WifiMarauderApp* app = context; + + WifiMarauderScriptStageSniffPmkid* stage = + (WifiMarauderScriptStageSniffPmkid*)malloc(sizeof(WifiMarauderScriptStageSniffPmkid)); + stage->channel = 0; + stage->force_deauth = WifiMarauderScriptBooleanTrue; + stage->timeout = WIFI_MARAUDER_DEFAULT_TIMEOUT_SNIFF; + + wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeSniffPmkid, stage); + scene_manager_previous_scene(app->scene_manager); +} + +// Sniff Pwn +static void wifi_marauder_scene_script_stage_add_sniffpwn_callback(void* context, uint32_t index) { + UNUSED(index); + WifiMarauderApp* app = context; + + WifiMarauderScriptStageSniffPwn* stage = + (WifiMarauderScriptStageSniffPwn*)malloc(sizeof(WifiMarauderScriptStageSniffPwn)); + stage->timeout = WIFI_MARAUDER_DEFAULT_TIMEOUT_SNIFF; + + wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeSniffPwn, stage); + scene_manager_previous_scene(app->scene_manager); +} + +// Beacon list +static void + wifi_marauder_scene_script_stage_add_beaconlist_callback(void* context, uint32_t index) { + UNUSED(index); + WifiMarauderApp* app = context; + + WifiMarauderScriptStageBeaconList* stage = + (WifiMarauderScriptStageBeaconList*)malloc(sizeof(WifiMarauderScriptStageBeaconList)); + stage->ssids = NULL; + stage->ssid_count = 0; + stage->random_ssids = 0; + stage->timeout = WIFI_MARAUDER_DEFAULT_TIMEOUT_BEACON; + + wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeBeaconList, stage); + scene_manager_previous_scene(app->scene_manager); +} + +// Beacon AP +static void wifi_marauder_scene_script_stage_add_beaconap_callback(void* context, uint32_t index) { + UNUSED(index); + WifiMarauderApp* app = context; + + WifiMarauderScriptStageBeaconAp* stage = + (WifiMarauderScriptStageBeaconAp*)malloc(sizeof(WifiMarauderScriptStageBeaconAp)); + stage->timeout = WIFI_MARAUDER_DEFAULT_TIMEOUT_BEACON; + + wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeBeaconAp, stage); + scene_manager_previous_scene(app->scene_manager); +} + +// Exec +static void wifi_marauder_scene_script_stage_add_exec_callback(void* context, uint32_t index) { + UNUSED(index); + WifiMarauderApp* app = context; + + WifiMarauderScriptStageExec* stage = + (WifiMarauderScriptStageExec*)malloc(sizeof(WifiMarauderScriptStageExec)); + stage->command = NULL; + + wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeExec, stage); + scene_manager_previous_scene(app->scene_manager); +} + +// Delay +static void wifi_marauder_scene_script_stage_add_delay_callback(void* context, uint32_t index) { + UNUSED(index); + WifiMarauderApp* app = context; + + WifiMarauderScriptStageDelay* stage = + (WifiMarauderScriptStageDelay*)malloc(sizeof(WifiMarauderScriptStageDelay)); + stage->timeout = 0; + + wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeDelay, stage); + scene_manager_previous_scene(app->scene_manager); +} + +void wifi_marauder_scene_script_stage_add_on_enter(void* context) { + WifiMarauderApp* app = context; + Submenu* submenu = app->submenu; + submenu_set_header(submenu, "Add stage"); + + int menu_index = 0; + submenu_add_item( + submenu, "[+] Scan", menu_index++, wifi_marauder_scene_script_stage_add_scan_callback, app); + submenu_add_item( + submenu, + "[+] Select", + menu_index++, + wifi_marauder_scene_script_stage_add_select_callback, + app); + submenu_add_item( + submenu, + "[+] Deauth", + menu_index++, + wifi_marauder_scene_script_stage_add_deauth_callback, + app); + submenu_add_item( + submenu, + "[+] Probe", + menu_index++, + wifi_marauder_scene_script_stage_add_probe_callback, + app); + submenu_add_item( + submenu, + "[+] Sniff RAW", + menu_index++, + wifi_marauder_scene_script_stage_add_sniffraw_callback, + app); + submenu_add_item( + submenu, + "[+] Sniff Beacon", + menu_index++, + wifi_marauder_scene_script_stage_add_sniffbeacon_callback, + app); + submenu_add_item( + submenu, + "[+] Sniff Deauth", + menu_index++, + wifi_marauder_scene_script_stage_add_sniffdeauth_callback, + app); + submenu_add_item( + submenu, + "[+] Sniff Esp", + menu_index++, + wifi_marauder_scene_script_stage_add_sniffesp_callback, + app); + submenu_add_item( + submenu, + "[+] Sniff PMKID", + menu_index++, + wifi_marauder_scene_script_stage_add_sniffpmkid_callback, + app); + submenu_add_item( + submenu, + "[+] Sniff Pwnagotchi", + menu_index++, + wifi_marauder_scene_script_stage_add_sniffpwn_callback, + app); + submenu_add_item( + submenu, + "[+] Beacon List", + menu_index++, + wifi_marauder_scene_script_stage_add_beaconlist_callback, + app); + submenu_add_item( + submenu, + "[+] Beacon AP", + menu_index++, + wifi_marauder_scene_script_stage_add_beaconap_callback, + app); + submenu_add_item( + submenu, + "[+] Custom command", + menu_index++, + wifi_marauder_scene_script_stage_add_exec_callback, + app); + submenu_add_item( + submenu, + "[+] Delay", + menu_index++, + wifi_marauder_scene_script_stage_add_delay_callback, + app); + + submenu_set_selected_item( + submenu, scene_manager_get_scene_state(app->scene_manager, WifiMarauderSceneScriptEdit)); + view_dispatcher_switch_to_view(app->view_dispatcher, WifiMarauderAppViewSubmenu); +} + +bool wifi_marauder_scene_script_stage_add_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void wifi_marauder_scene_script_stage_add_on_exit(void* context) { + WifiMarauderApp* app = context; + submenu_reset(app->submenu); +} diff --git a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_stage_edit.c b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_stage_edit.c new file mode 100644 index 000000000..b8581e3e7 --- /dev/null +++ b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_stage_edit.c @@ -0,0 +1,203 @@ +#include "../wifi_marauder_app_i.h" + +void wifi_marauder_scene_script_stage_edit_create_list_strings( + WifiMarauderApp* app, + char** strings, + int string_count) { + // Deallocates the existing list + WifiMarauderScriptStageListItem* current_item = app->script_stage_edit_first_item; + while(current_item != NULL) { + WifiMarauderScriptStageListItem* next_item = current_item->next_item; + free(current_item->value); + free(current_item); + current_item = next_item; + } + + // Create a new list with numbers + WifiMarauderScriptStageListItem* first_item = NULL; + WifiMarauderScriptStageListItem* previous_item = NULL; + for(int i = 0; i < string_count; i++) { + WifiMarauderScriptStageListItem* item = malloc(sizeof(WifiMarauderScriptStageListItem)); + item->value = strdup(strings[i]); + item->next_item = NULL; + + if(previous_item == NULL) { + first_item = item; + } else { + previous_item->next_item = item; + } + previous_item = item; + } + + app->script_stage_edit_first_item = first_item; +} + +void wifi_marauder_scene_script_stage_edit_create_list_numbers( + WifiMarauderApp* app, + int* numbers, + int number_count) { + // Deallocates the existing list + WifiMarauderScriptStageListItem* current_item = app->script_stage_edit_first_item; + while(current_item != NULL) { + WifiMarauderScriptStageListItem* next_item = current_item->next_item; + free(current_item->value); + free(current_item); + current_item = next_item; + } + + // Create a new list with numbers + WifiMarauderScriptStageListItem* first_item = NULL; + WifiMarauderScriptStageListItem* previous_item = NULL; + for(int i = 0; i < number_count; i++) { + char number_str[32]; + snprintf(number_str, sizeof(number_str), "%d", numbers[i]); + + WifiMarauderScriptStageListItem* item = malloc(sizeof(WifiMarauderScriptStageListItem)); + item->value = strdup(number_str); + item->next_item = NULL; + + if(previous_item == NULL) { + first_item = item; + } else { + previous_item->next_item = item; + } + previous_item = item; + } + + app->script_stage_edit_first_item = first_item; +} + +static void + wifi_marauder_scene_script_stage_edit_list_enter_callback(void* context, uint32_t index) { + WifiMarauderApp* app = context; + const WifiMarauderScriptMenuItem* menu_item = &app->script_stage_menu->items[index]; + + // Fixed delete item + if(index == app->script_stage_menu->num_items) { + uint32_t deleted_stage_index = + scene_manager_get_scene_state(app->scene_manager, WifiMarauderSceneScriptEdit); + if(deleted_stage_index > 0) { + scene_manager_set_scene_state( + app->scene_manager, WifiMarauderSceneScriptEdit, deleted_stage_index - 1); + } + WifiMarauderScriptStage* previous_stage = NULL; + WifiMarauderScriptStage* current_stage = app->script->first_stage; + uint32_t current_stage_index = 0; + + while(current_stage != NULL && current_stage_index < deleted_stage_index) { + previous_stage = current_stage; + current_stage = current_stage->next_stage; + current_stage_index++; + } + + // Delete the stage + if(current_stage != NULL) { + if(previous_stage != NULL) { + if(current_stage->next_stage != NULL) { + previous_stage->next_stage = current_stage->next_stage; + } else { + previous_stage->next_stage = NULL; + app->script->last_stage = previous_stage; + } + } else { + if(current_stage->next_stage != NULL) { + app->script->first_stage = current_stage->next_stage; + } else { + app->script->first_stage = NULL; + app->script->last_stage = NULL; + } + } + } + app->script_edit_selected_stage = NULL; + + scene_manager_previous_scene(app->scene_manager); + return; + } + + if(menu_item->select_callback == NULL) { + return; + } + if(menu_item->type == WifiMarauderScriptMenuItemTypeNumber) { + // Accepts user number input, assigning the value to the reference passed as a parameter + menu_item->select_callback(app); + scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptStageEdit, index); + app->user_input_type = WifiMarauderUserInputTypeNumber; + scene_manager_next_scene(app->scene_manager, WifiMarauderSceneUserInput); + } else if(menu_item->type == WifiMarauderScriptMenuItemTypeString) { + // Accepts user string input, assigning the value to the reference passed as a parameter + menu_item->select_callback(app); + scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptStageEdit, index); + app->user_input_type = WifiMarauderUserInputTypeString; + scene_manager_next_scene(app->scene_manager, WifiMarauderSceneUserInput); + } else if(menu_item->type == WifiMarauderScriptMenuItemTypeListString) { + // Accepts the strings that compose the list + menu_item->select_callback(app); + wifi_marauder_scene_script_stage_edit_create_list_strings( + app, + *app->script_stage_edit_strings_reference, + *app->script_stage_edit_string_count_reference); + scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptStageEdit, index); + scene_manager_next_scene(app->scene_manager, WifiMarauderSceneScriptStageEditList); + } else if(menu_item->type == WifiMarauderScriptMenuItemTypeListNumber) { + // Accepts the numbers that compose the list + menu_item->select_callback(app); + wifi_marauder_scene_script_stage_edit_create_list_numbers( + app, + *app->script_stage_edit_numbers_reference, + *app->script_stage_edit_number_count_reference); + scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptStageEdit, index); + scene_manager_next_scene(app->scene_manager, WifiMarauderSceneScriptStageEditList); + } +} + +void wifi_marauder_scene_script_stage_edit_on_enter(void* context) { + WifiMarauderApp* app = context; + VariableItemList* var_item_list = app->var_item_list; + + variable_item_list_set_enter_callback( + app->var_item_list, wifi_marauder_scene_script_stage_edit_list_enter_callback, app); + app->script_stage_menu = + wifi_marauder_script_stage_menu_create(app->script_edit_selected_stage->type); + + if(app->script_stage_menu->items != NULL) { + for(uint32_t i = 0; i < app->script_stage_menu->num_items; i++) { + WifiMarauderScriptMenuItem* stage_item = &app->script_stage_menu->items[i]; + + // Changes the list item to handle it in callbacks + VariableItem* list_item = variable_item_list_add( + app->var_item_list, + stage_item->name, + stage_item->num_options, + stage_item->change_callback, + app); + + variable_item_list_set_selected_item(app->var_item_list, i); + if(stage_item->setup_callback != NULL) { + stage_item->setup_callback(list_item); + } + if(stage_item->change_callback != NULL) { + stage_item->change_callback(list_item); + } + } + } + + variable_item_list_add(app->var_item_list, "[-] DELETE STAGE", 0, NULL, app); + + variable_item_list_set_selected_item( + var_item_list, + scene_manager_get_scene_state(app->scene_manager, WifiMarauderSceneScriptStageEdit)); + view_dispatcher_switch_to_view(app->view_dispatcher, WifiMarauderAppViewVarItemList); +} + +bool wifi_marauder_scene_script_stage_edit_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void wifi_marauder_scene_script_stage_edit_on_exit(void* context) { + WifiMarauderApp* app = context; + wifi_marauder_script_stage_menu_free(app->script_stage_menu); + app->script_stage_menu = NULL; + variable_item_list_reset(app->var_item_list); +} diff --git a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_start.c b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_start.c index 2b2ee3a8a..c77543fd0 100644 --- a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_start.c +++ b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_start.c @@ -127,6 +127,7 @@ const WifiMarauderItem items[NUM_MENU_ITEMS] = { {"Update", {"ota", "sd"}, 2, {"update -w", "update -s"}, NO_ARGS, FOCUS_CONSOLE_END, NO_TIP}, {"Reboot", {""}, 1, {"reboot"}, NO_ARGS, FOCUS_CONSOLE_END, NO_TIP}, {"Help", {""}, 1, {"help"}, NO_ARGS, FOCUS_CONSOLE_START, SHOW_STOPSCAN_TIP}, + {"Scripts", {""}, 1, {""}, NO_ARGS, FOCUS_CONSOLE_END, NO_TIP}, {"Save to flipper sdcard", // keep as last entry or change logic in callback below {""}, 1, @@ -143,13 +144,6 @@ static void wifi_marauder_scene_start_var_list_enter_callback(void* context, uin furi_assert(index < NUM_MENU_ITEMS); const WifiMarauderItem* item = &items[index]; - if(index == NUM_MENU_ITEMS - 1) { - // "Save to flipper sdcard" special case - start SettingsInit widget - view_dispatcher_send_custom_event( - app->view_dispatcher, WifiMarauderEventStartSettingsInit); - return; - } - const int selected_option_index = app->selected_option_index[index]; furi_assert(selected_option_index < item->num_options_menu); app->selected_tx_string = item->actual_commands[selected_option_index]; @@ -167,6 +161,20 @@ static void wifi_marauder_scene_start_var_list_enter_callback(void* context, uin return; } + // Select automation script + if(index == NUM_MENU_ITEMS - 2) { + view_dispatcher_send_custom_event( + app->view_dispatcher, WifiMarauderEventStartScriptSelect); + return; + } + + if(index == NUM_MENU_ITEMS - 1) { + // "Save to flipper sdcard" special case - start SettingsInit widget + view_dispatcher_send_custom_event( + app->view_dispatcher, WifiMarauderEventStartSettingsInit); + return; + } + bool needs_keyboard = (item->needs_keyboard == TOGGLE_ARGS) ? (selected_option_index != 0) : item->needs_keyboard; if(needs_keyboard) { @@ -242,6 +250,10 @@ bool wifi_marauder_scene_start_on_event(void* context, SceneManagerEvent event) scene_manager_set_scene_state( app->scene_manager, WifiMarauderSceneStart, app->selected_menu_index); scene_manager_next_scene(app->scene_manager, WifiMarauderSceneLogViewer); + } else if(event.event == WifiMarauderEventStartScriptSelect) { + scene_manager_set_scene_state( + app->scene_manager, WifiMarauderSceneStart, app->selected_menu_index); + scene_manager_next_scene(app->scene_manager, WifiMarauderSceneScriptSelect); } consumed = true; } else if(event.type == SceneManagerEventTypeTick) { diff --git a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_text_input.c b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_text_input.c index b721e868d..e6091a410 100644 --- a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_text_input.c +++ b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_text_input.c @@ -44,24 +44,24 @@ void wifi_marauder_scene_text_input_on_enter(void* context) { } // Setup view - TextInput* text_input = app->text_input; + WIFI_TextInput* text_input = app->text_input; // Add help message to header if(app->special_case_input_step == 1) { - text_input_set_header_text(text_input, "Enter source MAC"); + wifi_text_input_set_header_text(text_input, "Enter source MAC"); } else if(0 == strncmp("ssid -a -g", app->selected_tx_string, strlen("ssid -a -g"))) { - text_input_set_header_text(text_input, "Enter # SSIDs to generate"); + wifi_text_input_set_header_text(text_input, "Enter # SSIDs to generate"); } else if(0 == strncmp("ssid -a -n", app->selected_tx_string, strlen("ssid -a -n"))) { - text_input_set_header_text(text_input, "Enter SSID name to add"); + wifi_text_input_set_header_text(text_input, "Enter SSID name to add"); } else if(0 == strncmp("ssid -r", app->selected_tx_string, strlen("ssid -r"))) { - text_input_set_header_text(text_input, "Remove target from SSID list"); + wifi_text_input_set_header_text(text_input, "Remove target from SSID list"); } else if(0 == strncmp("select -a", app->selected_tx_string, strlen("select -a"))) { - text_input_set_header_text(text_input, "Add target from AP list"); + wifi_text_input_set_header_text(text_input, "Add target from AP list"); } else if(0 == strncmp("select -s", app->selected_tx_string, strlen("select -s"))) { - text_input_set_header_text(text_input, "Add target from SSID list"); + wifi_text_input_set_header_text(text_input, "Add target from SSID list"); } else { - text_input_set_header_text(text_input, "Add command arguments"); + wifi_text_input_set_header_text(text_input, "Add command arguments"); } - text_input_set_result_callback( + wifi_text_input_set_result_callback( text_input, wifi_marauder_scene_text_input_callback, app, @@ -84,7 +84,7 @@ bool wifi_marauder_scene_text_input_on_event(void* context, SceneManagerEvent ev consumed = true; } else if(event.event == WifiMarauderEventSaveSourceMac) { if(12 != strlen(app->text_input_store)) { - text_input_set_header_text(app->text_input, "MAC must be 12 hex chars!"); + wifi_text_input_set_header_text(app->text_input, "MAC must be 12 hex chars!"); } else { snprintf( app->special_case_input_src_addr, @@ -106,12 +106,12 @@ bool wifi_marauder_scene_text_input_on_event(void* context, SceneManagerEvent ev // Advance scene to input destination MAC, clear text input app->special_case_input_step = 2; bzero(app->text_input_store, WIFI_MARAUDER_TEXT_INPUT_STORE_SIZE); - text_input_set_header_text(app->text_input, "Enter destination MAC"); + wifi_text_input_set_header_text(app->text_input, "Enter destination MAC"); } consumed = true; } else if(event.event == WifiMarauderEventSaveDestinationMac) { if(12 != strlen(app->text_input_store)) { - text_input_set_header_text(app->text_input, "MAC must be 12 hex chars!"); + wifi_text_input_set_header_text(app->text_input, "MAC must be 12 hex chars!"); } else { snprintf( app->special_case_input_dst_addr, @@ -150,5 +150,5 @@ bool wifi_marauder_scene_text_input_on_event(void* context, SceneManagerEvent ev void wifi_marauder_scene_text_input_on_exit(void* context) { WifiMarauderApp* app = context; - text_input_reset(app->text_input); + wifi_text_input_reset(app->text_input); } diff --git a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_user_input.c b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_user_input.c new file mode 100644 index 000000000..3d5697caf --- /dev/null +++ b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_user_input.c @@ -0,0 +1,155 @@ +#include "../wifi_marauder_app_i.h" + +bool wifi_marauder_scene_user_input_validator_number_callback( + const char* text, + FuriString* error, + void* context) { + UNUSED(context); + for(int i = 0; text[i] != '\0'; i++) { + if(text[i] < '0' || text[i] > '9') { + furi_string_printf(error, "This is not\na valid\nnumber!"); + return false; + } + } + return true; +} + +bool wifi_marauder_scene_user_input_validator_file_callback( + const char* text, + FuriString* error, + void* context) { + UNUSED(context); + if(strlen(text) == 0) { + furi_string_printf(error, "File name\ncannot be\nblank!"); + return false; + } + return true; +} + +void wifi_marauder_scene_user_input_ok_callback(void* context) { + WifiMarauderApp* app = context; + + File* file = NULL; + char* file_path = NULL; + + switch(app->user_input_type) { + // Writes the string value of the reference + case WifiMarauderUserInputTypeString: + if(app->user_input_string_reference != NULL) { + strncpy( + *app->user_input_string_reference, + app->text_input_store, + strlen(app->text_input_store) + 1); + app->user_input_string_reference = NULL; + } + break; + // Writes the numerical value of the reference + case WifiMarauderUserInputTypeNumber: + if(app->user_input_number_reference != NULL) { + *app->user_input_number_reference = atoi(app->text_input_store); + app->user_input_number_reference = NULL; + } + break; + // Creates a file with the name entered by the user, if it does not exist + case WifiMarauderUserInputTypeFileName: + file = storage_file_alloc(app->storage); + // Use application directory if not specified + if(app->user_input_file_dir == NULL) { + app->user_input_file_dir = strdup(MARAUDER_APP_FOLDER); + } + if(app->user_input_file_extension != NULL) { + size_t file_path_len = strlen(app->user_input_file_dir) + + strlen(app->text_input_store) + + strlen(app->user_input_file_extension) + 3; + file_path = (char*)malloc(file_path_len); + snprintf( + file_path, + file_path_len, + "%s/%s.%s", + app->user_input_file_dir, + app->text_input_store, + app->user_input_file_extension); + } else { + size_t file_path_len = + strlen(app->user_input_file_dir) + strlen(app->text_input_store) + 2; + file_path = (char*)malloc(file_path_len); + snprintf( + file_path, file_path_len, "%s/%s", app->user_input_file_dir, app->text_input_store); + } + if(storage_file_open(file, file_path, FSAM_WRITE, FSOM_CREATE_NEW)) { + storage_file_close(file); + } + // Free memory + free(app->user_input_file_dir); + app->user_input_file_dir = NULL; + free(app->user_input_file_extension); + app->user_input_file_extension = NULL; + free(file_path); + storage_file_free(file); + break; + default: + break; + } + + scene_manager_previous_scene(app->scene_manager); +} + +void wifi_marauder_scene_user_input_on_enter(void* context) { + WifiMarauderApp* app = context; + + switch(app->user_input_type) { + // Loads the string value of the reference + case WifiMarauderUserInputTypeString: + wifi_text_input_set_header_text(app->text_input, "Enter value:"); + wifi_text_input_set_validator(app->text_input, NULL, app); + if(app->user_input_string_reference != NULL) { + strncpy( + app->text_input_store, + *app->user_input_string_reference, + strlen(*app->user_input_string_reference) + 1); + } + break; + // Loads the numerical value of the reference + case WifiMarauderUserInputTypeNumber: + wifi_text_input_set_header_text(app->text_input, "Enter a valid number:"); + wifi_text_input_set_validator( + app->text_input, wifi_marauder_scene_user_input_validator_number_callback, app); + if(app->user_input_number_reference != NULL) { + char number_str[32]; + snprintf(number_str, sizeof(number_str), "%d", *app->user_input_number_reference); + strncpy(app->text_input_store, number_str, strlen(number_str) + 1); + } + break; + // File name + case WifiMarauderUserInputTypeFileName: + wifi_text_input_set_header_text(app->text_input, "Enter file name:"); + wifi_text_input_set_validator( + app->text_input, wifi_marauder_scene_user_input_validator_file_callback, app); + break; + default: + scene_manager_previous_scene(app->scene_manager); + return; + } + + wifi_text_input_set_result_callback( + app->text_input, + wifi_marauder_scene_user_input_ok_callback, + app, + app->text_input_store, + WIFI_MARAUDER_TEXT_INPUT_STORE_SIZE, + false); + + view_dispatcher_switch_to_view(app->view_dispatcher, WifiMarauderAppViewTextInput); +} + +bool wifi_marauder_scene_user_input_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void wifi_marauder_scene_user_input_on_exit(void* context) { + WifiMarauderApp* app = context; + memset(app->text_input_store, 0, sizeof(app->text_input_store)); + wifi_text_input_reset(app->text_input); +} diff --git a/applications/external/wifi_marauder_companion/script/cJSON.c b/applications/external/wifi_marauder_companion/script/cJSON.c new file mode 100644 index 000000000..06341fe38 --- /dev/null +++ b/applications/external/wifi_marauder_companion/script/cJSON.c @@ -0,0 +1,2743 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + 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. +*/ + +/* cJSON */ +/* JSON parser in C. */ + +/* disable warnings about old C89 functions in MSVC */ +#if !defined(_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER) +#define _CRT_SECURE_NO_DEPRECATE +#endif + +#ifdef __GNUC__ +#pragma GCC visibility push(default) +#endif +#if defined(_MSC_VER) +#pragma warning(push) +/* disable warning about single line comments in system headers */ +#pragma warning(disable : 4001) +#endif + +#include +#include +#include +#include +#include +#include +#include + +#ifdef ENABLE_LOCALES +#include +#endif + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif +#ifdef __GNUC__ +#pragma GCC visibility pop +#endif + +#include "cJSON.h" + +/* define our own boolean type */ +#ifdef true +#undef true +#endif +#define true ((cJSON_bool)1) + +#ifdef false +#undef false +#endif +#define false ((cJSON_bool)0) + +/* define isnan and isinf for ANSI C, if in C99 or above, isnan and isinf has been defined in math.h */ +#ifndef isinf +#define isinf(d) (isnan((d - d)) && !isnan(d)) +#endif +#ifndef isnan +#define isnan(d) (d != d) +#endif + +#ifndef NAN +#ifdef _WIN32 +#define NAN sqrt(-1.0) +#else +#define NAN 0.0 / 0.0 +#endif +#endif + +typedef struct { + const unsigned char* json; + size_t position; +} error; +static error global_error = {NULL, 0}; + +CJSON_PUBLIC(const char*) cJSON_GetErrorPtr(void) { + return (const char*)(global_error.json + global_error.position); +} + +CJSON_PUBLIC(char*) cJSON_GetStringValue(const cJSON* const item) { + if(!cJSON_IsString(item)) { + return NULL; + } + + return item->valuestring; +} + +CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON* const item) { + if(!cJSON_IsNumber(item)) { + return (double)NAN; + } + + return item->valuedouble; +} + +/* This is a safeguard to prevent copy-pasters from using incompatible C and header files */ +#if(CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 7) || (CJSON_VERSION_PATCH != 15) +#error cJSON.h and cJSON.c have different versions. Make sure that both have the same. +#endif + +CJSON_PUBLIC(const char*) cJSON_Version(void) { + static char version[15]; + sprintf(version, "%i.%i.%i", CJSON_VERSION_MAJOR, CJSON_VERSION_MINOR, CJSON_VERSION_PATCH); + + return version; +} + +/* Case insensitive string comparison, doesn't consider two NULL pointers equal though */ +static int case_insensitive_strcmp(const unsigned char* string1, const unsigned char* string2) { + if((string1 == NULL) || (string2 == NULL)) { + return 1; + } + + if(string1 == string2) { + return 0; + } + + for(; tolower(*string1) == tolower(*string2); (void)string1++, string2++) { + if(*string1 == '\0') { + return 0; + } + } + + return tolower(*string1) - tolower(*string2); +} + +typedef struct internal_hooks { + void*(CJSON_CDECL* allocate)(size_t size); + void(CJSON_CDECL* deallocate)(void* pointer); + void*(CJSON_CDECL* reallocate)(void* pointer, size_t size); +} internal_hooks; + +#if defined(_MSC_VER) +/* work around MSVC error C2322: '...' address of dllimport '...' is not static */ +static void* CJSON_CDECL internal_malloc(size_t size) { + return malloc(size); +} +static void CJSON_CDECL internal_free(void* pointer) { + free(pointer); +} +static void* CJSON_CDECL internal_realloc(void* pointer, size_t size) { + return realloc(pointer, size); +} +#else +#define internal_malloc malloc +#define internal_free free +#define internal_realloc realloc +#endif + +/* strlen of character literals resolved at compile time */ +#define static_strlen(string_literal) (sizeof(string_literal) - sizeof("")) + +static internal_hooks global_hooks = {internal_malloc, internal_free, internal_realloc}; + +static unsigned char* + cJSON_strdup(const unsigned char* string, const internal_hooks* const hooks) { + size_t length = 0; + unsigned char* copy = NULL; + + if(string == NULL) { + return NULL; + } + + length = strlen((const char*)string) + sizeof(""); + copy = (unsigned char*)hooks->allocate(length); + if(copy == NULL) { + return NULL; + } + memcpy(copy, string, length); + + return copy; +} + +CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks) { + if(hooks == NULL) { + /* Reset hooks */ + global_hooks.allocate = malloc; + global_hooks.deallocate = free; + global_hooks.reallocate = realloc; + return; + } + + global_hooks.allocate = malloc; + if(hooks->malloc_fn != NULL) { + global_hooks.allocate = hooks->malloc_fn; + } + + global_hooks.deallocate = free; + if(hooks->free_fn != NULL) { + global_hooks.deallocate = hooks->free_fn; + } + + /* use realloc only if both free and malloc are used */ + global_hooks.reallocate = NULL; + if((global_hooks.allocate == malloc) && (global_hooks.deallocate == free)) { + global_hooks.reallocate = realloc; + } +} + +/* Internal constructor. */ +static cJSON* cJSON_New_Item(const internal_hooks* const hooks) { + cJSON* node = (cJSON*)hooks->allocate(sizeof(cJSON)); + if(node) { + memset(node, '\0', sizeof(cJSON)); + } + + return node; +} + +/* Delete a cJSON structure. */ +CJSON_PUBLIC(void) cJSON_Delete(cJSON* item) { + cJSON* next = NULL; + while(item != NULL) { + next = item->next; + if(!(item->type & cJSON_IsReference) && (item->child != NULL)) { + cJSON_Delete(item->child); + } + if(!(item->type & cJSON_IsReference) && (item->valuestring != NULL)) { + global_hooks.deallocate(item->valuestring); + } + if(!(item->type & cJSON_StringIsConst) && (item->string != NULL)) { + global_hooks.deallocate(item->string); + } + global_hooks.deallocate(item); + item = next; + } +} + +/* get the decimal point character of the current locale */ +static unsigned char get_decimal_point(void) { +#ifdef ENABLE_LOCALES + struct lconv* lconv = localeconv(); + return (unsigned char)lconv->decimal_point[0]; +#else + return '.'; +#endif +} + +typedef struct { + const unsigned char* content; + size_t length; + size_t offset; + size_t depth; /* How deeply nested (in arrays/objects) is the input at the current offset. */ + internal_hooks hooks; +} parse_buffer; + +/* check if the given size is left to read in a given parse buffer (starting with 1) */ +#define can_read(buffer, size) \ + ((buffer != NULL) && (((buffer)->offset + size) <= (buffer)->length)) +/* check if the buffer can be accessed at the given index (starting with 0) */ +#define can_access_at_index(buffer, index) \ + ((buffer != NULL) && (((buffer)->offset + index) < (buffer)->length)) +#define cannot_access_at_index(buffer, index) (!can_access_at_index(buffer, index)) +/* get a pointer to the buffer at the position */ +#define buffer_at_offset(buffer) ((buffer)->content + (buffer)->offset) + +/* Converts an array of characters to double. Alternative implementation of strtod() */ +double string_to_double(const char* str, char** endptr) { + double result = 0.0; + int sign = 1; + const char* p = str; + + while(isspace((unsigned char)*p)) p++; + + if(*p == '-') { + sign = -1; + p++; + } else if(*p == '+') { + p++; + } + + while(isdigit((unsigned char)*p)) { + result = result * (double)(10) + ((double)(*p - '0')); + p++; + } + + if(*p == '.') { + double fraction = 0.1; + p++; + + while(isdigit((unsigned char)p[0])) { + fraction *= 0.1L; + result += (p++[0] - '0') * fraction; + } + } + + if(*p == 'e' || *p == 'E') { + int exponent = 0; + int exp_sign = 1; + p++; + + if(*p == '-') { + exp_sign = -1; + p++; + } else if(*p == '+') { + p++; + } + + while(isdigit((unsigned char)*p)) { + exponent = exponent * 10 + (*p - '0'); + p++; + } + + exponent *= exp_sign; + result *= pow(10, exponent); + } + + *endptr = (char*)p; + + return sign * result; +} + +/* Parse the input text to generate a number, and populate the result into item. */ +static cJSON_bool parse_number(cJSON* const item, parse_buffer* const input_buffer) { + double number = 0; + unsigned char* after_end = NULL; + unsigned char number_c_string[64]; + unsigned char decimal_point = get_decimal_point(); + size_t i = 0; + + if((input_buffer == NULL) || (input_buffer->content == NULL)) { + return false; + } + + /* copy the number into a temporary buffer and replace '.' with the decimal point + * of the current locale (for string_to_double) + * This also takes care of '\0' not necessarily being available for marking the end of the input */ + for(i = 0; (i < (sizeof(number_c_string) - 1)) && can_access_at_index(input_buffer, i); i++) { + switch(buffer_at_offset(input_buffer)[i]) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '+': + case '-': + case 'e': + case 'E': + number_c_string[i] = buffer_at_offset(input_buffer)[i]; + break; + + case '.': + number_c_string[i] = decimal_point; + break; + + default: + goto loop_end; + } + } +loop_end: + number_c_string[i] = '\0'; + + number = string_to_double((const char*)number_c_string, (char**)&after_end); + if(number_c_string == after_end) { + return false; /* parse_error */ + } + + item->valuedouble = number; + + /* use saturation in case of overflow */ + if(number >= INT_MAX) { + item->valueint = INT_MAX; + } else if(number <= (double)INT_MIN) { + item->valueint = INT_MIN; + } else { + item->valueint = (int)number; + } + + item->type = cJSON_Number; + + input_buffer->offset += (size_t)(after_end - number_c_string); + return true; +} + +/* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */ +CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON* object, double number) { + if(number >= INT_MAX) { + object->valueint = INT_MAX; + } else if(number <= (double)INT_MIN) { + object->valueint = INT_MIN; + } else { + object->valueint = (int)number; + } + + return object->valuedouble = number; +} + +CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON* object, const char* valuestring) { + char* copy = NULL; + /* if object's type is not cJSON_String or is cJSON_IsReference, it should not set valuestring */ + if(!(object->type & cJSON_String) || (object->type & cJSON_IsReference)) { + return NULL; + } + if(strlen(valuestring) <= strlen(object->valuestring)) { + strcpy(object->valuestring, valuestring); + return object->valuestring; + } + copy = (char*)cJSON_strdup((const unsigned char*)valuestring, &global_hooks); + if(copy == NULL) { + return NULL; + } + if(object->valuestring != NULL) { + cJSON_free(object->valuestring); + } + object->valuestring = copy; + + return copy; +} + +typedef struct { + unsigned char* buffer; + size_t length; + size_t offset; + size_t depth; /* current nesting depth (for formatted printing) */ + cJSON_bool noalloc; + cJSON_bool format; /* is this print a formatted print */ + internal_hooks hooks; +} printbuffer; + +/* realloc printbuffer if necessary to have at least "needed" bytes more */ +static unsigned char* ensure(printbuffer* const p, size_t needed) { + unsigned char* newbuffer = NULL; + size_t newsize = 0; + + if((p == NULL) || (p->buffer == NULL)) { + return NULL; + } + + if((p->length > 0) && (p->offset >= p->length)) { + /* make sure that offset is valid */ + return NULL; + } + + if(needed > INT_MAX) { + /* sizes bigger than INT_MAX are currently not supported */ + return NULL; + } + + needed += p->offset + 1; + if(needed <= p->length) { + return p->buffer + p->offset; + } + + if(p->noalloc) { + return NULL; + } + + /* calculate new buffer size */ + if(needed > (INT_MAX / 2)) { + /* overflow of int, use INT_MAX if possible */ + if(needed <= INT_MAX) { + newsize = INT_MAX; + } else { + return NULL; + } + } else { + newsize = needed * 2; + } + + if(p->hooks.reallocate != NULL) { + /* reallocate with realloc if available */ + newbuffer = (unsigned char*)p->hooks.reallocate(p->buffer, newsize); + if(newbuffer == NULL) { + p->hooks.deallocate(p->buffer); + p->length = 0; + p->buffer = NULL; + + return NULL; + } + } else { + /* otherwise reallocate manually */ + newbuffer = (unsigned char*)p->hooks.allocate(newsize); + if(!newbuffer) { + p->hooks.deallocate(p->buffer); + p->length = 0; + p->buffer = NULL; + + return NULL; + } + + memcpy(newbuffer, p->buffer, p->offset + 1); + p->hooks.deallocate(p->buffer); + } + p->length = newsize; + p->buffer = newbuffer; + + return newbuffer + p->offset; +} + +/* calculate the new length of the string in a printbuffer and update the offset */ +static void update_offset(printbuffer* const buffer) { + const unsigned char* buffer_pointer = NULL; + if((buffer == NULL) || (buffer->buffer == NULL)) { + return; + } + buffer_pointer = buffer->buffer + buffer->offset; + + buffer->offset += strlen((const char*)buffer_pointer); +} + +/* securely comparison of floating-point variables */ +static cJSON_bool compare_double(double a, double b) { + double maxVal = fabs(a) > fabs(b) ? fabs(a) : fabs(b); + return (fabs(a - b) <= maxVal * DBL_EPSILON); +} + +/* Render the number nicely from the given item into a string. */ +static cJSON_bool print_number(const cJSON* const item, printbuffer* const output_buffer) { + unsigned char* output_pointer = NULL; + double d = item->valuedouble; + int length = 0; + size_t i = 0; + unsigned char number_buffer[26] = {0}; /* temporary buffer to print the number into */ + unsigned char decimal_point = get_decimal_point(); + double test = 0.0; + + if(output_buffer == NULL) { + return false; + } + + /* This checks for NaN and Infinity */ + if(isnan(d) || isinf(d)) { + length = snprintf((char*)number_buffer, sizeof(number_buffer), "null"); + } else { + /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */ + length = snprintf((char*)number_buffer, sizeof(number_buffer), "%1.15g", d); + + /* Check whether the original double can be recovered */ + if((sscanf((char*)number_buffer, "%lg", &test) != 1) || !compare_double((double)test, d)) { + /* If not, print with 17 decimal places of precision */ + length = snprintf((char*)number_buffer, sizeof(number_buffer), "%1.17g", d); + } + } + + /* sprintf failed or buffer overrun occurred */ + if((length < 0) || (length > (int)(sizeof(number_buffer) - 1))) { + return false; + } + + /* reserve appropriate space in the output */ + output_pointer = ensure(output_buffer, (size_t)length + sizeof("")); + if(output_pointer == NULL) { + return false; + } + + /* copy the printed number to the output and replace locale + * dependent decimal point with '.' */ + for(i = 0; i < ((size_t)length); i++) { + if(number_buffer[i] == decimal_point) { + output_pointer[i] = '.'; + continue; + } + + output_pointer[i] = number_buffer[i]; + } + output_pointer[i] = '\0'; + + output_buffer->offset += (size_t)length; + + return true; +} + +/* parse 4 digit hexadecimal number */ +static unsigned parse_hex4(const unsigned char* const input) { + unsigned int h = 0; + size_t i = 0; + + for(i = 0; i < 4; i++) { + /* parse digit */ + if((input[i] >= '0') && (input[i] <= '9')) { + h += (unsigned int)input[i] - '0'; + } else if((input[i] >= 'A') && (input[i] <= 'F')) { + h += (unsigned int)10 + input[i] - 'A'; + } else if((input[i] >= 'a') && (input[i] <= 'f')) { + h += (unsigned int)10 + input[i] - 'a'; + } else /* invalid */ + { + return 0; + } + + if(i < 3) { + /* shift left to make place for the next nibble */ + h = h << 4; + } + } + + return h; +} + +/* converts a UTF-16 literal to UTF-8 + * A literal can be one or two sequences of the form \uXXXX */ +static unsigned char utf16_literal_to_utf8( + const unsigned char* const input_pointer, + const unsigned char* const input_end, + unsigned char** output_pointer) { + long unsigned int codepoint = 0; + unsigned int first_code = 0; + const unsigned char* first_sequence = input_pointer; + unsigned char utf8_length = 0; + unsigned char utf8_position = 0; + unsigned char sequence_length = 0; + unsigned char first_byte_mark = 0; + + if((input_end - first_sequence) < 6) { + /* input ends unexpectedly */ + goto fail; + } + + /* get the first utf16 sequence */ + first_code = parse_hex4(first_sequence + 2); + + /* check that the code is valid */ + if(((first_code >= 0xDC00) && (first_code <= 0xDFFF))) { + goto fail; + } + + /* UTF16 surrogate pair */ + if((first_code >= 0xD800) && (first_code <= 0xDBFF)) { + const unsigned char* second_sequence = first_sequence + 6; + unsigned int second_code = 0; + sequence_length = 12; /* \uXXXX\uXXXX */ + + if((input_end - second_sequence) < 6) { + /* input ends unexpectedly */ + goto fail; + } + + if((second_sequence[0] != '\\') || (second_sequence[1] != 'u')) { + /* missing second half of the surrogate pair */ + goto fail; + } + + /* get the second utf16 sequence */ + second_code = parse_hex4(second_sequence + 2); + /* check that the code is valid */ + if((second_code < 0xDC00) || (second_code > 0xDFFF)) { + /* invalid second half of the surrogate pair */ + goto fail; + } + + /* calculate the unicode codepoint from the surrogate pair */ + codepoint = 0x10000 + (((first_code & 0x3FF) << 10) | (second_code & 0x3FF)); + } else { + sequence_length = 6; /* \uXXXX */ + codepoint = first_code; + } + + /* encode as UTF-8 + * takes at maximum 4 bytes to encode: + * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ + if(codepoint < 0x80) { + /* normal ascii, encoding 0xxxxxxx */ + utf8_length = 1; + } else if(codepoint < 0x800) { + /* two bytes, encoding 110xxxxx 10xxxxxx */ + utf8_length = 2; + first_byte_mark = 0xC0; /* 11000000 */ + } else if(codepoint < 0x10000) { + /* three bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx */ + utf8_length = 3; + first_byte_mark = 0xE0; /* 11100000 */ + } else if(codepoint <= 0x10FFFF) { + /* four bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx */ + utf8_length = 4; + first_byte_mark = 0xF0; /* 11110000 */ + } else { + /* invalid unicode codepoint */ + goto fail; + } + + /* encode as utf8 */ + for(utf8_position = (unsigned char)(utf8_length - 1); utf8_position > 0; utf8_position--) { + /* 10xxxxxx */ + (*output_pointer)[utf8_position] = (unsigned char)((codepoint | 0x80) & 0xBF); + codepoint >>= 6; + } + /* encode first byte */ + if(utf8_length > 1) { + (*output_pointer)[0] = (unsigned char)((codepoint | first_byte_mark) & 0xFF); + } else { + (*output_pointer)[0] = (unsigned char)(codepoint & 0x7F); + } + + *output_pointer += utf8_length; + + return sequence_length; + +fail: + return 0; +} + +/* Parse the input text into an unescaped cinput, and populate item. */ +static cJSON_bool parse_string(cJSON* const item, parse_buffer* const input_buffer) { + const unsigned char* input_pointer = buffer_at_offset(input_buffer) + 1; + const unsigned char* input_end = buffer_at_offset(input_buffer) + 1; + unsigned char* output_pointer = NULL; + unsigned char* output = NULL; + + /* not a string */ + if(buffer_at_offset(input_buffer)[0] != '\"') { + goto fail; + } + + { + /* calculate approximate size of the output (overestimate) */ + size_t allocation_length = 0; + size_t skipped_bytes = 0; + while(((size_t)(input_end - input_buffer->content) < input_buffer->length) && + (*input_end != '\"')) { + /* is escape sequence */ + if(input_end[0] == '\\') { + if((size_t)(input_end + 1 - input_buffer->content) >= input_buffer->length) { + /* prevent buffer overflow when last input character is a backslash */ + goto fail; + } + skipped_bytes++; + input_end++; + } + input_end++; + } + if(((size_t)(input_end - input_buffer->content) >= input_buffer->length) || + (*input_end != '\"')) { + goto fail; /* string ended unexpectedly */ + } + + /* This is at most how much we need for the output */ + allocation_length = (size_t)(input_end - buffer_at_offset(input_buffer)) - skipped_bytes; + output = (unsigned char*)input_buffer->hooks.allocate(allocation_length + sizeof("")); + if(output == NULL) { + goto fail; /* allocation failure */ + } + } + + output_pointer = output; + /* loop through the string literal */ + while(input_pointer < input_end) { + if(*input_pointer != '\\') { + *output_pointer++ = *input_pointer++; + } + /* escape sequence */ + else { + unsigned char sequence_length = 2; + if((input_end - input_pointer) < 1) { + goto fail; + } + + switch(input_pointer[1]) { + case 'b': + *output_pointer++ = '\b'; + break; + case 'f': + *output_pointer++ = '\f'; + break; + case 'n': + *output_pointer++ = '\n'; + break; + case 'r': + *output_pointer++ = '\r'; + break; + case 't': + *output_pointer++ = '\t'; + break; + case '\"': + case '\\': + case '/': + *output_pointer++ = input_pointer[1]; + break; + + /* UTF-16 literal */ + case 'u': + sequence_length = utf16_literal_to_utf8(input_pointer, input_end, &output_pointer); + if(sequence_length == 0) { + /* failed to convert UTF16-literal to UTF-8 */ + goto fail; + } + break; + + default: + goto fail; + } + input_pointer += sequence_length; + } + } + + /* zero terminate the output */ + *output_pointer = '\0'; + + item->type = cJSON_String; + item->valuestring = (char*)output; + + input_buffer->offset = (size_t)(input_end - input_buffer->content); + input_buffer->offset++; + + return true; + +fail: + if(output != NULL) { + input_buffer->hooks.deallocate(output); + } + + if(input_pointer != NULL) { + input_buffer->offset = (size_t)(input_pointer - input_buffer->content); + } + + return false; +} + +/* Render the cstring provided to an escaped version that can be printed. */ +static cJSON_bool + print_string_ptr(const unsigned char* const input, printbuffer* const output_buffer) { + const unsigned char* input_pointer = NULL; + unsigned char* output = NULL; + unsigned char* output_pointer = NULL; + size_t output_length = 0; + /* numbers of additional characters needed for escaping */ + size_t escape_characters = 0; + + if(output_buffer == NULL) { + return false; + } + + /* empty string */ + if(input == NULL) { + output = ensure(output_buffer, sizeof("\"\"")); + if(output == NULL) { + return false; + } + strcpy((char*)output, "\"\""); + + return true; + } + + /* set "flag" to 1 if something needs to be escaped */ + for(input_pointer = input; *input_pointer; input_pointer++) { + switch(*input_pointer) { + case '\"': + case '\\': + case '\b': + case '\f': + case '\n': + case '\r': + case '\t': + /* one character escape sequence */ + escape_characters++; + break; + default: + if(*input_pointer < 32) { + /* UTF-16 escape sequence uXXXX */ + escape_characters += 5; + } + break; + } + } + output_length = (size_t)(input_pointer - input) + escape_characters; + + output = ensure(output_buffer, output_length + sizeof("\"\"")); + if(output == NULL) { + return false; + } + + /* no characters have to be escaped */ + if(escape_characters == 0) { + output[0] = '\"'; + memcpy(output + 1, input, output_length); + output[output_length + 1] = '\"'; + output[output_length + 2] = '\0'; + + return true; + } + + output[0] = '\"'; + output_pointer = output + 1; + /* copy the string */ + for(input_pointer = input; *input_pointer != '\0'; (void)input_pointer++, output_pointer++) { + if((*input_pointer > 31) && (*input_pointer != '\"') && (*input_pointer != '\\')) { + /* normal character, copy */ + *output_pointer = *input_pointer; + } else { + /* character needs to be escaped */ + *output_pointer++ = '\\'; + switch(*input_pointer) { + case '\\': + *output_pointer = '\\'; + break; + case '\"': + *output_pointer = '\"'; + break; + case '\b': + *output_pointer = 'b'; + break; + case '\f': + *output_pointer = 'f'; + break; + case '\n': + *output_pointer = 'n'; + break; + case '\r': + *output_pointer = 'r'; + break; + case '\t': + *output_pointer = 't'; + break; + default: + /* escape and print as unicode codepoint */ + snprintf((char*)output_pointer, 6, "u%04x", *input_pointer); + output_pointer += 4; + break; + } + } + } + output[output_length + 1] = '\"'; + output[output_length + 2] = '\0'; + + return true; +} + +/* Invoke print_string_ptr (which is useful) on an item. */ +static cJSON_bool print_string(const cJSON* const item, printbuffer* const p) { + return print_string_ptr((unsigned char*)item->valuestring, p); +} + +/* Predeclare these prototypes. */ +static cJSON_bool parse_value(cJSON* const item, parse_buffer* const input_buffer); +static cJSON_bool print_value(const cJSON* const item, printbuffer* const output_buffer); +static cJSON_bool parse_array(cJSON* const item, parse_buffer* const input_buffer); +static cJSON_bool print_array(const cJSON* const item, printbuffer* const output_buffer); +static cJSON_bool parse_object(cJSON* const item, parse_buffer* const input_buffer); +static cJSON_bool print_object(const cJSON* const item, printbuffer* const output_buffer); + +/* Utility to jump whitespace and cr/lf */ +static parse_buffer* buffer_skip_whitespace(parse_buffer* const buffer) { + if((buffer == NULL) || (buffer->content == NULL)) { + return NULL; + } + + if(cannot_access_at_index(buffer, 0)) { + return buffer; + } + + while(can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32)) { + buffer->offset++; + } + + if(buffer->offset == buffer->length) { + buffer->offset--; + } + + return buffer; +} + +/* skip the UTF-8 BOM (byte order mark) if it is at the beginning of a buffer */ +static parse_buffer* skip_utf8_bom(parse_buffer* const buffer) { + if((buffer == NULL) || (buffer->content == NULL) || (buffer->offset != 0)) { + return NULL; + } + + if(can_access_at_index(buffer, 4) && + (strncmp((const char*)buffer_at_offset(buffer), "\xEF\xBB\xBF", 3) == 0)) { + buffer->offset += 3; + } + + return buffer; +} + +CJSON_PUBLIC(cJSON*) +cJSON_ParseWithOpts( + const char* value, + const char** return_parse_end, + cJSON_bool require_null_terminated) { + size_t buffer_length; + + if(NULL == value) { + return NULL; + } + + /* Adding null character size due to require_null_terminated. */ + buffer_length = strlen(value) + sizeof(""); + + return cJSON_ParseWithLengthOpts( + value, buffer_length, return_parse_end, require_null_terminated); +} + +/* Parse an object - create a new root, and populate. */ +CJSON_PUBLIC(cJSON*) +cJSON_ParseWithLengthOpts( + const char* value, + size_t buffer_length, + const char** return_parse_end, + cJSON_bool require_null_terminated) { + parse_buffer buffer = {0, 0, 0, 0, {0, 0, 0}}; + cJSON* item = NULL; + + /* reset error position */ + global_error.json = NULL; + global_error.position = 0; + + if(value == NULL || 0 == buffer_length) { + goto fail; + } + + buffer.content = (const unsigned char*)value; + buffer.length = buffer_length; + buffer.offset = 0; + buffer.hooks = global_hooks; + + item = cJSON_New_Item(&global_hooks); + if(item == NULL) /* memory fail */ + { + goto fail; + } + + if(!parse_value(item, buffer_skip_whitespace(skip_utf8_bom(&buffer)))) { + /* parse failure. ep is set. */ + goto fail; + } + + /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */ + if(require_null_terminated) { + buffer_skip_whitespace(&buffer); + if((buffer.offset >= buffer.length) || buffer_at_offset(&buffer)[0] != '\0') { + goto fail; + } + } + if(return_parse_end) { + *return_parse_end = (const char*)buffer_at_offset(&buffer); + } + + return item; + +fail: + if(item != NULL) { + cJSON_Delete(item); + } + + if(value != NULL) { + error local_error; + local_error.json = (const unsigned char*)value; + local_error.position = 0; + + if(buffer.offset < buffer.length) { + local_error.position = buffer.offset; + } else if(buffer.length > 0) { + local_error.position = buffer.length - 1; + } + + if(return_parse_end != NULL) { + *return_parse_end = (const char*)local_error.json + local_error.position; + } + + global_error = local_error; + } + + return NULL; +} + +/* Default options for cJSON_Parse */ +CJSON_PUBLIC(cJSON*) cJSON_Parse(const char* value) { + return cJSON_ParseWithOpts(value, 0, 0); +} + +CJSON_PUBLIC(cJSON*) cJSON_ParseWithLength(const char* value, size_t buffer_length) { + return cJSON_ParseWithLengthOpts(value, buffer_length, 0, 0); +} + +#define cjson_min(a, b) (((a) < (b)) ? (a) : (b)) + +static unsigned char* + print(const cJSON* const item, cJSON_bool format, const internal_hooks* const hooks) { + static const size_t default_buffer_size = 256; + printbuffer buffer[1]; + unsigned char* printed = NULL; + + memset(buffer, 0, sizeof(buffer)); + + /* create buffer */ + buffer->buffer = (unsigned char*)hooks->allocate(default_buffer_size); + buffer->length = default_buffer_size; + buffer->format = format; + buffer->hooks = *hooks; + if(buffer->buffer == NULL) { + goto fail; + } + + /* print the value */ + if(!print_value(item, buffer)) { + goto fail; + } + update_offset(buffer); + + /* check if reallocate is available */ + if(hooks->reallocate != NULL) { + printed = (unsigned char*)hooks->reallocate(buffer->buffer, buffer->offset + 1); + if(printed == NULL) { + goto fail; + } + buffer->buffer = NULL; + } else /* otherwise copy the JSON over to a new buffer */ + { + printed = (unsigned char*)hooks->allocate(buffer->offset + 1); + if(printed == NULL) { + goto fail; + } + memcpy(printed, buffer->buffer, cjson_min(buffer->length, buffer->offset + 1)); + printed[buffer->offset] = '\0'; /* just to be sure */ + + /* free the buffer */ + hooks->deallocate(buffer->buffer); + } + + return printed; + +fail: + if(buffer->buffer != NULL) { + hooks->deallocate(buffer->buffer); + } + + if(printed != NULL) { + hooks->deallocate(printed); + } + + return NULL; +} + +/* Render a cJSON item/entity/structure to text. */ +CJSON_PUBLIC(char*) cJSON_Print(const cJSON* item) { + return (char*)print(item, true, &global_hooks); +} + +CJSON_PUBLIC(char*) cJSON_PrintUnformatted(const cJSON* item) { + return (char*)print(item, false, &global_hooks); +} + +CJSON_PUBLIC(char*) cJSON_PrintBuffered(const cJSON* item, int prebuffer, cJSON_bool fmt) { + printbuffer p = {0, 0, 0, 0, 0, 0, {0, 0, 0}}; + + if(prebuffer < 0) { + return NULL; + } + + p.buffer = (unsigned char*)global_hooks.allocate((size_t)prebuffer); + if(!p.buffer) { + return NULL; + } + + p.length = (size_t)prebuffer; + p.offset = 0; + p.noalloc = false; + p.format = fmt; + p.hooks = global_hooks; + + if(!print_value(item, &p)) { + global_hooks.deallocate(p.buffer); + return NULL; + } + + return (char*)p.buffer; +} + +CJSON_PUBLIC(cJSON_bool) +cJSON_PrintPreallocated(cJSON* item, char* buffer, const int length, const cJSON_bool format) { + printbuffer p = {0, 0, 0, 0, 0, 0, {0, 0, 0}}; + + if((length < 0) || (buffer == NULL)) { + return false; + } + + p.buffer = (unsigned char*)buffer; + p.length = (size_t)length; + p.offset = 0; + p.noalloc = true; + p.format = format; + p.hooks = global_hooks; + + return print_value(item, &p); +} + +/* Parser core - when encountering text, process appropriately. */ +static cJSON_bool parse_value(cJSON* const item, parse_buffer* const input_buffer) { + if((input_buffer == NULL) || (input_buffer->content == NULL)) { + return false; /* no input */ + } + + /* parse the different types of values */ + /* null */ + if(can_read(input_buffer, 4) && + (strncmp((const char*)buffer_at_offset(input_buffer), "null", 4) == 0)) { + item->type = cJSON_NULL; + input_buffer->offset += 4; + return true; + } + /* false */ + if(can_read(input_buffer, 5) && + (strncmp((const char*)buffer_at_offset(input_buffer), "false", 5) == 0)) { + item->type = cJSON_False; + input_buffer->offset += 5; + return true; + } + /* true */ + if(can_read(input_buffer, 4) && + (strncmp((const char*)buffer_at_offset(input_buffer), "true", 4) == 0)) { + item->type = cJSON_True; + item->valueint = 1; + input_buffer->offset += 4; + return true; + } + /* string */ + if(can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '\"')) { + return parse_string(item, input_buffer); + } + /* number */ + if(can_access_at_index(input_buffer, 0) && ((buffer_at_offset(input_buffer)[0] == '-') || + ((buffer_at_offset(input_buffer)[0] >= '0') && + (buffer_at_offset(input_buffer)[0] <= '9')))) { + return parse_number(item, input_buffer); + } + /* array */ + if(can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '[')) { + return parse_array(item, input_buffer); + } + /* object */ + if(can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '{')) { + return parse_object(item, input_buffer); + } + + return false; +} + +/* Render a value to text. */ +static cJSON_bool print_value(const cJSON* const item, printbuffer* const output_buffer) { + unsigned char* output = NULL; + + if((item == NULL) || (output_buffer == NULL)) { + return false; + } + + switch((item->type) & 0xFF) { + case cJSON_NULL: + output = ensure(output_buffer, 5); + if(output == NULL) { + return false; + } + strcpy((char*)output, "null"); + return true; + + case cJSON_False: + output = ensure(output_buffer, 6); + if(output == NULL) { + return false; + } + strcpy((char*)output, "false"); + return true; + + case cJSON_True: + output = ensure(output_buffer, 5); + if(output == NULL) { + return false; + } + strcpy((char*)output, "true"); + return true; + + case cJSON_Number: + return print_number(item, output_buffer); + + case cJSON_Raw: { + size_t raw_length = 0; + if(item->valuestring == NULL) { + return false; + } + + raw_length = strlen(item->valuestring) + sizeof(""); + output = ensure(output_buffer, raw_length); + if(output == NULL) { + return false; + } + memcpy(output, item->valuestring, raw_length); + return true; + } + + case cJSON_String: + return print_string(item, output_buffer); + + case cJSON_Array: + return print_array(item, output_buffer); + + case cJSON_Object: + return print_object(item, output_buffer); + + default: + return false; + } +} + +/* Build an array from input text. */ +static cJSON_bool parse_array(cJSON* const item, parse_buffer* const input_buffer) { + cJSON* head = NULL; /* head of the linked list */ + cJSON* current_item = NULL; + + if(input_buffer->depth >= CJSON_NESTING_LIMIT) { + return false; /* to deeply nested */ + } + input_buffer->depth++; + + if(buffer_at_offset(input_buffer)[0] != '[') { + /* not an array */ + goto fail; + } + + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if(can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ']')) { + /* empty array */ + goto success; + } + + /* check if we skipped to the end of the buffer */ + if(cannot_access_at_index(input_buffer, 0)) { + input_buffer->offset--; + goto fail; + } + + /* step back to character in front of the first element */ + input_buffer->offset--; + /* loop through the comma separated array elements */ + do { + /* allocate next item */ + cJSON* new_item = cJSON_New_Item(&(input_buffer->hooks)); + if(new_item == NULL) { + goto fail; /* allocation failure */ + } + + /* attach next item to list */ + if(head == NULL) { + /* start the linked list */ + current_item = head = new_item; + } else { + /* add to the end and advance */ + current_item->next = new_item; + new_item->prev = current_item; + current_item = new_item; + } + + /* parse next value */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if(!parse_value(current_item, input_buffer)) { + goto fail; /* failed to parse value */ + } + buffer_skip_whitespace(input_buffer); + } while(can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); + + if(cannot_access_at_index(input_buffer, 0) || buffer_at_offset(input_buffer)[0] != ']') { + goto fail; /* expected end of array */ + } + +success: + input_buffer->depth--; + + if(head != NULL) { + head->prev = current_item; + } + + item->type = cJSON_Array; + item->child = head; + + input_buffer->offset++; + + return true; + +fail: + if(head != NULL) { + cJSON_Delete(head); + } + + return false; +} + +/* Render an array to text */ +static cJSON_bool print_array(const cJSON* const item, printbuffer* const output_buffer) { + unsigned char* output_pointer = NULL; + size_t length = 0; + cJSON* current_element = item->child; + + if(output_buffer == NULL) { + return false; + } + + /* Compose the output array. */ + /* opening square bracket */ + output_pointer = ensure(output_buffer, 1); + if(output_pointer == NULL) { + return false; + } + + *output_pointer = '['; + output_buffer->offset++; + output_buffer->depth++; + + while(current_element != NULL) { + if(!print_value(current_element, output_buffer)) { + return false; + } + update_offset(output_buffer); + if(current_element->next) { + length = (size_t)(output_buffer->format ? 2 : 1); + output_pointer = ensure(output_buffer, length + 1); + if(output_pointer == NULL) { + return false; + } + *output_pointer++ = ','; + if(output_buffer->format) { + *output_pointer++ = ' '; + } + *output_pointer = '\0'; + output_buffer->offset += length; + } + current_element = current_element->next; + } + + output_pointer = ensure(output_buffer, 2); + if(output_pointer == NULL) { + return false; + } + *output_pointer++ = ']'; + *output_pointer = '\0'; + output_buffer->depth--; + + return true; +} + +/* Build an object from the text. */ +static cJSON_bool parse_object(cJSON* const item, parse_buffer* const input_buffer) { + cJSON* head = NULL; /* linked list head */ + cJSON* current_item = NULL; + + if(input_buffer->depth >= CJSON_NESTING_LIMIT) { + return false; /* to deeply nested */ + } + input_buffer->depth++; + + if(cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '{')) { + goto fail; /* not an object */ + } + + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if(can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '}')) { + goto success; /* empty object */ + } + + /* check if we skipped to the end of the buffer */ + if(cannot_access_at_index(input_buffer, 0)) { + input_buffer->offset--; + goto fail; + } + + /* step back to character in front of the first element */ + input_buffer->offset--; + /* loop through the comma separated array elements */ + do { + /* allocate next item */ + cJSON* new_item = cJSON_New_Item(&(input_buffer->hooks)); + if(new_item == NULL) { + goto fail; /* allocation failure */ + } + + /* attach next item to list */ + if(head == NULL) { + /* start the linked list */ + current_item = head = new_item; + } else { + /* add to the end and advance */ + current_item->next = new_item; + new_item->prev = current_item; + current_item = new_item; + } + + /* parse the name of the child */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if(!parse_string(current_item, input_buffer)) { + goto fail; /* failed to parse name */ + } + buffer_skip_whitespace(input_buffer); + + /* swap valuestring and string, because we parsed the name */ + current_item->string = current_item->valuestring; + current_item->valuestring = NULL; + + if(cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != ':')) { + goto fail; /* invalid object */ + } + + /* parse the value */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if(!parse_value(current_item, input_buffer)) { + goto fail; /* failed to parse value */ + } + buffer_skip_whitespace(input_buffer); + } while(can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); + + if(cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '}')) { + goto fail; /* expected end of object */ + } + +success: + input_buffer->depth--; + + if(head != NULL) { + head->prev = current_item; + } + + item->type = cJSON_Object; + item->child = head; + + input_buffer->offset++; + return true; + +fail: + if(head != NULL) { + cJSON_Delete(head); + } + + return false; +} + +/* Render an object to text. */ +static cJSON_bool print_object(const cJSON* const item, printbuffer* const output_buffer) { + unsigned char* output_pointer = NULL; + size_t length = 0; + cJSON* current_item = item->child; + + if(output_buffer == NULL) { + return false; + } + + /* Compose the output: */ + length = (size_t)(output_buffer->format ? 2 : 1); /* fmt: {\n */ + output_pointer = ensure(output_buffer, length + 1); + if(output_pointer == NULL) { + return false; + } + + *output_pointer++ = '{'; + output_buffer->depth++; + if(output_buffer->format) { + *output_pointer++ = '\n'; + } + output_buffer->offset += length; + + while(current_item) { + if(output_buffer->format) { + size_t i; + output_pointer = ensure(output_buffer, output_buffer->depth); + if(output_pointer == NULL) { + return false; + } + for(i = 0; i < output_buffer->depth; i++) { + *output_pointer++ = '\t'; + } + output_buffer->offset += output_buffer->depth; + } + + /* print key */ + if(!print_string_ptr((unsigned char*)current_item->string, output_buffer)) { + return false; + } + update_offset(output_buffer); + + length = (size_t)(output_buffer->format ? 2 : 1); + output_pointer = ensure(output_buffer, length); + if(output_pointer == NULL) { + return false; + } + *output_pointer++ = ':'; + if(output_buffer->format) { + *output_pointer++ = '\t'; + } + output_buffer->offset += length; + + /* print value */ + if(!print_value(current_item, output_buffer)) { + return false; + } + update_offset(output_buffer); + + /* print comma if not last */ + length = ((size_t)(output_buffer->format ? 1 : 0) + (size_t)(current_item->next ? 1 : 0)); + output_pointer = ensure(output_buffer, length + 1); + if(output_pointer == NULL) { + return false; + } + if(current_item->next) { + *output_pointer++ = ','; + } + + if(output_buffer->format) { + *output_pointer++ = '\n'; + } + *output_pointer = '\0'; + output_buffer->offset += length; + + current_item = current_item->next; + } + + output_pointer = ensure(output_buffer, output_buffer->format ? (output_buffer->depth + 1) : 2); + if(output_pointer == NULL) { + return false; + } + if(output_buffer->format) { + size_t i; + for(i = 0; i < (output_buffer->depth - 1); i++) { + *output_pointer++ = '\t'; + } + } + *output_pointer++ = '}'; + *output_pointer = '\0'; + output_buffer->depth--; + + return true; +} + +/* Get Array size/item / object item. */ +CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON* array) { + cJSON* child = NULL; + size_t size = 0; + + if(array == NULL) { + return 0; + } + + child = array->child; + + while(child != NULL) { + size++; + child = child->next; + } + + /* FIXME: Can overflow here. Cannot be fixed without breaking the API */ + + return (int)size; +} + +static cJSON* get_array_item(const cJSON* array, size_t index) { + cJSON* current_child = NULL; + + if(array == NULL) { + return NULL; + } + + current_child = array->child; + while((current_child != NULL) && (index > 0)) { + index--; + current_child = current_child->next; + } + + return current_child; +} + +CJSON_PUBLIC(cJSON*) cJSON_GetArrayItem(const cJSON* array, int index) { + if(index < 0) { + return NULL; + } + + return get_array_item(array, (size_t)index); +} + +static cJSON* get_object_item( + const cJSON* const object, + const char* const name, + const cJSON_bool case_sensitive) { + cJSON* current_element = NULL; + + if((object == NULL) || (name == NULL)) { + return NULL; + } + + current_element = object->child; + if(case_sensitive) { + while((current_element != NULL) && (current_element->string != NULL) && + (strcmp(name, current_element->string) != 0)) { + current_element = current_element->next; + } + } else { + while((current_element != NULL) && + (case_insensitive_strcmp( + (const unsigned char*)name, (const unsigned char*)(current_element->string)) != + 0)) { + current_element = current_element->next; + } + } + + if((current_element == NULL) || (current_element->string == NULL)) { + return NULL; + } + + return current_element; +} + +CJSON_PUBLIC(cJSON*) cJSON_GetObjectItem(const cJSON* const object, const char* const string) { + return get_object_item(object, string, false); +} + +CJSON_PUBLIC(cJSON*) +cJSON_GetObjectItemCaseSensitive(const cJSON* const object, const char* const string) { + return get_object_item(object, string, true); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON* object, const char* string) { + return cJSON_GetObjectItem(object, string) ? 1 : 0; +} + +/* Utility for array list handling. */ +static void suffix_object(cJSON* prev, cJSON* item) { + prev->next = item; + item->prev = prev; +} + +/* Utility for handling references. */ +static cJSON* create_reference(const cJSON* item, const internal_hooks* const hooks) { + cJSON* reference = NULL; + if(item == NULL) { + return NULL; + } + + reference = cJSON_New_Item(hooks); + if(reference == NULL) { + return NULL; + } + + memcpy(reference, item, sizeof(cJSON)); + reference->string = NULL; + reference->type |= cJSON_IsReference; + reference->next = reference->prev = NULL; + return reference; +} + +static cJSON_bool add_item_to_array(cJSON* array, cJSON* item) { + cJSON* child = NULL; + + if((item == NULL) || (array == NULL) || (array == item)) { + return false; + } + + child = array->child; + /* + * To find the last item in array quickly, we use prev in array + */ + if(child == NULL) { + /* list is empty, start new one */ + array->child = item; + item->prev = item; + item->next = NULL; + } else { + /* append to the end */ + if(child->prev) { + suffix_object(child->prev, item); + array->child->prev = item; + } + } + + return true; +} + +/* Add item to array/object. */ +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON* array, cJSON* item) { + return add_item_to_array(array, item); +} + +#if defined(__clang__) || \ + (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) +#pragma GCC diagnostic push +#endif +#ifdef __GNUC__ +#pragma GCC diagnostic ignored "-Wcast-qual" +#endif +/* helper function to cast away const */ +static void* cast_away_const(const void* string) { + return (void*)string; +} +#if defined(__clang__) || \ + (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) +#pragma GCC diagnostic pop +#endif + +static cJSON_bool add_item_to_object( + cJSON* const object, + const char* const string, + cJSON* const item, + const internal_hooks* const hooks, + const cJSON_bool constant_key) { + char* new_key = NULL; + int new_type = cJSON_Invalid; + + if((object == NULL) || (string == NULL) || (item == NULL) || (object == item)) { + return false; + } + + if(constant_key) { + new_key = (char*)cast_away_const(string); + new_type = item->type | cJSON_StringIsConst; + } else { + new_key = (char*)cJSON_strdup((const unsigned char*)string, hooks); + if(new_key == NULL) { + return false; + } + + new_type = item->type & ~cJSON_StringIsConst; + } + + if(!(item->type & cJSON_StringIsConst) && (item->string != NULL)) { + hooks->deallocate(item->string); + } + + item->string = new_key; + item->type = new_type; + + return add_item_to_array(object, item); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON* object, const char* string, cJSON* item) { + return add_item_to_object(object, string, item, &global_hooks, false); +} + +/* Add an item to an object with constant string as key */ +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON* object, const char* string, cJSON* item) { + return add_item_to_object(object, string, item, &global_hooks, true); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON* array, cJSON* item) { + if(array == NULL) { + return false; + } + + return add_item_to_array(array, create_reference(item, &global_hooks)); +} + +CJSON_PUBLIC(cJSON_bool) +cJSON_AddItemReferenceToObject(cJSON* object, const char* string, cJSON* item) { + if((object == NULL) || (string == NULL)) { + return false; + } + + return add_item_to_object( + object, string, create_reference(item, &global_hooks), &global_hooks, false); +} + +CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON* const object, const char* const name) { + cJSON* null = cJSON_CreateNull(); + if(add_item_to_object(object, name, null, &global_hooks, false)) { + return null; + } + + cJSON_Delete(null); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON* const object, const char* const name) { + cJSON* true_item = cJSON_CreateTrue(); + if(add_item_to_object(object, name, true_item, &global_hooks, false)) { + return true_item; + } + + cJSON_Delete(true_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON* const object, const char* const name) { + cJSON* false_item = cJSON_CreateFalse(); + if(add_item_to_object(object, name, false_item, &global_hooks, false)) { + return false_item; + } + + cJSON_Delete(false_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) +cJSON_AddBoolToObject(cJSON* const object, const char* const name, const cJSON_bool boolean) { + cJSON* bool_item = cJSON_CreateBool(boolean); + if(add_item_to_object(object, name, bool_item, &global_hooks, false)) { + return bool_item; + } + + cJSON_Delete(bool_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) +cJSON_AddNumberToObject(cJSON* const object, const char* const name, const double number) { + cJSON* number_item = cJSON_CreateNumber(number); + if(add_item_to_object(object, name, number_item, &global_hooks, false)) { + return number_item; + } + + cJSON_Delete(number_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) +cJSON_AddStringToObject(cJSON* const object, const char* const name, const char* const string) { + cJSON* string_item = cJSON_CreateString(string); + if(add_item_to_object(object, name, string_item, &global_hooks, false)) { + return string_item; + } + + cJSON_Delete(string_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) +cJSON_AddRawToObject(cJSON* const object, const char* const name, const char* const raw) { + cJSON* raw_item = cJSON_CreateRaw(raw); + if(add_item_to_object(object, name, raw_item, &global_hooks, false)) { + return raw_item; + } + + cJSON_Delete(raw_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON* const object, const char* const name) { + cJSON* object_item = cJSON_CreateObject(); + if(add_item_to_object(object, name, object_item, &global_hooks, false)) { + return object_item; + } + + cJSON_Delete(object_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON* const object, const char* const name) { + cJSON* array = cJSON_CreateArray(); + if(add_item_to_object(object, name, array, &global_hooks, false)) { + return array; + } + + cJSON_Delete(array); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_DetachItemViaPointer(cJSON* parent, cJSON* const item) { + if((parent == NULL) || (item == NULL)) { + return NULL; + } + + if(item != parent->child) { + /* not the first element */ + item->prev->next = item->next; + } + if(item->next != NULL) { + /* not the last element */ + item->next->prev = item->prev; + } + + if(item == parent->child) { + /* first element */ + parent->child = item->next; + } else if(item->next == NULL) { + /* last element */ + parent->child->prev = item->prev; + } + + /* make sure the detached item doesn't point anywhere anymore */ + item->prev = NULL; + item->next = NULL; + + return item; +} + +CJSON_PUBLIC(cJSON*) cJSON_DetachItemFromArray(cJSON* array, int which) { + if(which < 0) { + return NULL; + } + + return cJSON_DetachItemViaPointer(array, get_array_item(array, (size_t)which)); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON* array, int which) { + cJSON_Delete(cJSON_DetachItemFromArray(array, which)); +} + +CJSON_PUBLIC(cJSON*) cJSON_DetachItemFromObject(cJSON* object, const char* string) { + cJSON* to_detach = cJSON_GetObjectItem(object, string); + + return cJSON_DetachItemViaPointer(object, to_detach); +} + +CJSON_PUBLIC(cJSON*) cJSON_DetachItemFromObjectCaseSensitive(cJSON* object, const char* string) { + cJSON* to_detach = cJSON_GetObjectItemCaseSensitive(object, string); + + return cJSON_DetachItemViaPointer(object, to_detach); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON* object, const char* string) { + cJSON_Delete(cJSON_DetachItemFromObject(object, string)); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON* object, const char* string) { + cJSON_Delete(cJSON_DetachItemFromObjectCaseSensitive(object, string)); +} + +/* Replace array/object items with new ones. */ +CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON* array, int which, cJSON* newitem) { + cJSON* after_inserted = NULL; + + if(which < 0) { + return false; + } + + after_inserted = get_array_item(array, (size_t)which); + if(after_inserted == NULL) { + return add_item_to_array(array, newitem); + } + + newitem->next = after_inserted; + newitem->prev = after_inserted->prev; + after_inserted->prev = newitem; + if(after_inserted == array->child) { + array->child = newitem; + } else { + newitem->prev->next = newitem; + } + return true; +} + +CJSON_PUBLIC(cJSON_bool) +cJSON_ReplaceItemViaPointer(cJSON* const parent, cJSON* const item, cJSON* replacement) { + if((parent == NULL) || (replacement == NULL) || (item == NULL)) { + return false; + } + + if(replacement == item) { + return true; + } + + replacement->next = item->next; + replacement->prev = item->prev; + + if(replacement->next != NULL) { + replacement->next->prev = replacement; + } + if(parent->child == item) { + if(parent->child->prev == parent->child) { + replacement->prev = replacement; + } + parent->child = replacement; + } else { /* + * To find the last item in array quickly, we use prev in array. + * We can't modify the last item's next pointer where this item was the parent's child + */ + if(replacement->prev != NULL) { + replacement->prev->next = replacement; + } + if(replacement->next == NULL) { + parent->child->prev = replacement; + } + } + + item->next = NULL; + item->prev = NULL; + cJSON_Delete(item); + + return true; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON* array, int which, cJSON* newitem) { + if(which < 0) { + return false; + } + + return cJSON_ReplaceItemViaPointer(array, get_array_item(array, (size_t)which), newitem); +} + +static cJSON_bool replace_item_in_object( + cJSON* object, + const char* string, + cJSON* replacement, + cJSON_bool case_sensitive) { + if((replacement == NULL) || (string == NULL)) { + return false; + } + + /* replace the name in the replacement */ + if(!(replacement->type & cJSON_StringIsConst) && (replacement->string != NULL)) { + cJSON_free(replacement->string); + } + replacement->string = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); + replacement->type &= ~cJSON_StringIsConst; + + return cJSON_ReplaceItemViaPointer( + object, get_object_item(object, string, case_sensitive), replacement); +} + +CJSON_PUBLIC(cJSON_bool) +cJSON_ReplaceItemInObject(cJSON* object, const char* string, cJSON* newitem) { + return replace_item_in_object(object, string, newitem, false); +} + +CJSON_PUBLIC(cJSON_bool) +cJSON_ReplaceItemInObjectCaseSensitive(cJSON* object, const char* string, cJSON* newitem) { + return replace_item_in_object(object, string, newitem, true); +} + +/* Create basic types: */ +CJSON_PUBLIC(cJSON*) cJSON_CreateNull(void) { + cJSON* item = cJSON_New_Item(&global_hooks); + if(item) { + item->type = cJSON_NULL; + } + + return item; +} + +CJSON_PUBLIC(cJSON*) cJSON_CreateTrue(void) { + cJSON* item = cJSON_New_Item(&global_hooks); + if(item) { + item->type = cJSON_True; + } + + return item; +} + +CJSON_PUBLIC(cJSON*) cJSON_CreateFalse(void) { + cJSON* item = cJSON_New_Item(&global_hooks); + if(item) { + item->type = cJSON_False; + } + + return item; +} + +CJSON_PUBLIC(cJSON*) cJSON_CreateBool(cJSON_bool boolean) { + cJSON* item = cJSON_New_Item(&global_hooks); + if(item) { + item->type = boolean ? cJSON_True : cJSON_False; + } + + return item; +} + +CJSON_PUBLIC(cJSON*) cJSON_CreateNumber(double num) { + cJSON* item = cJSON_New_Item(&global_hooks); + if(item) { + item->type = cJSON_Number; + item->valuedouble = num; + + /* use saturation in case of overflow */ + if(num >= INT_MAX) { + item->valueint = INT_MAX; + } else if(num <= (double)INT_MIN) { + item->valueint = INT_MIN; + } else { + item->valueint = (int)num; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON*) cJSON_CreateString(const char* string) { + cJSON* item = cJSON_New_Item(&global_hooks); + if(item) { + item->type = cJSON_String; + item->valuestring = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); + if(!item->valuestring) { + cJSON_Delete(item); + return NULL; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON*) cJSON_CreateStringReference(const char* string) { + cJSON* item = cJSON_New_Item(&global_hooks); + if(item != NULL) { + item->type = cJSON_String | cJSON_IsReference; + item->valuestring = (char*)cast_away_const(string); + } + + return item; +} + +CJSON_PUBLIC(cJSON*) cJSON_CreateObjectReference(const cJSON* child) { + cJSON* item = cJSON_New_Item(&global_hooks); + if(item != NULL) { + item->type = cJSON_Object | cJSON_IsReference; + item->child = (cJSON*)cast_away_const(child); + } + + return item; +} + +CJSON_PUBLIC(cJSON*) cJSON_CreateArrayReference(const cJSON* child) { + cJSON* item = cJSON_New_Item(&global_hooks); + if(item != NULL) { + item->type = cJSON_Array | cJSON_IsReference; + item->child = (cJSON*)cast_away_const(child); + } + + return item; +} + +CJSON_PUBLIC(cJSON*) cJSON_CreateRaw(const char* raw) { + cJSON* item = cJSON_New_Item(&global_hooks); + if(item) { + item->type = cJSON_Raw; + item->valuestring = (char*)cJSON_strdup((const unsigned char*)raw, &global_hooks); + if(!item->valuestring) { + cJSON_Delete(item); + return NULL; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON*) cJSON_CreateArray(void) { + cJSON* item = cJSON_New_Item(&global_hooks); + if(item) { + item->type = cJSON_Array; + } + + return item; +} + +CJSON_PUBLIC(cJSON*) cJSON_CreateObject(void) { + cJSON* item = cJSON_New_Item(&global_hooks); + if(item) { + item->type = cJSON_Object; + } + + return item; +} + +/* Create Arrays: */ +CJSON_PUBLIC(cJSON*) cJSON_CreateIntArray(const int* numbers, int count) { + size_t i = 0; + cJSON* n = NULL; + cJSON* p = NULL; + cJSON* a = NULL; + + if((count < 0) || (numbers == NULL)) { + return NULL; + } + + a = cJSON_CreateArray(); + + for(i = 0; a && (i < (size_t)count); i++) { + n = cJSON_CreateNumber(numbers[i]); + if(!n) { + cJSON_Delete(a); + return NULL; + } + if(!i) { + a->child = n; + } else { + suffix_object(p, n); + } + p = n; + } + + if(a && a->child) { + a->child->prev = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON*) cJSON_CreateFloatArray(const float* numbers, int count) { + size_t i = 0; + cJSON* n = NULL; + cJSON* p = NULL; + cJSON* a = NULL; + + if((count < 0) || (numbers == NULL)) { + return NULL; + } + + a = cJSON_CreateArray(); + + for(i = 0; a && (i < (size_t)count); i++) { + n = cJSON_CreateNumber((double)numbers[i]); + if(!n) { + cJSON_Delete(a); + return NULL; + } + if(!i) { + a->child = n; + } else { + suffix_object(p, n); + } + p = n; + } + + if(a && a->child) { + a->child->prev = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON*) cJSON_CreateDoubleArray(const double* numbers, int count) { + size_t i = 0; + cJSON* n = NULL; + cJSON* p = NULL; + cJSON* a = NULL; + + if((count < 0) || (numbers == NULL)) { + return NULL; + } + + a = cJSON_CreateArray(); + + for(i = 0; a && (i < (size_t)count); i++) { + n = cJSON_CreateNumber(numbers[i]); + if(!n) { + cJSON_Delete(a); + return NULL; + } + if(!i) { + a->child = n; + } else { + suffix_object(p, n); + } + p = n; + } + + if(a && a->child) { + a->child->prev = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON*) cJSON_CreateStringArray(const char* const* strings, int count) { + size_t i = 0; + cJSON* n = NULL; + cJSON* p = NULL; + cJSON* a = NULL; + + if((count < 0) || (strings == NULL)) { + return NULL; + } + + a = cJSON_CreateArray(); + + for(i = 0; a && (i < (size_t)count); i++) { + n = cJSON_CreateString(strings[i]); + if(!n) { + cJSON_Delete(a); + return NULL; + } + if(!i) { + a->child = n; + } else { + suffix_object(p, n); + } + p = n; + } + + if(a && a->child) { + a->child->prev = n; + } + + return a; +} + +/* Duplication */ +CJSON_PUBLIC(cJSON*) cJSON_Duplicate(const cJSON* item, cJSON_bool recurse) { + cJSON* newitem = NULL; + cJSON* child = NULL; + cJSON* next = NULL; + cJSON* newchild = NULL; + + /* Bail on bad ptr */ + if(!item) { + goto fail; + } + /* Create new item */ + newitem = cJSON_New_Item(&global_hooks); + if(!newitem) { + goto fail; + } + /* Copy over all vars */ + newitem->type = item->type & (~cJSON_IsReference); + newitem->valueint = item->valueint; + newitem->valuedouble = item->valuedouble; + if(item->valuestring) { + newitem->valuestring = + (char*)cJSON_strdup((unsigned char*)item->valuestring, &global_hooks); + if(!newitem->valuestring) { + goto fail; + } + } + if(item->string) { + newitem->string = (item->type & cJSON_StringIsConst) ? + item->string : + (char*)cJSON_strdup((unsigned char*)item->string, &global_hooks); + if(!newitem->string) { + goto fail; + } + } + /* If non-recursive, then we're done! */ + if(!recurse) { + return newitem; + } + /* Walk the ->next chain for the child. */ + child = item->child; + while(child != NULL) { + newchild = cJSON_Duplicate( + child, true); /* Duplicate (with recurse) each item in the ->next chain */ + if(!newchild) { + goto fail; + } + if(next != NULL) { + /* If newitem->child already set, then crosswire ->prev and ->next and move on */ + next->next = newchild; + newchild->prev = next; + next = newchild; + } else { + /* Set newitem->child and move to it */ + newitem->child = newchild; + next = newchild; + } + child = child->next; + } + if(newitem && newitem->child) { + newitem->child->prev = newchild; + } + + return newitem; + +fail: + if(newitem != NULL) { + cJSON_Delete(newitem); + } + + return NULL; +} + +static void skip_oneline_comment(char** input) { + *input += static_strlen("//"); + + for(; (*input)[0] != '\0'; ++(*input)) { + if((*input)[0] == '\n') { + *input += static_strlen("\n"); + return; + } + } +} + +static void skip_multiline_comment(char** input) { + *input += static_strlen("/*"); + + for(; (*input)[0] != '\0'; ++(*input)) { + if(((*input)[0] == '*') && ((*input)[1] == '/')) { + *input += static_strlen("*/"); + return; + } + } +} + +static void minify_string(char** input, char** output) { + (*output)[0] = (*input)[0]; + *input += static_strlen("\""); + *output += static_strlen("\""); + + for(; (*input)[0] != '\0'; (void)++(*input), ++(*output)) { + (*output)[0] = (*input)[0]; + + if((*input)[0] == '\"') { + (*output)[0] = '\"'; + *input += static_strlen("\""); + *output += static_strlen("\""); + return; + } else if(((*input)[0] == '\\') && ((*input)[1] == '\"')) { + (*output)[1] = (*input)[1]; + *input += static_strlen("\""); + *output += static_strlen("\""); + } + } +} + +CJSON_PUBLIC(void) cJSON_Minify(char* json) { + char* into = json; + + if(json == NULL) { + return; + } + + while(json[0] != '\0') { + switch(json[0]) { + case ' ': + case '\t': + case '\r': + case '\n': + json++; + break; + + case '/': + if(json[1] == '/') { + skip_oneline_comment(&json); + } else if(json[1] == '*') { + skip_multiline_comment(&json); + } else { + json++; + } + break; + + case '\"': + minify_string(&json, (char**)&into); + break; + + default: + into[0] = json[0]; + json++; + into++; + } + } + + /* and null-terminate. */ + *into = '\0'; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON* const item) { + if(item == NULL) { + return false; + } + + return (item->type & 0xFF) == cJSON_Invalid; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON* const item) { + if(item == NULL) { + return false; + } + + return (item->type & 0xFF) == cJSON_False; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON* const item) { + if(item == NULL) { + return false; + } + + return (item->type & 0xff) == cJSON_True; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON* const item) { + if(item == NULL) { + return false; + } + + return (item->type & (cJSON_True | cJSON_False)) != 0; +} +CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON* const item) { + if(item == NULL) { + return false; + } + + return (item->type & 0xFF) == cJSON_NULL; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON* const item) { + if(item == NULL) { + return false; + } + + return (item->type & 0xFF) == cJSON_Number; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON* const item) { + if(item == NULL) { + return false; + } + + return (item->type & 0xFF) == cJSON_String; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON* const item) { + if(item == NULL) { + return false; + } + + return (item->type & 0xFF) == cJSON_Array; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON* const item) { + if(item == NULL) { + return false; + } + + return (item->type & 0xFF) == cJSON_Object; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON* const item) { + if(item == NULL) { + return false; + } + + return (item->type & 0xFF) == cJSON_Raw; +} + +CJSON_PUBLIC(cJSON_bool) +cJSON_Compare(const cJSON* const a, const cJSON* const b, const cJSON_bool case_sensitive) { + if((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF))) { + return false; + } + + /* check if type is valid */ + switch(a->type & 0xFF) { + case cJSON_False: + case cJSON_True: + case cJSON_NULL: + case cJSON_Number: + case cJSON_String: + case cJSON_Raw: + case cJSON_Array: + case cJSON_Object: + break; + + default: + return false; + } + + /* identical objects are equal */ + if(a == b) { + return true; + } + + switch(a->type & 0xFF) { + /* in these cases and equal type is enough */ + case cJSON_False: + case cJSON_True: + case cJSON_NULL: + return true; + + case cJSON_Number: + if(compare_double(a->valuedouble, b->valuedouble)) { + return true; + } + return false; + + case cJSON_String: + case cJSON_Raw: + if((a->valuestring == NULL) || (b->valuestring == NULL)) { + return false; + } + if(strcmp(a->valuestring, b->valuestring) == 0) { + return true; + } + + return false; + + case cJSON_Array: { + cJSON* a_element = a->child; + cJSON* b_element = b->child; + + for(; (a_element != NULL) && (b_element != NULL);) { + if(!cJSON_Compare(a_element, b_element, case_sensitive)) { + return false; + } + + a_element = a_element->next; + b_element = b_element->next; + } + + /* one of the arrays is longer than the other */ + if(a_element != b_element) { + return false; + } + + return true; + } + + case cJSON_Object: { + cJSON* a_element = NULL; + cJSON* b_element = NULL; + cJSON_ArrayForEach(a_element, a) { + /* TODO This has O(n^2) runtime, which is horrible! */ + b_element = get_object_item(b, a_element->string, case_sensitive); + if(b_element == NULL) { + return false; + } + + if(!cJSON_Compare(a_element, b_element, case_sensitive)) { + return false; + } + } + + /* doing this twice, once on a and b to prevent true comparison if a subset of b + * TODO: Do this the proper way, this is just a fix for now */ + cJSON_ArrayForEach(b_element, b) { + a_element = get_object_item(a, b_element->string, case_sensitive); + if(a_element == NULL) { + return false; + } + + if(!cJSON_Compare(b_element, a_element, case_sensitive)) { + return false; + } + } + + return true; + } + + default: + return false; + } +} + +CJSON_PUBLIC(void*) cJSON_malloc(size_t size) { + return global_hooks.allocate(size); +} + +CJSON_PUBLIC(void) cJSON_free(void* object) { + global_hooks.deallocate(object); +} diff --git a/applications/external/wifi_marauder_companion/script/cJSON.h b/applications/external/wifi_marauder_companion/script/cJSON.h new file mode 100644 index 000000000..14ec83d9d --- /dev/null +++ b/applications/external/wifi_marauder_companion/script/cJSON.h @@ -0,0 +1,321 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + 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 cJSON__h +#define cJSON__h + +#ifdef __cplusplus +extern "C" { +#endif + +#if !defined(__WINDOWS__) && \ + (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32)) +#define __WINDOWS__ +#endif + +#ifdef __WINDOWS__ + +/* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project with a different default calling convention. For windows you have 3 define options: + +CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols +CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default) +CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol + +For *nix builds that support visibility attribute, you can define similar behavior by + +setting default visibility to hidden by adding +-fvisibility=hidden (for gcc) +or +-xldscope=hidden (for sun cc) +to CFLAGS + +then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does + +*/ + +#define CJSON_CDECL __cdecl +#define CJSON_STDCALL __stdcall + +/* export symbols by default, this is necessary for copy pasting the C and header file */ +#if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && \ + !defined(CJSON_EXPORT_SYMBOLS) +#define CJSON_EXPORT_SYMBOLS +#endif + +#if defined(CJSON_HIDE_SYMBOLS) +#define CJSON_PUBLIC(type) type CJSON_STDCALL +#elif defined(CJSON_EXPORT_SYMBOLS) +#define CJSON_PUBLIC(type) __declspec(dllexport) type CJSON_STDCALL +#elif defined(CJSON_IMPORT_SYMBOLS) +#define CJSON_PUBLIC(type) __declspec(dllimport) type CJSON_STDCALL +#endif +#else /* !__WINDOWS__ */ +#define CJSON_CDECL +#define CJSON_STDCALL + +#if(defined(__GNUC__) || defined(__SUNPRO_CC) || defined(__SUNPRO_C)) && \ + defined(CJSON_API_VISIBILITY) +#define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type +#else +#define CJSON_PUBLIC(type) type +#endif +#endif + +/* project version */ +#define CJSON_VERSION_MAJOR 1 +#define CJSON_VERSION_MINOR 7 +#define CJSON_VERSION_PATCH 15 + +#include + +/* cJSON Types: */ +#define cJSON_Invalid (0) +#define cJSON_False (1 << 0) +#define cJSON_True (1 << 1) +#define cJSON_NULL (1 << 2) +#define cJSON_Number (1 << 3) +#define cJSON_String (1 << 4) +#define cJSON_Array (1 << 5) +#define cJSON_Object (1 << 6) +#define cJSON_Raw (1 << 7) /* raw json */ + +#define cJSON_IsReference 256 +#define cJSON_StringIsConst 512 + +/* The cJSON structure: */ +typedef struct cJSON { + /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */ + struct cJSON* next; + struct cJSON* prev; + /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */ + struct cJSON* child; + + /* The type of the item, as above. */ + int type; + + /* The item's string, if type==cJSON_String and type == cJSON_Raw */ + char* valuestring; + /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */ + int valueint; + /* The item's number, if type==cJSON_Number */ + double valuedouble; + + /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ + char* string; +} cJSON; + +typedef struct cJSON_Hooks { + /* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow passing those functions directly. */ + void*(CJSON_CDECL* malloc_fn)(size_t sz); + void(CJSON_CDECL* free_fn)(void* ptr); +} cJSON_Hooks; + +typedef int cJSON_bool; + +/* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them. + * This is to prevent stack overflows. */ +#ifndef CJSON_NESTING_LIMIT +#define CJSON_NESTING_LIMIT 1000 +#endif + +/* returns the version of cJSON as a string */ +CJSON_PUBLIC(const char*) cJSON_Version(void); + +/* Supply malloc, realloc and free functions to cJSON */ +CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks); + +/* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */ +/* Supply a block of JSON, and this returns a cJSON object you can interrogate. */ +CJSON_PUBLIC(cJSON*) cJSON_Parse(const char* value); +CJSON_PUBLIC(cJSON*) cJSON_ParseWithLength(const char* value, size_t buffer_length); +/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */ +/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match cJSON_GetErrorPtr(). */ +CJSON_PUBLIC(cJSON*) +cJSON_ParseWithOpts( + const char* value, + const char** return_parse_end, + cJSON_bool require_null_terminated); +CJSON_PUBLIC(cJSON*) +cJSON_ParseWithLengthOpts( + const char* value, + size_t buffer_length, + const char** return_parse_end, + cJSON_bool require_null_terminated); + +/* Render a cJSON entity to text for transfer/storage. */ +CJSON_PUBLIC(char*) cJSON_Print(const cJSON* item); +/* Render a cJSON entity to text for transfer/storage without any formatting. */ +CJSON_PUBLIC(char*) cJSON_PrintUnformatted(const cJSON* item); +/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */ +CJSON_PUBLIC(char*) cJSON_PrintBuffered(const cJSON* item, int prebuffer, cJSON_bool fmt); +/* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */ +/* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */ +CJSON_PUBLIC(cJSON_bool) +cJSON_PrintPreallocated(cJSON* item, char* buffer, const int length, const cJSON_bool format); +/* Delete a cJSON entity and all subentities. */ +CJSON_PUBLIC(void) cJSON_Delete(cJSON* item); + +/* Returns the number of items in an array (or object). */ +CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON* array); +/* Retrieve item number "index" from array "array". Returns NULL if unsuccessful. */ +CJSON_PUBLIC(cJSON*) cJSON_GetArrayItem(const cJSON* array, int index); +/* Get item "string" from object. Case insensitive. */ +CJSON_PUBLIC(cJSON*) cJSON_GetObjectItem(const cJSON* const object, const char* const string); +CJSON_PUBLIC(cJSON*) +cJSON_GetObjectItemCaseSensitive(const cJSON* const object, const char* const string); +CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON* object, const char* string); +/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */ +CJSON_PUBLIC(const char*) cJSON_GetErrorPtr(void); + +/* Check item type and return its value */ +CJSON_PUBLIC(char*) cJSON_GetStringValue(const cJSON* const item); +CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON* const item); + +/* These functions check the type of an item */ +CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON* const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON* const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON* const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON* const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON* const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON* const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON* const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON* const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON* const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON* const item); + +/* These calls create a cJSON item of the appropriate type. */ +CJSON_PUBLIC(cJSON*) cJSON_CreateNull(void); +CJSON_PUBLIC(cJSON*) cJSON_CreateTrue(void); +CJSON_PUBLIC(cJSON*) cJSON_CreateFalse(void); +CJSON_PUBLIC(cJSON*) cJSON_CreateBool(cJSON_bool boolean); +CJSON_PUBLIC(cJSON*) cJSON_CreateNumber(double num); +CJSON_PUBLIC(cJSON*) cJSON_CreateString(const char* string); +/* raw json */ +CJSON_PUBLIC(cJSON*) cJSON_CreateRaw(const char* raw); +CJSON_PUBLIC(cJSON*) cJSON_CreateArray(void); +CJSON_PUBLIC(cJSON*) cJSON_CreateObject(void); + +/* Create a string where valuestring references a string so + * it will not be freed by cJSON_Delete */ +CJSON_PUBLIC(cJSON*) cJSON_CreateStringReference(const char* string); +/* Create an object/array that only references it's elements so + * they will not be freed by cJSON_Delete */ +CJSON_PUBLIC(cJSON*) cJSON_CreateObjectReference(const cJSON* child); +CJSON_PUBLIC(cJSON*) cJSON_CreateArrayReference(const cJSON* child); + +/* These utilities create an Array of count items. + * The parameter count cannot be greater than the number of elements in the number array, otherwise array access will be out of bounds.*/ +CJSON_PUBLIC(cJSON*) cJSON_CreateIntArray(const int* numbers, int count); +CJSON_PUBLIC(cJSON*) cJSON_CreateFloatArray(const float* numbers, int count); +CJSON_PUBLIC(cJSON*) cJSON_CreateDoubleArray(const double* numbers, int count); +CJSON_PUBLIC(cJSON*) cJSON_CreateStringArray(const char* const* strings, int count); + +/* Append item to the specified array/object. */ +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON* array, cJSON* item); +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON* object, const char* string, cJSON* item); +/* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object. + * WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before + * writing to `item->string` */ +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON* object, const char* string, cJSON* item); +/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */ +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON* array, cJSON* item); +CJSON_PUBLIC(cJSON_bool) +cJSON_AddItemReferenceToObject(cJSON* object, const char* string, cJSON* item); + +/* Remove/Detach items from Arrays/Objects. */ +CJSON_PUBLIC(cJSON*) cJSON_DetachItemViaPointer(cJSON* parent, cJSON* const item); +CJSON_PUBLIC(cJSON*) cJSON_DetachItemFromArray(cJSON* array, int which); +CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON* array, int which); +CJSON_PUBLIC(cJSON*) cJSON_DetachItemFromObject(cJSON* object, const char* string); +CJSON_PUBLIC(cJSON*) cJSON_DetachItemFromObjectCaseSensitive(cJSON* object, const char* string); +CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON* object, const char* string); +CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON* object, const char* string); + +/* Update array items. */ +CJSON_PUBLIC(cJSON_bool) +cJSON_InsertItemInArray( + cJSON* array, + int which, + cJSON* newitem); /* Shifts pre-existing items to the right. */ +CJSON_PUBLIC(cJSON_bool) +cJSON_ReplaceItemViaPointer(cJSON* const parent, cJSON* const item, cJSON* replacement); +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON* array, int which, cJSON* newitem); +CJSON_PUBLIC(cJSON_bool) +cJSON_ReplaceItemInObject(cJSON* object, const char* string, cJSON* newitem); +CJSON_PUBLIC(cJSON_bool) +cJSON_ReplaceItemInObjectCaseSensitive(cJSON* object, const char* string, cJSON* newitem); + +/* Duplicate a cJSON item */ +CJSON_PUBLIC(cJSON*) cJSON_Duplicate(const cJSON* item, cJSON_bool recurse); +/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will + * need to be released. With recurse!=0, it will duplicate any children connected to the item. + * The item->next and ->prev pointers are always zero on return from Duplicate. */ +/* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal. + * case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */ +CJSON_PUBLIC(cJSON_bool) +cJSON_Compare(const cJSON* const a, const cJSON* const b, const cJSON_bool case_sensitive); + +/* Minify a strings, remove blank characters(such as ' ', '\t', '\r', '\n') from strings. + * The input pointer json cannot point to a read-only address area, such as a string constant, + * but should point to a readable and writable address area. */ +CJSON_PUBLIC(void) cJSON_Minify(char* json); + +/* Helper functions for creating and adding items to an object at the same time. + * They return the added item or NULL on failure. */ +CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON* const object, const char* const name); +CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON* const object, const char* const name); +CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON* const object, const char* const name); +CJSON_PUBLIC(cJSON*) +cJSON_AddBoolToObject(cJSON* const object, const char* const name, const cJSON_bool boolean); +CJSON_PUBLIC(cJSON*) +cJSON_AddNumberToObject(cJSON* const object, const char* const name, const double number); +CJSON_PUBLIC(cJSON*) +cJSON_AddStringToObject(cJSON* const object, const char* const name, const char* const string); +CJSON_PUBLIC(cJSON*) +cJSON_AddRawToObject(cJSON* const object, const char* const name, const char* const raw); +CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON* const object, const char* const name); +CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON* const object, const char* const name); + +/* When assigning an integer value, it needs to be propagated to valuedouble too. */ +#define cJSON_SetIntValue(object, number) \ + ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number)) +/* helper for the cJSON_SetNumberValue macro */ +CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON* object, double number); +#define cJSON_SetNumberValue(object, number) \ + ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number)) +/* Change the valuestring of a cJSON_String object, only takes effect when type of object is cJSON_String */ +CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON* object, const char* valuestring); + +/* Macro for iterating over an array or object */ +#define cJSON_ArrayForEach(element, array) \ + for(element = (array != NULL) ? (array)->child : NULL; element != NULL; \ + element = element->next) + +/* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */ +CJSON_PUBLIC(void*) cJSON_malloc(size_t size); +CJSON_PUBLIC(void) cJSON_free(void* object); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu.c b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu.c new file mode 100644 index 000000000..6fe853eb6 --- /dev/null +++ b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu.c @@ -0,0 +1,32 @@ +#include "wifi_marauder_script_stage_menu.h" + +WifiMarauderScriptStageMenu* + wifi_marauder_script_stage_menu_create(WifiMarauderScriptStageType stage_type) { + WifiMarauderScriptStageMenu* script_stage_menu = malloc(sizeof(WifiMarauderScriptStageMenu)); + + switch(stage_type) { +#define ADD_STAGE(name, id) \ + case WifiMarauderScriptStageType##id: \ + wifi_marauder_script_stage_menu_##name##_load(script_stage_menu); \ + break; + +#include "wifi_marauder_script_stage_menu_config.h" +#undef ADD_STAGE + } + return script_stage_menu; +} + +void wifi_marauder_script_stage_menu_free(WifiMarauderScriptStageMenu* stage_menu) { + if(stage_menu == NULL) { + return; + } + for(uint32_t i = 0; i < stage_menu->num_items; i++) { + WifiMarauderScriptMenuItem* item = &(stage_menu->items[i]); + for(int j = 0; j < item->num_options; j++) { + free(item->options[j]); + } + free(item->name); + } + free(stage_menu->items); + free(stage_menu); +} diff --git a/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu.h b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu.h new file mode 100644 index 000000000..f5186526c --- /dev/null +++ b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu.h @@ -0,0 +1,42 @@ +#pragma once + +#include +#include "../wifi_marauder_script.h" + +#define ITEM_EDIT_MAX_OPTIONS (12) + +typedef void (*VariableItemSetupCallback)(VariableItem* item); +typedef void (*VariableItemSelectCallback)(void* context); + +typedef enum WifiMarauderScriptMenuItemType { + WifiMarauderScriptMenuItemTypeString, + WifiMarauderScriptMenuItemTypeNumber, + WifiMarauderScriptMenuItemTypeOptionsString, + WifiMarauderScriptMenuItemTypeOptionsNumber, + WifiMarauderScriptMenuItemTypeListString, + WifiMarauderScriptMenuItemTypeListNumber +} WifiMarauderScriptMenuItemType; + +typedef struct WifiMarauderScriptMenuItem { + char* name; + WifiMarauderScriptMenuItemType type; + int num_options; + char* options[ITEM_EDIT_MAX_OPTIONS]; + VariableItemSetupCallback setup_callback; + VariableItemChangeCallback change_callback; + VariableItemSelectCallback select_callback; +} WifiMarauderScriptMenuItem; + +typedef struct WifiMarauderScriptStageMenu { + WifiMarauderScriptMenuItem* items; + uint32_t num_items; +} WifiMarauderScriptStageMenu; + +#define ADD_STAGE(name, id) \ + void wifi_marauder_script_stage_menu_##name##_load(WifiMarauderScriptStageMenu*); +#include "wifi_marauder_script_stage_menu_config.h" +#undef ADD_STAGE + +WifiMarauderScriptStageMenu* + wifi_marauder_script_stage_menu_create(WifiMarauderScriptStageType stage_type); +void wifi_marauder_script_stage_menu_free(WifiMarauderScriptStageMenu* list); diff --git a/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_beaconap.c b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_beaconap.c new file mode 100644 index 000000000..35a74ee3d --- /dev/null +++ b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_beaconap.c @@ -0,0 +1,27 @@ +#include "../../wifi_marauder_app_i.h" + +void wifi_marauder_beaconap_stage_timeout_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageBeaconAp* stage = app->script_edit_selected_stage->stage; + char timeout_str[32]; + snprintf(timeout_str, sizeof(timeout_str), "%d", stage->timeout); + variable_item_set_current_value_text(item, timeout_str); +} + +void wifi_marauder_beaconap_stage_timeout_select_callback(void* context) { + WifiMarauderApp* app = context; + WifiMarauderScriptStageBeaconAp* stage_beaconap = app->script_edit_selected_stage->stage; + app->user_input_number_reference = &stage_beaconap->timeout; +} + +void wifi_marauder_script_stage_menu_beaconap_load(WifiMarauderScriptStageMenu* stage_menu) { + stage_menu->num_items = 1; + stage_menu->items = malloc(1 * sizeof(WifiMarauderScriptMenuItem)); + + stage_menu->items[0] = (WifiMarauderScriptMenuItem){ + .name = "Timeout", + .type = WifiMarauderScriptMenuItemTypeNumber, + .num_options = 1, + .setup_callback = wifi_marauder_beaconap_stage_timeout_setup_callback, + .select_callback = wifi_marauder_beaconap_stage_timeout_select_callback}; +} \ No newline at end of file diff --git a/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_beaconlist.c b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_beaconlist.c new file mode 100644 index 000000000..6f320db3e --- /dev/null +++ b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_beaconlist.c @@ -0,0 +1,59 @@ +#include "../../wifi_marauder_app_i.h" + +void wifi_marauder_beaconlist_stage_ssids_select_callback(void* context) { + WifiMarauderApp* app = context; + WifiMarauderScriptStageBeaconList* stage_beaconlist = app->script_edit_selected_stage->stage; + app->script_stage_edit_strings_reference = &stage_beaconlist->ssids; + app->script_stage_edit_string_count_reference = &stage_beaconlist->ssid_count; +} + +void wifi_marauder_beaconlist_stage_random_ssids_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageBeaconList* stage = app->script_edit_selected_stage->stage; + char random_ssids_str[32]; + snprintf(random_ssids_str, sizeof(random_ssids_str), "%d", stage->random_ssids); + variable_item_set_current_value_text(item, random_ssids_str); +} + +void wifi_marauder_beaconlist_stage_random_ssids_select_callback(void* context) { + WifiMarauderApp* app = context; + WifiMarauderScriptStageBeaconList* stage_beaconlist = app->script_edit_selected_stage->stage; + app->user_input_number_reference = &stage_beaconlist->random_ssids; +} + +void wifi_marauder_beaconlist_stage_timeout_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageBeaconList* stage = app->script_edit_selected_stage->stage; + char timeout_str[32]; + snprintf(timeout_str, sizeof(timeout_str), "%d", stage->timeout); + variable_item_set_current_value_text(item, timeout_str); +} + +void wifi_marauder_beaconlist_stage_timeout_select_callback(void* context) { + WifiMarauderApp* app = context; + WifiMarauderScriptStageBeaconList* stage_beaconlist = app->script_edit_selected_stage->stage; + app->user_input_number_reference = &stage_beaconlist->timeout; +} + +void wifi_marauder_script_stage_menu_beaconlist_load(WifiMarauderScriptStageMenu* stage_menu) { + stage_menu->num_items = 3; + stage_menu->items = malloc(3 * sizeof(WifiMarauderScriptMenuItem)); + + stage_menu->items[0] = (WifiMarauderScriptMenuItem){ + .name = strdup("SSIDs"), + .type = WifiMarauderScriptMenuItemTypeListString, + .num_options = 1, + .select_callback = wifi_marauder_beaconlist_stage_ssids_select_callback}; + stage_menu->items[1] = (WifiMarauderScriptMenuItem){ + .name = strdup("Generate random"), + .type = WifiMarauderScriptMenuItemTypeNumber, + .num_options = 1, + .setup_callback = wifi_marauder_beaconlist_stage_random_ssids_setup_callback, + .select_callback = wifi_marauder_beaconlist_stage_random_ssids_select_callback}; + stage_menu->items[2] = (WifiMarauderScriptMenuItem){ + .name = strdup("Timeout"), + .type = WifiMarauderScriptMenuItemTypeNumber, + .num_options = 1, + .setup_callback = wifi_marauder_beaconlist_stage_timeout_setup_callback, + .select_callback = wifi_marauder_beaconlist_stage_timeout_select_callback}; +} \ No newline at end of file diff --git a/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_config.h b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_config.h new file mode 100644 index 000000000..1fd2a314b --- /dev/null +++ b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_config.h @@ -0,0 +1,14 @@ +ADD_STAGE(scan, Scan) +ADD_STAGE(select, Select) +ADD_STAGE(deauth, Deauth) +ADD_STAGE(probe, Probe) +ADD_STAGE(sniffraw, SniffRaw) +ADD_STAGE(sniffbeacon, SniffBeacon) +ADD_STAGE(sniffdeauth, SniffDeauth) +ADD_STAGE(sniffesp, SniffEsp) +ADD_STAGE(sniffpmkid, SniffPmkid) +ADD_STAGE(sniffpwn, SniffPwn) +ADD_STAGE(beaconlist, BeaconList) +ADD_STAGE(beaconap, BeaconAp) +ADD_STAGE(exec, Exec) +ADD_STAGE(delay, Delay) \ No newline at end of file diff --git a/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_deauth.c b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_deauth.c new file mode 100644 index 000000000..b15b6f461 --- /dev/null +++ b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_deauth.c @@ -0,0 +1,27 @@ +#include "../../wifi_marauder_app_i.h" + +void wifi_marauder_deauth_stage_timeout_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageDeauth* stage = app->script_edit_selected_stage->stage; + char timeout_str[32]; + snprintf(timeout_str, sizeof(timeout_str), "%d", stage->timeout); + variable_item_set_current_value_text(item, timeout_str); +} + +void wifi_marauder_deauth_stage_timeout_select_callback(void* context) { + WifiMarauderApp* app = context; + WifiMarauderScriptStageDeauth* stage_deauth = app->script_edit_selected_stage->stage; + app->user_input_number_reference = &stage_deauth->timeout; +} + +void wifi_marauder_script_stage_menu_deauth_load(WifiMarauderScriptStageMenu* stage_menu) { + stage_menu->num_items = 1; + stage_menu->items = malloc(1 * sizeof(WifiMarauderScriptMenuItem)); + + stage_menu->items[0] = (WifiMarauderScriptMenuItem){ + .name = strdup("Timeout"), + .type = WifiMarauderScriptMenuItemTypeNumber, + .num_options = 1, + .setup_callback = wifi_marauder_deauth_stage_timeout_setup_callback, + .select_callback = wifi_marauder_deauth_stage_timeout_select_callback}; +} \ No newline at end of file diff --git a/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_delay.c b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_delay.c new file mode 100644 index 000000000..ffd74f720 --- /dev/null +++ b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_delay.c @@ -0,0 +1,27 @@ +#include "../../wifi_marauder_app_i.h" + +void wifi_marauder_delay_stage_timeout_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageDelay* stage = app->script_edit_selected_stage->stage; + char timeout_str[32]; + snprintf(timeout_str, sizeof(timeout_str), "%d", stage->timeout); + variable_item_set_current_value_text(item, timeout_str); +} + +void wifi_marauder_delay_stage_timeout_select_callback(void* context) { + WifiMarauderApp* app = context; + WifiMarauderScriptStageDelay* stage_delay = app->script_edit_selected_stage->stage; + app->user_input_number_reference = &stage_delay->timeout; +} + +void wifi_marauder_script_stage_menu_delay_load(WifiMarauderScriptStageMenu* stage_menu) { + stage_menu->num_items = 1; + stage_menu->items = malloc(1 * sizeof(WifiMarauderScriptMenuItem)); + + stage_menu->items[0] = (WifiMarauderScriptMenuItem){ + .name = strdup("Timeout"), + .type = WifiMarauderScriptMenuItemTypeNumber, + .num_options = 1, + .setup_callback = wifi_marauder_delay_stage_timeout_setup_callback, + .select_callback = wifi_marauder_delay_stage_timeout_select_callback}; +} \ No newline at end of file diff --git a/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_exec.c b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_exec.c new file mode 100644 index 000000000..62afdc2f3 --- /dev/null +++ b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_exec.c @@ -0,0 +1,30 @@ +#include "../../wifi_marauder_app_i.h" + +void wifi_marauder_exec_stage_filter_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageExec* stage = app->script_edit_selected_stage->stage; + if(stage->command != NULL) { + variable_item_set_current_value_text(item, stage->command); + } +} + +void wifi_marauder_exec_stage_filter_select_callback(void* context) { + WifiMarauderApp* app = context; + WifiMarauderScriptStageExec* stage_select = app->script_edit_selected_stage->stage; + if(stage_select->command == NULL) { + stage_select->command = malloc(128); + } + app->user_input_string_reference = &stage_select->command; +} + +void wifi_marauder_script_stage_menu_exec_load(WifiMarauderScriptStageMenu* stage_menu) { + stage_menu->num_items = 1; + stage_menu->items = malloc(1 * sizeof(WifiMarauderScriptMenuItem)); + + stage_menu->items[0] = (WifiMarauderScriptMenuItem){ + .name = strdup("Command"), + .type = WifiMarauderScriptMenuItemTypeString, + .num_options = 1, + .setup_callback = wifi_marauder_exec_stage_filter_setup_callback, + .select_callback = wifi_marauder_exec_stage_filter_select_callback}; +} \ No newline at end of file diff --git a/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_probe.c b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_probe.c new file mode 100644 index 000000000..53fa26f47 --- /dev/null +++ b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_probe.c @@ -0,0 +1,27 @@ +#include "../../wifi_marauder_app_i.h" + +void wifi_marauder_probe_stage_timeout_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageProbe* stage = app->script_edit_selected_stage->stage; + char timeout_str[32]; + snprintf(timeout_str, sizeof(timeout_str), "%d", stage->timeout); + variable_item_set_current_value_text(item, timeout_str); +} + +void wifi_marauder_probe_stage_timeout_select_callback(void* context) { + WifiMarauderApp* app = context; + WifiMarauderScriptStageProbe* stage_probe = app->script_edit_selected_stage->stage; + app->user_input_number_reference = &stage_probe->timeout; +} + +void wifi_marauder_script_stage_menu_probe_load(WifiMarauderScriptStageMenu* stage_menu) { + stage_menu->num_items = 1; + stage_menu->items = malloc(1 * sizeof(WifiMarauderScriptMenuItem)); + + stage_menu->items[0] = (WifiMarauderScriptMenuItem){ + .name = strdup("Timeout"), + .type = WifiMarauderScriptMenuItemTypeNumber, + .num_options = 1, + .setup_callback = wifi_marauder_probe_stage_timeout_setup_callback, + .select_callback = wifi_marauder_probe_stage_timeout_select_callback}; +} \ No newline at end of file diff --git a/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_scan.c b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_scan.c new file mode 100644 index 000000000..3aab740bb --- /dev/null +++ b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_scan.c @@ -0,0 +1,93 @@ +#include "../../wifi_marauder_app_i.h" + +void wifi_marauder_scan_stage_type_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageScan* stage = app->script_edit_selected_stage->stage; + variable_item_set_current_value_index(item, stage->type); +} + +void wifi_marauder_scan_stage_type_change_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + + // Get menu item + uint8_t current_stage_index = variable_item_list_get_selected_item_index(app->var_item_list); + const WifiMarauderScriptMenuItem* menu_item = + &app->script_stage_menu->items[current_stage_index]; + + // Defines the text of the selected option + uint8_t option_index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, menu_item->options[option_index]); + + // Updates the attribute value of the current stage + WifiMarauderScriptStageScan* stage = app->script_edit_selected_stage->stage; + stage->type = option_index; +} + +void wifi_marauder_scan_stage_channel_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageScan* stage = app->script_edit_selected_stage->stage; + if(stage->channel >= 0 && stage->channel < 12) { + variable_item_set_current_value_index(item, stage->channel); + } else { + variable_item_set_current_value_index(item, 0); + } +} + +void wifi_marauder_scan_stage_channel_change_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + + // Get menu item + uint8_t current_stage_index = variable_item_list_get_selected_item_index(app->var_item_list); + const WifiMarauderScriptMenuItem* menu_item = + &app->script_stage_menu->items[current_stage_index]; + + // Defines the text of the selected option + uint8_t option_index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, menu_item->options[option_index]); + + // Updates the attribute value of the current stage + WifiMarauderScriptStageScan* stage = app->script_edit_selected_stage->stage; + stage->channel = option_index; +} + +void wifi_marauder_scan_stage_timeout_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageScan* stage = app->script_edit_selected_stage->stage; + char timeout_str[32]; + snprintf(timeout_str, sizeof(timeout_str), "%d", stage->timeout); + variable_item_set_current_value_text(item, timeout_str); +} + +void wifi_marauder_scan_stage_timeout_select_callback(void* context) { + WifiMarauderApp* app = context; + WifiMarauderScriptStageScan* stage_scan = app->script_edit_selected_stage->stage; + app->user_input_number_reference = &stage_scan->timeout; +} + +void wifi_marauder_script_stage_menu_scan_load(WifiMarauderScriptStageMenu* stage_menu) { + stage_menu->num_items = 3; + stage_menu->items = malloc(3 * sizeof(WifiMarauderScriptMenuItem)); + + stage_menu->items[0] = (WifiMarauderScriptMenuItem){ + .name = strdup("Type"), + .type = WifiMarauderScriptMenuItemTypeOptionsString, + .num_options = 2, + .options = {"ap", "station"}, + .setup_callback = wifi_marauder_scan_stage_type_setup_callback, + .change_callback = wifi_marauder_scan_stage_type_change_callback, + }; + stage_menu->items[1] = (WifiMarauderScriptMenuItem){ + .name = strdup("Channel"), + .type = WifiMarauderScriptMenuItemTypeOptionsNumber, + .num_options = 12, + .options = {"none", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11"}, + .setup_callback = wifi_marauder_scan_stage_channel_setup_callback, + .change_callback = wifi_marauder_scan_stage_channel_change_callback, + }; + stage_menu->items[2] = (WifiMarauderScriptMenuItem){ + .name = strdup("Timeout"), + .type = WifiMarauderScriptMenuItemTypeNumber, + .num_options = 1, + .setup_callback = wifi_marauder_scan_stage_timeout_setup_callback, + .select_callback = wifi_marauder_scan_stage_timeout_select_callback}; +} \ No newline at end of file diff --git a/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_select.c b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_select.c new file mode 100644 index 000000000..a6121db95 --- /dev/null +++ b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_select.c @@ -0,0 +1,95 @@ +#include "../../wifi_marauder_app_i.h" + +void wifi_marauder_select_stage_type_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageSelect* stage = app->script_edit_selected_stage->stage; + variable_item_set_current_value_index(item, stage->type); +} + +void wifi_marauder_select_stage_type_change_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + + // Get menu item + uint8_t current_stage_index = variable_item_list_get_selected_item_index(app->var_item_list); + const WifiMarauderScriptMenuItem* menu_item = + &app->script_stage_menu->items[current_stage_index]; + + // Defines the text of the selected option + uint8_t option_index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, menu_item->options[option_index]); + + // Updates the attribute value of the current stage + WifiMarauderScriptStageSelect* stage = app->script_edit_selected_stage->stage; + stage->type = option_index; +} + +void wifi_marauder_select_stage_filter_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageSelect* stage = app->script_edit_selected_stage->stage; + + if(stage->filter != NULL) { + variable_item_set_current_value_index(item, 0); + variable_item_set_current_value_text(item, stage->filter); + } else { + variable_item_set_current_value_index(item, 1); + } +} + +void wifi_marauder_select_stage_filter_change_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageSelect* stage = app->script_edit_selected_stage->stage; + + // Clears the filter if you change the option. Flipper input box does not accept blank text + if(variable_item_get_current_value_index(item) == 1) { + stage->filter = NULL; + variable_item_set_current_value_index(item, 0); + variable_item_set_values_count(item, 1); + } + + if(stage->filter != NULL) { + variable_item_set_current_value_text(item, stage->filter); + } else { + variable_item_set_current_value_text(item, ""); + } +} + +void wifi_marauder_select_stage_filter_select_callback(void* context) { + WifiMarauderApp* app = context; + WifiMarauderScriptStageSelect* stage_select = app->script_edit_selected_stage->stage; + if(stage_select->filter == NULL) { + stage_select->filter = malloc(128); + } + app->user_input_string_reference = &stage_select->filter; +} + +void wifi_marauder_select_stage_indexes_select_callback(void* context) { + WifiMarauderApp* app = context; + WifiMarauderScriptStageSelect* stage_select = app->script_edit_selected_stage->stage; + app->script_stage_edit_numbers_reference = &stage_select->indexes; + app->script_stage_edit_number_count_reference = &stage_select->index_count; +} + +void wifi_marauder_script_stage_menu_select_load(WifiMarauderScriptStageMenu* stage_menu) { + stage_menu->num_items = 3; + stage_menu->items = malloc(3 * sizeof(WifiMarauderScriptMenuItem)); + + stage_menu->items[0] = (WifiMarauderScriptMenuItem){ + .name = strdup("Type"), + .type = WifiMarauderScriptMenuItemTypeOptionsString, + .num_options = 2, + .options = {"ap", "station"}, + .setup_callback = wifi_marauder_select_stage_type_setup_callback, + .change_callback = wifi_marauder_select_stage_type_change_callback}; + stage_menu->items[1] = (WifiMarauderScriptMenuItem){ + .name = strdup("Filter"), + .type = WifiMarauderScriptMenuItemTypeString, + .num_options = 2, + .setup_callback = wifi_marauder_select_stage_filter_setup_callback, + .change_callback = wifi_marauder_select_stage_filter_change_callback, + .select_callback = wifi_marauder_select_stage_filter_select_callback}; + stage_menu->items[2] = (WifiMarauderScriptMenuItem){ + .name = strdup("Indexes"), + .type = WifiMarauderScriptMenuItemTypeListNumber, + .num_options = 1, + .select_callback = wifi_marauder_select_stage_indexes_select_callback}; +} \ No newline at end of file diff --git a/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffbeacon.c b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffbeacon.c new file mode 100644 index 000000000..11e7b3297 --- /dev/null +++ b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffbeacon.c @@ -0,0 +1,27 @@ +#include "../../wifi_marauder_app_i.h" + +void wifi_marauder_sniffbeacon_stage_timeout_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageSniffBeacon* stage = app->script_edit_selected_stage->stage; + char timeout_str[32]; + snprintf(timeout_str, sizeof(timeout_str), "%d", stage->timeout); + variable_item_set_current_value_text(item, timeout_str); +} + +void wifi_marauder_sniffbeacon_stage_timeout_select_callback(void* context) { + WifiMarauderApp* app = context; + WifiMarauderScriptStageSniffBeacon* stage_sniffbeacon = app->script_edit_selected_stage->stage; + app->user_input_number_reference = &stage_sniffbeacon->timeout; +} + +void wifi_marauder_script_stage_menu_sniffbeacon_load(WifiMarauderScriptStageMenu* stage_menu) { + stage_menu->num_items = 1; + stage_menu->items = malloc(1 * sizeof(WifiMarauderScriptMenuItem)); + + stage_menu->items[0] = (WifiMarauderScriptMenuItem){ + .name = strdup("Timeout"), + .type = WifiMarauderScriptMenuItemTypeNumber, + .num_options = 1, + .setup_callback = wifi_marauder_sniffbeacon_stage_timeout_setup_callback, + .select_callback = wifi_marauder_sniffbeacon_stage_timeout_select_callback}; +} \ No newline at end of file diff --git a/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffdeauth.c b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffdeauth.c new file mode 100644 index 000000000..935a55936 --- /dev/null +++ b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffdeauth.c @@ -0,0 +1,27 @@ +#include "../../wifi_marauder_app_i.h" + +void wifi_marauder_sniffdeauth_stage_timeout_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageSniffDeauth* stage = app->script_edit_selected_stage->stage; + char timeout_str[32]; + snprintf(timeout_str, sizeof(timeout_str), "%d", stage->timeout); + variable_item_set_current_value_text(item, timeout_str); +} + +void wifi_marauder_sniffdeauth_stage_timeout_select_callback(void* context) { + WifiMarauderApp* app = context; + WifiMarauderScriptStageSniffDeauth* stage_sniffdeauth = app->script_edit_selected_stage->stage; + app->user_input_number_reference = &stage_sniffdeauth->timeout; +} + +void wifi_marauder_script_stage_menu_sniffdeauth_load(WifiMarauderScriptStageMenu* stage_menu) { + stage_menu->num_items = 1; + stage_menu->items = malloc(1 * sizeof(WifiMarauderScriptMenuItem)); + + stage_menu->items[0] = (WifiMarauderScriptMenuItem){ + .name = strdup("Timeout"), + .type = WifiMarauderScriptMenuItemTypeNumber, + .num_options = 1, + .setup_callback = wifi_marauder_sniffdeauth_stage_timeout_setup_callback, + .select_callback = wifi_marauder_sniffdeauth_stage_timeout_select_callback}; +} \ No newline at end of file diff --git a/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffesp.c b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffesp.c new file mode 100644 index 000000000..e90d6b06c --- /dev/null +++ b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffesp.c @@ -0,0 +1,27 @@ +#include "../../wifi_marauder_app_i.h" + +void wifi_marauder_sniffesp_stage_timeout_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageSniffEsp* stage = app->script_edit_selected_stage->stage; + char timeout_str[32]; + snprintf(timeout_str, sizeof(timeout_str), "%d", stage->timeout); + variable_item_set_current_value_text(item, timeout_str); +} + +void wifi_marauder_sniffesp_stage_timeout_select_callback(void* context) { + WifiMarauderApp* app = context; + WifiMarauderScriptStageSniffEsp* stage_sniffesp = app->script_edit_selected_stage->stage; + app->user_input_number_reference = &stage_sniffesp->timeout; +} + +void wifi_marauder_script_stage_menu_sniffesp_load(WifiMarauderScriptStageMenu* stage_menu) { + stage_menu->num_items = 1; + stage_menu->items = malloc(1 * sizeof(WifiMarauderScriptMenuItem)); + + stage_menu->items[0] = (WifiMarauderScriptMenuItem){ + .name = strdup("Timeout"), + .type = WifiMarauderScriptMenuItemTypeNumber, + .num_options = 1, + .setup_callback = wifi_marauder_sniffesp_stage_timeout_setup_callback, + .select_callback = wifi_marauder_sniffesp_stage_timeout_select_callback}; +} \ No newline at end of file diff --git a/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffpmkid.c b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffpmkid.c new file mode 100644 index 000000000..d4f1f8f36 --- /dev/null +++ b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffpmkid.c @@ -0,0 +1,91 @@ +#include "../../wifi_marauder_app_i.h" + +static void wifi_marauder_sniffpmkid_stage_force_deauth_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageSniffPmkid* stage = app->script_edit_selected_stage->stage; + variable_item_set_current_value_index(item, stage->force_deauth); +} + +static void wifi_marauder_sniffpmkid_stage_force_deauth_change_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + + // Get menu item + uint8_t current_stage_index = variable_item_list_get_selected_item_index(app->var_item_list); + const WifiMarauderScriptMenuItem* menu_item = + &app->script_stage_menu->items[current_stage_index]; + + // Defines the text of the selected option + uint8_t option_index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, menu_item->options[option_index]); + + // Updates the attribute value of the current stage + WifiMarauderScriptStageSniffPmkid* stage = app->script_edit_selected_stage->stage; + stage->force_deauth = option_index; +} + +static void wifi_marauder_sniffpmkid_stage_channel_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageSniffPmkid* stage = app->script_edit_selected_stage->stage; + if(stage->channel >= 0 && stage->channel < 12) { + variable_item_set_current_value_index(item, stage->channel); + } else { + variable_item_set_current_value_index(item, 0); + } +} + +static void wifi_marauder_sniffpmkid_stage_channel_change_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + + // Get menu item + uint8_t current_stage_index = variable_item_list_get_selected_item_index(app->var_item_list); + const WifiMarauderScriptMenuItem* menu_item = + &app->script_stage_menu->items[current_stage_index]; + + // Defines the text of the selected option + uint8_t option_index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, menu_item->options[option_index]); + + // Updates the attribute value of the current stage + WifiMarauderScriptStageSniffPmkid* stage = app->script_edit_selected_stage->stage; + stage->channel = option_index; +} + +static void wifi_marauder_sniffpmkid_stage_timeout_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageSniffPmkid* stage = app->script_edit_selected_stage->stage; + char timeout_str[32]; + snprintf(timeout_str, sizeof(timeout_str), "%d", stage->timeout); + variable_item_set_current_value_text(item, timeout_str); +} + +static void wifi_marauder_sniffpmkid_stage_timeout_select_callback(void* context) { + WifiMarauderApp* app = context; + WifiMarauderScriptStageSniffPmkid* stage_sniffpmkid = app->script_edit_selected_stage->stage; + app->user_input_number_reference = &stage_sniffpmkid->timeout; +} + +void wifi_marauder_script_stage_menu_sniffpmkid_load(WifiMarauderScriptStageMenu* stage_menu) { + stage_menu->num_items = 3; + stage_menu->items = malloc(3 * sizeof(WifiMarauderScriptMenuItem)); + + stage_menu->items[0] = (WifiMarauderScriptMenuItem){ + .name = strdup("Force deauth"), + .type = WifiMarauderScriptMenuItemTypeOptionsString, + .num_options = 2, + .options = {"no", "yes"}, + .setup_callback = wifi_marauder_sniffpmkid_stage_force_deauth_setup_callback, + .change_callback = wifi_marauder_sniffpmkid_stage_force_deauth_change_callback}; + stage_menu->items[1] = (WifiMarauderScriptMenuItem){ + .name = strdup("Channel"), + .type = WifiMarauderScriptMenuItemTypeOptionsNumber, + .num_options = 12, + .options = {"none", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11"}, + .setup_callback = wifi_marauder_sniffpmkid_stage_channel_setup_callback, + .change_callback = wifi_marauder_sniffpmkid_stage_channel_change_callback}; + stage_menu->items[2] = (WifiMarauderScriptMenuItem){ + .name = strdup("Timeout"), + .type = WifiMarauderScriptMenuItemTypeNumber, + .num_options = 1, + .setup_callback = wifi_marauder_sniffpmkid_stage_timeout_setup_callback, + .select_callback = wifi_marauder_sniffpmkid_stage_timeout_select_callback}; +} \ No newline at end of file diff --git a/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffpwn.c b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffpwn.c new file mode 100644 index 000000000..d0859cd8b --- /dev/null +++ b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffpwn.c @@ -0,0 +1,27 @@ +#include "../../wifi_marauder_app_i.h" + +void wifi_marauder_sniffpwn_stage_timeout_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageSniffPwn* stage = app->script_edit_selected_stage->stage; + char timeout_str[32]; + snprintf(timeout_str, sizeof(timeout_str), "%d", stage->timeout); + variable_item_set_current_value_text(item, timeout_str); +} + +void wifi_marauder_sniffpwn_stage_timeout_select_callback(void* context) { + WifiMarauderApp* app = context; + WifiMarauderScriptStageSniffPwn* stage_sniffpwn = app->script_edit_selected_stage->stage; + app->user_input_number_reference = &stage_sniffpwn->timeout; +} + +void wifi_marauder_script_stage_menu_sniffpwn_load(WifiMarauderScriptStageMenu* stage_menu) { + stage_menu->num_items = 1; + stage_menu->items = malloc(1 * sizeof(WifiMarauderScriptMenuItem)); + + stage_menu->items[0] = (WifiMarauderScriptMenuItem){ + .name = strdup("Timeout"), + .type = WifiMarauderScriptMenuItemTypeNumber, + .num_options = 1, + .setup_callback = wifi_marauder_sniffpwn_stage_timeout_setup_callback, + .select_callback = wifi_marauder_sniffpwn_stage_timeout_select_callback}; +} \ No newline at end of file diff --git a/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffraw.c b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffraw.c new file mode 100644 index 000000000..39641f1ee --- /dev/null +++ b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffraw.c @@ -0,0 +1,27 @@ +#include "../../wifi_marauder_app_i.h" + +void wifi_marauder_sniffraw_stage_timeout_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageSniffRaw* stage = app->script_edit_selected_stage->stage; + char timeout_str[32]; + snprintf(timeout_str, sizeof(timeout_str), "%d", stage->timeout); + variable_item_set_current_value_text(item, timeout_str); +} + +void wifi_marauder_sniffraw_stage_timeout_select_callback(void* context) { + WifiMarauderApp* app = context; + WifiMarauderScriptStageSniffRaw* stage_sniffraw = app->script_edit_selected_stage->stage; + app->user_input_number_reference = &stage_sniffraw->timeout; +} + +void wifi_marauder_script_stage_menu_sniffraw_load(WifiMarauderScriptStageMenu* stage_menu) { + stage_menu->num_items = 1; + stage_menu->items = malloc(1 * sizeof(WifiMarauderScriptMenuItem)); + + stage_menu->items[0] = (WifiMarauderScriptMenuItem){ + .name = strdup("Timeout"), + .type = WifiMarauderScriptMenuItemTypeNumber, + .num_options = 1, + .setup_callback = wifi_marauder_sniffraw_stage_timeout_setup_callback, + .select_callback = wifi_marauder_sniffraw_stage_timeout_select_callback}; +} \ No newline at end of file diff --git a/applications/external/wifi_marauder_companion/script/wifi_marauder_script.c b/applications/external/wifi_marauder_companion/script/wifi_marauder_script.c new file mode 100644 index 000000000..64dfacef5 --- /dev/null +++ b/applications/external/wifi_marauder_companion/script/wifi_marauder_script.c @@ -0,0 +1,947 @@ +#include "../wifi_marauder_app_i.h" +#include "wifi_marauder_script.h" + +WifiMarauderScript* wifi_marauder_script_alloc() { + WifiMarauderScript* script = (WifiMarauderScript*)malloc(sizeof(WifiMarauderScript)); + if(script == NULL) { + return NULL; + } + script->name = NULL; + script->description = NULL; + script->first_stage = NULL; + script->last_stage = NULL; + script->enable_led = WifiMarauderScriptBooleanUndefined; + script->save_pcap = WifiMarauderScriptBooleanUndefined; + script->repeat = 1; + return script; +} + +WifiMarauderScript* wifi_marauder_script_create(const char* script_name) { + WifiMarauderScript* script = wifi_marauder_script_alloc(); + script->name = strdup(script_name); + return script; +} + +void _wifi_marauder_script_load_meta(WifiMarauderScript* script, cJSON* meta_section) { + if(meta_section != NULL) { + // Script description + cJSON* description = cJSON_GetObjectItem(meta_section, "description"); + if(description != NULL) { + script->description = strdup(description->valuestring); + } + // Enable LED + cJSON* enable_led_json = cJSON_GetObjectItem(meta_section, "enableLed"); + if(cJSON_IsBool(enable_led_json)) { + script->enable_led = enable_led_json->valueint; + } + // Save PCAP + cJSON* save_pcap_json = cJSON_GetObjectItem(meta_section, "savePcap"); + if(cJSON_IsBool(save_pcap_json)) { + script->save_pcap = save_pcap_json->valueint; + } + // Times the script will be repeated + cJSON* repeat = cJSON_GetObjectItem(meta_section, "repeat"); + if(repeat != NULL) { + script->repeat = repeat->valueint; + } + } + if(script->description == NULL) { + script->description = strdup("My script"); + } +} + +WifiMarauderScriptStageScan* _wifi_marauder_script_get_stage_scan(cJSON* stages) { + cJSON* stage_scan = cJSON_GetObjectItem(stages, "scan"); + if(stage_scan == NULL) { + return NULL; + } + cJSON* type = cJSON_GetObjectItem(stage_scan, "type"); + if(type == NULL) { + return NULL; + } + WifiMarauderScriptScanType scan_type; + if(strcmp(type->valuestring, "ap") == 0) { + scan_type = WifiMarauderScriptScanTypeAp; + } else if(strcmp(type->valuestring, "station") == 0) { + scan_type = WifiMarauderScriptScanTypeStation; + } else { + return NULL; + } + cJSON* channel = cJSON_GetObjectItem(stage_scan, "channel"); + int scan_channel = channel != NULL ? (int)cJSON_GetNumberValue(channel) : 0; + cJSON* timeout = cJSON_GetObjectItem(stage_scan, "timeout"); + int scan_timeout = timeout != NULL ? (int)cJSON_GetNumberValue(timeout) : + WIFI_MARAUDER_DEFAULT_TIMEOUT_SCAN; + + WifiMarauderScriptStageScan* scan_stage = + (WifiMarauderScriptStageScan*)malloc(sizeof(WifiMarauderScriptStageScan)); + scan_stage->type = scan_type; + scan_stage->channel = scan_channel; + scan_stage->timeout = scan_timeout; + + return scan_stage; +} + +WifiMarauderScriptStageSelect* _wifi_marauder_script_get_stage_select(cJSON* stages) { + cJSON* select_stage_json = cJSON_GetObjectItemCaseSensitive(stages, "select"); + if(select_stage_json == NULL) { + return NULL; + } + + cJSON* type_json = cJSON_GetObjectItemCaseSensitive(select_stage_json, "type"); + cJSON* filter_json = cJSON_GetObjectItemCaseSensitive(select_stage_json, "filter"); + cJSON* indexes_json = cJSON_GetObjectItemCaseSensitive(select_stage_json, "indexes"); + cJSON* allow_repeat_json = cJSON_GetObjectItemCaseSensitive(select_stage_json, "allow_repeat"); + + if(!cJSON_IsString(type_json)) { + return NULL; + } + WifiMarauderScriptSelectType select_type; + if(strcmp(type_json->valuestring, "ap") == 0) { + select_type = WifiMarauderScriptSelectTypeAp; + } else if(strcmp(type_json->valuestring, "station") == 0) { + select_type = WifiMarauderScriptSelectTypeStation; + } else if(strcmp(type_json->valuestring, "ssid") == 0) { + select_type = WifiMarauderScriptSelectTypeSsid; + } else { + return NULL; + } + char* filter_str = cJSON_IsString(filter_json) ? strdup(filter_json->valuestring) : NULL; + + WifiMarauderScriptStageSelect* stage_select = + (WifiMarauderScriptStageSelect*)malloc(sizeof(WifiMarauderScriptStageSelect)); + stage_select->type = select_type; + stage_select->allow_repeat = cJSON_IsBool(allow_repeat_json) ? allow_repeat_json->valueint : + true; + stage_select->filter = filter_str; + + if(cJSON_IsArray(indexes_json)) { + int indexes_size = cJSON_GetArraySize(indexes_json); + int* indexes = (int*)malloc(indexes_size * sizeof(int)); + for(int i = 0; i < indexes_size; i++) { + cJSON* index_item = cJSON_GetArrayItem(indexes_json, i); + if(cJSON_IsNumber(index_item)) { + indexes[i] = index_item->valueint; + } + } + stage_select->indexes = indexes; + stage_select->index_count = indexes_size; + } else { + stage_select->indexes = NULL; + stage_select->index_count = 0; + } + + return stage_select; +} + +WifiMarauderScriptStageDeauth* _wifi_marauder_script_get_stage_deauth(cJSON* stages) { + cJSON* deauth_stage_json = cJSON_GetObjectItemCaseSensitive(stages, "deauth"); + if(deauth_stage_json == NULL) { + return NULL; + } + + cJSON* timeout = cJSON_GetObjectItem(deauth_stage_json, "timeout"); + int deauth_timeout = timeout != NULL ? (int)cJSON_GetNumberValue(timeout) : + WIFI_MARAUDER_DEFAULT_TIMEOUT_DEAUTH; + + WifiMarauderScriptStageDeauth* deauth_stage = + (WifiMarauderScriptStageDeauth*)malloc(sizeof(WifiMarauderScriptStageDeauth)); + deauth_stage->timeout = deauth_timeout; + + return deauth_stage; +} + +WifiMarauderScriptStageProbe* _wifi_marauder_script_get_stage_probe(cJSON* stages) { + cJSON* probe_stage_json = cJSON_GetObjectItemCaseSensitive(stages, "probe"); + if(probe_stage_json == NULL) { + return NULL; + } + + cJSON* timeout = cJSON_GetObjectItem(probe_stage_json, "timeout"); + int probe_timeout = timeout != NULL ? (int)cJSON_GetNumberValue(timeout) : + WIFI_MARAUDER_DEFAULT_TIMEOUT_PROBE; + + WifiMarauderScriptStageProbe* probe_stage = + (WifiMarauderScriptStageProbe*)malloc(sizeof(WifiMarauderScriptStageProbe)); + probe_stage->timeout = probe_timeout; + + return probe_stage; +} + +WifiMarauderScriptStageSniffRaw* _wifi_marauder_script_get_stage_sniff_raw(cJSON* stages) { + cJSON* sniffraw_stage_json = cJSON_GetObjectItem(stages, "sniffraw"); + if(sniffraw_stage_json == NULL) { + return NULL; + } + + cJSON* timeout_json = cJSON_GetObjectItem(sniffraw_stage_json, "timeout"); + int timeout = timeout_json != NULL ? (int)cJSON_GetNumberValue(timeout_json) : + WIFI_MARAUDER_DEFAULT_TIMEOUT_SNIFF; + + WifiMarauderScriptStageSniffRaw* sniff_raw_stage = + (WifiMarauderScriptStageSniffRaw*)malloc(sizeof(WifiMarauderScriptStageSniffRaw)); + sniff_raw_stage->timeout = timeout; + + return sniff_raw_stage; +} + +WifiMarauderScriptStageSniffBeacon* _wifi_marauder_script_get_stage_sniff_beacon(cJSON* stages) { + cJSON* sniffbeacon_stage_json = cJSON_GetObjectItem(stages, "sniffbeacon"); + if(sniffbeacon_stage_json == NULL) { + return NULL; + } + + cJSON* timeout_json = cJSON_GetObjectItem(sniffbeacon_stage_json, "timeout"); + int timeout = timeout_json != NULL ? (int)cJSON_GetNumberValue(timeout_json) : + WIFI_MARAUDER_DEFAULT_TIMEOUT_SNIFF; + + WifiMarauderScriptStageSniffBeacon* sniff_beacon_stage = + (WifiMarauderScriptStageSniffBeacon*)malloc(sizeof(WifiMarauderScriptStageSniffBeacon)); + sniff_beacon_stage->timeout = timeout; + + return sniff_beacon_stage; +} + +WifiMarauderScriptStageSniffDeauth* _wifi_marauder_script_get_stage_sniff_deauth(cJSON* stages) { + cJSON* sniffdeauth_stage_json = cJSON_GetObjectItem(stages, "sniffdeauth"); + if(sniffdeauth_stage_json == NULL) { + return NULL; + } + + cJSON* timeout_json = cJSON_GetObjectItem(sniffdeauth_stage_json, "timeout"); + int timeout = timeout_json != NULL ? (int)cJSON_GetNumberValue(timeout_json) : + WIFI_MARAUDER_DEFAULT_TIMEOUT_SNIFF; + + WifiMarauderScriptStageSniffDeauth* sniff_deauth_stage = + (WifiMarauderScriptStageSniffDeauth*)malloc(sizeof(WifiMarauderScriptStageSniffDeauth)); + sniff_deauth_stage->timeout = timeout; + + return sniff_deauth_stage; +} + +WifiMarauderScriptStageSniffEsp* _wifi_marauder_script_get_stage_sniff_esp(cJSON* stages) { + cJSON* sniffesp_stage_json = cJSON_GetObjectItem(stages, "sniffesp"); + if(sniffesp_stage_json == NULL) { + return NULL; + } + + cJSON* timeout_json = cJSON_GetObjectItem(sniffesp_stage_json, "timeout"); + int timeout = timeout_json != NULL ? (int)cJSON_GetNumberValue(timeout_json) : + WIFI_MARAUDER_DEFAULT_TIMEOUT_SNIFF; + + WifiMarauderScriptStageSniffEsp* sniff_esp_stage = + (WifiMarauderScriptStageSniffEsp*)malloc(sizeof(WifiMarauderScriptStageSniffEsp)); + sniff_esp_stage->timeout = timeout; + + return sniff_esp_stage; +} + +WifiMarauderScriptStageSniffPmkid* _wifi_marauder_script_get_stage_sniff_pmkid(cJSON* stages) { + cJSON* sniffpmkid_stage_json = cJSON_GetObjectItem(stages, "sniffpmkid"); + if(sniffpmkid_stage_json == NULL) { + return NULL; + } + + cJSON* channel_json = cJSON_GetObjectItem(sniffpmkid_stage_json, "channel"); + int channel = channel_json != NULL ? (int)cJSON_GetNumberValue(channel_json) : 0; + cJSON* timeout_json = cJSON_GetObjectItem(sniffpmkid_stage_json, "timeout"); + int timeout = timeout_json != NULL ? (int)cJSON_GetNumberValue(timeout_json) : + WIFI_MARAUDER_DEFAULT_TIMEOUT_SNIFF; + cJSON* force_deauth_json = + cJSON_GetObjectItemCaseSensitive(sniffpmkid_stage_json, "forceDeauth"); + bool force_deauth = cJSON_IsBool(force_deauth_json) ? force_deauth_json->valueint : true; + + WifiMarauderScriptStageSniffPmkid* sniff_pmkid_stage = + (WifiMarauderScriptStageSniffPmkid*)malloc(sizeof(WifiMarauderScriptStageSniffPmkid)); + sniff_pmkid_stage->channel = channel; + sniff_pmkid_stage->timeout = timeout; + sniff_pmkid_stage->force_deauth = force_deauth; + + return sniff_pmkid_stage; +} + +WifiMarauderScriptStageSniffPwn* _wifi_marauder_script_get_stage_sniff_pwn(cJSON* stages) { + cJSON* sniffpwn_stage_json = cJSON_GetObjectItem(stages, "sniffpwn"); + if(sniffpwn_stage_json == NULL) { + return NULL; + } + + cJSON* timeout_json = cJSON_GetObjectItem(sniffpwn_stage_json, "timeout"); + int timeout = timeout_json != NULL ? (int)cJSON_GetNumberValue(timeout_json) : + WIFI_MARAUDER_DEFAULT_TIMEOUT_SNIFF; + + WifiMarauderScriptStageSniffPwn* sniff_pwn_stage = + (WifiMarauderScriptStageSniffPwn*)malloc(sizeof(WifiMarauderScriptStageSniffPwn)); + sniff_pwn_stage->timeout = timeout; + + return sniff_pwn_stage; +} + +WifiMarauderScriptStageBeaconList* _wifi_marauder_script_get_stage_beacon_list(cJSON* stages) { + cJSON* stage_beaconlist = cJSON_GetObjectItem(stages, "beaconList"); + if(stage_beaconlist == NULL) { + return NULL; + } + WifiMarauderScriptStageBeaconList* beaconlist_stage = + (WifiMarauderScriptStageBeaconList*)malloc(sizeof(WifiMarauderScriptStageBeaconList)); + if(beaconlist_stage == NULL) { + return NULL; + } + cJSON* ssids = cJSON_GetObjectItem(stage_beaconlist, "ssids"); + if(ssids == NULL) { + return NULL; + } + // SSID count + int ssid_count = cJSON_GetArraySize(ssids); + if(ssid_count == 0) { + return NULL; + } + beaconlist_stage->ssid_count = ssid_count; + // SSIDs + beaconlist_stage->ssids = (char**)malloc(sizeof(char*) * ssid_count); + if(beaconlist_stage->ssids == NULL) { + return NULL; + } + for(int i = 0; i < ssid_count; i++) { + cJSON* ssid = cJSON_GetArrayItem(ssids, i); + if(ssid == NULL) { + continue; + } + char* ssid_string = cJSON_GetStringValue(ssid); + if(ssid_string == NULL) { + continue; + } + beaconlist_stage->ssids[i] = (char*)malloc(sizeof(char) * (strlen(ssid_string) + 1)); + strcpy(beaconlist_stage->ssids[i], ssid_string); + } + // Timeout + cJSON* timeout = cJSON_GetObjectItem(stage_beaconlist, "timeout"); + beaconlist_stage->timeout = timeout != NULL ? (int)cJSON_GetNumberValue(timeout) : + WIFI_MARAUDER_DEFAULT_TIMEOUT_BEACON; + // Random SSIDs + cJSON* random_ssids = cJSON_GetObjectItem(stage_beaconlist, "generate"); + beaconlist_stage->random_ssids = + random_ssids != NULL ? (int)cJSON_GetNumberValue(random_ssids) : 0; + + return beaconlist_stage; +} + +WifiMarauderScriptStageBeaconAp* _wifi_marauder_script_get_stage_beacon_ap(cJSON* stages) { + cJSON* beaconap_stage_json = cJSON_GetObjectItem(stages, "beaconAp"); + if(beaconap_stage_json == NULL) { + return NULL; + } + + cJSON* timeout_json = cJSON_GetObjectItem(beaconap_stage_json, "timeout"); + int timeout = timeout_json != NULL ? (int)cJSON_GetNumberValue(timeout_json) : + WIFI_MARAUDER_DEFAULT_TIMEOUT_BEACON; + + WifiMarauderScriptStageBeaconAp* beacon_ap_stage = + (WifiMarauderScriptStageBeaconAp*)malloc(sizeof(WifiMarauderScriptStageBeaconAp)); + beacon_ap_stage->timeout = timeout; + + return beacon_ap_stage; +} + +WifiMarauderScriptStageExec* _wifi_marauder_script_get_stage_exec(cJSON* stages) { + cJSON* exec_stage_json = cJSON_GetObjectItem(stages, "exec"); + if(exec_stage_json == NULL) { + return NULL; + } + + cJSON* command_json = cJSON_GetObjectItemCaseSensitive(exec_stage_json, "command"); + char* command_str = cJSON_IsString(command_json) ? strdup(command_json->valuestring) : NULL; + + WifiMarauderScriptStageExec* exec_stage = + (WifiMarauderScriptStageExec*)malloc(sizeof(WifiMarauderScriptStageExec)); + exec_stage->command = command_str; + + return exec_stage; +} + +WifiMarauderScriptStageDelay* _wifi_marauder_script_get_stage_delay(cJSON* stages) { + cJSON* delay_stage_json = cJSON_GetObjectItem(stages, "delay"); + if(delay_stage_json == NULL) { + return NULL; + } + + cJSON* timeout_json = cJSON_GetObjectItem(delay_stage_json, "timeout"); + int timeout = timeout_json != NULL ? (int)cJSON_GetNumberValue(timeout_json) : 0; + + WifiMarauderScriptStageDelay* delay_stage = + (WifiMarauderScriptStageDelay*)malloc(sizeof(WifiMarauderScriptStageDelay)); + delay_stage->timeout = timeout; + + return delay_stage; +} + +WifiMarauderScriptStage* + _wifi_marauder_script_create_stage(WifiMarauderScriptStageType type, void* stage_data) { + WifiMarauderScriptStage* stage = + (WifiMarauderScriptStage*)malloc(sizeof(WifiMarauderScriptStage)); + stage->type = type; + stage->stage = stage_data; + stage->next_stage = NULL; + return stage; +} + +void wifi_marauder_script_add_stage( + WifiMarauderScript* script, + WifiMarauderScriptStageType stage_type, + void* stage_data) { + if(script == NULL || stage_data == NULL) { + return; + } + WifiMarauderScriptStage* stage = _wifi_marauder_script_create_stage(stage_type, stage_data); + if(script->last_stage != NULL) { + script->last_stage->next_stage = stage; + } else { + script->first_stage = stage; + } + script->last_stage = stage; +} + +void _wifi_marauder_script_load_stages(WifiMarauderScript* script, cJSON* stages) { + // Scan stage + wifi_marauder_script_add_stage( + script, WifiMarauderScriptStageTypeScan, _wifi_marauder_script_get_stage_scan(stages)); + // Select stage + wifi_marauder_script_add_stage( + script, WifiMarauderScriptStageTypeSelect, _wifi_marauder_script_get_stage_select(stages)); + // Deauth stage + wifi_marauder_script_add_stage( + script, WifiMarauderScriptStageTypeDeauth, _wifi_marauder_script_get_stage_deauth(stages)); + // Probe stage + wifi_marauder_script_add_stage( + script, WifiMarauderScriptStageTypeProbe, _wifi_marauder_script_get_stage_probe(stages)); + // Sniff raw stage + wifi_marauder_script_add_stage( + script, + WifiMarauderScriptStageTypeSniffRaw, + _wifi_marauder_script_get_stage_sniff_raw(stages)); + // Sniff beacon stage + wifi_marauder_script_add_stage( + script, + WifiMarauderScriptStageTypeSniffBeacon, + _wifi_marauder_script_get_stage_sniff_beacon(stages)); + // Sniff deauth stage + wifi_marauder_script_add_stage( + script, + WifiMarauderScriptStageTypeSniffDeauth, + _wifi_marauder_script_get_stage_sniff_deauth(stages)); + // Sniff esp stage + wifi_marauder_script_add_stage( + script, + WifiMarauderScriptStageTypeSniffEsp, + _wifi_marauder_script_get_stage_sniff_esp(stages)); + // Sniff PMKID stage + wifi_marauder_script_add_stage( + script, + WifiMarauderScriptStageTypeSniffPmkid, + _wifi_marauder_script_get_stage_sniff_pmkid(stages)); + // Sniff pwn stage + wifi_marauder_script_add_stage( + script, + WifiMarauderScriptStageTypeSniffPwn, + _wifi_marauder_script_get_stage_sniff_pwn(stages)); + // Beacon List stage + wifi_marauder_script_add_stage( + script, + WifiMarauderScriptStageTypeBeaconList, + _wifi_marauder_script_get_stage_beacon_list(stages)); + // Beacon Ap stage + wifi_marauder_script_add_stage( + script, + WifiMarauderScriptStageTypeBeaconAp, + _wifi_marauder_script_get_stage_beacon_ap(stages)); + // Exec stage + wifi_marauder_script_add_stage( + script, WifiMarauderScriptStageTypeExec, _wifi_marauder_script_get_stage_exec(stages)); + // Delay stage + wifi_marauder_script_add_stage( + script, WifiMarauderScriptStageTypeDelay, _wifi_marauder_script_get_stage_delay(stages)); +} + +WifiMarauderScript* wifi_marauder_script_parse_raw(const char* json_raw) { + WifiMarauderScript* script = wifi_marauder_script_alloc(); + if(script == NULL) { + return NULL; + } + cJSON* json = cJSON_Parse(json_raw); + if(json == NULL) { + return NULL; + } + cJSON* meta = cJSON_GetObjectItem(json, "meta"); + _wifi_marauder_script_load_meta(script, meta); + + cJSON* stages = cJSON_GetObjectItem(json, "stages"); + if(cJSON_IsArray(stages)) { + cJSON* stage_item = NULL; + cJSON_ArrayForEach(stage_item, stages) { + _wifi_marauder_script_load_stages(script, stage_item); + } + } else { + _wifi_marauder_script_load_stages(script, stages); + } + + return script; +} + +WifiMarauderScript* wifi_marauder_script_parse_json(Storage* storage, const char* file_path) { + WifiMarauderScript* script = NULL; + File* script_file = storage_file_alloc(storage); + FuriString* script_name = furi_string_alloc(); + path_extract_filename_no_ext(file_path, script_name); + + if(storage_file_open(script_file, file_path, FSAM_READ, FSOM_OPEN_EXISTING)) { + uint32_t file_size = storage_file_size(script_file); + char* json_buffer = (char*)malloc(file_size + 1); + uint16_t bytes_read = storage_file_read(script_file, json_buffer, file_size); + json_buffer[bytes_read] = '\0'; + + script = wifi_marauder_script_parse_raw(json_buffer); + } + if(script == NULL) { + script = wifi_marauder_script_create(furi_string_get_cstr(script_name)); + } + script->name = strdup(furi_string_get_cstr(script_name)); + + furi_string_free(script_name); + storage_file_close(script_file); + storage_file_free(script_file); + return script; +} + +cJSON* _wifi_marauder_script_create_json_meta(WifiMarauderScript* script) { + cJSON* meta_json = cJSON_CreateObject(); + if(script->description != NULL) { + cJSON_AddStringToObject(meta_json, "description", script->description); + } else { + cJSON_AddStringToObject(meta_json, "description", "My Script"); + } + if(script->enable_led != WifiMarauderScriptBooleanUndefined) { + cJSON_AddBoolToObject( + meta_json, "enableLed", (script->enable_led == WifiMarauderScriptBooleanTrue)); + } + if(script->save_pcap != WifiMarauderScriptBooleanUndefined) { + cJSON_AddBoolToObject( + meta_json, "savePcap", (script->save_pcap == WifiMarauderScriptBooleanTrue)); + } + cJSON_AddNumberToObject(meta_json, "repeat", script->repeat); + return meta_json; +} + +cJSON* _wifi_marauder_script_create_json_scan(WifiMarauderScriptStageScan* scan_stage) { + cJSON* stage_json = cJSON_CreateObject(); + cJSON_AddItemToObject(stage_json, "scan", cJSON_CreateObject()); + cJSON* scan_json = cJSON_GetObjectItem(stage_json, "scan"); + // Scan type + cJSON_AddStringToObject( + scan_json, "type", scan_stage->type == WifiMarauderScriptScanTypeAp ? "ap" : "station"); + // Channel + if(scan_stage->channel > 0) { + cJSON_AddNumberToObject(scan_json, "channel", scan_stage->channel); + } + // Timeout + if(scan_stage->timeout > 0) { + cJSON_AddNumberToObject(scan_json, "timeout", scan_stage->timeout); + } + return stage_json; +} + +cJSON* _wifi_marauder_script_create_json_select(WifiMarauderScriptStageSelect* select_stage) { + cJSON* stage_json = cJSON_CreateObject(); + cJSON_AddItemToObject(stage_json, "select", cJSON_CreateObject()); + cJSON* select_json = cJSON_GetObjectItem(stage_json, "select"); + // Select type + cJSON_AddStringToObject( + select_json, + "type", + select_stage->type == WifiMarauderScriptSelectTypeAp ? "ap" : + select_stage->type == WifiMarauderScriptSelectTypeStation ? "station" : + "ssid"); + if(select_stage->filter != NULL) { + cJSON_AddStringToObject(select_json, "filter", select_stage->filter); + } + // Indexes + if(select_stage->indexes != NULL && select_stage->index_count > 0) { + cJSON* indexes_json = cJSON_CreateArray(); + for(int i = 0; i < select_stage->index_count; i++) { + cJSON_AddItemToArray(indexes_json, cJSON_CreateNumber(select_stage->indexes[i])); + } + cJSON_AddItemToObject(select_json, "indexes", indexes_json); + } + return stage_json; +} + +cJSON* _wifi_marauder_script_create_json_deauth(WifiMarauderScriptStageDeauth* deauth_stage) { + cJSON* stage_json = cJSON_CreateObject(); + cJSON_AddItemToObject(stage_json, "deauth", cJSON_CreateObject()); + cJSON* deauth_json = cJSON_GetObjectItem(stage_json, "deauth"); + // Timeout + if(deauth_stage->timeout > 0) { + cJSON_AddNumberToObject(deauth_json, "timeout", deauth_stage->timeout); + } + return stage_json; +} + +cJSON* _wifi_marauder_script_create_json_probe(WifiMarauderScriptStageProbe* probe_stage) { + cJSON* stage_json = cJSON_CreateObject(); + cJSON_AddItemToObject(stage_json, "probe", cJSON_CreateObject()); + cJSON* probe_json = cJSON_GetObjectItem(stage_json, "probe"); + // Timeout + if(probe_stage->timeout > 0) { + cJSON_AddNumberToObject(probe_json, "timeout", probe_stage->timeout); + } + return stage_json; +} + +cJSON* + _wifi_marauder_script_create_json_sniffraw(WifiMarauderScriptStageSniffRaw* sniffraw_stage) { + cJSON* stage_json = cJSON_CreateObject(); + cJSON_AddItemToObject(stage_json, "sniffRaw", cJSON_CreateObject()); + cJSON* sniffraw_json = cJSON_GetObjectItem(stage_json, "sniffRaw"); + // Timeout + if(sniffraw_stage->timeout > 0) { + cJSON_AddNumberToObject(sniffraw_json, "timeout", sniffraw_stage->timeout); + } + return stage_json; +} + +cJSON* _wifi_marauder_script_create_json_sniffbeacon( + WifiMarauderScriptStageSniffBeacon* sniffbeacon_stage) { + cJSON* stage_json = cJSON_CreateObject(); + cJSON_AddItemToObject(stage_json, "sniffBeacon", cJSON_CreateObject()); + cJSON* sniffbeacon_json = cJSON_GetObjectItem(stage_json, "sniffBeacon"); + // Timeout + if(sniffbeacon_stage->timeout > 0) { + cJSON_AddNumberToObject(sniffbeacon_json, "timeout", sniffbeacon_stage->timeout); + } + return stage_json; +} + +cJSON* _wifi_marauder_script_create_json_sniffdeauth( + WifiMarauderScriptStageSniffDeauth* sniffdeauth_stage) { + cJSON* stage_json = cJSON_CreateObject(); + cJSON_AddItemToObject(stage_json, "sniffDeauth", cJSON_CreateObject()); + cJSON* sniffdeauth_json = cJSON_GetObjectItem(stage_json, "sniffDeauth"); + // Timeout + if(sniffdeauth_stage->timeout > 0) { + cJSON_AddNumberToObject(sniffdeauth_json, "timeout", sniffdeauth_stage->timeout); + } + return stage_json; +} + +cJSON* + _wifi_marauder_script_create_json_sniffesp(WifiMarauderScriptStageSniffEsp* sniffesp_stage) { + cJSON* stage_json = cJSON_CreateObject(); + cJSON_AddItemToObject(stage_json, "sniffEsp", cJSON_CreateObject()); + cJSON* sniffesp_json = cJSON_GetObjectItem(stage_json, "sniffEsp"); + // Timeout + if(sniffesp_stage->timeout > 0) { + cJSON_AddNumberToObject(sniffesp_json, "timeout", sniffesp_stage->timeout); + } + return stage_json; +} + +cJSON* _wifi_marauder_script_create_json_sniffpmkid( + WifiMarauderScriptStageSniffPmkid* sniffpmkid_stage) { + cJSON* stage_json = cJSON_CreateObject(); + cJSON_AddItemToObject(stage_json, "sniffPmkid", cJSON_CreateObject()); + cJSON* sniffpmkid_json = cJSON_GetObjectItem(stage_json, "sniffPmkid"); + // Force deauth + cJSON_AddBoolToObject(sniffpmkid_json, "forceDeauth", sniffpmkid_stage->force_deauth); + // Channel + if(sniffpmkid_stage->channel > 0) { + cJSON_AddNumberToObject(sniffpmkid_json, "channel", sniffpmkid_stage->channel); + } + // Timeout + if(sniffpmkid_stage->timeout > 0) { + cJSON_AddNumberToObject(sniffpmkid_json, "timeout", sniffpmkid_stage->timeout); + } + return stage_json; +} + +cJSON* + _wifi_marauder_script_create_json_sniffpwn(WifiMarauderScriptStageSniffPwn* sniffpwn_stage) { + cJSON* stage_json = cJSON_CreateObject(); + cJSON_AddItemToObject(stage_json, "sniffPwn", cJSON_CreateObject()); + cJSON* sniffpwn_json = cJSON_GetObjectItem(stage_json, "sniffPwn"); + // Timeout + if(sniffpwn_stage->timeout > 0) { + cJSON_AddNumberToObject(sniffpwn_json, "timeout", sniffpwn_stage->timeout); + } + return stage_json; +} + +cJSON* _wifi_marauder_script_create_json_beaconlist( + WifiMarauderScriptStageBeaconList* beaconlist_stage) { + cJSON* stage_json = cJSON_CreateObject(); + cJSON_AddItemToObject(stage_json, "beaconList", cJSON_CreateObject()); + cJSON* beaconlist_json = cJSON_GetObjectItem(stage_json, "beaconList"); + // SSIDs + if(beaconlist_stage->ssids != NULL) { + cJSON* ssids_json = cJSON_CreateStringArray( + (const char**)beaconlist_stage->ssids, beaconlist_stage->ssid_count); + cJSON_AddItemToObject(beaconlist_json, "ssids", ssids_json); + } + // Random SSIDs + if(beaconlist_stage->random_ssids > 0) { + cJSON_AddNumberToObject(beaconlist_json, "generate", beaconlist_stage->random_ssids); + } + // Timeout + if(beaconlist_stage->timeout > 0) { + cJSON_AddNumberToObject(beaconlist_json, "timeout", beaconlist_stage->timeout); + } + return stage_json; +} + +cJSON* + _wifi_marauder_script_create_json_beaconap(WifiMarauderScriptStageBeaconAp* beaconap_stage) { + cJSON* stage_json = cJSON_CreateObject(); + cJSON_AddItemToObject(stage_json, "beaconAp", cJSON_CreateObject()); + cJSON* beaconap_json = cJSON_GetObjectItem(stage_json, "beaconAp"); + // Timeout + if(beaconap_stage->timeout > 0) { + cJSON_AddNumberToObject(beaconap_json, "timeout", beaconap_stage->timeout); + } + return stage_json; +} + +cJSON* _wifi_marauder_script_create_json_exec(WifiMarauderScriptStageExec* exec_stage) { + cJSON* stage_json = cJSON_CreateObject(); + cJSON_AddItemToObject(stage_json, "exec", cJSON_CreateObject()); + cJSON* exec_json = cJSON_GetObjectItem(stage_json, "exec"); + // Command + cJSON_AddStringToObject( + exec_json, "command", exec_stage->command != NULL ? exec_stage->command : ""); + return stage_json; +} + +cJSON* _wifi_marauder_script_create_json_delay(WifiMarauderScriptStageDelay* delay_stage) { + cJSON* stage_json = cJSON_CreateObject(); + cJSON_AddItemToObject(stage_json, "delay", cJSON_CreateObject()); + cJSON* delay_json = cJSON_GetObjectItem(stage_json, "delay"); + // Timeout + if(delay_stage->timeout > 0) { + cJSON_AddNumberToObject(delay_json, "timeout", delay_stage->timeout); + } + return stage_json; +} + +void wifi_marauder_script_save_json( + Storage* storage, + const char* file_path, + WifiMarauderScript* script) { + File* script_file = storage_file_alloc(storage); + + if(storage_file_open(script_file, file_path, FSAM_WRITE, FSOM_CREATE_ALWAYS)) { + cJSON* root_json = cJSON_CreateObject(); + + // Meta info + cJSON* meta_json = _wifi_marauder_script_create_json_meta(script); + cJSON_AddItemToObject(root_json, "meta", meta_json); + + // Create array for stages + cJSON* stages_array = cJSON_CreateArray(); + cJSON_AddItemToObject(root_json, "stages", stages_array); + + // Iterate over each stage and create the corresponding JSON object + WifiMarauderScriptStage* stage = script->first_stage; + while(stage != NULL) { + cJSON* stage_json = NULL; + + switch(stage->type) { + case WifiMarauderScriptStageTypeScan: { + WifiMarauderScriptStageScan* scan_stage = + (WifiMarauderScriptStageScan*)stage->stage; + stage_json = _wifi_marauder_script_create_json_scan(scan_stage); + break; + } + case WifiMarauderScriptStageTypeSelect: { + WifiMarauderScriptStageSelect* select_stage = + (WifiMarauderScriptStageSelect*)stage->stage; + stage_json = _wifi_marauder_script_create_json_select(select_stage); + break; + } + case WifiMarauderScriptStageTypeDeauth: { + WifiMarauderScriptStageDeauth* deauth_stage = + (WifiMarauderScriptStageDeauth*)stage->stage; + stage_json = _wifi_marauder_script_create_json_deauth(deauth_stage); + break; + } + case WifiMarauderScriptStageTypeProbe: { + WifiMarauderScriptStageProbe* probe_stage = + (WifiMarauderScriptStageProbe*)stage->stage; + stage_json = _wifi_marauder_script_create_json_probe(probe_stage); + break; + } + case WifiMarauderScriptStageTypeSniffRaw: { + WifiMarauderScriptStageSniffRaw* sniffraw_stage = + (WifiMarauderScriptStageSniffRaw*)stage->stage; + stage_json = _wifi_marauder_script_create_json_sniffraw(sniffraw_stage); + break; + } + case WifiMarauderScriptStageTypeSniffBeacon: { + WifiMarauderScriptStageSniffBeacon* sniffbeacon_stage = + (WifiMarauderScriptStageSniffBeacon*)stage->stage; + stage_json = _wifi_marauder_script_create_json_sniffbeacon(sniffbeacon_stage); + break; + } + case WifiMarauderScriptStageTypeSniffDeauth: { + WifiMarauderScriptStageSniffDeauth* sniffdeauth_stage = + (WifiMarauderScriptStageSniffDeauth*)stage->stage; + stage_json = _wifi_marauder_script_create_json_sniffdeauth(sniffdeauth_stage); + break; + } + case WifiMarauderScriptStageTypeSniffEsp: { + WifiMarauderScriptStageSniffEsp* sniffesp_stage = + (WifiMarauderScriptStageSniffEsp*)stage->stage; + stage_json = _wifi_marauder_script_create_json_sniffesp(sniffesp_stage); + break; + } + case WifiMarauderScriptStageTypeSniffPmkid: { + WifiMarauderScriptStageSniffPmkid* sniffpmkid_stage = + (WifiMarauderScriptStageSniffPmkid*)stage->stage; + stage_json = _wifi_marauder_script_create_json_sniffpmkid(sniffpmkid_stage); + break; + } + case WifiMarauderScriptStageTypeSniffPwn: { + WifiMarauderScriptStageSniffPwn* sniffpwn_stage = + (WifiMarauderScriptStageSniffPwn*)stage->stage; + stage_json = _wifi_marauder_script_create_json_sniffpwn(sniffpwn_stage); + break; + } + case WifiMarauderScriptStageTypeBeaconList: { + WifiMarauderScriptStageBeaconList* beaconlist_stage = + (WifiMarauderScriptStageBeaconList*)stage->stage; + stage_json = _wifi_marauder_script_create_json_beaconlist(beaconlist_stage); + break; + } + case WifiMarauderScriptStageTypeBeaconAp: { + WifiMarauderScriptStageBeaconAp* beaconap_stage = + (WifiMarauderScriptStageBeaconAp*)stage->stage; + stage_json = _wifi_marauder_script_create_json_beaconap(beaconap_stage); + break; + } + case WifiMarauderScriptStageTypeExec: { + WifiMarauderScriptStageExec* exec_stage = + (WifiMarauderScriptStageExec*)stage->stage; + stage_json = _wifi_marauder_script_create_json_exec(exec_stage); + break; + } + case WifiMarauderScriptStageTypeDelay: { + WifiMarauderScriptStageDelay* delay_stage = + (WifiMarauderScriptStageDelay*)stage->stage; + stage_json = _wifi_marauder_script_create_json_delay(delay_stage); + break; + } + } + + // Add the stage JSON object to the "stages" array + if(stage_json != NULL) { + cJSON_AddItemToArray(stages_array, stage_json); + } + + stage = stage->next_stage; + } + + // Write JSON to file + char* json_str = cJSON_Print(root_json); + storage_file_write(script_file, json_str, strlen(json_str)); + + //free(json_str); + storage_file_close(script_file); + } + storage_file_free(script_file); +} + +bool wifi_marauder_script_has_stage( + WifiMarauderScript* script, + WifiMarauderScriptStageType stage_type) { + if(script == NULL) { + return false; + } + WifiMarauderScriptStage* current_stage = script->first_stage; + while(current_stage != NULL) { + if(current_stage->type == stage_type) { + return true; + } + current_stage = current_stage->next_stage; + } + return false; +} + +void wifi_marauder_script_free(WifiMarauderScript* script) { + if(script == NULL) { + return; + } + WifiMarauderScriptStage* current_stage = script->first_stage; + while(current_stage != NULL) { + WifiMarauderScriptStage* next_stage = current_stage->next_stage; + switch(current_stage->type) { + case WifiMarauderScriptStageTypeScan: + free(current_stage->stage); + break; + case WifiMarauderScriptStageTypeSelect: + if(((WifiMarauderScriptStageSelect*)current_stage->stage)->filter != NULL) { + free(((WifiMarauderScriptStageSelect*)current_stage->stage)->filter); + } + if(((WifiMarauderScriptStageSelect*)current_stage->stage)->indexes != NULL) { + free(((WifiMarauderScriptStageSelect*)current_stage->stage)->indexes); + } + free(current_stage->stage); + break; + case WifiMarauderScriptStageTypeDeauth: + free(current_stage->stage); + break; + case WifiMarauderScriptStageTypeProbe: + free(current_stage->stage); + break; + case WifiMarauderScriptStageTypeSniffRaw: + free(current_stage->stage); + break; + case WifiMarauderScriptStageTypeSniffBeacon: + free(current_stage->stage); + break; + case WifiMarauderScriptStageTypeSniffDeauth: + free(current_stage->stage); + break; + case WifiMarauderScriptStageTypeSniffEsp: + free(current_stage->stage); + break; + case WifiMarauderScriptStageTypeSniffPmkid: + free(current_stage->stage); + break; + case WifiMarauderScriptStageTypeSniffPwn: + free(current_stage->stage); + break; + case WifiMarauderScriptStageTypeBeaconList: + for(int i = 0; + i < ((WifiMarauderScriptStageBeaconList*)current_stage->stage)->ssid_count; + i++) { + free(((WifiMarauderScriptStageBeaconList*)current_stage->stage)->ssids[i]); + } + free(((WifiMarauderScriptStageBeaconList*)current_stage->stage)->ssids); + free(current_stage->stage); + break; + case WifiMarauderScriptStageTypeBeaconAp: + free(current_stage->stage); + break; + case WifiMarauderScriptStageTypeExec: + if(((WifiMarauderScriptStageExec*)current_stage->stage)->command != NULL) { + free(((WifiMarauderScriptStageExec*)current_stage->stage)->command); + } + free(current_stage->stage); + break; + case WifiMarauderScriptStageTypeDelay: + free(current_stage->stage); + break; + } + free(current_stage); + current_stage = next_stage; + } + free(script->name); + free(script->description); + free(script); +} \ No newline at end of file diff --git a/applications/external/wifi_marauder_companion/script/wifi_marauder_script.h b/applications/external/wifi_marauder_companion/script/wifi_marauder_script.h new file mode 100644 index 000000000..e11ee267f --- /dev/null +++ b/applications/external/wifi_marauder_companion/script/wifi_marauder_script.h @@ -0,0 +1,257 @@ +/* + * ---------------------------------------------------------------------------------------------------- + * STEPS TO ADD A NEW STAGE: + * + * wifi_marauder_script.h + * - Complement WifiMarauderScriptStageType enum with new stage + * - Create struct WifiMarauderScriptStage???? for the new stage + * + * wifi_marauder_script.c + * - Change _wifi_marauder_script_load_stages() to load new stage + * - Change wifi_marauder_script_save_json() to support the new stage + * - Add case to free memory in wifi_marauder_script_free() + * + * wifi_marauder_script_executor.c + * - Create function "void _wifi_marauder_script_execute_????(WifiMarauderScriptStage????* stage)" + * - Add case in wifi_marauder_script_execute_stage() + * + * wifi_marauder_scene_script_edit.c + * - Add case in wifi_marauder_scene_script_edit_on_enter() + * + * wifi_marauder_scene_script_stage_add.c + * - Create stage creation function and add in wifi_marauder_scene_script_stage_add_on_enter() + * + * wifi_marauder_script_stage_menu_config.h + * - Add the new stage and implement its functions in a new file + * + * ---------------------------------------------------------------------------------------------------- + * SCRIPT SYNTAX (In order of execution): + * { + * "meta": { + * "description": "My script", + * "repeat": times the script will repeat (default 1), + * "enableLed": true (default) | false, + * "savePcap": true (default) | false + * }, + * "stages": { + * "scan": { + * "type": "ap" | "station", + * "timeout": seconds, + * "channel": 1-11 + * }, + * "select": { + * "type": "ap" | "station" | "ssid", + * "filter": "all" | "contains -f '{SSID fragment}' or equals '{SSID}' or ...", + * "indexes": [0, 1, 2, 3...], + * }, + * "deauth": { + * "timeout": seconds + * }, + * "probe": { + * "timeout": seconds + * }, + * "sniffRaw": { + * "timeout": seconds + * }, + * "sniffBeacon": { + * "timeout": seconds + * }, + * "sniffDeauth": { + * "timeout": seconds + * }, + * "sniffEsp": { + * "timeout": seconds + * }, + * "sniffPmkid": { + * "forceDeauth": true (default) | false, + * "channel": 1-11, + * "timeout": seconds + * }, + * "sniffPwn": { + * "timeout": seconds + * }, + * "beaconList": { + * "ssids": [ + * "SSID 1", + * "SSID 2", + * "SSID 3" + * ], + * "generate": number of random SSIDs that will be generated, + * "timeout": seconds + * } + * "beaconAp": { + * "timeout": seconds + * } + * "exec": { + * "command": Command (eg: "clearlist -a") + * } + * "delay": { + * "timeout": seconds + * } + * } + * } + * + * Note: It is possible to inform "stages" as an array, allowing ordering and repetition of stages of the same type: + * "stages": [ + * { + * "beaconList": { "ssids": ["SSID 1", "SSID 2"] } + * }, + * { + * "beaconList": { "generate": 4 } + * }, + * ] + * ---------------------------------------------------------------------------------------------------- + */ + +#pragma once + +#include +#include "cJSON.h" + +#define WIFI_MARAUDER_DEFAULT_TIMEOUT_SCAN 15 +#define WIFI_MARAUDER_DEFAULT_TIMEOUT_DEAUTH 30 +#define WIFI_MARAUDER_DEFAULT_TIMEOUT_PROBE 60 +#define WIFI_MARAUDER_DEFAULT_TIMEOUT_SNIFF 60 +#define WIFI_MARAUDER_DEFAULT_TIMEOUT_BEACON 60 + +typedef enum { + WifiMarauderScriptBooleanFalse = 0, + WifiMarauderScriptBooleanTrue = 1, + WifiMarauderScriptBooleanUndefined = 2 +} WifiMarauderScriptBoolean; + +typedef enum { + WifiMarauderScriptStageTypeScan, + WifiMarauderScriptStageTypeSelect, + WifiMarauderScriptStageTypeDeauth, + WifiMarauderScriptStageTypeProbe, + WifiMarauderScriptStageTypeSniffRaw, + WifiMarauderScriptStageTypeSniffBeacon, + WifiMarauderScriptStageTypeSniffDeauth, + WifiMarauderScriptStageTypeSniffEsp, + WifiMarauderScriptStageTypeSniffPmkid, + WifiMarauderScriptStageTypeSniffPwn, + WifiMarauderScriptStageTypeBeaconList, + WifiMarauderScriptStageTypeBeaconAp, + WifiMarauderScriptStageTypeExec, + WifiMarauderScriptStageTypeDelay, +} WifiMarauderScriptStageType; + +typedef enum { + WifiMarauderScriptScanTypeAp = 0, + WifiMarauderScriptScanTypeStation = 1 +} WifiMarauderScriptScanType; + +typedef enum { + WifiMarauderScriptSelectTypeAp, + WifiMarauderScriptSelectTypeStation, + WifiMarauderScriptSelectTypeSsid +} WifiMarauderScriptSelectType; + +// Stages +typedef struct WifiMarauderScriptStage { + WifiMarauderScriptStageType type; + void* stage; + struct WifiMarauderScriptStage* next_stage; +} WifiMarauderScriptStage; + +typedef struct WifiMarauderScriptStageScan { + WifiMarauderScriptScanType type; + int channel; + int timeout; +} WifiMarauderScriptStageScan; + +typedef struct WifiMarauderScriptStageSelect { + WifiMarauderScriptSelectType type; + char* filter; + int* indexes; + int index_count; + // TODO: Implement a feature to not select the same items in the next iteration of the script + bool allow_repeat; +} WifiMarauderScriptStageSelect; + +typedef struct WifiMarauderScriptStageDeauth { + int timeout; +} WifiMarauderScriptStageDeauth; + +typedef struct WifiMarauderScriptStageProbe { + int timeout; +} WifiMarauderScriptStageProbe; + +typedef struct WifiMarauderScriptStageSniffRaw { + int timeout; +} WifiMarauderScriptStageSniffRaw; + +typedef struct WifiMarauderScriptStageSniffBeacon { + int timeout; +} WifiMarauderScriptStageSniffBeacon; + +typedef struct WifiMarauderScriptStageSniffDeauth { + int timeout; +} WifiMarauderScriptStageSniffDeauth; + +typedef struct WifiMarauderScriptStageSniffEsp { + int timeout; +} WifiMarauderScriptStageSniffEsp; + +typedef struct WifiMarauderScriptStageSniffPmkid { + bool force_deauth; + int channel; + int timeout; +} WifiMarauderScriptStageSniffPmkid; + +typedef struct WifiMarauderScriptStageSniffPwn { + int timeout; +} WifiMarauderScriptStageSniffPwn; + +typedef struct WifiMarauderScriptStageBeaconList { + char** ssids; + int ssid_count; + int random_ssids; + int timeout; +} WifiMarauderScriptStageBeaconList; + +typedef struct WifiMarauderScriptStageBeaconAp { + int timeout; +} WifiMarauderScriptStageBeaconAp; + +typedef struct WifiMarauderScriptStageExec { + char* command; +} WifiMarauderScriptStageExec; + +typedef struct WifiMarauderScriptStageDelay { + int timeout; +} WifiMarauderScriptStageDelay; + +// Script +typedef struct WifiMarauderScript { + char* name; + char* description; + WifiMarauderScriptStage* first_stage; + WifiMarauderScriptStage* last_stage; + WifiMarauderScriptBoolean enable_led; + WifiMarauderScriptBoolean save_pcap; + int repeat; +} WifiMarauderScript; + +typedef struct WifiMarauderScriptStageListItem { + char* value; + struct WifiMarauderScriptStageListItem* next_item; +} WifiMarauderScriptStageListItem; + +WifiMarauderScript* wifi_marauder_script_alloc(); +WifiMarauderScript* wifi_marauder_script_create(const char* script_name); +WifiMarauderScript* wifi_marauder_script_parse_raw(const char* script_raw); +WifiMarauderScript* wifi_marauder_script_parse_json(Storage* storage, const char* file_path); +void wifi_marauder_script_save_json( + Storage* storage, + const char* file_path, + WifiMarauderScript* script); +void wifi_marauder_script_add_stage( + WifiMarauderScript* script, + WifiMarauderScriptStageType stage_type, + void* stage_data); +bool wifi_marauder_script_has_stage( + WifiMarauderScript* script, + WifiMarauderScriptStageType stage_type); +void wifi_marauder_script_free(WifiMarauderScript* script); diff --git a/applications/external/wifi_marauder_companion/script/wifi_marauder_script_executor.c b/applications/external/wifi_marauder_companion/script/wifi_marauder_script_executor.c new file mode 100644 index 000000000..d7799c300 --- /dev/null +++ b/applications/external/wifi_marauder_companion/script/wifi_marauder_script_executor.c @@ -0,0 +1,307 @@ +#include "../wifi_marauder_app_i.h" +#include "wifi_marauder_script_executor.h" + +void _wifi_marauder_script_delay(WifiMarauderScriptWorker* worker, uint32_t delay_secs) { + for(uint32_t i = 0; i < delay_secs && worker->is_running; i++) furi_delay_ms(1000); +} + +void _send_stop() { + const char stop_command[] = "stopscan\n"; + wifi_marauder_uart_tx((uint8_t*)(stop_command), strlen(stop_command)); +} + +void _send_line_break() { + wifi_marauder_uart_tx((uint8_t*)("\n"), 1); +} + +void _send_channel_select(int channel) { + char command[30]; + wifi_marauder_uart_tx((uint8_t*)("\n"), 1); + snprintf(command, sizeof(command), "channel -s %d\n", channel); + wifi_marauder_uart_tx((uint8_t*)(command), strlen(command)); +} + +void _wifi_marauder_script_execute_scan( + WifiMarauderScriptStageScan* stage, + WifiMarauderScriptWorker* worker) { + char command[15]; + // Set channel + if(stage->channel > 0) { + _send_channel_select(stage->channel); + } + // Start scan + if(stage->type == WifiMarauderScriptScanTypeAp) { + snprintf(command, sizeof(command), "scanap\n"); + } else { + snprintf(command, sizeof(command), "scansta\n"); + } + wifi_marauder_uart_tx((uint8_t*)(command), strlen(command)); + _wifi_marauder_script_delay(worker, stage->timeout); + _send_stop(); +} + +void _wifi_marauder_script_execute_select(WifiMarauderScriptStageSelect* stage) { + const char* select_type = NULL; + switch(stage->type) { + case WifiMarauderScriptSelectTypeAp: + select_type = "-a"; + break; + case WifiMarauderScriptSelectTypeStation: + select_type = "-c"; + break; + case WifiMarauderScriptSelectTypeSsid: + select_type = "-s"; + break; + default: + return; // invalid stage + } + + char command[256]; + size_t command_length = 0; + + if(stage->indexes != NULL && stage->index_count > 0) { + command_length = snprintf(command, sizeof(command), "select %s ", select_type); + + for(int i = 0; i < stage->index_count; i++) { + int index = stage->indexes[i]; + command_length += snprintf( + command + command_length, sizeof(command) - command_length, "%d, ", index); + } + + // Remove the trailing comma and space + command_length -= 2; + command[command_length] = '\n'; + command_length++; + } else if(stage->filter == NULL || strcmp(stage->filter, "all") == 0) { + command_length = snprintf(command, sizeof(command), "select %s all\n", select_type); + } else { + command_length = snprintf( + command, sizeof(command), "select %s -f \"%s\"\n", select_type, stage->filter); + } + + wifi_marauder_uart_tx((uint8_t*)command, command_length); +} + +void _wifi_marauder_script_execute_deauth( + WifiMarauderScriptStageDeauth* stage, + WifiMarauderScriptWorker* worker) { + const char attack_command[] = "attack -t deauth\n"; + wifi_marauder_uart_tx((uint8_t*)(attack_command), strlen(attack_command)); + _wifi_marauder_script_delay(worker, stage->timeout); + _send_stop(); +} + +void _wifi_marauder_script_execute_probe( + WifiMarauderScriptStageProbe* stage, + WifiMarauderScriptWorker* worker) { + const char attack_command[] = "attack -t probe\n"; + wifi_marauder_uart_tx((uint8_t*)(attack_command), strlen(attack_command)); + _wifi_marauder_script_delay(worker, stage->timeout); + _send_stop(); +} + +void _wifi_marauder_script_execute_sniff_raw( + WifiMarauderScriptStageSniffRaw* stage, + WifiMarauderScriptWorker* worker) { + const char sniff_command[] = "sniffraw\n"; + wifi_marauder_uart_tx((uint8_t*)sniff_command, strlen(sniff_command)); + _wifi_marauder_script_delay(worker, stage->timeout); + _send_stop(); +} + +void _wifi_marauder_script_execute_sniff_beacon( + WifiMarauderScriptStageSniffBeacon* stage, + WifiMarauderScriptWorker* worker) { + const char sniff_command[] = "sniffbeacon\n"; + wifi_marauder_uart_tx((uint8_t*)sniff_command, strlen(sniff_command)); + _wifi_marauder_script_delay(worker, stage->timeout); + _send_stop(); +} + +void _wifi_marauder_script_execute_sniff_deauth( + WifiMarauderScriptStageSniffDeauth* stage, + WifiMarauderScriptWorker* worker) { + const char sniff_command[] = "sniffdeauth\n"; + wifi_marauder_uart_tx((uint8_t*)sniff_command, strlen(sniff_command)); + _wifi_marauder_script_delay(worker, stage->timeout); + _send_stop(); +} + +void _wifi_marauder_script_execute_sniff_esp( + WifiMarauderScriptStageSniffEsp* stage, + WifiMarauderScriptWorker* worker) { + const char sniff_command[] = "sniffesp\n"; + wifi_marauder_uart_tx((uint8_t*)sniff_command, strlen(sniff_command)); + _wifi_marauder_script_delay(worker, stage->timeout); + _send_stop(); +} + +void _wifi_marauder_script_execute_sniff_pmkid( + WifiMarauderScriptStageSniffPmkid* stage, + WifiMarauderScriptWorker* worker) { + char attack_command[50] = "sniffpmkid"; + int len = strlen(attack_command); + + if(stage->channel > 0) { + len += + snprintf(attack_command + len, sizeof(attack_command) - len, " -c %d", stage->channel); + } + + if(stage->force_deauth) { + len += snprintf(attack_command + len, sizeof(attack_command) - len, " -d"); + } + + len += snprintf(attack_command + len, sizeof(attack_command) - len, "\n"); + + wifi_marauder_uart_tx((uint8_t*)attack_command, len); + _wifi_marauder_script_delay(worker, stage->timeout); + _send_stop(); +} + +void _wifi_marauder_script_execute_sniff_pwn( + WifiMarauderScriptStageSniffPwn* stage, + WifiMarauderScriptWorker* worker) { + const char sniff_command[] = "sniffpwn\n"; + wifi_marauder_uart_tx((uint8_t*)sniff_command, strlen(sniff_command)); + _wifi_marauder_script_delay(worker, stage->timeout); + _send_stop(); +} + +void _wifi_marauder_script_execute_beacon_list( + WifiMarauderScriptStageBeaconList* stage, + WifiMarauderScriptWorker* worker) { + const char clearlist_command[] = "clearlist -s\n"; + wifi_marauder_uart_tx((uint8_t*)(clearlist_command), strlen(clearlist_command)); + + char command[100]; + char* ssid; + + for(int i = 0; i < stage->ssid_count; i++) { + ssid = stage->ssids[i]; + snprintf(command, sizeof(command), "ssid -a -n \"%s\"", ssid); + wifi_marauder_uart_tx((uint8_t*)(command), strlen(command)); + _send_line_break(); + } + if(stage->random_ssids > 0) { + char add_random_command[50]; + snprintf( + add_random_command, + sizeof(add_random_command), + "ssid -a -r -g %d\n", + stage->random_ssids); + wifi_marauder_uart_tx((uint8_t*)add_random_command, strlen(add_random_command)); + } + const char attack_command[] = "attack -t beacon -l\n"; + wifi_marauder_uart_tx((uint8_t*)(attack_command), strlen(attack_command)); + _wifi_marauder_script_delay(worker, stage->timeout); + _send_stop(); +} + +void _wifi_marauder_script_execute_beacon_ap( + WifiMarauderScriptStageBeaconAp* stage, + WifiMarauderScriptWorker* worker) { + const char command[] = "attack -t beacon -a\n"; + wifi_marauder_uart_tx((uint8_t*)command, strlen(command)); + _wifi_marauder_script_delay(worker, stage->timeout); + _send_stop(); +} + +void _wifi_marauder_script_execute_exec(WifiMarauderScriptStageExec* stage) { + if(stage->command != NULL) { + wifi_marauder_uart_tx((uint8_t*)stage->command, strlen(stage->command)); + } +} + +void _wifi_marauder_script_execute_delay( + WifiMarauderScriptStageDelay* stage, + WifiMarauderScriptWorker* worker) { + _wifi_marauder_script_delay(worker, stage->timeout); +} + +void wifi_marauder_script_execute_start(void* context) { + furi_assert(context); + WifiMarauderScriptWorker* worker = context; + WifiMarauderScript* script = worker->script; + char command[100]; + + // Enables or disables the LED according to script settings + if(script->enable_led != WifiMarauderScriptBooleanUndefined) { + snprintf( + command, + sizeof(command), + "settings -s EnableLED %s", + script->enable_led ? "enable" : "disable"); + wifi_marauder_uart_tx((uint8_t*)command, strlen(command)); + _send_line_break(); + } + + // Enables or disables PCAP saving according to script settings + if(script->save_pcap != WifiMarauderScriptBooleanUndefined) { + snprintf( + command, + sizeof(command), + "settings -s SavePCAP %s", + script->save_pcap ? "enable" : "disable"); + wifi_marauder_uart_tx((uint8_t*)command, strlen(command)); + _send_line_break(); + } +} + +void wifi_marauder_script_execute_stage(WifiMarauderScriptStage* stage, void* context) { + furi_assert(context); + WifiMarauderScriptWorker* worker = context; + void* stage_data = stage->stage; + + switch(stage->type) { + case WifiMarauderScriptStageTypeScan: + _wifi_marauder_script_execute_scan((WifiMarauderScriptStageScan*)stage_data, worker); + break; + case WifiMarauderScriptStageTypeSelect: + _wifi_marauder_script_execute_select((WifiMarauderScriptStageSelect*)stage_data); + break; + case WifiMarauderScriptStageTypeDeauth: + _wifi_marauder_script_execute_deauth((WifiMarauderScriptStageDeauth*)stage_data, worker); + break; + case WifiMarauderScriptStageTypeProbe: + _wifi_marauder_script_execute_probe((WifiMarauderScriptStageProbe*)stage_data, worker); + break; + case WifiMarauderScriptStageTypeSniffRaw: + _wifi_marauder_script_execute_sniff_raw( + (WifiMarauderScriptStageSniffRaw*)stage_data, worker); + break; + case WifiMarauderScriptStageTypeSniffBeacon: + _wifi_marauder_script_execute_sniff_beacon( + (WifiMarauderScriptStageSniffBeacon*)stage_data, worker); + break; + case WifiMarauderScriptStageTypeSniffDeauth: + _wifi_marauder_script_execute_sniff_deauth( + (WifiMarauderScriptStageSniffDeauth*)stage_data, worker); + break; + case WifiMarauderScriptStageTypeSniffEsp: + _wifi_marauder_script_execute_sniff_esp( + (WifiMarauderScriptStageSniffEsp*)stage_data, worker); + break; + case WifiMarauderScriptStageTypeSniffPmkid: + _wifi_marauder_script_execute_sniff_pmkid( + (WifiMarauderScriptStageSniffPmkid*)stage_data, worker); + break; + case WifiMarauderScriptStageTypeSniffPwn: + _wifi_marauder_script_execute_sniff_pwn( + (WifiMarauderScriptStageSniffPwn*)stage_data, worker); + break; + case WifiMarauderScriptStageTypeBeaconList: + _wifi_marauder_script_execute_beacon_list( + (WifiMarauderScriptStageBeaconList*)stage_data, worker); + break; + case WifiMarauderScriptStageTypeBeaconAp: + _wifi_marauder_script_execute_beacon_ap( + (WifiMarauderScriptStageBeaconAp*)stage_data, worker); + break; + case WifiMarauderScriptStageTypeExec: + _wifi_marauder_script_execute_exec((WifiMarauderScriptStageExec*)stage_data); + break; + case WifiMarauderScriptStageTypeDelay: + _wifi_marauder_script_execute_delay((WifiMarauderScriptStageDelay*)stage_data, worker); + break; + } +} \ No newline at end of file diff --git a/applications/external/wifi_marauder_companion/script/wifi_marauder_script_executor.h b/applications/external/wifi_marauder_companion/script/wifi_marauder_script_executor.h new file mode 100644 index 000000000..654712849 --- /dev/null +++ b/applications/external/wifi_marauder_companion/script/wifi_marauder_script_executor.h @@ -0,0 +1,6 @@ +#pragma once + +#include "wifi_marauder_script.h" + +void wifi_marauder_script_execute_start(void* context); +void wifi_marauder_script_execute_stage(WifiMarauderScriptStage* stage, void* context); diff --git a/applications/external/wifi_marauder_companion/script/wifi_marauder_script_worker.c b/applications/external/wifi_marauder_companion/script/wifi_marauder_script_worker.c new file mode 100644 index 000000000..45c5b56ba --- /dev/null +++ b/applications/external/wifi_marauder_companion/script/wifi_marauder_script_worker.c @@ -0,0 +1,74 @@ +#include "../wifi_marauder_app_i.h" +#include "wifi_marauder_script_worker.h" + +WifiMarauderScriptWorker* wifi_marauder_script_worker_alloc() { + WifiMarauderScriptWorker* worker = malloc(sizeof(WifiMarauderScriptWorker)); + if(worker == NULL) { + return NULL; + } + worker->callback_start = NULL; + worker->callback_stage = NULL; + worker->worker_thread = NULL; + worker->is_running = false; + return worker; +} + +int32_t _wifi_marauder_script_worker_task(void* worker) { + WifiMarauderScriptWorker* script_worker = worker; + WifiMarauderScript* script = script_worker->script; + if(script == NULL) { + return WifiMarauderScriptWorkerStatusInvalidScript; + } + + // Setup + script_worker->callback_start(script_worker->context); + if(!script_worker->is_running) { + return WifiMarauderScriptWorkerStatusForceExit; + } + + // Stages + for(int i = 0; i < script->repeat; i++) { + WifiMarauderScriptStage* current_stage = script->first_stage; + while(current_stage != NULL && script_worker->is_running) { + script_worker->callback_stage(current_stage, script_worker->context); + current_stage = current_stage->next_stage; + } + if(!script_worker->is_running) { + return WifiMarauderScriptWorkerStatusForceExit; + } + } + + script_worker->is_running = false; + return WifiMarauderScriptWorkerStatusSuccess; +} + +bool wifi_marauder_script_worker_start( + WifiMarauderScriptWorker* instance, + WifiMarauderScript* script) { + if(!instance || !script) { + return false; + } + instance->callback_start = wifi_marauder_script_execute_start; + instance->callback_stage = wifi_marauder_script_execute_stage; + instance->script = script; + instance->context = instance; + instance->is_running = true; + instance->worker_thread = furi_thread_alloc_ex( + "WifiMarauderScriptWorker", 1024, _wifi_marauder_script_worker_task, instance); + if(!instance->worker_thread) { + return false; + } + furi_thread_start(instance->worker_thread); + return true; +} + +void wifi_marauder_script_worker_free(WifiMarauderScriptWorker* worker) { + if(worker != NULL) { + if(worker->worker_thread != NULL) { + worker->is_running = false; + furi_thread_join(worker->worker_thread); + furi_thread_free(worker->worker_thread); + } + free(worker); + } +} \ No newline at end of file diff --git a/applications/external/wifi_marauder_companion/script/wifi_marauder_script_worker.h b/applications/external/wifi_marauder_companion/script/wifi_marauder_script_worker.h new file mode 100644 index 000000000..76ff070d2 --- /dev/null +++ b/applications/external/wifi_marauder_companion/script/wifi_marauder_script_worker.h @@ -0,0 +1,43 @@ +#pragma once + +#include "wifi_marauder_script.h" + +typedef enum { + WifiMarauderScriptWorkerStatusSuccess = 0, + WifiMarauderScriptWorkerStatusInvalidScript = 1, + WifiMarauderScriptWorkerStatusForceExit = 2 +} WifiMarauderScriptWorkerStatus; + +typedef struct WifiMarauderScriptWorker { + WifiMarauderScript* script; + FuriThread* worker_thread; + void (*callback_start)(void*); + void (*callback_stage)(WifiMarauderScriptStage*, void*); + void* context; + bool is_running; +} WifiMarauderScriptWorker; + +/** + * @brief Allocates a new instance of WifiMarauderScriptWorker. + * + * @return A pointer to the allocated instance or NULL if allocation fails. + */ +WifiMarauderScriptWorker* wifi_marauder_script_worker_alloc(); + +/** + * @brief Starts the execution of the worker and sets the callback function to be called after each stage is executed. + * + * @param instance A pointer to the instance of WifiMarauderScriptWorker to start. + * @param script Script to be executed + * @return True if the worker was successfully started, false otherwise. + */ +bool wifi_marauder_script_worker_start( + WifiMarauderScriptWorker* instance, + WifiMarauderScript* script); + +/** + * @brief Frees the memory used by the instance of WifiMarauderScriptWorker. + * + * @param script A pointer to the instance of WifiMarauderScriptWorker to free. + */ +void wifi_marauder_script_worker_free(WifiMarauderScriptWorker* script); diff --git a/applications/external/wifi_marauder_companion/wifi_marauder_app.c b/applications/external/wifi_marauder_companion/wifi_marauder_app.c index 42e94a8b8..821987a8a 100644 --- a/applications/external/wifi_marauder_companion/wifi_marauder_app.c +++ b/applications/external/wifi_marauder_companion/wifi_marauder_app.c @@ -66,9 +66,11 @@ WifiMarauderApp* wifi_marauder_app_alloc() { app->text_box_store = furi_string_alloc(); furi_string_reserve(app->text_box_store, WIFI_MARAUDER_TEXT_BOX_STORE_SIZE); - app->text_input = text_input_alloc(); + app->text_input = wifi_text_input_alloc(); view_dispatcher_add_view( - app->view_dispatcher, WifiMarauderAppViewTextInput, text_input_get_view(app->text_input)); + app->view_dispatcher, + WifiMarauderAppViewTextInput, + wifi_text_input_get_view(app->text_input)); app->widget = widget_alloc(); view_dispatcher_add_view( @@ -81,6 +83,11 @@ WifiMarauderApp* wifi_marauder_app_alloc() { (!storage_file_exists(app->storage, SAVE_PCAP_SETTING_FILEPATH) || !storage_file_exists(app->storage, SAVE_LOGS_SETTING_FILEPATH)); + // Submenu + app->submenu = submenu_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, WifiMarauderAppViewSubmenu, submenu_get_view(app->submenu)); + scene_manager_next_scene(app->scene_manager, WifiMarauderSceneStart); return app; @@ -100,6 +107,10 @@ void wifi_marauder_make_app_folder(WifiMarauderApp* app) { if(!storage_simply_mkdir(app->storage, MARAUDER_APP_FOLDER_LOGS)) { dialog_message_show_storage_error(app->dialogs, "Cannot create\npcaps folder"); } + + if(!storage_simply_mkdir(app->storage, MARAUDER_APP_FOLDER_SCRIPTS)) { + dialog_message_show_storage_error(app->dialogs, "Cannot create\nscripts folder"); + } } void wifi_marauder_load_settings(WifiMarauderApp* app) { @@ -134,10 +145,14 @@ void wifi_marauder_app_free(WifiMarauderApp* app) { view_dispatcher_remove_view(app->view_dispatcher, WifiMarauderAppViewConsoleOutput); view_dispatcher_remove_view(app->view_dispatcher, WifiMarauderAppViewTextInput); view_dispatcher_remove_view(app->view_dispatcher, WifiMarauderAppViewWidget); + view_dispatcher_remove_view(app->view_dispatcher, WifiMarauderAppViewSubmenu); + widget_free(app->widget); text_box_free(app->text_box); furi_string_free(app->text_box_store); - text_input_free(app->text_input); + wifi_text_input_free(app->text_input); + submenu_free(app->submenu); + variable_item_list_free(app->var_item_list); storage_file_free(app->capture_file); storage_file_free(app->log_file); storage_file_free(app->save_pcap_setting_file); 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 39b48fae3..2a16522bb 100644 --- a/applications/external/wifi_marauder_companion/wifi_marauder_app_i.h +++ b/applications/external/wifi_marauder_companion/wifi_marauder_app_i.h @@ -6,20 +6,27 @@ #include "scenes/wifi_marauder_scene.h" #include "wifi_marauder_custom_event.h" #include "wifi_marauder_uart.h" -#include "wifi_marauder_pcap.h" +#include "file/sequential_file.h" +#include "script/wifi_marauder_script.h" +#include "script/wifi_marauder_script_worker.h" +#include "script/wifi_marauder_script_executor.h" +#include "script/menu/wifi_marauder_script_stage_menu.h" #include #include #include #include -#include +#include #include #include +#include "wifi_marauder_text_input.h" +#include #include +#include #include -#define NUM_MENU_ITEMS (17) +#define NUM_MENU_ITEMS (18) #define WIFI_MARAUDER_TEXT_BOX_STORE_SIZE (4096) #define WIFI_MARAUDER_TEXT_INPUT_STORE_SIZE (512) @@ -30,9 +37,17 @@ #define MARAUDER_APP_FOLDER_LOGS MARAUDER_APP_FOLDER "/logs" #define MARAUDER_APP_FOLDER_USER_PCAPS MARAUDER_APP_FOLDER_USER "/pcaps" #define MARAUDER_APP_FOLDER_USER_LOGS MARAUDER_APP_FOLDER_USER "/logs" +#define MARAUDER_APP_FOLDER_SCRIPTS MARAUDER_APP_FOLDER "/scripts" +#define MARAUDER_APP_SCRIPT_PATH(file_name) MARAUDER_APP_FOLDER_SCRIPTS "/" file_name ".json" #define SAVE_PCAP_SETTING_FILEPATH MARAUDER_APP_FOLDER "/save_pcaps_here.setting" #define SAVE_LOGS_SETTING_FILEPATH MARAUDER_APP_FOLDER "/save_logs_here.setting" +typedef enum WifiMarauderUserInputType { + WifiMarauderUserInputTypeString, + WifiMarauderUserInputTypeNumber, + WifiMarauderUserInputTypeFileName +} WifiMarauderUserInputType; + struct WifiMarauderApp { Gui* gui; ViewDispatcher* view_dispatcher; @@ -42,7 +57,7 @@ struct WifiMarauderApp { FuriString* text_box_store; size_t text_box_store_strlen; TextBox* text_box; - TextInput* text_input; + WIFI_TextInput* text_input; Storage* storage; File* capture_file; File* log_file; @@ -58,6 +73,7 @@ struct WifiMarauderApp { VariableItemList* var_item_list; Widget* widget; + Submenu* submenu; int open_log_file_page; int open_log_file_num_pages; @@ -73,6 +89,26 @@ struct WifiMarauderApp { bool is_writing_pcap; bool is_writing_log; + // User input + WifiMarauderUserInputType user_input_type; + char** user_input_string_reference; + int* user_input_number_reference; + char* user_input_file_dir; + char* user_input_file_extension; + + // Automation script + WifiMarauderScript* script; + WifiMarauderScriptWorker* script_worker; + FuriString** script_list; + int script_list_count; + WifiMarauderScriptStage* script_edit_selected_stage; + WifiMarauderScriptStageMenu* script_stage_menu; + WifiMarauderScriptStageListItem* script_stage_edit_first_item; + char*** script_stage_edit_strings_reference; + int* script_stage_edit_string_count_reference; + int** script_stage_edit_numbers_reference; + int* script_stage_edit_number_count_reference; + // For input source and destination MAC in targeted deauth attack int special_case_input_step; char special_case_input_src_addr[20]; @@ -105,4 +141,5 @@ typedef enum { WifiMarauderAppViewConsoleOutput, WifiMarauderAppViewTextInput, WifiMarauderAppViewWidget, + WifiMarauderAppViewSubmenu, } WifiMarauderAppView; diff --git a/applications/external/wifi_marauder_companion/wifi_marauder_custom_event.h b/applications/external/wifi_marauder_companion/wifi_marauder_custom_event.h index 79f96b107..5acdfa38e 100644 --- a/applications/external/wifi_marauder_companion/wifi_marauder_custom_event.h +++ b/applications/external/wifi_marauder_companion/wifi_marauder_custom_event.h @@ -7,5 +7,6 @@ typedef enum { WifiMarauderEventSaveSourceMac, WifiMarauderEventSaveDestinationMac, WifiMarauderEventStartSettingsInit, - WifiMarauderEventStartLogViewer + WifiMarauderEventStartLogViewer, + WifiMarauderEventStartScriptSelect } WifiMarauderCustomEvent; diff --git a/applications/external/wifi_marauder_companion/wifi_marauder_text_input.c b/applications/external/wifi_marauder_companion/wifi_marauder_text_input.c new file mode 100644 index 000000000..e17e5aaee --- /dev/null +++ b/applications/external/wifi_marauder_companion/wifi_marauder_text_input.c @@ -0,0 +1,616 @@ +#include "wifi_marauder_text_input.h" +#include +#include "ESP32_WiFi_Marauder_icons.h" +#include "wifi_marauder_app_i.h" +#include + +struct WIFI_TextInput { + View* view; + FuriTimer* timer; +}; + +typedef struct { + const char text; + const uint8_t x; + const uint8_t y; +} WIFI_TextInputKey; + +typedef struct { + const char* header; + char* text_buffer; + size_t text_buffer_size; + bool clear_default_text; + + WIFI_TextInputCallback callback; + void* callback_context; + + uint8_t selected_row; + uint8_t selected_column; + + WIFI_TextInputValidatorCallback validator_callback; + void* validator_callback_context; + FuriString* validator_text; + bool valadator_message_visible; +} WIFI_TextInputModel; + +static const uint8_t keyboard_origin_x = 1; +static const uint8_t keyboard_origin_y = 29; +static const uint8_t keyboard_row_count = 4; + +#define ENTER_KEY '\r' +#define BACKSPACE_KEY '\b' + +static const WIFI_TextInputKey keyboard_keys_row_1[] = { + {'{', 1, 0}, + {'(', 9, 0}, + {'[', 17, 0}, + {'|', 25, 0}, + {'@', 33, 0}, + {'&', 41, 0}, + {'#', 49, 0}, + {';', 57, 0}, + {'^', 65, 0}, + {'*', 73, 0}, + {'`', 81, 0}, + {'"', 89, 0}, + {'~', 97, 0}, + {'\'', 105, 0}, + {'.', 113, 0}, + {'/', 120, 0}, +}; + +static const WIFI_TextInputKey keyboard_keys_row_2[] = { + {'q', 1, 10}, + {'w', 9, 10}, + {'e', 17, 10}, + {'r', 25, 10}, + {'t', 33, 10}, + {'y', 41, 10}, + {'u', 49, 10}, + {'i', 57, 10}, + {'o', 65, 10}, + {'p', 73, 10}, + {'0', 81, 10}, + {'1', 89, 10}, + {'2', 97, 10}, + {'3', 105, 10}, + {'=', 113, 10}, + {'-', 120, 10}, +}; + +static const WIFI_TextInputKey keyboard_keys_row_3[] = { + {'a', 1, 21}, + {'s', 9, 21}, + {'d', 18, 21}, + {'f', 25, 21}, + {'g', 33, 21}, + {'h', 41, 21}, + {'j', 49, 21}, + {'k', 57, 21}, + {'l', 65, 21}, + {BACKSPACE_KEY, 72, 13}, + {'4', 89, 21}, + {'5', 97, 21}, + {'6', 105, 21}, + {'$', 113, 21}, + {'%', 120, 21}, + +}; + +static const WIFI_TextInputKey keyboard_keys_row_4[] = { + {'z', 1, 33}, + {'x', 9, 33}, + {'c', 18, 33}, + {'v', 25, 33}, + {'b', 33, 33}, + {'n', 41, 33}, + {'m', 49, 33}, + {'_', 57, 33}, + {ENTER_KEY, 64, 24}, + {'7', 89, 33}, + {'8', 97, 33}, + {'9', 105, 33}, + {'!', 113, 33}, + {'+', 120, 33}, +}; + +static uint8_t get_row_size(uint8_t row_index) { + uint8_t row_size = 0; + + switch(row_index + 1) { + case 1: + row_size = sizeof(keyboard_keys_row_1) / sizeof(WIFI_TextInputKey); + break; + case 2: + row_size = sizeof(keyboard_keys_row_2) / sizeof(WIFI_TextInputKey); + break; + case 3: + row_size = sizeof(keyboard_keys_row_3) / sizeof(WIFI_TextInputKey); + break; + case 4: + row_size = sizeof(keyboard_keys_row_4) / sizeof(WIFI_TextInputKey); + break; + } + + return row_size; +} + +static const WIFI_TextInputKey* get_row(uint8_t row_index) { + const WIFI_TextInputKey* row = NULL; + + switch(row_index + 1) { + case 1: + row = keyboard_keys_row_1; + break; + case 2: + row = keyboard_keys_row_2; + break; + case 3: + row = keyboard_keys_row_3; + break; + case 4: + row = keyboard_keys_row_4; + break; + } + + return row; +} + +static char get_selected_char(WIFI_TextInputModel* model) { + return get_row(model->selected_row)[model->selected_column].text; +} + +static bool char_is_lowercase(char letter) { + return (letter >= 0x61 && letter <= 0x7A); +} + +static char char_to_uppercase(const char letter) { + switch(letter) { + case '_': + return 0x20; + case '(': + return 0x29; + case '{': + return 0x7d; + case '[': + return 0x5d; + case '/': + return 0x5c; + case ';': + return 0x3a; + case '.': + return 0x2c; + case '!': + return 0x3f; + case '<': + return 0x3e; + } + if(char_is_lowercase(letter)) { + return (letter - 0x20); + } else { + return letter; + } +} + +static void wifi_text_input_backspace_cb(WIFI_TextInputModel* model) { + uint8_t text_length = model->clear_default_text ? 1 : strlen(model->text_buffer); + if(text_length > 0) { + model->text_buffer[text_length - 1] = 0; + } +} + +static void wifi_text_input_view_draw_callback(Canvas* canvas, void* _model) { + WIFI_TextInputModel* model = _model; + //uint8_t text_length = model->text_buffer ? strlen(model->text_buffer) : 0; + uint8_t needed_string_width = canvas_width(canvas) - 8; + uint8_t start_pos = 4; + + const char* text = model->text_buffer; + + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + + canvas_draw_str(canvas, 2, 7, model->header); + elements_slightly_rounded_frame(canvas, 1, 8, 126, 12); + + if(canvas_string_width(canvas, text) > needed_string_width) { + canvas_draw_str(canvas, start_pos, 17, "..."); + start_pos += 6; + needed_string_width -= 8; + } + + while(text != 0 && canvas_string_width(canvas, text) > needed_string_width) { + text++; + } + + if(model->clear_default_text) { + elements_slightly_rounded_box( + canvas, start_pos - 1, 14, canvas_string_width(canvas, text) + 2, 10); + canvas_set_color(canvas, ColorWhite); + } else { + canvas_draw_str(canvas, start_pos + canvas_string_width(canvas, text) + 1, 18, "|"); + canvas_draw_str(canvas, start_pos + canvas_string_width(canvas, text) + 2, 18, "|"); + } + canvas_draw_str(canvas, start_pos, 17, text); + + canvas_set_font(canvas, FontKeyboard); + + for(uint8_t row = 0; row <= keyboard_row_count; row++) { + const uint8_t column_count = get_row_size(row); + const WIFI_TextInputKey* keys = get_row(row); + + for(size_t column = 0; column < column_count; column++) { + if(keys[column].text == ENTER_KEY) { + 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].text == BACKSPACE_KEY) { + canvas_set_color(canvas, ColorBlack); + if(model->selected_row == row && model->selected_column == column) { + canvas_draw_icon( + canvas, + keyboard_origin_x + keys[column].x, + keyboard_origin_y + keys[column].y, + &I_KeyBackspaceSelected_16x9); + } else { + canvas_draw_icon( + canvas, + keyboard_origin_x + keys[column].x, + keyboard_origin_y + keys[column].y, + &I_KeyBackspace_16x9); + } + } else { + if(model->selected_row == row && model->selected_column == column) { + canvas_set_color(canvas, ColorBlack); + canvas_draw_box( + canvas, + keyboard_origin_x + keys[column].x - 1, + keyboard_origin_y + keys[column].y - 8, + 7, + 10); + canvas_set_color(canvas, ColorWhite); + } else { + canvas_set_color(canvas, ColorBlack); + } + + canvas_draw_glyph( + canvas, + keyboard_origin_x + keys[column].x, + keyboard_origin_y + keys[column].y, + keys[column].text); + } + } + } + if(model->valadator_message_visible) { + canvas_set_font(canvas, FontSecondary); + canvas_set_color(canvas, ColorWhite); + canvas_draw_box(canvas, 8, 10, 110, 48); + canvas_set_color(canvas, ColorBlack); + canvas_draw_icon(canvas, 10, 14, &I_WarningDolphin_45x42); + canvas_draw_rframe(canvas, 8, 8, 112, 50, 3); + canvas_draw_rframe(canvas, 9, 9, 110, 48, 2); + elements_multiline_text(canvas, 62, 20, furi_string_get_cstr(model->validator_text)); + canvas_set_font(canvas, FontKeyboard); + } +} + +static void + wifi_text_input_handle_up(WIFI_TextInput* wifi_text_input, WIFI_TextInputModel* model) { + UNUSED(wifi_text_input); + if(model->selected_row > 0) { + model->selected_row--; + if(model->selected_column > get_row_size(model->selected_row) - 6) { + model->selected_column = model->selected_column + 1; + } + } +} + +static void + wifi_text_input_handle_down(WIFI_TextInput* wifi_text_input, WIFI_TextInputModel* model) { + UNUSED(wifi_text_input); + if(model->selected_row < keyboard_row_count - 1) { + model->selected_row++; + if(model->selected_column > get_row_size(model->selected_row) - 4) { + model->selected_column = model->selected_column - 1; + } + } +} + +static void + wifi_text_input_handle_left(WIFI_TextInput* wifi_text_input, WIFI_TextInputModel* model) { + UNUSED(wifi_text_input); + if(model->selected_column > 0) { + model->selected_column--; + } else { + model->selected_column = get_row_size(model->selected_row) - 1; + } +} + +static void + wifi_text_input_handle_right(WIFI_TextInput* wifi_text_input, WIFI_TextInputModel* model) { + UNUSED(wifi_text_input); + if(model->selected_column < get_row_size(model->selected_row) - 1) { + model->selected_column++; + } else { + model->selected_column = 0; + } +} + +static void wifi_text_input_handle_ok( + WIFI_TextInput* wifi_text_input, + WIFI_TextInputModel* model, + bool shift) { + char selected = get_selected_char(model); + uint8_t text_length = strlen(model->text_buffer); + + if(shift) { + selected = char_to_uppercase(selected); + } + + if(selected == ENTER_KEY) { + if(model->validator_callback && + (!model->validator_callback( + model->text_buffer, model->validator_text, model->validator_callback_context))) { + model->valadator_message_visible = true; + furi_timer_start(wifi_text_input->timer, furi_kernel_get_tick_frequency() * 4); + } else if(model->callback != 0 && text_length > 0) { + model->callback(model->callback_context); + } + } else if(selected == BACKSPACE_KEY) { + wifi_text_input_backspace_cb(model); + } else { + if(model->clear_default_text) { + text_length = 0; + } + if(text_length < (model->text_buffer_size - 1)) { + model->text_buffer[text_length] = selected; + model->text_buffer[text_length + 1] = 0; + } + } + model->clear_default_text = false; +} + +static bool wifi_text_input_view_input_callback(InputEvent* event, void* context) { + WIFI_TextInput* wifi_text_input = context; + furi_assert(wifi_text_input); + + bool consumed = false; + + // Acquire model + WIFI_TextInputModel* model = view_get_model(wifi_text_input->view); + + if((!(event->type == InputTypePress) && !(event->type == InputTypeRelease)) && + model->valadator_message_visible) { + model->valadator_message_visible = false; + consumed = true; + } else if(event->type == InputTypeShort) { + consumed = true; + switch(event->key) { + case InputKeyUp: + wifi_text_input_handle_up(wifi_text_input, model); + break; + case InputKeyDown: + wifi_text_input_handle_down(wifi_text_input, model); + break; + case InputKeyLeft: + wifi_text_input_handle_left(wifi_text_input, model); + break; + case InputKeyRight: + wifi_text_input_handle_right(wifi_text_input, model); + break; + case InputKeyOk: + wifi_text_input_handle_ok(wifi_text_input, model, false); + break; + default: + consumed = false; + break; + } + } else if(event->type == InputTypeLong) { + consumed = true; + switch(event->key) { + case InputKeyUp: + wifi_text_input_handle_up(wifi_text_input, model); + break; + case InputKeyDown: + wifi_text_input_handle_down(wifi_text_input, model); + break; + case InputKeyLeft: + wifi_text_input_handle_left(wifi_text_input, model); + break; + case InputKeyRight: + wifi_text_input_handle_right(wifi_text_input, model); + break; + case InputKeyOk: + wifi_text_input_handle_ok(wifi_text_input, model, true); + break; + case InputKeyBack: + wifi_text_input_backspace_cb(model); + break; + default: + consumed = false; + break; + } + } else if(event->type == InputTypeRepeat) { + consumed = true; + switch(event->key) { + case InputKeyUp: + wifi_text_input_handle_up(wifi_text_input, model); + break; + case InputKeyDown: + wifi_text_input_handle_down(wifi_text_input, model); + break; + case InputKeyLeft: + wifi_text_input_handle_left(wifi_text_input, model); + break; + case InputKeyRight: + wifi_text_input_handle_right(wifi_text_input, model); + break; + case InputKeyBack: + wifi_text_input_backspace_cb(model); + break; + default: + consumed = false; + break; + } + } + + // Commit model + view_commit_model(wifi_text_input->view, consumed); + + return consumed; +} + +void wifi_text_input_timer_callback(void* context) { + furi_assert(context); + WIFI_TextInput* wifi_text_input = context; + + with_view_model( + wifi_text_input->view, + WIFI_TextInputModel * model, + { model->valadator_message_visible = false; }, + true); +} + +WIFI_TextInput* wifi_text_input_alloc() { + WIFI_TextInput* wifi_text_input = malloc(sizeof(WIFI_TextInput)); + wifi_text_input->view = view_alloc(); + view_set_context(wifi_text_input->view, wifi_text_input); + view_allocate_model(wifi_text_input->view, ViewModelTypeLocking, sizeof(WIFI_TextInputModel)); + view_set_draw_callback(wifi_text_input->view, wifi_text_input_view_draw_callback); + view_set_input_callback(wifi_text_input->view, wifi_text_input_view_input_callback); + + wifi_text_input->timer = + furi_timer_alloc(wifi_text_input_timer_callback, FuriTimerTypeOnce, wifi_text_input); + + with_view_model( + wifi_text_input->view, + WIFI_TextInputModel * model, + { model->validator_text = furi_string_alloc(); }, + false); + + wifi_text_input_reset(wifi_text_input); + + return wifi_text_input; +} + +void wifi_text_input_free(WIFI_TextInput* wifi_text_input) { + furi_assert(wifi_text_input); + with_view_model( + wifi_text_input->view, + WIFI_TextInputModel * model, + { furi_string_free(model->validator_text); }, + false); + + // Send stop command + furi_timer_stop(wifi_text_input->timer); + // Release allocated memory + furi_timer_free(wifi_text_input->timer); + + view_free(wifi_text_input->view); + + free(wifi_text_input); +} + +void wifi_text_input_reset(WIFI_TextInput* wifi_text_input) { + furi_assert(wifi_text_input); + with_view_model( + wifi_text_input->view, + WIFI_TextInputModel * model, + { + model->text_buffer_size = 0; + model->header = ""; + model->selected_row = 0; + model->selected_column = 0; + model->clear_default_text = false; + model->text_buffer = NULL; + model->text_buffer_size = 0; + model->callback = NULL; + model->callback_context = NULL; + model->validator_callback = NULL; + model->validator_callback_context = NULL; + furi_string_reset(model->validator_text); + model->valadator_message_visible = false; + }, + true); +} + +View* wifi_text_input_get_view(WIFI_TextInput* wifi_text_input) { + furi_assert(wifi_text_input); + return wifi_text_input->view; +} + +void wifi_text_input_set_result_callback( + WIFI_TextInput* wifi_text_input, + WIFI_TextInputCallback callback, + void* callback_context, + char* text_buffer, + size_t text_buffer_size, + bool clear_default_text) { + with_view_model( + wifi_text_input->view, + WIFI_TextInputModel * model, + { + model->callback = callback; + model->callback_context = callback_context; + model->text_buffer = text_buffer; + model->text_buffer_size = text_buffer_size; + model->clear_default_text = clear_default_text; + if(text_buffer && text_buffer[0] != '\0') { + // Set focus on Save + model->selected_row = 2; + model->selected_column = 8; + } + }, + true); +} + +void wifi_text_input_set_validator( + WIFI_TextInput* wifi_text_input, + WIFI_TextInputValidatorCallback callback, + void* callback_context) { + with_view_model( + wifi_text_input->view, + WIFI_TextInputModel * model, + { + model->validator_callback = callback; + model->validator_callback_context = callback_context; + }, + true); +} + +WIFI_TextInputValidatorCallback + wifi_text_input_get_validator_callback(WIFI_TextInput* wifi_text_input) { + WIFI_TextInputValidatorCallback validator_callback = NULL; + with_view_model( + wifi_text_input->view, + WIFI_TextInputModel * model, + { validator_callback = model->validator_callback; }, + false); + return validator_callback; +} + +void* wifi_text_input_get_validator_callback_context(WIFI_TextInput* wifi_text_input) { + void* validator_callback_context = NULL; + with_view_model( + wifi_text_input->view, + WIFI_TextInputModel * model, + { validator_callback_context = model->validator_callback_context; }, + false); + return validator_callback_context; +} + +void wifi_text_input_set_header_text(WIFI_TextInput* wifi_text_input, const char* text) { + with_view_model( + wifi_text_input->view, WIFI_TextInputModel * model, { model->header = text; }, true); +} diff --git a/applications/external/wifi_marauder_companion/wifi_marauder_text_input.h b/applications/external/wifi_marauder_companion/wifi_marauder_text_input.h new file mode 100644 index 000000000..b6b1f7bdf --- /dev/null +++ b/applications/external/wifi_marauder_companion/wifi_marauder_text_input.h @@ -0,0 +1,82 @@ +#pragma once + +#include +#include "wifi_marauder_validators.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** Text input anonymous structure */ +typedef struct WIFI_TextInput WIFI_TextInput; +typedef void (*WIFI_TextInputCallback)(void* context); +typedef bool (*WIFI_TextInputValidatorCallback)(const char* text, FuriString* error, void* context); + +/** Allocate and initialize text input + * + * This text input is used to enter string + * + * @return WIFI_TextInput instance + */ +WIFI_TextInput* wifi_text_input_alloc(); + +/** Deinitialize and free text input + * + * @param wifi_text_input WIFI_TextInput instance + */ +void wifi_text_input_free(WIFI_TextInput* wifi_text_input); + +/** Clean text input view Note: this function does not free memory + * + * @param wifi_text_input Text input instance + */ +void wifi_text_input_reset(WIFI_TextInput* wifi_text_input); + +/** Get text input view + * + * @param wifi_text_input WIFI_TextInput instance + * + * @return View instance that can be used for embedding + */ +View* wifi_text_input_get_view(WIFI_TextInput* wifi_text_input); + +/** Set text input result callback + * + * @param wifi_text_input WIFI_TextInput instance + * @param callback callback fn + * @param callback_context callback context + * @param text_buffer pointer to YOUR text buffer, that we going + * to modify + * @param text_buffer_size YOUR text buffer size in bytes. Max string + * length will be text_buffer_size-1. + * @param clear_default_text clear text from text_buffer on first OK + * event + */ +void wifi_text_input_set_result_callback( + WIFI_TextInput* wifi_text_input, + WIFI_TextInputCallback callback, + void* callback_context, + char* text_buffer, + size_t text_buffer_size, + bool clear_default_text); + +void wifi_text_input_set_validator( + WIFI_TextInput* wifi_text_input, + WIFI_TextInputValidatorCallback callback, + void* callback_context); + +WIFI_TextInputValidatorCallback + wifi_text_input_get_validator_callback(WIFI_TextInput* wifi_text_input); + +void* wifi_text_input_get_validator_callback_context(WIFI_TextInput* wifi_text_input); + +/** Set text input header text + * + * @param wifi_text_input WIFI_TextInput instance + * @param text text to be shown + */ +void wifi_text_input_set_header_text(WIFI_TextInput* wifi_text_input, const char* text); + +#ifdef __cplusplus +} +#endif diff --git a/applications/external/wifi_marauder_companion/wifi_marauder_validators.c b/applications/external/wifi_marauder_companion/wifi_marauder_validators.c new file mode 100644 index 000000000..5bec88269 --- /dev/null +++ b/applications/external/wifi_marauder_companion/wifi_marauder_validators.c @@ -0,0 +1,57 @@ +#include +#include "wifi_marauder_validators.h" +#include + +struct ValidatorIsFile { + char* app_path_folder; + const char* app_extension; + char* current_name; +}; + +bool validator_is_file_callback(const char* text, FuriString* error, void* context) { + furi_assert(context); + ValidatorIsFile* instance = context; + + if(instance->current_name != NULL) { + if(strcmp(instance->current_name, text) == 0) { + return true; + } + } + + bool ret = true; + FuriString* path = furi_string_alloc_printf( + "%s/%s%s", instance->app_path_folder, text, instance->app_extension); + Storage* storage = furi_record_open(RECORD_STORAGE); + if(storage_common_stat(storage, furi_string_get_cstr(path), NULL) == FSE_OK) { + ret = false; + furi_string_printf(error, "This name\nexists!\nChoose\nanother one."); + } else { + ret = true; + } + furi_string_free(path); + furi_record_close(RECORD_STORAGE); + + return ret; +} + +ValidatorIsFile* validator_is_file_alloc_init( + const char* app_path_folder, + const char* app_extension, + const char* current_name) { + ValidatorIsFile* instance = malloc(sizeof(ValidatorIsFile)); + + instance->app_path_folder = strdup(app_path_folder); + instance->app_extension = app_extension; + if(current_name != NULL) { + instance->current_name = strdup(current_name); + } + + return instance; +} + +void validator_is_file_free(ValidatorIsFile* instance) { + furi_assert(instance); + free(instance->app_path_folder); + free(instance->current_name); + free(instance); +} diff --git a/applications/external/wifi_marauder_companion/wifi_marauder_validators.h b/applications/external/wifi_marauder_companion/wifi_marauder_validators.h new file mode 100644 index 000000000..d9200b6db --- /dev/null +++ b/applications/external/wifi_marauder_companion/wifi_marauder_validators.h @@ -0,0 +1,21 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif +typedef struct ValidatorIsFile ValidatorIsFile; + +ValidatorIsFile* validator_is_file_alloc_init( + const char* app_path_folder, + const char* app_extension, + const char* current_name); + +void validator_is_file_free(ValidatorIsFile* instance); + +bool validator_is_file_callback(const char* text, FuriString* error, void* context); + +#ifdef __cplusplus +} +#endif diff --git a/applications/external/wii_ec_anal/LICENSE b/applications/external/wiiec/LICENSE similarity index 100% rename from applications/external/wii_ec_anal/LICENSE rename to applications/external/wiiec/LICENSE diff --git a/applications/external/wii_ec_anal/WiiEC.png b/applications/external/wiiec/WiiEC.png similarity index 100% rename from applications/external/wii_ec_anal/WiiEC.png rename to applications/external/wiiec/WiiEC.png diff --git a/applications/external/wii_ec_anal/_image_tool/LICENSE b/applications/external/wiiec/_image_tool/LICENSE similarity index 100% rename from applications/external/wii_ec_anal/_image_tool/LICENSE rename to applications/external/wiiec/_image_tool/LICENSE diff --git a/applications/external/wii_ec_anal/_image_tool/README b/applications/external/wiiec/_image_tool/README similarity index 100% rename from applications/external/wii_ec_anal/_image_tool/README rename to applications/external/wiiec/_image_tool/README diff --git a/applications/external/wii_ec_anal/_image_tool/_convert.c b/applications/external/wiiec/_image_tool/_convert.c similarity index 100% rename from applications/external/wii_ec_anal/_image_tool/_convert.c rename to applications/external/wiiec/_image_tool/_convert.c diff --git a/applications/external/wii_ec_anal/_image_tool/_convert.sh b/applications/external/wiiec/_image_tool/_convert.sh similarity index 100% rename from applications/external/wii_ec_anal/_image_tool/_convert.sh rename to applications/external/wiiec/_image_tool/_convert.sh diff --git a/applications/external/wii_ec_anal/_image_tool/_convert_images.c b/applications/external/wiiec/_image_tool/_convert_images.c similarity index 100% rename from applications/external/wii_ec_anal/_image_tool/_convert_images.c rename to applications/external/wiiec/_image_tool/_convert_images.c diff --git a/applications/external/wii_ec_anal/_image_tool/_convert_images.h b/applications/external/wiiec/_image_tool/_convert_images.h similarity index 100% rename from applications/external/wii_ec_anal/_image_tool/_convert_images.h rename to applications/external/wiiec/_image_tool/_convert_images.h diff --git a/applications/external/wii_ec_anal/_image_tool/_convert_test.c b/applications/external/wiiec/_image_tool/_convert_test.c similarity index 100% rename from applications/external/wii_ec_anal/_image_tool/_convert_test.c rename to applications/external/wiiec/_image_tool/_convert_test.c diff --git a/applications/external/wii_ec_anal/application.fam b/applications/external/wiiec/application.fam similarity index 100% rename from applications/external/wii_ec_anal/application.fam rename to applications/external/wiiec/application.fam diff --git a/applications/external/wii_ec_anal/bc_logging.h b/applications/external/wiiec/bc_logging.h similarity index 100% rename from applications/external/wii_ec_anal/bc_logging.h rename to applications/external/wiiec/bc_logging.h diff --git a/applications/external/wii_ec_anal/err.h b/applications/external/wiiec/err.h similarity index 100% rename from applications/external/wii_ec_anal/err.h rename to applications/external/wiiec/err.h diff --git a/applications/external/wii_ec_anal/gfx/images.c b/applications/external/wiiec/gfx/images.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/images.c rename to applications/external/wiiec/gfx/images.c diff --git a/applications/external/wii_ec_anal/gfx/images.h b/applications/external/wiiec/gfx/images.h similarity index 100% rename from applications/external/wii_ec_anal/gfx/images.h rename to applications/external/wiiec/gfx/images.h diff --git a/applications/external/wii_ec_anal/gfx/img_3x5_0.c b/applications/external/wiiec/gfx/img_3x5_0.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_3x5_0.c rename to applications/external/wiiec/gfx/img_3x5_0.c diff --git a/applications/external/wii_ec_anal/gfx/img_3x5_1.c b/applications/external/wiiec/gfx/img_3x5_1.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_3x5_1.c rename to applications/external/wiiec/gfx/img_3x5_1.c diff --git a/applications/external/wii_ec_anal/gfx/img_3x5_2.c b/applications/external/wiiec/gfx/img_3x5_2.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_3x5_2.c rename to applications/external/wiiec/gfx/img_3x5_2.c diff --git a/applications/external/wii_ec_anal/gfx/img_3x5_3.c b/applications/external/wiiec/gfx/img_3x5_3.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_3x5_3.c rename to applications/external/wiiec/gfx/img_3x5_3.c diff --git a/applications/external/wii_ec_anal/gfx/img_3x5_4.c b/applications/external/wiiec/gfx/img_3x5_4.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_3x5_4.c rename to applications/external/wiiec/gfx/img_3x5_4.c diff --git a/applications/external/wii_ec_anal/gfx/img_3x5_5.c b/applications/external/wiiec/gfx/img_3x5_5.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_3x5_5.c rename to applications/external/wiiec/gfx/img_3x5_5.c diff --git a/applications/external/wii_ec_anal/gfx/img_3x5_6.c b/applications/external/wiiec/gfx/img_3x5_6.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_3x5_6.c rename to applications/external/wiiec/gfx/img_3x5_6.c diff --git a/applications/external/wii_ec_anal/gfx/img_3x5_7.c b/applications/external/wiiec/gfx/img_3x5_7.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_3x5_7.c rename to applications/external/wiiec/gfx/img_3x5_7.c diff --git a/applications/external/wii_ec_anal/gfx/img_3x5_8.c b/applications/external/wiiec/gfx/img_3x5_8.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_3x5_8.c rename to applications/external/wiiec/gfx/img_3x5_8.c diff --git a/applications/external/wii_ec_anal/gfx/img_3x5_9.c b/applications/external/wiiec/gfx/img_3x5_9.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_3x5_9.c rename to applications/external/wiiec/gfx/img_3x5_9.c diff --git a/applications/external/wii_ec_anal/gfx/img_3x5_v.c b/applications/external/wiiec/gfx/img_3x5_v.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_3x5_v.c rename to applications/external/wiiec/gfx/img_3x5_v.c diff --git a/applications/external/wii_ec_anal/gfx/img_5x7_0.c b/applications/external/wiiec/gfx/img_5x7_0.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_5x7_0.c rename to applications/external/wiiec/gfx/img_5x7_0.c diff --git a/applications/external/wii_ec_anal/gfx/img_5x7_1.c b/applications/external/wiiec/gfx/img_5x7_1.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_5x7_1.c rename to applications/external/wiiec/gfx/img_5x7_1.c diff --git a/applications/external/wii_ec_anal/gfx/img_5x7_2.c b/applications/external/wiiec/gfx/img_5x7_2.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_5x7_2.c rename to applications/external/wiiec/gfx/img_5x7_2.c diff --git a/applications/external/wii_ec_anal/gfx/img_5x7_3.c b/applications/external/wiiec/gfx/img_5x7_3.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_5x7_3.c rename to applications/external/wiiec/gfx/img_5x7_3.c diff --git a/applications/external/wii_ec_anal/gfx/img_5x7_4.c b/applications/external/wiiec/gfx/img_5x7_4.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_5x7_4.c rename to applications/external/wiiec/gfx/img_5x7_4.c diff --git a/applications/external/wii_ec_anal/gfx/img_5x7_5.c b/applications/external/wiiec/gfx/img_5x7_5.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_5x7_5.c rename to applications/external/wiiec/gfx/img_5x7_5.c diff --git a/applications/external/wii_ec_anal/gfx/img_5x7_6.c b/applications/external/wiiec/gfx/img_5x7_6.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_5x7_6.c rename to applications/external/wiiec/gfx/img_5x7_6.c diff --git a/applications/external/wii_ec_anal/gfx/img_5x7_7.c b/applications/external/wiiec/gfx/img_5x7_7.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_5x7_7.c rename to applications/external/wiiec/gfx/img_5x7_7.c diff --git a/applications/external/wii_ec_anal/gfx/img_5x7_8.c b/applications/external/wiiec/gfx/img_5x7_8.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_5x7_8.c rename to applications/external/wiiec/gfx/img_5x7_8.c diff --git a/applications/external/wii_ec_anal/gfx/img_5x7_9.c b/applications/external/wiiec/gfx/img_5x7_9.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_5x7_9.c rename to applications/external/wiiec/gfx/img_5x7_9.c diff --git a/applications/external/wii_ec_anal/gfx/img_5x7_A.c b/applications/external/wiiec/gfx/img_5x7_A.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_5x7_A.c rename to applications/external/wiiec/gfx/img_5x7_A.c diff --git a/applications/external/wii_ec_anal/gfx/img_5x7_B.c b/applications/external/wiiec/gfx/img_5x7_B.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_5x7_B.c rename to applications/external/wiiec/gfx/img_5x7_B.c diff --git a/applications/external/wii_ec_anal/gfx/img_5x7_C.c b/applications/external/wiiec/gfx/img_5x7_C.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_5x7_C.c rename to applications/external/wiiec/gfx/img_5x7_C.c diff --git a/applications/external/wii_ec_anal/gfx/img_5x7_D.c b/applications/external/wiiec/gfx/img_5x7_D.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_5x7_D.c rename to applications/external/wiiec/gfx/img_5x7_D.c diff --git a/applications/external/wii_ec_anal/gfx/img_5x7_E.c b/applications/external/wiiec/gfx/img_5x7_E.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_5x7_E.c rename to applications/external/wiiec/gfx/img_5x7_E.c diff --git a/applications/external/wii_ec_anal/gfx/img_5x7_F.c b/applications/external/wiiec/gfx/img_5x7_F.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_5x7_F.c rename to applications/external/wiiec/gfx/img_5x7_F.c diff --git a/applications/external/wii_ec_anal/gfx/img_6x8_0.c b/applications/external/wiiec/gfx/img_6x8_0.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_6x8_0.c rename to applications/external/wiiec/gfx/img_6x8_0.c diff --git a/applications/external/wii_ec_anal/gfx/img_6x8_1.c b/applications/external/wiiec/gfx/img_6x8_1.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_6x8_1.c rename to applications/external/wiiec/gfx/img_6x8_1.c diff --git a/applications/external/wii_ec_anal/gfx/img_6x8_2.c b/applications/external/wiiec/gfx/img_6x8_2.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_6x8_2.c rename to applications/external/wiiec/gfx/img_6x8_2.c diff --git a/applications/external/wii_ec_anal/gfx/img_6x8_3.c b/applications/external/wiiec/gfx/img_6x8_3.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_6x8_3.c rename to applications/external/wiiec/gfx/img_6x8_3.c diff --git a/applications/external/wii_ec_anal/gfx/img_6x8_4.c b/applications/external/wiiec/gfx/img_6x8_4.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_6x8_4.c rename to applications/external/wiiec/gfx/img_6x8_4.c diff --git a/applications/external/wii_ec_anal/gfx/img_6x8_5.c b/applications/external/wiiec/gfx/img_6x8_5.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_6x8_5.c rename to applications/external/wiiec/gfx/img_6x8_5.c diff --git a/applications/external/wii_ec_anal/gfx/img_6x8_6.c b/applications/external/wiiec/gfx/img_6x8_6.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_6x8_6.c rename to applications/external/wiiec/gfx/img_6x8_6.c diff --git a/applications/external/wii_ec_anal/gfx/img_6x8_7.c b/applications/external/wiiec/gfx/img_6x8_7.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_6x8_7.c rename to applications/external/wiiec/gfx/img_6x8_7.c diff --git a/applications/external/wii_ec_anal/gfx/img_6x8_8.c b/applications/external/wiiec/gfx/img_6x8_8.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_6x8_8.c rename to applications/external/wiiec/gfx/img_6x8_8.c diff --git a/applications/external/wii_ec_anal/gfx/img_6x8_9.c b/applications/external/wiiec/gfx/img_6x8_9.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_6x8_9.c rename to applications/external/wiiec/gfx/img_6x8_9.c diff --git a/applications/external/wii_ec_anal/gfx/img_6x8_A.c b/applications/external/wiiec/gfx/img_6x8_A.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_6x8_A.c rename to applications/external/wiiec/gfx/img_6x8_A.c diff --git a/applications/external/wii_ec_anal/gfx/img_6x8_B.c b/applications/external/wiiec/gfx/img_6x8_B.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_6x8_B.c rename to applications/external/wiiec/gfx/img_6x8_B.c diff --git a/applications/external/wii_ec_anal/gfx/img_6x8_C.c b/applications/external/wiiec/gfx/img_6x8_C.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_6x8_C.c rename to applications/external/wiiec/gfx/img_6x8_C.c diff --git a/applications/external/wii_ec_anal/gfx/img_6x8_D.c b/applications/external/wiiec/gfx/img_6x8_D.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_6x8_D.c rename to applications/external/wiiec/gfx/img_6x8_D.c diff --git a/applications/external/wii_ec_anal/gfx/img_6x8_E.c b/applications/external/wiiec/gfx/img_6x8_E.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_6x8_E.c rename to applications/external/wiiec/gfx/img_6x8_E.c diff --git a/applications/external/wii_ec_anal/gfx/img_6x8_F.c b/applications/external/wiiec/gfx/img_6x8_F.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_6x8_F.c rename to applications/external/wiiec/gfx/img_6x8_F.c diff --git a/applications/external/wii_ec_anal/gfx/img_6x8_G.c b/applications/external/wiiec/gfx/img_6x8_G.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_6x8_G.c rename to applications/external/wiiec/gfx/img_6x8_G.c diff --git a/applications/external/wii_ec_anal/gfx/img_6x8_X.c b/applications/external/wiiec/gfx/img_6x8_X.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_6x8_X.c rename to applications/external/wiiec/gfx/img_6x8_X.c diff --git a/applications/external/wii_ec_anal/gfx/img_6x8_Y.c b/applications/external/wiiec/gfx/img_6x8_Y.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_6x8_Y.c rename to applications/external/wiiec/gfx/img_6x8_Y.c diff --git a/applications/external/wii_ec_anal/gfx/img_6x8_Z.c b/applications/external/wiiec/gfx/img_6x8_Z.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_6x8_Z.c rename to applications/external/wiiec/gfx/img_6x8_Z.c diff --git a/applications/external/wii_ec_anal/gfx/img_6x8_d_.c b/applications/external/wiiec/gfx/img_6x8_d_.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_6x8_d_.c rename to applications/external/wiiec/gfx/img_6x8_d_.c diff --git a/applications/external/wii_ec_anal/gfx/img_6x8_n_.c b/applications/external/wiiec/gfx/img_6x8_n_.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_6x8_n_.c rename to applications/external/wiiec/gfx/img_6x8_n_.c diff --git a/applications/external/wii_ec_anal/gfx/img_6x8_v_.c b/applications/external/wiiec/gfx/img_6x8_v_.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_6x8_v_.c rename to applications/external/wiiec/gfx/img_6x8_v_.c diff --git a/applications/external/wii_ec_anal/gfx/img_RIP.c b/applications/external/wiiec/gfx/img_RIP.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_RIP.c rename to applications/external/wiiec/gfx/img_RIP.c diff --git a/applications/external/wii_ec_anal/gfx/img_cc_Cable.c b/applications/external/wiiec/gfx/img_cc_Cable.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_cc_Cable.c rename to applications/external/wiiec/gfx/img_cc_Cable.c diff --git a/applications/external/wii_ec_anal/gfx/img_cc_Joy.c b/applications/external/wiiec/gfx/img_cc_Joy.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_cc_Joy.c rename to applications/external/wiiec/gfx/img_cc_Joy.c diff --git a/applications/external/wii_ec_anal/gfx/img_cc_Main.c b/applications/external/wiiec/gfx/img_cc_Main.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_cc_Main.c rename to applications/external/wiiec/gfx/img_cc_Main.c diff --git a/applications/external/wii_ec_anal/gfx/img_cc_btn_A1.c b/applications/external/wiiec/gfx/img_cc_btn_A1.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_cc_btn_A1.c rename to applications/external/wiiec/gfx/img_cc_btn_A1.c diff --git a/applications/external/wii_ec_anal/gfx/img_cc_btn_B1.c b/applications/external/wiiec/gfx/img_cc_btn_B1.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_cc_btn_B1.c rename to applications/external/wiiec/gfx/img_cc_btn_B1.c diff --git a/applications/external/wii_ec_anal/gfx/img_cc_btn_X1.c b/applications/external/wiiec/gfx/img_cc_btn_X1.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_cc_btn_X1.c rename to applications/external/wiiec/gfx/img_cc_btn_X1.c diff --git a/applications/external/wii_ec_anal/gfx/img_cc_btn_Y1.c b/applications/external/wiiec/gfx/img_cc_btn_Y1.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_cc_btn_Y1.c rename to applications/external/wiiec/gfx/img_cc_btn_Y1.c diff --git a/applications/external/wii_ec_anal/gfx/img_cc_pad_LR1.c b/applications/external/wiiec/gfx/img_cc_pad_LR1.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_cc_pad_LR1.c rename to applications/external/wiiec/gfx/img_cc_pad_LR1.c diff --git a/applications/external/wii_ec_anal/gfx/img_cc_pad_UD1.c b/applications/external/wiiec/gfx/img_cc_pad_UD1.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_cc_pad_UD1.c rename to applications/external/wiiec/gfx/img_cc_pad_UD1.c diff --git a/applications/external/wii_ec_anal/gfx/img_cc_trg_L1.c b/applications/external/wiiec/gfx/img_cc_trg_L1.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_cc_trg_L1.c rename to applications/external/wiiec/gfx/img_cc_trg_L1.c diff --git a/applications/external/wii_ec_anal/gfx/img_cc_trg_L2.c b/applications/external/wiiec/gfx/img_cc_trg_L2.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_cc_trg_L2.c rename to applications/external/wiiec/gfx/img_cc_trg_L2.c diff --git a/applications/external/wii_ec_anal/gfx/img_cc_trg_L3.c b/applications/external/wiiec/gfx/img_cc_trg_L3.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_cc_trg_L3.c rename to applications/external/wiiec/gfx/img_cc_trg_L3.c diff --git a/applications/external/wii_ec_anal/gfx/img_cc_trg_L4.c b/applications/external/wiiec/gfx/img_cc_trg_L4.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_cc_trg_L4.c rename to applications/external/wiiec/gfx/img_cc_trg_L4.c diff --git a/applications/external/wii_ec_anal/gfx/img_cc_trg_R1.c b/applications/external/wiiec/gfx/img_cc_trg_R1.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_cc_trg_R1.c rename to applications/external/wiiec/gfx/img_cc_trg_R1.c diff --git a/applications/external/wii_ec_anal/gfx/img_cc_trg_R2.c b/applications/external/wiiec/gfx/img_cc_trg_R2.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_cc_trg_R2.c rename to applications/external/wiiec/gfx/img_cc_trg_R2.c diff --git a/applications/external/wii_ec_anal/gfx/img_cc_trg_R3.c b/applications/external/wiiec/gfx/img_cc_trg_R3.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_cc_trg_R3.c rename to applications/external/wiiec/gfx/img_cc_trg_R3.c diff --git a/applications/external/wii_ec_anal/gfx/img_cc_trg_R4.c b/applications/external/wiiec/gfx/img_cc_trg_R4.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_cc_trg_R4.c rename to applications/external/wiiec/gfx/img_cc_trg_R4.c diff --git a/applications/external/wii_ec_anal/gfx/img_csLogo_FULL.c b/applications/external/wiiec/gfx/img_csLogo_FULL.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_csLogo_FULL.c rename to applications/external/wiiec/gfx/img_csLogo_FULL.c diff --git a/applications/external/wii_ec_anal/gfx/img_csLogo_Small.c b/applications/external/wiiec/gfx/img_csLogo_Small.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_csLogo_Small.c rename to applications/external/wiiec/gfx/img_csLogo_Small.c diff --git a/applications/external/wii_ec_anal/gfx/img_ecp_SCL.c b/applications/external/wiiec/gfx/img_ecp_SCL.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_ecp_SCL.c rename to applications/external/wiiec/gfx/img_ecp_SCL.c diff --git a/applications/external/wii_ec_anal/gfx/img_ecp_SDA.c b/applications/external/wiiec/gfx/img_ecp_SDA.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_ecp_SDA.c rename to applications/external/wiiec/gfx/img_ecp_SDA.c diff --git a/applications/external/wii_ec_anal/gfx/img_ecp_port.c b/applications/external/wiiec/gfx/img_ecp_port.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_ecp_port.c rename to applications/external/wiiec/gfx/img_ecp_port.c diff --git a/applications/external/wii_ec_anal/gfx/img_key_Back.c b/applications/external/wiiec/gfx/img_key_Back.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_key_Back.c rename to applications/external/wiiec/gfx/img_key_Back.c diff --git a/applications/external/wii_ec_anal/gfx/img_key_D.c b/applications/external/wiiec/gfx/img_key_D.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_key_D.c rename to applications/external/wiiec/gfx/img_key_D.c diff --git a/applications/external/wii_ec_anal/gfx/img_key_L.c b/applications/external/wiiec/gfx/img_key_L.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_key_L.c rename to applications/external/wiiec/gfx/img_key_L.c diff --git a/applications/external/wii_ec_anal/gfx/img_key_OK.c b/applications/external/wiiec/gfx/img_key_OK.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_key_OK.c rename to applications/external/wiiec/gfx/img_key_OK.c diff --git a/applications/external/wii_ec_anal/gfx/img_key_OKi.c b/applications/external/wiiec/gfx/img_key_OKi.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_key_OKi.c rename to applications/external/wiiec/gfx/img_key_OKi.c diff --git a/applications/external/wii_ec_anal/gfx/img_key_R.c b/applications/external/wiiec/gfx/img_key_R.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_key_R.c rename to applications/external/wiiec/gfx/img_key_R.c diff --git a/applications/external/wii_ec_anal/gfx/img_key_U.c b/applications/external/wiiec/gfx/img_key_U.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_key_U.c rename to applications/external/wiiec/gfx/img_key_U.c diff --git a/applications/external/wii_ec_anal/gfx/img_key_Ui.c b/applications/external/wiiec/gfx/img_key_Ui.c similarity index 100% rename from applications/external/wii_ec_anal/gfx/img_key_Ui.c rename to applications/external/wiiec/gfx/img_key_Ui.c diff --git a/applications/external/wii_ec_anal/i2c_workaround.h b/applications/external/wiiec/i2c_workaround.h similarity index 100% rename from applications/external/wii_ec_anal/i2c_workaround.h rename to applications/external/wiiec/i2c_workaround.h diff --git a/applications/external/wii_ec_anal/info.sh b/applications/external/wiiec/info.sh similarity index 100% rename from applications/external/wii_ec_anal/info.sh rename to applications/external/wiiec/info.sh diff --git a/applications/external/wii_ec_anal/wii_anal.c b/applications/external/wiiec/wii_anal.c similarity index 95% rename from applications/external/wii_ec_anal/wii_anal.c rename to applications/external/wiiec/wii_anal.c index 34b4f318b..5882dd60b 100644 --- a/applications/external/wii_ec_anal/wii_anal.c +++ b/applications/external/wiiec/wii_anal.c @@ -80,6 +80,8 @@ static void showVer(Canvas* const canvas) { show(canvas, 4, 59, VER_MAJ, SHOW_SET_BLK); canvas_draw_frame(canvas, 8, 62, 2, 2); show(canvas, 11, 59, VER_MIN, SHOW_SET_BLK); + canvas_draw_frame(canvas, 15, 62, 2, 2); + show(canvas, 18, 59, VER_SUB, SHOW_SET_BLK); } //+============================================================================ @@ -94,9 +96,10 @@ static void cbDraw(Canvas* const canvas, void* ctx) { furi_assert(canvas); furi_assert(ctx); - // Try to acquire the mutex for the plugin state variables, timeout = 25mS state_t* state = ctx; - furi_mutex_acquire(state->mutex, FuriWaitForever); + + // Try to acquire the mutex for the plugin state variables, timeout = 25mS + if(furi_mutex_acquire(state->mutex, 25) != FuriStatusOk) return; switch(state->scene) { //--------------------------------------------------------------------- @@ -344,8 +347,7 @@ int32_t wii_ec_anal(void) { goto bail; } // 5. Create a mutex for (reading/writing) the plugin state variables - state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); - if(!state->mutex) { + if(!(state->mutex = furi_mutex_alloc(FuriMutexTypeNormal))) { ERROR(wii_errs[(error = ERR_NO_MUTEX)]); goto bail; } @@ -434,7 +436,10 @@ int32_t wii_ec_anal(void) { // Read successful // *** Try to lock the plugin state variables *** - furi_mutex_acquire(state->mutex, FuriWaitForever); + if(furi_mutex_acquire(state->mutex, FuriWaitForever) != FuriStatusOk) { + ERROR(wii_errs[(error = ERR_MUTEX_BLOCK)]); + goto bail; + } // *** Handle events *** switch(msg.id) { @@ -472,7 +477,10 @@ int32_t wii_ec_anal(void) { if(redraw) view_port_update(vpp); // *** Try to release the plugin state variables *** - furi_mutex_release(state->mutex); + if(furi_mutex_release(state->mutex) != FuriStatusOk) { + ERROR(wii_errs[(error = ERR_MUTEX_RELEASE)]); + goto bail; + } } while(state->run); // ===== Game Over ===== @@ -480,13 +488,13 @@ int32_t wii_ec_anal(void) { bail: // 10. Release system notification queue - if(state->notify) { + if(state && state->notify) { furi_record_close(RECORD_NOTIFICATION); state->notify = NULL; } // 9. Stop the timer - if(state->timer) { + if(state && state->timer) { (void)furi_timer_stop(state->timer); furi_timer_free(state->timer); state->timer = NULL; @@ -507,7 +515,10 @@ bail: } // 5. Free the mutex - furi_mutex_free(state->mutex); + if(state && state->mutex) { + furi_mutex_free(state->mutex); + state->mutex = NULL; + } // 4. Free up state pointer(s) // none diff --git a/applications/external/wii_ec_anal/wii_anal.h b/applications/external/wiiec/wii_anal.h similarity index 98% rename from applications/external/wii_ec_anal/wii_anal.h rename to applications/external/wiiec/wii_anal.h index d8997b030..3be398a54 100644 --- a/applications/external/wii_ec_anal/wii_anal.h +++ b/applications/external/wiiec/wii_anal.h @@ -56,7 +56,8 @@ typedef struct eventMsg { // Access to this memory is controlled by mutex // typedef struct state { - FuriMutex* mutex; + FuriMutex* mutex; // mutex for using this struct + bool run; // true : plugin is running bool timerEn; // controller scanning enabled diff --git a/applications/external/wii_ec_anal/wii_anal_ec.c b/applications/external/wiiec/wii_anal_ec.c similarity index 100% rename from applications/external/wii_ec_anal/wii_anal_ec.c rename to applications/external/wiiec/wii_anal_ec.c diff --git a/applications/external/wii_ec_anal/wii_anal_ec.h b/applications/external/wiiec/wii_anal_ec.h similarity index 100% rename from applications/external/wii_ec_anal/wii_anal_ec.h rename to applications/external/wiiec/wii_anal_ec.h diff --git a/applications/external/wii_ec_anal/wii_anal_keys.c b/applications/external/wiiec/wii_anal_keys.c similarity index 100% rename from applications/external/wii_ec_anal/wii_anal_keys.c rename to applications/external/wiiec/wii_anal_keys.c diff --git a/applications/external/wii_ec_anal/wii_anal_keys.h b/applications/external/wiiec/wii_anal_keys.h similarity index 100% rename from applications/external/wii_ec_anal/wii_anal_keys.h rename to applications/external/wiiec/wii_anal_keys.h diff --git a/applications/external/wii_ec_anal/wii_anal_lcd.c b/applications/external/wiiec/wii_anal_lcd.c similarity index 100% rename from applications/external/wii_ec_anal/wii_anal_lcd.c rename to applications/external/wiiec/wii_anal_lcd.c diff --git a/applications/external/wii_ec_anal/wii_anal_lcd.h b/applications/external/wiiec/wii_anal_lcd.h similarity index 100% rename from applications/external/wii_ec_anal/wii_anal_lcd.h rename to applications/external/wiiec/wii_anal_lcd.h diff --git a/applications/external/wii_ec_anal/wii_anal_ver.h b/applications/external/wiiec/wii_anal_ver.h similarity index 85% rename from applications/external/wii_ec_anal/wii_anal_ver.h rename to applications/external/wiiec/wii_anal_ver.h index 3f2c8c0e6..df2659d57 100644 --- a/applications/external/wii_ec_anal/wii_anal_ver.h +++ b/applications/external/wiiec/wii_anal_ver.h @@ -5,5 +5,6 @@ #define VER_MAJ &img_3x5_1 #define VER_MIN &img_3x5_0 +#define VER_SUB &img_3x5_1 #endif //WII_ANAL_VER_H_ diff --git a/applications/external/wii_ec_anal/wii_ec.c b/applications/external/wiiec/wii_ec.c similarity index 100% rename from applications/external/wii_ec_anal/wii_ec.c rename to applications/external/wiiec/wii_ec.c diff --git a/applications/external/wii_ec_anal/wii_ec.h b/applications/external/wiiec/wii_ec.h similarity index 100% rename from applications/external/wii_ec_anal/wii_ec.h rename to applications/external/wiiec/wii_ec.h diff --git a/applications/external/wii_ec_anal/wii_ec_classic.c b/applications/external/wiiec/wii_ec_classic.c similarity index 100% rename from applications/external/wii_ec_anal/wii_ec_classic.c rename to applications/external/wiiec/wii_ec_classic.c diff --git a/applications/external/wii_ec_anal/wii_ec_classic.h b/applications/external/wiiec/wii_ec_classic.h similarity index 100% rename from applications/external/wii_ec_anal/wii_ec_classic.h rename to applications/external/wiiec/wii_ec_classic.h diff --git a/applications/external/wii_ec_anal/wii_ec_macros.h b/applications/external/wiiec/wii_ec_macros.h similarity index 100% rename from applications/external/wii_ec_anal/wii_ec_macros.h rename to applications/external/wiiec/wii_ec_macros.h diff --git a/applications/external/wii_ec_anal/wii_ec_nunchuck.c b/applications/external/wiiec/wii_ec_nunchuck.c similarity index 100% rename from applications/external/wii_ec_anal/wii_ec_nunchuck.c rename to applications/external/wiiec/wii_ec_nunchuck.c diff --git a/applications/external/wii_ec_anal/wii_ec_nunchuck.h b/applications/external/wiiec/wii_ec_nunchuck.h similarity index 100% rename from applications/external/wii_ec_anal/wii_ec_nunchuck.h rename to applications/external/wiiec/wii_ec_nunchuck.h diff --git a/applications/external/wii_ec_anal/wii_ec_udraw.c b/applications/external/wiiec/wii_ec_udraw.c similarity index 100% rename from applications/external/wii_ec_anal/wii_ec_udraw.c rename to applications/external/wiiec/wii_ec_udraw.c diff --git a/applications/external/wii_ec_anal/wii_ec_udraw.h b/applications/external/wiiec/wii_ec_udraw.h similarity index 100% rename from applications/external/wii_ec_anal/wii_ec_udraw.h rename to applications/external/wiiec/wii_ec_udraw.h diff --git a/applications/external/wii_ec_anal/wii_i2c.c b/applications/external/wiiec/wii_i2c.c similarity index 100% rename from applications/external/wii_ec_anal/wii_i2c.c rename to applications/external/wiiec/wii_i2c.c diff --git a/applications/external/wii_ec_anal/wii_i2c.h b/applications/external/wiiec/wii_i2c.h similarity index 100% rename from applications/external/wii_ec_anal/wii_i2c.h rename to applications/external/wiiec/wii_i2c.h diff --git a/applications/main/archive/helpers/archive_browser.h b/applications/main/archive/helpers/archive_browser.h index 36da022da..a96fa9679 100644 --- a/applications/main/archive/helpers/archive_browser.h +++ b/applications/main/archive/helpers/archive_browser.h @@ -62,7 +62,7 @@ static inline const char* archive_get_default_path(ArchiveTabEnum tab) { } inline bool archive_is_known_app(ArchiveFileTypeEnum type) { - return (type != ArchiveFileTypeFolder && 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 7304be6b4..9d4b963f5 100644 --- a/applications/main/archive/helpers/archive_favorites.c +++ b/applications/main/archive/helpers/archive_favorites.c @@ -164,7 +164,7 @@ bool archive_favorites_read(void* context) { need_refresh = true; } } else { - if(storage_file_exists(storage, furi_string_get_cstr(buffer))) { + if(storage_common_exists(storage, furi_string_get_cstr(buffer))) { storage_common_stat(storage, furi_string_get_cstr(buffer), &file_info); archive_add_file_item( browser, file_info_is_dir(&file_info), furi_string_get_cstr(buffer)); diff --git a/applications/main/archive/helpers/archive_files.h b/applications/main/archive/helpers/archive_files.h index e4bed3a63..7680bbd57 100644 --- a/applications/main/archive/helpers/archive_files.h +++ b/applications/main/archive/helpers/archive_files.h @@ -5,7 +5,7 @@ #include #include #include "toolbox/path.h" -#include "xtreme/settings.h" +#include #define FAP_MANIFEST_MAX_ICON_SIZE 32 diff --git a/applications/main/archive/helpers/favorite_timeout.c b/applications/main/archive/helpers/favorite_timeout.c new file mode 100644 index 000000000..7643ebfc8 --- /dev/null +++ b/applications/main/archive/helpers/favorite_timeout.c @@ -0,0 +1,32 @@ +#include "favorite_timeout.h" +#include + +bool process_favorite_launch(char** args) { + if(*args && strlen(*args) > 4 && strncmp(*args, "fav/", 4) == 0) { + *args += 3; + return true; + } + return false; +} + +void favorite_timeout_callback(void* _ctx) { + FavoriteTImeoutCtx* ctx = _ctx; + while(scene_manager_previous_scene(ctx->scene_manager)) + ; + view_dispatcher_stop(ctx->view_dispatcher); +} + +void favorite_timeout_run(ViewDispatcher* view_dispatcher, SceneManager* scene_manager) { + int32_t timeout = XTREME_SETTINGS()->favorite_timeout; + if(timeout == 0) { + view_dispatcher_run(view_dispatcher); + return; + } + + FavoriteTImeoutCtx ctx = {.view_dispatcher = view_dispatcher, .scene_manager = scene_manager}; + FuriTimer* timer = furi_timer_alloc(favorite_timeout_callback, FuriTimerTypeOnce, &ctx); + furi_timer_start(timer, timeout * furi_kernel_get_tick_frequency()); + view_dispatcher_run(view_dispatcher); + furi_timer_stop(timer); + furi_timer_free(timer); +} diff --git a/applications/main/archive/helpers/favorite_timeout.h b/applications/main/archive/helpers/favorite_timeout.h new file mode 100644 index 000000000..aded05708 --- /dev/null +++ b/applications/main/archive/helpers/favorite_timeout.h @@ -0,0 +1,16 @@ +#pragma once + +#include +#include +#include + +bool process_favorite_launch(char** p); + +typedef struct { + ViewDispatcher* view_dispatcher; + SceneManager* scene_manager; +} FavoriteTImeoutCtx; + +void favorite_timeout_callback(void* _ctx); + +void favorite_timeout_run(ViewDispatcher* view_dispatcher, SceneManager* scene_manager); diff --git a/applications/main/archive/scenes/archive_scene_browser.c b/applications/main/archive/scenes/archive_scene_browser.c index 8d606cf09..f0884ab73 100644 --- a/applications/main/archive/scenes/archive_scene_browser.c +++ b/applications/main/archive/scenes/archive_scene_browser.c @@ -21,6 +21,7 @@ static const char* flipper_app_name[] = { [ArchiveFileTypeU2f] = "U2F", [ArchiveFileTypeApplication] = "Apps", [ArchiveFileTypeUpdateManifest] = "UpdaterApp", + [ArchiveFileTypeFolder] = "Archive", }; static void archive_loader_callback(const void* message, void* context) { @@ -35,7 +36,8 @@ static void archive_loader_callback(const void* message, void* context) { } } -static void archive_run_in_app(ArchiveBrowserView* browser, ArchiveFile_t* selected) { +static void + archive_run_in_app(ArchiveBrowserView* browser, ArchiveFile_t* selected, bool favorites) { UNUSED(browser); Loader* loader = furi_record_open(RECORD_LOADER); @@ -47,8 +49,14 @@ static void archive_run_in_app(ArchiveBrowserView* browser, ArchiveFile_t* selec } status = loader_start(loader, flipper_app_name[selected->type], param); } else { - status = loader_start( - loader, flipper_app_name[selected->type], furi_string_get_cstr(selected->path)); + const char* str = furi_string_get_cstr(selected->path); + if(favorites) { + char arg[strlen(str) + 4]; + snprintf(arg, sizeof(arg), "fav%s", str); + status = loader_start(loader, flipper_app_name[selected->type], arg); + } else { + status = loader_start(loader, flipper_app_name[selected->type], str); + } } if(status != LoaderStatusOk) { @@ -107,8 +115,12 @@ bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) { consumed = true; break; case ArchiveBrowserEventFileMenuRun: - if(archive_is_known_app(selected->type)) { - archive_run_in_app(browser, selected); + if(selected->type == ArchiveFileTypeFolder) { + archive_switch_tab(browser, TAB_LEFT); + archive_show_file_menu(browser, false); + archive_enter_dir(browser, selected->path); + } else if(archive_is_known_app(selected->type)) { + archive_run_in_app(browser, selected, favorites); archive_show_file_menu(browser, false); } consumed = true; diff --git a/applications/main/archive/views/archive_browser_view.c b/applications/main/archive/views/archive_browser_view.c index 994290a3c..cc2ed6263 100644 --- a/applications/main/archive/views/archive_browser_view.c +++ b/applications/main/archive/views/archive_browser_view.c @@ -66,8 +66,35 @@ static void render_item_menu(Canvas* canvas, ArchiveBrowserViewModel* model) { furi_string_set(item_pin, "Unpin"); } - if(selected->type == ArchiveFileTypeFolder) { + if(model->tab_idx == ArchiveTabFavorites) { + //FURI_LOG_D(TAG, "ArchiveTabFavorites"); + + furi_string_set(item_rename, "Move"); + + archive_menu_add_item( + menu_array_push_raw(model->context_menu), + item_run, + ArchiveBrowserEventFileMenuRun); + archive_menu_add_item( + menu_array_push_raw(model->context_menu), + item_pin, + ArchiveBrowserEventFileMenuPin); + if(selected->type <= ArchiveFileTypeBadKb) { + 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_rename, + 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); archive_menu_add_item( menu_array_push_raw(model->context_menu), item_rename, @@ -97,29 +124,6 @@ static void render_item_menu(Canvas* canvas, ArchiveBrowserViewModel* model) { menu_array_push_raw(model->context_menu), item_delete, ArchiveBrowserEventFileMenuDelete); - } else if(model->tab_idx == ArchiveTabFavorites) { - //FURI_LOG_D(TAG, "ArchiveTabFavorites"); - - furi_string_set(item_rename, "Move"); - - archive_menu_add_item( - menu_array_push_raw(model->context_menu), - item_run, - ArchiveBrowserEventFileMenuRun); - archive_menu_add_item( - menu_array_push_raw(model->context_menu), - item_pin, - ArchiveBrowserEventFileMenuPin); - if(selected->type <= ArchiveFileTypeBadKb) { - 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_rename, - ArchiveBrowserEventFileMenuRename); } else if(selected->is_app) { //FURI_LOG_D(TAG, "3 types"); archive_menu_add_item( @@ -320,7 +324,7 @@ static void archive_render_status_bar(Canvas* canvas, ArchiveBrowserViewModel* m const char* tab_name = ArchiveTabNames[model->tab_idx]; - canvas_draw_icon(canvas, 0, 0, &I_Background_128x11); + canvas_draw_icon(canvas, 0, 0, XTREME_ASSETS()->I_Background_128x11); canvas_set_color(canvas, ColorWhite); canvas_draw_box(canvas, 0, 0, 50, 13); diff --git a/applications/main/bad_kb/bad_kb_app.c b/applications/main/bad_kb/bad_kb_app.c index d1215d628..e8b29b837 100644 --- a/applications/main/bad_kb/bad_kb_app.c +++ b/applications/main/bad_kb/bad_kb_app.c @@ -4,8 +4,9 @@ #include #include #include -#include +#include #include +#include #include #include @@ -100,19 +101,19 @@ static void bad_kb_save_settings(BadKbApp* app) { 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 = 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); } -void bad_kb_config_switch_mode(BadKbApp* app) { - scene_manager_previous_scene(app->scene_manager); - if(app->is_bt) { - furi_hal_bt_start_advertising(); - } else { - furi_hal_bt_stop_advertising(); - } - scene_manager_next_scene(app->scene_manager, BadKbSceneConfig); +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(); + scene_manager_next_scene(app->scene_manager, BadKbSceneConfig); + scene_manager_previous_scene(app->scene_manager); + return 0; } void bad_kb_config_switch_remember_mode(BadKbApp* app) { @@ -204,6 +205,7 @@ 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); } @@ -269,7 +271,8 @@ BadKbApp* bad_kb_app_alloc(char* arg) { "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 = + 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 { @@ -335,8 +338,8 @@ void bad_kb_app_free(BadKbApp* app) { free(app); } -int32_t bad_kb_app(void* p) { - BadKbApp* bad_kb_app = bad_kb_app_alloc((char*)p); +int32_t bad_kb_app(char* p) { + BadKbApp* bad_kb_app = bad_kb_app_alloc(p); view_dispatcher_run(bad_kb_app->view_dispatcher); diff --git a/applications/main/bad_kb/bad_kb_app.h b/applications/main/bad_kb/bad_kb_app.h index 2da40a21f..923ba7780 100644 --- a/applications/main/bad_kb/bad_kb_app.h +++ b/applications/main/bad_kb/bad_kb_app.h @@ -6,67 +6,21 @@ #include #include -#include #include #include #include -#include -#include -#include -#include -#include "views/bad_kb_view.h" #define BAD_KB_APP_BASE_FOLDER EXT_PATH("badkb") #define BAD_KB_APP_PATH_LAYOUT_FOLDER BAD_KB_APP_BASE_FOLDER "/assets/layouts" #define BAD_KB_APP_SCRIPT_EXTENSION ".txt" #define BAD_KB_APP_LAYOUT_EXTENSION ".kl" -#define BAD_KB_ADV_NAME_MAX_LEN FURI_HAL_BT_ADV_NAME_LENGTH -#define BAD_KB_MAC_ADDRESS_LEN GAP_MAC_ADDR_SIZE - -typedef enum { - BadKbAppErrorNoFiles, - BadKbAppErrorCloseRpc, -} BadKbAppError; - typedef enum BadKbCustomEvent { BadKbAppCustomEventTextEditResult, BadKbAppCustomEventByteInputDone, BadKbCustomEventErrorBack } BadKbCustomEvent; -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; -} BadKbConfig; - -typedef struct { - Gui* gui; - ViewDispatcher* view_dispatcher; - SceneManager* scene_manager; - NotificationApp* notifications; - DialogsApp* dialogs; - Widget* widget; - VariableItemList* var_item_list; - TextInput* text_input; - ByteInput* byte_input; - - BadKbAppError error; - FuriString* file_path; - FuriString* keyboard_layout; - BadKb* bad_kb_view; - BadKbScript* bad_kb_script; - - Bt* bt; - bool is_bt; - bool bt_remember; - BadKbConfig config; - BadKbConfig prev_config; - FuriThread* conn_init_thread; -} BadKbApp; - typedef enum { BadKbAppViewError, BadKbAppViewWork, @@ -75,8 +29,6 @@ typedef enum { BadKbAppViewConfigName } BadKbAppView; -void bad_kb_config_switch_mode(BadKbApp* app); - void bad_kb_config_switch_remember_mode(BadKbApp* app); int32_t bad_kb_connection_init(BadKbApp* app); diff --git a/applications/main/bad_kb/helpers/ducky_script.c b/applications/main/bad_kb/helpers/ducky_script.c index 6b212a68d..71c307cdf 100644 --- a/applications/main/bad_kb/helpers/ducky_script.c +++ b/applications/main/bad_kb/helpers/ducky_script.c @@ -10,6 +10,7 @@ #include "ducky_script.h" #include "ducky_script_i.h" #include +#include #define TAG "BadKB" #define WORKER_TAG TAG "Worker" @@ -67,6 +68,7 @@ typedef enum { } WorkerEvtFlags; static const char ducky_cmd_id[] = {"ID"}; +static const char ducky_cmd_bt_id[] = {"BT_ID"}; static const uint8_t numpad_keys[10] = { HID_KEYPAD_0, @@ -327,6 +329,26 @@ static bool ducky_set_usb_id(BadKbScript* bad_kb, const char* line) { return false; } +static bool ducky_set_bt_id(BadKbScript* bad_kb, const char* line) { + size_t line_len = strlen(line); + size_t mac_len = BAD_KB_MAC_ADDRESS_LEN * 3; + 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++) { + 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])) { + return false; + } + } + + furi_hal_bt_set_profile_adv_name(FuriHalBtProfileHidKeyboard, line + mac_len); + bt_set_profile_mac_address(bad_kb->bt, mac); + return true; +} + static bool ducky_script_preload(BadKbScript* bad_kb, File* script_file) { uint8_t ret = 0; uint32_t line_len = 0; @@ -354,18 +376,48 @@ static bool ducky_script_preload(BadKbScript* bad_kb, File* script_file) { } } while(ret > 0); - if(!bad_kb->bt) { - const char* line_tmp = furi_string_get_cstr(bad_kb->line); - bool id_set = false; // Looking for ID command at first line - if(strncmp(line_tmp, ducky_cmd_id, strlen(ducky_cmd_id)) == 0) { - id_set = ducky_set_usb_id(bad_kb, &line_tmp[strlen(ducky_cmd_id) + 1]); + 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 + bool reset_bt_id = !!bad_kb->bt; + if(strncmp(line_tmp, ducky_cmd_id, strlen(ducky_cmd_id)) == 0) { + 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; } - - if(id_set) { + if(ducky_set_usb_id(bad_kb, &line_tmp[strlen(ducky_cmd_id) + 1])) { furi_check(furi_hal_usb_set_config(&usb_hid, &bad_kb->hid_cfg)); } else { furi_check(furi_hal_usb_set_config(&usb_hid, NULL)); } + } else if(strncmp(line_tmp, ducky_cmd_bt_id, strlen(ducky_cmd_bt_id)) == 0) { + 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) { + reset_bt_id = !ducky_set_bt_id(bad_kb, &line_tmp[strlen(ducky_cmd_bt_id) + 1]); + } + } + if(reset_bt_id) { + furi_hal_bt_set_profile_adv_name(FuriHalBtProfileHidKeyboard, bad_kb->app->config.bt_name); + bt_set_profile_mac_address(bad_kb->bt, bad_kb->app->config.bt_mac); } storage_file_seek(script_file, 0, true); @@ -766,10 +818,11 @@ static void bad_kb_script_set_default_keyboard_layout(BadKbScript* bad_kb) { memcpy(bad_kb->layout, hid_asciimap, MIN(sizeof(hid_asciimap), sizeof(bad_kb->layout))); } -BadKbScript* bad_kb_script_open(FuriString* file_path, Bt* bt) { +BadKbScript* bad_kb_script_open(FuriString* file_path, Bt* bt, BadKbApp* app) { furi_assert(file_path); BadKbScript* bad_kb = malloc(sizeof(BadKbScript)); + bad_kb->app = app; bad_kb->file_path = furi_string_alloc(); furi_string_set(bad_kb->file_path, file_path); bad_kb->keyboard_layout = furi_string_alloc(); diff --git a/applications/main/bad_kb/helpers/ducky_script.h b/applications/main/bad_kb/helpers/ducky_script.h index 6f5e03e0b..6a0386cf1 100644 --- a/applications/main/bad_kb/helpers/ducky_script.h +++ b/applications/main/bad_kb/helpers/ducky_script.h @@ -8,6 +8,13 @@ extern "C" { #include #include +#include +#include +#include +#include +#include +#include "../views/bad_kb_view.h" + #define FILE_BUFFER_LEN 16 typedef enum { @@ -38,7 +45,7 @@ typedef enum { BadKbStateFileError, } BadKbWorkerState; -typedef struct { +struct BadKbState { BadKbWorkerState state; bool is_bt; uint32_t pin; @@ -47,7 +54,9 @@ typedef struct { uint32_t delay_remain; uint16_t error_line; char error[64]; -} BadKbState; +}; + +typedef struct BadKbApp BadKbApp; typedef struct { FuriHalUsbHidConfig hid_cfg; @@ -74,9 +83,10 @@ typedef struct { size_t string_print_pos; Bt* bt; + BadKbApp* app; } BadKbScript; -BadKbScript* bad_kb_script_open(FuriString* file_path, Bt* bt); +BadKbScript* bad_kb_script_open(FuriString* file_path, Bt* bt, BadKbApp* app); void bad_kb_script_close(BadKbScript* bad_kb); @@ -90,6 +100,49 @@ void bad_kb_script_toggle(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 + +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; +} BadKbConfig; + +struct BadKbApp { + Gui* gui; + ViewDispatcher* view_dispatcher; + SceneManager* scene_manager; + NotificationApp* notifications; + DialogsApp* dialogs; + Widget* widget; + VariableItemList* var_item_list; + TextInput* text_input; + ByteInput* byte_input; + + BadKbAppError error; + FuriString* file_path; + FuriString* keyboard_layout; + BadKb* bad_kb_view; + BadKbScript* bad_kb_script; + + Bt* bt; + bool is_bt; + bool bt_remember; + BadKbConfig config; + BadKbConfig prev_config; + FuriThread* conn_init_thread; + FuriThread* switch_mode_thread; +}; + +int32_t bad_kb_config_switch_mode(BadKbApp* app); + #ifdef __cplusplus } #endif diff --git a/applications/main/bad_kb/helpers/ducky_script_commands.c b/applications/main/bad_kb/helpers/ducky_script_commands.c index ef8da0b8a..f23ab2f8a 100644 --- a/applications/main/bad_kb/helpers/ducky_script_commands.c +++ b/applications/main/bad_kb/helpers/ducky_script_commands.c @@ -169,22 +169,22 @@ static int32_t ducky_fnc_waitforbutton(BadKbScript* bad_kb, const char* line, in } static const DuckyCmd ducky_commands[] = { - {"REM ", NULL, -1}, - {"ID ", NULL, -1}, - {"DELAY ", ducky_fnc_delay, -1}, - {"STRING ", ducky_fnc_string, 0}, - {"STRINGLN ", ducky_fnc_string, 1}, - {"DEFAULT_DELAY ", ducky_fnc_defdelay, -1}, - {"DEFAULTDELAY ", ducky_fnc_defdelay, -1}, - {"STRINGDELAY ", ducky_fnc_strdelay, -1}, - {"STRING_DELAY ", ducky_fnc_strdelay, -1}, - {"REPEAT ", ducky_fnc_repeat, -1}, - {"SYSRQ ", ducky_fnc_sysrq, -1}, - {"ALTCHAR ", ducky_fnc_altchar, -1}, - {"ALTSTRING ", ducky_fnc_altstring, -1}, - {"ALTCODE ", ducky_fnc_altstring, -1}, - {"HOLD ", ducky_fnc_hold, -1}, - {"RELEASE ", ducky_fnc_release, -1}, + {"REM", NULL, -1}, + {"ID", NULL, -1}, + {"DELAY", ducky_fnc_delay, -1}, + {"STRING", ducky_fnc_string, 0}, + {"STRINGLN", ducky_fnc_string, 1}, + {"DEFAULT_DELAY", ducky_fnc_defdelay, -1}, + {"DEFAULTDELAY", ducky_fnc_defdelay, -1}, + {"STRINGDELAY", ducky_fnc_strdelay, -1}, + {"STRING_DELAY", ducky_fnc_strdelay, -1}, + {"REPEAT", ducky_fnc_repeat, -1}, + {"SYSRQ", ducky_fnc_sysrq, -1}, + {"ALTCHAR", ducky_fnc_altchar, -1}, + {"ALTSTRING", ducky_fnc_altstring, -1}, + {"ALTCODE", ducky_fnc_altstring, -1}, + {"HOLD", ducky_fnc_hold, -1}, + {"RELEASE", ducky_fnc_release, -1}, {"WAIT_FOR_BUTTON_PRESS", ducky_fnc_waitforbutton, -1}, }; @@ -192,8 +192,15 @@ static const DuckyCmd ducky_commands[] = { #define WORKER_TAG TAG "Worker" int32_t ducky_execute_cmd(BadKbScript* bad_kb, const char* line) { + size_t cmd_word_len = strcspn(line, " "); for(size_t i = 0; i < COUNT_OF(ducky_commands); i++) { - if(strncmp(line, ducky_commands[i].name, strlen(ducky_commands[i].name)) == 0) { + size_t cmd_compare_len = strlen(ducky_commands[i].name); + + if(cmd_compare_len != cmd_word_len) { + continue; + } + + if(strncmp(line, ducky_commands[i].name, cmd_compare_len) == 0) { if(ducky_commands[i].callback == NULL) { return 0; } else { 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 efdbf1ce4..3f16a4c48 100644 --- a/applications/main/bad_kb/scenes/bad_kb_scene_config.c +++ b/applications/main/bad_kb/scenes/bad_kb_scene_config.c @@ -1,7 +1,8 @@ #include "../bad_kb_app.h" +#include "../helpers/ducky_script.h" #include "furi_hal_power.h" #include "furi_hal_usb.h" -#include +#include enum VarItemListIndex { VarItemListIndexKeyboardLayout, @@ -9,13 +10,12 @@ enum VarItemListIndex { VarItemListIndexBtRemember, VarItemListIndexBtDeviceName, VarItemListIndexBtMacAddress, + VarItemListIndexRandomizeBtMac, }; void bad_kb_scene_config_connection_callback(VariableItem* item) { BadKbApp* bad_kb = variable_item_get_context(item); bad_kb->is_bt = variable_item_get_current_value_index(item); - XTREME_SETTINGS()->bad_bt = bad_kb->is_bt; - XTREME_SETTINGS_SAVE(); variable_item_set_current_value_text(item, bad_kb->is_bt ? "BT" : "USB"); view_dispatcher_send_custom_event(bad_kb->view_dispatcher, VarItemListIndexConnection); } @@ -52,10 +52,17 @@ void bad_kb_scene_config_on_enter(void* context) { variable_item_set_current_value_text(item, bad_kb->bt_remember ? "ON" : "OFF"); variable_item_set_locked(item, !bad_kb->is_bt, "Only in\nBT mode!"); - item = variable_item_list_add(var_item_list, "BT device name", 0, NULL, bad_kb); + item = variable_item_list_add(var_item_list, "BT Device Name", 0, NULL, bad_kb); variable_item_set_locked(item, !bad_kb->is_bt, "Only in\nBT mode!"); - item = variable_item_list_add(var_item_list, "BT MAC address", 0, NULL, bad_kb); + item = variable_item_list_add(var_item_list, "BT MAC Address", 0, NULL, bad_kb); + if(!bad_kb->is_bt) { + variable_item_set_locked(item, true, "Only in\nBT mode!"); + } else if(bad_kb->bt_remember) { + variable_item_set_locked(item, true, "Remember\nmust be Off!"); + } + + item = variable_item_list_add(var_item_list, "Randomize BT MAC", 0, NULL, bad_kb); if(!bad_kb->is_bt) { variable_item_set_locked(item, true, "Only in\nBT mode!"); } else if(bad_kb->bt_remember) { @@ -96,6 +103,10 @@ bool bad_kb_scene_config_on_event(void* context, SceneManagerEvent event) { 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); + break; default: break; } 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 f9bdc46d4..3d29467ae 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,5 @@ #include "../bad_kb_app.h" -#include "xtreme/assets.h" +#include static void bad_kb_scene_error_event_callback(GuiButtonType result, InputType type, void* context) { 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 d14624447..3a8c0748d 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 @@ -29,7 +29,7 @@ void bad_kb_scene_file_select_on_enter(void* context) { if(bad_kb_file_select(bad_kb)) { bad_kb->bad_kb_script = - bad_kb_script_open(bad_kb->file_path, bad_kb->is_bt ? bad_kb->bt : NULL); + bad_kb_script_open(bad_kb->file_path, bad_kb->is_bt ? bad_kb->bt : NULL, bad_kb); bad_kb_script_set_keyboard_layout(bad_kb->bad_kb_script, bad_kb->keyboard_layout); scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneWork); diff --git a/applications/main/bad_kb/views/bad_kb_view.c b/applications/main/bad_kb/views/bad_kb_view.c index 4cba891f1..95ffe3e4a 100644 --- a/applications/main/bad_kb/views/bad_kb_view.c +++ b/applications/main/bad_kb/views/bad_kb_view.c @@ -4,16 +4,10 @@ #include #include #include -#include "xtreme/assets.h" +#include #define MAX_NAME_LEN 64 -struct BadKb { - View* view; - BadKbButtonCallback callback; - void* context; -}; - typedef struct { char file_name[MAX_NAME_LEN]; char layout[MAX_NAME_LEN]; diff --git a/applications/main/bad_kb/views/bad_kb_view.h b/applications/main/bad_kb/views/bad_kb_view.h index d8f2559bb..797fafb69 100644 --- a/applications/main/bad_kb/views/bad_kb_view.h +++ b/applications/main/bad_kb/views/bad_kb_view.h @@ -1,11 +1,17 @@ #pragma once #include -#include "../helpers/ducky_script.h" -typedef struct BadKb BadKb; typedef void (*BadKbButtonCallback)(InputKey key, void* context); +typedef struct { + View* view; + BadKbButtonCallback callback; + void* context; +} BadKb; + +typedef struct BadKbState BadKbState; + BadKb* bad_kb_alloc(); void bad_kb_free(BadKb* bad_kb); diff --git a/applications/main/fap_loader/fap_loader_app.c b/applications/main/fap_loader/fap_loader_app.c index 391efa80c..2a06e348c 100644 --- a/applications/main/fap_loader/fap_loader_app.c +++ b/applications/main/fap_loader/fap_loader_app.c @@ -1,6 +1,7 @@ #include "fap_loader_app.h" #include +#include #include #include @@ -11,6 +12,7 @@ #include #include #include +#include #define TAG "FapLoader" @@ -24,8 +26,6 @@ struct FapLoader { Loading* loading; }; -volatile bool fap_loader_debug_active = false; - bool fap_loader_load_name_and_icon( FuriString* path, Storage* storage, @@ -154,7 +154,7 @@ static bool fap_loader_run_selected_app(FapLoader* loader, bool ignore_mismatch) FuriThread* thread = flipper_application_spawn(loader->app, NULL); /* This flag is set by the debugger - to break on app start */ - if(fap_loader_debug_active) { + 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 */ @@ -242,8 +242,9 @@ static void fap_loader_free(FapLoader* loader) { free(loader); } -int32_t fap_loader_app(void* p) { +int32_t fap_loader_app(char* p) { FapLoader* loader; + process_favorite_launch(&p); if(p) { loader = fap_loader_alloc((const char*)p); view_dispatcher_switch_to_view(loader->view_dispatcher, 0); @@ -257,7 +258,7 @@ int32_t fap_loader_app(void* p) { if(fap_loader_run_selected_app(loader, false)) { fap_loader_run_selected_app(loader, true); } - }; + } } fap_loader_free(loader); diff --git a/applications/main/ibutton/ibutton.c b/applications/main/ibutton/ibutton.c index 79999adb2..3fea2d2f6 100644 --- a/applications/main/ibutton/ibutton.c +++ b/applications/main/ibutton/ibutton.c @@ -2,6 +2,7 @@ #include #include +#include #define TAG "iButtonApp" @@ -258,13 +259,14 @@ void ibutton_widget_callback(GuiButtonType result, InputType type, void* context } } -int32_t ibutton_app(void* arg) { +int32_t ibutton_app(char* arg) { iButton* ibutton = ibutton_alloc(); ibutton_make_app_folder(ibutton); bool key_loaded = false; + bool is_favorite = process_favorite_launch(&arg); if((arg != NULL) && (strlen(arg) != 0)) { if(sscanf(arg, "RPC %lX", (uint32_t*)&ibutton->rpc) == 1) { FURI_LOG_D(TAG, "Running in RPC mode"); @@ -295,7 +297,11 @@ int32_t ibutton_app(void* arg) { } } - view_dispatcher_run(ibutton->view_dispatcher); + if(is_favorite) { + favorite_timeout_run(ibutton->view_dispatcher, ibutton->scene_manager); + } else { + view_dispatcher_run(ibutton->view_dispatcher); + } if(ibutton->rpc) { rpc_system_app_set_callback(ibutton->rpc, NULL, NULL); diff --git a/applications/main/ibutton/ibutton_cli.c b/applications/main/ibutton/ibutton_cli.c index 54bc808b5..12da64fcc 100644 --- a/applications/main/ibutton/ibutton_cli.c +++ b/applications/main/ibutton/ibutton_cli.c @@ -31,7 +31,7 @@ static void ibutton_cli_print_usage() { printf("\tCyfral (2 bytes key_data)\r\n"); printf("\tMetakom (4 bytes key_data), must contain correct parity\r\n"); printf("\t are hex-formatted\r\n"); -}; +} static bool ibutton_cli_parse_key(iButtonProtocols* protocols, iButtonKey* key, FuriString* args) { bool result = false; @@ -124,7 +124,7 @@ static void ibutton_cli_read(Cli* cli) { ibutton_protocols_free(protocols); furi_event_flag_free(event); -}; +} typedef struct { FuriEventFlag* event; @@ -216,7 +216,7 @@ void ibutton_cli_emulate(Cli* cli, FuriString* args) { while(!cli_cmd_interrupt_received(cli)) { furi_delay_ms(100); - }; + } } while(false); @@ -226,7 +226,7 @@ void ibutton_cli_emulate(Cli* cli, FuriString* args) { ibutton_key_free(key); ibutton_worker_free(worker); ibutton_protocols_free(protocols); -}; +} void ibutton_cli(Cli* cli, FuriString* args, void* context) { UNUSED(cli); diff --git a/applications/main/ibutton/scenes/ibutton_scene_delete_success.c b/applications/main/ibutton/scenes/ibutton_scene_delete_success.c index e2b7e3837..639ddbc16 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_delete_success.c +++ b/applications/main/ibutton/scenes/ibutton_scene_delete_success.c @@ -1,5 +1,5 @@ #include "../ibutton_i.h" -#include "xtreme/assets.h" +#include static void ibutton_scene_delete_success_popup_callback(void* context) { iButton* ibutton = context; diff --git a/applications/main/ibutton/scenes/ibutton_scene_read.c b/applications/main/ibutton/scenes/ibutton_scene_read.c index a2b3b53e4..069ce6ae1 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_read.c +++ b/applications/main/ibutton/scenes/ibutton_scene_read.c @@ -1,6 +1,6 @@ #include "../ibutton_i.h" #include -#include "xtreme/assets.h" +#include static void ibutton_scene_read_callback(void* context) { iButton* ibutton = context; diff --git a/applications/main/ibutton/scenes/ibutton_scene_save_success.c b/applications/main/ibutton/scenes/ibutton_scene_save_success.c index 03e88e047..81ae03059 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_save_success.c +++ b/applications/main/ibutton/scenes/ibutton_scene_save_success.c @@ -1,5 +1,5 @@ #include "../ibutton_i.h" -#include "xtreme/assets.h" +#include static void ibutton_scene_save_success_popup_callback(void* context) { iButton* ibutton = context; diff --git a/applications/main/ibutton/scenes/ibutton_scene_write_success.c b/applications/main/ibutton/scenes/ibutton_scene_write_success.c index 3f565e274..cef700fe7 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_write_success.c +++ b/applications/main/ibutton/scenes/ibutton_scene_write_success.c @@ -1,5 +1,5 @@ #include "../ibutton_i.h" -#include "xtreme/assets.h" +#include static void ibutton_scene_write_success_popup_callback(void* context) { iButton* ibutton = context; diff --git a/applications/main/infrared/infrared.c b/applications/main/infrared/infrared.c index a88306cc5..0abd6c709 100644 --- a/applications/main/infrared/infrared.c +++ b/applications/main/infrared/infrared.c @@ -2,6 +2,7 @@ #include #include +#include #define INFRARED_TX_MIN_INTERVAL_MS 50U @@ -435,7 +436,7 @@ void infrared_popup_closed_callback(void* context) { infrared->view_dispatcher, InfraredCustomEventTypePopupClosed); } -int32_t infrared_app(void* p) { +int32_t infrared_app(char* p) { Infrared* infrared = infrared_alloc(); infrared_make_app_folder(infrared); @@ -443,6 +444,7 @@ int32_t infrared_app(void* 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) { @@ -478,6 +480,8 @@ int32_t infrared_app(void* p) { view_dispatcher_run(infrared->view_dispatcher); + furi_hal_power_disable_otg(); + infrared_free(infrared); return 0; } diff --git a/applications/main/infrared/scenes/infrared_scene_debug_settings.c b/applications/main/infrared/scenes/infrared_scene_debug_settings.c index 0bc830788..28ec7dfdc 100644 --- a/applications/main/infrared/scenes/infrared_scene_debug_settings.c +++ b/applications/main/infrared/scenes/infrared_scene_debug_settings.c @@ -18,6 +18,20 @@ static void infrared_scene_debug_settings_changed(VariableItem* item) { furi_hal_infrared_set_debug_out(value_index_ir); } + +static void infrared_scene_debug_settings_power_changed(VariableItem* item) { + bool value = variable_item_get_current_value_index(item); + if(value) { + for(int i = 0; i < 5 && !furi_hal_power_is_otg_enabled(); i++) { + furi_hal_power_enable_otg(); + furi_delay_ms(10); + } + } else { + furi_hal_power_disable_otg(); + } + variable_item_set_current_value_text(item, value ? "ON" : "OFF"); +} + static void infrared_debug_settings_start_var_list_enter_callback(void* context, uint32_t index) { Infrared* infrared = context; view_dispatcher_send_custom_event(infrared->view_dispatcher, index); @@ -42,6 +56,16 @@ void infrared_scene_debug_settings_on_enter(void* context) { variable_item_set_current_value_index(item, value_index_ir); variable_item_set_current_value_text(item, infrared_debug_cfg_variables_text[value_index_ir]); + item = variable_item_list_add( + variable_item_list, + "Ext Module 5v", + 2, + infrared_scene_debug_settings_power_changed, + infrared); + bool enabled = furi_hal_power_is_otg_enabled(); + variable_item_set_current_value_index(item, enabled); + variable_item_set_current_value_text(item, enabled ? "ON" : "OFF"); + view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewVariableItemList); } diff --git a/applications/main/infrared/scenes/infrared_scene_edit_rename_done.c b/applications/main/infrared/scenes/infrared_scene_edit_rename_done.c index 36224f418..76e555c7d 100644 --- a/applications/main/infrared/scenes/infrared_scene_edit_rename_done.c +++ b/applications/main/infrared/scenes/infrared_scene_edit_rename_done.c @@ -1,5 +1,5 @@ #include "../infrared_i.h" -#include "xtreme/assets.h" +#include void infrared_scene_edit_rename_done_on_enter(void* context) { Infrared* infrared = context; diff --git a/applications/main/infrared/scenes/infrared_scene_learn_done.c b/applications/main/infrared/scenes/infrared_scene_learn_done.c index 0d2522946..97caa20db 100644 --- a/applications/main/infrared/scenes/infrared_scene_learn_done.c +++ b/applications/main/infrared/scenes/infrared_scene_learn_done.c @@ -1,5 +1,5 @@ #include "../infrared_i.h" -#include "xtreme/assets.h" +#include void infrared_scene_learn_done_on_enter(void* context) { Infrared* infrared = context; diff --git a/applications/main/infrared/scenes/infrared_scene_learn_success.c b/applications/main/infrared/scenes/infrared_scene_learn_success.c index bbc84ba3b..e646e7677 100644 --- a/applications/main/infrared/scenes/infrared_scene_learn_success.c +++ b/applications/main/infrared/scenes/infrared_scene_learn_success.c @@ -1,5 +1,5 @@ #include "../infrared_i.h" -#include "xtreme/assets.h" +#include static void infrared_scene_learn_success_dialog_result_callback(DialogExResult result, void* context) { diff --git a/applications/main/lfrfid/lfrfid.c b/applications/main/lfrfid/lfrfid.c index 85a00eea0..eacb05614 100644 --- a/applications/main/lfrfid/lfrfid.c +++ b/applications/main/lfrfid/lfrfid.c @@ -1,5 +1,6 @@ #include "lfrfid_i.h" #include +#include static bool lfrfid_debug_custom_event_callback(void* context, uint32_t event) { furi_assert(context); @@ -168,12 +169,12 @@ static void lfrfid_free(LfRfid* lfrfid) { free(lfrfid); } -int32_t lfrfid_app(void* p) { +int32_t lfrfid_app(char* args) { LfRfid* app = lfrfid_alloc(); - char* args = p; lfrfid_make_app_folder(app); + bool is_favorite = process_favorite_launch(&args); if(args && strlen(args)) { uint32_t rpc_ctx_ptr = 0; if(sscanf(args, "RPC %lX", &rpc_ctx_ptr) == 1) { @@ -199,7 +200,11 @@ int32_t lfrfid_app(void* p) { scene_manager_next_scene(app->scene_manager, LfRfidSceneStart); } - view_dispatcher_run(app->view_dispatcher); + if(is_favorite) { + favorite_timeout_run(app->view_dispatcher, app->scene_manager); + } else { + view_dispatcher_run(app->view_dispatcher); + } lfrfid_free(app); @@ -243,6 +248,27 @@ bool lfrfid_load_key_from_file_select(LfRfid* app) { return result; } +bool lfrfid_load_raw_key_from_file_select(LfRfid* app) { + furi_assert(app); + + DialogsFileBrowserOptions browser_options; + dialog_file_browser_set_basic_options( + &browser_options, LFRFID_APP_RAW_ASK_EXTENSION, &I_125_10px); + browser_options.base_path = LFRFID_APP_FOLDER; + + // Input events and views are managed by file_browser + bool result = + dialog_file_browser_show(app->dialogs, app->file_path, app->file_path, &browser_options); + + if(result) { + // Extract .raw and then .ask + path_extract_filename(app->file_path, app->file_name, true); + path_extract_filename(app->file_name, app->file_name, true); + } + + return result; +} + bool lfrfid_delete_key(LfRfid* app) { furi_assert(app); diff --git a/applications/main/lfrfid/lfrfid_cli.c b/applications/main/lfrfid/lfrfid_cli.c index a57e40de9..cc1fa2bd7 100644 --- a/applications/main/lfrfid/lfrfid_cli.c +++ b/applications/main/lfrfid/lfrfid_cli.c @@ -30,7 +30,7 @@ static void lfrfid_cli_print_usage() { printf("rfid raw_read \r\n"); printf("rfid raw_emulate \r\n"); printf("rfid raw_analyze \r\n"); -}; +} typedef struct { ProtocolId protocol; diff --git a/applications/main/lfrfid/lfrfid_i.h b/applications/main/lfrfid/lfrfid_i.h index 72b061930..86929a14a 100644 --- a/applications/main/lfrfid/lfrfid_i.h +++ b/applications/main/lfrfid/lfrfid_i.h @@ -125,6 +125,8 @@ bool lfrfid_save_key(LfRfid* app); bool lfrfid_load_key_from_file_select(LfRfid* app); +bool lfrfid_load_raw_key_from_file_select(LfRfid* app); + bool lfrfid_delete_key(LfRfid* app); bool lfrfid_load_key_data(LfRfid* app, FuriString* path, bool show_dialog); diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_clear_t5577.c b/applications/main/lfrfid/scenes/lfrfid_scene_clear_t5577.c index a950de708..6d960f331 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_clear_t5577.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_clear_t5577.c @@ -1,6 +1,6 @@ #include "../lfrfid_i.h" #include "../helpers/rfid_writer.h" -#include "xtreme/assets.h" +#include static void writer_initialize(T55xxTiming* t55xxtiming) { t55xxtiming->wait_time = 400; @@ -85,10 +85,11 @@ bool lfrfid_scene_clear_t5577_on_event(void* context, SceneManagerEvent event) { LfRfid* app = context; bool consumed = false; - const uint32_t prev_scene = LfRfidSceneExtraActions; - - if(event.type == SceneManagerEventTypeCustom && event.event == LfRfidEventPopupClosed) { - scene_manager_search_and_switch_to_previous_scene(app->scene_manager, prev_scene); + if(event.type == SceneManagerEventTypeBack) { + consumed = true; // Ignore Back button presses + } else if(event.type == SceneManagerEventTypeCustom && event.event == LfRfidEventPopupClosed) { + scene_manager_search_and_switch_to_previous_scene( + app->scene_manager, LfRfidSceneExtraActions); consumed = true; } return consumed; diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_clear_t5577_confirm.c b/applications/main/lfrfid/scenes/lfrfid_scene_clear_t5577_confirm.c new file mode 100644 index 000000000..ca0d43176 --- /dev/null +++ b/applications/main/lfrfid/scenes/lfrfid_scene_clear_t5577_confirm.c @@ -0,0 +1,46 @@ +#include "../lfrfid_i.h" + +void lfrfid_scene_clear_t5577_confirm_on_enter(void* context) { + LfRfid* app = context; + Widget* widget = app->widget; + + widget_add_button_element(widget, GuiButtonTypeLeft, "Exit", lfrfid_widget_callback, app); + widget_add_button_element(widget, GuiButtonTypeRight, "Start", lfrfid_widget_callback, app); + widget_add_string_multiline_element( + widget, 64, 22, AlignCenter, AlignBottom, FontPrimary, "Apply tag to\nFlipper's back"); + widget_add_string_multiline_element( + widget, + 64, + 45, + AlignCenter, + AlignBottom, + FontSecondary, + "And don't move it\nwhile process is running"); + + view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewWidget); +} + +bool lfrfid_scene_clear_t5577_confirm_on_event(void* context, SceneManagerEvent event) { + LfRfid* app = context; + SceneManager* scene_manager = app->scene_manager; + bool consumed = false; + + if(event.type == SceneManagerEventTypeBack) { + consumed = true; // Ignore Back button presses + } else if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + if(event.event == GuiButtonTypeLeft) { + scene_manager_search_and_switch_to_previous_scene( + scene_manager, LfRfidSceneExtraActions); + } else if(event.event == GuiButtonTypeRight) { + scene_manager_next_scene(scene_manager, LfRfidSceneClearT5577); + } + } + + return consumed; +} + +void lfrfid_scene_clear_t5577_confirm_on_exit(void* context) { + LfRfid* app = context; + widget_reset(app->widget); +} diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_config.h b/applications/main/lfrfid/scenes/lfrfid_scene_config.h index 10dc46fe6..7789e133e 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_config.h +++ b/applications/main/lfrfid/scenes/lfrfid_scene_config.h @@ -16,10 +16,13 @@ ADD_SCENE(lfrfid, save_data, SaveData) ADD_SCENE(lfrfid, save_type, SaveType) ADD_SCENE(lfrfid, saved_info, SavedInfo) ADD_SCENE(lfrfid, clear_t5577, ClearT5577) +ADD_SCENE(lfrfid, clear_t5577_confirm, ClearT5577Confirm) ADD_SCENE(lfrfid, delete_success, DeleteSuccess) ADD_SCENE(lfrfid, extra_actions, ExtraActions) ADD_SCENE(lfrfid, raw_info, RawInfo) ADD_SCENE(lfrfid, raw_name, RawName) ADD_SCENE(lfrfid, raw_read, RawRead) ADD_SCENE(lfrfid, raw_success, RawSuccess) +ADD_SCENE(lfrfid, raw_emulate, RawEmulate) +ADD_SCENE(lfrfid, select_raw_key, SelectRawKey) ADD_SCENE(lfrfid, rpc, Rpc) diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_delete_success.c b/applications/main/lfrfid/scenes/lfrfid_scene_delete_success.c index 991748515..d3952c08c 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_delete_success.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_delete_success.c @@ -1,5 +1,5 @@ #include "../lfrfid_i.h" -#include "xtreme/assets.h" +#include void lfrfid_scene_delete_success_on_enter(void* context) { LfRfid* app = context; diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_emulate.c b/applications/main/lfrfid/scenes/lfrfid_scene_emulate.c index eff92dc37..b2a8be4b8 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_emulate.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_emulate.c @@ -1,5 +1,5 @@ #include "../lfrfid_i.h" -#include "xtreme/assets.h" +#include void lfrfid_scene_emulate_on_enter(void* context) { LfRfid* app = context; diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c b/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c index d322b2d01..8a71b023a 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c @@ -6,6 +6,7 @@ typedef enum { SubmenuIndexPSK, SubmenuIndexClearT5577, SubmenuIndexRAW, + SubmenuIndexRAWEmulate, } SubmenuIndex; static void lfrfid_scene_extra_actions_submenu_callback(void* context, uint32_t index) { @@ -45,6 +46,14 @@ void lfrfid_scene_extra_actions_on_enter(void* context) { app, !furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug), "Enable\nDebug!"); + submenu_add_lockable_item( + submenu, + "Emulate RAW RFID data", + SubmenuIndexRAWEmulate, + lfrfid_scene_extra_actions_submenu_callback, + app, + !furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug), + "Enable\nDebug!"); submenu_set_selected_item( submenu, scene_manager_get_scene_state(app->scene_manager, LfRfidSceneExtraActions)); @@ -73,11 +82,14 @@ bool lfrfid_scene_extra_actions_on_event(void* context, SceneManagerEvent event) DOLPHIN_DEED(DolphinDeedRfidRead); consumed = true; } else if(event.event == SubmenuIndexClearT5577) { - scene_manager_next_scene(app->scene_manager, LfRfidSceneClearT5577); + scene_manager_next_scene(app->scene_manager, LfRfidSceneClearT5577Confirm); consumed = true; } else if(event.event == SubmenuIndexRAW) { scene_manager_next_scene(app->scene_manager, LfRfidSceneRawName); consumed = true; + } else if(event.event == SubmenuIndexRAWEmulate) { + scene_manager_next_scene(app->scene_manager, LfRfidSceneSelectRawKey); + consumed = true; } scene_manager_set_scene_state(app->scene_manager, LfRfidSceneExtraActions, event.event); } diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_raw_emulate.c b/applications/main/lfrfid/scenes/lfrfid_scene_raw_emulate.c new file mode 100644 index 000000000..c06a5eebf --- /dev/null +++ b/applications/main/lfrfid/scenes/lfrfid_scene_raw_emulate.c @@ -0,0 +1,86 @@ +#include "../lfrfid_i.h" + +#define TAG "ADC" + +typedef struct { + FuriString* string_file_name; + bool error; +} LfRfidEmulateRawState; + +static void lfrfid_raw_emulate_callback(LFRFIDWorkerEmulateRawResult result, void* context) { + LfRfid* app = context; + + if(result == LFRFIDWorkerEmulateRawFileError) { + view_dispatcher_send_custom_event(app->view_dispatcher, LfRfidEventReadError); + } else if(result == LFRFIDWorkerEmulateRawOverrun) { + view_dispatcher_send_custom_event(app->view_dispatcher, LfRfidEventReadOverrun); + } +} + +void lfrfid_scene_raw_emulate_on_enter(void* context) { + LfRfid* app = context; + Popup* popup = app->popup; + + LfRfidEmulateRawState* state = malloc(sizeof(LfRfidEmulateRawState)); + scene_manager_set_scene_state(app->scene_manager, LfRfidSceneRawEmulate, (uint32_t)state); + state->string_file_name = furi_string_alloc(); + + popup_set_icon(popup, 0, 3, &I_RFIDDolphinReceive_97x61); + view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewPopup); + lfrfid_worker_start_thread(app->lfworker); + lfrfid_make_app_folder(app); + + furi_string_printf( + state->string_file_name, + "%s/%s%s", + LFRFID_SD_FOLDER, + furi_string_get_cstr(app->file_name), + LFRFID_APP_RAW_ASK_EXTENSION); + FURI_LOG_D(TAG, "raw_emulate->file_name=%s", furi_string_get_cstr(state->string_file_name)); + popup_set_header(popup, "Emulating\nRAW RFID\nASK", 89, 30, AlignCenter, AlignTop); + lfrfid_worker_emulate_raw_start( + app->lfworker, + furi_string_get_cstr(state->string_file_name), + lfrfid_raw_emulate_callback, + app); + + notification_message(app->notifications, &sequence_blink_start_cyan); + + state->error = false; +} + +bool lfrfid_scene_raw_emulate_on_event(void* context, SceneManagerEvent event) { + LfRfid* app = context; + Popup* popup = app->popup; + LfRfidEmulateRawState* state = (LfRfidEmulateRawState*)scene_manager_get_scene_state( + app->scene_manager, LfRfidSceneRawEmulate); + bool consumed = false; + + furi_assert(state); + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == LfRfidEventReadError) { + consumed = true; + state->error = true; + popup_set_header( + popup, "Reading\nRAW RFID\nFile error", 89, 30, AlignCenter, AlignTop); + notification_message(app->notifications, &sequence_blink_start_red); + } + } + + return consumed; +} + +void lfrfid_scene_raw_emulate_on_exit(void* context) { + LfRfid* app = context; + LfRfidEmulateRawState* state = (LfRfidEmulateRawState*)scene_manager_get_scene_state( + app->scene_manager, LfRfidSceneRawEmulate); + + notification_message(app->notifications, &sequence_blink_stop); + popup_reset(app->popup); + lfrfid_worker_stop(app->lfworker); + lfrfid_worker_stop_thread(app->lfworker); + + furi_string_free(state->string_file_name); + free(state); +} diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_raw_read.c b/applications/main/lfrfid/scenes/lfrfid_scene_raw_read.c index 3213297d7..b9615f39c 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_raw_read.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_raw_read.c @@ -1,5 +1,5 @@ #include "../lfrfid_i.h" -#include "xtreme/assets.h" +#include #define RAW_READ_TIME 5000 diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_rpc.c b/applications/main/lfrfid/scenes/lfrfid_scene_rpc.c index 181346e9d..1555154fb 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_rpc.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_rpc.c @@ -1,5 +1,5 @@ #include "../lfrfid_i.h" -#include "xtreme/assets.h" +#include void lfrfid_scene_rpc_on_enter(void* context) { LfRfid* app = context; diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_save_success.c b/applications/main/lfrfid/scenes/lfrfid_scene_save_success.c index 738e90bfd..612445125 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_save_success.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_save_success.c @@ -1,5 +1,5 @@ #include "../lfrfid_i.h" -#include "xtreme/assets.h" +#include void lfrfid_scene_save_success_on_enter(void* context) { LfRfid* app = context; diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_select_raw_key.c b/applications/main/lfrfid/scenes/lfrfid_scene_select_raw_key.c new file mode 100644 index 000000000..f12d280aa --- /dev/null +++ b/applications/main/lfrfid/scenes/lfrfid_scene_select_raw_key.c @@ -0,0 +1,23 @@ +#include "../lfrfid_i.h" + +void lfrfid_scene_select_raw_key_on_enter(void* context) { + LfRfid* app = context; + + if(lfrfid_load_raw_key_from_file_select(app)) { + scene_manager_next_scene(app->scene_manager, LfRfidSceneRawEmulate); + + } else { + scene_manager_previous_scene(app->scene_manager); + } +} + +bool lfrfid_scene_select_raw_key_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + bool consumed = false; + return consumed; +} + +void lfrfid_scene_select_raw_key_on_exit(void* context) { + UNUSED(context); +} diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_write.c b/applications/main/lfrfid/scenes/lfrfid_scene_write.c index 0f74ece45..61e92ac4f 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_write.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_write.c @@ -1,5 +1,5 @@ #include "../lfrfid_i.h" -#include "xtreme/assets.h" +#include static void lfrfid_write_callback(LFRFIDWorkerWriteResult result, void* context) { LfRfid* app = context; diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_write_success.c b/applications/main/lfrfid/scenes/lfrfid_scene_write_success.c index 5eeb88616..a5b90ec38 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_write_success.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_write_success.c @@ -1,5 +1,5 @@ #include "../lfrfid_i.h" -#include "xtreme/assets.h" +#include void lfrfid_scene_write_success_on_enter(void* context) { LfRfid* app = context; diff --git a/applications/main/nfc/nfc.c b/applications/main/nfc/nfc.c index f68b7f2f2..9ff82eaa4 100644 --- a/applications/main/nfc/nfc.c +++ b/applications/main/nfc/nfc.c @@ -1,6 +1,7 @@ #include "nfc_i.h" #include #include +#include bool nfc_custom_event_callback(void* context, uint32_t event) { furi_assert(context); @@ -263,14 +264,14 @@ static bool nfc_is_hal_ready() { } } -int32_t nfc_app(void* p) { +int32_t nfc_app(char* p) { if(!nfc_is_hal_ready()) return 0; Nfc* nfc = nfc_alloc(); - char* args = p; // Check argument and run corresponding scene - if(args && strlen(args)) { + bool is_favorite = process_favorite_launch(&p); + if(p && strlen(p)) { nfc_device_set_loading_callback(nfc->dev, nfc_show_loading_popup, nfc); uint32_t rpc_ctx = 0; if(sscanf(p, "RPC %lX", &rpc_ctx) == 1) { @@ -311,7 +312,11 @@ int32_t nfc_app(void* p) { scene_manager_next_scene(nfc->scene_manager, NfcSceneStart); } - view_dispatcher_run(nfc->view_dispatcher); + if(is_favorite) { + favorite_timeout_run(nfc->view_dispatcher, nfc->scene_manager); + } else { + view_dispatcher_run(nfc->view_dispatcher); + } nfc_free(nfc); diff --git a/applications/main/nfc/nfc_cli.c b/applications/main/nfc/nfc_cli.c index 6e14e371f..577b9adf1 100644 --- a/applications/main/nfc/nfc_cli.c +++ b/applications/main/nfc/nfc_cli.c @@ -46,7 +46,7 @@ static void nfc_cli_detect(Cli* cli, FuriString* args) { for(size_t i = 0; i < 8; i++) { printf("%02X", dev_data.uid[i]); } - printf("\r\nPMm:"); + printf(", PMm:"); for(size_t i = 0; i < 8; i++) { printf("%02X", dev_data.f_data.pmm[i]); } diff --git a/applications/main/nfc/scenes/nfc_scene_config.h b/applications/main/nfc/scenes/nfc_scene_config.h index c02e83787..deaaa4c21 100644 --- a/applications/main/nfc/scenes/nfc_scene_config.h +++ b/applications/main/nfc/scenes/nfc_scene_config.h @@ -20,6 +20,8 @@ ADD_SCENE(nfc, nfcv_key_input, NfcVKeyInput) ADD_SCENE(nfc, nfcv_unlock, NfcVUnlock) ADD_SCENE(nfc, nfcv_emulate, NfcVEmulate) ADD_SCENE(nfc, nfcv_sniff, NfcVSniff) +ADD_SCENE(nfc, nfcf_read_success, NfcfReadSuccess) +ADD_SCENE(nfc, nfcf_menu, NfcfMenu) ADD_SCENE(nfc, mf_ultralight_read_success, MfUltralightReadSuccess) ADD_SCENE(nfc, mf_ultralight_data, MfUltralightData) ADD_SCENE(nfc, mf_ultralight_menu, MfUltralightMenu) diff --git a/applications/main/nfc/scenes/nfc_scene_delete_success.c b/applications/main/nfc/scenes/nfc_scene_delete_success.c index d8e2652a9..07b637eb4 100644 --- a/applications/main/nfc/scenes/nfc_scene_delete_success.c +++ b/applications/main/nfc/scenes/nfc_scene_delete_success.c @@ -1,5 +1,5 @@ #include "../nfc_i.h" -#include "xtreme/assets.h" +#include void nfc_scene_delete_success_popup_callback(void* context) { Nfc* nfc = context; diff --git a/applications/main/nfc/scenes/nfc_scene_emulate_uid.c b/applications/main/nfc/scenes/nfc_scene_emulate_uid.c index 7c016ceda..c46a2a075 100644 --- a/applications/main/nfc/scenes/nfc_scene_emulate_uid.c +++ b/applications/main/nfc/scenes/nfc_scene_emulate_uid.c @@ -1,5 +1,5 @@ #include "../nfc_i.h" -#include "xtreme/assets.h" +#include #define NFC_SCENE_EMULATE_UID_LOG_SIZE_MAX (200) diff --git a/applications/main/nfc/scenes/nfc_scene_felica_info_select.c b/applications/main/nfc/scenes/nfc_scene_felica_info_select.c index 3b7c570d7..7a59521fa 100644 --- a/applications/main/nfc/scenes/nfc_scene_felica_info_select.c +++ b/applications/main/nfc/scenes/nfc_scene_felica_info_select.c @@ -19,24 +19,24 @@ void nfc_scene_felica_info_select_on_enter(void* context) { uint8_t i = 1; if(state->selected_system == NULL || state->selected_system->code == LITE_SYSTEM_CODE) { submenu_set_header(submenu, "Systems"); - FelicaSystemList_it_t it; - for(FelicaSystemList_it(it, data->systems); !FelicaSystemList_end_p(it); - FelicaSystemList_next(it)) { - FelicaSystem* current_system = *FelicaSystemList_ref(it); - FuriString* system_name = felica_get_system_name(current_system); - submenu_add_item( - submenu, - furi_string_get_cstr(system_name), - i++, - nfc_scene_felica_info_select_submenu_callback, - nfc); - furi_string_free(system_name); - } + for + M_EACH(current_system, data->systems, FelicaSystemArray_t) { + FuriString* system_name = felica_get_system_name(current_system); + submenu_add_item( + submenu, + furi_string_get_cstr(system_name), + i++, + nfc_scene_felica_info_select_submenu_callback, + nfc); + furi_string_free(system_name); + } } else { FelicaSystem* system = state->selected_system; FuriString* header = furi_string_alloc_printf("%04X/", system->code); - FelicaArea* area = &system->root_area; + FelicaNode* root = &system->root; + furi_assert(root->type == FelicaNodeTypeArea); + FelicaArea* area = root->area; if(FelicaAreaPath_size(state->selected_areas) > 0) { FelicaAreaPath_it_t it; for(FelicaAreaPath_it(it, state->selected_areas); !FelicaAreaPath_end_p(it); @@ -51,32 +51,30 @@ void nfc_scene_felica_info_select_on_enter(void* context) { submenu_set_header(submenu, furi_string_get_cstr(header)); furi_string_free(header); - FelicaNodeList_it_t it; - for(FelicaNodeList_it(it, area->nodes); !FelicaNodeList_end_p(it); - FelicaNodeList_next(it)) { - FelicaNode* node = *FelicaNodeList_ref(it); - FuriString* node_name = furi_string_alloc(); - if(node->type == FelicaNodeTypeArea) { - furi_string_printf(node_name, "Area %d", node->area->number); - submenu_add_item( - submenu, - furi_string_get_cstr(node_name), - i++, - nfc_scene_felica_info_select_submenu_callback, - nfc); - } else { - uint16_t service_code = node->service->number << 6; - furi_string_printf(node_name, "Service %04X", service_code); - submenu_add_item( - submenu, - furi_string_get_cstr(node_name), - i++, - nfc_scene_felica_info_select_submenu_callback, - nfc); - } + for + M_EACH(node, area->nodes, FelicaNodeArray_t) { + FuriString* node_name = furi_string_alloc(); + if(node->type == FelicaNodeTypeArea) { + furi_string_printf(node_name, "Area %d", node->area->number); + submenu_add_item( + submenu, + furi_string_get_cstr(node_name), + i++, + nfc_scene_felica_info_select_submenu_callback, + nfc); + } else { + uint16_t service_code = node->service->number << 6; + furi_string_printf(node_name, "Service %04X", service_code); + submenu_add_item( + submenu, + furi_string_get_cstr(node_name), + i++, + nfc_scene_felica_info_select_submenu_callback, + nfc); + } - furi_string_free(node_name); - } + furi_string_free(node_name); + } } state->selected_service = NULL; @@ -102,7 +100,7 @@ bool nfc_scene_felica_info_select_on_event(void* context, SceneManagerEvent even index -= 1; if(state->selected_system == NULL) { - state->selected_system = *FelicaSystemList_get(data->systems, index); + state->selected_system = FelicaSystemArray_get(data->systems, index); if(state->selected_system->code == LITE_SYSTEM_CODE) { scene_manager_next_scene(nfc->scene_manager, NfcSceneFelicaServiceData); } else { @@ -111,12 +109,15 @@ bool nfc_scene_felica_info_select_on_event(void* context, SceneManagerEvent even consumed = true; } else { FelicaNode* selected_node = NULL; + + FelicaNode* root = &(state->selected_system->root); + furi_assert(root->type == FelicaNodeTypeArea); + if(FelicaAreaPath_size(state->selected_areas) == 0) { - selected_node = - *FelicaNodeList_get(state->selected_system->root_area.nodes, index); + selected_node = FelicaNodeArray_get(root->area->nodes, index); } else { FelicaArea* current_area = *FelicaAreaPath_back(state->selected_areas); - selected_node = *FelicaNodeList_get(current_area->nodes, index); + selected_node = FelicaNodeArray_get(current_area->nodes, index); } if(selected_node->type == FelicaNodeTypeArea) { diff --git a/applications/main/nfc/scenes/nfc_scene_felica_read_success.c b/applications/main/nfc/scenes/nfc_scene_felica_read_success.c index 52bba0ee3..79dd82297 100644 --- a/applications/main/nfc/scenes/nfc_scene_felica_read_success.c +++ b/applications/main/nfc/scenes/nfc_scene_felica_read_success.c @@ -29,21 +29,19 @@ void nfc_scene_felica_read_success_on_enter(void* context) { } else { temp_str = furi_string_alloc_printf("\e#%s", nfc_felica_type(felica_data->type)); - FelicaSystemList_it_t it; - for(FelicaSystemList_it(it, felica_data->systems); !FelicaSystemList_end_p(it); - FelicaSystemList_next(it)) { - FelicaSystem* current_system = *FelicaSystemList_ref(it); - furi_string_cat_printf( - temp_str, "\nSystem %04X (#%d):", current_system->code, current_system->number); - furi_string_cat_printf(temp_str, "\nIDm:\n "); - for(size_t i = 0; i < 8; i++) { - furi_string_cat_printf(temp_str, "%02X", current_system->idm[i]); + for + M_EACH(current_system, felica_data->systems, FelicaSystemArray_t) { + furi_string_cat_printf( + temp_str, "\nSystem %04X (#%d):", current_system->code, current_system->number); + furi_string_cat_printf(temp_str, "\nIDm:\n "); + for(size_t i = 0; i < 8; i++) { + furi_string_cat_printf(temp_str, "%02X", current_system->idm[i]); + } + furi_string_cat_printf(temp_str, "\nPMm:\n "); + for(size_t i = 0; i < 8; i++) { + furi_string_cat_printf(temp_str, "%02X", current_system->pmm[i]); + } } - furi_string_cat_printf(temp_str, "\nPMm:\n "); - for(size_t i = 0; i < 8; i++) { - furi_string_cat_printf(temp_str, "%02X", current_system->pmm[i]); - } - } } widget_add_text_scroll_element(widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str)); diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_emulate.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_emulate.c index 7f5cac406..9caf0390b 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_emulate.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_emulate.c @@ -1,5 +1,5 @@ #include "../nfc_i.h" -#include "xtreme/assets.h" +#include #define NFC_MF_CLASSIC_DATA_NOT_CHANGED (0UL) #define NFC_MF_CLASSIC_DATA_CHANGED (1UL) 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 9544721b6..79256525f 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 @@ -1,6 +1,6 @@ #include "../nfc_i.h" #include -#include "xtreme/assets.h" +#include void nfc_scene_mf_classic_update_success_popup_callback(void* context) { Nfc* nfc = context; 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 e82dedeaf..97dc50167 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 @@ -1,6 +1,6 @@ #include "../nfc_i.h" #include -#include "xtreme/assets.h" +#include void nfc_scene_mf_classic_write_success_popup_callback(void* context) { Nfc* nfc = context; diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_emulate.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_emulate.c index f90d177e2..b13e169e4 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_emulate.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_emulate.c @@ -1,5 +1,5 @@ #include "../nfc_i.h" -#include "xtreme/assets.h" +#include #define NFC_MF_UL_DATA_NOT_CHANGED (0UL) #define NFC_MF_UL_DATA_CHANGED (1UL) 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 7f3a8907c..1a879d8a0 100644 --- a/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c +++ b/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c @@ -1,4 +1,5 @@ #include "../nfc_i.h" +#include void nfc_scene_nfc_data_info_widget_callback(GuiButtonType result, InputType type, void* context) { Nfc* nfc = context; @@ -22,6 +23,7 @@ void nfc_scene_nfc_data_info_on_enter(void* context) { Nfc* nfc = context; Widget* widget = nfc->widget; FuriHalNfcDevData* nfc_data = &nfc->dev->dev_data.nfc_data; + FuriHalNfcType type = nfc_data->type; NfcDeviceData* dev_data = &nfc->dev->dev_data; NfcProtocol protocol = dev_data->protocol; uint8_t text_scroll_height = 0; @@ -73,6 +75,8 @@ void nfc_scene_nfc_data_info_on_enter(void* context) { furi_string_cat_printf(temp_str, "\e#ISO15693 (unknown)\n"); break; } + } else if(protocol == NfcDeviceProtocolFelica) { + furi_string_cat_printf(temp_str, "\e#%s\n", nfc_felica_type(dev_data->felica_data.type)); } else { furi_string_cat_printf(temp_str, "\e#Unknown ISO tag\n"); } @@ -200,7 +204,63 @@ void nfc_scene_nfc_data_info_on_enter(void* context) { furi_string_cat_printf(temp_str, "\e#ISO15693 (unknown)\n"); break; } - } else { + // Set tag general data + } else if(type == FuriHalNfcTypeF) { + // Set NFC-F data + furi_string_cat_printf(temp_str, "ISO 18092 (NFC-F)\n"); + furi_string_cat_printf(temp_str, "CIN:"); + // NFC-F Card Identification Number (CIN) starts at "UID" byte 2. + for(size_t i = 2; i < nfc_data->uid_len; i++) { + furi_string_cat_printf(temp_str, " %02X", nfc_data->uid[i]); + } + // The first 2 bytes of the "UID" are Manufacturer Code (MC) + furi_string_cat_printf( + temp_str, + "\nMC: %02X %02X ROM: %02X IC: %02X\n\n", + nfc_data->uid[0], + nfc_data->uid[1], + nfc_data->f_data.pmm[0], + nfc_data->f_data.pmm[1]); + + furi_string_cat_printf(temp_str, "MRT (1 node/blk):\n"); + furi_string_cat_printf( + temp_str, + "- ReqSvc: %" PRIuLEAST32 "us\n", + felica_estimate_timing_us(nfc_data->f_data.pmm[FELICA_PMM_VARIABLE_MRT], 1)); + furi_string_cat_printf( + temp_str, + "- Fixed: %" PRIuLEAST32 "us\n", + felica_estimate_timing_us(nfc_data->f_data.pmm[FELICA_PMM_FIXED_MRT], 0)); + furi_string_cat_printf( + temp_str, + "- Auth1: %" PRIuLEAST32 "us\n", + felica_estimate_timing_us(nfc_data->f_data.pmm[FELICA_PMM_MUTUAL_AUTH_MRT], 1)); + furi_string_cat_printf( + temp_str, + "- Auth2: %" PRIuLEAST32 "us\n", + felica_estimate_timing_us(nfc_data->f_data.pmm[FELICA_PMM_MUTUAL_AUTH_MRT], 0)); + furi_string_cat_printf( + temp_str, + "- Read: %" PRIuLEAST32 "us\n", + felica_estimate_timing_us(nfc_data->f_data.pmm[FELICA_PMM_READ_MRT], 1)); + furi_string_cat_printf( + temp_str, + "- Write: %" PRIuLEAST32 "us\n", + felica_estimate_timing_us(nfc_data->f_data.pmm[FELICA_PMM_WRITE_MRT], 1)); + furi_string_cat_printf( + temp_str, + "- Other: %" PRIuLEAST32 "us\n\n", + felica_estimate_timing_us(nfc_data->f_data.pmm[FELICA_PMM_OTHER_MRT], 0)); + + furi_string_cat_printf(temp_str, "IDm:"); + 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, "\nPMm:"); + for(size_t i = 0; i < sizeof(nfc_data->f_data.pmm); i++) { + furi_string_cat_printf(temp_str, " %02X", nfc_data->f_data.pmm[i]); + } + } else { // FuriHalNfcTypeA char iso_type = FURI_BIT(nfc_data->a_data.sak, 5) ? '4' : '3'; furi_string_cat_printf(temp_str, "ISO 14443-%c (NFC-A)\n", iso_type); furi_string_cat_printf(temp_str, "UID:"); diff --git a/applications/main/nfc/scenes/nfc_scene_nfcf_menu.c b/applications/main/nfc/scenes/nfc_scene_nfcf_menu.c new file mode 100644 index 000000000..cae7055b1 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_nfcf_menu.c @@ -0,0 +1,80 @@ +#include "../nfc_i.h" +#include + +enum SubmenuIndex { + /* + SubmenuIndexSave, + SubmenuIndexEmulate, + */ + SubmenuIndexInfo, +}; + +void nfc_scene_nfcf_menu_submenu_callback(void* context, uint32_t index) { + Nfc* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, index); +} + +void nfc_scene_nfcf_menu_on_enter(void* context) { + Nfc* nfc = context; + Submenu* submenu = nfc->submenu; + // FelicaData* data = &nfc->dev->dev_data.felica_data; + + /* + submenu_add_item( + submenu, "Save IDm", SubmenuIndexSave, nfc_scene_felica_menu_submenu_callback, nfc); + submenu_add_item( + submenu, "Emulate IDm", SubmenuIndexEmulate, nfc_scene_felica_menu_submenu_callback, nfc); + */ + submenu_add_item(submenu, "Info", SubmenuIndexInfo, nfc_scene_nfcf_menu_submenu_callback, nfc); + + submenu_set_selected_item( + nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneNfcfMenu)); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); +} + +bool nfc_scene_nfcf_menu_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + 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); + } else { + 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) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcDataInfo); + consumed = true; + } + scene_manager_set_scene_state(nfc->scene_manager, NfcSceneNfcfMenu, event.event); + + } else if(event.type == SceneManagerEventTypeBack) { + consumed = scene_manager_previous_scene(nfc->scene_manager); + } + + return consumed; +} + +void nfc_scene_nfcf_menu_on_exit(void* context) { + Nfc* nfc = context; + + // Clear view + submenu_reset(nfc->submenu); +} diff --git a/applications/main/nfc/scenes/nfc_scene_nfcf_read_success.c b/applications/main/nfc/scenes/nfc_scene_nfcf_read_success.c new file mode 100644 index 000000000..da695ba83 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_nfcf_read_success.c @@ -0,0 +1,88 @@ +#include "../nfc_i.h" +#include "nfc_device.h" + +void nfc_scene_nfcf_read_success_widget_callback( + GuiButtonType result, + InputType type, + void* context) { + furi_assert(context); + Nfc* nfc = context; + + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(nfc->view_dispatcher, result); + } +} + +void nfc_scene_nfcf_read_success_on_enter(void* context) { + Nfc* nfc = context; + + FuriHalNfcDevData* data = &nfc->dev->dev_data.nfc_data; + NfcDeviceData* dev_data = &nfc->dev->dev_data; + + // Setup view + Widget* widget = nfc->widget; + widget_add_button_element( + widget, GuiButtonTypeLeft, "Retry", nfc_scene_nfcf_read_success_widget_callback, nfc); + widget_add_button_element( + widget, GuiButtonTypeRight, "More", nfc_scene_nfcf_read_success_widget_callback, nfc); + + FuriString* temp_str = furi_string_alloc(); + + if(dev_data->protocol == NfcDeviceProtocolFelica) { + furi_string_cat_printf(temp_str, "\e#%s", nfc_felica_type(dev_data->felica_data.type)); + } else { + furi_string_cat_printf(temp_str, "\e#Unknown ISO tag"); + } + + furi_string_cat_printf(temp_str, "\nISO 18092 (NFC-F)"); + + furi_string_cat_printf(temp_str, "\nCIN:"); + // NFC-F Card Identification Number (CIN) starts at "UID" byte 2. + for(size_t i = 2; i < data->uid_len; i++) { + furi_string_cat_printf(temp_str, " %02X", data->uid[i]); + } + + // The first 2 bytes of the "UID" are Manufacturer Code (MC) + furi_string_cat_printf( + temp_str, + "\nMC: %02X %02X ROM: %02X IC: %02X", + data->uid[0], + data->uid[1], + data->f_data.pmm[0], + data->f_data.pmm[1]); + + widget_add_text_scroll_element(widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str)); + furi_string_free(temp_str); + + notification_message_block(nfc->notifications, &sequence_set_green_255); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); +} + +bool nfc_scene_nfcf_read_success_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeLeft) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneRetryConfirm); + consumed = true; + } else if(event.event == GuiButtonTypeRight) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcfMenu); + consumed = true; + } + } else if(event.type == SceneManagerEventTypeBack) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneExitConfirm); + consumed = true; + } + return consumed; +} + +void nfc_scene_nfcf_read_success_on_exit(void* context) { + Nfc* nfc = context; + + notification_message_block(nfc->notifications, &sequence_reset_green); + + // Clear view + widget_reset(nfc->widget); +} diff --git a/applications/main/nfc/scenes/nfc_scene_read.c b/applications/main/nfc/scenes/nfc_scene_read.c index c96562275..7327c88b4 100644 --- a/applications/main/nfc/scenes/nfc_scene_read.c +++ b/applications/main/nfc/scenes/nfc_scene_read.c @@ -57,7 +57,6 @@ bool nfc_scene_read_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if((event.event == NfcWorkerEventReadUidNfcB) || - (event.event == NfcWorkerEventReadUidNfcF) || (event.event == NfcWorkerEventReadUidNfcV)) { notification_message(nfc->notifications, &sequence_success); scene_manager_next_scene(nfc->scene_manager, NfcSceneReadCardSuccess); @@ -102,6 +101,11 @@ bool nfc_scene_read_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(nfc->scene_manager, NfcSceneDictNotFound); } consumed = true; + } else if(event.event == NfcWorkerEventReadUidNfcF) { + notification_message(nfc->notifications, &sequence_success); + scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcfReadSuccess); + DOLPHIN_DEED(DolphinDeedNfcReadSuccess); + consumed = true; } else if(event.event == NfcWorkerEventReadFelica) { notification_message(nfc->notifications, &sequence_success); scene_manager_next_scene(nfc->scene_manager, NfcSceneFelicaReadSuccess); diff --git a/applications/main/nfc/scenes/nfc_scene_read_card_type.c b/applications/main/nfc/scenes/nfc_scene_read_card_type.c index 865b3f54b..ed33515d4 100644 --- a/applications/main/nfc/scenes/nfc_scene_read_card_type.c +++ b/applications/main/nfc/scenes/nfc_scene_read_card_type.c @@ -8,6 +8,7 @@ enum SubmenuIndex { SubmenuIndexReadEMV, SubmenuIndexReadNFCA, SubmenuIndexReadFelica, + SubmenuIndexReadNFCF, }; void nfc_scene_read_card_type_submenu_callback(void* context, uint32_t index) { @@ -56,6 +57,12 @@ void nfc_scene_read_card_type_on_enter(void* context) { SubmenuIndexReadFelica, nfc_scene_read_card_type_submenu_callback, nfc); + submenu_add_item( + submenu, + "Read NFC-F data", + SubmenuIndexReadNFCF, + nfc_scene_read_card_type_submenu_callback, + nfc); uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneReadCardType); submenu_set_selected_item(submenu, state); @@ -97,6 +104,11 @@ bool nfc_scene_read_card_type_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(nfc->scene_manager, NfcSceneRead); consumed = true; } + if(event.event == SubmenuIndexReadNFCF) { + nfc->dev->dev_data.read_mode = NfcReadModeNFCF; + scene_manager_next_scene(nfc->scene_manager, NfcSceneRead); + consumed = true; + } scene_manager_set_scene_state(nfc->scene_manager, NfcSceneReadCardType, event.event); } return consumed; diff --git a/applications/main/nfc/scenes/nfc_scene_restore_original.c b/applications/main/nfc/scenes/nfc_scene_restore_original.c index 409785e26..198c54e85 100644 --- a/applications/main/nfc/scenes/nfc_scene_restore_original.c +++ b/applications/main/nfc/scenes/nfc_scene_restore_original.c @@ -1,5 +1,5 @@ #include "../nfc_i.h" -#include "xtreme/assets.h" +#include void nfc_scene_restore_original_popup_callback(void* context) { Nfc* nfc = context; diff --git a/applications/main/nfc/scenes/nfc_scene_rpc.c b/applications/main/nfc/scenes/nfc_scene_rpc.c index 6adacfda5..b57dfed37 100644 --- a/applications/main/nfc/scenes/nfc_scene_rpc.c +++ b/applications/main/nfc/scenes/nfc_scene_rpc.c @@ -1,5 +1,5 @@ #include "../nfc_i.h" -#include "xtreme/assets.h" +#include void nfc_scene_rpc_on_enter(void* context) { Nfc* nfc = context; diff --git a/applications/main/nfc/scenes/nfc_scene_save_success.c b/applications/main/nfc/scenes/nfc_scene_save_success.c index d3e31fdc3..deef64524 100644 --- a/applications/main/nfc/scenes/nfc_scene_save_success.c +++ b/applications/main/nfc/scenes/nfc_scene_save_success.c @@ -1,5 +1,5 @@ #include "../nfc_i.h" -#include "xtreme/assets.h" +#include void nfc_scene_save_success_popup_callback(void* context) { Nfc* nfc = context; diff --git a/applications/main/onewire/onewire_cli.c b/applications/main/onewire/onewire_cli.c index 5f6cdc670..9a7ffe55c 100644 --- a/applications/main/onewire/onewire_cli.c +++ b/applications/main/onewire/onewire_cli.c @@ -21,7 +21,7 @@ void onewire_on_system_start() { static void onewire_cli_print_usage() { printf("Usage:\r\n"); printf("onewire search\r\n"); -}; +} static void onewire_cli_search(Cli* cli) { UNUSED(cli); diff --git a/applications/main/subghz/helpers/subghz_custom_event.h b/applications/main/subghz/helpers/subghz_custom_event.h index 9d2be7ee5..ad49bf09b 100644 --- a/applications/main/subghz/helpers/subghz_custom_event.h +++ b/applications/main/subghz/helpers/subghz_custom_event.h @@ -103,5 +103,8 @@ typedef enum { SubGhzCustomEventViewTransmitterSendStop, SubGhzCustomEventViewTransmitterError, + SubGhzCustomEventViewFreqAnalOkShort, + SubGhzCustomEventViewFreqAnalOkLong, + SubGhzCustomEventByteInputDone, } SubGhzCustomEvent; diff --git a/applications/main/subghz/helpers/subghz_types.h b/applications/main/subghz/helpers/subghz_types.h index 270fd7a21..3c5982427 100644 --- a/applications/main/subghz/helpers/subghz_types.h +++ b/applications/main/subghz/helpers/subghz_types.h @@ -35,12 +35,6 @@ typedef enum { SubGhzSpeakerStateEnable, } SubGhzSpeakerState; -/** SubGhzStarLineIgnore state */ -typedef enum { - SubGhzStarLineIgnoreDisable, - SubGhzStarLineIgnoreEnable, -} SubGhzStarLineIgnoreState; - /** SubGhzRxKeyState state */ typedef enum { SubGhzRxKeyStateIDLE, diff --git a/applications/main/subghz/scenes/subghz_scene_config.h b/applications/main/subghz/scenes/subghz_scene_config.h index 5acd534dc..6fb183d60 100644 --- a/applications/main/subghz/scenes/subghz_scene_config.h +++ b/applications/main/subghz/scenes/subghz_scene_config.h @@ -19,12 +19,9 @@ ADD_SCENE(subghz, test_static, TestStatic) ADD_SCENE(subghz, test_packet, TestPacket) #endif ADD_SCENE(subghz, set_type, SetType) -ADD_SCENE(subghz, set_fix_faac, SetFixFaac) -ADD_SCENE(subghz, set_cnt_faac, SetCntFaac) -ADD_SCENE(subghz, set_seed_faac, SetSeedFaac) -ADD_SCENE(subghz, set_fix_bft, SetFixBft) -ADD_SCENE(subghz, set_cnt_bft, SetCntBft) -ADD_SCENE(subghz, set_seed_bft, SetSeedBft) +ADD_SCENE(subghz, set_fix, SetFix) +ADD_SCENE(subghz, set_cnt, SetCnt) +ADD_SCENE(subghz, set_seed, SetSeed) ADD_SCENE(subghz, frequency_analyzer, FrequencyAnalyzer) ADD_SCENE(subghz, ext_module_settings, ExtModuleSettings) ADD_SCENE(subghz, read_raw, ReadRAW) diff --git a/applications/main/subghz/scenes/subghz_scene_decode_raw.c b/applications/main/subghz/scenes/subghz_scene_decode_raw.c index 5746efbed..5ac4fcccd 100644 --- a/applications/main/subghz/scenes/subghz_scene_decode_raw.c +++ b/applications/main/subghz/scenes/subghz_scene_decode_raw.c @@ -62,26 +62,31 @@ static void subghz_scene_add_to_history_callback( void* context) { furi_assert(context); SubGhz* subghz = context; - FuriString* str_buff; - str_buff = furi_string_alloc(); + FuriString* item_name; + FuriString* item_time; + uint16_t idx = subghz_history_get_item(subghz->txrx->history); + item_name = furi_string_alloc(); + item_time = furi_string_alloc(); if(subghz_history_add_to_history(subghz->txrx->history, decoder_base, subghz->txrx->preset)) { - furi_string_reset(str_buff); + furi_string_reset(item_name); + furi_string_reset(item_time); subghz->state_notifications = SubGhzNotificationStateRxDone; - subghz_history_get_text_item_menu( - subghz->txrx->history, str_buff, subghz_history_get_item(subghz->txrx->history) - 1); + subghz_history_get_text_item_menu(subghz->txrx->history, item_name, idx); + subghz_history_get_time_item_menu(subghz->txrx->history, item_time, idx); subghz_view_receiver_add_item_to_menu( subghz->subghz_receiver, - furi_string_get_cstr(str_buff), - subghz_history_get_type_protocol( - subghz->txrx->history, subghz_history_get_item(subghz->txrx->history) - 1)); + furi_string_get_cstr(item_name), + furi_string_get_cstr(item_time), + subghz_history_get_type_protocol(subghz->txrx->history, idx)); subghz_scene_receiver_update_statusbar(subghz); } subghz_receiver_reset(receiver); - furi_string_free(str_buff); + furi_string_free(item_name); + furi_string_free(item_time); } bool subghz_scene_decode_raw_start(SubGhz* subghz) { @@ -159,8 +164,10 @@ bool subghz_scene_decode_raw_next(SubGhz* subghz) { void subghz_scene_decode_raw_on_enter(void* context) { SubGhz* subghz = context; - FuriString* str_buff; - str_buff = furi_string_alloc(); + FuriString* item_name; + FuriString* item_time; + item_name = furi_string_alloc(); + item_time = furi_string_alloc(); subghz_view_receiver_set_lock(subghz->subghz_receiver, subghz->lock); subghz_view_receiver_set_mode(subghz->subghz_receiver, SubGhzViewReceiverModeFile); @@ -183,14 +190,18 @@ void subghz_scene_decode_raw_on_enter(void* context) { //Load history to receiver subghz_view_receiver_exit(subghz->subghz_receiver); for(uint8_t i = 0; i < subghz_history_get_item(subghz->txrx->history); i++) { - furi_string_reset(str_buff); - subghz_history_get_text_item_menu(subghz->txrx->history, str_buff, i); + furi_string_reset(item_name); + furi_string_reset(item_time); + subghz_history_get_text_item_menu(subghz->txrx->history, item_name, i); + subghz_history_get_time_item_menu(subghz->txrx->history, item_time, i); subghz_view_receiver_add_item_to_menu( subghz->subghz_receiver, - furi_string_get_cstr(str_buff), + furi_string_get_cstr(item_name), + furi_string_get_cstr(item_time), subghz_history_get_type_protocol(subghz->txrx->history, i)); } - furi_string_free(str_buff); + furi_string_free(item_name); + furi_string_free(item_time); subghz_view_receiver_set_idx_menu(subghz->subghz_receiver, subghz->txrx->idx_menu_chosen); } diff --git a/applications/main/subghz/scenes/subghz_scene_delete_success.c b/applications/main/subghz/scenes/subghz_scene_delete_success.c index 8e3ca5c78..252736f2d 100644 --- a/applications/main/subghz/scenes/subghz_scene_delete_success.c +++ b/applications/main/subghz/scenes/subghz_scene_delete_success.c @@ -1,6 +1,6 @@ #include "../subghz_i.h" #include "../helpers/subghz_custom_event.h" -#include "xtreme/assets.h" +#include void subghz_scene_delete_success_popup_callback(void* context) { SubGhz* subghz = context; diff --git a/applications/main/subghz/scenes/subghz_scene_ext_module_settings.c b/applications/main/subghz/scenes/subghz_scene_ext_module_settings.c index 6bd97c805..b6b9e18a7 100644 --- a/applications/main/subghz/scenes/subghz_scene_ext_module_settings.c +++ b/applications/main/subghz/scenes/subghz_scene_ext_module_settings.c @@ -1,10 +1,11 @@ #include "../subghz_i.h" #include "../helpers/subghz_custom_event.h" -uint8_t value_index_exm; -uint8_t value_index_dpin; -uint8_t value_index_cnt; -uint8_t value_index_pwr; +static uint8_t value_index_exm; +static uint8_t value_index_dpin; +static uint8_t value_index_cnt; +static uint8_t value_index_pwr; +static uint8_t value_index_time; #define EXT_MODULES_COUNT (sizeof(radio_modules_variables_text) / sizeof(char* const)) const char* const radio_modules_variables_text[] = { @@ -18,6 +19,12 @@ const char* const ext_mod_power_text[EXT_MOD_POWER_COUNT] = { "OFF", }; +#define TIMESTAMP_NAMES_COUNT 2 +const char* const timestamp_names_text[TIMESTAMP_NAMES_COUNT] = { + "OFF", + "ON", +}; + #define DEBUG_P_COUNT 2 const char* const debug_pin_text[DEBUG_P_COUNT] = { "OFF", @@ -104,6 +111,17 @@ static void subghz_scene_receiver_config_set_ext_mod_power(VariableItem* item) { subghz_last_settings_save(subghz->last_settings); } +static void subghz_scene_receiver_config_set_timestamp_file_names(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, 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); +} + void subghz_scene_ext_module_settings_on_enter(void* context) { SubGhz* subghz = context; @@ -129,6 +147,16 @@ void subghz_scene_ext_module_settings_on_enter(void* context) { variable_item_set_current_value_index(item, value_index_pwr); variable_item_set_current_value_text(item, ext_mod_power_text[value_index_pwr]); + item = variable_item_list_add( + subghz->variable_item_list, + "Time in names", + TIMESTAMP_NAMES_COUNT, + subghz_scene_receiver_config_set_timestamp_file_names, + subghz); + value_index_time = furi_hal_subghz_get_timestamp_file_names(); + variable_item_set_current_value_index(item, value_index_time); + variable_item_set_current_value_text(item, timestamp_names_text[value_index_time]); + item = variable_item_list_add( subghz->variable_item_list, "Debug Pin", @@ -183,14 +211,16 @@ bool subghz_scene_ext_module_settings_on_event(void* context, SceneManagerEvent UNUSED(event); // Set selected radio module - furi_hal_subghz_set_radio_type(value_index_exm); + furi_hal_subghz_select_radio_type(value_index_exm); + furi_hal_subghz_init_radio_type(value_index_exm); furi_hal_subghz_enable_ext_power(); // Check if module is present, if no -> show error if(!furi_hal_subghz_check_radio()) { value_index_exm = 0; - furi_hal_subghz_set_radio_type(SubGhzRadioInternal); + furi_hal_subghz_select_radio_type(SubGhzRadioInternal); + furi_hal_subghz_init_radio_type(SubGhzRadioInternal); furi_string_set(subghz->error_str, "Please connect\nexternal radio"); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowErrorSub); } diff --git a/applications/main/subghz/scenes/subghz_scene_frequency_analyzer.c b/applications/main/subghz/scenes/subghz_scene_frequency_analyzer.c index bed044244..b1071d836 100644 --- a/applications/main/subghz/scenes/subghz_scene_frequency_analyzer.c +++ b/applications/main/subghz/scenes/subghz_scene_frequency_analyzer.c @@ -54,7 +54,7 @@ bool subghz_scene_frequency_analyzer_on_event(void* context, SceneManagerEvent e } else if(event.event == SubGhzCustomEventSceneAnalyzerUnlock) { notification_message(subghz->notifications, &sequence_reset_rgb); return true; - } else if(event.event == SubGhzCustomEventViewReceiverOK) { + } else if(event.event == SubGhzCustomEventViewFreqAnalOkShort) { notification_message(subghz->notifications, &sequence_saved); uint32_t frequency = subghz_frequency_analyzer_get_frequency_to_save(subghz->subghz_frequency_analyzer); @@ -64,11 +64,12 @@ bool subghz_scene_frequency_analyzer_on_event(void* context, SceneManagerEvent e } return true; - } else if(event.event == SubGhzCustomEventViewReceiverUnlock) { + } else if(event.event == SubGhzCustomEventViewFreqAnalOkLong) { // Don't need to save, we already saved on short event #ifdef FURI_DEBUG FURI_LOG_W(TAG, "Goto next scene!"); #endif + //scene_manager_set_scene_state(subghz->scene_manager, SubGhzSceneStart, 10); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiver); return true; } diff --git a/applications/main/subghz/scenes/subghz_scene_read_raw.c b/applications/main/subghz/scenes/subghz_scene_read_raw.c index 1e3d6a9c0..5734a8e9f 100644 --- a/applications/main/subghz/scenes/subghz_scene_read_raw.c +++ b/applications/main/subghz/scenes/subghz_scene_read_raw.c @@ -122,6 +122,19 @@ void subghz_scene_read_raw_on_enter(void* context) { //set filter RAW feed subghz_receiver_set_filter(subghz->txrx->receiver, SubGhzProtocolFlag_RAW); view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdReadRAW); + + // Start sending immediately with favorites + if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW)) { + with_view_model( + subghz->subghz_read_raw->view, + SubGhzReadRAWModel * model, + { + scene_manager_handle_custom_event( + subghz->scene_manager, SubGhzCustomEventViewReadRAWSendStart); + model->status = SubGhzReadRAWStatusTXRepeat; + }, + true); + } } bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { @@ -139,7 +152,7 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) { subghz_rx_end(subghz); subghz_sleep(subghz); - }; + } //Stop save file subghz_protocol_raw_save_to_file_stop( (SubGhzProtocolDecoderRAW*)subghz->txrx->decoder_result); @@ -183,7 +196,7 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) { subghz_rx_end(subghz); subghz_sleep(subghz); - }; + } subghz->state_notifications = SubGhzNotificationStateIDLE; consumed = true; break; @@ -239,7 +252,11 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { (subghz->txrx->txrx_state == SubGhzTxRxStateSleep)) { if(!subghz_tx_start(subghz, subghz->txrx->fff_data)) { subghz->txrx->rx_key_state = SubGhzRxKeyStateBack; - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowOnlyRx); + subghz_read_raw_set_status( + subghz->subghz_read_raw, + SubGhzReadRAWStatusIDLE, + "", + subghz->txrx->raw_threshold_rssi); } else { if(scene_manager_has_previous_scene( subghz->scene_manager, SubGhzSceneSaved) || @@ -274,6 +291,13 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { subghz_sleep(subghz); } subghz_read_raw_stop_send(subghz->subghz_read_raw); + + // Exit / stop with favorites + if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW)) { + while(scene_manager_handle_back_event(subghz->scene_manager)) + ; + view_dispatcher_stop(subghz->view_dispatcher); + } consumed = true; break; @@ -281,7 +305,7 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) { subghz_rx_end(subghz); subghz_sleep(subghz); - }; + } size_t spl_count = subghz_protocol_raw_get_sample_write( (SubGhzProtocolDecoderRAW*)subghz->txrx->decoder_result); @@ -415,7 +439,7 @@ void subghz_scene_read_raw_on_exit(void* context) { if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) { subghz_rx_end(subghz); subghz_sleep(subghz); - }; + } subghz->state_notifications = SubGhzNotificationStateIDLE; notification_message(subghz->notifications, &sequence_reset_rgb); diff --git a/applications/main/subghz/scenes/subghz_scene_receiver.c b/applications/main/subghz/scenes/subghz_scene_receiver.c index 08fed3aa4..b17a0700b 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver.c @@ -91,34 +91,41 @@ static void subghz_scene_add_to_history_callback( void* context) { furi_assert(context); SubGhz* subghz = context; - FuriString* str_buff; - str_buff = furi_string_alloc(); + FuriString* item_name; + FuriString* item_time; + uint16_t idx = subghz_history_get_item(subghz->txrx->history); + item_name = furi_string_alloc(); + item_time = furi_string_alloc(); if(subghz_history_add_to_history(subghz->txrx->history, decoder_base, subghz->txrx->preset)) { - furi_string_reset(str_buff); + furi_string_reset(item_name); + furi_string_reset(item_time); subghz->state_notifications = SubGhzNotificationStateRxDone; - subghz_history_get_text_item_menu( - subghz->txrx->history, str_buff, subghz_history_get_item(subghz->txrx->history) - 1); + subghz_history_get_text_item_menu(subghz->txrx->history, item_name, idx); + subghz_history_get_time_item_menu(subghz->txrx->history, item_time, idx); subghz_view_receiver_add_item_to_menu( subghz->subghz_receiver, - furi_string_get_cstr(str_buff), - subghz_history_get_type_protocol( - subghz->txrx->history, subghz_history_get_item(subghz->txrx->history) - 1)); + furi_string_get_cstr(item_name), + furi_string_get_cstr(item_time), + subghz_history_get_type_protocol(subghz->txrx->history, idx)); subghz_scene_receiver_update_statusbar(subghz); } subghz_receiver_reset(receiver); - furi_string_free(str_buff); + furi_string_free(item_name); + furi_string_free(item_time); subghz->txrx->rx_key_state = SubGhzRxKeyStateAddKey; } void subghz_scene_receiver_on_enter(void* context) { SubGhz* subghz = context; - FuriString* str_buff; - str_buff = furi_string_alloc(); + FuriString* item_name; + FuriString* item_time; + item_name = furi_string_alloc(); + item_time = furi_string_alloc(); if(subghz->txrx->rx_key_state == SubGhzRxKeyStateIDLE) { subghz_preset_init(subghz, "AM650", subghz->last_settings->frequency, NULL, 0); @@ -132,22 +139,28 @@ void subghz_scene_receiver_on_enter(void* context) { //Load history to receiver subghz_view_receiver_exit(subghz->subghz_receiver); for(uint8_t i = 0; i < subghz_history_get_item(subghz->txrx->history); i++) { - furi_string_reset(str_buff); - subghz_history_get_text_item_menu(subghz->txrx->history, str_buff, i); + furi_string_reset(item_name); + furi_string_reset(item_time); + subghz_history_get_text_item_menu(subghz->txrx->history, item_name, i); + subghz_history_get_time_item_menu(subghz->txrx->history, item_time, i); subghz_view_receiver_add_item_to_menu( subghz->subghz_receiver, - furi_string_get_cstr(str_buff), + furi_string_get_cstr(item_name), + furi_string_get_cstr(item_time), subghz_history_get_type_protocol(subghz->txrx->history, i)); subghz->txrx->rx_key_state = SubGhzRxKeyStateAddKey; } - furi_string_free(str_buff); + furi_string_free(item_name); + furi_string_free(item_time); subghz_scene_receiver_update_statusbar(subghz); subghz_view_receiver_set_callback( subghz->subghz_receiver, subghz_scene_receiver_callback, subghz); subghz_receiver_set_rx_callback( subghz->txrx->receiver, subghz_scene_add_to_history_callback, subghz); - if(subghz->txrx->starline_state == SubGhzStarLineIgnoreEnable) { + // 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->txrx->ignore_starline == true) { SubGhzProtocolDecoderBase* protocoldecoderbase = NULL; protocoldecoderbase = subghz_receiver_search_decoder_base_by_name(subghz->txrx->receiver, "Star Line"); @@ -156,6 +169,31 @@ void subghz_scene_receiver_on_enter(void* context) { protocoldecoderbase, NULL, subghz->txrx->receiver); } } + if(subghz->txrx->ignore_auto_alarms == true) { + SubGhzProtocolDecoderBase* protocoldecoderbase = NULL; + protocoldecoderbase = + subghz_receiver_search_decoder_base_by_name(subghz->txrx->receiver, "KIA Seed"); + if(protocoldecoderbase) { + subghz_protocol_decoder_base_set_decoder_callback( + protocoldecoderbase, NULL, subghz->txrx->receiver); + } + protocoldecoderbase = NULL; + protocoldecoderbase = + subghz_receiver_search_decoder_base_by_name(subghz->txrx->receiver, "Scher-Khan"); + if(protocoldecoderbase) { + subghz_protocol_decoder_base_set_decoder_callback( + protocoldecoderbase, NULL, subghz->txrx->receiver); + } + } + if(subghz->txrx->ignore_magellan == true) { + SubGhzProtocolDecoderBase* protocoldecoderbase = NULL; + protocoldecoderbase = + subghz_receiver_search_decoder_base_by_name(subghz->txrx->receiver, "Magellan"); + if(protocoldecoderbase) { + subghz_protocol_decoder_base_set_decoder_callback( + protocoldecoderbase, NULL, subghz->txrx->receiver); + } + } subghz->state_notifications = SubGhzNotificationStateRx; if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) { diff --git a/applications/main/subghz/scenes/subghz_scene_receiver_config.c b/applications/main/subghz/scenes/subghz_scene_receiver_config.c index 6a6cf860b..68c5b4b7a 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver_config.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver_config.c @@ -7,6 +7,8 @@ enum SubGhzSettingIndex { SubGhzSettingIndexModulation, SubGhzSettingIndexBinRAW, SubGhzSettingIndexIgnoreStarline, + SubGhzSettingIndexIgnoreCars, + SubGhzSettingIndexIgnoreMagellan, SubGhzSettingIndexSound, SubGhzSettingIndexLock, SubGhzSettingIndexRAWThresholdRSSI, @@ -69,14 +71,22 @@ const uint32_t bin_raw_value[BIN_RAW_COUNT] = { SubGhzProtocolFlag_Decodable, SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_BinRAW, }; -#define STAR_LINE_COUNT 2 -const char* const star_line_text[STAR_LINE_COUNT] = { +#define STARLINE_COUNT 2 +const char* const starline_text[STARLINE_COUNT] = { "OFF", "ON", }; -const uint32_t star_line_value[STAR_LINE_COUNT] = { - SubGhzStarLineIgnoreDisable, - SubGhzStarLineIgnoreEnable, + +#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] = { + "OFF", + "ON", }; uint8_t subghz_scene_receiver_config_next_frequency(const uint32_t value, void* context) { @@ -231,8 +241,24 @@ static void subghz_scene_receiver_config_set_starline(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, star_line_text[index]); - subghz->txrx->starline_state = star_line_value[index]; + variable_item_set_current_value_text(item, starline_text[index]); + subghz->txrx->ignore_starline = (index == 1); +} + +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->txrx->ignore_auto_alarms = (index == 1); +} + +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->txrx->ignore_magellan = (index == 1); } static void subghz_scene_receiver_config_var_list_enter_callback(void* context, uint32_t index) { @@ -313,15 +339,36 @@ void subghz_scene_receiver_config_on_enter(void* context) { SubGhzCustomEventManagerSet) { item = variable_item_list_add( subghz->variable_item_list, - "Ignore StarLine:", - STAR_LINE_COUNT, + "Ignore Starline:", + STARLINE_COUNT, subghz_scene_receiver_config_set_starline, subghz); - value_index = - value_index_uint32(subghz->txrx->starline_state, star_line_value, STAR_LINE_COUNT); + value_index = subghz->txrx->ignore_starline; variable_item_set_current_value_index(item, value_index); - variable_item_set_current_value_text(item, star_line_text[value_index]); + variable_item_set_current_value_text(item, starline_text[value_index]); + + item = variable_item_list_add( + subghz->variable_item_list, + "Ignore Cars:", + AUTO_ALARMS_COUNT, + subghz_scene_receiver_config_set_auto_alarms, + subghz); + + value_index = subghz->txrx->ignore_auto_alarms; + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, auto_alarms_text[value_index]); + + item = variable_item_list_add( + subghz->variable_item_list, + "Ignore Magellan:", + MAGELLAN_COUNT, + subghz_scene_receiver_config_set_magellan, + subghz); + + value_index = subghz->txrx->ignore_magellan; + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, magellan_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 99765ee16..58e428785 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver_info.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver_info.c @@ -115,6 +115,9 @@ void subghz_scene_receiver_info_draw_widget(SubGhz* subghz) { void subghz_scene_receiver_info_on_enter(void* context) { SubGhz* subghz = context; + keeloq_reset_original_btn(); + subghz_custom_btns_reset(); + subghz_scene_receiver_info_draw_widget(subghz); } diff --git a/applications/main/subghz/scenes/subghz_scene_rpc.c b/applications/main/subghz/scenes/subghz_scene_rpc.c index e313ab3c2..82ab184d1 100644 --- a/applications/main/subghz/scenes/subghz_scene_rpc.c +++ b/applications/main/subghz/scenes/subghz_scene_rpc.c @@ -4,7 +4,7 @@ #include -#include "xtreme/assets.h" +#include typedef enum { SubGhzRpcStateIdle, diff --git a/applications/main/subghz/scenes/subghz_scene_save_name.c b/applications/main/subghz/scenes/subghz_scene_save_name.c index 4f37ebc0b..d3f5474be 100644 --- a/applications/main/subghz/scenes/subghz_scene_save_name.c +++ b/applications/main/subghz/scenes/subghz_scene_save_name.c @@ -14,18 +14,34 @@ void subghz_scene_save_name_text_input_callback(void* context) { view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventSceneSaveName); } -void subghz_scene_save_name_get_timefilename(FuriString* name) { +void subghz_scene_save_name_get_timefilename( + FuriString* name, + const char* proto_name, + bool fulldate) { FuriHalRtcDateTime datetime = {0}; furi_hal_rtc_get_datetime(&datetime); - furi_string_printf( - name, - "RAW_%.4d%.2d%.2d-%.2d%.2d%.2d", - datetime.year, - datetime.month, - datetime.day, - datetime.hour, - datetime.minute, - datetime.second); + if(fulldate) { + furi_string_printf( + name, + "%s_%.4d%.2d%.2d-%.2d%.2d%.2d", + proto_name, + datetime.year, + datetime.month, + datetime.day, + datetime.hour, + datetime.minute, + datetime.second); + } else { + furi_string_printf( + name, + "%s_%.2d%.2d-%.2d%.2d%.2d", + proto_name, + datetime.month, + datetime.day, + datetime.hour, + datetime.minute, + datetime.second); + } } void subghz_scene_save_name_on_enter(void* context) { @@ -42,8 +58,31 @@ 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}; - set_random_name(file_name_buf, SUBGHZ_MAX_LEN_NAME); - furi_string_set(file_name, file_name_buf); + if(furi_hal_subghz_get_timestamp_file_names()) { + if(subghz->txrx->decoder_result != 0x0) { + if(subghz->txrx->decoder_result != NULL) { + if(strlen(subghz->txrx->decoder_result->protocol->name) != 0) { + if(scene_manager_has_previous_scene( + subghz->scene_manager, SubGhzSceneSetType)) { + subghz_scene_save_name_get_timefilename(file_name, "S", true); + } else { + subghz_scene_save_name_get_timefilename( + file_name, subghz->txrx->decoder_result->protocol->name, false); + } + + } else { + subghz_scene_save_name_get_timefilename(file_name, "S", true); + } + } else { + subghz_scene_save_name_get_timefilename(file_name, "S", true); + } + } else { + subghz_scene_save_name_get_timefilename(file_name, "S", true); + } + } else { + set_random_name(file_name_buf, SUBGHZ_MAX_LEN_NAME); + furi_string_set(file_name, file_name_buf); + } furi_string_set(subghz->file_path, SUBGHZ_APP_FOLDER); //highlighting the entire filename by default dev_name_empty = true; @@ -57,7 +96,7 @@ void subghz_scene_save_name_on_enter(void* context) { if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) == SubGhzCustomEventManagerSetRAW) { dev_name_empty = true; - subghz_scene_save_name_get_timefilename(file_name); + subghz_scene_save_name_get_timefilename(file_name, "RAW", true); } } furi_string_set(subghz->file_path, dir_name); diff --git a/applications/main/subghz/scenes/subghz_scene_save_success.c b/applications/main/subghz/scenes/subghz_scene_save_success.c index 48804fe54..107dc7b7d 100644 --- a/applications/main/subghz/scenes/subghz_scene_save_success.c +++ b/applications/main/subghz/scenes/subghz_scene_save_success.c @@ -1,6 +1,6 @@ #include "../subghz_i.h" #include "../helpers/subghz_custom_event.h" -#include "xtreme/assets.h" +#include void subghz_scene_save_success_popup_callback(void* context) { SubGhz* subghz = context; diff --git a/applications/main/subghz/scenes/subghz_scene_set_cnt.c b/applications/main/subghz/scenes/subghz_scene_set_cnt.c new file mode 100644 index 000000000..62ef581ff --- /dev/null +++ b/applications/main/subghz/scenes/subghz_scene_set_cnt.c @@ -0,0 +1,66 @@ +#include "../subghz_i.h" + +#define TAG "SubGhzSetCnt" + +void subghz_scene_set_cnt_byte_input_callback(void* context) { + SubGhz* subghz = context; + + view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventByteInputDone); +} + +void subghz_scene_set_cnt_on_enter(void* context) { + SubGhz* subghz = context; + + // Setup view + ByteInput* byte_input = subghz->byte_input; + SubGhzCustomEvent state = + scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneSetType); + + switch(state) { + case SubmenuIndexBFTClone: + byte_input_set_header_text(byte_input, "Enter COUNTER in hex"); + byte_input_set_result_callback( + byte_input, + subghz_scene_set_cnt_byte_input_callback, + NULL, + subghz, + subghz->txrx->secure_data->cnt, + 2); + break; + case SubmenuIndexFaacSLH_433: + case SubmenuIndexFaacSLH_868: + byte_input_set_header_text(byte_input, "Enter COUNTER in hex, 20bits"); + byte_input_set_result_callback( + byte_input, + subghz_scene_set_cnt_byte_input_callback, + NULL, + subghz, + subghz->txrx->secure_data->cnt, + 3); + break; + default: + break; + } + view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdByteInput); +} + +bool subghz_scene_set_cnt_on_event(void* context, SceneManagerEvent event) { + SubGhz* subghz = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubGhzCustomEventByteInputDone) { + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSetSeed); + consumed = true; + } + } + return consumed; +} + +void subghz_scene_set_cnt_on_exit(void* context) { + SubGhz* subghz = context; + + // Clear view + byte_input_set_result_callback(subghz->byte_input, NULL, NULL, NULL, NULL, 0); + byte_input_set_header_text(subghz->byte_input, ""); +} diff --git a/applications/main/subghz/scenes/subghz_scene_set_cnt_bft.c b/applications/main/subghz/scenes/subghz_scene_set_cnt_bft.c deleted file mode 100644 index d58409a7e..000000000 --- a/applications/main/subghz/scenes/subghz_scene_set_cnt_bft.c +++ /dev/null @@ -1,46 +0,0 @@ -#include "../subghz_i.h" - -#define TAG "SubGhzSetCntBft" - -void subghz_scene_set_cnt_bft_byte_input_callback(void* context) { - SubGhz* subghz = context; - - view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventByteInputDone); -} - -void subghz_scene_set_cnt_bft_on_enter(void* context) { - SubGhz* subghz = context; - - // Setup view - ByteInput* byte_input = subghz->byte_input; - byte_input_set_header_text(byte_input, "Enter COUNTER in hex"); - byte_input_set_result_callback( - byte_input, - subghz_scene_set_cnt_bft_byte_input_callback, - NULL, - subghz, - subghz->txrx->secure_data->cnt, - 2); - view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdByteInput); -} - -bool subghz_scene_set_cnt_bft_on_event(void* context, SceneManagerEvent event) { - SubGhz* subghz = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubGhzCustomEventByteInputDone) { - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSetSeedBft); - consumed = true; - } - } - return consumed; -} - -void subghz_scene_set_cnt_bft_on_exit(void* context) { - SubGhz* subghz = context; - - // Clear view - byte_input_set_result_callback(subghz->byte_input, NULL, NULL, NULL, NULL, 0); - byte_input_set_header_text(subghz->byte_input, ""); -} diff --git a/applications/main/subghz/scenes/subghz_scene_set_cnt_faac.c b/applications/main/subghz/scenes/subghz_scene_set_cnt_faac.c deleted file mode 100644 index 6002dea74..000000000 --- a/applications/main/subghz/scenes/subghz_scene_set_cnt_faac.c +++ /dev/null @@ -1,46 +0,0 @@ -#include "../subghz_i.h" - -#define TAG "SubGhzSetCntFaac" - -void subghz_scene_set_cnt_faac_byte_input_callback(void* context) { - SubGhz* subghz = context; - - view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventByteInputDone); -} - -void subghz_scene_set_cnt_faac_on_enter(void* context) { - SubGhz* subghz = context; - - // Setup view - ByteInput* byte_input = subghz->byte_input; - byte_input_set_header_text(byte_input, "Enter COUNTER in hex, 20bits"); - byte_input_set_result_callback( - byte_input, - subghz_scene_set_cnt_faac_byte_input_callback, - NULL, - subghz, - subghz->txrx->secure_data->cnt, - 3); - view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdByteInput); -} - -bool subghz_scene_set_cnt_faac_on_event(void* context, SceneManagerEvent event) { - SubGhz* subghz = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubGhzCustomEventByteInputDone) { - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSetSeedFaac); - consumed = true; - } - } - return consumed; -} - -void subghz_scene_set_cnt_faac_on_exit(void* context) { - SubGhz* subghz = context; - - // Clear view - byte_input_set_result_callback(subghz->byte_input, NULL, NULL, NULL, NULL, 0); - byte_input_set_header_text(subghz->byte_input, ""); -} diff --git a/applications/main/subghz/scenes/subghz_scene_set_fix_bft.c b/applications/main/subghz/scenes/subghz_scene_set_fix.c similarity index 74% rename from applications/main/subghz/scenes/subghz_scene_set_fix_bft.c rename to applications/main/subghz/scenes/subghz_scene_set_fix.c index 8153fa2a0..01431dac6 100644 --- a/applications/main/subghz/scenes/subghz_scene_set_fix_bft.c +++ b/applications/main/subghz/scenes/subghz_scene_set_fix.c @@ -1,14 +1,14 @@ #include "../subghz_i.h" -#define TAG "SubGhzSetFixBft" +#define TAG "SubGhzSetFix" -void subghz_scene_set_fix_bft_byte_input_callback(void* context) { +void subghz_scene_set_fix_byte_input_callback(void* context) { SubGhz* subghz = context; view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventByteInputDone); } -void subghz_scene_set_fix_bft_on_enter(void* context) { +void subghz_scene_set_fix_on_enter(void* context) { SubGhz* subghz = context; // Setup view @@ -16,7 +16,7 @@ void subghz_scene_set_fix_bft_on_enter(void* context) { byte_input_set_header_text(byte_input, "Enter FIX in hex"); byte_input_set_result_callback( byte_input, - subghz_scene_set_fix_bft_byte_input_callback, + subghz_scene_set_fix_byte_input_callback, NULL, subghz, subghz->txrx->secure_data->fix, @@ -24,20 +24,20 @@ void subghz_scene_set_fix_bft_on_enter(void* context) { view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdByteInput); } -bool subghz_scene_set_fix_bft_on_event(void* context, SceneManagerEvent event) { +bool subghz_scene_set_fix_on_event(void* context, SceneManagerEvent event) { SubGhz* subghz = context; bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { if(event.event == SubGhzCustomEventByteInputDone) { - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSetCntBft); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSetCnt); consumed = true; } } return consumed; } -void subghz_scene_set_fix_bft_on_exit(void* context) { +void subghz_scene_set_fix_on_exit(void* context) { SubGhz* subghz = context; // Clear view diff --git a/applications/main/subghz/scenes/subghz_scene_set_fix_faac.c b/applications/main/subghz/scenes/subghz_scene_set_fix_faac.c deleted file mode 100644 index 333062c38..000000000 --- a/applications/main/subghz/scenes/subghz_scene_set_fix_faac.c +++ /dev/null @@ -1,46 +0,0 @@ -#include "../subghz_i.h" - -#define TAG "SubGhzSetFixFaac" - -void subghz_scene_set_fix_faac_byte_input_callback(void* context) { - SubGhz* subghz = context; - - view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventByteInputDone); -} - -void subghz_scene_set_fix_faac_on_enter(void* context) { - SubGhz* subghz = context; - - // Setup view - ByteInput* byte_input = subghz->byte_input; - byte_input_set_header_text(byte_input, "Enter FIX in hex"); - byte_input_set_result_callback( - byte_input, - subghz_scene_set_fix_faac_byte_input_callback, - NULL, - subghz, - subghz->txrx->secure_data->fix, - 4); - view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdByteInput); -} - -bool subghz_scene_set_fix_faac_on_event(void* context, SceneManagerEvent event) { - SubGhz* subghz = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubGhzCustomEventByteInputDone) { - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSetCntFaac); - consumed = true; - } - } - return consumed; -} - -void subghz_scene_set_fix_faac_on_exit(void* context) { - SubGhz* subghz = context; - - // Clear view - byte_input_set_result_callback(subghz->byte_input, NULL, NULL, NULL, NULL, 0); - byte_input_set_header_text(subghz->byte_input, ""); -} diff --git a/applications/main/subghz/scenes/subghz_scene_set_seed.c b/applications/main/subghz/scenes/subghz_scene_set_seed.c new file mode 100644 index 000000000..aa2a270f3 --- /dev/null +++ b/applications/main/subghz/scenes/subghz_scene_set_seed.c @@ -0,0 +1,168 @@ +#include "../subghz_i.h" +#include +#include + +#define TAG "SubGhzSetSeed" + +void subghz_scene_set_seed_byte_input_callback(void* context) { + SubGhz* subghz = context; + + view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventByteInputDone); +} + +void subghz_scene_set_seed_on_enter(void* context) { + SubGhz* subghz = context; + + // Setup view + ByteInput* byte_input = subghz->byte_input; + byte_input_set_header_text(byte_input, "Enter SEED in hex"); + byte_input_set_result_callback( + byte_input, + subghz_scene_set_seed_byte_input_callback, + NULL, + subghz, + subghz->txrx->secure_data->seed, + 4); + view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdByteInput); +} + +bool subghz_scene_set_seed_on_event(void* context, SceneManagerEvent event) { + SubGhz* subghz = context; + bool consumed = false; + bool generated_protocol = false; + uint32_t fix_part, cnt, seed; + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubGhzCustomEventByteInputDone) { + SubGhzCustomEvent state = + scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneSetType); + + switch(state) { + case SubmenuIndexBFTClone: + fix_part = subghz->txrx->secure_data->fix[0] << 24 | + subghz->txrx->secure_data->fix[1] << 16 | + subghz->txrx->secure_data->fix[2] << 8 | + subghz->txrx->secure_data->fix[3]; + + cnt = subghz->txrx->secure_data->cnt[0] << 8 | subghz->txrx->secure_data->cnt[1]; + + seed = subghz->txrx->secure_data->seed[0] << 24 | + subghz->txrx->secure_data->seed[1] << 16 | + subghz->txrx->secure_data->seed[2] << 8 | + subghz->txrx->secure_data->seed[3]; + + subghz->txrx->transmitter = + subghz_transmitter_alloc_init(subghz->txrx->environment, "KeeLoq"); + if(subghz->txrx->transmitter) { + subghz_preset_init(subghz, "AM650", 433920000, NULL, 0); + subghz_protocol_keeloq_bft_create_data( + subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter), + subghz->txrx->fff_data, + fix_part & 0x0FFFFFFF, + fix_part >> 28, + cnt, + seed, + "BFT", + subghz->txrx->preset); + + uint8_t seed_data[sizeof(uint32_t)] = {0}; + for(size_t i = 0; i < sizeof(uint32_t); i++) { + seed_data[sizeof(uint32_t) - i - 1] = (seed >> i * 8) & 0xFF; + } + + flipper_format_write_hex( + subghz->txrx->fff_data, "Seed", seed_data, sizeof(uint32_t)); + + flipper_format_write_string_cstr(subghz->txrx->fff_data, "Manufacture", "BFT"); + + generated_protocol = true; + } + + subghz_transmitter_free(subghz->txrx->transmitter); + + if(!generated_protocol) { + furi_string_set( + subghz->error_str, "Function requires\nan SD card with\nfresh databases."); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError); + } + consumed = true; + break; + case SubmenuIndexFaacSLH_433: + case SubmenuIndexFaacSLH_868: + fix_part = subghz->txrx->secure_data->fix[0] << 24 | + subghz->txrx->secure_data->fix[1] << 16 | + subghz->txrx->secure_data->fix[2] << 8 | + subghz->txrx->secure_data->fix[3]; + + cnt = subghz->txrx->secure_data->cnt[0] << 16 | + subghz->txrx->secure_data->cnt[1] << 8 | subghz->txrx->secure_data->cnt[2]; + + seed = subghz->txrx->secure_data->seed[0] << 24 | + subghz->txrx->secure_data->seed[1] << 16 | + subghz->txrx->secure_data->seed[2] << 8 | + subghz->txrx->secure_data->seed[3]; + + subghz->txrx->transmitter = + subghz_transmitter_alloc_init(subghz->txrx->environment, "Faac SLH"); + if(subghz->txrx->transmitter) { + SubGhzCustomEvent state = + scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneSetType); + + if(state == SubmenuIndexFaacSLH_433) { + subghz_preset_init(subghz, "AM650", 433920000, NULL, 0); + } else if(state == SubmenuIndexFaacSLH_868) { + subghz_preset_init(subghz, "AM650", 868350000, NULL, 0); + } + subghz_protocol_faac_slh_create_data( + subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter), + subghz->txrx->fff_data, + fix_part >> 4, + fix_part & 0xf, + (cnt & 0xFFFFF), + seed, + "FAAC_SLH", + subghz->txrx->preset); + // RogueMaster dont steal! + uint8_t seed_data[sizeof(uint32_t)] = {0}; + for(size_t i = 0; i < sizeof(uint32_t); i++) { + seed_data[sizeof(uint32_t) - i - 1] = (seed >> i * 8) & 0xFF; + } + + flipper_format_write_hex( + subghz->txrx->fff_data, "Seed", seed_data, sizeof(uint32_t)); + + generated_protocol = true; + } + + subghz_transmitter_free(subghz->txrx->transmitter); + + if(!generated_protocol) { + furi_string_set( + subghz->error_str, "Function requires\nan SD card with\nfresh databases."); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError); + } + consumed = true; + break; + + default: + break; + } + } + + if(generated_protocol) { + subghz_file_name_clear(subghz); + scene_manager_set_scene_state( + subghz->scene_manager, SubGhzSceneSetType, SubGhzCustomEventManagerSet); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName); + return true; + } + } + return consumed; +} + +void subghz_scene_set_seed_on_exit(void* context) { + SubGhz* subghz = context; + + // Clear view + byte_input_set_result_callback(subghz->byte_input, NULL, NULL, NULL, NULL, 0); + byte_input_set_header_text(subghz->byte_input, ""); +} diff --git a/applications/main/subghz/scenes/subghz_scene_set_seed_bft.c b/applications/main/subghz/scenes/subghz_scene_set_seed_bft.c deleted file mode 100644 index 2c48462ef..000000000 --- a/applications/main/subghz/scenes/subghz_scene_set_seed_bft.c +++ /dev/null @@ -1,105 +0,0 @@ -#include "../subghz_i.h" -#include -#include - -#define TAG "SubGhzSetSeedBft" - -void subghz_scene_set_seed_bft_byte_input_callback(void* context) { - SubGhz* subghz = context; - - view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventByteInputDone); -} - -void subghz_scene_set_seed_bft_on_enter(void* context) { - SubGhz* subghz = context; - - // Setup view - // RogueMaster don't steal!!! - ByteInput* byte_input = subghz->byte_input; - byte_input_set_header_text(byte_input, "Enter SEED in hex"); - byte_input_set_result_callback( - byte_input, - subghz_scene_set_seed_bft_byte_input_callback, - NULL, - subghz, - subghz->txrx->secure_data->seed, - 4); - view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdByteInput); -} - -bool subghz_scene_set_seed_bft_on_event(void* context, SceneManagerEvent event) { - SubGhz* subghz = context; - bool consumed = false; - bool generated_protocol = false; - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubGhzCustomEventByteInputDone) { - uint32_t fix_part = - subghz->txrx->secure_data->fix[0] << 24 | subghz->txrx->secure_data->fix[1] << 16 | - subghz->txrx->secure_data->fix[2] << 8 | subghz->txrx->secure_data->fix[3]; - - uint16_t cnt = subghz->txrx->secure_data->cnt[0] << 8 | - subghz->txrx->secure_data->cnt[1]; - - uint32_t seed = subghz->txrx->secure_data->seed[0] << 24 | - subghz->txrx->secure_data->seed[1] << 16 | - subghz->txrx->secure_data->seed[2] << 8 | - subghz->txrx->secure_data->seed[3]; - - subghz->txrx->transmitter = - subghz_transmitter_alloc_init(subghz->txrx->environment, "KeeLoq"); - if(subghz->txrx->transmitter) { - subghz_preset_init(subghz, "AM650", 433920000, NULL, 0); - subghz_protocol_keeloq_bft_create_data( - subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter), - subghz->txrx->fff_data, - fix_part & 0x0FFFFFFF, - fix_part >> 28, - cnt, - seed, - "BFT", - subghz->txrx->preset); - - uint8_t seed_data[sizeof(uint32_t)] = {0}; - for(size_t i = 0; i < sizeof(uint32_t); i++) { - seed_data[sizeof(uint32_t) - i - 1] = (seed >> i * 8) & 0xFF; - } - - flipper_format_write_hex( - subghz->txrx->fff_data, "Seed", seed_data, sizeof(uint32_t)); - - flipper_format_write_string_cstr(subghz->txrx->fff_data, "Manufacture", "BFT"); - - generated_protocol = true; - } else { - generated_protocol = false; - } - - subghz_transmitter_free(subghz->txrx->transmitter); - - if(!generated_protocol) { - furi_string_set( - subghz->error_str, "Function requires\nan SD card with\nfresh databases."); - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError); - } - consumed = true; - } - - if(generated_protocol) { - subghz_file_name_clear(subghz); - DOLPHIN_DEED(DolphinDeedSubGhzAddManually); - scene_manager_set_scene_state( - subghz->scene_manager, SubGhzSceneSetType, SubGhzCustomEventManagerSet); - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName); - return true; - } - } - return consumed; -} - -void subghz_scene_set_seed_bft_on_exit(void* context) { - SubGhz* subghz = context; - - // Clear view - byte_input_set_result_callback(subghz->byte_input, NULL, NULL, NULL, NULL, 0); - byte_input_set_header_text(subghz->byte_input, ""); -} diff --git a/applications/main/subghz/scenes/subghz_scene_set_seed_faac.c b/applications/main/subghz/scenes/subghz_scene_set_seed_faac.c deleted file mode 100644 index 55387a0a5..000000000 --- a/applications/main/subghz/scenes/subghz_scene_set_seed_faac.c +++ /dev/null @@ -1,110 +0,0 @@ -#include "../subghz_i.h" -#include -#include - -#define TAG "SubGhzSetSeedFaac" - -void subghz_scene_set_seed_faac_byte_input_callback(void* context) { - SubGhz* subghz = context; - - view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventByteInputDone); -} - -void subghz_scene_set_seed_faac_on_enter(void* context) { - SubGhz* subghz = context; - - // Setup view - ByteInput* byte_input = subghz->byte_input; - byte_input_set_header_text(byte_input, "Enter SEED in hex"); - byte_input_set_result_callback( - byte_input, - subghz_scene_set_seed_faac_byte_input_callback, - NULL, - subghz, - subghz->txrx->secure_data->seed, - 4); - view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdByteInput); -} - -bool subghz_scene_set_seed_faac_on_event(void* context, SceneManagerEvent event) { - SubGhz* subghz = context; - bool consumed = false; - bool generated_protocol = false; - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubGhzCustomEventByteInputDone) { - uint32_t fix_part = - subghz->txrx->secure_data->fix[0] << 24 | subghz->txrx->secure_data->fix[1] << 16 | - subghz->txrx->secure_data->fix[2] << 8 | subghz->txrx->secure_data->fix[3]; - - uint32_t cnt = subghz->txrx->secure_data->cnt[0] << 16 | - subghz->txrx->secure_data->cnt[1] << 8 | - subghz->txrx->secure_data->cnt[2]; - - uint32_t seed = subghz->txrx->secure_data->seed[0] << 24 | - subghz->txrx->secure_data->seed[1] << 16 | - subghz->txrx->secure_data->seed[2] << 8 | - subghz->txrx->secure_data->seed[3]; - - subghz->txrx->transmitter = - subghz_transmitter_alloc_init(subghz->txrx->environment, "Faac SLH"); - if(subghz->txrx->transmitter) { - SubGhzCustomEvent state = - scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneSetType); - - if(state == SubmenuIndexFaacSLH_433) { - subghz_preset_init(subghz, "AM650", 433920000, NULL, 0); - } else if(state == SubmenuIndexFaacSLH_868) { - subghz_preset_init(subghz, "AM650", 868350000, NULL, 0); - } - subghz_protocol_faac_slh_create_data( - subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter), - subghz->txrx->fff_data, - fix_part >> 4, - fix_part & 0xf, - (cnt & 0xFFFFF), - seed, - "FAAC_SLH", - subghz->txrx->preset); - // RogueMaster dont steal! - uint8_t seed_data[sizeof(uint32_t)] = {0}; - for(size_t i = 0; i < sizeof(uint32_t); i++) { - seed_data[sizeof(uint32_t) - i - 1] = (seed >> i * 8) & 0xFF; - } - - flipper_format_write_hex( - subghz->txrx->fff_data, "Seed", seed_data, sizeof(uint32_t)); - - generated_protocol = true; - } else { - generated_protocol = false; - } - - subghz_transmitter_free(subghz->txrx->transmitter); - - if(!generated_protocol) { - furi_string_set( - subghz->error_str, "Function requires\nan SD card with\nfresh databases."); - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError); - } - consumed = true; - } - - if(generated_protocol) { - subghz_file_name_clear(subghz); - DOLPHIN_DEED(DolphinDeedSubGhzAddManually); - scene_manager_set_scene_state( - subghz->scene_manager, SubGhzSceneSetType, SubGhzCustomEventManagerSet); - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName); - return true; - } - } - return consumed; -} - -void subghz_scene_set_seed_faac_on_exit(void* context) { - SubGhz* subghz = context; - - // Clear view - byte_input_set_result_callback(subghz->byte_input, NULL, NULL, NULL, NULL, 0); - byte_input_set_header_text(subghz->byte_input, ""); -} diff --git a/applications/main/subghz/scenes/subghz_scene_set_type.c b/applications/main/subghz/scenes/subghz_scene_set_type.c index 431d5d2d7..e871a6439 100644 --- a/applications/main/subghz/scenes/subghz_scene_set_type.c +++ b/applications/main/subghz/scenes/subghz_scene_set_type.c @@ -389,13 +389,13 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) { uint32_t key = subghz_random_serial(); switch(event.event) { case SubmenuIndexFaacSLH_868: - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSetFixFaac); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSetFix); break; case SubmenuIndexFaacSLH_433: - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSetFixFaac); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSetFix); break; case SubmenuIndexBFTClone: - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSetFixBft); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSetFix); break; case SubmenuIndexPricenton: key = (key & 0x00FFFFF0) | 0x4; //btn 0x1, 0x2, 0x4, 0x8 diff --git a/applications/main/subghz/scenes/subghz_scene_start.c b/applications/main/subghz/scenes/subghz_scene_start.c index 00b7ddbac..e3c70aaf6 100644 --- a/applications/main/subghz/scenes/subghz_scene_start.c +++ b/applications/main/subghz/scenes/subghz_scene_start.c @@ -88,7 +88,8 @@ bool subghz_scene_start_on_event(void* context, SceneManagerEvent event) { furi_hal_subghz_enable_ext_power(); if(!furi_hal_subghz_check_radio()) { - furi_hal_subghz_set_radio_type(SubGhzRadioInternal); + furi_hal_subghz_select_radio_type(SubGhzRadioInternal); + furi_hal_subghz_init_radio_type(SubGhzRadioInternal); subghz->last_settings->external_module_enabled = false; furi_string_set(subghz->error_str, "Please connect\nexternal radio"); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowErrorSub); diff --git a/applications/main/subghz/scenes/subghz_scene_transmitter.c b/applications/main/subghz/scenes/subghz_scene_transmitter.c index 7895d8bc3..7877e3c14 100644 --- a/applications/main/subghz/scenes/subghz_scene_transmitter.c +++ b/applications/main/subghz/scenes/subghz_scene_transmitter.c @@ -1,6 +1,7 @@ #include "../subghz_i.h" #include "../views/transmitter.h" #include +#include #include #include @@ -50,8 +51,20 @@ bool subghz_scene_transmitter_update_data_show(void* context) { return ret; } +FuriTimer* fav_timer = NULL; + +void fav_timer_callback(void* context) { + SubGhz* subghz = context; + scene_manager_handle_custom_event( + subghz->scene_manager, SubGhzCustomEventViewTransmitterSendStop); +} + void subghz_scene_transmitter_on_enter(void* context) { SubGhz* subghz = context; + + keeloq_reset_original_btn(); + subghz_custom_btns_reset(); + if(!subghz_scene_transmitter_update_data_show(subghz)) { view_dispatcher_send_custom_event( subghz->view_dispatcher, SubGhzCustomEventViewTransmitterError); @@ -62,6 +75,22 @@ void subghz_scene_transmitter_on_enter(void* context) { subghz->state_notifications = SubGhzNotificationStateIDLE; view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdTransmitter); + + // Auto send and exit with favorites + if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneTransmitter)) { + subghz_custom_btn_set(0); + scene_manager_handle_custom_event( + subghz->scene_manager, SubGhzCustomEventViewTransmitterSendStart); + with_view_model( + subghz->subghz_transmitter->view, + SubGhzViewTransmitterModel * model, + { model->show_button = false; }, + true); + fav_timer = furi_timer_alloc(fav_timer_callback, FuriTimerTypeOnce, subghz); + furi_timer_start( + fav_timer, XTREME_SETTINGS()->favorite_timeout * furi_kernel_get_tick_frequency()); + subghz->state_notifications = SubGhzNotificationStateTx; + } } bool subghz_scene_transmitter_on_event(void* context, SceneManagerEvent event) { @@ -74,9 +103,7 @@ bool subghz_scene_transmitter_on_event(void* context, SceneManagerEvent event) { } if((subghz->txrx->txrx_state == SubGhzTxRxStateIDLE) || (subghz->txrx->txrx_state == SubGhzTxRxStateSleep)) { - if(!subghz_tx_start(subghz, subghz->txrx->fff_data)) { - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowOnlyRx); - } else { + if(subghz_tx_start(subghz, subghz->txrx->fff_data)) { subghz->state_notifications = SubGhzNotificationStateTx; subghz_scene_transmitter_update_data_show(subghz); DOLPHIN_DEED(DolphinDeedSubGhzSend); @@ -107,6 +134,15 @@ bool subghz_scene_transmitter_on_event(void* context, SceneManagerEvent event) { subghz_sleep(subghz); furi_hal_subghz_set_rolling_counter_mult(tmp_counter); } + if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneTransmitter)) { + if(fav_timer) { + furi_timer_stop(fav_timer); + furi_timer_free(fav_timer); + } + while(scene_manager_handle_back_event(subghz->scene_manager)) + ; + view_dispatcher_stop(subghz->view_dispatcher); + } return true; } else if(event.event == SubGhzCustomEventViewTransmitterBack) { subghz->state_notifications = SubGhzNotificationStateIDLE; diff --git a/applications/main/subghz/subghz.c b/applications/main/subghz/subghz.c index 75c6e627a..6752b4e55 100644 --- a/applications/main/subghz/subghz.c +++ b/applications/main/subghz/subghz.c @@ -4,6 +4,8 @@ #include #include "subghz_i.h" #include +#include +#include #define TAG "SubGhzApp" @@ -423,7 +425,7 @@ void subghz_free(SubGhz* subghz, bool alloc_for_tx_only) { free(subghz); } -int32_t subghz_app(void* p) { +int32_t subghz_app(char* p) { bool alloc_for_tx; if(p && strlen(p)) { alloc_for_tx = true; @@ -451,9 +453,11 @@ int32_t subghz_app(void* p) { // Auto switch to internal radio if external radio is not available if(!furi_hal_subghz_check_radio()) { subghz->last_settings->external_module_enabled = false; - furi_hal_subghz_set_radio_type(SubGhzRadioInternal); + furi_hal_subghz_select_radio_type(SubGhzRadioInternal); + furi_hal_subghz_init_radio_type(SubGhzRadioInternal); } // Check argument and run corresponding scene + bool is_favorite = process_favorite_launch(&p) && XTREME_SETTINGS()->favorite_timeout; if(p && strlen(p)) { uint32_t rpc_ctx = 0; @@ -473,9 +477,13 @@ int32_t subghz_app(void* p) { if((!strcmp(subghz->txrx->decoder_result->protocol->name, "RAW"))) { //Load Raw TX subghz->txrx->rx_key_state = SubGhzRxKeyStateRAWLoad; + scene_manager_set_scene_state( + subghz->scene_manager, SubGhzSceneReadRAW, is_favorite); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReadRAW); } else { //Load transmitter TX + scene_manager_set_scene_state( + subghz->scene_manager, SubGhzSceneTransmitter, is_favorite); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneTransmitter); } } else { @@ -507,6 +515,8 @@ int32_t subghz_app(void* p) { furi_hal_power_suppress_charge_exit(); // 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); subghz_free(subghz, alloc_for_tx); diff --git a/applications/main/subghz/subghz_history.c b/applications/main/subghz/subghz_history.c index f6530238e..3c018ec8b 100644 --- a/applications/main/subghz/subghz_history.c +++ b/applications/main/subghz/subghz_history.c @@ -12,6 +12,7 @@ typedef struct { FlipperFormat* flipper_string; uint8_t type; SubGhzRadioPreset* preset; + FuriHalRtcDateTime datetime; } SubGhzHistoryItem; ARRAY_DEF(SubGhzHistoryItemArray, SubGhzHistoryItem, M_POD_OPLIST) @@ -124,6 +125,11 @@ uint8_t subghz_history_get_type_protocol(SubGhzHistory* instance, uint16_t idx) const char* subghz_history_get_protocol_name(SubGhzHistory* instance, uint16_t idx) { furi_assert(instance); SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx); + if(!item || !item->flipper_string) { + FURI_LOG_E(TAG, "Missing Item"); + furi_string_reset(instance->tmp_string); + return furi_string_get_cstr(instance->tmp_string); + } flipper_format_rewind(item->flipper_string); if(!flipper_format_read_string(item->flipper_string, "Protocol", instance->tmp_string)) { FURI_LOG_E(TAG, "Missing Protocol"); @@ -164,6 +170,12 @@ void subghz_history_get_text_item_menu(SubGhzHistory* instance, FuriString* outp furi_string_set(output, item->item_str); } +void subghz_history_get_time_item_menu(SubGhzHistory* instance, FuriString* output, uint16_t idx) { + SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx); + FuriHalRtcDateTime* t = &item->datetime; + furi_string_printf(output, "%.2d:%.2d:%.2d ", t->hour, t->minute, t->second); +} + bool subghz_history_add_to_history( SubGhzHistory* instance, void* context, @@ -195,6 +207,7 @@ bool subghz_history_add_to_history( furi_string_set(item->preset->name, preset->name); item->preset->data = preset->data; item->preset->data_size = preset->data_size; + furi_hal_rtc_get_datetime(&item->datetime); item->item_str = furi_string_alloc(); item->flipper_string = flipper_format_string_alloc(); diff --git a/applications/main/subghz/subghz_history.h b/applications/main/subghz/subghz_history.h index 1f2f8d246..1b27d52ad 100644 --- a/applications/main/subghz/subghz_history.h +++ b/applications/main/subghz/subghz_history.h @@ -78,6 +78,14 @@ const char* subghz_history_get_protocol_name(SubGhzHistory* instance, uint16_t i */ void subghz_history_get_text_item_menu(SubGhzHistory* instance, FuriString* output, uint16_t idx); +/** Get time item menu to history[idx] + * + * @param instance - SubGhzHistory instance + * @param output - FuriString* output + * @param idx - record index + */ +void subghz_history_get_time_item_menu(SubGhzHistory* instance, FuriString* output, uint16_t idx); + /** Get string the remaining number of records to history * * @param instance - SubGhzHistory instance diff --git a/applications/main/subghz/subghz_history_private.h b/applications/main/subghz/subghz_history_private.h deleted file mode 100644 index 12ce28fff..000000000 --- a/applications/main/subghz/subghz_history_private.h +++ /dev/null @@ -1,163 +0,0 @@ -#pragma once - -#include "subghz_history.h" -#include - -/** - * @brief Generate filename like 000.tmp - * - * @param index - index of file, timestamp doesn't accepted! - */ -FuriString* subghz_history_generate_temp_filename(uint32_t index); - -/** - * @brief Check if directory for temporary files is exists - * - * @param instance SubGhzHistory* - * @return true - * @return false - */ -bool subghz_history_is_tmp_dir_exists(SubGhzHistory* instance); - -/** - * @brief Check SD card and create temporary dir if not exists, - * Result write_tmp_files without this unstable work is GUARANTEED - * - * @param instance - SubGhzHistory* - * @return - true all ok - * @return - false we have a problems - */ -bool subghz_history_check_sdcard(SubGhzHistory* instance); - -/** - * @brief Recursive delete dir and files and create new temp dir - * - * @param instance - SubGhzHistory* - * @return true - if all ok - * @return false - if something failed - */ -void subghz_history_clear_tmp_dir(SubGhzHistory* instance); - -/** - * @brief Free item and free all resources - * - * @param current_item - SubGhzHistoryItem* - */ -void subghz_history_item_free(void* current_item); - -/** - * @brief free all items in array - * - * @param instance - */ -void subghz_history_clean_item_array(SubGhzHistory* instance); - -/** - * @brief Write temp file fully, without splitting - * - * @param instance - SubGhzHistory* - * @param current_item - SubGhzHistoryItem* - * @param dir_path - full path to file - */ -void subghz_history_tmp_write_file_full( - SubGhzHistory* instance, - void* current_item, - FuriString* dir_path); - -/** - * @brief Write temp spited to lines - * - * @param instance - SubGhzHistory* - * @param current_item - SubGhzHistoryItem* - * @param dir_path - full path to file - * @return true - file saved - * @return false - error occurred - */ -bool subghz_history_tmp_write_file_split( - SubGhzHistory* instance, - void* current_item, - const char* dir_path); - -/** - * @brief generate random value - * - * @param min - min value - * @param max - max value - * @return uint32_t - */ -uint32_t subghz_history_rand_range(uint32_t min, uint32_t max); - -/** - * @brief write random noise signals to file applying to max line value - * - * @param file - Stream* - * @param is_negative_start - first value is negative or positive? - * @param current_position - 0 if started from beginning - * @param empty_line - add RAW_Data to this line - * @return true - * @return false - */ -bool subghz_history_write_file_noise( - Stream* file, - bool is_negative_start, - size_t current_position, - bool empty_line); - -/** - * @brief taken from flipper_format_stream_read_value_line but takes only one int32 value - * - * @param stream - Stream* - * @param _data - int32_t* output data - * @param data_size - size of data - * @return true - * @return false - */ -bool subghz_history_read_int32(Stream* stream, int32_t* _data, const uint16_t data_size); - -/** - * @brief write payload to file spliting by lines - * - * @param src - Stream* of source - * @param file - Stream* of file - * @param is_negative_start - first value is negative or positive? - * @param current_position - by default is 0 but in this value returned last position of payload - * @return true - * @return false - */ -bool subghz_history_write_file_data( - Stream* src, - Stream* file, - bool* is_negative_start, - size_t* current_position); - -/** - * @brief taken from flipper_format_stream_read_valid_key - * - * @param stream - Stream* - * @param key - FuriString* output value - * @return true - * @return false - */ -bool subghz_history_stream_read_valid_key(Stream* stream, FuriString* key); - -/** - * @brief taken from flipper_format_stream_seek_to_key - * - * @param stream - Stream* - * @param key - key - * @param strict_mode - false - * @return true - * @return false - */ -bool subghz_history_stream_seek_to_key(Stream* stream, const char* key, bool strict_mode); - -/** - * @brief taken from flipper_format_stream_read_value - * - * @param stream - Stream* - * @param value - FuriString* output value - * @param last - return position is last flag - * @return true - * @return false - */ -bool subghz_history_stream_read_value(Stream* stream, FuriString* value, bool* last); diff --git a/applications/main/subghz/subghz_i.c b/applications/main/subghz/subghz_i.c index c1c17c374..cbef8fe97 100644 --- a/applications/main/subghz/subghz_i.c +++ b/applications/main/subghz/subghz_i.c @@ -106,9 +106,11 @@ static bool subghz_tx(SubGhz* subghz, uint32_t frequency) { furi_hal_gpio_write(furi_hal_subghz.cc1101_g0_pin, false); furi_hal_gpio_init( furi_hal_subghz.cc1101_g0_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); - subghz_speaker_on(subghz); bool ret = furi_hal_subghz_tx(); - subghz->txrx->txrx_state = SubGhzTxRxStateTx; + if(ret) { + subghz_speaker_on(subghz); + subghz->txrx->txrx_state = SubGhzTxRxStateTx; + } return ret; } @@ -116,6 +118,7 @@ void subghz_idle(SubGhz* subghz) { furi_assert(subghz); furi_assert(subghz->txrx->txrx_state != SubGhzTxRxStateSleep); furi_hal_subghz_idle(); + subghz_speaker_off(subghz); subghz->txrx->txrx_state = SubGhzTxRxStateIDLE; } @@ -600,7 +603,7 @@ void subghz_hopper_update(SubGhz* subghz) { if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) { subghz_rx_end(subghz); - }; + } if(subghz->txrx->txrx_state == SubGhzTxRxStateIDLE) { subghz_receiver_reset(subghz->txrx->receiver); subghz->txrx->preset->frequency = subghz_setting_get_hopper_frequency( diff --git a/applications/main/subghz/subghz_i.h b/applications/main/subghz/subghz_i.h index adabe4aad..faae35fa2 100644 --- a/applications/main/subghz/subghz_i.h +++ b/applications/main/subghz/subghz_i.h @@ -73,7 +73,9 @@ struct SubGhzTxRx { SubGhzTxRxState txrx_state; SubGhzHopperState hopper_state; SubGhzSpeakerState speaker_state; - SubGhzStarLineIgnoreState starline_state; + bool ignore_starline; + bool ignore_auto_alarms; + bool ignore_magellan; uint8_t hopper_timeout; uint8_t hopper_idx_frequency; SubGhzRxKeyState rx_key_state; diff --git a/applications/main/subghz/subghz_last_settings.c b/applications/main/subghz/subghz_last_settings.c index 6fc51554d..7ee2554b0 100644 --- a/applications/main/subghz/subghz_last_settings.c +++ b/applications/main/subghz/subghz_last_settings.c @@ -19,6 +19,7 @@ #define SUBGHZ_LAST_SETTING_FIELD_FREQUENCY_ANALYZER_TRIGGER "FATrigger" #define SUBGHZ_LAST_SETTING_FIELD_EXTERNAL_MODULE_ENABLED "External" #define SUBGHZ_LAST_SETTING_FIELD_EXTERNAL_MODULE_POWER "ExtPower" +#define SUBGHZ_LAST_SETTING_FIELD_TIMESTAMP_FILE_NAMES "TimestampNames" SubGhzLastSettings* subghz_last_settings_alloc(void) { SubGhzLastSettings* instance = malloc(sizeof(SubGhzLastSettings)); @@ -45,6 +46,7 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count float temp_frequency_analyzer_trigger = 0; bool temp_external_module_enabled = false; bool temp_external_module_power_5v_disable = false; + bool temp_timestamp_file_names = false; //int32_t temp_preset = 0; bool frequency_analyzer_feedback_level_was_read = false; bool frequency_analyzer_trigger_was_read = false; @@ -76,6 +78,11 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count SUBGHZ_LAST_SETTING_FIELD_EXTERNAL_MODULE_POWER, (bool*)&temp_external_module_power_5v_disable, 1); + flipper_format_read_bool( + fff_data_file, + SUBGHZ_LAST_SETTING_FIELD_TIMESTAMP_FILE_NAMES, + (bool*)&temp_timestamp_file_names, + 1); } else { FURI_LOG_E(TAG, "Error open file %s", SUBGHZ_LAST_SETTINGS_PATH); @@ -89,6 +96,7 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count SUBGHZ_LAST_SETTING_FREQUENCY_ANALYZER_FEEDBACK_LEVEL; instance->frequency_analyzer_trigger = SUBGHZ_LAST_SETTING_FREQUENCY_ANALYZER_TRIGGER; instance->external_module_enabled = false; + instance->timestamp_file_names = false; } else { instance->frequency = temp_frequency; @@ -109,6 +117,11 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count instance->external_module_power_5v_disable = temp_external_module_power_5v_disable; + 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(); @@ -116,7 +129,8 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count // Set selected radio module if(instance->external_module_enabled) { - furi_hal_subghz_set_radio_type(SubGhzRadioExternal); + furi_hal_subghz_select_radio_type(SubGhzRadioExternal); + furi_hal_subghz_init_radio_type(SubGhzRadioExternal); } /*/} else { @@ -189,6 +203,13 @@ bool subghz_last_settings_save(SubGhzLastSettings* instance) { 1)) { break; } + if(!flipper_format_insert_or_update_bool( + file, + SUBGHZ_LAST_SETTING_FIELD_TIMESTAMP_FILE_NAMES, + &instance->timestamp_file_names, + 1)) { + break; + } saved = true; } while(0); diff --git a/applications/main/subghz/subghz_last_settings.h b/applications/main/subghz/subghz_last_settings.h index 5e3630468..260c879f4 100644 --- a/applications/main/subghz/subghz_last_settings.h +++ b/applications/main/subghz/subghz_last_settings.h @@ -12,6 +12,7 @@ typedef struct { float frequency_analyzer_trigger; bool external_module_enabled; bool external_module_power_5v_disable; + bool timestamp_file_names; } SubGhzLastSettings; SubGhzLastSettings* subghz_last_settings_alloc(void); diff --git a/applications/main/subghz/views/receiver.c b/applications/main/subghz/views/receiver.c index 2a432deb5..0d38503da 100644 --- a/applications/main/subghz/views/receiver.c +++ b/applications/main/subghz/views/receiver.c @@ -7,7 +7,7 @@ #include #include -#include "xtreme/assets.h" +#include #define FRAME_HEIGHT 12 #define MAX_LEN_PX 111 @@ -16,8 +16,12 @@ #define SUBGHZ_RAW_THRESHOLD_MIN -90.0f +#define SCROLL_INTERVAL (606) +#define SCROLL_DELAY (2) + typedef struct { FuriString* item_str; + FuriString* time; uint8_t type; } SubGhzReceiverMenuItem; @@ -53,6 +57,7 @@ struct SubGhzViewReceiver { View* view; SubGhzViewReceiverCallback callback; void* context; + FuriTimer* scroll_timer; }; typedef struct { @@ -67,6 +72,7 @@ typedef struct { SubGhzViewReceiverBarShow bar_show; SubGhzViewReceiverMode mode; uint8_t u_rssi; + size_t scroll_counter; } SubGhzViewReceiverModel; void subghz_view_receiver_set_mode( @@ -146,6 +152,7 @@ static void subghz_view_receiver_update_offset(SubGhzViewReceiver* subghz_receiv void subghz_view_receiver_add_item_to_menu( SubGhzViewReceiver* subghz_receiver, const char* name, + const char* time, uint8_t type) { furi_assert(subghz_receiver); with_view_model( @@ -154,6 +161,7 @@ void subghz_view_receiver_add_item_to_menu( { SubGhzReceiverMenuItem* item_menu = SubGhzReceiverMenuItemArray_push_raw(model->history->data); + item_menu->time = furi_string_alloc_set(time); item_menu->item_str = furi_string_alloc_set(name); item_menu->type = type; if((model->idx == model->history_item - 1)) { @@ -242,14 +250,30 @@ void subghz_view_receiver_draw(Canvas* canvas, SubGhzViewReceiverModel* model) { size_t idx = CLAMP((uint16_t)(i + model->list_offset), model->history_item, 0); item_menu = SubGhzReceiverMenuItemArray_get(model->history->data, idx); furi_string_set(str_buff, item_menu->item_str); - elements_string_fit_width(canvas, str_buff, scrollbar ? MAX_LEN_PX - 7 : MAX_LEN_PX); + size_t scroll_counter = model->scroll_counter; if(model->idx == idx) { subghz_view_receiver_draw_frame(canvas, i, scrollbar); + if(scroll_counter < SCROLL_DELAY) { + // Show time of signal one moment + furi_string_set(str_buff, item_menu->time); + scroll_counter = 0; + } else { + scroll_counter -= SCROLL_DELAY; + } } else { canvas_set_color(canvas, ColorBlack); + scroll_counter = 0; } canvas_draw_icon(canvas, 4, 2 + i * FRAME_HEIGHT, ReceiverItemIcons[item_menu->type]); - canvas_draw_str(canvas, 15, 9 + i * FRAME_HEIGHT, furi_string_get_cstr(str_buff)); + 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); furi_string_reset(str_buff); } if(scrollbar) { @@ -357,6 +381,13 @@ 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; @@ -416,6 +447,7 @@ bool subghz_view_receiver_input(InputEvent* event, void* context) { SubGhzViewReceiverModel * model, { if(model->idx != 0) model->idx--; + model->scroll_counter = 0; }, true); } else if( @@ -427,6 +459,7 @@ bool subghz_view_receiver_input(InputEvent* event, void* context) { { if((model->history_item != 0) && (model->idx != model->history_item - 1)) model->idx++; + model->scroll_counter = 0; }, true); } else if(event->key == InputKeyLeft && event->type == InputTypeShort) { @@ -446,6 +479,7 @@ bool subghz_view_receiver_input(InputEvent* event, void* context) { if(it->index == (size_t)(model->idx)) { furi_string_free(item->item_str); + furi_string_free(item->time); item->type = 0; SubGhzReceiverMenuItemArray_remove(model->history->data, it); } @@ -479,6 +513,13 @@ 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); } void subghz_view_receiver_exit(void* context) { @@ -495,6 +536,7 @@ void subghz_view_receiver_exit(void* context) { for M_EACH(item_menu, model->history->data, SubGhzReceiverMenuItemArray_t) { furi_string_free(item_menu->item_str); + furi_string_free(item_menu->time); item_menu->type = 0; } SubGhzReceiverMenuItemArray_reset(model->history->data); @@ -504,6 +546,7 @@ void subghz_view_receiver_exit(void* context) { }, false); furi_timer_stop(subghz_receiver->timer); + furi_timer_stop(subghz_receiver->scroll_timer); } SubGhzViewReceiver* subghz_view_receiver_alloc() { @@ -522,6 +565,9 @@ 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); + with_view_model( subghz_receiver->view, SubGhzViewReceiverModel * model, @@ -543,6 +589,8 @@ SubGhzViewReceiver* subghz_view_receiver_alloc() { void subghz_view_receiver_free(SubGhzViewReceiver* subghz_receiver) { furi_assert(subghz_receiver); + furi_timer_free(subghz_receiver->scroll_timer); + with_view_model( subghz_receiver->view, SubGhzViewReceiverModel * model, @@ -554,6 +602,7 @@ void subghz_view_receiver_free(SubGhzViewReceiver* subghz_receiver) { for M_EACH(item_menu, model->history->data, SubGhzReceiverMenuItemArray_t) { furi_string_free(item_menu->item_str); + furi_string_free(item_menu->time); item_menu->type = 0; } SubGhzReceiverMenuItemArray_clear(model->history->data); diff --git a/applications/main/subghz/views/receiver.h b/applications/main/subghz/views/receiver.h index ad8c31eda..f5ec86b4b 100644 --- a/applications/main/subghz/views/receiver.h +++ b/applications/main/subghz/views/receiver.h @@ -40,6 +40,7 @@ void subghz_view_receiver_add_data_progress( void subghz_view_receiver_add_item_to_menu( SubGhzViewReceiver* subghz_receiver, const char* name, + const char* time, uint8_t type); uint16_t subghz_view_receiver_get_idx_menu(SubGhzViewReceiver* subghz_receiver); diff --git a/applications/main/subghz/views/subghz_frequency_analyzer.c b/applications/main/subghz/views/subghz_frequency_analyzer.c index 3b8c37d73..23c05e4c5 100644 --- a/applications/main/subghz/views/subghz_frequency_analyzer.c +++ b/applications/main/subghz/views/subghz_frequency_analyzer.c @@ -372,7 +372,7 @@ bool subghz_frequency_analyzer_input(InputEvent* event, void* context) { #endif if(updated) { - instance->callback(SubGhzCustomEventViewReceiverOK, instance->context); + instance->callback(SubGhzCustomEventViewFreqAnalOkShort, instance->context); } // First device receive short, then when user release button we get long @@ -385,7 +385,7 @@ bool subghz_frequency_analyzer_input(InputEvent* event, void* context) { subghz_frequency_analyzer_worker_stop(instance->worker); } - instance->callback(SubGhzCustomEventViewReceiverUnlock, instance->context); + instance->callback(SubGhzCustomEventViewFreqAnalOkLong, instance->context); } } diff --git a/applications/main/subghz/views/subghz_read_raw.c b/applications/main/subghz/views/subghz_read_raw.c index fcc077efa..7676c527d 100644 --- a/applications/main/subghz/views/subghz_read_raw.c +++ b/applications/main/subghz/views/subghz_read_raw.c @@ -11,28 +11,6 @@ #define SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE 100 #define TAG "SubGhzReadRAW" -struct SubGhzReadRAW { - View* view; - SubGhzReadRAWCallback callback; - void* context; -}; - -typedef struct { - FuriString* frequency_str; - FuriString* preset_str; - FuriString* sample_write; - FuriString* file_name; - uint8_t* rssi_history; - uint8_t rssi_current; - bool rssi_history_end; - uint8_t ind_write; - uint8_t ind_sin; - SubGhzReadRAWStatus status; - bool raw_send_only; - float raw_threshold_rssi; - bool not_showing_samples; -} SubGhzReadRAWModel; - void subghz_read_raw_set_callback( SubGhzReadRAW* subghz_read_raw, SubGhzReadRAWCallback callback, diff --git a/applications/main/subghz/views/subghz_read_raw.h b/applications/main/subghz/views/subghz_read_raw.h index 9d63870d5..f9a56ca04 100644 --- a/applications/main/subghz/views/subghz_read_raw.h +++ b/applications/main/subghz/views/subghz_read_raw.h @@ -5,10 +5,14 @@ #define SUBGHZ_RAW_THRESHOLD_MIN -90.0f -typedef struct SubGhzReadRAW SubGhzReadRAW; - typedef void (*SubGhzReadRAWCallback)(SubGhzCustomEvent event, void* context); +typedef struct { + View* view; + SubGhzReadRAWCallback callback; + void* context; +} SubGhzReadRAW; + typedef enum { SubGhzReadRAWStatusStart, SubGhzReadRAWStatusIDLE, @@ -22,6 +26,22 @@ typedef enum { SubGhzReadRAWStatusSaveKey, } SubGhzReadRAWStatus; +typedef struct { + FuriString* frequency_str; + FuriString* preset_str; + FuriString* sample_write; + FuriString* file_name; + uint8_t* rssi_history; + uint8_t rssi_current; + bool rssi_history_end; + uint8_t ind_write; + uint8_t ind_sin; + SubGhzReadRAWStatus status; + bool raw_send_only; + float raw_threshold_rssi; + bool not_showing_samples; +} SubGhzReadRAWModel; + void subghz_read_raw_set_callback( SubGhzReadRAW* subghz_read_raw, SubGhzReadRAWCallback callback, diff --git a/applications/main/subghz/views/transmitter.c b/applications/main/subghz/views/transmitter.c index e8c53d1ed..5667cd59f 100644 --- a/applications/main/subghz/views/transmitter.c +++ b/applications/main/subghz/views/transmitter.c @@ -6,21 +6,6 @@ #include -struct SubGhzViewTransmitter { - View* view; - SubGhzViewTransmitterCallback callback; - void* context; -}; - -typedef struct { - FuriString* frequency_str; - FuriString* preset_str; - FuriString* key_str; - uint8_t show_button; - FuriString* temp_button_id; - bool draw_temp_button; -} SubGhzViewTransmitterModel; - void subghz_view_transmitter_set_callback( SubGhzViewTransmitter* subghz_transmitter, SubGhzViewTransmitterCallback callback, @@ -137,6 +122,7 @@ bool subghz_view_transmitter_input(InputEvent* event, void* context) { true); if(can_be_sent && event->key == InputKeyOk && event->type == InputTypePress) { + subghz_custom_btn_set(0); with_view_model( subghz_transmitter->view, SubGhzViewTransmitterModel * model, diff --git a/applications/main/subghz/views/transmitter.h b/applications/main/subghz/views/transmitter.h index 64bcbd1af..1ae51afbd 100644 --- a/applications/main/subghz/views/transmitter.h +++ b/applications/main/subghz/views/transmitter.h @@ -3,10 +3,23 @@ #include #include "../helpers/subghz_custom_event.h" -typedef struct SubGhzViewTransmitter SubGhzViewTransmitter; +typedef struct { + FuriString* frequency_str; + FuriString* preset_str; + FuriString* key_str; + uint8_t show_button; + FuriString* temp_button_id; + bool draw_temp_button; +} SubGhzViewTransmitterModel; typedef void (*SubGhzViewTransmitterCallback)(SubGhzCustomEvent event, void* context); +typedef struct { + View* view; + SubGhzViewTransmitterCallback callback; + void* context; +} SubGhzViewTransmitter; + void subghz_view_transmitter_set_callback( SubGhzViewTransmitter* subghz_transmitter, SubGhzViewTransmitterCallback callback, diff --git a/applications/main/u2f/scenes/u2f_scene_error.c b/applications/main/u2f/scenes/u2f_scene_error.c index d87b13063..f77f3d731 100644 --- a/applications/main/u2f/scenes/u2f_scene_error.c +++ b/applications/main/u2f/scenes/u2f_scene_error.c @@ -1,5 +1,5 @@ #include "../u2f_app_i.h" -#include "xtreme/assets.h" +#include static void u2f_scene_error_event_callback(GuiButtonType result, InputType type, void* context) { furi_assert(context); diff --git a/applications/main/u2f/u2f_app.c b/applications/main/u2f/u2f_app.c index 216481976..60500152a 100644 --- a/applications/main/u2f/u2f_app.c +++ b/applications/main/u2f/u2f_app.c @@ -2,6 +2,7 @@ #include "u2f_data.h" #include #include +#include static bool u2f_app_custom_event_callback(void* context, uint32_t event) { furi_assert(context); @@ -27,6 +28,11 @@ U2fApp* u2f_app_alloc() { app->gui = furi_record_open(RECORD_GUI); app->notifications = furi_record_open(RECORD_NOTIFICATION); + Storage* storage = furi_record_open(RECORD_STORAGE); + storage_common_copy(storage, U2F_CNT_OLD_FILE, U2F_CNT_FILE); + storage_common_copy(storage, U2F_KEY_OLD_FILE, U2F_KEY_FILE); + furi_record_close(RECORD_STORAGE); + app->view_dispatcher = view_dispatcher_alloc(); app->scene_manager = scene_manager_alloc(&u2f_scene_handlers, app); view_dispatcher_enable_queue(app->view_dispatcher); diff --git a/applications/main/u2f/u2f_data.c b/applications/main/u2f/u2f_data.c index 217733c94..34f037aed 100644 --- a/applications/main/u2f/u2f_data.c +++ b/applications/main/u2f/u2f_data.c @@ -7,12 +7,6 @@ #define TAG "U2F" -#define U2F_DATA_FOLDER ANY_PATH("u2f/") -#define U2F_CERT_FILE U2F_DATA_FOLDER "assets/cert.der" -#define U2F_CERT_KEY_FILE U2F_DATA_FOLDER "assets/cert_key.u2f" -#define U2F_KEY_FILE U2F_DATA_FOLDER "key.u2f" -#define U2F_CNT_FILE U2F_DATA_FOLDER "cnt.u2f" - #define U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_FACTORY 2 #define U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE 11 diff --git a/applications/main/u2f/u2f_data.h b/applications/main/u2f/u2f_data.h index 8d3923464..f3c62f89d 100644 --- a/applications/main/u2f/u2f_data.h +++ b/applications/main/u2f/u2f_data.h @@ -6,6 +6,14 @@ extern "C" { #include +#define U2F_DATA_FOLDER EXT_PATH("u2f/") +#define U2F_CERT_FILE U2F_DATA_FOLDER "assets/cert.der" +#define U2F_CERT_KEY_FILE U2F_DATA_FOLDER "assets/cert_key.u2f" +#define U2F_KEY_OLD_FILE U2F_DATA_FOLDER "key.u2f" +#define U2F_CNT_OLD_FILE U2F_DATA_FOLDER "cnt.u2f" +#define U2F_KEY_FILE INT_PATH(".key.u2f") +#define U2F_CNT_FILE INT_PATH(".cnt.u2f") + bool u2f_data_check(bool cert_only); bool u2f_data_cert_check(); diff --git a/applications/main/u2f/views/u2f_view.c b/applications/main/u2f/views/u2f_view.c index 7bd2cf94f..4b76c2187 100644 --- a/applications/main/u2f/views/u2f_view.c +++ b/applications/main/u2f/views/u2f_view.c @@ -1,7 +1,7 @@ #include "u2f_view.h" #include #include -#include "xtreme/assets.h" +#include struct U2fView { View* view; diff --git a/applications/main/xtreme_app/application.fam b/applications/main/xtreme_app/application.fam index 38623386e..ce39a8739 100644 --- a/applications/main/xtreme_app/application.fam +++ b/applications/main/xtreme_app/application.fam @@ -7,7 +7,6 @@ App( requires=[ "gui", "dolphin", - "xtreme", ], stack_size=2 * 1024, icon="A_Xtreme_14", diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_common.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_common.c index 259d8a260..54b0241d9 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_common.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_common.c @@ -37,6 +37,16 @@ static void xtreme_app_scene_interface_common_left_handed_changed(VariableItem* } } +static void xtreme_app_scene_interface_common_favorite_timeout_changed(VariableItem* item) { + XtremeApp* app = variable_item_get_context(item); + uint32_t value = variable_item_get_current_value_index(item); + char text[6]; + snprintf(text, sizeof(text), "%lu S", value); + variable_item_set_current_value_text(item, value ? text : "OFF"); + XTREME_SETTINGS()->favorite_timeout = value; + app->save_settings = true; +} + void xtreme_app_scene_interface_common_on_enter(void* context) { XtremeApp* app = context; XtremeSettings* xtreme_settings = XTREME_SETTINGS(); @@ -67,6 +77,17 @@ void xtreme_app_scene_interface_common_on_enter(void* context) { variable_item_set_current_value_index(item, value); variable_item_set_current_value_text(item, value ? "ON" : "OFF"); + item = variable_item_list_add( + var_item_list, + "Favorite Timeout", + 61, + xtreme_app_scene_interface_common_favorite_timeout_changed, + app); + variable_item_set_current_value_index(item, xtreme_settings->favorite_timeout); + char text[4]; + snprintf(text, sizeof(text), "%lu S", xtreme_settings->favorite_timeout); + 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); 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 2cfb1f7cc..462f7c6d3 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 @@ -20,7 +20,7 @@ static void xtreme_app_scene_interface_graphics_asset_pack_changed(VariableItem* strlcpy( XTREME_SETTINGS()->asset_pack, index == 0 ? "" : *CharList_get(app->asset_pack_names, index - 1), - MAX_PACK_NAME_LEN); + XTREME_ASSETS_PACK_NAME_LEN); app->asset_pack_index = index; app->save_settings = true; app->require_reboot = true; 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 5544ee941..0fc1e1e9c 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 @@ -10,6 +10,14 @@ void xtreme_app_scene_interface_lockscreen_var_item_list_callback(void* context, view_dispatcher_send_custom_event(app->view_dispatcher, index); } +static void xtreme_app_scene_interface_lockscreen_bad_pins_format_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()->bad_pins_format = value; + app->save_settings = true; +} + static void xtreme_app_scene_interface_lockscreen_show_time_changed(VariableItem* item) { XtremeApp* app = variable_item_get_context(item); bool value = variable_item_get_current_value_index(item); @@ -18,6 +26,14 @@ 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) { + XtremeApp* app = variable_item_get_context(item); + bool value = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, value ? "ON" : "OFF"); + XTREME_SETTINGS()->lockscreen_seconds = value; + app->save_settings = true; +} + static void xtreme_app_scene_interface_lockscreen_show_date_changed(VariableItem* item) { XtremeApp* app = variable_item_get_context(item); bool value = variable_item_get_current_value_index(item); @@ -48,6 +64,15 @@ void xtreme_app_scene_interface_lockscreen_on_enter(void* context) { VariableItemList* var_item_list = app->var_item_list; VariableItem* item; + item = variable_item_list_add( + var_item_list, + "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, "Show Time", @@ -57,6 +82,15 @@ void xtreme_app_scene_interface_lockscreen_on_enter(void* context) { variable_item_set_current_value_index(item, xtreme_settings->lockscreen_time); variable_item_set_current_value_text(item, xtreme_settings->lockscreen_time ? "ON" : "OFF"); + item = variable_item_list_add( + var_item_list, + "Show Seconds", + 2, + xtreme_app_scene_interface_lockscreen_show_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"); + item = variable_item_list_add( var_item_list, "Show Date", diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_misc_rename.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_misc_rename.c index a5f5dd8c5..47a0fb607 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_misc_rename.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_misc_rename.c @@ -12,12 +12,31 @@ static void xtreme_app_scene_misc_rename_text_input_callback(void* context) { view_dispatcher_send_custom_event(app->view_dispatcher, TextInputResultOk); } +static bool + xtreme_app_scene_misc_rename_validator(const char* text, FuriString* error, void* context) { + UNUSED(context); + + for(; *text; ++text) { + const char c = *text; + if((c < '0' || c > '9') && (c < 'A' || c > 'Z') && (c < 'a' || c > 'z')) { + furi_string_printf(error, "Please only\nenter letters\nand numbers!"); + return false; + } + } + + return true; +} + void xtreme_app_scene_misc_rename_on_enter(void* context) { XtremeApp* app = context; TextInput* text_input = app->text_input; text_input_set_header_text(text_input, "Leave empty for default"); + text_input_set_validator(text_input, xtreme_app_scene_misc_rename_validator, NULL); + + text_input_set_minimum_length(text_input, 0); + text_input_set_result_callback( text_input, xtreme_app_scene_misc_rename_text_input_callback, 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 eb8285e34..d8244d30d 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols.c @@ -55,7 +55,7 @@ void xtreme_app_scene_protocols_on_enter(void* context) { variable_item_set_current_value_text(item, xtreme_settings->bad_bt ? "BT" : "USB"); item = variable_item_list_add( - var_item_list, "BadBT Rmembr", 2, xtreme_app_scene_protocols_badbt_remember_changed, app); + var_item_list, "BadBT Remember", 2, xtreme_app_scene_protocols_badbt_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"); diff --git a/applications/main/xtreme_app/xtreme_app.c b/applications/main/xtreme_app/xtreme_app.c index eabd25581..57706233e 100644 --- a/applications/main/xtreme_app/xtreme_app.c +++ b/applications/main/xtreme_app/xtreme_app.c @@ -188,12 +188,12 @@ XtremeApp* xtreme_app_alloc() { Storage* storage = furi_record_open(RECORD_STORAGE); File* folder = storage_file_alloc(storage); FileInfo info; - char* name = malloc(MAX_PACK_NAME_LEN); - if(storage_dir_open(folder, PACKS_DIR)) { - while(storage_dir_read(folder, &info, name, MAX_PACK_NAME_LEN)) { + char* name = malloc(XTREME_ASSETS_PACK_NAME_LEN); + if(storage_dir_open(folder, XTREME_ASSETS_PATH)) { + while(storage_dir_read(folder, &info, name, XTREME_ASSETS_PACK_NAME_LEN)) { if(info.flags & FSF_DIRECTORY) { - char* copy = malloc(MAX_PACK_NAME_LEN); - strlcpy(copy, name, MAX_PACK_NAME_LEN); + char* copy = malloc(XTREME_ASSETS_PACK_NAME_LEN); + strlcpy(copy, name, XTREME_ASSETS_PACK_NAME_LEN); uint idx = 0; if(strcmp(copy, "NSFW") != 0) { for(; idx < CharList_size(app->asset_pack_names); idx++) { diff --git a/applications/main/xtreme_app/xtreme_app.h b/applications/main/xtreme_app/xtreme_app.h index a352d86ee..15cd71ce9 100644 --- a/applications/main/xtreme_app/xtreme_app.h +++ b/applications/main/xtreme_app/xtreme_app.h @@ -23,8 +23,7 @@ #include #include #include -#include "xtreme/settings.h" -#include "xtreme/assets.h" +#include #define XTREME_SUBGHZ_FREQ_BUFFER_SIZE 6 diff --git a/applications/services/application.fam b/applications/services/application.fam index 62aec990d..af02ef1a1 100644 --- a/applications/services/application.fam +++ b/applications/services/application.fam @@ -6,7 +6,6 @@ App( "crypto_start", "rpc_start", "bt", - "xtreme", "desktop", "loader", "power", diff --git a/applications/services/bt/bt_service/bt.c b/applications/services/bt/bt_service/bt.c index 9cb9f1b24..b4a6b25a4 100644 --- a/applications/services/bt/bt_service/bt.c +++ b/applications/services/bt/bt_service/bt.c @@ -5,7 +5,7 @@ #include #include #include -#include "xtreme/assets.h" +#include #define TAG "BtSrv" @@ -240,7 +240,7 @@ static bool bt_on_gap_event_callback(GapEvent event, void* context) { furi_event_flag_clear(bt->rpc_event, BT_RPC_EVENT_DISCONNECTED); if(bt->profile == BtProfileSerial) { // Open RPC session - bt->rpc_session = rpc_session_open(bt->rpc); + bt->rpc_session = rpc_session_open(bt->rpc, RpcOwnerBle); if(bt->rpc_session) { FURI_LOG_I(TAG, "Open RPC connection"); rpc_session_set_send_bytes_callback(bt->rpc_session, bt_rpc_send_bytes_callback); @@ -469,7 +469,7 @@ int32_t bt_srv(void* p) { UNUSED(p); Bt* bt = bt_alloc(); - if(furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal) { + if(!furi_hal_is_normal_boot()) { FURI_LOG_W(TAG, "Skipping start in special boot mode"); ble_glue_wait_for_c2_start(FURI_HAL_BT_C2_START_TIMEOUT); furi_record_create(RECORD_BT, bt); diff --git a/applications/services/cli/cli.c b/applications/services/cli/cli.c index ad3bbd665..a6aaa8429 100644 --- a/applications/services/cli/cli.c +++ b/applications/services/cli/cli.c @@ -460,7 +460,7 @@ int32_t cli_srv(void* p) { furi_thread_set_stdout_callback(NULL); } - if(furi_hal_rtc_get_boot_mode() == FuriHalRtcBootModeNormal) { + if(furi_hal_is_normal_boot()) { cli_session_open(cli, &cli_vcp); } else { FURI_LOG_W(TAG, "Skipping start in special boot mode"); diff --git a/applications/services/cli/cli_commands.c b/applications/services/cli/cli_commands.c index a8d1b7e67..a652d0f0c 100644 --- a/applications/services/cli/cli_commands.c +++ b/applications/services/cli/cli_commands.c @@ -79,7 +79,7 @@ void cli_command_help(Cli* cli, FuriString* args, void* context) { printf("%s", furi_string_get_cstr(*CliCommandTree_ref(it_right)->key_ptr)); CliCommandTree_next(it_right); } - }; + } if(furi_string_size(args) > 0) { cli_nl(); diff --git a/applications/services/crypto/crypto_cli.c b/applications/services/crypto/crypto_cli.c index d91b448ec..34aa34dd3 100644 --- a/applications/services/crypto/crypto_cli.c +++ b/applications/services/crypto/crypto_cli.c @@ -15,7 +15,7 @@ void crypto_cli_print_usage() { printf("\thas_key \t - Check if secure enclave has key in slot\r\n"); printf( "\tstore_key \t - Store key in secure enclave. !!! NON-REVERSIBLE OPERATION - READ MANUAL FIRST !!!\r\n"); -}; +} void crypto_cli_encrypt(Cli* cli, FuriString* args) { int key_slot = 0; diff --git a/applications/services/desktop/animations/animation_manager.c b/applications/services/desktop/animations/animation_manager.c index f4bf320ff..394ffee30 100644 --- a/applications/services/desktop/animations/animation_manager.c +++ b/applications/services/desktop/animations/animation_manager.c @@ -14,7 +14,7 @@ #include "animation_storage.h" #include "animation_manager.h" -#include "xtreme/assets.h" +#include #define TAG "AnimationManager" diff --git a/applications/services/desktop/animations/animation_storage.c b/applications/services/desktop/animations/animation_storage.c index 30fa0c014..0905a6d43 100644 --- a/applications/services/desktop/animations/animation_storage.c +++ b/applications/services/desktop/animations/animation_storage.c @@ -11,7 +11,7 @@ #include "animation_storage_i.h" #include #include -#include "xtreme/assets.h" +#include #define ANIMATION_META_FILE "meta.txt" #define BASE_ANIMATION_DIR EXT_PATH("dolphin") #define TAG "AnimationStorage" @@ -20,7 +20,7 @@ #define ANIMATION_MANIFEST_FILE ANIMATION_DIR "/manifest.txt" */ -// 59 Max length = strlen("/ext/dolphin_custom//Anims") + MAX_PACK_NAME_LEN + 1 (Null terminator) +// 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]; @@ -36,7 +36,8 @@ void animation_handler_select_manifest(bool force_stock) { FuriString* manifest = furi_string_alloc(); bool use_asset_pack = !force_stock && xtreme_settings->asset_pack[0] != '\0'; if(use_asset_pack) { - furi_string_printf(anim_dir, "%s/%s/Anims", PACKS_DIR, xtreme_settings->asset_pack); + furi_string_printf( + anim_dir, "%s/%s/Anims", XTREME_ASSETS_PATH, xtreme_settings->asset_pack); furi_string_printf(manifest, "%s/manifest.txt", furi_string_get_cstr(anim_dir)); Storage* storage = furi_record_open(RECORD_STORAGE); if(storage_common_stat(storage, furi_string_get_cstr(manifest), NULL) == FSE_OK) { diff --git a/applications/services/desktop/desktop.c b/applications/services/desktop/desktop.c index a578ecfcb..45435411a 100644 --- a/applications/services/desktop/desktop.c +++ b/applications/services/desktop/desktop.c @@ -15,6 +15,7 @@ #include "desktop/views/desktop_view_pin_timeout.h" #include "desktop_i.h" #include "helpers/pin_lock.h" +#include #define TAG "Desktop" @@ -39,6 +40,12 @@ static void desktop_lock_icon_draw_callback(Canvas* canvas, void* context) { canvas_draw_icon(canvas, 0, 0, &I_Lock_7x8); } +static void desktop_stealth_mode_icon_draw_callback(Canvas* canvas, void* context) { + UNUSED(context); + furi_assert(canvas); + canvas_draw_icon(canvas, 0, 0, &I_Muted_8x8); +} + static bool desktop_custom_event_callback(void* context, uint32_t event) { furi_assert(context); Desktop* desktop = (Desktop*)context; @@ -139,6 +146,18 @@ void desktop_unlock(Desktop* desktop) { desktop_auto_lock_arm(desktop); } +void desktop_set_stealth_mode_state(Desktop* desktop, bool enabled) { + desktop->in_transition = true; + if(enabled) { + furi_hal_rtc_set_flag(FuriHalRtcFlagStealthMode); + } else { + furi_hal_rtc_reset_flag(FuriHalRtcFlagStealthMode); + } + desktop_lock_menu_set_stealth_mode_state(desktop->lock_menu, enabled); + view_port_enabled_set(desktop->stealth_mode_icon_viewport, enabled); + desktop->in_transition = false; +} + Desktop* desktop_alloc() { Desktop* desktop = malloc(sizeof(Desktop)); @@ -222,6 +241,18 @@ Desktop* desktop_alloc() { view_port_enabled_set(desktop->lock_icon_viewport, false); gui_add_view_port(desktop->gui, desktop->lock_icon_viewport, GuiLayerStatusBarLeft); + // Stealth mode icon + desktop->stealth_mode_icon_viewport = view_port_alloc(); + view_port_set_width(desktop->stealth_mode_icon_viewport, icon_get_width(&I_Muted_8x8)); + view_port_draw_callback_set( + desktop->stealth_mode_icon_viewport, desktop_stealth_mode_icon_draw_callback, desktop); + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagStealthMode)) { + view_port_enabled_set(desktop->stealth_mode_icon_viewport, true); + } else { + view_port_enabled_set(desktop->stealth_mode_icon_viewport, false); + } + gui_add_view_port(desktop->gui, desktop->stealth_mode_icon_viewport, GuiLayerStatusBarLeft); + // Special case: autostart application is already running desktop->loader = furi_record_open(RECORD_LOADER); if(loader_is_locked(desktop->loader) && @@ -304,11 +335,22 @@ static bool desktop_check_file_flag(const char* flag_path) { int32_t desktop_srv(void* p) { UNUSED(p); - if(furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal) { + if(!furi_hal_is_normal_boot()) { FURI_LOG_W(TAG, "Skipping start in special boot mode"); return 0; } + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagResetPin)) { + Storage* storage = furi_record_open(RECORD_STORAGE); + storage_common_remove(storage, DESKTOP_SETTINGS_PATH); + storage_common_remove(storage, DESKTOP_SETTINGS_OLD_PATH); + furi_record_close(RECORD_STORAGE); + furi_hal_rtc_reset_flag(FuriHalRtcFlagResetPin); + } + + XTREME_SETTINGS_LOAD(); + XTREME_ASSETS_LOAD(); + Desktop* desktop = desktop_alloc(); bool loaded = DESKTOP_SETTINGS_LOAD(&desktop->settings); diff --git a/applications/services/desktop/desktop_i.h b/applications/services/desktop/desktop_i.h index 822eecc76..988092f0e 100644 --- a/applications/services/desktop/desktop_i.h +++ b/applications/services/desktop/desktop_i.h @@ -58,6 +58,7 @@ struct Desktop { DesktopViewPinInput* pin_input_view; ViewPort* lock_icon_viewport; + ViewPort* stealth_mode_icon_viewport; AnimationManager* animation_manager; @@ -77,3 +78,4 @@ Desktop* desktop_alloc(); void desktop_free(Desktop* desktop); void desktop_lock(Desktop* desktop); void desktop_unlock(Desktop* desktop); +void desktop_set_stealth_mode_state(Desktop* desktop, bool enabled); diff --git a/applications/services/desktop/helpers/pin_lock.c b/applications/services/desktop/helpers/pin_lock.c index 22fcabe7d..2439a4ebc 100644 --- a/applications/services/desktop/helpers/pin_lock.c +++ b/applications/services/desktop/helpers/pin_lock.c @@ -10,6 +10,7 @@ #include "../desktop_i.h" #include #include +#include static const NotificationSequence sequence_pin_fail = { &message_display_backlight_on, @@ -30,20 +31,6 @@ static const NotificationSequence sequence_pin_fail = { NULL, }; -static const uint8_t desktop_helpers_fails_timeout[] = { - 0, - 0, - 0, - 0, - 30, - 60, - 90, - 120, - 150, - 180, - /* +60 for every next fail */ -}; - void desktop_pin_lock_error_notify() { NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); notification_message(notification, &sequence_pin_fail); @@ -52,15 +39,10 @@ void desktop_pin_lock_error_notify() { uint32_t desktop_pin_lock_get_fail_timeout() { uint32_t pin_fails = furi_hal_rtc_get_pin_fails(); - uint32_t pin_timeout = 0; - uint32_t max_index = COUNT_OF(desktop_helpers_fails_timeout) - 1; - if(pin_fails <= max_index) { - pin_timeout = desktop_helpers_fails_timeout[pin_fails]; - } else { - pin_timeout = desktop_helpers_fails_timeout[max_index] + (pin_fails - max_index) * 60; + if(pin_fails < 3) { + return 0; } - - return pin_timeout; + return 30 * pow(2, pin_fails - 3); } void desktop_pin_lock(DesktopSettings* settings) { @@ -117,6 +99,16 @@ bool desktop_pin_lock_verify(const PinCode* pin_set, const PinCode* pin_entered) result = true; } else { uint32_t pin_fails = furi_hal_rtc_get_pin_fails(); + if(pin_fails >= 9 && XTREME_SETTINGS()->bad_pins_format) { + furi_hal_rtc_set_pin_fails(0); + furi_hal_rtc_set_flag(FuriHalRtcFlagFactoryReset); + Storage* storage = furi_record_open(RECORD_STORAGE); + storage_simply_remove(storage, INT_PATH(".cnt.u2f")); + storage_simply_remove(storage, INT_PATH(".key.u2f")); + storage_sd_format(storage); + furi_record_close(RECORD_STORAGE); + power_reboot(PowerBootModeNormal); + } furi_hal_rtc_set_pin_fails(pin_fails + 1); result = false; } diff --git a/applications/services/desktop/scenes/desktop_scene_fault.c b/applications/services/desktop/scenes/desktop_scene_fault.c index b3801d78d..2b2dce50c 100644 --- a/applications/services/desktop/scenes/desktop_scene_fault.c +++ b/applications/services/desktop/scenes/desktop_scene_fault.c @@ -1,7 +1,7 @@ #include #include "../desktop_i.h" -#include "xtreme/assets.h" +#include #define DesktopFaultEventExit 0x00FF00FF diff --git a/applications/services/desktop/scenes/desktop_scene_lock_menu.c b/applications/services/desktop/scenes/desktop_scene_lock_menu.c index 3b2309844..e68d28076 100644 --- a/applications/services/desktop/scenes/desktop_scene_lock_menu.c +++ b/applications/services/desktop/scenes/desktop_scene_lock_menu.c @@ -5,7 +5,7 @@ #include #include // #include -#include +#include #include "../desktop_i.h" #include @@ -28,6 +28,8 @@ void desktop_scene_lock_menu_on_enter(void* context) { 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_stealth_mode_state( + desktop->lock_menu, furi_hal_rtc_is_flag_set(FuriHalRtcFlagStealthMode)); desktop_lock_menu_set_idx(desktop->lock_menu, 3); Gui* gui = furi_record_open(RECORD_GUI); @@ -129,6 +131,12 @@ bool desktop_scene_lock_menu_on_event(void* context, SceneManagerEvent event) { desktop->loader, FAP_LOADER_APP_NAME, EXT_PATH("apps/.Main/xtreme_app.fap")); consumed = true; break; + case DesktopLockMenuEventStealthModeOn: + desktop_set_stealth_mode_state(desktop, true); + break; + case DesktopLockMenuEventStealthModeOff: + desktop_set_stealth_mode_state(desktop, false); + break; default: break; } diff --git a/applications/services/desktop/views/desktop_events.h b/applications/services/desktop/views/desktop_events.h index 9e4a3bb4d..af7597012 100644 --- a/applications/services/desktop/views/desktop_events.h +++ b/applications/services/desktop/views/desktop_events.h @@ -44,6 +44,8 @@ typedef enum { DesktopLockMenuEventLockPin, DesktopLockMenuEventLockPinOff, DesktopLockMenuEventXtreme, + DesktopLockMenuEventStealthModeOn, + DesktopLockMenuEventStealthModeOff, DesktopAnimationEventCheckAnimation, DesktopAnimationEventNewIdleAnimation, diff --git a/applications/services/desktop/views/desktop_view_lock_menu.c b/applications/services/desktop/views/desktop_view_lock_menu.c index 0ccc4f326..204dd2cfd 100644 --- a/applications/services/desktop/views/desktop_view_lock_menu.c +++ b/applications/services/desktop/views/desktop_view_lock_menu.c @@ -1,7 +1,7 @@ #include #include #include -#include +#include #include #include "../desktop_i.h" @@ -48,6 +48,14 @@ void desktop_lock_menu_set_pin_state(DesktopLockMenuView* lock_menu, bool pin_is true); } +void desktop_lock_menu_set_stealth_mode_state(DesktopLockMenuView* lock_menu, bool stealth_mode) { + with_view_model( + lock_menu->view, + DesktopLockMenuViewModel * model, + { model->stealth_mode = stealth_mode; }, + true); +} + void desktop_lock_menu_set_idx(DesktopLockMenuView* lock_menu, uint8_t idx) { furi_assert(idx < DesktopLockMenuIndexTotalCount); with_view_model( @@ -110,7 +118,7 @@ void desktop_lock_menu_draw_callback(Canvas* canvas, void* model) { value = total - m->lock_menu->notification->settings.display_brightness * total; break; case DesktopLockMenuIndexVolume: - icon = &I_Volup_8x6; + icon = m->stealth_mode ? &I_Muted_8x8 : &I_Volup_8x6; value = total - m->lock_menu->notification->settings.speaker_volume * total; break; default: @@ -181,6 +189,7 @@ bool desktop_lock_menu_input_callback(InputEvent* event, void* context) { uint8_t idx = 0; int pin_lock = 0; bool show_lock_menu = false; + bool stealth_mode = false; bool consumed = true; with_view_model( @@ -188,6 +197,7 @@ bool desktop_lock_menu_input_callback(InputEvent* event, void* context) { DesktopLockMenuViewModel * model, { show_lock_menu = model->show_lock_menu; + stealth_mode = model->stealth_mode; if((event->type == InputTypeShort) || (event->type == InputTypeRepeat)) { if(model->show_lock_menu) { if(event->key == InputKeyUp) { @@ -292,6 +302,10 @@ bool desktop_lock_menu_input_callback(InputEvent* event, void* context) { case DesktopLockMenuIndexXtreme: desktop_event = DesktopLockMenuEventXtreme; break; + case DesktopLockMenuIndexVolume: + desktop_event = stealth_mode ? DesktopLockMenuEventStealthModeOff : + DesktopLockMenuEventStealthModeOn; + break; default: break; } diff --git a/applications/services/desktop/views/desktop_view_lock_menu.h b/applications/services/desktop/views/desktop_view_lock_menu.h index 957551b36..67d0f6f67 100644 --- a/applications/services/desktop/views/desktop_view_lock_menu.h +++ b/applications/services/desktop/views/desktop_view_lock_menu.h @@ -28,6 +28,7 @@ typedef struct { int pin_lock; bool show_lock_menu; DesktopLockMenuView* lock_menu; + bool stealth_mode; } DesktopLockMenuViewModel; void desktop_lock_menu_set_callback( @@ -37,6 +38,7 @@ void desktop_lock_menu_set_callback( View* desktop_lock_menu_get_view(DesktopLockMenuView* lock_menu); void desktop_lock_menu_set_pin_state(DesktopLockMenuView* lock_menu, bool pin_is_set); +void desktop_lock_menu_set_stealth_mode_state(DesktopLockMenuView* lock_menu, bool stealth_mode); void desktop_lock_menu_set_idx(DesktopLockMenuView* lock_menu, uint8_t idx); DesktopLockMenuView* desktop_lock_menu_alloc(); void desktop_lock_menu_free(DesktopLockMenuView* lock_menu); diff --git a/applications/services/desktop/views/desktop_view_locked.c b/applications/services/desktop/views/desktop_view_locked.c index 9a24414f7..d7cc7f236 100644 --- a/applications/services/desktop/views/desktop_view_locked.c +++ b/applications/services/desktop/views/desktop_view_locked.c @@ -7,7 +7,7 @@ #include #include #include -#include +#include #include #include "../desktop_i.h" @@ -67,22 +67,24 @@ void desktop_view_locked_draw_lockscreen(Canvas* canvas, void* m) { DesktopViewLockedModel* model = m; int y = model->cover_offset; char time_str[9]; + char second_str[5]; char date_str[14]; char meridian_str[3]; FuriHalRtcDateTime datetime; furi_hal_rtc_get_datetime(&datetime); LocaleTimeFormat time_format = locale_get_time_format(); LocaleDateFormat date_format = locale_get_date_format(); + XtremeSettings* xtreme_settings = XTREME_SETTINGS(); + bool pm; if(time_format == LocaleTimeFormat24h) { - snprintf(time_str, 9, "%.2d:%.2d", datetime.hour, datetime.minute); + pm = false; } else { - bool pm = datetime.hour > 12; - bool pm12 = datetime.hour >= 12; - snprintf( - time_str, 9, "%.2d:%.2d", pm ? datetime.hour - 12 : datetime.hour, datetime.minute); - snprintf(meridian_str, 3, pm12 ? "PM" : "AM"); + pm = datetime.hour > 12; + snprintf(meridian_str, 3, datetime.hour >= 12 ? "PM" : "AM"); } + snprintf(time_str, 9, "%.2d:%.2d", pm ? datetime.hour - 12 : datetime.hour, datetime.minute); + snprintf(second_str, 5, ":%.2d", datetime.second); if(date_format == LocaleDateFormatYMD) { snprintf(date_str, 14, "%.4d-%.2d-%.2d", datetime.year, datetime.month, datetime.day); @@ -92,15 +94,19 @@ void desktop_view_locked_draw_lockscreen(Canvas* canvas, void* m) { snprintf(date_str, 14, "%.2d-%.2d-%.4d", datetime.day, datetime.month, datetime.year); } - XtremeSettings* xtreme_settings = XTREME_SETTINGS(); canvas_draw_icon(canvas, 0, 0 + y, XTREME_ASSETS()->I_Lockscreen); if(xtreme_settings->lockscreen_time) { canvas_set_font(canvas, FontBigNumbers); canvas_draw_str(canvas, 0, 64 + y, time_str); - if(time_format == LocaleTimeFormat12h) { - int meridian_offset = canvas_string_width(canvas, time_str) + 2; + int offset = canvas_string_width(canvas, time_str) + 2; + if(xtreme_settings->lockscreen_seconds) { canvas_set_font(canvas, FontSecondary); - canvas_draw_str(canvas, 0 + meridian_offset, 64 + y, meridian_str); + canvas_draw_str(canvas, 0 + offset, 64 + y, second_str); + offset += canvas_string_width(canvas, ":00") + 2; + } + if(time_format == LocaleTimeFormat12h) { + canvas_set_font(canvas, FontKeyboard); + canvas_draw_str(canvas, 0 + offset, 64 + y, meridian_str); } } if(xtreme_settings->lockscreen_date) { @@ -303,5 +309,6 @@ bool desktop_view_locked_is_locked_hint_visible(DesktopViewLocked* locked_view) DesktopViewLockedModel* model = view_get_model(locked_view->view); const DesktopViewLockedState view_state = model->view_state; view_commit_model(locked_view->view, false); - return view_state == DesktopViewLockedStateLockedHintShown; + return view_state == DesktopViewLockedStateLockedHintShown || + view_state == DesktopViewLockedStateLocked; } diff --git a/applications/services/dolphin/dolphin.c b/applications/services/dolphin/dolphin.c index cf37c5cd9..145224542 100644 --- a/applications/services/dolphin/dolphin.c +++ b/applications/services/dolphin/dolphin.c @@ -6,7 +6,7 @@ #include #include #include -#include +#include #define DOLPHIN_LOCK_EVENT_FLAG (0x1) #define TAG "Dolphin" @@ -80,7 +80,7 @@ Dolphin* dolphin_alloc() { dolphin->state = dolphin_state_alloc(); dolphin->event_queue = furi_message_queue_alloc(8, sizeof(DolphinEvent)); dolphin->pubsub = furi_pubsub_alloc(); - int32_t butthurt = XTREME_SETTINGS()->butthurt_timer; + int32_t butthurt = XTREME_SETTINGS_WAIT()->butthurt_timer; dolphin->butthurt_timer = xTimerCreate( NULL, (butthurt > 0) ? (butthurt * 1000) : -1, @@ -161,7 +161,7 @@ static void dolphin_update_clear_limits_timer_period(Dolphin* dolphin) { int32_t dolphin_srv(void* p) { UNUSED(p); - if(furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal) { + if(!furi_hal_is_normal_boot()) { FURI_LOG_W(TAG, "Skipping start in special boot mode"); return 0; } diff --git a/applications/services/dolphin/helpers/dolphin_state.c b/applications/services/dolphin/helpers/dolphin_state.c index 190efa5ec..fba3c5a90 100644 --- a/applications/services/dolphin/helpers/dolphin_state.c +++ b/applications/services/dolphin/helpers/dolphin_state.c @@ -101,7 +101,7 @@ bool dolphin_state_is_levelup(int icounter) { if((icounter == DOLPHIN_LEVELS[i])) { return true; } - }; + } return false; } diff --git a/applications/services/gui/application.fam b/applications/services/gui/application.fam index 7981a4fcb..193b9dfd3 100644 --- a/applications/services/gui/application.fam +++ b/applications/services/gui/application.fam @@ -7,7 +7,6 @@ App( requires=[ "input", "notification", - "xtreme", ], stack_size=2 * 1024, order=70, diff --git a/applications/services/gui/canvas.c b/applications/services/gui/canvas.c index fc4064282..e4ddfb21e 100644 --- a/applications/services/gui/canvas.c +++ b/applications/services/gui/canvas.c @@ -7,7 +7,7 @@ #include #include #include -#include +#include const CanvasFontParameters canvas_font_params[FontTotalNumber] = { [FontPrimary] = {.leading_default = 12, .leading_min = 11, .height = 8, .descender = 2}, @@ -140,18 +140,25 @@ void canvas_invert_color(Canvas* canvas) { void canvas_set_font(Canvas* canvas, Font font) { furi_assert(canvas); u8g2_SetFontMode(&canvas->fb, 1); - if(font == FontPrimary) { + switch(font) { + case FontPrimary: u8g2_SetFont(&canvas->fb, u8g2_font_helvB08_tr); - } else if(font == FontSecondary) { + break; + case FontSecondary: u8g2_SetFont(&canvas->fb, u8g2_font_haxrcorp4089_tr); - } else if(font == FontKeyboard) { + break; + case FontKeyboard: u8g2_SetFont(&canvas->fb, u8g2_font_profont11_mr); - } else if(font == FontBigNumbers) { + break; + case FontBigNumbers: u8g2_SetFont(&canvas->fb, u8g2_font_profont22_tn); - } else if(font == FontBatteryPercent) { + break; + case FontBatteryPercent: u8g2_SetFont(&canvas->fb, u8g2_font_5x7_tf); //u8g2_font_micro_tr); - } else { + break; + default: furi_crash(NULL); + break; } } diff --git a/applications/services/gui/elements.c b/applications/services/gui/elements.c index ffe52d354..fc5609a36 100644 --- a/applications/services/gui/elements.c +++ b/applications/services/gui/elements.c @@ -573,16 +573,16 @@ void elements_string_fit_width(Canvas* canvas, FuriString* string, uint8_t width } } -void elements_scrollable_text_line( +void elements_scrollable_text_line_str( Canvas* canvas, uint8_t x, uint8_t y, uint8_t width, - FuriString* string, + const char* string, size_t scroll, bool ellipsis, bool centered) { - FuriString* line = furi_string_alloc_set(string); + FuriString* line = furi_string_alloc_set_str(string); size_t len_px = canvas_string_width(canvas, furi_string_get_cstr(line)); if(len_px > width) { @@ -631,6 +631,19 @@ void elements_scrollable_text_line( furi_string_free(line); } +void elements_scrollable_text_line( + Canvas* canvas, + uint8_t x, + uint8_t y, + uint8_t width, + FuriString* string, + size_t scroll, + bool ellipsis, + bool centered) { + elements_scrollable_text_line_str( + canvas, x, y, width, furi_string_get_cstr(string), scroll, ellipsis, centered); +} + void elements_text_box( Canvas* canvas, uint8_t x, diff --git a/applications/services/gui/elements.h b/applications/services/gui/elements.h index 00bffc9a6..7cab3a67f 100644 --- a/applications/services/gui/elements.h +++ b/applications/services/gui/elements.h @@ -220,6 +220,15 @@ void elements_string_fit_width(Canvas* canvas, FuriString* string, uint8_t width * @param[in] ellipsis The ellipsis flag: true to add ellipse * @param[in] centered The centered flag: true to center text on x and y */ +void elements_scrollable_text_line_str( + Canvas* canvas, + uint8_t x, + uint8_t y, + uint8_t width, + const char* string, + size_t scroll, + bool ellipsis, + bool centered); void elements_scrollable_text_line( Canvas* canvas, uint8_t x, diff --git a/applications/services/gui/gui.c b/applications/services/gui/gui.c index dd1291890..8f0deaa6f 100644 --- a/applications/services/gui/gui.c +++ b/applications/services/gui/gui.c @@ -1,4 +1,4 @@ -#include "xtreme/settings.h" +#include #include "gui_i.h" #include @@ -75,7 +75,7 @@ static void gui_redraw_status_bar(Gui* gui, bool need_attention) { canvas_draw_box(gui->canvas, 89, 3, 38, 6); canvas_set_color(gui->canvas, ColorBlack); canvas_set_bitmap_mode(gui->canvas, 1); - canvas_draw_icon(gui->canvas, 0, 0, &I_Background_128x11); + canvas_draw_icon(gui->canvas, 0, 0, XTREME_ASSETS()->I_Background_128x11); } else { canvas_set_color(gui->canvas, ColorBlack); } diff --git a/applications/services/gui/modules/file_browser.c b/applications/services/gui/modules/file_browser.c index 8affe8330..4f11ffe8d 100644 --- a/applications/services/gui/modules/file_browser.c +++ b/applications/services/gui/modules/file_browser.c @@ -13,7 +13,7 @@ #include #include "m-algo.h" #include -#include "xtreme/settings.h" +#include #define LIST_ITEMS 5u #define MAX_LEN_PX 110 diff --git a/applications/services/gui/modules/menu.c b/applications/services/gui/modules/menu.c index d259caaba..cf42524af 100644 --- a/applications/services/gui/modules/menu.c +++ b/applications/services/gui/modules/menu.c @@ -6,7 +6,7 @@ #include #include #include -#include +#include #include struct Menu { @@ -47,9 +47,9 @@ static void menu_draw_callback(Canvas* canvas, void* _model) { size_t items_count = MenuItemArray_size(model->items); if(items_count) { MenuItem* item; - FuriString* name = furi_string_alloc(); size_t shift_position; if(XTREME_SETTINGS()->wii_menu) { + FuriString* name = furi_string_alloc(); if(position < 2) { shift_position = 0; } else if(position >= items_count - 2 + (items_count % 2)) { @@ -99,6 +99,7 @@ static void menu_draw_callback(Canvas* canvas, void* _model) { elements_frame(canvas, 0 + x_off, 0 + y_off, 40, 30); } } + furi_string_free(name); } else { // First line canvas_set_font(canvas, FontSecondary); @@ -129,8 +130,8 @@ static void menu_draw_callback(Canvas* canvas, void* _model) { } else { scroll_counter -= 1; } - furi_string_set(name, item->label); - elements_scrollable_text_line(canvas, 22, 36, 98, name, scroll_counter, false, false); + elements_scrollable_text_line_str( + canvas, 22, 36, 98, item->label, scroll_counter, false, false); // Third line canvas_set_font(canvas, FontSecondary); shift_position = (2 + position + items_count - 1) % items_count; @@ -147,7 +148,6 @@ static void menu_draw_callback(Canvas* canvas, void* _model) { elements_frame(canvas, 0, 21, 128 - 5, 21); elements_scrollbar(canvas, position, items_count); } - furi_string_free(name); } else { canvas_draw_str(canvas, 2, 32, "Empty"); elements_scrollbar(canvas, 0, 0); @@ -273,6 +273,8 @@ Menu* menu_alloc() { void menu_free(Menu* menu) { furi_assert(menu); menu_reset(menu); + with_view_model( + menu->view, MenuModel * model, { MenuItemArray_clear(model->items); }, false); view_free(menu->view); furi_timer_free(menu->scroll_timer); free(menu); diff --git a/applications/services/gui/modules/popup.c b/applications/services/gui/modules/popup.c index d75abb95f..ad26b2691 100644 --- a/applications/services/gui/modules/popup.c +++ b/applications/services/gui/modules/popup.c @@ -99,7 +99,7 @@ void popup_start_timer(void* context) { if(furi_timer_start(popup->timer, timer_period) != FuriStatusOk) { furi_assert(0); - }; + } } } diff --git a/applications/services/gui/modules/text_box.c b/applications/services/gui/modules/text_box.c index 01ccdbf52..6dc558815 100644 --- a/applications/services/gui/modules/text_box.c +++ b/applications/services/gui/modules/text_box.c @@ -85,7 +85,7 @@ static void text_box_insert_endline(Canvas* canvas, TextBoxModel* model) { // Set text position to 5th line from the end for(uint8_t i = 0; i < line_num - 5; i++) { while(*model->text_pos++ != '\n') { - }; + } } model->scroll_num = line_num - 4; model->scroll_pos = line_num - 5; diff --git a/applications/services/gui/modules/text_input.c b/applications/services/gui/modules/text_input.c index 35ab30c16..520526e48 100644 --- a/applications/services/gui/modules/text_input.c +++ b/applications/services/gui/modules/text_input.c @@ -23,6 +23,7 @@ typedef struct { const char* header; char* text_buffer; size_t text_buffer_size; + size_t minimum_length; bool clear_default_text; FuriString* temp_str; @@ -101,16 +102,15 @@ static const TextInputKey keyboard_keys_row_3[] = { }; static const TextInputKey symbol_keyboard_keys_row_1[] = { - {'!', 1, 8}, - {'"', 10, 8}, - {'#', 19, 8}, - {'$', 28, 8}, - {'%', 37, 8}, - {'&', 46, 8}, - {'/', 55, 8}, - {'(', 64, 8}, - {')', 73, 8}, - {'=', 82, 8}, + {'!', 2, 8}, + {'@', 12, 8}, + {'#', 22, 8}, + {'$', 32, 8}, + {'%', 42, 8}, + {'^', 52, 8}, + {'&', 62, 8}, + {'(', 71, 8}, + {')', 81, 8}, {'0', 91, 8}, {'1', 100, 8}, {'2', 110, 8}, @@ -118,15 +118,14 @@ static const TextInputKey symbol_keyboard_keys_row_1[] = { }; static const TextInputKey symbol_keyboard_keys_row_2[] = { - {'{', 1, 20}, - {'}', 10, 20}, - {'[', 19, 20}, - {']', 28, 20}, - {'<', 37, 20}, - {'>', 46, 20}, - {'\\', 55, 20}, - {'@', 64, 20}, - {'?', 73, 20}, + {'~', 2, 20}, + {'+', 12, 20}, + {'-', 22, 20}, + {'=', 32, 20}, + {'[', 42, 20}, + {']', 52, 20}, + {'{', 62, 20}, + {'}', 72, 20}, {BACKSPACE_KEY, 82, 12}, {'4', 100, 20}, {'5', 110, 20}, @@ -135,14 +134,11 @@ static const TextInputKey symbol_keyboard_keys_row_2[] = { static const TextInputKey symbol_keyboard_keys_row_3[] = { {SWITCH_KEYBOARD_KEY, 1, 23}, - {'+', 13, 32}, - {'`', 21, 32}, - {'\'', 28, 32}, - {'^', 36, 32}, - {'*', 44, 32}, - {',', 52, 32}, - {'.', 59, 32}, - {'-', 67, 32}, + {'.', 15, 32}, + {',', 29, 32}, + {';', 41, 32}, + {'`', 53, 32}, + {'\'', 65, 32}, {ENTER_KEY, 74, 23}, {'7', 100, 32}, {'8', 110, 32}, @@ -290,7 +286,7 @@ static void text_input_view_draw_callback(Canvas* canvas, void* _model) { canvas_draw_str(canvas, start_pos, 22, "..."); start_pos += 6; needed_string_width -= 8; - for(uint off = 0; + for(uint32_t off = 0; !furi_string_empty(str) && canvas_string_width(canvas, cstr) > needed_string_width && off < model->cursor_pos; off++) { @@ -376,11 +372,18 @@ static void text_input_handle_up(TextInput* text_input, TextInputModel* model) { UNUSED(text_input); if(model->selected_row > 0) { model->selected_row--; - if(model->selected_column > - get_row_size(keyboards[model->selected_keyboard], model->selected_row) - 6 && - model->selected_row == 0) { + if(model->selected_row == 0 && + model->selected_column > + get_row_size(keyboards[model->selected_keyboard], model->selected_row) - 6) { model->selected_column = model->selected_column + 1; } + if(model->selected_row == 1 && + model->selected_keyboard == symbol_keyboard.keyboard_index) { + if(model->selected_column > 5) + model->selected_column += 2; + else if(model->selected_column > 1) + model->selected_column += 1; + } } else { model->cursor_select = true; model->clear_default_text = false; @@ -393,11 +396,18 @@ static void text_input_handle_down(TextInput* text_input, TextInputModel* model) model->cursor_select = false; } else if(model->selected_row < keyboard_row_count - 1) { model->selected_row++; - if(model->selected_column > - get_row_size(keyboards[model->selected_keyboard], model->selected_row) - 4 && - model->selected_row == 1) { + if(model->selected_row == 1 && + model->selected_column > + get_row_size(keyboards[model->selected_keyboard], model->selected_row) - 4) { model->selected_column = model->selected_column - 1; } + if(model->selected_row == 2 && + model->selected_keyboard == symbol_keyboard.keyboard_index) { + if(model->selected_column > 7) + model->selected_column -= 2; + else if(model->selected_column > 1) + model->selected_column -= 1; + } } } @@ -441,7 +451,7 @@ static void text_input_handle_ok(TextInput* text_input, TextInputModel* model, I model->text_buffer, model->validator_text, model->validator_callback_context))) { model->validator_message_visible = true; furi_timer_start(text_input->timer, furi_kernel_get_tick_frequency() * 4); - } else if(model->callback != 0 && text_length > 0) { + } else if(model->callback != 0 && text_length >= model->minimum_length) { model->callback(model->callback_context); } } else if(selected == SWITCH_KEYBOARD_KEY) { @@ -631,6 +641,7 @@ void text_input_reset(TextInput* text_input) { model->selected_row = 0; model->selected_column = 0; model->selected_keyboard = 0; + model->minimum_length = 1; model->clear_default_text = false; model->cursor_pos = 0; model->cursor_select = false; @@ -681,6 +692,14 @@ void text_input_set_result_callback( true); } +void text_input_set_minimum_length(TextInput* text_input, size_t minimum_length) { + with_view_model( + text_input->view, + TextInputModel * model, + { model->minimum_length = minimum_length; }, + true); +} + void text_input_set_validator( TextInput* text_input, TextInputValidatorCallback callback, diff --git a/applications/services/gui/modules/text_input.h b/applications/services/gui/modules/text_input.h index 218df3141..65207532e 100644 --- a/applications/services/gui/modules/text_input.h +++ b/applications/services/gui/modules/text_input.h @@ -70,6 +70,8 @@ void text_input_set_validator( TextInputValidatorCallback callback, void* callback_context); +void text_input_set_minimum_length(TextInput* text_input, size_t minimum_length); + TextInputValidatorCallback text_input_get_validator_callback(TextInput* text_input); void* text_input_get_validator_callback_context(TextInput* text_input); diff --git a/applications/services/gui/modules/variable_item_list.c b/applications/services/gui/modules/variable_item_list.c index 182cd127f..225118586 100644 --- a/applications/services/gui/modules/variable_item_list.c +++ b/applications/services/gui/modules/variable_item_list.c @@ -79,7 +79,13 @@ static void variable_item_list_draw_callback(Canvas* canvas, void* _model) { canvas_set_color(canvas, ColorBlack); } - canvas_draw_str(canvas, 6, item_text_y, item->label); + if(item->current_value_index == 0 && furi_string_empty(item->current_value_text)) { + // Only left text, no right text + canvas_draw_str(canvas, 6, item_text_y, item->label); + } else { + elements_scrollable_text_line_str( + canvas, 6, item_text_y, 66, item->label, scroll_counter, false, false); + } if(item->locked) { canvas_draw_icon(canvas, 110, item_text_y - 8, &I_Lock_7x8); diff --git a/applications/services/loader/loader.c b/applications/services/loader/loader.c index f915cda1d..808c8e18e 100644 --- a/applications/services/loader/loader.c +++ b/applications/services/loader/loader.c @@ -8,7 +8,7 @@ #include #include #include -#include +#include #include #define TAG "LoaderSrv" @@ -43,10 +43,11 @@ static bool furi_assert(loader_instance->application_arguments == NULL); if(arguments && strlen(arguments) > 0) { loader_instance->application_arguments = strdup(arguments); + FURI_LOG_I(TAG, "Starting: %s, args: %s", loader_instance->application->name, arguments); + } else { + FURI_LOG_I(TAG, "Starting: %s", loader_instance->application->name); } - FURI_LOG_I(TAG, "Starting: %s", loader_instance->application->name); - FuriHalRtcHeapTrackMode mode = furi_hal_rtc_get_heap_track_mode(); if(mode > FuriHalRtcHeapTrackModeNone) { furi_thread_enable_heap_trace(loader_instance->application_thread); diff --git a/applications/services/namechangersrv/namechangersrv.c b/applications/services/namechangersrv/namechangersrv.c index fe25906ae..33aeb3fdd 100644 --- a/applications/services/namechangersrv/namechangersrv.c +++ b/applications/services/namechangersrv/namechangersrv.c @@ -4,7 +4,7 @@ #include void namechanger_on_system_start() { - if(furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal) { + if(!furi_hal_is_normal_boot()) { FURI_LOG_W(TAG, "NameChangerSRV load skipped. Device is in special startup mode."); } else { Storage* storage = furi_record_open(RECORD_STORAGE); diff --git a/applications/services/notification/notification_app.c b/applications/services/notification/notification_app.c index 2e170f547..76dc2e052 100644 --- a/applications/services/notification/notification_app.c +++ b/applications/services/notification/notification_app.c @@ -20,9 +20,9 @@ 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(); +void notification_vibro_on(bool force); void notification_vibro_off(); -void notification_sound_on(float freq, float volume); +void notification_sound_on(float freq, float volume, bool force); void notification_sound_off(); uint8_t notification_settings_get_display_brightness(NotificationApp* app, uint8_t value); @@ -36,7 +36,7 @@ void notification_message_save_settings(NotificationApp* app) { furi_event_flag_wait( m.back_event, NOTIFICATION_EVENT_COMPLETE, FuriFlagWaitAny, FuriWaitForever); furi_event_flag_free(m.back_event); -}; +} // internal layer void notification_apply_internal_led_layer(NotificationLedLayer* layer, uint8_t layer_value) { @@ -141,17 +141,21 @@ uint32_t notification_settings_display_off_delay_ticks(NotificationApp* app) { } // generics -void notification_vibro_on() { - furi_hal_vibro_on(true); +void notification_vibro_on(bool force) { + if(!furi_hal_rtc_is_flag_set(FuriHalRtcFlagStealthMode) || force) { + furi_hal_vibro_on(true); + } } void notification_vibro_off() { furi_hal_vibro_on(false); } -void notification_sound_on(float freq, float volume) { - if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(30)) { - furi_hal_speaker_start(freq, volume); +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); + } } } @@ -174,6 +178,8 @@ void notification_process_notification_message( NotificationApp* app, NotificationAppMessage* message) { uint32_t notification_message_index = 0; + bool force_volume = false; + bool force_vibro = false; const NotificationMessage* notification_message; notification_message = (*message->sequence)[notification_message_index]; @@ -269,7 +275,7 @@ void notification_process_notification_message( break; case NotificationMessageTypeVibro: if(notification_message->data.vibro.on) { - if(vibro_setting) notification_vibro_on(); + if(vibro_setting) notification_vibro_on(force_vibro); } else { notification_vibro_off(); } @@ -278,7 +284,8 @@ void notification_process_notification_message( case NotificationMessageTypeSoundOn: notification_sound_on( notification_message->data.sound.frequency, - notification_message->data.sound.volume * speaker_volume_setting); + notification_message->data.sound.volume * speaker_volume_setting, + force_volume); reset_mask |= reset_sound_mask; break; case NotificationMessageTypeSoundOff: @@ -307,9 +314,11 @@ void notification_process_notification_message( break; case NotificationMessageTypeForceSpeakerVolumeSetting: speaker_volume_setting = notification_message->data.forced_settings.speaker_volume; + force_volume = true; break; case NotificationMessageTypeForceVibroSetting: vibro_setting = notification_message->data.forced_settings.vibro; + force_vibro = true; break; case NotificationMessageTypeForceDisplayBrightnessSetting: display_brightness_setting = @@ -327,7 +336,7 @@ void notification_process_notification_message( } notification_message_index++; notification_message = (*message->sequence)[notification_message_index]; - }; + } // send and do minimal delay if(led_active) { @@ -438,7 +447,7 @@ static bool notification_load_settings(NotificationApp* app) { furi_record_close(RECORD_STORAGE); return fs_result; -}; +} static bool notification_save_settings(NotificationApp* app) { NotificationSettings settings; @@ -473,7 +482,7 @@ static bool notification_save_settings(NotificationApp* app) { furi_record_close(RECORD_STORAGE); return fs_result; -}; +} static void input_event_callback(const void* value, void* context) { furi_assert(value); @@ -522,15 +531,17 @@ static NotificationApp* notification_app_alloc() { notification_message(app, &sequence_display_backlight_on); return app; -}; +} // App int32_t notification_srv(void* p) { UNUSED(p); NotificationApp* app = notification_app_alloc(); - if(!notification_load_settings(app)) { - notification_save_settings(app); + if(furi_hal_is_normal_boot()) { + if(!notification_load_settings(app)) { + notification_save_settings(app); + } } notification_vibro_off(); diff --git a/applications/services/notification/notification_app_api.c b/applications/services/notification/notification_app_api.c index 9bc06b013..9bcf5964f 100644 --- a/applications/services/notification/notification_app_api.c +++ b/applications/services/notification/notification_app_api.c @@ -8,13 +8,13 @@ void notification_message(NotificationApp* app, const NotificationSequence* sequ NotificationAppMessage m = { .type = NotificationLayerMessage, .sequence = sequence, .back_event = NULL}; furi_check(furi_message_queue_put(app->queue, &m, FuriWaitForever) == FuriStatusOk); -}; +} void notification_internal_message(NotificationApp* app, const NotificationSequence* sequence) { NotificationAppMessage m = { .type = InternalLayerMessage, .sequence = sequence, .back_event = NULL}; furi_check(furi_message_queue_put(app->queue, &m, FuriWaitForever) == FuriStatusOk); -}; +} void notification_message_block(NotificationApp* app, const NotificationSequence* sequence) { NotificationAppMessage m = { @@ -25,7 +25,7 @@ void notification_message_block(NotificationApp* app, const NotificationSequence furi_event_flag_wait( m.back_event, NOTIFICATION_EVENT_COMPLETE, FuriFlagWaitAny, FuriWaitForever); furi_event_flag_free(m.back_event); -}; +} void notification_internal_message_block( NotificationApp* app, @@ -36,4 +36,4 @@ void notification_internal_message_block( furi_event_flag_wait( m.back_event, NOTIFICATION_EVENT_COMPLETE, FuriFlagWaitAny, FuriWaitForever); furi_event_flag_free(m.back_event); -}; +} diff --git a/applications/services/power/power_service/power.c b/applications/services/power/power_service/power.c index 7c1b9be58..b6f8d98a9 100644 --- a/applications/services/power/power_service/power.c +++ b/applications/services/power/power_service/power.c @@ -2,7 +2,7 @@ #include #include -#include "xtreme/settings.h" +#include #define POWER_OFF_TIMEOUT 90 #define TAG "Power" @@ -512,10 +512,14 @@ static void power_check_battery_level_change(Power* power) { } } +void power_trigger_ui_update(Power* power) { + view_port_update(power->battery_view_port); +} + int32_t power_srv(void* p) { UNUSED(p); - if(furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal) { + if(!furi_hal_is_normal_boot()) { FURI_LOG_W(TAG, "Skipping start in special boot mode"); return 0; } @@ -543,7 +547,9 @@ int32_t power_srv(void* p) { power_check_battery_level_change(power); // Update battery view port - if(need_refresh) view_port_update(power->battery_view_port); + if(need_refresh) { + view_port_update(power->battery_view_port); + } // Check OTG status and disable it in case of fault if(furi_hal_power_is_otg_enabled()) { diff --git a/applications/services/power/power_service/power.h b/applications/services/power/power_service/power.h index cb2fc8fad..4a8f07a89 100644 --- a/applications/services/power/power_service/power.h +++ b/applications/services/power/power_service/power.h @@ -113,6 +113,12 @@ bool power_is_battery_healthy(Power* power); */ void power_enable_low_battery_level_notification(Power* power, bool enable); +/** Trigger UI update for changing battery layout + * + * @param power Power instance + */ +void power_trigger_ui_update(Power* power); + #ifdef __cplusplus } #endif diff --git a/applications/services/rgb_backlight/rgb_backlight.c b/applications/services/rgb_backlight/rgb_backlight.c index d7ab89975..9b39b469a 100644 --- a/applications/services/rgb_backlight/rgb_backlight.c +++ b/applications/services/rgb_backlight/rgb_backlight.c @@ -63,8 +63,7 @@ const char* rgb_backlight_get_color_text(uint8_t index) { void rgb_backlight_load_settings(void) { //НС Π·Π°Π³Ρ€ΡƒΠΆΠ°Ρ‚ΡŒ Π΄Π°Π½Π½Ρ‹Π΅ ΠΈΠ· Π²Π½ΡƒΡ‚Ρ€Π΅Π½Π½Π΅ΠΉ памяти ΠΏΡ€ΠΈ Π·Π°Π³Ρ€ΡƒΠ·ΠΊΠ΅ Π² Ρ€Π΅ΠΆΠΈΠΌΠ΅ DFU - FuriHalRtcBootMode bm = furi_hal_rtc_get_boot_mode(); - if(bm == FuriHalRtcBootModeDfu) { + if(!furi_hal_is_normal_boot()) { rgb_settings.settings_is_loaded = true; return; } @@ -104,7 +103,7 @@ void rgb_backlight_load_settings(void) { storage_file_free(file); furi_record_close(RECORD_STORAGE); rgb_settings.settings_is_loaded = true; -}; +} void rgb_backlight_save_settings(void) { RGBBacklightSettings settings; @@ -135,7 +134,7 @@ void rgb_backlight_save_settings(void) { storage_file_close(file); storage_file_free(file); furi_record_close(RECORD_STORAGE); -}; +} RGBBacklightSettings* rgb_backlight_get_settings(void) { if(!rgb_settings.settings_is_loaded) { diff --git a/applications/services/rpc/rpc.c b/applications/services/rpc/rpc.c index 2326e7d2f..62428d79d 100644 --- a/applications/services/rpc/rpc.c +++ b/applications/services/rpc/rpc.c @@ -76,6 +76,7 @@ struct RpcSession { RpcBufferIsEmptyCallback buffer_is_empty_callback; RpcSessionClosedCallback closed_callback; RpcSessionTerminatedCallback terminated_callback; + RpcOwner owner; void* context; }; @@ -83,6 +84,11 @@ struct Rpc { FuriMutex* busy_mutex; }; +RpcOwner rpc_session_get_owner(RpcSession* session) { + furi_assert(session); + return session->owner; +} + static void rpc_close_session_process(const PB_Main* request, void* context) { furi_assert(request); furi_assert(context); @@ -348,7 +354,7 @@ static void rpc_session_free_callback(FuriThreadState thread_state, void* contex } } -RpcSession* rpc_session_open(Rpc* rpc) { +RpcSession* rpc_session_open(Rpc* rpc, RpcOwner owner) { furi_assert(rpc); RpcSession* session = malloc(sizeof(RpcSession)); @@ -357,6 +363,7 @@ RpcSession* rpc_session_open(Rpc* rpc) { session->rpc = rpc; session->terminate = false; session->decode_error = false; + session->owner = owner; RpcHandlerDict_init(session->handlers); session->decoded_message = malloc(sizeof(PB_Main)); diff --git a/applications/services/rpc/rpc.h b/applications/services/rpc/rpc.h index ec290e083..d11fdc162 100644 --- a/applications/services/rpc/rpc.h +++ b/applications/services/rpc/rpc.h @@ -30,6 +30,21 @@ typedef void (*RpcSessionClosedCallback)(void* context); * and all operations were finished */ typedef void (*RpcSessionTerminatedCallback)(void* context); +/** RPC owner */ +typedef enum { + RpcOwnerUnknown = 0, + RpcOwnerBle, + RpcOwnerUsb, + RpcOwnerCount, +} RpcOwner; + +/** Get RPC session owner + * + * @param session pointer to RpcSession descriptor + * @return session owner + */ +RpcOwner rpc_session_get_owner(RpcSession* session); + /** Open RPC session * * USAGE: @@ -44,10 +59,11 @@ typedef void (*RpcSessionTerminatedCallback)(void* context); * * * @param rpc instance + * @param owner owner of session * @return pointer to RpcSession descriptor, or * NULL if RPC is busy and can't open session now */ -RpcSession* rpc_session_open(Rpc* rpc); +RpcSession* rpc_session_open(Rpc* rpc, RpcOwner owner); /** Close RPC session * It is guaranteed that no callbacks will be called diff --git a/applications/services/rpc/rpc_cli.c b/applications/services/rpc/rpc_cli.c index d14b8eee2..f1c139b5f 100644 --- a/applications/services/rpc/rpc_cli.c +++ b/applications/services/rpc/rpc_cli.c @@ -47,7 +47,7 @@ void rpc_cli_command_start_session(Cli* cli, FuriString* args, void* context) { FURI_LOG_D(TAG, "Free memory %lu", mem_before); furi_hal_usb_lock(); - RpcSession* rpc_session = rpc_session_open(rpc); + RpcSession* rpc_session = rpc_session_open(rpc, RpcOwnerUsb); if(rpc_session == NULL) { printf("Session start error\r\n"); furi_hal_usb_unlock(); diff --git a/applications/services/rpc/rpc_gui.c b/applications/services/rpc/rpc_gui.c index 0c70702cf..9ba20a832 100644 --- a/applications/services/rpc/rpc_gui.c +++ b/applications/services/rpc/rpc_gui.c @@ -2,6 +2,7 @@ #include "rpc_i.h" #include "gui.pb.h" #include +#include #define TAG "RpcGui" @@ -31,6 +32,8 @@ typedef struct { uint32_t input_key_counter[InputKeyMAX]; uint32_t input_counter; + + ViewPort* rpc_session_active_viewport; } RpcGuiSystem; static const PB_Gui_ScreenOrientation rpc_system_gui_screen_orientation_map[] = { @@ -352,6 +355,12 @@ static void rpc_system_gui_virtual_display_frame_process(const PB_Main* request, (void)session; } +static void rpc_active_session_icon_draw_callback(Canvas* canvas, void* context) { + UNUSED(context); + furi_assert(canvas); + canvas_draw_icon(canvas, 0, 0, &I_Rpc_active_7x8); +} + void* rpc_system_gui_alloc(RpcSession* session) { furi_assert(session); @@ -359,6 +368,18 @@ void* rpc_system_gui_alloc(RpcSession* session) { rpc_gui->gui = furi_record_open(RECORD_GUI); rpc_gui->session = session; + // Active session icon + rpc_gui->rpc_session_active_viewport = view_port_alloc(); + view_port_set_width(rpc_gui->rpc_session_active_viewport, icon_get_width(&I_Rpc_active_7x8)); + view_port_draw_callback_set( + rpc_gui->rpc_session_active_viewport, rpc_active_session_icon_draw_callback, session); + if(rpc_session_get_owner(rpc_gui->session) != RpcOwnerBle) { + view_port_enabled_set(rpc_gui->rpc_session_active_viewport, true); + } else { + view_port_enabled_set(rpc_gui->rpc_session_active_viewport, false); + } + gui_add_view_port(rpc_gui->gui, rpc_gui->rpc_session_active_viewport, GuiLayerStatusBarLeft); + RpcHandler rpc_handler = { .message_handler = NULL, .decode_submessage = NULL, @@ -399,6 +420,9 @@ void rpc_system_gui_free(void* context) { rpc_gui->virtual_display_not_empty = false; } + gui_remove_view_port(rpc_gui->gui, rpc_gui->rpc_session_active_viewport); + view_port_free(rpc_gui->rpc_session_active_viewport); + if(rpc_gui->is_streaming) { rpc_gui->is_streaming = false; // Remove GUI framebuffer callback @@ -415,4 +439,4 @@ void rpc_system_gui_free(void* context) { } furi_record_close(RECORD_GUI); free(rpc_gui); -} +} \ No newline at end of file diff --git a/applications/services/storage/storage_cli.c b/applications/services/storage/storage_cli.c index 8e2dcdbbb..d720de9d1 100644 --- a/applications/services/storage/storage_cli.c +++ b/applications/services/storage/storage_cli.c @@ -33,7 +33,7 @@ static void storage_cli_print_usage() { printf("\tmd5\t - md5 hash of the file\r\n"); printf("\tstat\t - info about file or dir\r\n"); printf("\ttimestamp\t - last modification timestamp\r\n"); -}; +} static void storage_cli_print_error(FS_Error error) { printf("Storage error: %s\r\n", storage_error_get_desc(error)); @@ -86,7 +86,7 @@ static void storage_cli_info(Cli* cli, FuriString* path) { } furi_record_close(RECORD_STORAGE); -}; +} static void storage_cli_format(Cli* cli, FuriString* path) { if(furi_string_cmp_str(path, STORAGE_INT_PATH_PREFIX) == 0) { @@ -112,7 +112,7 @@ static void storage_cli_format(Cli* cli, FuriString* path) { } else { storage_cli_print_usage(); } -}; +} static void storage_cli_list(Cli* cli, FuriString* path) { UNUSED(cli); diff --git a/applications/services/storage/storages/storage_int.c b/applications/services/storage/storages/storage_int.c index 2534d47a1..5342c2bdd 100644 --- a/applications/services/storage/storages/storage_int.c +++ b/applications/services/storage/storages/storage_int.c @@ -160,7 +160,7 @@ static LFSData* storage_int_lfs_data_alloc() { lfs_data->config.lookahead_size = 16; return lfs_data; -}; +} // Returns true if fingerprint was invalid and LFS reformatting is needed static bool storage_int_check_and_set_fingerprint(LFSData* lfs_data) { @@ -193,15 +193,65 @@ static void storage_int_lfs_mount(LFSData* lfs_data, StorageData* storage) { was_fingerprint_outdated; if(need_format) { + // Backup U2F keys + // lfs_file_t file; + // uint8_t* cnt = NULL; + // uint32_t cnt_size; + // uint8_t* key = NULL; + // uint32_t key_size; + // if(lfs_mount(lfs, &lfs_data->config) == 0) { + // FURI_LOG_I(TAG, "Factory reset: Mounted for backup"); + + // if(lfs_file_open(lfs, &file, ".cnt.u2f", LFS_O_RDONLY) == 0) { + // cnt_size = file.ctz.size; + // cnt = malloc(cnt_size); + // if(lfs_file_read(lfs, &file, cnt, cnt_size) != (int32_t)cnt_size) { + // free(cnt); + // cnt = NULL; + // } + // lfs_file_close(lfs, &file); + // if(lfs_file_open(lfs, &file, ".key.u2f", LFS_O_RDONLY) == 0) { + // key_size = file.ctz.size; + // key = malloc(key_size); + // if(lfs_file_read(lfs, &file, key, key_size) != (int32_t)key_size) { + // free(key); + // key = NULL; + // } + // lfs_file_close(lfs, &file); + // } + // } + + // if(lfs_unmount(lfs) == 0) { + // FURI_LOG_E(TAG, "Factory reset: Unmounted after backup"); + // } else { + // FURI_LOG_E(TAG, "Factory reset: Unmount after backup failed"); + // } + // } else { + // FURI_LOG_E(TAG, "Factory reset: Mount for backup failed"); + // } + // Format storage - err = lfs_format(lfs, &lfs_data->config); - if(err == 0) { + if(lfs_format(lfs, &lfs_data->config) == 0) { FURI_LOG_I(TAG, "Factory reset: Format successful, trying to mount"); furi_hal_rtc_reset_flag(FuriHalRtcFlagFactoryReset); - err = lfs_mount(lfs, &lfs_data->config); - if(err == 0) { + if(lfs_mount(lfs, &lfs_data->config) == 0) { FURI_LOG_I(TAG, "Factory reset: Mounted"); storage->status = StorageStatusOK; + + // Restore U2F keys + // if(cnt != NULL && key != NULL) { + // if(lfs_file_open(lfs, &file, ".cnt.u2f", LFS_O_WRONLY | LFS_O_CREAT) == 0) { + // lfs_file_write(lfs, &file, cnt, cnt_size); + // lfs_file_close(lfs, &file); + // if(lfs_file_open(lfs, &file, ".key.u2f", LFS_O_WRONLY | LFS_O_CREAT) == + // 0) { + // lfs_file_write(lfs, &file, key, key_size); + // lfs_file_close(lfs, &file); + // } + // } + // } + // if(cnt != NULL) free(cnt); + // if(key != NULL) free(key); } else { FURI_LOG_E(TAG, "Factory reset: Mount after format failed"); storage->status = StorageStatusNotMounted; diff --git a/applications/services/xtreme/application.fam b/applications/services/xtreme/application.fam deleted file mode 100644 index de90d455f..000000000 --- a/applications/services/xtreme/application.fam +++ /dev/null @@ -1,14 +0,0 @@ -App( - appid="xtreme", - name="Xtreme", - apptype=FlipperAppType.SERVICE, - entry_point="xtreme_srv", - cdefines=["SRV_XTREME"], - requires=["storage"], - stack_size=1 * 1024, - order=46, - sdk_headers=[ - "settings.h", - "assets.h", - ], -) diff --git a/applications/services/xtreme/settings.c b/applications/services/xtreme/settings.c deleted file mode 100644 index 799c1a768..000000000 --- a/applications/services/xtreme/settings.c +++ /dev/null @@ -1,87 +0,0 @@ -#include "settings.h" - -#define TAG "XtremeSettings" - -XtremeSettings* xtreme_settings = NULL; - -XtremeSettings* XTREME_SETTINGS() { - if(xtreme_settings == NULL) { - XTREME_SETTINGS_LOAD(); - } - return xtreme_settings; -} - -void XTREME_SETTINGS_LOAD() { - if(xtreme_settings == NULL) { - xtreme_settings = malloc(sizeof(XtremeSettings)); - bool loaded = false; - bool skip = furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal; - - if(skip) { - FURI_LOG_W(TAG, "Load skipped. Device is in special startup mode."); - loaded = false; - } else { - loaded = saved_struct_load( - XTREME_SETTINGS_PATH, - xtreme_settings, - sizeof(XtremeSettings), - XTREME_SETTINGS_MAGIC, - XTREME_SETTINGS_VERSION); - if(!loaded) { - Storage* storage = furi_record_open(RECORD_STORAGE); - storage_common_copy(storage, XTREME_SETTINGS_OLD_PATH, XTREME_SETTINGS_PATH); - storage_common_copy(storage, XTREME_SETTINGS_OLD_INT_PATH, XTREME_SETTINGS_PATH); - storage_common_remove(storage, XTREME_SETTINGS_OLD_PATH); - storage_common_remove(storage, XTREME_SETTINGS_OLD_INT_PATH); - furi_record_close(RECORD_STORAGE); - loaded = saved_struct_load( - XTREME_SETTINGS_PATH, - xtreme_settings, - sizeof(XtremeSettings), - XTREME_SETTINGS_MAGIC, - XTREME_SETTINGS_VERSION); - } - } - - if(!loaded) { - memset(xtreme_settings, 0, sizeof(XtremeSettings)); - strlcpy(xtreme_settings->asset_pack, "", MAX_PACK_NAME_LEN); // SFW - xtreme_settings->anim_speed = 100; // 100% - xtreme_settings->cycle_anims = 0; // Meta.txt - xtreme_settings->unlock_anims = false; // OFF - xtreme_settings->fallback_anim = true; // ON - xtreme_settings->wii_menu = true; // ON - xtreme_settings->lockscreen_time = true; // ON - xtreme_settings->lockscreen_date = true; // ON - xtreme_settings->lockscreen_statusbar = true; // ON - xtreme_settings->lockscreen_prompt = true; // ON - xtreme_settings->battery_icon = BatteryIconBarPercent; // Bar % - xtreme_settings->status_icons = true; // ON - xtreme_settings->bar_borders = true; // ON - xtreme_settings->bar_background = false; // OFF - xtreme_settings->sort_dirs_first = true; // ON - xtreme_settings->dark_mode = false; // OFF - xtreme_settings->bad_bt = false; // USB - xtreme_settings->bad_bt_remember = false; // OFF - xtreme_settings->butthurt_timer = 43200; // 12 H - xtreme_settings->rgb_backlight = false; // OFF - } - } -} - -bool XTREME_SETTINGS_SAVE() { - if(xtreme_settings == NULL) { - XTREME_SETTINGS_LOAD(); - } - - if(furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal) { - return true; - } - - return saved_struct_save( - XTREME_SETTINGS_PATH, - xtreme_settings, - sizeof(XtremeSettings), - XTREME_SETTINGS_MAGIC, - XTREME_SETTINGS_VERSION); -} diff --git a/applications/services/xtreme/settings.h b/applications/services/xtreme/settings.h deleted file mode 100644 index ea4838e5a..000000000 --- a/applications/services/xtreme/settings.h +++ /dev/null @@ -1,55 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#define MAX_PACK_NAME_LEN 32 - -#define XTREME_SETTINGS_VERSION (10) -#define XTREME_SETTINGS_MAGIC (0x69) -#define XTREME_SETTINGS_OLD_INT_PATH INT_PATH(".xtreme.settings") -#define XTREME_SETTINGS_OLD_PATH EXT_PATH(".xtreme.settings") -#define XTREME_SETTINGS_PATH CFG_PATH("xtreme.settings") - -#define XTREME_APPS_PATH CFG_PATH("xtreme_apps.txt") - -typedef struct { - char asset_pack[MAX_PACK_NAME_LEN]; - uint16_t anim_speed; - int32_t cycle_anims; - bool unlock_anims; - bool fallback_anim; - bool wii_menu; - bool lockscreen_time; - bool lockscreen_date; - bool lockscreen_statusbar; - bool lockscreen_prompt; - BatteryIcon battery_icon; - bool status_icons; - bool bar_borders; - bool bar_background; - bool sort_dirs_first; - bool dark_mode; - bool bad_bt; - bool bad_bt_remember; - int32_t butthurt_timer; - bool rgb_backlight; -} XtremeSettings; - -XtremeSettings* XTREME_SETTINGS(); - -void XTREME_SETTINGS_LOAD(); - -bool XTREME_SETTINGS_SAVE(); - -#ifdef __cplusplus -} -#endif diff --git a/applications/services/xtreme/xtreme_srv.c b/applications/services/xtreme/xtreme_srv.c deleted file mode 100644 index fa6fb97f2..000000000 --- a/applications/services/xtreme/xtreme_srv.c +++ /dev/null @@ -1,11 +0,0 @@ -#include "settings.h" -#include "assets.h" - -int32_t xtreme_srv(void* p) { - UNUSED(p); - - XTREME_SETTINGS_LOAD(); - XTREME_ASSETS_LOAD(); - - return 0; -} diff --git a/applications/settings/about/about.c b/applications/settings/about/about.c index bf0b8acfc..2f61ceb2c 100644 --- a/applications/settings/about/about.c +++ b/applications/settings/about/about.c @@ -169,8 +169,8 @@ static DialogMessageButton fw_version_screen(DialogsApp* dialogs, DialogMessage* #include #include -#define LOW_CHARGE_THRESHOLD 10 -#define HIGH_DRAIN_CURRENT_THRESHOLD 100 +#define LOW_CHARGE_THRESHOLD (10) +#define HIGH_DRAIN_CURRENT_THRESHOLD (-100) static void draw_stat(Canvas* canvas, int x, int y, const Icon* icon, char* val) { canvas_draw_frame(canvas, x - 7, y + 7, 30, 13); @@ -179,20 +179,19 @@ static void draw_stat(Canvas* canvas, int x, int y, const Icon* icon, char* val) canvas_draw_box(canvas, x - 4, y + 16, 24, 6); canvas_set_color(canvas, ColorBlack); canvas_draw_str_aligned(canvas, x + 8, y + 22, AlignCenter, AlignBottom, val); -}; +} static void draw_battery(Canvas* canvas, PowerInfo* info, int x, int y) { char header[20] = {}; char value[20] = {}; - int32_t drain_current = info->current_gauge * (-1000); - uint32_t charge_current = info->current_gauge * 1000; + int32_t current = 1000.0f * info->current_gauge; // Draw battery canvas_draw_icon(canvas, x, y, &I_BatteryBody_52x28); - if(charge_current > 0) { + if(current > 0) { canvas_draw_icon(canvas, x + 16, y + 7, &I_FaceCharging_29x14); - } else if(drain_current > HIGH_DRAIN_CURRENT_THRESHOLD) { + } else if(current < HIGH_DRAIN_CURRENT_THRESHOLD) { canvas_draw_icon(canvas, x + 16, y + 7, &I_FaceConfused_29x14); } else if(info->charge < LOW_CHARGE_THRESHOLD) { canvas_draw_icon(canvas, x + 16, y + 7, &I_FaceNopower_29x14); @@ -204,7 +203,7 @@ static void draw_battery(Canvas* canvas, PowerInfo* info, int x, int y) { elements_bubble(canvas, x + 53, y + 0, 71, 28); // Set text - if(charge_current > 0) { + if(current > 0) { snprintf(header, sizeof(header), "%s", "Charging at"); snprintf( value, @@ -212,28 +211,30 @@ static void draw_battery(Canvas* canvas, PowerInfo* info, int x, int y) { "%lu.%luV %lumA", (uint32_t)(info->voltage_vbus), (uint32_t)(info->voltage_vbus * 10) % 10, - charge_current); - } else if(drain_current > 0) { + current); + } else if(current < 0) { snprintf(header, sizeof(header), "%s", "Consumption is"); snprintf( value, sizeof(value), "%ld %s", - drain_current, - drain_current > HIGH_DRAIN_CURRENT_THRESHOLD ? "mA!" : "mA"); - } else if(drain_current != 0) { - snprintf(header, 20, "..."); - } else if(info->voltage_battery_charge_limit < 4.2) { - // Non-default battery charging limit, mention it - snprintf(header, sizeof(header), "Limited to"); - snprintf( - value, - sizeof(value), - "%lu.%luV", - (uint32_t)(info->voltage_battery_charge_limit), - (uint32_t)(info->voltage_battery_charge_limit * 10) % 10); + ABS(current), + current < HIGH_DRAIN_CURRENT_THRESHOLD ? "mA!" : "mA"); + } else if(info->voltage_vbus > 0) { + if(info->voltage_battery_charge_limit < 4.2) { + // Non-default battery charging limit, mention it + snprintf(header, sizeof(header), "Limited to"); + snprintf( + value, + sizeof(value), + "%lu.%luV", + (uint32_t)(info->voltage_battery_charge_limit), + (uint32_t)(info->voltage_battery_charge_limit * 10) % 10); + } else { + snprintf(header, sizeof(header), "Charged!"); + } } else { - snprintf(header, sizeof(header), "Charged!"); + snprintf(header, sizeof(header), "Napping..."); } if(!strcmp(value, "")) { @@ -244,7 +245,7 @@ static void draw_battery(Canvas* canvas, PowerInfo* info, int x, int y) { canvas_draw_str_aligned(canvas, x + 92, y + 9, AlignCenter, AlignCenter, header); canvas_draw_str_aligned(canvas, x + 92, y + 19, AlignCenter, AlignCenter, value); } -}; +} static void battery_info_draw_callback(Canvas* canvas, void* context) { furi_assert(context); diff --git a/applications/settings/bt_settings_app/bt_settings_app.c b/applications/settings/bt_settings_app/bt_settings_app.c index f211c7128..71d0495e3 100644 --- a/applications/settings/bt_settings_app/bt_settings_app.c +++ b/applications/settings/bt_settings_app/bt_settings_app.c @@ -16,9 +16,9 @@ BtSettingsApp* bt_settings_app_alloc() { BtSettingsApp* app = malloc(sizeof(BtSettingsApp)); // Load settings - bt_settings_load(&app->settings); app->gui = furi_record_open(RECORD_GUI); app->bt = furi_record_open(RECORD_BT); + bt_settings_load(&app->bt->bt_settings); // View Dispatcher and Scene Manager app->view_dispatcher = view_dispatcher_alloc(); @@ -79,7 +79,7 @@ extern int32_t bt_settings_app(void* p) { UNUSED(p); BtSettingsApp* app = bt_settings_app_alloc(); view_dispatcher_run(app->view_dispatcher); - bt_settings_save(&app->settings); + bt_settings_save(&app->bt->bt_settings); bt_settings_app_free(app); return 0; } diff --git a/applications/settings/bt_settings_app/bt_settings_app.h b/applications/settings/bt_settings_app/bt_settings_app.h index b79e36951..4dc20b020 100644 --- a/applications/settings/bt_settings_app/bt_settings_app.h +++ b/applications/settings/bt_settings_app/bt_settings_app.h @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -24,7 +25,6 @@ enum BtSettingsCustomEvent { }; typedef struct { - BtSettings settings; Bt* bt; Gui* gui; SceneManager* scene_manager; diff --git a/applications/settings/bt_settings_app/scenes/bt_settings_scene_forget_dev_success.c b/applications/settings/bt_settings_app/scenes/bt_settings_scene_forget_dev_success.c index c9e4b17d4..e8aed9b45 100644 --- a/applications/settings/bt_settings_app/scenes/bt_settings_scene_forget_dev_success.c +++ b/applications/settings/bt_settings_app/scenes/bt_settings_scene_forget_dev_success.c @@ -1,5 +1,5 @@ #include "../bt_settings_app.h" -#include "xtreme/assets.h" +#include #include void bt_settings_app_scene_forget_dev_success_popup_callback(void* context) { diff --git a/applications/settings/bt_settings_app/scenes/bt_settings_scene_start.c b/applications/settings/bt_settings_app/scenes/bt_settings_scene_start.c index 5db98e9de..696e5c8d0 100644 --- a/applications/settings/bt_settings_app/scenes/bt_settings_scene_start.c +++ b/applications/settings/bt_settings_app/scenes/bt_settings_scene_start.c @@ -46,7 +46,7 @@ void bt_settings_scene_start_on_enter(void* context) { BtSettingNum, bt_settings_scene_start_var_list_change_callback, app); - if(app->settings.enabled) { + if(app->bt->bt_settings.enabled) { variable_item_set_current_value_index(item, BtSettingOn); variable_item_set_current_value_text(item, bt_settings_text[BtSettingOn]); } else { @@ -71,10 +71,10 @@ bool bt_settings_scene_start_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == BtSettingOn) { furi_hal_bt_start_advertising(); - app->settings.enabled = true; + app->bt->bt_settings.enabled = true; consumed = true; } else if(event.event == BtSettingOff) { - app->settings.enabled = false; + app->bt->bt_settings.enabled = false; furi_hal_bt_stop_advertising(); consumed = true; } else if(event.event == BtSettingsCustomEventForgetDevices) { 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 cad5f43f4..0ae92062a 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 @@ -6,7 +6,7 @@ #include "../desktop_settings_app.h" #include #include "desktop_settings_scene.h" -#include "xtreme/assets.h" +#include #define SCENE_EVENT_EXIT (0U) 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 4ec90d097..bcf8eaf12 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_start.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_start.c @@ -3,6 +3,7 @@ #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 @@ -132,4 +133,9 @@ 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/dolphin_passport/passport.c b/applications/settings/dolphin_passport/passport.c index d6474b4cf..dc7f754eb 100644 --- a/applications/settings/dolphin_passport/passport.c +++ b/applications/settings/dolphin_passport/passport.c @@ -6,7 +6,7 @@ #include #include #include "dolphin/dolphin.h" -#include "xtreme/assets.h" +#include #include "math.h" typedef struct { diff --git a/applications/settings/notification_settings/notification_settings_app.c b/applications/settings/notification_settings/notification_settings_app.c index 01ab6bd86..364734a62 100644 --- a/applications/settings/notification_settings/notification_settings_app.c +++ b/applications/settings/notification_settings/notification_settings_app.c @@ -157,18 +157,33 @@ static NotificationAppSettings* alloc_settings() { variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_text(item, backlight_text[value_index]); - item = variable_item_list_add( - app->variable_item_list, "Volume", VOLUME_COUNT, volume_changed, app); - value_index = - value_index_float(app->notification->settings.speaker_volume, volume_value, VOLUME_COUNT); - variable_item_set_current_value_index(item, value_index); - variable_item_set_current_value_text(item, volume_text[value_index]); + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagStealthMode)) { + item = variable_item_list_add(app->variable_item_list, "Volume", 1, NULL, app); + value_index = 0; + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, "Stealth"); + } else { + item = variable_item_list_add( + app->variable_item_list, "Volume", VOLUME_COUNT, volume_changed, app); + value_index = value_index_float( + app->notification->settings.speaker_volume, volume_value, VOLUME_COUNT); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, volume_text[value_index]); + } - item = - variable_item_list_add(app->variable_item_list, "Vibro", VIBRO_COUNT, vibro_changed, app); - value_index = value_index_bool(app->notification->settings.vibro_on, vibro_value, VIBRO_COUNT); - variable_item_set_current_value_index(item, value_index); - variable_item_set_current_value_text(item, vibro_text[value_index]); + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagStealthMode)) { + item = variable_item_list_add(app->variable_item_list, "Vibro", 1, NULL, app); + value_index = 0; + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, "Stealth"); + } else { + item = variable_item_list_add( + app->variable_item_list, "Vibro", VIBRO_COUNT, vibro_changed, app); + value_index = + value_index_bool(app->notification->settings.vibro_on, vibro_value, VIBRO_COUNT); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, vibro_text[value_index]); + } app->view_dispatcher = view_dispatcher_alloc(); view_dispatcher_enable_queue(app->view_dispatcher); diff --git a/applications/settings/power_settings_app/scenes/power_settings_scene_power_off.c b/applications/settings/power_settings_app/scenes/power_settings_scene_power_off.c index afcc6f950..9b35e9eb6 100644 --- a/applications/settings/power_settings_app/scenes/power_settings_scene_power_off.c +++ b/applications/settings/power_settings_app/scenes/power_settings_scene_power_off.c @@ -1,5 +1,5 @@ #include "../power_settings_app.h" -#include "xtreme/assets.h" +#include void power_settings_scene_power_off_dialog_callback(DialogExResult result, void* context) { furi_assert(context); diff --git a/applications/settings/power_settings_app/views/battery_info.c b/applications/settings/power_settings_app/views/battery_info.c index 7394fd3c5..828c518d6 100644 --- a/applications/settings/power_settings_app/views/battery_info.c +++ b/applications/settings/power_settings_app/views/battery_info.c @@ -4,8 +4,8 @@ #include #include -#define LOW_CHARGE_THRESHOLD 10 -#define HIGH_DRAIN_CURRENT_THRESHOLD 100 +#define LOW_CHARGE_THRESHOLD (10) +#define HIGH_DRAIN_CURRENT_THRESHOLD (-100) struct BatteryInfo { View* view; @@ -18,21 +18,20 @@ static void draw_stat(Canvas* canvas, int x, int y, const Icon* icon, char* val) canvas_draw_box(canvas, x - 4, y + 16, 24, 6); canvas_set_color(canvas, ColorBlack); canvas_draw_str_aligned(canvas, x + 8, y + 22, AlignCenter, AlignBottom, val); -}; +} static void draw_battery(Canvas* canvas, BatteryInfoModel* data, int x, int y) { char emote[20] = {}; char header[20] = {}; char value[20] = {}; - int32_t drain_current = data->gauge_current * (-1000); - uint32_t charge_current = data->gauge_current * 1000; + int32_t current = 1000.0f * data->gauge_current; // Draw battery canvas_draw_icon(canvas, x, y, &I_BatteryBody_52x28); - if(charge_current > 0) { + if(current > 0) { canvas_draw_icon(canvas, x + 16, y + 7, &I_FaceCharging_29x14); - } else if(drain_current > HIGH_DRAIN_CURRENT_THRESHOLD) { + } else if(current < HIGH_DRAIN_CURRENT_THRESHOLD) { canvas_draw_icon(canvas, x + 16, y + 7, &I_FaceConfused_29x14); } else if(data->charge < LOW_CHARGE_THRESHOLD) { canvas_draw_icon(canvas, x + 16, y + 7, &I_FaceNopower_29x14); @@ -44,7 +43,7 @@ static void draw_battery(Canvas* canvas, BatteryInfoModel* data, int x, int y) { elements_bubble(canvas, 53, 0, 71, 39); // Set text - if(charge_current > 0) { + if(current > 0) { snprintf(emote, sizeof(emote), "%s", "Yummy!"); snprintf(header, sizeof(header), "%s", "Charging at"); snprintf( @@ -53,40 +52,42 @@ static void draw_battery(Canvas* canvas, BatteryInfoModel* data, int x, int y) { "%lu.%luV %lumA", (uint32_t)(data->vbus_voltage), (uint32_t)(data->vbus_voltage * 10) % 10, - charge_current); - } else if(drain_current > 0) { + current); + } else if(current < 0) { snprintf( emote, sizeof(emote), "%s", - drain_current > HIGH_DRAIN_CURRENT_THRESHOLD ? "Oh no!" : "Om-nom-nom!"); + current < HIGH_DRAIN_CURRENT_THRESHOLD ? "Oh no!" : "Om-nom-nom!"); snprintf(header, sizeof(header), "%s", "Consumption is"); snprintf( value, sizeof(value), "%ld %s", - drain_current, - drain_current > HIGH_DRAIN_CURRENT_THRESHOLD ? "mA!" : "mA"); - } else if(drain_current != 0) { - snprintf(header, 20, "..."); - } else if(data->charge_voltage_limit < 4.2) { - // Non-default battery charging limit, mention it - snprintf(emote, sizeof(emote), "Charged!"); - snprintf(header, sizeof(header), "Limited to"); - snprintf( - value, - sizeof(value), - "%lu.%luV", - (uint32_t)(data->charge_voltage_limit), - (uint32_t)(data->charge_voltage_limit * 10) % 10); + ABS(current), + current < HIGH_DRAIN_CURRENT_THRESHOLD ? "mA!" : "mA"); + } else if(data->vbus_voltage > 0) { + if(data->charge_voltage_limit < 4.2) { + // Non-default battery charging limit, mention it + snprintf(emote, sizeof(emote), "Charged!"); + snprintf(header, sizeof(header), "Limited to"); + snprintf( + value, + sizeof(value), + "%lu.%luV", + (uint32_t)(data->charge_voltage_limit), + (uint32_t)(data->charge_voltage_limit * 10) % 10); + } else { + snprintf(header, sizeof(header), "Charged!"); + } } else { - snprintf(header, sizeof(header), "Charged!"); + snprintf(header, sizeof(header), "Napping..."); } canvas_draw_str_aligned(canvas, 92, y + 3, AlignCenter, AlignCenter, emote); canvas_draw_str_aligned(canvas, 92, y + 15, AlignCenter, AlignCenter, header); canvas_draw_str_aligned(canvas, 92, y + 27, AlignCenter, AlignCenter, value); -}; +} static void battery_info_draw_callback(Canvas* canvas, void* context) { furi_assert(context); diff --git a/applications/settings/storage_settings/scenes/storage_settings_scene_unmounted.c b/applications/settings/storage_settings/scenes/storage_settings_scene_unmounted.c index 13f53acd1..dca53b60a 100644 --- a/applications/settings/storage_settings/scenes/storage_settings_scene_unmounted.c +++ b/applications/settings/storage_settings/scenes/storage_settings_scene_unmounted.c @@ -1,5 +1,5 @@ #include "../storage_settings.h" -#include "xtreme/assets.h" +#include static void storage_settings_scene_unmounted_dialog_callback(DialogExResult result, void* context) { diff --git a/applications/settings/system/system_settings.c b/applications/settings/system/system_settings.c index f9abdb693..655541be5 100644 --- a/applications/settings/system/system_settings.c +++ b/applications/settings/system/system_settings.c @@ -124,6 +124,21 @@ static void date_format_changed(VariableItem* item) { locale_set_date_format(date_format_value[index]); } +const char* const sleep_method[] = { + "Default", + "Legacy", +}; + +static void sleep_method_changed(VariableItem* item) { + uint8_t index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, sleep_method[index]); + if(index) { + furi_hal_rtc_set_flag(FuriHalRtcFlagLegacySleep); + } else { + furi_hal_rtc_reset_flag(FuriHalRtcFlagLegacySleep); + } +} + static uint32_t system_settings_exit(void* context) { UNUSED(context); return VIEW_NONE; @@ -195,6 +210,12 @@ SystemSettings* system_settings_alloc() { variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_text(item, heap_trace_mode_text[value_index]); + item = variable_item_list_add( + app->var_item_list, "Sleep Method", COUNT_OF(sleep_method), sleep_method_changed, app); + value_index = furi_hal_rtc_is_flag_set(FuriHalRtcFlagLegacySleep) ? 1 : 0; + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, sleep_method[value_index]); + view_set_previous_callback( variable_item_list_get_view(app->var_item_list), system_settings_exit); view_dispatcher_add_view( diff --git a/applications/system/updater/application.fam b/applications/system/updater/application.fam index ae6914eb7..4d62c1d48 100644 --- a/applications/system/updater/application.fam +++ b/applications/system/updater/application.fam @@ -7,6 +7,7 @@ App( "gui", "storage", "rgb_backlight", + "archive", ], conflicts=["desktop"], entry_point="updater_srv", @@ -23,6 +24,7 @@ App( "gui", "storage", "bt", + "archive", ], conflicts=["updater"], provides=["updater_start"], diff --git a/applications/system/updater/updater.c b/applications/system/updater/updater.c index e749f3ce6..59af8a3b0 100644 --- a/applications/system/updater/updater.c +++ b/applications/system/updater/updater.c @@ -7,6 +7,7 @@ #include #include #include +#include static bool updater_custom_event_callback(void* context, uint32_t event) { furi_assert(context); @@ -32,8 +33,9 @@ static void updater_main_model_set_state(main_view, message, progress, failed); } -Updater* updater_alloc(const char* arg) { +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("")); @@ -118,10 +120,8 @@ void updater_free(Updater* updater) { free(updater); } -int32_t updater_srv(void* p) { - const char* cfgpath = p; - - Updater* updater = updater_alloc(cfgpath); +int32_t updater_srv(char* p) { + Updater* updater = updater_alloc(p); view_dispatcher_run(updater->view_dispatcher); updater_free(updater); diff --git a/applications/system/updater/updater_i.h b/applications/system/updater/updater_i.h index 4e3c704d2..4940eb60e 100644 --- a/applications/system/updater/updater_i.h +++ b/applications/system/updater/updater_i.h @@ -52,7 +52,7 @@ typedef struct { int32_t idle_ticks; } Updater; -Updater* updater_alloc(const char* arg); +Updater* updater_alloc(char* arg); void updater_free(Updater* updater); diff --git a/assets/icons/Interface/Lockscreen.png b/assets/icons/Interface/Lockscreen.png index c9aa8bcd7..ab4f0a739 100644 Binary files a/assets/icons/Interface/Lockscreen.png and b/assets/icons/Interface/Lockscreen.png differ diff --git a/assets/icons/StatusBar/Muted_8x8.png b/assets/icons/StatusBar/Muted_8x8.png new file mode 100644 index 000000000..fee4e09f5 Binary files /dev/null and b/assets/icons/StatusBar/Muted_8x8.png differ diff --git a/assets/icons/StatusBar/Rpc_active_7x8.png b/assets/icons/StatusBar/Rpc_active_7x8.png new file mode 100644 index 000000000..f643a82aa Binary files /dev/null and b/assets/icons/StatusBar/Rpc_active_7x8.png differ diff --git a/assets/resources/infrared/assets/ac.ir b/assets/resources/infrared/assets/ac.ir index edddec899..49a4acd20 100644 --- a/assets/resources/infrared/assets/ac.ir +++ b/assets/resources/infrared/assets/ac.ir @@ -1,7 +1,7 @@ Filetype: IR library file Version: 1 -# Last Updated 21st Mar, 2023 -# Last Checked 29th Mar, 2023 +# Last Updated 14th Apr, 2023 +# Last Checked 25th Apr, 2023 # name: POWER type: raw @@ -1686,3 +1686,57 @@ type: raw frequency: 38000 duty_cycle: 0.330000 data: 1373 348 1310 376 463 1190 1318 400 1286 401 439 1244 442 1244 1288 400 465 1218 468 1218 468 1219 467 7970 1307 404 1281 405 435 1252 1281 406 1280 406 434 1252 434 1252 1281 406 434 1253 434 1252 434 1252 434 8000 1280 406 1281 406 434 1252 1281 406 1280 406 434 1252 434 1252 1281 406 434 1253 433 1253 433 1253 434 8000 1280 406 1280 406 434 1253 1280 406 1280 406 434 1253 433 1253 1280 406 434 1253 433 1253 433 1253 433 8001 1279 406 1280 406 434 1253 1280 407 1279 407 433 1253 434 1253 1280 407 433 1253 433 1253 433 1253 433 8001 1279 407 1279 407 433 1253 1280 407 1280 407 433 1253 433 1253 1280 407 433 1253 433 1253 434 1253 433 +# OFF +name: POWER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3827 1854 505 433 507 1382 506 434 506 1383 505 434 506 1410 478 433 507 1381 507 433 507 1383 505 435 505 1382 505 1383 505 435 505 1382 506 435 505 1383 505 1382 506 1384 504 1381 507 434 506 434 506 1382 506 1382 506 432 508 434 506 436 504 433 507 1382 506 435 505 434 506 436 504 1381 507 1383 505 434 506 1382 506 1381 507 434 506 1381 507 1381 507 1383 505 435 505 434 506 435 505 433 507 1383 505 435 505 435 505 434 506 1381 507 433 507 435 505 434 506 1382 506 433 507 434 506 433 507 438 502 434 506 433 507 434 506 434 506 432 508 435 505 434 506 433 507 433 507 1387 501 435 505 436 504 433 507 433 507 435 505 433 507 434 506 434 506 435 505 435 505 432 508 1410 478 461 479 432 508 435 505 434 506 433 507 436 504 434 506 434 506 433 507 435 505 1383 505 435 505 1381 507 1381 507 1382 506 1382 506 1381 507 435 505 433 507 433 507 461 478 1383 505 433 507 434 506 +# +name: TEMP- +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3778 1905 506 434 506 1381 507 436 504 1381 507 435 505 1382 506 432 508 1380 508 433 507 1381 507 432 508 1380 508 1381 507 432 508 1380 508 432 508 1380 508 1380 508 1380 508 1380 508 433 507 432 508 1380 508 1380 508 432 508 431 509 432 508 433 507 1380 508 432 508 432 508 431 509 433 507 1380 508 434 506 1380 508 1380 508 434 506 1381 507 1380 508 1380 508 433 507 431 509 433 507 1381 507 1381 507 432 508 431 509 431 509 1381 507 431 509 432 508 432 508 1381 507 431 509 432 508 432 508 432 508 432 508 432 508 435 505 432 508 432 508 431 509 432 508 432 508 434 506 1379 509 432 508 432 508 433 507 431 509 433 507 432 508 433 507 432 508 432 508 432 508 431 509 1380 508 432 508 432 508 1380 508 431 509 433 507 432 508 432 508 432 508 431 509 432 508 1381 507 431 509 1382 506 1380 508 1380 508 1380 508 1380 507 434 506 433 507 433 507 431 509 1380 508 1380 508 432 508 +# +name: TEMP+ +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3828 1855 504 431 508 1382 506 434 506 1382 506 434 506 1381 507 432 508 1381 507 434 506 1381 560 381 506 1381 507 1410 478 433 507 1382 506 436 504 1381 507 1382 506 1409 479 1382 506 433 507 433 507 1381 507 1383 505 435 505 434 506 432 508 433 507 1381 507 433 507 435 505 432 508 1381 507 1382 506 433 507 1382 506 1380 508 433 507 1382 506 1379 509 1381 507 432 508 433 507 433 507 1381 507 1380 508 433 507 433 507 432 508 1380 508 432 508 432 508 434 506 1380 508 432 508 432 508 432 508 432 508 434 506 434 506 433 507 433 507 434 506 432 508 433 507 432 508 435 505 1383 505 433 507 433 507 434 506 433 507 433 507 432 508 433 507 434 506 433 507 433 507 432 508 1382 506 432 508 431 509 1381 507 433 507 434 506 434 506 432 508 433 507 434 506 433 507 1380 508 431 509 1381 507 1409 479 1382 506 1383 505 1381 507 435 505 433 507 433 507 1382 506 1380 508 1382 506 433 507 +# +name: MODE +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3778 1903 508 432 508 1380 508 431 509 1379 509 430 510 1379 509 431 509 1382 506 432 508 1378 510 431 509 1379 509 1381 507 431 509 1379 509 432 508 1379 509 1379 509 1380 508 1381 507 431 509 431 509 1380 508 1379 509 430 510 431 509 431 509 432 508 1379 509 430 510 432 508 431 508 1379 509 430 510 1379 509 1381 507 430 509 432 508 431 509 431 509 1378 510 431 509 431 509 431 509 1380 508 1378 510 432 508 430 510 1379 509 1380 508 431 509 432 508 432 508 1379 509 432 508 431 509 431 509 432 508 431 509 431 509 431 508 431 509 432 508 431 509 431 509 431 509 431 509 1381 507 430 510 431 509 431 509 431 509 431 509 431 509 431 509 430 510 431 509 431 509 432 508 1378 510 431 509 431 509 430 510 431 509 432 508 431 509 431 509 430 510 431 509 431 509 1379 509 431 509 1379 509 1380 508 1379 509 1379 509 1379 509 432 508 431 509 431 509 1379 509 431 509 431 509 1379 509 +# +name: POWER +type: parsed +protocol: NEC +address: 04 00 00 00 +command: 04 00 00 00 +# +name: TEMP+ +type: parsed +protocol: NEC +address: 04 00 00 00 +command: 05 00 00 00 +# +name: TEMP- +type: parsed +protocol: NEC +address: 04 00 00 00 +command: 01 00 00 00 +# +name: MODE +type: parsed +protocol: NEC +address: 04 00 00 00 +command: 0D 00 00 00 +# +name: TIMER +type: parsed +protocol: NEC +address: 04 00 00 00 +command: 00 00 00 00 diff --git a/assets/resources/infrared/assets/audio.ir b/assets/resources/infrared/assets/audio.ir index 0adb61efb..f6183ed86 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 29th Mar, 2023 -# Last Checked 29th Mar, 2023 +# Last Updated 14th Apr, 2023 +# Last Checked 25th Apr, 2023 # name: POWER type: parsed @@ -2134,3 +2134,45 @@ type: raw frequency: 38000 duty_cycle: 0.330000 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+ +type: parsed +protocol: NECext +address: 10 E7 00 00 +command: 3C C3 00 00 +# +name: VOL- +type: parsed +protocol: NECext +address: 10 E7 00 00 +command: 4D B2 00 00 +# +name: POWER +type: parsed +protocol: Kaseikyo +address: 52 54 32 00 +command: 83 00 00 00 +# +name: MUTE +type: parsed +protocol: Kaseikyo +address: 52 54 32 00 +command: 86 00 00 00 +# +name: VOL+ +type: parsed +protocol: Kaseikyo +address: 52 54 32 00 +command: 84 00 00 00 +# +name: VOL- +type: parsed +protocol: Kaseikyo +address: 52 54 32 00 +command: 85 00 00 00 +# +name: POWER +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 46 00 00 00 diff --git a/assets/resources/infrared/assets/fans.ir b/assets/resources/infrared/assets/fans.ir index f451ba483..6bef11b34 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 15th Feb, 2023 -# Last Checked 29th Mar, 2023 +# Last Updated 14th Apr, 2023 +# Last Checked 25th Apr, 2023 # name: POWER type: raw @@ -1419,3 +1419,81 @@ type: parsed protocol: NECext address: 80 DE 00 00 command: 10 EF 00 00 +# +name: POWER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4640 4393 562 1442 562 1443 562 1443 537 1470 535 1468 562 1444 562 2398 562 1444 562 1446 560 2424 535 1470 535 2425 534 1472 533 1473 533 2427 533 1474 531 1473 4577 4456 531 1473 531 1474 532 1474 531 1474 532 1474 531 1474 531 2429 531 1474 531 1474 531 2429 531 1474 531 2429 531 1474 531 1474 531 2429 531 1474 531 14007 9125 2259 530 +# +name: SPEED+ +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4609 4425 563 1441 563 1442 563 1442 538 1469 537 1469 561 1444 562 2400 560 1470 535 1470 536 1470 536 2424 536 1470 535 1471 534 1472 533 2427 533 2427 533 1472 4580 4454 531 1472 533 1474 532 1474 531 1474 532 1474 532 1474 532 2428 532 1474 532 1474 532 1474 532 2428 532 1474 532 1474 532 1474 532 2428 532 2429 532 14008 9127 2258 528 50213 9131 2253 532 +# +name: MODE +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4607 4424 563 1441 563 1443 563 1444 536 1469 537 1469 537 1496 534 2399 561 1470 535 2424 536 1470 536 1470 535 2424 535 1471 534 1472 534 1472 533 2427 533 1472 4579 4455 531 1472 532 1474 532 1474 532 1474 532 1474 532 1474 532 2429 531 1474 532 2428 532 1474 532 1474 532 2429 532 1474 532 1474 532 1474 532 2428 532 14007 9125 2258 530 +# +name: TIMER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4604 4427 561 1442 562 1443 561 1444 537 1469 536 1469 562 1445 561 2399 560 1446 559 2424 534 1472 533 2426 533 1473 532 2428 531 1475 531 1474 531 1474 531 1473 4575 4457 530 1473 531 1475 531 1475 530 1475 531 1475 531 1475 530 2429 531 1475 530 2429 530 1475 530 2430 530 1475 530 2429 531 1475 531 1475 531 1475 530 14008 9122 2260 530 +# +name: ROTATE +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4582 4424 562 1442 562 1471 534 1471 509 1496 510 1496 534 1472 534 2424 536 1470 536 1470 536 1470 536 1470 535 2424 535 2425 534 2426 534 1472 533 1473 533 1472 4581 4453 532 1472 532 1473 533 1473 533 1473 533 1473 533 1473 533 2428 532 1473 532 1473 532 1474 532 1474 532 2428 533 2428 532 2428 532 1474 532 1474 532 +# +name: POWER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 1369 311 1327 312 498 1162 1286 355 1285 362 457 1221 458 1221 458 1221 458 1222 457 1221 458 1219 1312 6796 1310 330 1282 356 454 1219 1281 360 1280 367 452 1227 452 1227 452 1227 452 1227 452 1227 452 1225 1280 6815 1280 359 1279 359 451 1220 1280 361 1279 368 451 1228 451 1228 451 1228 451 1228 451 1228 451 1226 1279 6827 1279 382 1256 382 428 1245 1255 384 1256 391 427 1251 428 1252 426 1252 427 1252 427 1252 427 1250 1255 6838 1255 383 1255 383 426 1245 1255 385 1255 392 426 1252 427 1252 426 1252 426 1252 427 1252 426 1250 1255 6849 1255 383 1255 383 426 1245 1255 385 1254 392 426 1252 426 1252 426 1252 426 1252 426 1252 426 1250 1254 6835 1254 383 1254 383 426 1245 1254 385 1254 392 426 1252 426 1252 426 1252 426 1252 426 1252 426 1250 1254 6852 1254 383 1254 384 425 1245 1254 386 1253 392 426 1253 425 1252 426 1252 426 1253 425 1253 425 1251 1253 6835 1253 384 1253 384 425 1245 1253 386 1253 393 425 1252 425 1253 425 1253 425 1253 425 1253 425 1251 1253 6852 1252 384 1253 384 425 1246 1252 386 1253 393 425 1253 424 1253 425 1253 425 1254 424 1253 425 1252 1252 6835 1253 385 1252 385 424 1247 1252 387 1252 394 424 1254 424 1254 424 1254 424 1254 424 1254 424 1252 1251 6850 1251 386 1251 386 423 1248 1251 388 1250 395 423 1255 422 1256 422 1279 399 1280 398 1279 399 1277 1227 6862 1226 411 1226 411 398 1273 1226 413 1226 420 397 1280 398 1279 398 1280 397 1280 398 1280 398 1278 1226 6892 1226 411 1225 411 397 1273 1225 413 1225 420 397 1280 397 1280 398 1280 397 1280 397 1280 397 1279 1224 +# +name: SPEED+ +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 1308 332 1305 332 479 1190 1309 333 1307 364 455 1199 479 1198 481 1198 480 1198 480 1197 1331 339 480 7657 1306 332 1305 332 479 1194 1304 335 1305 342 478 1201 477 1201 478 1201 477 1201 478 1199 1305 342 478 7647 1305 333 1304 333 478 1194 1304 335 1304 342 477 1201 477 1201 478 1201 477 1201 477 1199 1305 342 478 7660 1303 334 1304 333 478 1194 1304 336 1303 343 477 1202 477 1202 476 1202 477 1202 476 1200 1304 343 477 7647 1303 334 1304 334 476 1195 1303 336 1303 343 476 1202 476 1202 476 1202 476 1202 476 1200 1303 343 476 7659 1302 335 1302 335 476 1196 1302 337 1302 344 475 1203 475 1203 475 1203 475 1203 475 1201 1302 344 476 7646 1302 335 1300 338 474 1197 1300 338 1301 345 474 1204 474 1204 474 1204 449 1229 449 1227 1275 370 449 7690 1275 362 1275 361 449 1222 1275 364 1275 371 448 1230 448 1230 448 1231 447 1232 446 1229 1274 372 447 +# +name: ROTATE +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 1338 312 1380 310 446 1133 1313 356 1284 362 457 1221 457 1219 1285 363 456 1221 457 1222 482 1196 482 7655 1282 356 1281 357 453 1219 1279 360 1279 367 452 1226 452 1224 1280 367 452 1226 452 1226 452 1226 452 7671 1279 358 1279 358 452 1219 1279 360 1279 367 452 1227 451 1225 1279 367 451 1227 451 1227 451 1227 451 7682 1279 359 1278 359 451 1220 1278 361 1278 368 451 1227 450 1225 1278 368 451 1227 451 1228 450 1228 450 7691 1277 360 1277 360 450 1221 1277 362 1277 368 450 1228 449 1226 1277 369 449 1229 449 1252 426 1252 426 +# +name: TIMER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 1338 311 1326 312 497 1134 1364 311 1276 361 457 1221 457 1220 457 1218 1283 361 456 1221 456 1221 457 7676 1285 351 1285 351 457 1213 1286 353 1285 360 457 1220 457 1220 457 1219 1285 361 456 1221 456 1222 481 7639 1283 353 1284 353 455 1216 1283 356 1282 363 454 1223 455 1223 454 1221 1283 363 454 1223 454 1223 454 7676 1282 354 1282 354 454 1217 1282 356 1282 364 453 1223 454 1223 454 1221 1282 363 454 1224 453 1224 454 7681 1281 355 1281 355 453 1217 1281 357 1281 364 453 1225 452 1225 452 1222 1281 365 452 1225 452 1225 452 +# +name: POWER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3565 3379 984 2506 984 2505 985 797 900 2590 900 2590 926 2572 926 826 925 829 922 858 894 859 894 858 895 2604 894 859 894 859 894 2596 894 859 894 859 894 867 894 2596 894 2596 894 2596 894 2596 894 2596 894 867 894 40343 3530 3468 895 2596 894 2596 894 859 894 2596 894 2596 894 2604 894 859 894 859 894 859 894 859 894 859 894 2604 894 859 894 859 894 2596 894 859 894 859 894 867 894 2596 894 2596 894 2596 894 2596 895 2596 894 866 895 +# +name: SPEED+ +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3595 3377 930 2561 929 2561 929 853 900 2591 899 2590 900 2597 901 2590 925 2565 924 2567 922 2570 920 2596 894 866 895 858 895 858 895 2596 894 858 895 859 894 866 895 858 895 858 895 858 895 859 894 858 895 2604 894 40343 3533 3467 895 2595 895 2596 894 858 895 2596 894 2596 894 2604 895 2596 894 2596 894 2596 894 2596 894 2596 894 867 894 859 894 859 894 2596 894 859 894 859 894 867 894 859 894 859 894 859 894 859 894 859 894 2605 893 40319 3533 3442 920 2595 895 2596 894 858 895 2596 894 2596 894 2604 894 2596 894 2596 894 2596 894 2596 894 2596 894 867 894 859 894 859 894 2596 894 859 894 859 894 867 894 859 894 859 894 859 894 859 894 859 894 2604 894 +# +name: ROTATE +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3594 3380 981 2505 930 2560 930 853 900 2590 900 2590 900 2598 900 2589 900 853 924 2568 921 2571 919 2596 894 867 894 859 894 859 894 2596 894 859 894 859 894 867 894 859 894 2596 894 859 894 859 894 859 894 2604 894 40335 3532 3467 895 2596 894 2596 895 859 894 2596 894 2596 894 2605 894 2596 894 859 894 2596 894 2597 894 2596 894 867 894 859 894 859 894 2596 894 859 894 859 894 867 894 859 894 2597 894 859 894 859 894 859 894 2605 893 +# +name: TIMER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3534 3439 954 2505 985 2535 955 798 954 2535 899 2591 899 2599 899 853 925 2566 924 2567 922 2596 894 2596 894 867 894 859 894 860 893 2597 894 859 894 859 894 867 894 2597 893 859 894 859 894 860 893 859 894 2605 894 40336 3531 3469 894 2597 893 2597 893 859 894 2597 893 2597 893 2605 893 860 893 2597 893 2597 893 2597 894 2597 893 868 893 860 893 860 893 2597 893 860 893 860 893 868 893 2597 893 860 893 860 893 860 893 860 893 2605 893 diff --git a/assets/resources/infrared/assets/projectors.ir b/assets/resources/infrared/assets/projectors.ir index 92f5a8eeb..00e331bd4 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 07th Mar, 2023 -# Last Checked 29th Mar, 2023 +# Last Updated 14th Apr, 2023 +# Last Checked 25th Apr, 2023 # # ON name: POWER @@ -874,3 +874,15 @@ type: raw frequency: 38000 duty_cycle: 0.330000 data: 9035 4437 563 548 563 548 563 522 594 1645 591 1639 592 518 593 548 563 552 563 1640 592 548 563 553 562 1668 564 524 592 1642 594 1674 562 1673 563 1639 593 548 563 552 564 1669 562 548 563 520 615 529 586 1645 587 529 587 1650 586 1646 586 529 586 1650 586 1649 587 1646 586 524 587 524 587 524 587 524 587 525 643 467 644 440 671 467 644 472 643 1592 644 1593 643 1593 642 1594 641 1594 587 1649 585 1651 563 1682 562 14430 9008 2205 562 +# +name: VOL+ +type: parsed +protocol: NECext +address: 84 F4 00 00 +command: 2C D3 00 00 +# +name: VOL- +type: parsed +protocol: NECext +address: 84 F4 00 00 +command: 2F D0 00 00 diff --git a/assets/resources/infrared/assets/tv.ir b/assets/resources/infrared/assets/tv.ir index fe2108537..bd246f3d6 100644 --- 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 07th Mar, 2023 -# Last Checked 29th Mar, 2023 +# Last Updated 2, May, 2023 +# Last Checked 2, May, 2023 # name: POWER type: parsed @@ -1899,3 +1899,39 @@ type: parsed protocol: SIRC20 address: 10 01 00 00 command: 33 00 00 00 +# +name: POWER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 195 1833 300 766 280 760 275 790 276 737 309 731 304 1801 301 1804 309 731 304 1801 270 795 282 758 277 762 273 1832 270 769 246 45851 326 1780 302 739 307 785 282 732 303 736 310 1795 307 732 303 763 303 1775 307 733 334 1798 273 1832 270 1810 251 814 273 1780 281 43762 302 1804 309 758 277 737 330 762 284 730 305 734 301 1803 310 1796 306 733 302 1829 273 767 279 734 301 791 275 1804 278 762 253 45870 307 1798 304 763 272 767 279 787 279 760 275 1829 284 730 305 734 301 1804 309 757 278 1827 275 1804 278 1828 274 765 270 1835 278 43740 303 1776 306 787 279 760 275 765 281 759 307 758 277 1775 307 1799 303 736 299 1832 281 759 276 763 304 736 299 1832 281 733 302 45820 306 1800 302 764 282 758 277 788 278 762 284 1821 281 732 303 736 310 1796 307 733 302 1829 273 1806 276 1830 272 767 268 1837 245 43772 302 1778 304 789 277 762 284 756 279 786 249 765 301 1777 336 1770 301 764 282 1824 278 761 274 765 301 738 308 1824 278 761 274 +# +name: MUTE +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 254 1721 360 681 354 738 308 706 329 711 355 1774 307 1772 361 1744 327 687 359 1772 299 742 335 705 330 736 279 1825 298 742 283 44773 384 1721 360 707 308 707 359 733 302 711 335 705 361 704 331 708 338 1766 336 704 331 1773 329 1776 306 1773 360 681 323 1782 331 44726 411 1722 328 686 360 733 302 711 335 705 361 1742 329 1803 330 1749 332 708 327 1777 335 705 330 710 325 741 274 1830 303 737 278 44778 359 1747 355 712 303 711 355 711 335 705 330 709 337 703 363 703 332 1770 332 709 337 1767 335 1771 300 1752 360 733 302 1776 326 44731 355 1751 330 711 355 737 309 705 330 710 336 1793 309 1771 331 1774 307 706 360 1771 300 740 326 714 332 735 280 1798 325 741 274 +# +name: POWER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9219 4484 662 469 661 469 661 1627 660 471 658 474 656 499 631 499 631 499 631 1657 630 1657 631 500 630 1657 631 1657 631 1657 630 1657 631 1657 631 500 630 500 630 500 630 1657 630 500 631 500 630 500 631 500 630 1657 630 1658 630 1657 631 500 630 1657 631 1658 630 1658 630 1658 630 40107 9106 2202 631 +# +name: VOL+ +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9218 4484 636 495 660 469 661 1627 660 471 658 472 658 474 656 475 655 474 656 1632 655 1632 656 474 657 1632 656 1631 657 1632 656 1631 656 1632 656 474 656 1632 655 475 656 474 657 474 656 474 656 474 656 474 656 1632 655 474 656 1632 656 1632 656 1632 656 1632 656 1632 656 1632 656 40103 9107 2177 655 +# +name: VOL- +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9245 4429 689 467 662 468 661 1626 660 471 658 473 657 474 656 474 656 474 656 1631 657 1631 657 474 656 1631 656 1632 656 1631 657 1631 657 1631 656 1632 656 1631 657 474 656 474 656 474 657 474 656 474 656 474 657 474 656 474 656 1631 656 1632 656 1632 656 1632 656 1632 656 1631 656 40082 9109 2175 656 +# +name: MUTE +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9219 4485 636 495 660 469 661 1626 661 471 658 473 657 474 656 499 631 500 630 1657 630 1657 631 500 630 1657 630 1657 631 1657 631 1657 630 1657 631 1657 631 500 630 500 630 1657 631 500 630 500 630 500 630 500 631 500 630 1657 631 1657 631 500 630 1657 631 1658 630 1657 631 1658 630 39868 9106 2178 655 diff --git a/debug/flipperapps.py b/debug/flipperapps.py index 1dc5ebd04..90582c1e4 100644 --- a/debug/flipperapps.py +++ b/debug/flipperapps.py @@ -135,6 +135,7 @@ class FlipperAppStateHelper: self.app_list_ptr = None self.app_list_entry_type = None self._current_apps: list[AppState] = [] + self.set_debug_mode(True) def _walk_app_list(self, list_head): while list_head: @@ -195,7 +196,7 @@ class FlipperAppStateHelper: self.set_debug_mode(False) def set_debug_mode(self, mode: bool) -> None: - gdb.execute(f"set variable fap_loader_debug_active = {int(mode)}") + gdb.execute(f"set variable furi_hal_debug_gdb_session_active = {int(mode)}") # Init additional 'fap-set-debug-elf-root' command and set up hooks diff --git a/fbt_options.py b/fbt_options.py index 0e87521a1..5c874357a 100644 --- a/fbt_options.py +++ b/fbt_options.py @@ -14,7 +14,7 @@ DEBUG = 0 # Suffix to add to files when building distribution # If OS environment has DIST_SUFFIX set, it will be used instead -DIST_SUFFIX = "XFW-0044_09042023" +DIST_SUFFIX = "XFW-0045_02052023" # Coprocessor firmware COPRO_OB_DATA = "scripts/ob.data" diff --git a/firmware/targets/f18/api_symbols.csv b/firmware/targets/f18/api_symbols.csv index df4401aab..0852b9243 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,+,21.0,, +Version,+,23.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -176,17 +176,17 @@ Header,+,lib/toolbox/tar/tar_archive.h,, Header,+,lib/toolbox/value_index.h,, Header,+,lib/toolbox/version.h,, Function,-,LL_ADC_CommonDeInit,ErrorStatus,ADC_Common_TypeDef* -Function,-,LL_ADC_CommonInit,ErrorStatus,"ADC_Common_TypeDef*, LL_ADC_CommonInitTypeDef*" +Function,-,LL_ADC_CommonInit,ErrorStatus,"ADC_Common_TypeDef*, const LL_ADC_CommonInitTypeDef*" Function,-,LL_ADC_CommonStructInit,void,LL_ADC_CommonInitTypeDef* Function,-,LL_ADC_DeInit,ErrorStatus,ADC_TypeDef* -Function,-,LL_ADC_INJ_Init,ErrorStatus,"ADC_TypeDef*, LL_ADC_INJ_InitTypeDef*" +Function,-,LL_ADC_INJ_Init,ErrorStatus,"ADC_TypeDef*, const LL_ADC_INJ_InitTypeDef*" Function,-,LL_ADC_INJ_StructInit,void,LL_ADC_INJ_InitTypeDef* -Function,-,LL_ADC_Init,ErrorStatus,"ADC_TypeDef*, LL_ADC_InitTypeDef*" -Function,-,LL_ADC_REG_Init,ErrorStatus,"ADC_TypeDef*, LL_ADC_REG_InitTypeDef*" +Function,-,LL_ADC_Init,ErrorStatus,"ADC_TypeDef*, const LL_ADC_InitTypeDef*" +Function,-,LL_ADC_REG_Init,ErrorStatus,"ADC_TypeDef*, const LL_ADC_REG_InitTypeDef*" Function,-,LL_ADC_REG_StructInit,void,LL_ADC_REG_InitTypeDef* Function,-,LL_ADC_StructInit,void,LL_ADC_InitTypeDef* Function,-,LL_COMP_DeInit,ErrorStatus,COMP_TypeDef* -Function,+,LL_COMP_Init,ErrorStatus,"COMP_TypeDef*, LL_COMP_InitTypeDef*" +Function,+,LL_COMP_Init,ErrorStatus,"COMP_TypeDef*, const LL_COMP_InitTypeDef*" Function,-,LL_COMP_StructInit,void,LL_COMP_InitTypeDef* Function,-,LL_CRC_DeInit,ErrorStatus,CRC_TypeDef* Function,-,LL_CRS_DeInit,ErrorStatus, @@ -205,10 +205,10 @@ Function,-,LL_I2C_StructInit,void,LL_I2C_InitTypeDef* Function,-,LL_Init1msTick,void,uint32_t Function,+,LL_LPTIM_DeInit,ErrorStatus,LPTIM_TypeDef* Function,-,LL_LPTIM_Disable,void,LPTIM_TypeDef* -Function,+,LL_LPTIM_Init,ErrorStatus,"LPTIM_TypeDef*, LL_LPTIM_InitTypeDef*" +Function,+,LL_LPTIM_Init,ErrorStatus,"LPTIM_TypeDef*, const LL_LPTIM_InitTypeDef*" Function,-,LL_LPTIM_StructInit,void,LL_LPTIM_InitTypeDef* -Function,-,LL_LPUART_DeInit,ErrorStatus,USART_TypeDef* -Function,+,LL_LPUART_Init,ErrorStatus,"USART_TypeDef*, LL_LPUART_InitTypeDef*" +Function,-,LL_LPUART_DeInit,ErrorStatus,const USART_TypeDef* +Function,+,LL_LPUART_Init,ErrorStatus,"USART_TypeDef*, const LL_LPUART_InitTypeDef*" Function,-,LL_LPUART_StructInit,void,LL_LPUART_InitTypeDef* Function,-,LL_PKA_DeInit,ErrorStatus,PKA_TypeDef* Function,-,LL_PKA_Init,ErrorStatus,"PKA_TypeDef*, LL_PKA_InitTypeDef*" @@ -253,23 +253,23 @@ Function,+,LL_SPI_Init,ErrorStatus,"SPI_TypeDef*, LL_SPI_InitTypeDef*" Function,-,LL_SPI_StructInit,void,LL_SPI_InitTypeDef* Function,-,LL_SetFlashLatency,ErrorStatus,uint32_t Function,+,LL_SetSystemCoreClock,void,uint32_t -Function,-,LL_TIM_BDTR_Init,ErrorStatus,"TIM_TypeDef*, LL_TIM_BDTR_InitTypeDef*" +Function,-,LL_TIM_BDTR_Init,ErrorStatus,"TIM_TypeDef*, const LL_TIM_BDTR_InitTypeDef*" Function,-,LL_TIM_BDTR_StructInit,void,LL_TIM_BDTR_InitTypeDef* -Function,+,LL_TIM_DeInit,ErrorStatus,TIM_TypeDef* -Function,-,LL_TIM_ENCODER_Init,ErrorStatus,"TIM_TypeDef*, LL_TIM_ENCODER_InitTypeDef*" +Function,-,LL_TIM_DeInit,ErrorStatus,TIM_TypeDef* +Function,-,LL_TIM_ENCODER_Init,ErrorStatus,"TIM_TypeDef*, const LL_TIM_ENCODER_InitTypeDef*" Function,-,LL_TIM_ENCODER_StructInit,void,LL_TIM_ENCODER_InitTypeDef* -Function,-,LL_TIM_HALLSENSOR_Init,ErrorStatus,"TIM_TypeDef*, LL_TIM_HALLSENSOR_InitTypeDef*" +Function,-,LL_TIM_HALLSENSOR_Init,ErrorStatus,"TIM_TypeDef*, const LL_TIM_HALLSENSOR_InitTypeDef*" Function,-,LL_TIM_HALLSENSOR_StructInit,void,LL_TIM_HALLSENSOR_InitTypeDef* -Function,-,LL_TIM_IC_Init,ErrorStatus,"TIM_TypeDef*, uint32_t, LL_TIM_IC_InitTypeDef*" +Function,-,LL_TIM_IC_Init,ErrorStatus,"TIM_TypeDef*, uint32_t, const LL_TIM_IC_InitTypeDef*" Function,-,LL_TIM_IC_StructInit,void,LL_TIM_IC_InitTypeDef* -Function,+,LL_TIM_Init,ErrorStatus,"TIM_TypeDef*, LL_TIM_InitTypeDef*" -Function,+,LL_TIM_OC_Init,ErrorStatus,"TIM_TypeDef*, uint32_t, LL_TIM_OC_InitTypeDef*" +Function,+,LL_TIM_Init,ErrorStatus,"TIM_TypeDef*, const LL_TIM_InitTypeDef*" +Function,+,LL_TIM_OC_Init,ErrorStatus,"TIM_TypeDef*, uint32_t, const LL_TIM_OC_InitTypeDef*" Function,-,LL_TIM_OC_StructInit,void,LL_TIM_OC_InitTypeDef* Function,-,LL_TIM_StructInit,void,LL_TIM_InitTypeDef* -Function,-,LL_USART_ClockInit,ErrorStatus,"USART_TypeDef*, LL_USART_ClockInitTypeDef*" +Function,-,LL_USART_ClockInit,ErrorStatus,"USART_TypeDef*, const LL_USART_ClockInitTypeDef*" Function,-,LL_USART_ClockStructInit,void,LL_USART_ClockInitTypeDef* -Function,-,LL_USART_DeInit,ErrorStatus,USART_TypeDef* -Function,+,LL_USART_Init,ErrorStatus,"USART_TypeDef*, LL_USART_InitTypeDef*" +Function,-,LL_USART_DeInit,ErrorStatus,const USART_TypeDef* +Function,+,LL_USART_Init,ErrorStatus,"USART_TypeDef*, const LL_USART_InitTypeDef*" Function,-,LL_USART_StructInit,void,LL_USART_InitTypeDef* Function,-,LL_mDelay,void,uint32_t Function,-,SystemCoreClockUpdate,void, @@ -901,6 +901,7 @@ Function,+,furi_hal_crypto_verify_enclave,_Bool,"uint8_t*, uint8_t*" Function,+,furi_hal_crypto_verify_key,_Bool,uint8_t Function,+,furi_hal_debug_disable,void, Function,+,furi_hal_debug_enable,void, +Function,+,furi_hal_debug_is_gdb_session_active,_Bool, Function,-,furi_hal_deinit_early,void, Function,-,furi_hal_flash_erase,void,uint8_t Function,-,furi_hal_flash_get_base,size_t, @@ -983,7 +984,6 @@ Function,-,furi_hal_os_init,void, Function,+,furi_hal_os_tick,void, Function,+,furi_hal_power_check_otg_status,void, Function,+,furi_hal_power_debug_get,void,"PropertyValueCallback, void*" -Function,+,furi_hal_power_deep_sleep_available,_Bool, Function,+,furi_hal_power_disable_external_3_3v,void, Function,+,furi_hal_power_disable_otg,void, Function,+,furi_hal_power_enable_external_3_3v,void, @@ -1059,6 +1059,7 @@ Function,+,furi_hal_rtc_set_locale_units,void,FuriHalRtcLocaleUnits Function,+,furi_hal_rtc_set_log_level,void,uint8_t Function,+,furi_hal_rtc_set_pin_fails,void,uint32_t Function,+,furi_hal_rtc_set_register,void,"FuriHalRtcRegister, uint32_t" +Function,+,furi_hal_rtc_sync_shadow,void, Function,+,furi_hal_rtc_validate_datetime,_Bool,FuriHalRtcDateTime* Function,+,furi_hal_speaker_acquire,_Bool,uint32_t Function,-,furi_hal_speaker_deinit,void, @@ -1596,7 +1597,8 @@ Function,-,rindex,char*,"const char*, int" Function,+,rpc_session_close,void,RpcSession* Function,+,rpc_session_feed,size_t,"RpcSession*, uint8_t*, size_t, TickType_t" Function,+,rpc_session_get_available_size,size_t,RpcSession* -Function,+,rpc_session_open,RpcSession*,Rpc* +Function,+,rpc_session_get_owner,RpcOwner,RpcSession* +Function,+,rpc_session_open,RpcSession*,"Rpc*, RpcOwner" Function,+,rpc_session_set_buffer_is_empty_callback,void,"RpcSession*, RpcBufferIsEmptyCallback" Function,+,rpc_session_set_close_callback,void,"RpcSession*, RpcSessionClosedCallback" Function,+,rpc_session_set_context,void,"RpcSession*, void*" @@ -2150,6 +2152,8 @@ Variable,+,gpio_ext_pd0,const GpioPin, Variable,+,gpio_ext_pe4,const GpioPin, Variable,+,gpio_i2c_power_scl,const GpioPin, Variable,+,gpio_i2c_power_sda,const GpioPin, +Variable,+,gpio_ibutton,const GpioPin, +Variable,+,gpio_periph_power,const GpioPin, Variable,+,gpio_pins,const GpioPinRecord[], Variable,+,gpio_pins_count,const size_t, Variable,+,gpio_sdcard_cd,const GpioPin, @@ -2158,11 +2162,13 @@ Variable,+,gpio_speaker,const GpioPin, Variable,+,gpio_spi_d_miso,const GpioPin, Variable,+,gpio_spi_d_mosi,const GpioPin, Variable,+,gpio_spi_d_sck,const GpioPin, +Variable,+,gpio_swclk,const GpioPin, +Variable,+,gpio_swdio,const GpioPin, Variable,+,gpio_usart_rx,const GpioPin, Variable,+,gpio_usart_tx,const GpioPin, Variable,+,gpio_usb_dm,const GpioPin, Variable,+,gpio_usb_dp,const GpioPin, -Variable,+,gpio_ibutton,const GpioPin, +Variable,+,gpio_vibro,const GpioPin, Variable,+,input_pins,const InputPin[], Variable,+,input_pins_count,const size_t, Variable,+,message_blink_set_color_blue,const NotificationMessage, @@ -2310,7 +2316,6 @@ Variable,+,message_red_255,const NotificationMessage, Variable,+,message_sound_off,const NotificationMessage, Variable,+,message_vibro_off,const NotificationMessage, Variable,+,message_vibro_on,const NotificationMessage, -Variable,+,gpio_periph_power,const GpioPin, Variable,+,sequence_audiovisual_alert,const NotificationSequence, Variable,+,sequence_blink_blue_10,const NotificationSequence, Variable,+,sequence_blink_blue_100,const NotificationSequence, @@ -2365,4 +2370,3 @@ Variable,+,usb_cdc_single,FuriHalUsbInterface, Variable,+,usb_hid,FuriHalUsbInterface, Variable,+,usb_hid_u2f,FuriHalUsbInterface, Variable,+,usbd_devfs,const usbd_driver, -Variable,+,gpio_vibro,const GpioPin, diff --git a/firmware/targets/f18/furi_hal/furi_hal.c b/firmware/targets/f18/furi_hal/furi_hal.c index 4064dd647..1a180e698 100644 --- a/firmware/targets/f18/furi_hal/furi_hal.c +++ b/firmware/targets/f18/furi_hal/furi_hal.c @@ -6,6 +6,16 @@ #define TAG "FuriHal" +bool normal_boot = false; + +void furi_hal_set_is_normal_boot(bool value) { + normal_boot = value; +} + +bool furi_hal_is_normal_boot() { + return normal_boot; +} + void furi_hal_init_early() { furi_hal_cortex_init_early(); furi_hal_clock_init_early(); diff --git a/firmware/targets/f18/furi_hal/furi_hal_resources.c b/firmware/targets/f18/furi_hal/furi_hal_resources.c index 19bc9f998..6db483dbc 100644 --- a/firmware/targets/f18/furi_hal/furi_hal_resources.c +++ b/firmware/targets/f18/furi_hal/furi_hal_resources.c @@ -6,6 +6,9 @@ #define TAG "FuriHalResources" +const GpioPin gpio_swdio = {.port = GPIOA, .pin = LL_GPIO_PIN_13}; +const GpioPin gpio_swclk = {.port = GPIOA, .pin = LL_GPIO_PIN_14}; + const GpioPin gpio_vibro = {.port = GPIOA, .pin = LL_GPIO_PIN_8}; const GpioPin gpio_ibutton = {.port = GPIOB, .pin = LL_GPIO_PIN_14}; @@ -166,8 +169,9 @@ void furi_hal_resources_init() { furi_hal_resources_init_input_pins(GpioModeInterruptRiseFall); // Explicit pulls pins - furi_hal_gpio_init(&gpio_speaker, GpioModeAnalog, GpioPullDown, GpioSpeedLow); - furi_hal_gpio_init(&gpio_vibro, GpioModeAnalog, GpioPullDown, GpioSpeedLow); + LL_PWR_EnablePUPDCfg(); + LL_PWR_EnableGPIOPullDown(LL_PWR_GPIO_B, LL_PWR_GPIO_BIT_8); // gpio_speaker + LL_PWR_EnableGPIOPullDown(LL_PWR_GPIO_A, LL_PWR_GPIO_BIT_8); // gpio_vibro // Display pins furi_hal_gpio_init(&gpio_display_rst_n, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); diff --git a/firmware/targets/f18/furi_hal/furi_hal_resources.h b/firmware/targets/f18/furi_hal/furi_hal_resources.h index 3c4708d15..7d2caab36 100644 --- a/firmware/targets/f18/furi_hal/furi_hal_resources.h +++ b/firmware/targets/f18/furi_hal/furi_hal_resources.h @@ -50,6 +50,9 @@ extern const size_t input_pins_count; extern const GpioPinRecord gpio_pins[]; extern const size_t gpio_pins_count; +extern const GpioPin gpio_swdio; +extern const GpioPin gpio_swclk; + extern const GpioPin gpio_vibro; extern const GpioPin gpio_ibutton; diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 66536e574..2ccfe3ed5 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,21.0,, +Version,+,23.0,, Header,+,applications/main/fap_loader/fap_loader_app.h,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, @@ -40,8 +40,6 @@ Header,+,applications/services/power/power_service/power.h,, Header,+,applications/services/rgb_backlight/rgb_backlight.h,, Header,+,applications/services/rpc/rpc_app.h,, Header,+,applications/services/storage/storage.h,, -Header,+,applications/services/xtreme/assets.h,, -Header,+,applications/services/xtreme/settings.h,, Header,+,firmware/targets/f7/furi_hal/furi_hal_clock.h,, Header,+,firmware/targets/f7/furi_hal/furi_hal_console.h,, Header,+,firmware/targets/f7/furi_hal/furi_hal_flash.h,, @@ -218,6 +216,7 @@ Header,+,lib/toolbox/tar/tar_archive.h,, Header,+,lib/toolbox/value_index.h,, Header,+,lib/toolbox/version.h,, Header,+,lib/u8g2/u8g2.h,, +Header,+,lib/xtreme/xtreme.h,, Function,-,LL_ADC_CommonDeInit,ErrorStatus,ADC_Common_TypeDef* Function,-,LL_ADC_CommonInit,ErrorStatus,"ADC_Common_TypeDef*, LL_ADC_CommonInitTypeDef*" Function,-,LL_ADC_CommonStructInit,void,LL_ADC_CommonInitTypeDef* @@ -322,10 +321,9 @@ Function,-,SK6805_update,void, Function,-,SystemCoreClockUpdate,void, Function,-,SystemInit,void, Function,+,XTREME_ASSETS,XtremeAssets*, -Function,-,XTREME_ASSETS_LOAD,void, Function,+,XTREME_SETTINGS,XtremeSettings*, -Function,-,XTREME_SETTINGS_LOAD,void, -Function,+,XTREME_SETTINGS_SAVE,_Bool, +Function,+,XTREME_SETTINGS_SAVE,void, +Function,+,XTREME_SETTINGS_WAIT,XtremeSettings*, Function,-,_Exit,void,int Function,-,__assert,void,"const char*, int, const char*" Function,+,__assert_func,void,"const char*, int, const char*, const char*" @@ -853,6 +851,7 @@ Function,+,elements_multiline_text_framed,void,"Canvas*, uint8_t, uint8_t, const Function,+,elements_progress_bar,void,"Canvas*, uint8_t, uint8_t, uint8_t, float" Function,+,elements_progress_bar_with_text,void,"Canvas*, uint8_t, uint8_t, uint8_t, float, const char*" Function,+,elements_scrollable_text_line,void,"Canvas*, uint8_t, uint8_t, uint8_t, FuriString*, size_t, _Bool, _Bool" +Function,+,elements_scrollable_text_line_str,void,"Canvas*, uint8_t, uint8_t, uint8_t, const char*, size_t, _Bool, _Bool" Function,+,elements_scrollbar,void,"Canvas*, uint16_t, uint16_t" Function,+,elements_scrollbar_pos,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint16_t, uint16_t" Function,+,elements_slightly_rounded_box,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t" @@ -898,18 +897,20 @@ Function,-,fdopen,FILE*,"int, const char*" Function,-,felica_check_ic_type,_Bool,uint8_t* Function,-,felica_clear,void,FelicaData* Function,-,felica_define_normal_block,void,"FelicaService*, uint16_t, uint8_t*" +Function,-,felica_estimate_timing_us,uint_least32_t,"uint_least8_t, uint_least8_t" 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_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" Function,-,felica_parse_unencrypted_write,_Bool,"uint8_t*, uint8_t, FelicaReader*" Function,-,felica_prepare_unencrypted_read,uint8_t,"uint8_t*, const FelicaReader*, const uint16_t*, uint8_t, const uint32_t*, uint8_t" Function,-,felica_prepare_unencrypted_write,uint8_t,"uint8_t*, FelicaReader*, const uint16_t*, uint8_t, const uint32_t*, uint8_t, const uint8_t*" +Function,-,felica_push_normal_block,void,"FelicaService*, uint8_t*" Function,-,felica_read_card,_Bool,"FuriHalNfcTxRxContext*, FelicaData*, uint8_t*, uint8_t*" -Function,-,felica_read_lite_system,_Bool,"FuriHalNfcTxRxContext*, FelicaReader*, FelicaData*, FelicaSystem*" Function,-,feof,int,FILE* Function,-,feof_unlocked,int,FILE* Function,-,ferror,int,FILE* @@ -1179,6 +1180,7 @@ Function,+,furi_hal_crypto_verify_enclave,_Bool,"uint8_t*, uint8_t*" Function,+,furi_hal_crypto_verify_key,_Bool,uint8_t Function,+,furi_hal_debug_disable,void, Function,+,furi_hal_debug_enable,void, +Function,+,furi_hal_debug_is_gdb_session_active,_Bool, Function,-,furi_hal_deinit_early,void, Function,-,furi_hal_flash_erase,void,uint8_t Function,-,furi_hal_flash_get_base,size_t, @@ -1261,6 +1263,7 @@ Function,-,furi_hal_init_early,void, Function,-,furi_hal_interrupt_init,void, Function,+,furi_hal_interrupt_set_isr,void,"FuriHalInterruptId, FuriHalInterruptISR, void*" Function,+,furi_hal_interrupt_set_isr_ex,void,"FuriHalInterruptId, uint16_t, FuriHalInterruptISR, void*" +Function,-,furi_hal_is_normal_boot,_Bool, Function,+,furi_hal_light_blink_set_color,void,Light Function,+,furi_hal_light_blink_start,void,"Light, uint8_t, uint16_t, uint16_t" Function,+,furi_hal_light_blink_stop,void, @@ -1311,7 +1314,6 @@ Function,-,furi_hal_os_init,void, Function,+,furi_hal_os_tick,void, Function,+,furi_hal_power_check_otg_status,void, Function,+,furi_hal_power_debug_get,void,"PropertyValueCallback, void*" -Function,+,furi_hal_power_deep_sleep_available,_Bool, Function,+,furi_hal_power_disable_external_3_3v,void, Function,+,furi_hal_power_disable_otg,void, Function,+,furi_hal_power_enable_external_3_3v,void, @@ -1412,7 +1414,9 @@ Function,+,furi_hal_rtc_set_locale_units,void,FuriHalRtcLocaleUnits Function,+,furi_hal_rtc_set_log_level,void,uint8_t Function,+,furi_hal_rtc_set_pin_fails,void,uint32_t Function,+,furi_hal_rtc_set_register,void,"FuriHalRtcRegister, uint32_t" +Function,+,furi_hal_rtc_sync_shadow,void, Function,+,furi_hal_rtc_validate_datetime,_Bool,FuriHalRtcDateTime* +Function,-,furi_hal_set_is_normal_boot,void,_Bool Function,+,furi_hal_speaker_acquire,_Bool,uint32_t Function,-,furi_hal_speaker_deinit,void, Function,-,furi_hal_speaker_init,void, @@ -1447,9 +1451,11 @@ 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, +Function,+,furi_hal_subghz_init_radio_type,_Bool,SubGhzRadioType Function,+,furi_hal_subghz_is_async_tx_complete,_Bool, Function,+,furi_hal_subghz_is_frequency_valid,_Bool,uint32_t Function,+,furi_hal_subghz_is_rx_data_crc_valid,_Bool, @@ -1462,14 +1468,15 @@ Function,+,furi_hal_subghz_read_packet,void,"uint8_t*, uint8_t*" Function,+,furi_hal_subghz_reset,void, Function,+,furi_hal_subghz_rx,void, Function,+,furi_hal_subghz_rx_pipe_not_empty,_Bool, +Function,+,furi_hal_subghz_select_radio_type,void,SubGhzRadioType Function,+,furi_hal_subghz_set_async_mirror_pin,void,const GpioPin* Function,+,furi_hal_subghz_set_extend_settings,void,"_Bool, _Bool" Function,+,furi_hal_subghz_set_external_power_disable,void,_Bool Function,+,furi_hal_subghz_set_frequency,uint32_t,uint32_t Function,+,furi_hal_subghz_set_frequency_and_path,uint32_t,uint32_t Function,+,furi_hal_subghz_set_path,void,FuriHalSubGhzPath -Function,+,furi_hal_subghz_set_radio_type,_Bool,SubGhzRadioType 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*" @@ -2264,6 +2271,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_trigger_ui_update,void,Power* Function,+,powf,float,"float, float" Function,-,powl,long double,"long double, long double" Function,+,pretty_format_bytes_hex_canonical,void,"FuriString*, size_t, const char*, const uint8_t*, size_t" @@ -2522,7 +2530,8 @@ Function,-,roundl,long double,long double Function,+,rpc_session_close,void,RpcSession* Function,+,rpc_session_feed,size_t,"RpcSession*, uint8_t*, size_t, TickType_t" Function,+,rpc_session_get_available_size,size_t,RpcSession* -Function,+,rpc_session_open,RpcSession*,Rpc* +Function,+,rpc_session_get_owner,RpcOwner,RpcSession* +Function,+,rpc_session_open,RpcSession*,"Rpc*, RpcOwner" Function,+,rpc_session_set_buffer_is_empty_callback,void,"RpcSession*, RpcBufferIsEmptyCallback" Function,+,rpc_session_set_close_callback,void,"RpcSession*, RpcSessionClosedCallback" Function,+,rpc_session_set_context,void,"RpcSession*, void*" @@ -2757,7 +2766,7 @@ Function,+,subghz_block_generic_get_preset_name,void,"const char*, FuriString*" Function,+,subghz_block_generic_serialize,SubGhzProtocolStatus,"SubGhzBlockGeneric*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_custom_btn_get,uint8_t, Function,-,subghz_custom_btn_get_original,uint8_t, -Function,-,subghz_custom_btn_set,void,uint8_t +Function,+,subghz_custom_btn_set,void,uint8_t Function,-,subghz_custom_btn_set_max,void,uint8_t Function,-,subghz_custom_btn_set_original,void,uint8_t Function,+,subghz_custom_btns_reset,void, @@ -3453,6 +3462,7 @@ Function,+,text_input_get_validator_callback_context,void*,TextInput* Function,+,text_input_get_view,View*,TextInput* Function,+,text_input_reset,void,TextInput* Function,+,text_input_set_header_text,void,"TextInput*, const char*" +Function,+,text_input_set_minimum_length,void,"TextInput*, size_t" Function,+,text_input_set_result_callback,void,"TextInput*, TextInputCallback, void*, char*, size_t, _Bool" Function,+,text_input_set_validator,void,"TextInput*, TextInputValidatorCallback, void*" Function,-,tga_save,void,const char* @@ -4848,10 +4858,12 @@ Variable,+,gpio_ext_pc1,const GpioPin, Variable,+,gpio_ext_pc3,const GpioPin, Variable,+,gpio_i2c_power_scl,const GpioPin, Variable,+,gpio_i2c_power_sda,const GpioPin, +Variable,+,gpio_ibutton,const GpioPin, Variable,+,gpio_infrared_rx,const GpioPin, Variable,+,gpio_infrared_tx,const GpioPin, Variable,+,gpio_nfc_cs,const GpioPin, Variable,+,gpio_nfc_irq_rfid_pull,const GpioPin, +Variable,+,gpio_periph_power,const GpioPin, Variable,+,gpio_pins,const GpioPinRecord[], Variable,+,gpio_pins_count,const size_t, Variable,+,gpio_rf_sw_0,const GpioPin, @@ -4872,11 +4884,13 @@ Variable,+,gpio_spi_r_sck,const GpioPin, Variable,+,gpio_spi_r_sck_ext,const GpioPin, Variable,+,gpio_subghz_cs,const GpioPin, Variable,+,gpio_subghz_cs_ext,const GpioPin, +Variable,+,gpio_swclk,const GpioPin, +Variable,+,gpio_swdio,const GpioPin, Variable,+,gpio_usart_rx,const GpioPin, Variable,+,gpio_usart_tx,const GpioPin, Variable,+,gpio_usb_dm,const GpioPin, Variable,+,gpio_usb_dp,const GpioPin, -Variable,+,gpio_ibutton,const GpioPin, +Variable,+,gpio_vibro,const GpioPin, Variable,+,input_pins,const InputPin[], Variable,+,input_pins_count,const size_t, Variable,+,lfrfid_protocols,const ProtocolBase*[], @@ -5025,7 +5039,6 @@ Variable,+,message_red_255,const NotificationMessage, Variable,+,message_sound_off,const NotificationMessage, Variable,+,message_vibro_off,const NotificationMessage, Variable,+,message_vibro_on,const NotificationMessage, -Variable,+,gpio_periph_power,const GpioPin, Variable,+,sequence_audiovisual_alert,const NotificationSequence, Variable,+,sequence_blink_blue_10,const NotificationSequence, Variable,+,sequence_blink_blue_100,const NotificationSequence, @@ -7032,4 +7045,3 @@ Variable,+,usb_cdc_single,FuriHalUsbInterface, Variable,+,usb_hid,FuriHalUsbInterface, Variable,+,usb_hid_u2f,FuriHalUsbInterface, Variable,+,usbd_devfs,const usbd_driver, -Variable,+,gpio_vibro,const GpioPin, diff --git a/firmware/targets/f7/ble_glue/app_debug.c b/firmware/targets/f7/ble_glue/app_debug.c index d84588540..78e789ac3 100644 --- a/firmware/targets/f7/ble_glue/app_debug.c +++ b/firmware/targets/f7/ble_glue/app_debug.c @@ -68,7 +68,7 @@ static const APPD_GpioConfig_t aGpioConfigList[GPIO_CFG_NBR_OF_FEATURES] = { {GPIOA, LL_GPIO_PIN_0, 0, 0}, /* END_OF_CONNECTION_EVENT - Set on Entry / Reset on Exit */ {GPIOA, LL_GPIO_PIN_0, 0, 0}, /* TIMER_SERVER_CALLBACK - Toggle on Entry */ {GPIOA, LL_GPIO_PIN_4, 1, 0}, /* PES_ACTIVITY - Set on Entry / Reset on Exit */ - {GPIOB, LL_GPIO_PIN_2, 1, 0}, /* MB_BLE_SEND_EVT - Set on Entry / Reset on Exit */ + {GPIOC, LL_GPIO_PIN_0, 1, 0}, /* MB_BLE_SEND_EVT - Set on Entry / Reset on Exit */ /* From v1.3.0 */ {GPIOA, LL_GPIO_PIN_0, 0, 0}, /* BLE_NO_DELAY - Set on Entry / Reset on Exit */ {GPIOA, LL_GPIO_PIN_0, 0, 0}, /* BLE_STACK_STORE_NVM_CB - Set on Entry / Reset on Exit */ diff --git a/firmware/targets/f7/ble_glue/ble_app.c b/firmware/targets/f7/ble_glue/ble_app.c index 30be3c7ce..4fc4d521b 100644 --- a/firmware/targets/f7/ble_glue/ble_app.c +++ b/firmware/targets/f7/ble_glue/ble_app.c @@ -137,38 +137,33 @@ static int32_t ble_app_hci_thread(void* arg) { // Called by WPAN lib void hci_notify_asynch_evt(void* pdata) { UNUSED(pdata); - if(ble_app) { - FuriThreadId thread_id = furi_thread_get_id(ble_app->thread); - furi_assert(thread_id); - furi_thread_flags_set(thread_id, BLE_APP_FLAG_HCI_EVENT); - } + furi_check(ble_app); + FuriThreadId thread_id = furi_thread_get_id(ble_app->thread); + furi_assert(thread_id); + furi_thread_flags_set(thread_id, BLE_APP_FLAG_HCI_EVENT); } void hci_cmd_resp_release(uint32_t flag) { UNUSED(flag); - if(ble_app) { - furi_semaphore_release(ble_app->hci_sem); - } + furi_check(ble_app); + furi_check(furi_semaphore_release(ble_app->hci_sem) == FuriStatusOk); } void hci_cmd_resp_wait(uint32_t timeout) { - UNUSED(timeout); - if(ble_app) { - furi_semaphore_acquire(ble_app->hci_sem, FuriWaitForever); - } + furi_check(ble_app); + furi_check(furi_semaphore_acquire(ble_app->hci_sem, timeout) == FuriStatusOk); } static void ble_app_hci_event_handler(void* pPayload) { SVCCTL_UserEvtFlowStatus_t svctl_return_status; tHCI_UserEvtRxParam* pParam = (tHCI_UserEvtRxParam*)pPayload; - if(ble_app) { - svctl_return_status = SVCCTL_UserEvtRx((void*)&(pParam->pckt->evtserial)); - if(svctl_return_status != SVCCTL_UserEvtFlowDisable) { - pParam->status = HCI_TL_UserEventFlow_Enable; - } else { - pParam->status = HCI_TL_UserEventFlow_Disable; - } + furi_check(ble_app); + svctl_return_status = SVCCTL_UserEvtRx((void*)&(pParam->pckt->evtserial)); + if(svctl_return_status != SVCCTL_UserEvtFlowDisable) { + pParam->status = HCI_TL_UserEventFlow_Enable; + } else { + pParam->status = HCI_TL_UserEventFlow_Disable; } } diff --git a/firmware/targets/f7/ble_glue/ble_glue.c b/firmware/targets/f7/ble_glue/ble_glue.c index 83562c73e..c73bbd866 100644 --- a/firmware/targets/f7/ble_glue/ble_glue.c +++ b/firmware/targets/f7/ble_glue/ble_glue.c @@ -58,12 +58,6 @@ void ble_glue_init() { ble_glue = malloc(sizeof(BleGlue)); ble_glue->status = BleGlueStatusStartup; - // Configure the system Power Mode - // Select HSI as system clock source after Wake Up from Stop mode - LL_RCC_SetClkAfterWakeFromStop(LL_RCC_STOP_WAKEUPCLOCK_HSI); - /* Initialize the CPU2 reset value before starting CPU2 with C2BOOT */ - LL_C2_PWR_SetPowerMode(LL_PWR_MODE_SHUTDOWN); - #ifdef BLE_GLUE_DEBUG APPD_Init(); #endif @@ -409,7 +403,9 @@ void shci_cmd_resp_release(uint32_t flag) { void shci_cmd_resp_wait(uint32_t timeout) { UNUSED(timeout); if(ble_glue) { + furi_hal_power_insomnia_enter(); furi_semaphore_acquire(ble_glue->shci_sem, FuriWaitForever); + furi_hal_power_insomnia_exit(); } } diff --git a/firmware/targets/f7/ble_glue/dev_info_service.c b/firmware/targets/f7/ble_glue/dev_info_service.c index 8bdb2eea8..d24058632 100644 --- a/firmware/targets/f7/ble_glue/dev_info_service.c +++ b/firmware/targets/f7/ble_glue/dev_info_service.c @@ -33,7 +33,7 @@ void dev_info_svc_start() { dev_info_svc->version_string = furi_string_alloc_printf( "%s %s %s %s", version_get_githash(NULL), - version_get_gitbranch(NULL), + version_get_version(NULL), version_get_gitbranchnum(NULL), version_get_builddate(NULL)); snprintf( diff --git a/firmware/targets/f7/ble_glue/gap.c b/firmware/targets/f7/ble_glue/gap.c index ebf27b369..7ad9db3ae 100644 --- a/firmware/targets/f7/ble_glue/gap.c +++ b/firmware/targets/f7/ble_glue/gap.c @@ -121,6 +121,8 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { FURI_LOG_I( TAG, "Disconnect from client. Reason: %02X", disconnection_complete_event->Reason); } + // Enterprise sleep + furi_delay_us(666 + 666); if(gap->enable_adv) { // Restart advertising gap_advertise_start(GapStateAdvFast); @@ -312,8 +314,6 @@ static void gap_init_svc(Gap* gap) { tBleStatus status; uint32_t srd_bd_addr[2]; - // HCI Reset to synchronise BLE Stack - hci_reset(); // Configure mac address aci_hal_write_config_data( CONFIG_DATA_PUBADDR_OFFSET, CONFIG_DATA_PUBADDR_LEN, gap->config->mac_address); diff --git a/firmware/targets/f7/furi_hal/furi_hal.c b/firmware/targets/f7/furi_hal/furi_hal.c index c9b4c8088..1378b6955 100644 --- a/firmware/targets/f7/furi_hal/furi_hal.c +++ b/firmware/targets/f7/furi_hal/furi_hal.c @@ -6,6 +6,16 @@ #define TAG "FuriHal" +bool normal_boot = false; + +void furi_hal_set_is_normal_boot(bool value) { + normal_boot = value; +} + +bool furi_hal_is_normal_boot() { + return normal_boot; +} + void furi_hal_init_early() { furi_hal_cortex_init_early(); furi_hal_clock_init_early(); diff --git a/firmware/targets/f7/furi_hal/furi_hal_bt.c b/firmware/targets/f7/furi_hal/furi_hal_bt.c index ce01b8871..d60cfe755 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_bt.c +++ b/firmware/targets/f7/furi_hal/furi_hal_bt.c @@ -84,9 +84,7 @@ void furi_hal_bt_init() { } // Explicitly tell that we are in charge of CLK48 domain - if(!LL_HSEM_IsSemaphoreLocked(HSEM, CFG_HW_CLK48_CONFIG_SEMID)) { - furi_check(LL_HSEM_1StepLock(HSEM, CFG_HW_CLK48_CONFIG_SEMID) == 0); - } + furi_check(LL_HSEM_1StepLock(HSEM, CFG_HW_CLK48_CONFIG_SEMID) == 0); // Start Core2 ble_glue_init(); @@ -129,9 +127,7 @@ bool furi_hal_bt_start_radio_stack() { furi_mutex_acquire(furi_hal_bt_core2_mtx, FuriWaitForever); // Explicitly tell that we are in charge of CLK48 domain - if(!LL_HSEM_IsSemaphoreLocked(HSEM, CFG_HW_CLK48_CONFIG_SEMID)) { - furi_check(LL_HSEM_1StepLock(HSEM, CFG_HW_CLK48_CONFIG_SEMID) == 0); - } + furi_check(LL_HSEM_1StepLock(HSEM, CFG_HW_CLK48_CONFIG_SEMID) == 0); do { // Wait until C2 is started or timeout diff --git a/firmware/targets/f7/furi_hal/furi_hal_clock.c b/firmware/targets/f7/furi_hal/furi_hal_clock.c index cf19451ec..a76fbfbca 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_clock.c +++ b/firmware/targets/f7/furi_hal/furi_hal_clock.c @@ -63,6 +63,10 @@ void furi_hal_clock_init() { LL_RCC_HSI_Enable(); while(!HS_CLOCK_IS_READY()) ; + /* Select HSI as system clock source after Wake Up from Stop mode + * Must be set before enabling CSS */ + LL_RCC_SetClkAfterWakeFromStop(LL_RCC_STOP_WAKEUPCLOCK_HSI); + LL_RCC_HSE_EnableCSS(); /* LSE and LSI1 configuration and activation */ @@ -140,6 +144,7 @@ void furi_hal_clock_init() { LL_RCC_SetRNGClockSource(LL_RCC_RNG_CLKSOURCE_CLK48); LL_RCC_SetUSBClockSource(LL_RCC_USB_CLKSOURCE_PLLSAI1); 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_SetSMPSPrescaler(LL_RCC_SMPS_DIV_1); LL_RCC_SetRFWKPClockSource(LL_RCC_RFWKP_CLKSOURCE_LSE); @@ -203,25 +208,36 @@ void furi_hal_clock_switch_to_hsi() { while(!LL_RCC_HSI_IsReady()) ; - LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_HSI); LL_RCC_SetSMPSClockSource(LL_RCC_SMPS_CLKSOURCE_HSI); + LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_HSI); while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_HSI) ; - LL_FLASH_SetLatency(LL_FLASH_LATENCY_1); + LL_C2_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_1); + + LL_FLASH_SetLatency(LL_FLASH_LATENCY_0); + while(LL_FLASH_GetLatency() != LL_FLASH_LATENCY_0) + ; } void furi_hal_clock_switch_to_pll() { LL_RCC_HSE_Enable(); LL_RCC_PLL_Enable(); + LL_RCC_PLLSAI1_Enable(); while(!LL_RCC_HSE_IsReady()) ; while(!LL_RCC_PLL_IsReady()) ; + while(!LL_RCC_PLLSAI1_IsReady()) + ; + + LL_C2_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_2); LL_FLASH_SetLatency(LL_FLASH_LATENCY_3); + while(LL_FLASH_GetLatency() != LL_FLASH_LATENCY_3) + ; LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_PLL); LL_RCC_SetSMPSClockSource(LL_RCC_SMPS_CLKSOURCE_HSE); @@ -296,4 +312,4 @@ void furi_hal_clock_mco_disable() { LL_RCC_MSI_Disable(); while(LL_RCC_MSI_IsReady() != 0) ; -} \ No newline at end of file +} diff --git a/firmware/targets/f7/furi_hal/furi_hal_debug.c b/firmware/targets/f7/furi_hal/furi_hal_debug.c index 3b5dfe622..3dc03ea69 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_debug.c +++ b/firmware/targets/f7/furi_hal/furi_hal_debug.c @@ -3,12 +3,26 @@ #include #include +#include +#include + +volatile bool furi_hal_debug_gdb_session_active = false; + void furi_hal_debug_enable() { // Low power mode debug LL_DBGMCU_EnableDBGSleepMode(); LL_DBGMCU_EnableDBGStopMode(); LL_DBGMCU_EnableDBGStandbyMode(); LL_EXTI_EnableIT_32_63(LL_EXTI_LINE_48); + // SWD GPIO + furi_hal_gpio_init_ex( + &gpio_swdio, + GpioModeAltFunctionPushPull, + GpioPullUp, + GpioSpeedVeryHigh, + GpioAltFn0JTMS_SWDIO); + furi_hal_gpio_init_ex( + &gpio_swclk, GpioModeAltFunctionPushPull, GpioPullDown, GpioSpeedLow, GpioAltFn0JTCK_SWCLK); } void furi_hal_debug_disable() { @@ -17,4 +31,11 @@ void furi_hal_debug_disable() { LL_DBGMCU_DisableDBGStopMode(); LL_DBGMCU_DisableDBGStandbyMode(); LL_EXTI_DisableIT_32_63(LL_EXTI_LINE_48); + // SWD GPIO + furi_hal_gpio_init_simple(&gpio_swdio, GpioModeAnalog); + furi_hal_gpio_init_simple(&gpio_swclk, GpioModeAnalog); } + +bool furi_hal_debug_is_gdb_session_active() { + return furi_hal_debug_gdb_session_active; +} \ No newline at end of file diff --git a/firmware/targets/f7/furi_hal/furi_hal_flash.c b/firmware/targets/f7/furi_hal/furi_hal_flash.c index fc021d969..464d88d95 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_flash.c +++ b/firmware/targets/f7/furi_hal/furi_hal_flash.c @@ -1,5 +1,7 @@ #include #include +#include +#include #include #include #include @@ -25,6 +27,16 @@ #define FURI_HAL_FLASH_OPT_KEY2 0x4C5D6E7F #define FURI_HAL_FLASH_OB_TOTAL_WORDS (0x80 / (sizeof(uint32_t) * 2)) +/* lib/STM32CubeWB/Projects/P-NUCLEO-WB55.Nucleo/Applications/BLE/BLE_RfWithFlash/Core/Src/flash_driver.c + * ProcessSingleFlashOperation, quote: + > In most BLE application, the flash should not be blocked by the CPU2 longer than FLASH_TIMEOUT_VALUE (1000ms) + > However, it could be that for some marginal application, this time is longer. + > ... there is no other way than waiting the operation to be completed. + > If for any reason this test is never passed, this means there is a failure in the system and there is no other + > way to recover than applying a device reset. + */ +#define FURI_HAL_FLASH_C2_LOCK_TIMEOUT_MS 3000u /* 3 seconds */ + #define IS_ADDR_ALIGNED_64BITS(__VALUE__) (((__VALUE__)&0x7U) == (0x00UL)) #define IS_FLASH_PROGRAM_ADDRESS(__VALUE__) \ (((__VALUE__) >= FLASH_BASE) && ((__VALUE__) <= (FLASH_BASE + FLASH_SIZE - 8UL)) && \ @@ -114,6 +126,7 @@ static void furi_hal_flash_lock(void) { } static void furi_hal_flash_begin_with_core2(bool erase_flag) { + furi_hal_power_insomnia_enter(); /* Take flash controller ownership */ while(LL_HSEM_1StepLock(HSEM, CFG_HW_FLASH_SEMID) != 0) { furi_thread_yield(); @@ -129,9 +142,11 @@ static void furi_hal_flash_begin_with_core2(bool erase_flag) { for(volatile uint32_t i = 0; i < 35; i++) ; + FuriHalCortexTimer timer = furi_hal_cortex_timer_get(FURI_HAL_FLASH_C2_LOCK_TIMEOUT_MS * 1000); while(true) { /* Wait till flash controller become usable */ while(LL_FLASH_IsActiveFlag_OperationSuspended()) { + furi_check(!furi_hal_cortex_timer_is_expired(timer)); furi_thread_yield(); }; @@ -141,6 +156,7 @@ static void furi_hal_flash_begin_with_core2(bool erase_flag) { /* Actually we already have mutex for it, but specification is specification */ if(LL_HSEM_IsSemaphoreLocked(HSEM, CFG_HW_BLOCK_FLASH_REQ_BY_CPU1_SEMID)) { taskEXIT_CRITICAL(); + furi_check(!furi_hal_cortex_timer_is_expired(timer)); furi_thread_yield(); continue; } @@ -148,6 +164,7 @@ static void furi_hal_flash_begin_with_core2(bool erase_flag) { /* Take sempahopre and prevent core2 from anything funky */ if(LL_HSEM_1StepLock(HSEM, CFG_HW_BLOCK_FLASH_REQ_BY_CPU2_SEMID) != 0) { taskEXIT_CRITICAL(); + furi_check(!furi_hal_cortex_timer_is_expired(timer)); furi_thread_yield(); continue; } @@ -188,6 +205,7 @@ static void furi_hal_flash_end_with_core2(bool erase_flag) { /* Release flash controller ownership */ LL_HSEM_ReleaseLock(HSEM, CFG_HW_FLASH_SEMID, 0); + furi_hal_power_insomnia_exit(); } static void furi_hal_flash_end(bool erase_flag) { @@ -228,17 +246,13 @@ static void furi_hal_flush_cache(void) { bool furi_hal_flash_wait_last_operation(uint32_t timeout) { uint32_t error = 0; - uint32_t countdown = 0; /* Wait for the FLASH operation to complete by polling on BUSY flag to be reset. Even if the FLASH operation fails, the BUSY flag will be reset and an error flag will be set */ - countdown = timeout; + FuriHalCortexTimer timer = furi_hal_cortex_timer_get(timeout * 1000); while(READ_BIT(FLASH->SR, FLASH_SR_BSY)) { - if(LL_SYSTICK_IsActiveCounterFlag()) { - countdown--; - } - if(countdown == 0) { + if(furi_hal_cortex_timer_is_expired(timer)) { return false; } } @@ -261,12 +275,9 @@ bool furi_hal_flash_wait_last_operation(uint32_t timeout) { CLEAR_BIT(FLASH->SR, error); /* Wait for control register to be written */ - countdown = timeout; + timer = furi_hal_cortex_timer_get(timeout * 1000); while(READ_BIT(FLASH->SR, FLASH_SR_CFGBSY)) { - if(LL_SYSTICK_IsActiveCounterFlag()) { - countdown--; - } - if(countdown == 0) { + if(furi_hal_cortex_timer_is_expired(timer)) { return false; } } diff --git a/firmware/targets/f7/furi_hal/furi_hal_gpio.c b/firmware/targets/f7/furi_hal/furi_hal_gpio.c index e8318afcf..c0e409775 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_gpio.c +++ b/firmware/targets/f7/furi_hal/furi_hal_gpio.c @@ -178,10 +178,13 @@ void furi_hal_gpio_add_int_callback(const GpioPin* gpio, GpioExtiCallback cb, vo FURI_CRITICAL_ENTER(); uint8_t pin_num = furi_hal_gpio_get_pin_num(gpio); - furi_assert(gpio_interrupt[pin_num].callback == NULL); - gpio_interrupt[pin_num].callback = cb; - gpio_interrupt[pin_num].context = ctx; - gpio_interrupt[pin_num].ready = true; + volatile GpioInterrupt* interrupt = &gpio_interrupt[pin_num]; + furi_check( + (interrupt->callback == NULL) || + ((interrupt->callback == cb) && (interrupt->context == ctx))); + interrupt->callback = cb; + interrupt->context = ctx; + interrupt->ready = true; FURI_CRITICAL_EXIT(); } diff --git a/firmware/targets/f7/furi_hal/furi_hal_infrared.c b/firmware/targets/f7/furi_hal/furi_hal_infrared.c index b65ea42e1..8098f8bd0 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_infrared.c +++ b/firmware/targets/f7/furi_hal/furi_hal_infrared.c @@ -570,9 +570,9 @@ static void furi_hal_infrared_async_tx_free_resources(void) { (furi_hal_infrared_state == InfraredStateAsyncTxStopped)); if(infrared_external_output) { - furi_hal_gpio_init(&gpio_ext_pa7, GpioModeOutputOpenDrain, GpioPullDown, GpioSpeedLow); + furi_hal_gpio_init(&gpio_ext_pa7, GpioModeAnalog, GpioPullDown, GpioSpeedLow); } else { - furi_hal_gpio_init(&gpio_infrared_tx, GpioModeOutputOpenDrain, GpioPullDown, GpioSpeedLow); + furi_hal_gpio_init(&gpio_infrared_tx, GpioModeAnalog, GpioPullDown, GpioSpeedLow); } furi_hal_interrupt_set_isr(IR_DMA_CH1_IRQ, NULL, NULL); furi_hal_interrupt_set_isr(IR_DMA_CH2_IRQ, NULL, NULL); diff --git a/firmware/targets/f7/furi_hal/furi_hal_light.c b/firmware/targets/f7/furi_hal/furi_hal_light.c index 7d303cfaa..5ce624d1e 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_light.c +++ b/firmware/targets/f7/furi_hal/furi_hal_light.c @@ -3,7 +3,7 @@ #include #include #include -#include +#include #include #define LED_CURRENT_RED 50 diff --git a/firmware/targets/f7/furi_hal/furi_hal_os.c b/firmware/targets/f7/furi_hal/furi_hal_os.c index ee9743e62..3fc1fbea8 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_os.c +++ b/firmware/targets/f7/furi_hal/furi_hal_os.c @@ -28,11 +28,24 @@ // Arbitrary (but small) number for better tick consistency #define FURI_HAL_OS_EXTRA_CNT 3 +#ifndef FURI_HAL_OS_DEBUG_AWAKE_GPIO +#define FURI_HAL_OS_DEBUG_AWAKE_GPIO (&gpio_ext_pa7) +#endif + +#ifndef FURI_HAL_OS_DEBUG_TICK_GPIO +#define FURI_HAL_OS_DEBUG_TICK_GPIO (&gpio_ext_pa6) +#endif + +#ifndef FURI_HAL_OS_DEBUG_SECOND_GPIO +#define FURI_HAL_OS_DEBUG_SECOND_GPIO (&gpio_ext_pa4) +#endif + #ifdef FURI_HAL_OS_DEBUG #include void furi_hal_os_timer_callback() { - furi_hal_gpio_write(&gpio_ext_pa4, !furi_hal_gpio_read(&gpio_ext_pa4)); + furi_hal_gpio_write( + FURI_HAL_OS_DEBUG_SECOND_GPIO, !furi_hal_gpio_read(FURI_HAL_OS_DEBUG_SECOND_GPIO)); } #endif @@ -44,9 +57,11 @@ void furi_hal_os_init() { furi_hal_idle_timer_init(); #ifdef FURI_HAL_OS_DEBUG - furi_hal_gpio_init_simple(&gpio_ext_pa7, GpioModeOutputPushPull); - furi_hal_gpio_init_simple(&gpio_ext_pa6, GpioModeOutputPushPull); - furi_hal_gpio_init_simple(&gpio_ext_pa4, GpioModeOutputPushPull); + furi_hal_gpio_init_simple(FURI_HAL_OS_DEBUG_AWAKE_GPIO, GpioModeOutputPushPull); + furi_hal_gpio_init_simple(FURI_HAL_OS_DEBUG_TICK_GPIO, GpioModeOutputPushPull); + furi_hal_gpio_init_simple(FURI_HAL_OS_DEBUG_SECOND_GPIO, GpioModeOutputPushPull); + furi_hal_gpio_write(FURI_HAL_OS_DEBUG_AWAKE_GPIO, 1); + FuriTimer* second_timer = furi_timer_alloc(furi_hal_os_timer_callback, FuriTimerTypePeriodic, NULL); furi_timer_start(second_timer, FURI_HAL_OS_TICK_HZ); @@ -58,7 +73,8 @@ void furi_hal_os_init() { void furi_hal_os_tick() { if(xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) { #ifdef FURI_HAL_OS_DEBUG - furi_hal_gpio_write(&gpio_ext_pa6, !furi_hal_gpio_read(&gpio_ext_pa6)); + furi_hal_gpio_write( + FURI_HAL_OS_DEBUG_TICK_GPIO, !furi_hal_gpio_read(FURI_HAL_OS_DEBUG_TICK_GPIO)); #endif xPortSysTickHandler(); } @@ -121,14 +137,14 @@ static inline uint32_t furi_hal_os_sleep(TickType_t expected_idle_ticks) { furi_hal_idle_timer_start(FURI_HAL_OS_TICKS_TO_IDLE_CNT(expected_idle_ticks)); #ifdef FURI_HAL_OS_DEBUG - furi_hal_gpio_write(&gpio_ext_pa7, 0); + furi_hal_gpio_write(FURI_HAL_OS_DEBUG_AWAKE_GPIO, 0); #endif // Go to sleep mode furi_hal_power_sleep(); #ifdef FURI_HAL_OS_DEBUG - furi_hal_gpio_write(&gpio_ext_pa7, 1); + furi_hal_gpio_write(FURI_HAL_OS_DEBUG_AWAKE_GPIO, 1); #endif // Calculate how much time we spent in the sleep diff --git a/firmware/targets/f7/furi_hal/furi_hal_power.c b/firmware/targets/f7/furi_hal/furi_hal_power.c index fd601ec7e..e380de7fa 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_power.c +++ b/firmware/targets/f7/furi_hal/furi_hal_power.c @@ -4,6 +4,8 @@ #include #include #include +#include +#include #include #include @@ -19,15 +21,20 @@ #define TAG "FuriHalPower" -#ifdef FURI_HAL_POWER_DEEP_SLEEP_ENABLED -#define FURI_HAL_POWER_DEEP_INSOMNIA 0 -#else -#define FURI_HAL_POWER_DEEP_INSOMNIA 1 +#ifndef FURI_HAL_POWER_DEBUG_WFI_GPIO +#define FURI_HAL_POWER_DEBUG_WFI_GPIO (&gpio_ext_pb2) +#endif + +#ifndef FURI_HAL_POWER_DEBUG_STOP_GPIO +#define FURI_HAL_POWER_DEBUG_STOP_GPIO (&gpio_ext_pc3) +#endif + +#ifndef FURI_HAL_POWER_STOP_MODE +#define FURI_HAL_POWER_STOP_MODE (LL_PWR_MODE_STOP2) #endif typedef struct { volatile uint8_t insomnia; - volatile uint8_t deep_insomnia; volatile uint8_t suppress_charge; uint8_t gauge_initialized; @@ -36,7 +43,6 @@ typedef struct { static volatile FuriHalPower furi_hal_power = { .insomnia = 0, - .deep_insomnia = FURI_HAL_POWER_DEEP_INSOMNIA, .suppress_charge = 0, }; @@ -79,19 +85,24 @@ const ParamCEDV cedv = { }; void furi_hal_power_init() { +#ifdef FURI_HAL_POWER_DEBUG + furi_hal_gpio_init_simple(FURI_HAL_POWER_DEBUG_WFI_GPIO, GpioModeOutputPushPull); + furi_hal_gpio_init_simple(FURI_HAL_POWER_DEBUG_STOP_GPIO, GpioModeOutputPushPull); + furi_hal_gpio_write(FURI_HAL_POWER_DEBUG_WFI_GPIO, 0); + furi_hal_gpio_write(FURI_HAL_POWER_DEBUG_STOP_GPIO, 0); +#endif + LL_PWR_SetRegulVoltageScaling(LL_PWR_REGU_VOLTAGE_SCALE1); LL_PWR_SMPS_SetMode(LL_PWR_SMPS_STEP_DOWN); + LL_PWR_SetPowerMode(FURI_HAL_POWER_STOP_MODE); + LL_C2_PWR_SetPowerMode(FURI_HAL_POWER_STOP_MODE); + furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); bq27220_init(&furi_hal_i2c_handle_power, &cedv); bq25896_init(&furi_hal_i2c_handle_power); furi_hal_i2c_release(&furi_hal_i2c_handle_power); -#ifdef FURI_HAL_OS_DEBUG - furi_hal_gpio_init_simple(&gpio_ext_pb2, GpioModeOutputPushPull); - furi_hal_gpio_init_simple(&gpio_ext_pc3, GpioModeOutputPushPull); -#endif - FURI_LOG_I(TAG, "Init OK"); } @@ -140,11 +151,12 @@ bool furi_hal_power_sleep_available() { return furi_hal_power.insomnia == 0; } -bool furi_hal_power_deep_sleep_available() { - return furi_hal_bt_is_alive() && furi_hal_power.deep_insomnia == 0; +static inline bool furi_hal_power_deep_sleep_available() { + return furi_hal_bt_is_alive() && !furi_hal_rtc_is_flag_set(FuriHalRtcFlagLegacySleep) && + !furi_hal_debug_is_gdb_session_active(); } -void furi_hal_power_light_sleep() { +static inline void furi_hal_power_light_sleep() { __WFI(); } @@ -152,17 +164,15 @@ static inline void furi_hal_power_suspend_aux_periphs() { // Disable USART furi_hal_uart_suspend(FuriHalUartIdUSART1); furi_hal_uart_suspend(FuriHalUartIdLPUART1); - // TODO: Disable USB } static inline void furi_hal_power_resume_aux_periphs() { // Re-enable USART furi_hal_uart_resume(FuriHalUartIdUSART1); furi_hal_uart_resume(FuriHalUartIdLPUART1); - // TODO: Re-enable USB } -void furi_hal_power_deep_sleep() { +static inline void furi_hal_power_deep_sleep() { furi_hal_power_suspend_aux_periphs(); while(LL_HSEM_1StepLock(HSEM, CFG_HW_RCC_SEMID)) @@ -187,8 +197,6 @@ void furi_hal_power_deep_sleep() { LL_HSEM_ReleaseLock(HSEM, CFG_HW_RCC_SEMID, 0); // Prepare deep sleep - LL_PWR_SetPowerMode(LL_PWR_MODE_STOP2); - LL_C2_PWR_SetPowerMode(LL_PWR_MODE_STOP2); LL_LPM_EnableDeepSleep(); #if defined(__CC_ARM) @@ -200,13 +208,6 @@ void furi_hal_power_deep_sleep() { LL_LPM_EnableSleep(); - // Make sure that values differ to prevent disaster on wfi - LL_PWR_SetPowerMode(LL_PWR_MODE_STOP0); - LL_C2_PWR_SetPowerMode(LL_PWR_MODE_SHUTDOWN); - - LL_PWR_ClearFlag_C1STOP_C1STB(); - LL_PWR_ClearFlag_C2STOP_C2STB(); - /* Release ENTRY_STOP_MODE semaphore */ LL_HSEM_ReleaseLock(HSEM, CFG_HW_ENTRY_STOP_MODE_SEMID, 0); @@ -220,28 +221,25 @@ void furi_hal_power_deep_sleep() { LL_HSEM_ReleaseLock(HSEM, CFG_HW_RCC_SEMID, 0); furi_hal_power_resume_aux_periphs(); + furi_hal_rtc_sync_shadow(); } void furi_hal_power_sleep() { if(furi_hal_power_deep_sleep_available()) { -#ifdef FURI_HAL_OS_DEBUG - furi_hal_gpio_write(&gpio_ext_pc3, 1); +#ifdef FURI_HAL_POWER_DEBUG + furi_hal_gpio_write(FURI_HAL_POWER_DEBUG_STOP_GPIO, 1); #endif - furi_hal_power_deep_sleep(); - -#ifdef FURI_HAL_OS_DEBUG - furi_hal_gpio_write(&gpio_ext_pc3, 0); +#ifdef FURI_HAL_POWER_DEBUG + furi_hal_gpio_write(FURI_HAL_POWER_DEBUG_STOP_GPIO, 0); #endif } else { -#ifdef FURI_HAL_OS_DEBUG - furi_hal_gpio_write(&gpio_ext_pb2, 1); +#ifdef FURI_HAL_POWER_DEBUG + furi_hal_gpio_write(FURI_HAL_POWER_DEBUG_WFI_GPIO, 1); #endif - furi_hal_power_light_sleep(); - -#ifdef FURI_HAL_OS_DEBUG - furi_hal_gpio_write(&gpio_ext_pb2, 0); +#ifdef FURI_HAL_POWER_DEBUG + furi_hal_gpio_write(FURI_HAL_POWER_DEBUG_WFI_GPIO, 0); #endif } } diff --git a/firmware/targets/f7/furi_hal/furi_hal_random.c b/firmware/targets/f7/furi_hal/furi_hal_random.c index f36407cc1..d3461c4d1 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_random.c +++ b/firmware/targets/f7/furi_hal/furi_hal_random.c @@ -9,19 +9,35 @@ #define TAG "FuriHalRandom" +static uint32_t furi_hal_random_read_rng() { + while(LL_RNG_IsActiveFlag_CECS(RNG) && LL_RNG_IsActiveFlag_SECS(RNG) && + !LL_RNG_IsActiveFlag_DRDY(RNG)) { + /* Error handling as described in RM0434, pg. 582-583 */ + if(LL_RNG_IsActiveFlag_CECS(RNG)) { + /* Clock error occurred */ + LL_RNG_ClearFlag_CEIS(RNG); + } + + if(LL_RNG_IsActiveFlag_SECS(RNG)) { + /* Noise source error occurred */ + LL_RNG_ClearFlag_SEIS(RNG); + + for(uint32_t i = 0; i < 12; ++i) { + const volatile uint32_t discard = LL_RNG_ReadRandData32(RNG); + UNUSED(discard); + } + } + } + + return LL_RNG_ReadRandData32(RNG); +} + uint32_t furi_hal_random_get() { while(LL_HSEM_1StepLock(HSEM, CFG_HW_RNG_SEMID)) ; LL_RNG_Enable(RNG); - while(!LL_RNG_IsActiveFlag_DRDY(RNG)) - ; - - if((LL_RNG_IsActiveFlag_CECS(RNG)) || (LL_RNG_IsActiveFlag_SECS(RNG))) { - furi_crash("TRNG error"); - } - - uint32_t random_val = LL_RNG_ReadRandData32(RNG); + const uint32_t random_val = furi_hal_random_read_rng(); LL_RNG_Disable(RNG); LL_HSEM_ReleaseLock(HSEM, CFG_HW_RNG_SEMID, 0); @@ -35,15 +51,7 @@ void furi_hal_random_fill_buf(uint8_t* buf, uint32_t len) { LL_RNG_Enable(RNG); for(uint32_t i = 0; i < len; i += 4) { - while(!LL_RNG_IsActiveFlag_DRDY(RNG)) - ; - - if((LL_RNG_IsActiveFlag_CECS(RNG)) || (LL_RNG_IsActiveFlag_SECS(RNG))) { - furi_crash("TRNG error"); - } - - uint32_t random_val = LL_RNG_ReadRandData32(RNG); - + const uint32_t random_val = furi_hal_random_read_rng(); uint8_t len_cur = ((i + 4) < len) ? (4) : (len - i); memcpy(&buf[i], &random_val, len_cur); } diff --git a/firmware/targets/f7/furi_hal/furi_hal_resources.c b/firmware/targets/f7/furi_hal/furi_hal_resources.c index a6c72fd5a..f87f2a31a 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_resources.c +++ b/firmware/targets/f7/furi_hal/furi_hal_resources.c @@ -6,6 +6,9 @@ #define TAG "FuriHalResources" +const GpioPin gpio_swdio = {.port = GPIOA, .pin = LL_GPIO_PIN_13}; +const GpioPin gpio_swclk = {.port = GPIOA, .pin = LL_GPIO_PIN_14}; + const GpioPin gpio_vibro = {.port = VIBRO_GPIO_Port, .pin = VIBRO_Pin}; const GpioPin gpio_ibutton = {.port = iBTN_GPIO_Port, .pin = iBTN_Pin}; @@ -158,10 +161,11 @@ void furi_hal_resources_init() { // Button pins furi_hal_resources_init_input_pins(GpioModeInterruptRiseFall); - // Explicit pulls pins - furi_hal_gpio_init(&gpio_infrared_tx, GpioModeAnalog, GpioPullDown, GpioSpeedLow); - furi_hal_gpio_init(&gpio_speaker, GpioModeAnalog, GpioPullDown, GpioSpeedLow); - furi_hal_gpio_init(&gpio_vibro, GpioModeAnalog, GpioPullDown, GpioSpeedLow); + // Explicit, surviving reset, pulls + LL_PWR_EnablePUPDCfg(); + LL_PWR_EnableGPIOPullDown(LL_PWR_GPIO_B, LL_PWR_GPIO_BIT_9); // gpio_infrared_tx + LL_PWR_EnableGPIOPullDown(LL_PWR_GPIO_B, LL_PWR_GPIO_BIT_8); // gpio_speaker + LL_PWR_EnableGPIOPullDown(LL_PWR_GPIO_A, LL_PWR_GPIO_BIT_8); // gpio_vibro // Display pins furi_hal_gpio_init(&gpio_display_rst_n, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); diff --git a/firmware/targets/f7/furi_hal/furi_hal_resources.h b/firmware/targets/f7/furi_hal/furi_hal_resources.h index c0e32c313..391f8f4ff 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_resources.h +++ b/firmware/targets/f7/furi_hal/furi_hal_resources.h @@ -50,6 +50,9 @@ extern const size_t input_pins_count; extern const GpioPinRecord gpio_pins[]; extern const size_t gpio_pins_count; +extern const GpioPin gpio_swdio; +extern const GpioPin gpio_swclk; + extern const GpioPin gpio_vibro; extern const GpioPin gpio_ibutton; diff --git a/firmware/targets/f7/furi_hal/furi_hal_rtc.c b/firmware/targets/f7/furi_hal/furi_hal_rtc.c index 84e7fe395..7bd45c35d 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_rtc.c +++ b/firmware/targets/f7/furi_hal/furi_hal_rtc.c @@ -165,6 +165,14 @@ void furi_hal_rtc_init() { FURI_LOG_I(TAG, "Init OK"); } +void furi_hal_rtc_sync_shadow() { + if(!LL_RTC_IsShadowRegBypassEnabled(RTC)) { + LL_RTC_ClearFlag_RS(RTC); + while(!LL_RTC_IsActiveFlag_RS(RTC)) { + }; + } +} + uint32_t furi_hal_rtc_get_register(FuriHalRtcRegister reg) { return LL_RTC_BAK_GetRegister(RTC, reg); } @@ -312,12 +320,7 @@ void furi_hal_rtc_set_datetime(FuriHalRtcDateTime* datetime) { /* Exit Initialization mode */ LL_RTC_DisableInitMode(RTC); - /* If RTC_CR_BYPSHAD bit = 0, wait for synchro else this check is not needed */ - if(!LL_RTC_IsShadowRegBypassEnabled(RTC)) { - LL_RTC_ClearFlag_RS(RTC); - while(!LL_RTC_IsActiveFlag_RS(RTC)) { - }; - } + furi_hal_rtc_sync_shadow(); /* Enable write protection */ LL_RTC_EnableWriteProtection(RTC); diff --git a/firmware/targets/f7/furi_hal/furi_hal_subghz.c b/firmware/targets/f7/furi_hal/furi_hal_subghz.c index ce4b8f0db..573c5b8ab 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_subghz.c +++ b/firmware/targets/f7/furi_hal/furi_hal_subghz.c @@ -40,13 +40,22 @@ volatile FuriHalSubGhz furi_hal_subghz = { .cc1101_g0_pin = &gpio_cc1101_g0, .rolling_counter_mult = 1, .ext_module_power_disabled = false, + .timestamp_file_names = false, }; -bool furi_hal_subghz_set_radio_type(SubGhzRadioType state) { +void furi_hal_subghz_select_radio_type(SubGhzRadioType state) { furi_hal_subghz.radio_type = state; +} + +bool furi_hal_subghz_init_radio_type(SubGhzRadioType state) { + if(state == SubGhzRadioInternal && furi_hal_subghz.cc1101_g0_pin == &gpio_cc1101_g0) { + return true; + } else if(state == SubGhzRadioExternal && furi_hal_subghz.cc1101_g0_pin == &gpio_cc1101_g0_ext) { + return true; + } furi_hal_spi_bus_handle_deinit(furi_hal_subghz.spi_bus_handle); - if(furi_hal_subghz.radio_type == SubGhzRadioInternal) { + if(state == SubGhzRadioInternal) { furi_hal_subghz.spi_bus_handle = &furi_hal_spi_bus_handle_subghz; furi_hal_subghz.cc1101_g0_pin = &gpio_cc1101_g0; } else { @@ -79,6 +88,14 @@ 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_async_mirror_pin(const GpioPin* pin) { furi_hal_subghz.async_mirror_pin = pin; } @@ -111,6 +128,8 @@ void furi_hal_subghz_disable_ext_power(void) { bool furi_hal_subghz_check_radio(void) { bool result = true; + furi_hal_subghz_init_radio_type(furi_hal_subghz.radio_type); + furi_hal_spi_acquire(furi_hal_subghz.spi_bus_handle); uint8_t ver = cc1101_get_version(furi_hal_subghz.spi_bus_handle); @@ -702,11 +721,12 @@ void furi_hal_subghz_start_async_rx(FuriHalSubGhzCaptureCallback callback, void* GpioModeInterruptRiseFall, GpioPullUp, GpioSpeedVeryHigh); + furi_hal_gpio_disable_int_callback(furi_hal_subghz.cc1101_g0_pin); + furi_hal_gpio_remove_int_callback(furi_hal_subghz.cc1101_g0_pin); furi_hal_gpio_add_int_callback( furi_hal_subghz.cc1101_g0_pin, furi_hal_subghz_capture_ext_ISR, furi_hal_subghz_capture_callback); - furi_hal_gpio_enable_int_callback(furi_hal_subghz.cc1101_g0_pin); } // Start timer diff --git a/firmware/targets/f7/furi_hal/furi_hal_subghz.h b/firmware/targets/f7/furi_hal/furi_hal_subghz.h index 1a42d84ff..a292c642f 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_subghz.h +++ b/firmware/targets/f7/furi_hal/furi_hal_subghz.h @@ -79,6 +79,7 @@ typedef struct { const GpioPin* cc1101_g0_pin; uint8_t rolling_counter_mult; bool ext_module_power_disabled; + bool timestamp_file_names; } FuriHalSubGhz; extern volatile FuriHalSubGhz furi_hal_subghz; @@ -305,7 +306,7 @@ void furi_hal_subghz_stop_async_tx(); * @param state SubGhzRadioInternal or SubGhzRadioExternal * @return true if switching is successful */ -bool furi_hal_subghz_set_radio_type(SubGhzRadioType state); +bool furi_hal_subghz_init_radio_type(SubGhzRadioType state); /** Get current radio * @return SubGhzRadioInternal or SubGhzRadioExternal @@ -344,6 +345,18 @@ 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); + #ifdef __cplusplus } #endif diff --git a/firmware/targets/f7/src/main.c b/firmware/targets/f7/src/main.c index 1f2b5d6e4..2b1fef902 100644 --- a/firmware/targets/f7/src/main.c +++ b/firmware/targets/f7/src/main.c @@ -26,9 +26,12 @@ int main() { // Flipper critical FURI HAL furi_hal_init_early(); + furi_hal_set_is_normal_boot(false); FuriThread* main_thread = furi_thread_alloc_ex("Init", 4096, init_task, NULL); #ifdef FURI_RAM_EXEC + // Prevent entering sleep mode when executed from RAM + furi_hal_power_insomnia_enter(); furi_thread_start(main_thread); #else furi_hal_light_sequence("RGB"); @@ -44,6 +47,7 @@ int main() { furi_hal_power_reset(); } else if(boot_mode == FuriHalRtcBootModeUpdate) { furi_hal_light_sequence("rgb BR"); + // Do update flipper_boot_update_exec(); // if things go nice, we shouldn't reach this point. // But if we do, abandon to avoid bootloops @@ -55,6 +59,7 @@ int main() { furi_hal_power_reset(); } else { furi_hal_light_sequence("rgb G"); + furi_hal_set_is_normal_boot(true); furi_thread_start(main_thread); } #endif diff --git a/firmware/targets/f7/src/recovery.c b/firmware/targets/f7/src/recovery.c index 49d780d47..f1bfe5383 100644 --- a/firmware/targets/f7/src/recovery.c +++ b/firmware/targets/f7/src/recovery.c @@ -56,7 +56,7 @@ void flipper_boot_recovery_exec() { } if(!counter) { - furi_hal_rtc_set_flag(FuriHalRtcFlagFactoryReset); + furi_hal_rtc_set_flag(FuriHalRtcFlagResetPin); furi_hal_rtc_set_pin_fails(0); furi_hal_rtc_reset_flag(FuriHalRtcFlagLock); } diff --git a/firmware/targets/f7/target.json b/firmware/targets/f7/target.json index 906ee23ad..0d2367b3a 100644 --- a/firmware/targets/f7/target.json +++ b/firmware/targets/f7/target.json @@ -28,6 +28,7 @@ "flipperformat", "toolbox", "nfc", + "pulse_reader", "microtar", "usb_stm32", "st25rfal002", @@ -40,7 +41,6 @@ "mbedtls", "lfrfid", "flipper_application", - "pulse_reader", "flipperformat", "toolbox" ] diff --git a/firmware/targets/furi_hal_include/furi_hal.h b/firmware/targets/furi_hal_include/furi_hal.h index 2eb4688d4..3b1e8dc2d 100644 --- a/firmware/targets/furi_hal_include/furi_hal.h +++ b/firmware/targets/furi_hal_include/furi_hal.h @@ -42,6 +42,12 @@ struct STOP_EXTERNING_ME {}; extern "C" { #endif +/** Set whether booting normally with all subsystems */ +void furi_hal_set_is_normal_boot(bool value); + +/** True if booting normally with all subsystems */ +bool furi_hal_is_normal_boot(); + /** Early FuriHal init, only essential subsystems */ void furi_hal_init_early(); diff --git a/firmware/targets/furi_hal_include/furi_hal_debug.h b/firmware/targets/furi_hal_include/furi_hal_debug.h index 88397bbba..befbb4f40 100644 --- a/firmware/targets/furi_hal_include/furi_hal_debug.h +++ b/firmware/targets/furi_hal_include/furi_hal_debug.h @@ -18,6 +18,9 @@ void furi_hal_debug_enable(); /** Disable MCU debug */ void furi_hal_debug_disable(); +/** Check if GDB debug session is active */ +bool furi_hal_debug_is_gdb_session_active(); + #ifdef __cplusplus } #endif diff --git a/firmware/targets/furi_hal_include/furi_hal_power.h b/firmware/targets/furi_hal_include/furi_hal_power.h index 462e20e41..00182fa28 100644 --- a/firmware/targets/furi_hal_include/furi_hal_power.h +++ b/firmware/targets/furi_hal_include/furi_hal_power.h @@ -58,12 +58,6 @@ void furi_hal_power_insomnia_exit(); */ bool furi_hal_power_sleep_available(); -/** Check if deep sleep availble - * - * @return true if available - */ -bool furi_hal_power_deep_sleep_available(); - /** Go to sleep */ void furi_hal_power_sleep(); diff --git a/firmware/targets/furi_hal_include/furi_hal_rtc.h b/firmware/targets/furi_hal_include/furi_hal_rtc.h index b16b04a68..5babfdc59 100644 --- a/firmware/targets/furi_hal_include/furi_hal_rtc.h +++ b/firmware/targets/furi_hal_include/furi_hal_rtc.h @@ -30,6 +30,9 @@ typedef enum { FuriHalRtcFlagLock = (1 << 2), FuriHalRtcFlagC2Update = (1 << 3), FuriHalRtcFlagHandOrient = (1 << 4), + FuriHalRtcFlagResetPin = (1 << 5), + FuriHalRtcFlagLegacySleep = (1 << 6), + FuriHalRtcFlagStealthMode = (1 << 7), } FuriHalRtcFlag; typedef enum { @@ -85,6 +88,9 @@ void furi_hal_rtc_deinit_early(); /** Initialize RTC subsystem */ void furi_hal_rtc_init(); +/** Force sync shadow registers */ +void furi_hal_rtc_sync_shadow(); + /** Get RTC register content * * @param[in] reg The register identifier diff --git a/furi/core/check.c b/furi/core/check.c index 910527cee..f5390639d 100644 --- a/furi/core/check.c +++ b/furi/core/check.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -13,7 +14,7 @@ #include PLACE_IN_SECTION("MB_MEM2") const char* __furi_check_message = NULL; -PLACE_IN_SECTION("MB_MEM2") uint32_t __furi_check_registers[12] = {0}; +PLACE_IN_SECTION("MB_MEM2") uint32_t __furi_check_registers[13] = {0}; /** Load r12 value to __furi_check_message and store registers to __furi_check_registers */ #define GET_MESSAGE_AND_STORE_REGISTERS() \ @@ -21,6 +22,7 @@ PLACE_IN_SECTION("MB_MEM2") uint32_t __furi_check_registers[12] = {0}; "str r12, [r11] \n" \ "ldr r12, =__furi_check_registers \n" \ "stm r12, {r0-r11} \n" \ + "str lr, [r12, #48] \n" \ : \ : \ : "memory"); @@ -61,6 +63,25 @@ static void __furi_put_uint32_as_text(uint32_t data) { furi_hal_console_puts(tmp_str); } +static void __furi_put_uint32_as_hex(uint32_t data) { + char tmp_str[] = "0xFFFFFFFF"; + itoa(data, tmp_str, 16); + furi_hal_console_puts(tmp_str); +} + +static void __furi_print_register_info() { + // Print registers + for(uint8_t i = 0; i < 12; i++) { + furi_hal_console_puts("\r\n\tr"); + __furi_put_uint32_as_text(i); + furi_hal_console_puts(" : "); + __furi_put_uint32_as_hex(__furi_check_registers[i]); + } + + furi_hal_console_puts("\r\n\tlr : "); + __furi_put_uint32_as_hex(__furi_check_registers[12]); +} + static void __furi_print_stack_info() { furi_hal_console_puts("\r\n\tstack watermark: "); __furi_put_uint32_as_text(uxTaskGetStackHighWaterMark(NULL) * 4); @@ -100,30 +121,41 @@ FURI_NORETURN void __furi_crash() { if(__furi_check_message == NULL) { __furi_check_message = "Fatal Error"; + } else if(__furi_check_message == (void*)__FURI_ASSERT_MESSAGE_FLAG) { + __furi_check_message = "furi_assert failed"; + } else if(__furi_check_message == (void*)__FURI_CHECK_MESSAGE_FLAG) { + __furi_check_message = "furi_check failed"; } furi_hal_console_puts("\r\n\033[0;31m[CRASH]"); __furi_print_name(isr); furi_hal_console_puts(__furi_check_message); + __furi_print_register_info(); if(!isr) { __furi_print_stack_info(); } __furi_print_heap_info(); +#ifndef FURI_DEBUG // Check if debug enabled by DAP // https://developer.arm.com/documentation/ddi0403/d/Debug-Architecture/ARMv7-M-Debug/Debug-register-support-in-the-SCS/Debug-Halting-Control-and-Status-Register--DHCSR?lang=en bool debug = CoreDebug->DHCSR & CoreDebug_DHCSR_C_DEBUGEN_Msk; if(debug) { +#endif furi_hal_console_puts("\r\nSystem halted. Connect debugger for more info\r\n"); furi_hal_console_puts("\033[0m\r\n"); + furi_hal_debug_enable(); + RESTORE_REGISTERS_AND_HALT_MCU(true); +#ifndef FURI_DEBUG } else { furi_hal_rtc_set_fault_data((uint32_t)__furi_check_message); furi_hal_console_puts("\r\nRebooting system.\r\n"); furi_hal_console_puts("\033[0m\r\n"); furi_hal_power_reset(); } +#endif __builtin_unreachable(); } diff --git a/furi/core/check.h b/furi/core/check.h index 192c5260e..ea83f2219 100644 --- a/furi/core/check.h +++ b/furi/core/check.h @@ -21,6 +21,10 @@ extern "C" { #define FURI_NORETURN noreturn #endif +// Flags instead of pointers will save ~4 bytes on furi_assert and furi_check calls. +#define __FURI_ASSERT_MESSAGE_FLAG (0x01) +#define __FURI_CHECK_MESSAGE_FLAG (0x02) + /** Crash system */ FURI_NORETURN void __furi_crash(); @@ -44,20 +48,20 @@ 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 failed\r\n"); \ - } \ +#define furi_check(__e) \ + do { \ + if(!(__e)) { \ + furi_crash(__FURI_CHECK_MESSAGE_FLAG); \ + } \ } while(0) /** 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 failed\r\n"); \ - } \ +#define furi_assert(__e) \ + do { \ + if(!(__e)) { \ + furi_crash(__FURI_ASSERT_MESSAGE_FLAG); \ + } \ } while(0) #else #define furi_assert(__e) \ diff --git a/furi/core/core_defines.h b/furi/core/core_defines.h index 03a364abd..830bb191c 100644 --- a/furi/core/core_defines.h +++ b/furi/core/core_defines.h @@ -24,6 +24,10 @@ extern "C" { }) #endif +#ifndef ABS +#define ABS(a) ({ (a) < 0 ? -(a) : (a); }) +#endif + #ifndef ROUND_UP_TO #define ROUND_UP_TO(a, b) \ ({ \ diff --git a/lib/SConscript b/lib/SConscript index 9742d81a3..28a508471 100644 --- a/lib/SConscript +++ b/lib/SConscript @@ -45,6 +45,7 @@ env.Append( ) ), File("u8g2/u8g2.h"), + File("xtreme/xtreme.h"), ], CPPDEFINES=[ '"M_MEMORY_FULL(x)=abort()"', diff --git a/lib/ST25RFAL002/platform.c b/lib/ST25RFAL002/platform.c index 754e25650..a97be1c75 100644 --- a/lib/ST25RFAL002/platform.c +++ b/lib/ST25RFAL002/platform.c @@ -45,11 +45,14 @@ void platformDisableIrqCallback() { void platformSetIrqCallback(PlatformIrqCallback callback) { rfal_platform.callback = callback; - rfal_platform.thread = - furi_thread_alloc_ex("RfalIrqDriver", 1024, rfal_platform_irq_thread, NULL); - furi_thread_mark_as_service(rfal_platform.thread); - furi_thread_set_priority(rfal_platform.thread, FuriThreadPriorityIsr); - furi_thread_start(rfal_platform.thread); + + if(!rfal_platform.thread) { + rfal_platform.thread = + furi_thread_alloc_ex("RfalIrqDriver", 1024, rfal_platform_irq_thread, NULL); + furi_thread_mark_as_service(rfal_platform.thread); + furi_thread_set_priority(rfal_platform.thread, FuriThreadPriorityIsr); + furi_thread_start(rfal_platform.thread); + } furi_hal_gpio_add_int_callback(&gpio_nfc_irq_rfid_pull, nfc_isr, NULL); // Disable interrupt callback as the pin is shared between 2 apps diff --git a/lib/misc.scons b/lib/misc.scons index a68f9d47d..63681f2f2 100644 --- a/lib/misc.scons +++ b/lib/misc.scons @@ -11,6 +11,7 @@ env.Append( "#/lib/micro-ecc", "#/lib/nanopb", "#/lib/u8g2", + "#/lib/xtreme", ], CPPDEFINES=[ "PB_ENABLE_MALLOC", @@ -32,6 +33,7 @@ libs_recurse = [ "micro-ecc", "u8g2", "update_util", + "xtreme", ] for lib in libs_recurse: diff --git a/lib/nfc/nfc_device.h b/lib/nfc/nfc_device.h index 1d66ced68..793f733e4 100644 --- a/lib/nfc/nfc_device.h +++ b/lib/nfc/nfc_device.h @@ -66,6 +66,7 @@ typedef enum { NfcReadModeEMV, NfcReadModeNFCA, NfcReadModeFelica, + NfcReadModeNFCF, } NfcReadMode; typedef struct { diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c index 0715f528f..9314a4e53 100644 --- a/lib/nfc/nfc_worker.c +++ b/lib/nfc/nfc_worker.c @@ -756,6 +756,10 @@ void nfc_worker_read_type(NfcWorker* nfc_worker) { break; } } + } else if(read_mode == NfcReadModeNFCF) { + nfc_worker->dev_data->protocol = NfcDeviceProtocolUnknown; + event = NfcWorkerEventReadUidNfcF; + break; } } } else { @@ -1048,7 +1052,8 @@ void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker) { if(mf_classic_authenticate_skip_activate( &tx_rx, block_num, key, MfClassicKeyA, !deactivated, cuid)) { mf_classic_set_key_found(data, i, MfClassicKeyA, key); - FURI_LOG_D(TAG, "Key A found"); + FURI_LOG_D( + TAG, "Key A found: %04lx%08lx", (uint32_t)(key >> 32), (uint32_t)key); nfc_worker->callback(NfcWorkerEventFoundKeyA, nfc_worker->context); uint64_t found_key; @@ -1070,8 +1075,14 @@ void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker) { deactivated = true; } else { // If the key A is marked as found and matches the searching key, invalidate it + uint8_t found_key[6]; + memcpy(found_key, data->block[i].value, 6); + + uint8_t current_key[6]; + memcpy(current_key, &key, 6); + if(mf_classic_is_key_found(data, i, MfClassicKeyA) && - data->block[i].value[0] == key) { + found_key == current_key) { 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); @@ -1081,7 +1092,8 @@ void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker) { is_key_b_found = mf_classic_is_key_found(data, i, MfClassicKeyB); if(mf_classic_authenticate_skip_activate( &tx_rx, block_num, key, MfClassicKeyB, !deactivated, cuid)) { - FURI_LOG_D(TAG, "Key B found"); + FURI_LOG_D( + TAG, "Key B found: %04lx%08lx", (uint32_t)(key >> 32), (uint32_t)key); mf_classic_set_key_found(data, i, MfClassicKeyB, key); nfc_worker->callback(NfcWorkerEventFoundKeyB, nfc_worker->context); nfc_worker_mf_classic_key_attack(nfc_worker, key, &tx_rx, i + 1); @@ -1089,8 +1101,14 @@ void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker) { deactivated = true; } else { // If the key B is marked as found and matches the searching key, invalidate it + uint8_t found_key[6]; + memcpy(found_key, data->block[i].value + 10, 6); + + uint8_t current_key[6]; + memcpy(current_key, &key, 6); + if(mf_classic_is_key_found(data, i, MfClassicKeyB) && - data->block[i].value[10] == key) { + found_key == current_key) { 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); diff --git a/lib/nfc/protocols/felica.c b/lib/nfc/protocols/felica.c index d9489cb50..827187564 100644 --- a/lib/nfc/protocols/felica.c +++ b/lib/nfc/protocols/felica.c @@ -8,13 +8,15 @@ #define TAG "FeliCa" bool felica_check_ic_type(uint8_t* PMm) { - uint8_t ic_type = PMm[0]; - uint8_t rom_type = PMm[1]; + uint8_t rom_type = PMm[0]; + uint8_t ic_type = PMm[1]; bool is_valid_ic = false; if(ic_type == 0xff) { // RC-S967 in nfc-dep is_valid_ic = true; - } else if(ic_type == 0xf0 || ic_type == 0xf2) { // Lite(S) + } else if(ic_type == 0xf2) { // RC-S732? + is_valid_ic = true; + } else if(ic_type == 0xf0 || ic_type == 0xf1) { // Lite(S) is_valid_ic = true; } else if(ic_type == 0xe1) { // RC-S967 in plug mode is_valid_ic = true; @@ -149,6 +151,55 @@ FelicaICType felica_get_ic_type(uint8_t* PMm) { return FelicaICType2K; } +/** Parse common FeliCa response headers. + * + * This parses and validates the most commonly occurring response header types. + * + * The header needs to match the (length, res, idm) format, and also (sf1, sf2) when always_succeed + * is set to false. + * + * @param buf RX buffer. + * @param len RX buffer length. + * @param reader The FeliCa reader context. + * @param expected_resp Expected response code. Must be an odd number. + * @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( + uint8_t* buf, + uint8_t len, + FelicaReader* reader, + uint8_t expected_resp, + bool always_succeed) { + furi_assert(expected_resp & 1); + furi_assert(buf != NULL); + furi_assert(reader != NULL); + + uint8_t header_size = always_succeed ? 10 : 12; + if(len < header_size) { + FURI_LOG_E(TAG, "Malformed header: too short."); + return 0; + } + if(buf[1] != expected_resp) { + FURI_LOG_E(TAG, "Expecting %u, got %u.", expected_resp, buf[1]); + return 0; + } + if(memcmp(&buf[2], reader->current_idm, 8) != 0) { + FURI_LOG_E(TAG, "IDm mismatch."); + return 0; + } + if(always_succeed) { + reader->status_flags[0] = buf[10]; + reader->status_flags[1] = buf[11]; + if(reader->status_flags[0] != 0 || reader->status_flags[1] != 0) { + FURI_LOG_W( + TAG, "SF1: %02X SF2: %02X", reader->status_flags[0], reader->status_flags[1]); + return 0; + } + } + return header_size; +} + uint8_t felica_prepare_unencrypted_read( uint8_t* dest, const FelicaReader* reader, @@ -210,32 +261,12 @@ uint16_t felica_parse_unencrypted_read( FelicaReader* reader, uint8_t* out, uint16_t out_len) { - if(len < 12) { - return false; - } - len--; - buf++; - - if(*buf != FELICA_UNENCRYPTED_READ_RES) { - return false; - } - len--; - buf++; - - if(memcmp(buf, reader->current_idm, 8) != 0) { - return false; - } - len -= 8; - buf += 8; - - reader->status_flags[0] = buf[0]; - reader->status_flags[1] = buf[1]; - len -= 2; - buf += 2; - if(reader->status_flags[0] != 0) { - FURI_LOG_W(TAG, "SF1: %02X SF2: %02X", reader->status_flags[0], reader->status_flags[1]); + uint8_t consumed = felica_consume_header(buf, len, reader, FELICA_UNENCRYPTED_READ_RES, false); + if(!consumed) { return 0; } + len -= consumed; + buf += consumed; if(len < 1) { return 0; @@ -313,48 +344,77 @@ uint8_t felica_lite_prepare_unencrypted_write( } bool felica_parse_unencrypted_write(uint8_t* buf, uint8_t len, FelicaReader* reader) { - if(len < 12) { + uint8_t consumed = + felica_consume_header(buf, len, reader, FELICA_UNENCRYPTED_WRITE_RES, false); + if(!consumed) { return false; } + return true; +} + +uint8_t felica_prepare_request_system_code(uint8_t* dest, FelicaReader* reader) { + dest[0] = FELICA_REQUEST_SYSTEM_CODE_CMD; + memcpy(&dest[1], reader->current_idm, 8); + return 9; +} + +bool felica_parse_request_system_code( + uint8_t* buf, + uint8_t len, + FelicaReader* reader, + FelicaSystemArray_t* systems) { + uint8_t consumed = + felica_consume_header(buf, len, reader, FELICA_REQUEST_SYSTEM_CODE_RES, true); + if(consumed == 0) { + return false; + } + len -= consumed; + buf += consumed; + + uint8_t entries = *buf; len--; buf++; - if(*buf != FELICA_UNENCRYPTED_WRITE_RES) { + if(len < 2 * entries) { + FURI_LOG_E(TAG, "FELICA_REQUEST_SYSTEM_CODE_RES: Response too short"); return false; } - len--; - buf++; - if(memcmp(buf, reader->current_idm, 8) != 0) { - return false; - } - len -= 8; - buf += 8; + for(uint8_t idx = 0; idx < entries; idx++) { + FelicaSystem* system = FelicaSystemArray_push_new(*systems); + furi_assert(system != NULL); - reader->status_flags[0] = buf[0]; - reader->status_flags[1] = buf[1]; - len -= 2; - buf += 2; - if(reader->status_flags[0] != 0) { - FURI_LOG_W(TAG, "SF1: %02X SF2: %02X", reader->status_flags[0], reader->status_flags[1]); - return 0; + // Set system code + system->number = idx; + system->code = buf[2 * idx] | (buf[2 * idx + 1] << 8); + + FURI_LOG_D(TAG, "Found system code %04X", system->code); + + // Fill in IDm and PMm + memcpy(system->idm, reader->current_idm, 8); + memcpy(system->pmm, reader->current_pmm, 8); + + // Set system index field in IDm + system->idm[0] &= 0x0f; + system->idm[0] |= ((idx & 0xf) << 4); } return true; } -void felica_parse_system_info(FelicaSystem* system, uint8_t* IDm, uint8_t* PMm) { - memcpy(system->idm, IDm, 8); - memcpy(system->pmm, PMm, 8); - for(int i = 0; i < 6; i++) { - char MRT_byte = PMm[2 + i]; - FelicaMRTParts* mrt_data = &system->maximum_response_times[i]; - mrt_data->real_a = (MRT_byte & 7) + 1; - MRT_byte >>= 3; - mrt_data->real_b = (MRT_byte & 7) + 1; - MRT_byte >>= 3; - mrt_data->exponent = (MRT_byte & 3); - } +static FelicaSystem* felica_gen_monolithic_system_code( + FelicaReader* reader, + FelicaSystemArray_t* systems, + uint16_t system_code) { + FelicaSystem* system = FelicaSystemArray_push_new(*systems); + furi_assert(reader != NULL); + furi_assert(system != NULL); + + memcpy(system->idm, reader->current_idm, 8); + memcpy(system->pmm, reader->current_pmm, 8); + system->code = system_code; + + return system; } bool felica_lite_can_read_without_mac(uint8_t* mc_r_restr, uint8_t block_number) { @@ -366,12 +426,16 @@ 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) { - FelicaBlock* block = malloc(sizeof(FelicaBlock)); + FelicaBlock* block = FelicaBlockArray_safe_get(service->blocks, number); memcpy(block->data, data, FELICA_BLOCK_SIZE); - FelicaBlockList_set_at(service->blocks, number, block); } -bool felica_read_lite_system( +void felica_push_normal_block(FelicaService* service, uint8_t* data) { + FelicaBlock* block = FelicaBlockArray_push_new(service->blocks); + memcpy(block->data, data, FELICA_BLOCK_SIZE); +} + +bool felica_lite_dump_data( FuriHalNfcTxRxContext* tx_rx, FelicaReader* reader, FelicaData* data, @@ -423,8 +487,6 @@ bool felica_read_lite_system( return false; } - system->code = LITE_SYSTEM_CODE; - FelicaLiteInfo* lite_info = &system->lite_info; lite_info->card_key_1 = NULL; lite_info->card_key_2 = NULL; @@ -566,6 +628,22 @@ bool felica_read_lite_system( return true; } +bool felica_std_request_system_code( + FuriHalNfcTxRxContext* tx_rx, + FelicaReader* reader, + FelicaSystemArray_t* systems) { + tx_rx->tx_bits = 8 * felica_prepare_request_system_code(tx_rx->tx_data, reader); + if(!furi_hal_nfc_tx_rx_full(tx_rx)) { + FURI_LOG_E(TAG, "Bad exchange requesting system code"); + return false; + } + if(!felica_parse_request_system_code(tx_rx->rx_data, tx_rx->rx_bits / 8, reader, systems)) { + FURI_LOG_E(TAG, "Bad response to Request System Code command"); + return false; + } + return true; +} + bool felica_read_card( FuriHalNfcTxRxContext* tx_rx, FelicaData* data, @@ -581,31 +659,24 @@ bool felica_read_card( memcpy(reader.current_idm, polled_idm, 8); memcpy(reader.current_pmm, polled_pmm, 8); - FelicaSystem* current_system = malloc(sizeof(FelicaSystem)); - FelicaSystemList_init(data->systems); - FelicaSystemList_push_back(data->systems, current_system); - - felica_parse_system_info(current_system, polled_idm, polled_pmm); + FelicaSystemArray_init(data->systems); if(data->type == FelicaICTypeLite || data->type == FelicaICTypeLiteS) { FURI_LOG_I(TAG, "Reading Felica Lite system"); - felica_read_lite_system(tx_rx, &reader, data, current_system); + FelicaSystem* lite_system = + felica_gen_monolithic_system_code(&reader, &(data->systems), LITE_SYSTEM_CODE); + felica_lite_dump_data(tx_rx, &reader, data, lite_system); card_read = true; break; } + FURI_LOG_I(TAG, "Reading Felica Standard system"); } while(false); return card_read; } void felica_service_clear(FelicaService* service) { - FelicaBlockList_it_t it; - for(FelicaBlockList_it(it, service->blocks); !FelicaBlockList_end_p(it); - FelicaBlockList_next(it)) { - FelicaBlock* block = *FelicaBlockList_ref(it); - free(block); - } - FelicaBlockList_clear(service->blocks); + FelicaBlockArray_clear(service->blocks); } void felica_lite_clear(FelicaLiteInfo* lite_info) { @@ -628,31 +699,33 @@ void felica_lite_clear(FelicaLiteInfo* lite_info) { } } +void felica_node_clear(FelicaNode* node); + void felica_area_clear(FelicaArea* area) { - FelicaNodeList_it_t it; - for(FelicaNodeList_it(it, area->nodes); !FelicaNodeList_end_p(it); FelicaNodeList_next(it)) { - FelicaNode* node = *FelicaNodeList_ref(it); - if(node->type == FelicaNodeTypeArea) { - felica_area_clear(node->area); - } else if(node->type == FelicaNodeTypeService) { - felica_service_clear(node->service); + for + M_EACH(node, area->nodes, FelicaNodeArray_t) { + felica_node_clear(node); } - free(node); + FelicaNodeArray_clear(area->nodes); +} + +void felica_node_clear(FelicaNode* node) { + if(node->type == FelicaNodeTypeArea) { + felica_area_clear(node->area); + } else if(node->type == FelicaNodeTypeService) { + felica_service_clear(node->service); } - FelicaNodeList_clear(area->nodes); } void felica_clear(FelicaData* data) { - FelicaSystemList_it_t it; - for(FelicaSystemList_it(it, data->systems); !FelicaSystemList_end_p(it); - FelicaSystemList_next(it)) { - FelicaSystem* system = *FelicaSystemList_ref(it); - if(system->code == LITE_SYSTEM_CODE) { - felica_lite_clear(&system->lite_info); - ; - } else { - felica_area_clear(&system->root_area); + for + M_EACH(system, data->systems, FelicaSystemArray_t) { + if(system->code == LITE_SYSTEM_CODE) { + felica_lite_clear(&system->lite_info); + } else { + felica_node_clear(&system->root); + FelicaPublicServiceDict_clear(system->public_services); + } } - } - FelicaSystemList_clear(data->systems); + 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 fa81cba2f..f8e504102 100644 --- a/lib/nfc/protocols/felica.h +++ b/lib/nfc/protocols/felica.h @@ -10,6 +10,8 @@ #define MRT_T_SIG_x16 4833038.24 //ns, MRT_T_SIG * (4 ** 2) #define MRT_T_SIG_x64 19332152.96 //ns, MRT_T_SIG * (4 ** 2) +#define FELICA_PMM_MRT_BASE 2 + #define FELICA_VARIABLE_MRT 0 #define FELICA_FIXED_MRT 1 #define FELICA_MUTUAL_AUTH_MRT 2 @@ -17,6 +19,13 @@ #define FELICA_WRITE_MRT 4 #define FELICA_OTHER_MRT 5 +#define FELICA_PMM_VARIABLE_MRT (FELICA_PMM_MRT_BASE + FELICA_VARIABLE_MRT) +#define FELICA_PMM_FIXED_MRT (FELICA_PMM_MRT_BASE + FELICA_FIXED_MRT) +#define FELICA_PMM_MUTUAL_AUTH_MRT (FELICA_PMM_MRT_BASE + FELICA_MUTUAL_AUTH_MRT) +#define FELICA_PMM_READ_MRT (FELICA_PMM_MRT_BASE + FELICA_READ_MRT) +#define FELICA_PMM_WRITE_MRT (FELICA_PMM_MRT_BASE + FELICA_WRITE_MRT) +#define FELICA_PMM_OTHER_MRT (FELICA_PMM_MRT_BASE + FELICA_OTHER_MRT) + #define FELICA_BLOCK_SIZE 16 #define CYBERNET_SYSTEM_CODE 0x0003 @@ -55,9 +64,13 @@ #define FELICA_UNENCRYPTED_READ_CMD 0x06 #define FELICA_UNENCRYPTED_WRITE_CMD 0x08 +#define FELICA_SEARCH_SERVICE_CODE_CMD 0x0a +#define FELICA_REQUEST_SYSTEM_CODE_CMD 0x0c #define FELICA_UNENCRYPTED_READ_RES 0x07 #define FELICA_UNENCRYPTED_WRITE_RES 0x09 +#define FELICA_SEARCH_SERVICE_CODE_RES 0x0b +#define FELICA_REQUEST_SYSTEM_CODE_RES 0x0d typedef enum { FelicaICTypeRC_SA24_10K, // RC-SA24/1x @@ -88,13 +101,6 @@ typedef enum { FelicaICTypeSuica, // https://www.tuv-nederland.nl/assets/files/cerfiticaten/2019/07/cr-nscib-cc-10-30076-cr.pdf } FelicaICType; -typedef struct { - uint8_t exponent : 2; - // Incremented at read - uint8_t real_a : 4; - uint8_t real_b : 4; -} FelicaMRTParts; - typedef enum { FelicaServiceTypeRandom = (0b0010 << 2), FelicaServiceTypeCyclic = (0b0011 << 2), @@ -121,20 +127,20 @@ DICT_SET_DEF( FelicaServiceAttribute, M_ENUM_OPLIST(FelicaServiceAttribute, FelicaServiceAttributeAuthRW)) -typedef FelicaMRTParts FelicaMRTParameters[6]; - typedef struct { uint8_t data[FELICA_BLOCK_SIZE]; } FelicaBlock; -ARRAY_DEF(FelicaBlockList, FelicaBlock*, M_PTR_OPLIST) +ARRAY_DEF(FelicaBlockArray, FelicaBlock, M_POD_OPLIST) +#define M_OPL_FelicaBlockArray_t() ARRAY_OPLIST(FelicaBlockArray, M_POD_OPLIST) typedef struct { uint16_t number; FelicaServiceAttributeList_t access_control_list; // accounts for overlap services - bool is_extended_overlap; + bool is_extended_overlap; // We don't know much about this currently. will always be false union { - FelicaBlockList_t blocks; + // TODO change this to use FelicaBlockArray_t + FelicaBlockArray_t blocks; struct { uint16_t overlap_target; uint8_t block_start; @@ -149,23 +155,44 @@ typedef enum { } FelicaNodeType; struct _FelicaArea_t; -typedef struct { +typedef struct _FelicaArea_t FelicaArea; + +struct _FelicaNode_s; +typedef struct _FelicaNode_s FelicaNode; + +struct _FelicaNode_s { + /** Node type. */ FelicaNodeType type; + /** Borrowed pointer to its parent node. */ + FelicaNode* parent; union { - struct _FelicaArea_t* area; + /** (Area/dir type only) The area struct. */ + FelicaArea* area; + /** (Service/file type only) The service struct. */ FelicaService* service; }; -} FelicaNode; +}; -ARRAY_DEF(FelicaNodeList, FelicaNode*, M_PTR_OPLIST) +// TODO properly remove this +//ARRAY_DEF(FelicaNodeList, FelicaNode*, M_PTR_OPLIST) +ARRAY_DEF(FelicaNodeArray, FelicaNode, M_POD_OPLIST) +#define M_OPL_FelicaNodeArray_t() ARRAY_OPLIST(FelicaNodeArray, M_POD_OPLIST) -typedef struct _FelicaArea_t { +ARRAY_DEF(FelicaNodeRefArray, FelicaNode*, M_PTR_OPLIST) +#define M_OPL_FelicaNodeRefArray_t() ARRAY_OPLIST(FelicaNodeRefArray, M_PTR_OPLIST) + +// { service_number: service_ptr_in_tree } +DICT_DEF2(FelicaPublicServiceDict, uint16_t, M_DEFAULT_OPLIST, FelicaService*, M_PTR_OPLIST) +#define M_OPL_FelicaPublicServiceDict_t() \ + DICT_OPLIST(FelicaPublicServiceDict, M_DEFAULT_OPLIST, M_PTR_OPLIST) + +struct _FelicaArea_t { uint16_t number; bool can_create_subareas; uint16_t end_service_code; - FelicaNodeList_t nodes; -} FelicaArea; + FelicaNodeArray_t nodes; +}; typedef struct { uint8_t* S_PAD[14]; @@ -186,24 +213,39 @@ typedef struct { } FelicaLiteInfo; typedef struct _FelicaSystem_t { + /** FeliCa system index. */ uint8_t number; + /** If the system belongs to a FeliCa Lite (and be its only system). */ + bool is_lite; + /** FeliCa system code. */ uint16_t code; + /** System IDm with system index bitfield properly set. */ uint8_t idm[8]; + /** Cached card PMm. */ uint8_t pmm[8]; - FelicaMRTParameters maximum_response_times; union { + /** (For FeliCa Lite only) Card content. */ FelicaLiteInfo lite_info; - FelicaArea root_area; + struct { + /** (For FeliCa Standard only) The root of the raw filesystem tree. */ + FelicaNode root; + /** (For FeliCa Standard only) Shortcut for all publicly accessible services for quick + * access by card parsers. */ + FelicaPublicServiceDict_t public_services; + }; }; } FelicaSystem; -ARRAY_DEF(FelicaSystemList, FelicaSystem*, M_PTR_OPLIST) +// TODO properly remove this +//ARRAY_DEF(FelicaSystemList, FelicaSystem*, M_PTR_OPLIST) +ARRAY_DEF(FelicaSystemArray, FelicaSystem, M_POD_OPLIST) +#define M_OPL_FelicaSystemArray_t() ARRAY_OPLIST(FelicaSystemArray, M_POD_OPLIST) typedef struct { FelicaICType type; uint8_t subtype; - FelicaSystemList_t systems; + FelicaSystemArray_t systems; } FelicaData; typedef struct { @@ -255,8 +297,17 @@ bool felica_parse_unencrypted_write(uint8_t* buf, uint8_t len, FelicaReader* rea 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); -bool felica_read_lite_system( +/** Dump a FeliCa Lite or Lite-S tag. + * + * @param tx_rx NFC context. + * @param reader FeliCa reader context. + * @param data Output data object. + * @param system FeliCa system description. + * @return true if successful. + */ +bool felica_lite_dump_data( FuriHalNfcTxRxContext* tx_rx, FelicaReader* reader, FelicaData* data, diff --git a/lib/nfc/protocols/felica_util.c b/lib/nfc/protocols/felica_util.c index a5198aa09..481ea5ed7 100644 --- a/lib/nfc/protocols/felica_util.c +++ b/lib/nfc/protocols/felica_util.c @@ -1,6 +1,16 @@ #include "./felica.h" #include +static const uint32_t TIME_CONSTANT_US = 302; + +// TODO move this to felica.c +uint_least32_t felica_estimate_timing_us(uint_least8_t timing, uint_least8_t units) { + uint_least32_t base_cost_factor = 1 + (timing & 0x7); + uint_least32_t unit_cost_factor = 1 + ((timing >> 3) & 0x7); + uint_least32_t scale = 1 << ((timing >> 6) * 2); + return TIME_CONSTANT_US * scale * (base_cost_factor + unit_cost_factor * units); +} + 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 4224668eb..e53d66805 100644 --- a/lib/nfc/protocols/felica_util.h +++ b/lib/nfc/protocols/felica_util.h @@ -1,4 +1,5 @@ #include "./felica.h" +uint_least32_t felica_estimate_timing_us(uint_least8_t timing, uint_least8_t units); 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/nfcv.c b/lib/nfc/protocols/nfcv.c index e3ab4103f..2e817a538 100644 --- a/lib/nfc/protocols/nfcv.c +++ b/lib/nfc/protocols/nfcv.c @@ -1315,13 +1315,13 @@ bool nfcv_emu_loop( float fc_1024 = (4.0f * duration) / (4 * (frame_pos * 4 + 1) + 1); /* it should be 1024/fc in 64MHz ticks */ float fact = fc_1024 / ((1000000.0f * 64.0f * 1024.0f) / NFCV_FC); - FURI_LOG_D(TAG, "1024/fc: %f -> %f %%", (double)fc_1024, (double)fact * 100); + FURI_LOG_D(TAG, "1024/fc: %f -> %f %%", (double)fc_1024, (double)(fact * 100)); #if 0 if(fact > 0.99f && fact < 1.01f) { static float avg_err = 0.0f; avg_err = (avg_err * 15.0f + (fact - 1.0f)) / 16.0f; - FURI_LOG_D(TAG, " ==> set %f %%", (1.0f + avg_err) * 100); + FURI_LOG_D(TAG, " ==> set %f %%", (double)((1.0f + avg_err) * 100)); digital_sequence_timebase_correction(nfcv_data->emu_air.nfcv_signal, 1.0f + avg_err); } #endif diff --git a/lib/subghz/blocks/custom_btn.c b/lib/subghz/blocks/custom_btn.c index dd1436798..3023e09ea 100644 --- a/lib/subghz/blocks/custom_btn.c +++ b/lib/subghz/blocks/custom_btn.c @@ -1,7 +1,7 @@ #include "custom_btn.h" -static uint8_t custom_btn_id; -static uint8_t custom_btn_original; +static uint8_t custom_btn_id = 0; +static uint8_t custom_btn_original = 0; static uint8_t custom_btn_max_btns = 0; void subghz_custom_btn_set(uint8_t b) { diff --git a/lib/subghz/blocks/generic.c b/lib/subghz/blocks/generic.c index 331606fe5..1827388af 100644 --- a/lib/subghz/blocks/generic.c +++ b/lib/subghz/blocks/generic.c @@ -152,7 +152,7 @@ SubGhzProtocolStatus subghz_block_generic_deserialize_check_count_bit( break; } if(instance->data_count_bit != count_bit) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); + FURI_LOG_D(TAG, "Wrong number of bits in key"); ret = SubGhzProtocolStatusErrorValueBitCount; break; } diff --git a/lib/subghz/environment.c b/lib/subghz/environment.c index b39b259d4..5ded243c4 100644 --- a/lib/subghz/environment.c +++ b/lib/subghz/environment.c @@ -16,6 +16,7 @@ SubGhzEnvironment* subghz_environment_alloc() { instance->protocol_registry = NULL; instance->came_atomo_rainbow_table_file_name = NULL; instance->nice_flor_s_rainbow_table_file_name = NULL; + instance->alutech_at_4n_rainbow_table_file_name = NULL; return instance; } @@ -26,6 +27,7 @@ void subghz_environment_free(SubGhzEnvironment* instance) { instance->protocol_registry = NULL; instance->came_atomo_rainbow_table_file_name = NULL; instance->nice_flor_s_rainbow_table_file_name = NULL; + instance->alutech_at_4n_rainbow_table_file_name = NULL; subghz_keystore_free(instance->keystore); free(instance); diff --git a/lib/subghz/protocols/alutech_at_4n.c b/lib/subghz/protocols/alutech_at_4n.c index 04e10adf5..20dae49c1 100644 --- a/lib/subghz/protocols/alutech_at_4n.c +++ b/lib/subghz/protocols/alutech_at_4n.c @@ -79,6 +79,11 @@ const SubGhzProtocol subghz_protocol_alutech_at_4n = { .encoder = &subghz_protocol_alutech_at_4n_encoder, }; +static void subghz_protocol_alutech_at_4n_remote_controller( + SubGhzBlockGeneric* instance, + uint8_t crc, + const char* file_name); + void* subghz_protocol_encoder_alutech_at_4n_alloc(SubGhzEnvironment* environment) { UNUSED(environment); SubGhzProtocolEncoderAlutech_at_4n* instance = @@ -503,10 +508,18 @@ SubGhzProtocolStatus subghz_protocol_encoder_alutech_at_4n_deserialize( break; } + if(!flipper_format_read_uint32(flipper_format, "CRC", (uint32_t*)&instance->crc, 1)) { + FURI_LOG_E(TAG, "Missing CRC"); + break; + } + //optional parameter parameter flipper_format_read_uint32( flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + subghz_protocol_alutech_at_4n_remote_controller( + &instance->generic, instance->crc, instance->alutech_at_4n_rainbow_table_file_name); + subghz_protocol_encoder_alutech_at_4n_get_upload(instance, instance->generic.btn); if(!flipper_format_rewind(flipper_format)) { diff --git a/lib/subghz/protocols/keeloq.c b/lib/subghz/protocols/keeloq.c index 6698f54fe..4900e7e49 100644 --- a/lib/subghz/protocols/keeloq.c +++ b/lib/subghz/protocols/keeloq.c @@ -393,7 +393,6 @@ static bool uint8_t custom_btn_id = subghz_custom_btn_get(); uint8_t original_btn_num = subghz_custom_btn_get_original(); - // Set custom button if(custom_btn_id == 1) { switch(original_btn_num) { @@ -499,11 +498,9 @@ static bool break; } } - if((custom_btn_id == 0) && (original_btn_num != 0)) { btn = original_btn_num; } - // Generate new key if(subghz_protocol_keeloq_gen_data(instance, btn, true)) { diff --git a/lib/subghz/protocols/nero_radio.c b/lib/subghz/protocols/nero_radio.c index d7731dca6..7e787ffd0 100644 --- a/lib/subghz/protocols/nero_radio.c +++ b/lib/subghz/protocols/nero_radio.c @@ -117,8 +117,7 @@ static bool } //Send start bit - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_nero_radio_const.te_short * 4); + instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)830); instance->encoder.upload[index++] = level_duration_make(false, (uint32_t)subghz_protocol_nero_radio_const.te_short); @@ -142,14 +141,22 @@ static bool //send bit 1 instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)subghz_protocol_nero_radio_const.te_long); - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_nero_radio_const.te_short * 37); + if(instance->generic.data_count_bit == 57) { + instance->encoder.upload[index++] = level_duration_make(false, (uint32_t)1300); + } else { + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_nero_radio_const.te_short * 23); + } } else { //send bit 0 instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)subghz_protocol_nero_radio_const.te_short); - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_nero_radio_const.te_short * 37); + if(instance->generic.data_count_bit == 57) { + instance->encoder.upload[index++] = level_duration_make(false, (uint32_t)1300); + } else { + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_nero_radio_const.te_short * 23); + } } return true; } @@ -164,8 +171,14 @@ SubGhzProtocolStatus &instance->generic, flipper_format, subghz_protocol_nero_radio_const.min_count_bit_for_found); - if(ret != SubGhzProtocolStatusOk) { - break; + + if((ret == SubGhzProtocolStatusErrorValueBitCount) && + (instance->generic.data_count_bit == 57)) { + ret = SubGhzProtocolStatusOk; + } else { + if(ret != SubGhzProtocolStatusOk) { + break; + } } //optional parameter parameter flipper_format_read_uint32( @@ -284,8 +297,7 @@ void subghz_protocol_decoder_nero_radio_feed(void* context, bool level, uint32_t break; case NeroRadioDecoderStepCheckDuration: if(!level) { - if(duration >= ((uint32_t)subghz_protocol_nero_radio_const.te_short * 10 + - subghz_protocol_nero_radio_const.te_delta * 2)) { + if(duration >= ((uint32_t)1250)) { //Found stop bit if(DURATION_DIFF( instance->decoder.te_last, subghz_protocol_nero_radio_const.te_short) < @@ -298,8 +310,10 @@ void subghz_protocol_decoder_nero_radio_feed(void* context, bool level, uint32_t subghz_protocol_blocks_add_bit(&instance->decoder, 1); } instance->decoder.parser_step = NeroRadioDecoderStepReset; - if(instance->decoder.decode_count_bit == - subghz_protocol_nero_radio_const.min_count_bit_for_found) { + if((instance->decoder.decode_count_bit == + subghz_protocol_nero_radio_const.min_count_bit_for_found) || + (instance->decoder.decode_count_bit == + subghz_protocol_nero_radio_const.min_count_bit_for_found + 1)) { instance->generic.data = instance->decoder.decode_data; instance->generic.data_count_bit = instance->decoder.decode_count_bit; @@ -356,10 +370,19 @@ SubGhzProtocolStatus subghz_protocol_decoder_nero_radio_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolDecoderNeroRadio* instance = context; - return subghz_block_generic_deserialize_check_count_bit( + SubGhzProtocolStatus stat; + + stat = subghz_block_generic_deserialize_check_count_bit( &instance->generic, flipper_format, subghz_protocol_nero_radio_const.min_count_bit_for_found); + + if((stat == SubGhzProtocolStatusErrorValueBitCount) && + (instance->generic.data_count_bit == 57)) { + return SubGhzProtocolStatusOk; + } else { + return stat; + } } void subghz_protocol_decoder_nero_radio_get_string(void* context, FuriString* output) { diff --git a/lib/subghz/protocols/raw.c b/lib/subghz/protocols/raw.c index eaad78b91..a82c9cf83 100644 --- a/lib/subghz/protocols/raw.c +++ b/lib/subghz/protocols/raw.c @@ -244,8 +244,8 @@ void subghz_protocol_decoder_raw_reset(void* context) { void subghz_protocol_decoder_raw_feed(void* context, bool level, uint32_t duration) { furi_assert(context); SubGhzProtocolDecoderRAW* instance = context; - - if(!instance->pause && (instance->upload_raw != NULL)) { + // Add check if we got duration higher than 1 second, we skipping it, temp fix + if((!instance->pause && (instance->upload_raw != NULL)) && (duration < ((uint32_t)1000000))) { if(duration > subghz_protocol_raw_const.te_short) { if(instance->last_level != level) { instance->last_level = (level ? true : false); diff --git a/lib/subghz/subghz_file_encoder_worker.c b/lib/subghz/subghz_file_encoder_worker.c index 8bc6e8446..fce4e8592 100644 --- a/lib/subghz/subghz_file_encoder_worker.c +++ b/lib/subghz/subghz_file_encoder_worker.c @@ -56,6 +56,7 @@ void subghz_file_encoder_worker_add_level_duration( bool subghz_file_encoder_worker_data_parse(SubGhzFileEncoderWorker* instance, const char* strStart) { char* str1; + int32_t temp_ds = 0; bool res = false; // Line sample: "RAW_Data: -1, 2, -2..." @@ -72,7 +73,18 @@ bool subghz_file_encoder_worker_data_parse(SubGhzFileEncoderWorker* instance, co // Skip space str1 += 1; - subghz_file_encoder_worker_add_level_duration(instance, atoi(str1)); + // + temp_ds = atoi(str1); + if((temp_ds < -1000000) || (temp_ds > 1000000)) { + if(temp_ds > 0) { + subghz_file_encoder_worker_add_level_duration(instance, (int32_t)100); + } else { + subghz_file_encoder_worker_add_level_duration(instance, (int32_t)-100); + } + //FURI_LOG_I("PARSE", "Number overflow - %d", atoi(str1)); + } else { + subghz_file_encoder_worker_add_level_duration(instance, temp_ds); + } } res = true; } diff --git a/applications/services/xtreme/assets.c b/lib/xtreme/assets.c similarity index 71% rename from applications/services/xtreme/assets.c rename to lib/xtreme/assets.c index 2e4e0bda9..cc1a6fe0d 100644 --- a/applications/services/xtreme/assets.c +++ b/lib/xtreme/assets.c @@ -1,12 +1,41 @@ -#include "assets.h" +#include "xtreme.h" +#include "private.h" #include +#include #include #define TAG "XtremeAssets" -#define ICONS_FMT PACKS_DIR "/%s/Icons/%s" +#define ICONS_FMT XTREME_ASSETS_PATH "/%s/Icons/%s" -XtremeAssets* xtreme_assets = NULL; +XtremeAssets xtreme_assets = { + .A_Levelup_128x64 = &A_Levelup_128x64, + .I_BLE_Pairing_128x64 = &I_BLE_Pairing_128x64, + .I_DolphinCommon_56x48 = &I_DolphinCommon_56x48, + .I_DolphinMafia_115x62 = &I_DolphinMafia_115x62, + .I_DolphinNice_96x59 = &I_DolphinNice_96x59, + .I_DolphinWait_61x59 = &I_DolphinWait_61x59, + .I_iButtonDolphinVerySuccess_108x52 = &I_iButtonDolphinVerySuccess_108x52, + .I_DolphinReadingSuccess_59x63 = &I_DolphinReadingSuccess_59x63, + .I_Lockscreen = &I_Lockscreen, + .I_WarningDolphin_45x42 = &I_WarningDolphin_45x42, + .I_NFC_dolphin_emulation_47x61 = &I_NFC_dolphin_emulation_47x61, + .I_passport_bad_46x49 = &I_passport_bad_46x49, + .I_passport_DB = &I_passport_DB, + .I_passport_happy_46x49 = &I_passport_happy_46x49, + .I_passport_okay_46x49 = &I_passport_okay_46x49, + .I_RFIDDolphinReceive_97x61 = &I_RFIDDolphinReceive_97x61, + .I_RFIDDolphinSend_97x61 = &I_RFIDDolphinSend_97x61, + .I_RFIDDolphinSuccess_108x57 = &I_RFIDDolphinSuccess_108x57, + .I_Cry_dolph_55x52 = &I_Cry_dolph_55x52, + .I_Background_128x11 = &I_Background_128x11, + .I_Fishing_123x52 = &I_Fishing_123x52, + .I_Scanning_123x52 = &I_Scanning_123x52, + .I_Auth_62x31 = &I_Auth_62x31, + .I_Connect_me_62x31 = &I_Connect_me_62x31, + .I_Connected_62x31 = &I_Connected_62x31, + .I_Error_62x31 = &I_Error_62x31, +}; void anim(const Icon** replace, const char* name, FuriString* path, File* file) { do { @@ -104,6 +133,7 @@ void swap(XtremeAssets* x, FuriString* p, File* f) { icon(&x->I_RFIDDolphinSend_97x61, "RFID/RFIDDolphinSend_97x61", p, f); icon(&x->I_RFIDDolphinSuccess_108x57, "RFID/RFIDDolphinSuccess_108x57", p, f); icon(&x->I_Cry_dolph_55x52, "Settings/Cry_dolph_55x52", p, f); + icon(&x->I_Background_128x11, "StatusBar/Background_128x11", p, f); icon(&x->I_Fishing_123x52, "SubGhz/Fishing_123x52", p, f); icon(&x->I_Scanning_123x52, "SubGhz/Scanning_123x52", p, f); icon(&x->I_Auth_62x31, "U2F/Auth_62x31", p, f); @@ -113,44 +143,9 @@ void swap(XtremeAssets* x, FuriString* p, File* f) { } void XTREME_ASSETS_LOAD() { - if(xtreme_assets != NULL) return; - - xtreme_assets = malloc(sizeof(XtremeAssets)); - - xtreme_assets->A_Levelup_128x64 = &A_Levelup_128x64; - xtreme_assets->I_BLE_Pairing_128x64 = &I_BLE_Pairing_128x64; - xtreme_assets->I_DolphinCommon_56x48 = &I_DolphinCommon_56x48; - xtreme_assets->I_DolphinMafia_115x62 = &I_DolphinMafia_115x62; - xtreme_assets->I_DolphinNice_96x59 = &I_DolphinNice_96x59; - xtreme_assets->I_DolphinWait_61x59 = &I_DolphinWait_61x59; - xtreme_assets->I_iButtonDolphinVerySuccess_108x52 = &I_iButtonDolphinVerySuccess_108x52; - xtreme_assets->I_DolphinReadingSuccess_59x63 = &I_DolphinReadingSuccess_59x63; - xtreme_assets->I_Lockscreen = &I_Lockscreen; - xtreme_assets->I_WarningDolphin_45x42 = &I_WarningDolphin_45x42; - xtreme_assets->I_NFC_dolphin_emulation_47x61 = &I_NFC_dolphin_emulation_47x61; - xtreme_assets->I_passport_bad_46x49 = &I_passport_bad_46x49; - xtreme_assets->I_passport_DB = &I_passport_DB; - xtreme_assets->I_passport_happy_46x49 = &I_passport_happy_46x49; - xtreme_assets->I_passport_okay_46x49 = &I_passport_okay_46x49; - xtreme_assets->I_RFIDDolphinReceive_97x61 = &I_RFIDDolphinReceive_97x61; - xtreme_assets->I_RFIDDolphinSend_97x61 = &I_RFIDDolphinSend_97x61; - xtreme_assets->I_RFIDDolphinSuccess_108x57 = &I_RFIDDolphinSuccess_108x57; - xtreme_assets->I_Cry_dolph_55x52 = &I_Cry_dolph_55x52; - xtreme_assets->I_Fishing_123x52 = &I_Fishing_123x52; - xtreme_assets->I_Scanning_123x52 = &I_Scanning_123x52; - xtreme_assets->I_Auth_62x31 = &I_Auth_62x31; - xtreme_assets->I_Connect_me_62x31 = &I_Connect_me_62x31; - xtreme_assets->I_Connected_62x31 = &I_Connected_62x31; - xtreme_assets->I_Error_62x31 = &I_Error_62x31; - - if(furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal) { - FURI_LOG_W(TAG, "Load skipped. Device is in special startup mode."); - return; - } - XtremeSettings* xtreme_settings = XTREME_SETTINGS(); if(xtreme_settings->asset_pack[0] == '\0') return; - xtreme_assets->is_nsfw = strncmp(xtreme_settings->asset_pack, "NSFW", strlen("NSFW")) == 0; + xtreme_assets.is_nsfw = strncmp(xtreme_settings->asset_pack, "NSFW", strlen("NSFW")) == 0; Storage* storage = furi_record_open(RECORD_STORAGE); int32_t timeout = 5000; @@ -162,11 +157,11 @@ void XTREME_ASSETS_LOAD() { FileInfo info; FuriString* path = furi_string_alloc(); - furi_string_printf(path, PACKS_DIR "/%s", xtreme_settings->asset_pack); + furi_string_printf(path, XTREME_ASSETS_PATH "/%s", xtreme_settings->asset_pack); if(storage_common_stat(storage, furi_string_get_cstr(path), &info) == FSE_OK && info.flags & FSF_DIRECTORY) { File* file = storage_file_alloc(storage); - swap(xtreme_assets, path, file); + swap(&xtreme_assets, path, file); storage_file_free(file); } furi_string_free(path); @@ -174,8 +169,5 @@ void XTREME_ASSETS_LOAD() { } XtremeAssets* XTREME_ASSETS() { - if(xtreme_assets == NULL) { - XTREME_ASSETS_LOAD(); - } - return xtreme_assets; + return &xtreme_assets; } diff --git a/lib/xtreme/private.h b/lib/xtreme/private.h new file mode 100644 index 000000000..8b220f586 --- /dev/null +++ b/lib/xtreme/private.h @@ -0,0 +1,4 @@ +#pragma once + +void XTREME_ASSETS_LOAD(); +void XTREME_SETTINGS_LOAD(); diff --git a/lib/xtreme/settings.c b/lib/xtreme/settings.c new file mode 100644 index 000000000..88aa55b99 --- /dev/null +++ b/lib/xtreme/settings.c @@ -0,0 +1,142 @@ +#include "xtreme.h" +#include "private.h" +#include +#include + +#define TAG "XtremeSettings" + +XtremeSettings xtreme_settings = { + .loaded = false, + .asset_pack = "", + .anim_speed = 100, // 100% + .cycle_anims = 0, // Meta.txt + .unlock_anims = false, // OFF + .fallback_anim = true, // ON + .wii_menu = true, // ON + .bad_pins_format = 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 % + .status_icons = true, // ON + .bar_borders = true, // ON + .bar_background = false, // OFF + .sort_dirs_first = true, // ON + .dark_mode = false, // OFF + .favorite_timeout = 0, // OFF + .bad_bt = false, // USB + .bad_bt_remember = false, // OFF + .butthurt_timer = 21600, // 6 H + .rgb_backlight = false, // OFF +}; + +void XTREME_SETTINGS_LOAD() { + XtremeSettings* x = &xtreme_settings; + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* file = flipper_format_file_alloc(storage); + if(flipper_format_file_open_existing(file, XTREME_SETTINGS_PATH)) { + 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); + } + furi_string_free(string); + flipper_format_rewind(file); + flipper_format_read_uint32(file, "anim_speed", &x->anim_speed, 1); + flipper_format_rewind(file); + flipper_format_read_int32(file, "cycle_anims", &x->cycle_anims, 1); + flipper_format_rewind(file); + flipper_format_read_bool(file, "unlock_anims", &x->unlock_anims, 1); + flipper_format_rewind(file); + flipper_format_read_bool(file, "fallback_anim", &x->fallback_anim, 1); + flipper_format_rewind(file); + flipper_format_read_bool(file, "wii_menu", &x->wii_menu, 1); + flipper_format_rewind(file); + flipper_format_read_bool(file, "bad_pins_format", &x->bad_pins_format, 1); + flipper_format_rewind(file); + flipper_format_read_bool(file, "lockscreen_time", &x->lockscreen_time, 1); + flipper_format_rewind(file); + flipper_format_read_bool(file, "lockscreen_seconds", &x->lockscreen_seconds, 1); + flipper_format_rewind(file); + flipper_format_read_bool(file, "lockscreen_date", &x->lockscreen_date, 1); + flipper_format_rewind(file); + flipper_format_read_bool(file, "lockscreen_statusbar", &x->lockscreen_statusbar, 1); + flipper_format_rewind(file); + flipper_format_read_bool(file, "lockscreen_prompt", &x->lockscreen_prompt, 1); + flipper_format_rewind(file); + flipper_format_read_uint32(file, "battery_icon", (uint32_t*)&x->battery_icon, 1); + flipper_format_rewind(file); + flipper_format_read_bool(file, "status_icons", &x->status_icons, 1); + flipper_format_rewind(file); + flipper_format_read_bool(file, "bar_borders", &x->bar_borders, 1); + flipper_format_rewind(file); + flipper_format_read_bool(file, "bar_background", &x->bar_background, 1); + flipper_format_rewind(file); + flipper_format_read_bool(file, "sort_dirs_first", &x->sort_dirs_first, 1); + flipper_format_rewind(file); + flipper_format_read_bool(file, "dark_mode", &x->dark_mode, 1); + flipper_format_rewind(file); + flipper_format_read_uint32(file, "favorite_timeout", &x->favorite_timeout, 1); + flipper_format_rewind(file); + flipper_format_read_bool(file, "bad_bt", &x->bad_bt, 1); + flipper_format_rewind(file); + flipper_format_read_bool(file, "bad_bt_remember", &x->bad_bt_remember, 1); + flipper_format_rewind(file); + flipper_format_read_int32(file, "butthurt_timer", &x->butthurt_timer, 1); + flipper_format_rewind(file); + flipper_format_read_bool(file, "rgb_backlight", &x->rgb_backlight, 1); + } + flipper_format_free(file); + furi_record_close(RECORD_STORAGE); + + xtreme_settings.loaded = true; +} + +void XTREME_SETTINGS_SAVE() { + if(!furi_hal_is_normal_boot()) return; + + XtremeSettings* x = &xtreme_settings; + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* file = flipper_format_file_alloc(storage); + if(flipper_format_file_open_always(file, XTREME_SETTINGS_PATH)) { + flipper_format_write_string_cstr(file, "asset_pack", x->asset_pack); + flipper_format_write_uint32(file, "anim_speed", &x->anim_speed, 1); + flipper_format_write_int32(file, "cycle_anims", &x->cycle_anims, 1); + flipper_format_write_bool(file, "unlock_anims", &x->unlock_anims, 1); + 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, "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_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_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_int32(file, "butthurt_timer", &x->butthurt_timer, 1); + flipper_format_write_bool(file, "rgb_backlight", &x->rgb_backlight, 1); + } + flipper_format_free(file); + furi_record_close(RECORD_STORAGE); +} + +XtremeSettings* XTREME_SETTINGS() { + return &xtreme_settings; +} + +XtremeSettings* XTREME_SETTINGS_WAIT() { + if(furi_hal_is_normal_boot()) { + while(!xtreme_settings.loaded) { + furi_delay_ms(50); + } + } + return &xtreme_settings; +} diff --git a/applications/services/xtreme/assets.h b/lib/xtreme/xtreme.h similarity index 52% rename from applications/services/xtreme/assets.h rename to lib/xtreme/xtreme.h index a4995fd04..e4baeeeba 100644 --- a/applications/services/xtreme/assets.h +++ b/lib/xtreme/xtreme.h @@ -1,14 +1,47 @@ #pragma once -#include "settings.h" #include -#include +#include #ifdef __cplusplus extern "C" { #endif -#define PACKS_DIR EXT_PATH("dolphin_custom") +#define XTREME_SETTINGS_PATH CFG_PATH("xtreme_settings.txt") +#define XTREME_ASSETS_PATH EXT_PATH("dolphin_custom") +#define XTREME_APPS_PATH CFG_PATH("xtreme_apps.txt") +#define XTREME_ASSETS_PACK_NAME_LEN 32 + +typedef struct { + bool loaded; + char asset_pack[XTREME_ASSETS_PACK_NAME_LEN]; + uint32_t anim_speed; + int32_t cycle_anims; + bool unlock_anims; + bool fallback_anim; + bool wii_menu; + bool bad_pins_format; + bool lockscreen_time; + bool lockscreen_seconds; + bool lockscreen_date; + bool lockscreen_statusbar; + bool lockscreen_prompt; + BatteryIcon battery_icon; + bool status_icons; + bool bar_borders; + bool bar_background; + bool sort_dirs_first; + bool dark_mode; + uint32_t favorite_timeout; + bool bad_bt; + bool bad_bt_remember; + int32_t butthurt_timer; + bool rgb_backlight; +} XtremeSettings; + +void XTREME_SETTINGS_SAVE(); +XtremeSettings* XTREME_SETTINGS(); +XtremeSettings* XTREME_SETTINGS_WAIT(); typedef struct { bool is_nsfw; @@ -31,6 +64,7 @@ typedef struct { const Icon* I_RFIDDolphinSend_97x61; const Icon* I_RFIDDolphinSuccess_108x57; const Icon* I_Cry_dolph_55x52; + const Icon* I_Background_128x11; const Icon* I_Fishing_123x52; const Icon* I_Scanning_123x52; const Icon* I_Auth_62x31; @@ -39,8 +73,6 @@ typedef struct { const Icon* I_Error_62x31; } XtremeAssets; -void XTREME_ASSETS_LOAD(); - XtremeAssets* XTREME_ASSETS(); #ifdef __cplusplus diff --git a/scripts/fbt_tools/fbt_dist.py b/scripts/fbt_tools/fbt_dist.py index c0eca0d2a..a5669c488 100644 --- a/scripts/fbt_tools/fbt_dist.py +++ b/scripts/fbt_tools/fbt_dist.py @@ -112,6 +112,8 @@ def DistCommand(env, name, source, **kw): def generate(env): + if not env["VERBOSE"]: + env.SetDefault(COPROCOMSTR="\tCOPRO\t${TARGET}") env.AddMethod(AddFwProject) env.AddMethod(DistCommand) env.AddMethod(AddOpenOCDFlashTarget) @@ -147,7 +149,7 @@ def generate(env): '--stack_file="${COPRO_STACK_BIN}" ' "--stack_addr=${COPRO_STACK_ADDR} ", ], - "\tCOPRO\t${TARGET}", + "$COPROCOMSTR", ) ), } diff --git a/scripts/flipper/assets/copro.py b/scripts/flipper/assets/copro.py index b61ac0329..e0375b51f 100644 --- a/scripts/flipper/assets/copro.py +++ b/scripts/flipper/assets/copro.py @@ -34,7 +34,7 @@ class Copro: self.mcu_copro = None self.logger = logging.getLogger(self.__class__.__name__) - def loadCubeInfo(self, cube_dir, cube_version): + def loadCubeInfo(self, cube_dir, reference_cube_version): if not os.path.isdir(cube_dir): raise Exception(f'"{cube_dir}" doesn\'t exists') self.cube_dir = cube_dir @@ -50,7 +50,7 @@ class Copro: if not cube_version or not cube_version.startswith("FW.WB"): raise Exception(f"Incorrect Cube package or version info") cube_version = cube_version.replace("FW.WB.", "", 1) - if cube_version != cube_version: + if cube_version != reference_cube_version: raise Exception(f"Unsupported cube version") self.version = cube_version diff --git a/scripts/flipper/storage.py b/scripts/flipper/storage.py index 7b56ee0d0..cff32ceb1 100644 --- a/scripts/flipper/storage.py +++ b/scripts/flipper/storage.py @@ -335,7 +335,9 @@ class FlipperStorage: def _check_no_error(self, response, path=None): if self.has_error(response): - raise FlipperStorageException.from_error_code(self.get_error(response)) + raise FlipperStorageException.from_error_code( + path, self.get_error(response) + ) def size(self, path: str): """file size on Flipper""" diff --git a/scripts/sconsdist.py b/scripts/sconsdist.py index 01e9794f4..5cbd2a703 100644 --- a/scripts/sconsdist.py +++ b/scripts/sconsdist.py @@ -181,9 +181,9 @@ class Main(App): ) as zf: for component_key in sdk_components_keys: component_path = self._dist_components.get(component_key) - components_paths[component_key] = basename(component_path) if component_key.endswith(".dir"): + components_paths[component_key] = basename(component_path) for root, dirnames, files in walk(component_path): if "__pycache__" in dirnames: dirnames.remove("__pycache__") @@ -199,7 +199,9 @@ class Main(App): ), ) else: - zf.write(component_path, basename(component_path)) + # We use fixed names for files to avoid having to regenerate VSCode project + components_paths[component_key] = component_key + zf.write(component_path, component_key) zf.writestr( "components.json", diff --git a/scripts/ufbt/SConstruct b/scripts/ufbt/SConstruct index a82189c14..ce7c8b978 100644 --- a/scripts/ufbt/SConstruct +++ b/scripts/ufbt/SConstruct @@ -1,6 +1,8 @@ from SCons.Platform import TempFileMunge from SCons.Node import FS from SCons.Errors import UserError +from SCons.Warnings import warn, WarningOnByDefault + import os import multiprocessing @@ -163,6 +165,18 @@ dist_env.Alias("flash", openocd_target) if env["FORCE"]: env.AlwaysBuild(openocd_target) + +firmware_jflash = dist_env.JFlash( + dist_env["UFBT_STATE_DIR"].File("jflash"), + dist_env["FW_BIN"], + JFLASHADDR="0x20000000", +) +dist_env.Alias("firmware_jflash", firmware_jflash) +dist_env.Alias("jflash", firmware_jflash) +if env["FORCE"]: + env.AlwaysBuild(firmware_jflash) + + firmware_debug = dist_env.PhonyTarget( "debug", "${GDBPYCOM}", @@ -234,7 +248,12 @@ known_extapps = [ for apptype in apps_to_build_as_faps for app in appenv["APPBUILD"].get_apps_of_type(apptype, True) ] +incompatible_apps = [] for app in known_extapps: + if not app.supports_hardware_target(appenv.subst("f${TARGET_HW}")): + incompatible_apps.append(app) + continue + app_artifacts = appenv.BuildAppElf(app) app_src_dir = extract_abs_dir(app_artifacts.app._appdir) app_artifacts.installer = [ @@ -242,6 +261,13 @@ for app in known_extapps: appenv.Install(app_src_dir.Dir("dist").Dir("debug"), app_artifacts.debug), ] +if len(incompatible_apps): + print( + "WARNING: The following apps are not compatible with the current target hardware and will not be built: {}".format( + ", ".join([app.name for app in incompatible_apps]) + ) + ) + if appenv["FORCE"]: appenv.AlwaysBuild([extapp.compact for extapp in apps_artifacts.values()]) @@ -391,3 +417,13 @@ AddPostAction( dist_env.Precious(app_template_dist) dist_env.NoClean(app_template_dist) dist_env.Alias("create", app_template_dist) + +dist_env.PhonyTarget( + "get_blackmagic", + "@echo $( ${BLACKMAGIC_ADDR} $)", +) + +dist_env.PhonyTarget( + "get_apiversion", + "@echo $( ${UFBT_API_VERSION} $)", +) diff --git a/scripts/ufbt/project_template/.vscode/launch.json b/scripts/ufbt/project_template/.vscode/launch.json index d9c98dcc1..697de9a49 100644 --- a/scripts/ufbt/project_template/.vscode/launch.json +++ b/scripts/ufbt/project_template/.vscode/launch.json @@ -2,19 +2,16 @@ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "inputs": [ - // { - // "id": "BLACKMAGIC", - // "type": "command", - // "command": "shellCommand.execute", - // "args": { - // "useSingleResult": true, - // "env": { - // "PATH": "${workspaceFolder};${env:PATH}" - // }, - // "command": "./fbt get_blackmagic", - // "description": "Get Blackmagic device", - // } - // }, + { + "id": "BLACKMAGIC", + "type": "command", + "command": "shellCommand.execute", + "args": { + "description": "Get Blackmagic device", + "useSingleResult": true, + "command": "ufbt -s get_blackmagic", + } + }, ], "configurations": [ { @@ -57,26 +54,26 @@ ], // "showDevDebugOutput": "raw", }, - // { - // "name": "Attach FW (blackmagic)", - // "cwd": "${workspaceFolder}", - // "executable": "@UFBT_FIRMWARE_ELF@", - // "request": "attach", - // "type": "cortex-debug", - // "servertype": "external", - // "gdbTarget": "${input:BLACKMAGIC}", - // "svdFile": "@UFBT_DEBUG_DIR@/STM32WB55_CM4.svd", - // "rtos": "FreeRTOS", - // "postAttachCommands": [ - // "monitor swdp_scan", - // "attach 1", - // "set confirm off", - // "set mem inaccessible-by-default off", - // "source @UFBT_DEBUG_DIR@/flipperapps.py", - // "fap-set-debug-elf-root @UFBT_DEBUG_ELF_DIR@" - // ] - // // "showDevDebugOutput": "raw", - // }, + { + "name": "Attach FW (blackmagic)", + "cwd": "${workspaceFolder}", + "executable": "@UFBT_FIRMWARE_ELF@", + "request": "attach", + "type": "cortex-debug", + "servertype": "external", + "gdbTarget": "${input:BLACKMAGIC}", + "svdFile": "@UFBT_DEBUG_DIR@/STM32WB55_CM4.svd", + "rtos": "FreeRTOS", + "postAttachCommands": [ + "monitor swdp_scan", + "attach 1", + "set confirm off", + "set mem inaccessible-by-default off", + "source @UFBT_DEBUG_DIR@/flipperapps.py", + "fap-set-debug-elf-root @UFBT_DEBUG_ELF_DIR@" + ] + // "showDevDebugOutput": "raw", + }, { "name": "Attach FW (JLink)", "cwd": "${workspaceFolder}", diff --git a/scripts/ufbt/project_template/.vscode/tasks.json b/scripts/ufbt/project_template/.vscode/tasks.json index 6343bba7b..4b3f4bda5 100644 --- a/scripts/ufbt/project_template/.vscode/tasks.json +++ b/scripts/ufbt/project_template/.vscode/tasks.json @@ -20,24 +20,30 @@ "type": "shell", "command": "ufbt" }, + { + "label": "Clean", + "group": "build", + "type": "shell", + "command": "ufbt -c" + }, { "label": "Flash FW (ST-Link)", "group": "build", "type": "shell", "command": "ufbt FORCE=1 flash" }, - // { - // "label": "[NOTIMPL] Flash FW (blackmagic)", - // "group": "build", - // "type": "shell", - // "command": "ufbt flash_blackmagic" - // }, - // { - // "label": "[NOTIMPL] Flash FW (JLink)", - // "group": "build", - // "type": "shell", - // "command": "ufbt FORCE=1 jflash" - // }, + { + "label": "Flash FW (blackmagic)", + "group": "build", + "type": "shell", + "command": "ufbt flash_blackmagic" + }, + { + "label": "Flash FW (JLink)", + "group": "build", + "type": "shell", + "command": "ufbt FORCE=1 jflash" + }, { "label": "Flash FW (USB, with resources)", "group": "build", @@ -49,6 +55,12 @@ "group": "build", "type": "shell", "command": "ufbt update" + }, + { + "label": "Update VSCode config for current SDK", + "group": "build", + "type": "shell", + "command": "ufbt vscode_dist" } ] } \ No newline at end of file diff --git a/scripts/ufbt/project_template/app_template/application.fam b/scripts/ufbt/project_template/app_template/application.fam index 31fadb207..37a4ce665 100644 --- a/scripts/ufbt/project_template/app_template/application.fam +++ b/scripts/ufbt/project_template/app_template/application.fam @@ -6,7 +6,7 @@ App( apptype=FlipperAppType.EXTERNAL, entry_point="@FBT_APPID@_app", stack_size=2 * 1024, - fap_category="Misc", + fap_category="Examples", # Optional values # fap_version=(0, 1), # (major, minor) fap_icon="@FBT_APPID@.png", # 10x10 1-bit PNG diff --git a/scripts/ufbt/site_tools/ufbt_state.py b/scripts/ufbt/site_tools/ufbt_state.py index 6ba8c6962..76c6e9acf 100644 --- a/scripts/ufbt/site_tools/ufbt_state.py +++ b/scripts/ufbt/site_tools/ufbt_state.py @@ -75,12 +75,6 @@ def generate(env, **kw): if not sdk_state["meta"]["hw_target"].endswith(sdk_data["hardware"]): raise StopError("SDK state file doesn't match hardware target") - if sdk_state["meta"]["version"] != ufbt_state["version"]: - warn( - WarningOnByDefault, - f"Version mismatch: SDK state vs uFBT: {sdk_state['meta']['version']} vs {ufbt_state['version']}", - ) - scripts_dir = sdk_current_sdk_dir_node.Dir(sdk_components["scripts.dir"]) env.SetDefault( # Paths diff --git a/scripts/version.py b/scripts/version.py index 2527105d1..6011dea26 100644 --- a/scripts/version.py +++ b/scripts/version.py @@ -1,5 +1,5 @@ #!/usb/bin/env python3 -VERSION = "XFW-0044" +VERSION = "XFW-0045" from flipper.app import App @@ -152,6 +152,7 @@ class Main(App): "firmware_commit": current_info["GIT_COMMIT"], "firmware_branch": current_info["GIT_BRANCH"], "firmware_target": current_info["TARGET"], + "firmware_version": current_info["VERSION"], } with open(version_json_name, "w", newline="\n") as file: json.dump(version_json, file, indent=4) diff --git a/site_scons/cc.scons b/site_scons/cc.scons index 1eb6a3376..55ab72ba6 100644 --- a/site_scons/cc.scons +++ b/site_scons/cc.scons @@ -36,6 +36,7 @@ ENV.AppendUnique( ], CPPDEFINES=[ "_GNU_SOURCE", + *GetOption("extra_defines"), ], LINKFLAGS=[ "-mcpu=cortex-m4", diff --git a/site_scons/commandline.scons b/site_scons/commandline.scons index 5610478cb..84ef6ce19 100644 --- a/site_scons/commandline.scons +++ b/site_scons/commandline.scons @@ -26,6 +26,14 @@ AddOption( help="List of applications to add to firmware's built-ins. Also see FIRMWARE_APP_SET and FIRMWARE_APPS", ) +AddOption( + "--extra-define", + action="append", + dest="extra_defines", + default=[], + help="Extra global define that will be passed to C/C++ compiler, can be specified multiple times", +) + AddOption( "--extra-ext-apps", action="store", diff --git a/site_scons/extapps.scons b/site_scons/extapps.scons index 7789f95b1..707132dc8 100644 --- a/site_scons/extapps.scons +++ b/site_scons/extapps.scons @@ -1,5 +1,4 @@ from dataclasses import dataclass, field -from os.path import dirname from SCons.Node import NodeList from SCons.Warnings import warn, WarningOnByDefault @@ -132,6 +131,7 @@ Depends(sdk_source, appenv.ProcessSdkDepends(f"{amalgamated_api}.d")) appenv["SDK_DIR"] = appenv.Dir("${BUILD_DIR}/sdk_headers") sdk_header_tree = appenv.SDKHeaderTreeExtractor(appenv["SDK_DIR"], amalgamated_api) +Depends(sdk_header_tree, appenv["SDK_DEFINITION"]) # AlwaysBuild(sdk_tree) Alias("sdk_tree", sdk_header_tree) extapps.sdk_tree = sdk_header_tree