Merge branch 'ul-dev' into xfw-dev

This commit is contained in:
Willy-JL
2023-04-07 23:40:28 +01:00
107 changed files with 3653 additions and 607 deletions
+12 -12
View File
@@ -21,13 +21,6 @@ static void avr_isp_app_tick_event_callback(void* context) {
AvrIspApp* avr_isp_app_alloc() {
AvrIspApp* app = malloc(sizeof(AvrIspApp));
// Enable 5v power, multiple attempts to avoid issues with power chip protection false triggering
uint8_t attempts = 0;
while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) {
furi_hal_power_enable_otg();
furi_delay_ms(10);
}
app->file_path = furi_string_alloc();
furi_string_set(app->file_path, STORAGE_APP_DATA_PATH_PREFIX);
app->error = AvrIspErrorNoError;
@@ -102,6 +95,13 @@ AvrIspApp* avr_isp_app_alloc() {
AvrIspViewChipDetect,
avr_isp_chip_detect_view_get_view(app->avr_isp_chip_detect_view));
// Enable 5v power, multiple attempts to avoid issues with power chip protection false triggering
uint8_t attempts = 0;
while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) {
furi_hal_power_enable_otg();
furi_delay_ms(10);
}
scene_manager_next_scene(app->scene_manager, AvrIspSceneStart);
return app;
@@ -110,6 +110,11 @@ AvrIspApp* avr_isp_app_alloc() {
void avr_isp_app_free(AvrIspApp* app) {
furi_assert(app);
// Disable 5v power
if(furi_hal_power_is_otg_enabled()) {
furi_hal_power_disable_otg();
}
// Submenu
view_dispatcher_remove_view(app->view_dispatcher, AvrIspViewSubmenu);
submenu_free(app->submenu);
@@ -159,11 +164,6 @@ void avr_isp_app_free(AvrIspApp* app) {
// Path strings
furi_string_free(app->file_path);
// Disable 5v power
if(furi_hal_power_is_otg_enabled()) {
furi_hal_power_disable_otg();
}
free(app);
}
+4 -4
View File
@@ -51,10 +51,10 @@ static void dap_main_view_draw_callback(Canvas* canvas, void* _model) {
canvas_set_color(canvas, ColorBlack);
if(model->dap_active) {
canvas_draw_icon(canvas, 14, 16, &I_ArrowUpFilled_12x18);
canvas_draw_icon(canvas, 28, 16, &I_ArrowDownFilled_12x18);
canvas_draw_icon_ex(canvas, 28, 16, &I_ArrowUpFilled_12x18, IconRotation180);
} else {
canvas_draw_icon(canvas, 14, 16, &I_ArrowUpEmpty_12x18);
canvas_draw_icon(canvas, 28, 16, &I_ArrowDownEmpty_12x18);
canvas_draw_icon_ex(canvas, 28, 16, &I_ArrowUpEmpty_12x18, IconRotation180);
}
switch(model->mode) {
@@ -76,9 +76,9 @@ static void dap_main_view_draw_callback(Canvas* canvas, void* _model) {
}
if(model->rx_active) {
canvas_draw_icon(canvas, 101, 16, &I_ArrowDownFilled_12x18);
canvas_draw_icon_ex(canvas, 101, 16, &I_ArrowUpFilled_12x18, IconRotation180);
} else {
canvas_draw_icon(canvas, 101, 16, &I_ArrowDownEmpty_12x18);
canvas_draw_icon_ex(canvas, 101, 16, &I_ArrowUpEmpty_12x18, IconRotation180);
}
canvas_draw_str_aligned(canvas, 100, 38, AlignCenter, AlignTop, "UART");
Binary file not shown.

Before

Width:  |  Height:  |  Size: 160 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 168 B

+2 -2
View File
@@ -80,9 +80,9 @@ static void gpio_usb_uart_draw_callback(Canvas* canvas, void* _model) {
canvas_draw_icon(canvas, 48, 14, &I_ArrowUpEmpty_14x15);
if(model->rx_active)
canvas_draw_icon(canvas, 48, 34, &I_ArrowDownFilled_14x15);
canvas_draw_icon_ex(canvas, 48, 34, &I_ArrowUpFilled_14x15, IconRotation180);
else
canvas_draw_icon(canvas, 48, 34, &I_ArrowDownEmpty_14x15);
canvas_draw_icon_ex(canvas, 48, 34, &I_ArrowUpFilled_14x15, IconRotation180);
}
static bool gpio_usb_uart_input_callback(InputEvent* event, void* context) {
+11
View File
@@ -0,0 +1,11 @@
App(
appid="ir_scope",
name="IR Scope",
apptype=FlipperAppType.EXTERNAL,
entry_point="ir_scope_app",
cdefines=["APP_IR_SCOPE"],
requires=["gui"],
stack_size=2 * 1024,
fap_icon="ir_scope.png",
fap_category="Tools",
)
+183
View File
@@ -0,0 +1,183 @@
// Author: github.com/kallanreed
#include <furi.h>
#include <furi_hal.h>
#include <infrared.h>
#include <infrared_worker.h>
#include <furi_hal_infrared.h>
#include <gui/gui.h>
#define TAG "IR Scope"
#define COLS 128
#define ROWS 8
typedef struct {
bool autoscale;
uint16_t us_per_sample;
size_t timings_cnt;
uint32_t* timings;
uint32_t timings_sum;
FuriMutex* mutex;
} IRScopeState;
static void state_set_autoscale(IRScopeState* state) {
if(state->autoscale) state->us_per_sample = state->timings_sum / (ROWS * COLS);
}
static void canvas_draw_str_outline(Canvas* canvas, int x, int y, const char* str) {
canvas_set_color(canvas, ColorWhite);
for(int y1 = -1; y1 <= 1; ++y1)
for(int x1 = -1; x1 <= 1; ++x1) canvas_draw_str(canvas, x + x1, y + y1, str);
canvas_set_color(canvas, ColorBlack);
canvas_draw_str(canvas, x, y, str);
}
static void render_callback(Canvas* canvas, void* ctx) {
const IRScopeState* state = (IRScopeState*)ctx;
furi_mutex_acquire(state->mutex, FuriWaitForever);
canvas_clear(canvas);
canvas_draw_frame(canvas, 0, 0, 128, 64);
// Draw the signal chart.
bool on = false;
bool done = false;
size_t ix = 0;
int timing_cols = -1; // Count of columns used to draw the current timing
for(size_t row = 0; row < ROWS && !done; ++row) {
for(size_t col = 0; col < COLS && !done; ++col) {
done = ix >= state->timings_cnt;
if(!done && timing_cols < 0) {
timing_cols = state->timings[ix] / state->us_per_sample;
on = !on;
}
if(timing_cols == 0) ++ix;
int y = row * 8 + 7;
canvas_draw_line(canvas, col, y, col, y - (on ? 5 : 0));
--timing_cols;
}
}
canvas_set_font(canvas, FontSecondary);
if(state->autoscale)
canvas_draw_str_outline(canvas, 100, 64, "Auto");
else {
char buf[20];
snprintf(buf, sizeof(buf), "%uus", state->us_per_sample);
canvas_draw_str_outline(canvas, 100, 64, buf);
}
furi_mutex_release(state->mutex);
}
static void input_callback(InputEvent* input_event, void* ctx) {
FuriMessageQueue* event_queue = ctx;
furi_message_queue_put(event_queue, input_event, FuriWaitForever);
}
static void ir_received_callback(void* ctx, InfraredWorkerSignal* signal) {
furi_check(signal);
IRScopeState* state = (IRScopeState*)ctx;
furi_mutex_acquire(state->mutex, FuriWaitForever);
const uint32_t* timings;
infrared_worker_get_raw_signal(signal, &timings, &state->timings_cnt);
if(state->timings) {
free(state->timings);
state->timings_sum = 0;
}
state->timings = malloc(state->timings_cnt * sizeof(uint32_t));
// Copy and sum.
for(size_t i = 0; i < state->timings_cnt; ++i) {
state->timings[i] = timings[i];
state->timings_sum += timings[i];
}
state_set_autoscale(state);
furi_mutex_release(state->mutex);
}
int32_t ir_scope_app(void* p) {
UNUSED(p);
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent));
furi_check(event_queue);
if(furi_hal_infrared_is_busy()) {
FURI_LOG_E(TAG, "Infrared is busy.");
return -1;
}
IRScopeState state = {
.autoscale = false, .us_per_sample = 200, .timings = NULL, .timings_cnt = 0, .mutex = NULL};
state.mutex = furi_mutex_alloc(FuriMutexTypeNormal);
if(!state.mutex) {
FURI_LOG_E(TAG, "Cannot create mutex.");
return -1;
}
ViewPort* view_port = view_port_alloc();
view_port_draw_callback_set(view_port, render_callback, &state);
view_port_input_callback_set(view_port, input_callback, event_queue);
Gui* gui = furi_record_open("gui");
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
InfraredWorker* worker = infrared_worker_alloc();
infrared_worker_rx_enable_signal_decoding(worker, false);
infrared_worker_rx_enable_blink_on_receiving(worker, true);
infrared_worker_rx_set_received_signal_callback(worker, ir_received_callback, &state);
infrared_worker_rx_start(worker);
InputEvent event;
bool processing = true;
while(processing &&
furi_message_queue_get(event_queue, &event, FuriWaitForever) == FuriStatusOk) {
if(event.type == InputTypeRelease) {
furi_mutex_acquire(state.mutex, FuriWaitForever);
if(event.key == InputKeyBack) {
processing = false;
} else if(event.key == InputKeyUp) {
state.us_per_sample = MIN(1000, state.us_per_sample + 25);
state.autoscale = false;
} else if(event.key == InputKeyDown) {
state.us_per_sample = MAX(25, state.us_per_sample - 25);
state.autoscale = false;
} else if(event.key == InputKeyOk) {
state.autoscale = !state.autoscale;
if(state.autoscale)
state_set_autoscale(&state);
else
state.us_per_sample = 200;
}
view_port_update(view_port);
furi_mutex_release(state.mutex);
}
}
// Clean up.
infrared_worker_rx_stop(worker);
infrared_worker_free(worker);
if(state.timings) free(state.timings);
view_port_enabled_set(view_port, false);
gui_remove_view_port(gui, view_port);
furi_record_close("gui");
view_port_free(view_port);
furi_message_queue_free(event_queue);
furi_mutex_free(state.mutex);
return 0;
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 169 B

+32 -13
View File
@@ -386,17 +386,35 @@ bool subghz_remote_key_load(
FURI_LOG_E(TAG, "Could not read Protocol.");
break;
}
if(!furi_string_cmp_str(preset->protocol, "RAW")) {
subghz_protocol_raw_gen_fff_data(fff_data, path);
// repeat
if(!flipper_format_insert_or_update_uint32(fff_data, "Repeat", &preset->repeat, 1)) {
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));
// repeat
if(!flipper_format_insert_or_update_uint32(fff_data, "Repeat", &preset->repeat, 1)) {
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;
}
}
// repeat
if(!flipper_format_insert_or_update_uint32(fff_file, "Repeat", &preset->repeat, 1)) {
FURI_LOG_E(TAG, "Unable to insert or update Repeat");
break;
if(!flipper_format_rewind(fff_file)) {
FURI_LOG_E(TAG, "Rewind error");
return false;
}
preset->decoder = subghz_receiver_search_decoder_base_by_name(
@@ -435,9 +453,6 @@ bool subghz_remote_save_protocol_to_file(FlipperFormat* fff_file, const char* de
path_extract_dirname(dev_file_name, file_dir);
do {
flipper_format_delete_key(fff_file, "Repeat");
//flipper_format_delete_key(fff_file, "Manufacture");
if(!storage_simply_mkdir(storage, furi_string_get_cstr(file_dir))) {
FURI_LOG_E(TAG, "(save) Cannot mkdir");
break;
@@ -475,14 +490,18 @@ void subghz_remote_tx_stop(SubGHzRemote* app) {
//FURI_LOG_I(TAG, "TX Done!");
subghz_transmitter_stop(app->tx_transmitter);
FURI_LOG_D(TAG, "Checking if protocol is dynamic");
//FURI_LOG_D(TAG, "Checking if protocol is dynamic");
const SubGhzProtocolRegistry* protocol_registry_items =
subghz_environment_get_protocol_registry(app->environment);
const SubGhzProtocol* proto = subghz_protocol_registry_get_by_name(
protocol_registry_items, furi_string_get_cstr(app->txpreset->protocol));
FURI_LOG_D(TAG, "Protocol-TYPE %d", proto->type);
//FURI_LOG_D(TAG, "Protocol-TYPE %d", proto->type);
if(proto && proto->type == SubGhzProtocolTypeDynamic) {
FURI_LOG_D(TAG, "Protocol is dynamic. Saving key");
//FURI_LOG_D(TAG, "Protocol is dynamic. Saving key");
// Remove repeat if it was present
flipper_format_delete_key(app->tx_fff_data, "Repeat");
subghz_remote_save_protocol_to_file(app->tx_fff_data, app->tx_file_path);
keeloq_reset_mfname();
@@ -682,15 +701,15 @@ static void render_callback(Canvas* canvas, void* ctx) {
break;
case 2:
canvas_draw_icon(canvas, 113, 15, &I_Pin_cell_13x13);
canvas_draw_icon(canvas, 116, 17, &I_Pin_arrow_down_7x9);
canvas_draw_icon_ex(canvas, 116, 17, &I_Pin_arrow_up_7x9, IconRotation180);
break;
case 3:
canvas_draw_icon(canvas, 113, 15, &I_Pin_cell_13x13);
canvas_draw_icon(canvas, 115, 18, &I_Pin_arrow_right_9x7);
canvas_draw_icon_ex(canvas, 115, 18, &I_Pin_arrow_up_7x9, IconRotation90);
break;
case 4:
canvas_draw_icon(canvas, 113, 15, &I_Pin_cell_13x13);
canvas_draw_icon(canvas, 115, 18, &I_Pin_arrow_left_9x7);
canvas_draw_icon_ex(canvas, 115, 18, &I_Pin_arrow_up_7x9, IconRotation270);
break;
case 5:
canvas_draw_icon(canvas, 113, 15, &I_Pin_cell_13x13);
+4 -1
View File
@@ -17,7 +17,10 @@ App(
name="base32",
),
Lib(
name="list",
name="base64",
),
Lib(
name="linked_list"
),
Lib(
name="timezone_utils",
+3 -1
View File
@@ -41,7 +41,9 @@ bool totp_cli_read_line(Cli* cli, FuriString* out_str, bool mask_user_input) {
} else if(c == CliSymbolAsciiETX) {
cli_nl();
return false;
} else if((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
} else if(
(c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
c == '/' || c == '=' || c == '+') {
if(mask_user_input) {
putc('*', stdout);
} else {
+6 -17
View File
@@ -14,24 +14,13 @@
#define DOCOPT_OPTIONS "[options]"
#define DOCOPT_DEFAULT(val) "[default: " val "]"
#define TOTP_CLI_PRINTF(format, ...) \
do { \
_Pragma(STRINGIFY(GCC diagnostic push)) \
_Pragma(STRINGIFY(GCC diagnostic ignored "-Wdouble-promotion")) \
printf(format, ##__VA_ARGS__); \
_Pragma(STRINGIFY(GCC diagnostic pop)) \
} while(false)
#define TOTP_CLI_PRINTF(format, ...) printf(format, ##__VA_ARGS__)
#define TOTP_CLI_PRINTF_COLORFUL(color, format, ...) \
do { \
_Pragma(STRINGIFY(GCC diagnostic push)) \
_Pragma(STRINGIFY(GCC diagnostic ignored "-Wdouble-promotion")) \
printf("\e[%s", color); \
printf(format, ##__VA_ARGS__); \
printf("\e[0m"); \
fflush(stdout); \
_Pragma(STRINGIFY(GCC diagnostic pop)) \
} while(false)
#define TOTP_CLI_PRINTF_COLORFUL(color, format, ...) \
printf("\e[%s", color); \
printf(format, ##__VA_ARGS__); \
printf("\e[0m"); \
fflush(stdout)
#define TOTP_CLI_COLOR_ERROR "91m"
#define TOTP_CLI_COLOR_WARNING "93m"
+40 -24
View File
@@ -1,7 +1,7 @@
#include "add.h"
#include <stdlib.h>
#include <lib/toolbox/args.h>
#include "../../../lib/list/list.h"
#include <linked_list.h>
#include "../../../types/token_info.h"
#include "../../../services/config/config.h"
#include "../../../services/convert/convert.h"
@@ -17,11 +17,11 @@ void totp_cli_command_add_docopt_commands() {
void totp_cli_command_add_docopt_usage() {
TOTP_CLI_PRINTF(
" " TOTP_CLI_COMMAND_NAME
" " DOCOPT_REQUIRED(TOTP_CLI_COMMAND_ADD " | " TOTP_CLI_COMMAND_ADD_ALT " | " TOTP_CLI_COMMAND_ADD_ALT2) " " DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_NAME) " " DOCOPT_OPTIONAL(DOCOPT_OPTION(TOTP_CLI_COMMAND_ARG_ALGO_PREFIX, DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_ALGO))) " " DOCOPT_OPTIONAL(DOCOPT_OPTION(TOTP_CLI_COMMAND_ARG_DIGITS_PREFIX, DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_DIGITS))) " " DOCOPT_OPTIONAL(
" " DOCOPT_REQUIRED(TOTP_CLI_COMMAND_ADD " | " TOTP_CLI_COMMAND_ADD_ALT " | " TOTP_CLI_COMMAND_ADD_ALT2) " " DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_NAME) " " DOCOPT_OPTIONAL(DOCOPT_OPTION(TOTP_CLI_COMMAND_ARG_ALGO_PREFIX, DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_ALGO))) " " DOCOPT_OPTIONAL(DOCOPT_OPTION(TOTP_CLI_COMMAND_ARG_SECRET_ENCODING_PREFIX, DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_SECRET_ENCODING))) " " DOCOPT_OPTIONAL(
DOCOPT_OPTION(
TOTP_CLI_COMMAND_ARG_DURATION_PREFIX,
TOTP_CLI_COMMAND_ARG_DIGITS_PREFIX,
DOCOPT_ARGUMENT(
TOTP_CLI_COMMAND_ARG_DURATION))) " " DOCOPT_OPTIONAL(DOCOPT_SWITCH(TOTP_CLI_COMMAND_ARG_UNSECURE_PREFIX)) " " DOCOPT_MULTIPLE(DOCOPT_OPTIONAL(DOCOPT_OPTION(TOTP_CLI_COMMAND_ARG_AUTOMATION_FEATURE_PREFIX, DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_AUTOMATION_FEATURE)))) "\r\n");
TOTP_CLI_COMMAND_ARG_DIGITS))) " " DOCOPT_OPTIONAL(DOCOPT_OPTION(TOTP_CLI_COMMAND_ARG_DURATION_PREFIX, DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_DURATION))) " " DOCOPT_OPTIONAL(DOCOPT_SWITCH(TOTP_CLI_COMMAND_ARG_UNSECURE_PREFIX)) " " DOCOPT_MULTIPLE(DOCOPT_OPTIONAL(DOCOPT_OPTION(TOTP_CLI_COMMAND_ARG_AUTOMATION_FEATURE_PREFIX, DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_AUTOMATION_FEATURE)))) "\r\n");
}
void totp_cli_command_add_docopt_arguments() {
@@ -35,11 +35,21 @@ void totp_cli_command_add_docopt_options() {
TOTP_CLI_COMMAND_ARG_ALGO)) " Token hashing algorithm. Must be one of: " TOTP_TOKEN_ALGO_SHA1_NAME
", " TOTP_TOKEN_ALGO_SHA256_NAME
", " TOTP_TOKEN_ALGO_SHA512_NAME
", " TOTP_TOKEN_ALGO_STEAM_NAME
" " DOCOPT_DEFAULT(TOTP_TOKEN_ALGO_SHA1_NAME) "\r\n");
TOTP_CLI_PRINTF(" " DOCOPT_OPTION(
TOTP_CLI_COMMAND_ARG_DIGITS_PREFIX,
DOCOPT_ARGUMENT(
TOTP_CLI_COMMAND_ARG_DIGITS)) " Number of digits to generate, one of: 6, 8 " DOCOPT_DEFAULT("6") "\r\n");
TOTP_CLI_COMMAND_ARG_DIGITS)) " Number of digits to generate, one of: 5, 6, 8 " DOCOPT_DEFAULT("6") "\r\n");
TOTP_CLI_PRINTF(" " DOCOPT_OPTION(
TOTP_CLI_COMMAND_ARG_SECRET_ENCODING_PREFIX,
DOCOPT_ARGUMENT(
TOTP_CLI_COMMAND_ARG_SECRET_ENCODING)) " Token secret encoding, one of " PLAIN_TOKEN_ENCODING_BASE32_NAME
", " PLAIN_TOKEN_ENCODING_BASE64_NAME
" " DOCOPT_DEFAULT(
PLAIN_TOKEN_ENCODING_BASE32_NAME) "\r\n");
TOTP_CLI_PRINTF(" " DOCOPT_OPTION(
TOTP_CLI_COMMAND_ARG_DURATION_PREFIX,
DOCOPT_ARGUMENT(
@@ -83,13 +93,16 @@ void totp_cli_command_add_handle(PluginState* plugin_state, FuriString* args, Cl
// 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_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);
}
@@ -115,31 +128,34 @@ void totp_cli_command_add_handle(PluginState* plugin_state, FuriString* args, Cl
TOTP_CLI_DELETE_LAST_LINE();
if(!token_info_set_secret(
token_info,
furi_string_get_cstr(temp_str),
furi_string_size(temp_str),
plugin_state->iv)) {
TOTP_CLI_PRINTF_ERROR("Token secret seems to be invalid and can not be parsed\r\n");
furi_string_secure_free(temp_str);
token_info_free(token_info);
return;
}
furi_string_secure_free(temp_str);
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;
}
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);
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 {
TOTP_CLI_PRINT_ERROR_UPDATING_CONFIG_FILE();
token_info_free(token_info);
TOTP_CLI_PRINTF_ERROR("Token secret seems to be invalid and can not be parsed\r\n");
}
if(load_generate_token_scene) {
+1 -1
View File
@@ -3,7 +3,7 @@
#include <stdlib.h>
#include <ctype.h>
#include <lib/toolbox/args.h>
#include "../../../lib/list/list.h"
#include <linked_list.h>
#include "../../../services/config/config.h"
#include "../../cli_helpers.h"
#include "../../../ui/scene_director.h"
+1 -1
View File
@@ -1,7 +1,7 @@
#include "details.h"
#include <stdlib.h>
#include <lib/toolbox/args.h>
#include "../../../lib/list/list.h"
#include <linked_list.h>
#include "../../../types/token_info.h"
#include "../../../services/config/constants.h"
#include "../../cli_helpers.h"
+1 -1
View File
@@ -1,6 +1,6 @@
#include "list.h"
#include <stdlib.h>
#include "../../../lib/list/list.h"
#include <linked_list.h>
#include "../../../types/token_info.h"
#include "../../../services/config/constants.h"
#include "../../cli_helpers.h"
+1 -1
View File
@@ -2,7 +2,7 @@
#include <stdlib.h>
#include <lib/toolbox/args.h>
#include "../../../lib/list/list.h"
#include <linked_list.h>
#include "../../../types/token_info.h"
#include "../../../services/config/config.h"
#include "../../cli_helpers.h"
+2 -1
View File
@@ -2,11 +2,12 @@
#include <stdlib.h>
#include <lib/toolbox/args.h>
#include <linked_list.h>
#include "../../../types/token_info.h"
#include "../../../types/user_pin_codes.h"
#include "../../../services/config/config.h"
#include "../../cli_helpers.h"
#include "../../../lib/polyfills/memset_s.h"
#include <memset_s.h>
#include "../../../services/crypto/crypto.h"
#include "../../../ui/scene_director.h"
@@ -35,7 +35,7 @@ void totp_cli_command_timezone_handle(PluginState* plugin_state, FuriString* arg
if(*strtof_endptr == 0 && tz >= -12.75f && tz <= 12.75f) {
plugin_state->timezone_offset = tz;
if(totp_config_file_update_timezone_offset(tz) == TotpConfigFileUpdateSuccess) {
TOTP_CLI_PRINTF_SUCCESS("Timezone is set to %f\r\n", tz);
TOTP_CLI_PRINTF_SUCCESS("Timezone is set to %f\r\n", (double)tz);
} else {
TOTP_CLI_PRINT_ERROR_UPDATING_CONFIG_FILE();
}
@@ -50,7 +50,8 @@ void totp_cli_command_timezone_handle(PluginState* plugin_state, FuriString* arg
TOTP_CLI_PRINTF_ERROR("Invalid timezone offset\r\n");
}
} else {
TOTP_CLI_PRINTF_INFO("Current timezone offset is %f\r\n", plugin_state->timezone_offset);
TOTP_CLI_PRINTF_INFO(
"Current timezone offset is %f\r\n", (double)plugin_state->timezone_offset);
}
furi_string_free(temp_str);
}
+43 -36
View File
@@ -1,7 +1,7 @@
#include "update.h"
#include <stdlib.h>
#include <lib/toolbox/args.h>
#include "../../../lib/list/list.h"
#include <linked_list.h>
#include "../../../types/token_info.h"
#include "../../../services/config/config.h"
#include "../../../services/convert/convert.h"
@@ -18,17 +18,20 @@ void totp_cli_command_update_docopt_commands() {
void totp_cli_command_update_docopt_usage() {
TOTP_CLI_PRINTF(
" " TOTP_CLI_COMMAND_NAME
" " DOCOPT_REQUIRED(TOTP_CLI_COMMAND_UPDATE) " " DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_INDEX) " " DOCOPT_OPTIONAL(DOCOPT_OPTION(TOTP_CLI_COMMAND_ARG_ALGO_PREFIX, DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_ALGO))) " " DOCOPT_OPTIONAL(DOCOPT_OPTION(TOTP_CLI_COMMAND_ARG_NAME_PREFIX, DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_NAME))) " " DOCOPT_OPTIONAL(
" " DOCOPT_REQUIRED(TOTP_CLI_COMMAND_UPDATE) " " DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_INDEX) " " DOCOPT_OPTIONAL(DOCOPT_OPTION(TOTP_CLI_COMMAND_ARG_ALGO_PREFIX, DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_ALGO))) " " DOCOPT_OPTIONAL(DOCOPT_OPTION(TOTP_CLI_COMMAND_ARG_SECRET_ENCODING_PREFIX, DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_SECRET_ENCODING))) " " DOCOPT_OPTIONAL(DOCOPT_OPTION(TOTP_CLI_COMMAND_ARG_NAME_PREFIX, DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_NAME))) " " DOCOPT_OPTIONAL(
DOCOPT_OPTION(
TOTP_CLI_COMMAND_ARG_DIGITS_PREFIX,
DOCOPT_ARGUMENT(
TOTP_CLI_COMMAND_ARG_DIGITS))) " " DOCOPT_OPTIONAL(DOCOPT_OPTION(TOTP_CLI_COMMAND_ARG_DURATION_PREFIX, DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_DURATION))) " " DOCOPT_OPTIONAL(DOCOPT_SWITCH(TOTP_CLI_COMMAND_ARG_UNSECURE_PREFIX)) " " DOCOPT_MULTIPLE(DOCOPT_OPTIONAL(DOCOPT_OPTION(TOTP_CLI_COMMAND_ARG_AUTOMATION_FEATURE_PREFIX, DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_AUTOMATION_FEATURE)))) "\r\n");
TOTP_CLI_COMMAND_ARG_DIGITS))) " " DOCOPT_OPTIONAL(DOCOPT_OPTION(TOTP_CLI_COMMAND_ARG_DURATION_PREFIX, DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_DURATION))) " " DOCOPT_OPTIONAL(DOCOPT_SWITCH(TOTP_CLI_COMMAND_ARG_UNSECURE_PREFIX)) " " DOCOPT_OPTIONAL(DOCOPT_SWITCH(TOTP_CLI_COMMAND_UPDATE_ARG_SECRET_PREFIX)) " " DOCOPT_MULTIPLE(DOCOPT_OPTIONAL(DOCOPT_OPTION(TOTP_CLI_COMMAND_ARG_AUTOMATION_FEATURE_PREFIX, DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_AUTOMATION_FEATURE)))) "\r\n");
}
void totp_cli_command_update_docopt_options() {
TOTP_CLI_PRINTF(" " DOCOPT_OPTION(
TOTP_CLI_COMMAND_ARG_NAME_PREFIX,
DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_NAME)) " Token name\r\n");
TOTP_CLI_PRINTF(" " DOCOPT_SWITCH(
TOTP_CLI_COMMAND_UPDATE_ARG_SECRET_PREFIX) " Update token secret\r\n");
}
static bool
@@ -85,6 +88,7 @@ void totp_cli_command_update_handle(PluginState* plugin_state, FuriString* args,
// 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) &&
@@ -93,7 +97,9 @@ void totp_cli_command_update_handle(PluginState* plugin_state, FuriString* args,
!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_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);
}
@@ -105,54 +111,55 @@ void totp_cli_command_update_handle(PluginState* plugin_state, FuriString* args,
}
}
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");
if(!totp_cli_read_line(cli, temp_str, mask_user_input) ||
!totp_cli_ensure_authenticated(plugin_state, cli)) {
TOTP_CLI_DELETE_LAST_LINE();
TOTP_CLI_PRINTF_INFO("Cancelled by user\r\n");
furi_string_secure_free(temp_str);
token_info_free(token_info);
return;
}
token_secret_read = totp_cli_read_line(cli, temp_str, mask_user_input);
TOTP_CLI_DELETE_LAST_LINE();
if(token_info->token != NULL) {
free(token_info->token);
}
if(!token_info_set_secret(
token_info,
furi_string_get_cstr(temp_str),
furi_string_size(temp_str),
plugin_state->iv)) {
TOTP_CLI_PRINTF_ERROR("Token secret seems to be invalid and can not be parsed\r\n");
furi_string_secure_free(temp_str);
token_info_free(token_info);
return;
if(!token_secret_read) {
TOTP_CLI_PRINTF_INFO("Cancelled by user\r\n");
}
}
furi_string_secure_free(temp_str);
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;
}
list_item->data = token_info;
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");
}
}
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);
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 {
TOTP_CLI_PRINT_ERROR_UPDATING_CONFIG_FILE();
list_item->data = existing_token_info;
token_info_free(token_info);
}
@@ -108,5 +108,34 @@ bool totp_cli_try_read_unsecure_flag(const FuriString* arg, bool* parsed, bool*
return true;
}
return false;
}
bool totp_cli_try_read_plain_token_secret_encoding(
FuriString* arg,
FuriString* args,
bool* parsed,
PlainTokenSecretEncoding* secret_encoding) {
if(furi_string_cmpi_str(arg, TOTP_CLI_COMMAND_ARG_SECRET_ENCODING_PREFIX) == 0) {
if(!args_read_string_and_trim(args, arg)) {
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;
*parsed = true;
} else if(furi_string_cmpi_str(arg, PLAIN_TOKEN_ENCODING_BASE64_NAME) == 0) {
*secret_encoding = PLAIN_TOKEN_ENCODING_BASE64;
*parsed = true;
} else {
TOTP_CLI_PRINTF_ERROR(
"\"%s\" is incorrect value for argument \"" TOTP_CLI_COMMAND_ARG_SECRET_ENCODING_PREFIX
"\"\r\n",
furi_string_get_cstr(arg));
}
}
return true;
}
return false;
}
+9 -1
View File
@@ -15,6 +15,8 @@
#define TOTP_CLI_COMMAND_ARG_AUTOMATION_FEATURE_PREFIX "-b"
#define TOTP_CLI_COMMAND_ARG_AUTOMATION_FEATURE "feature"
#define TOTP_CLI_COMMAND_ARG_INDEX "index"
#define TOTP_CLI_COMMAND_ARG_SECRET_ENCODING_PREFIX "-e"
#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);
@@ -34,4 +36,10 @@ bool totp_cli_try_read_automation_features(
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_unsecure_flag(const FuriString* arg, bool* parsed, bool* unsecure_flag);
bool totp_cli_try_read_plain_token_secret_encoding(
FuriString* arg,
FuriString* args,
bool* parsed,
PlainTokenSecretEncoding* secret_encoding);
+13 -1
View File
@@ -1,2 +1,14 @@
// Include Bluetooth token input automation
#define TOTP_BADBT_TYPE_ENABLED
#define TOTP_AUTOMATION_ICONS_ENABLED
// Include token input automation icons on the main screen
#define TOTP_AUTOMATION_ICONS_ENABLED
// List of compatible firmwares
#define TOTP_FIRMWARE_OFFICIAL_STABLE 1
#define TOTP_FIRMWARE_OFFICIAL_DEV 2
#define TOTP_FIRMWARE_XTREME 3
// End of list
// Target firmware to build for
#define TOTP_TARGET_FIRMWARE TOTP_FIRMWARE_XTREME
+3 -3
View File
@@ -17,10 +17,10 @@
#include "base32.h"
int base32_decode(const uint8_t* encoded, uint8_t* result, int bufSize) {
size_t base32_decode(const uint8_t* encoded, uint8_t* result, size_t bufSize) {
int buffer = 0;
int bitsLeft = 0;
int count = 0;
size_t count = 0;
for(const uint8_t* ptr = encoded; count < bufSize && *ptr; ++ptr) {
uint8_t ch = *ptr;
if(ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n' || ch == '-') {
@@ -43,7 +43,7 @@ int base32_decode(const uint8_t* encoded, uint8_t* result, int bufSize) {
} else if(ch >= '2' && ch <= '7') {
ch -= '2' - 26;
} else {
return -1;
return 0;
}
buffer |= ch;
+3 -2
View File
@@ -27,6 +27,7 @@
#pragma once
#include <stdlib.h>
#include <stdint.h>
/**
@@ -34,6 +35,6 @@
* @param encoded Base-32 encoded bytes
* @param[out] result result output buffer
* @param bufSize result output buffer size
* @return Decoded result length in bytes if successfully decoded; \c -1 otherwise
* @return Decoded result length in bytes if successfully decoded; \c 0 otherwise
*/
int base32_decode(const uint8_t* encoded, uint8_t* result, int bufSize);
size_t base32_decode(const uint8_t* encoded, uint8_t* result, size_t bufSize);
+72
View File
@@ -0,0 +1,72 @@
/*
* Base64 encoding/decoding (RFC1341)
* Copyright (c) 2005, Jouni Malinen <j@w1.fi>
*
* 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
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
*/
#include "base64.h"
#include <string.h>
static const uint8_t dtable[] = {0x3e, 0x80, 0x80, 0x80, 0x3f, 0x34, 0x35, 0x36, 0x37, 0x38,
0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x80, 0x80, 0x80, 0x0, 0x80,
0x80, 0x80, 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7,
0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0x10, 0x11,
0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x80, 0x80,
0x80, 0x80, 0x80, 0x80, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29,
0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33};
// "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static uint8_t get_dtable_value(uint8_t index) {
return (index < 43 || index > 122) ? 0x80 : dtable[index - 43];
}
uint8_t* base64_decode(const uint8_t* src, size_t len, size_t* out_len, size_t* out_size) {
uint8_t *out;
uint8_t *pos;
uint8_t in[4];
uint8_t block[4];
uint8_t tmp;
size_t i;
size_t count;
size_t olen;
count = 0;
for(i = 0; i < len; i++) {
if(get_dtable_value(src[i]) != 0x80) count++;
}
if(count == 0 || count % 4) return NULL;
olen = count / 4 * 3;
pos = out = malloc(olen);
*out_size = olen;
if(out == NULL) return NULL;
count = 0;
for(i = 0; i < len; i++) {
tmp = get_dtable_value(src[i]);
if(tmp == 0x80) continue;
in[count] = src[i];
block[count] = tmp;
count++;
if(count == 4) {
*pos++ = (block[0] << 2) | (block[1] >> 4);
*pos++ = (block[1] << 4) | (block[2] >> 2);
*pos++ = (block[2] << 6) | block[3];
count = 0;
}
}
if(pos > out) {
if(in[2] == '=')
pos -= 2;
else if(in[3] == '=')
pos--;
}
*out_len = pos - out;
return out;
}
+14
View File
@@ -0,0 +1,14 @@
#pragma once
#include <stdlib.h>
#include <stdint.h>
/**
* @brief Decodes Base-64 encoded bytes into plain bytes.
* @param src Base-64 encoded bytes
* @param len Base-64 encoded bytes count
* @param[out] out_len decoded buffer length
* @param[out] out_size decoded buffer allocated size
* @return Decoded result buffer if successfully decoded; \c NULL otherwise
*/
uint8_t* base64_decode(const uint8_t* src, size_t len, size_t* out_len, size_t* out_size);
@@ -1,4 +1,4 @@
#include "list.h"
#include "linked_list.h"
ListNode* list_init_head(void* data) {
ListNode* new = malloc(sizeof(ListNode));
@@ -84,19 +84,15 @@ ListNode* list_insert_at(ListNode* head, uint16_t index, void* data);
void list_free(ListNode* head);
#define TOTP_LIST_INIT_OR_ADD(head, item, assert) \
do { \
if(head == NULL) { \
head = list_init_head(item); \
assert(head != NULL); \
} else { \
assert(list_add(head, item) != NULL); \
} \
} while(false)
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) \
do { \
ListNode* node = head; \
while(node != NULL) { \
action node = node->next; \
} \
} while(false)
ListNode* node = head; \
while(node != NULL) { \
action node = node->next; \
}
+5 -3
View File
@@ -1,7 +1,7 @@
#include "config.h"
#include <stdlib.h>
#include <string.h>
#include "../list/list.h"
#include <linked_list.h>
#include "../../types/common.h"
#include "../../types/token_info.h"
#include "../../features_config.h"
@@ -139,10 +139,11 @@ static TotpConfigFileOpenResult totp_open_config_file(Storage* storage, FlipperF
furi_string_printf(
temp_str,
" # Token hashing algorithm to use during code generation. Supported options are %s, %s and %s. If you are not use which one to use - use %s",
" # 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(
@@ -152,7 +153,7 @@ static TotpConfigFileOpenResult totp_open_config_file(Storage* storage, FlipperF
flipper_format_write_comment_cstr(
fff_data_file,
"# How many digits there should be in generated code. Available options are 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");
"# 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, " ");
@@ -686,6 +687,7 @@ TokenLoadingResult totp_config_file_load_tokens(PluginState* const plugin_state)
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 {
+1 -4
View File
@@ -26,12 +26,9 @@
#include <stdint.h>
#include <string.h>
#ifdef WORDS_BIGENDIAN
#define SWAP(n) (n)
#else
#include "byteswap.h"
#define SWAP(n) swap_uint64(n)
#endif
/* This array contains the bytes used to pad the buffer to the next
128-byte boundary. */
+5 -13
View File
@@ -11,7 +11,7 @@
#include "../hmac/byteswap.h"
#include "../../lib/timezone_utils/timezone_utils.h"
#define HMAC_MAX_SIZE 64
#define HMAC_MAX_RESULT_SIZE HMAC_SHA512_RESULT_SIZE
/**
* @brief Generates the timeblock for a time in seconds.
@@ -29,19 +29,17 @@ uint64_t totp_timecode(uint8_t interval, uint64_t for_time) {
/**
* @brief Generates an OTP (One Time Password)
* @param algo hashing algorithm to be used
* @param digits desired TOTP code length
* @param plain_secret plain token secret
* @param plain_secret_length plain token secret length
* @param input input data for OTP code generation
* @return OTP code if code was successfully generated; 0 otherwise
*/
uint32_t otp_generate(
uint64_t otp_generate(
TOTP_ALGO algo,
uint8_t digits,
const uint8_t* plain_secret,
size_t plain_secret_length,
uint64_t input) {
uint8_t hmac[HMAC_MAX_SIZE] = {0};
uint8_t hmac[HMAC_MAX_RESULT_SIZE] = {0};
uint64_t input_swapped = swap_uint64(input);
@@ -55,14 +53,12 @@ uint32_t otp_generate(
uint64_t i_code =
((hmac[offset] & 0x7F) << 24 | (hmac[offset + 1] & 0xFF) << 16 |
(hmac[offset + 2] & 0xFF) << 8 | (hmac[offset + 3] & 0xFF));
i_code %= (uint64_t)pow(10, digits);
return i_code;
}
uint32_t totp_at(
uint64_t totp_at(
TOTP_ALGO algo,
uint8_t digits,
const uint8_t* plain_secret,
size_t plain_secret_length,
uint64_t for_time,
@@ -71,11 +67,7 @@ uint32_t totp_at(
uint64_t for_time_adjusted =
timezone_offset_apply(for_time, timezone_offset_from_hours(timezone));
return otp_generate(
algo,
digits,
plain_secret,
plain_secret_length,
totp_timecode(interval, for_time_adjusted));
algo, plain_secret, plain_secret_length, totp_timecode(interval, for_time_adjusted));
}
static int totp_algo_sha1(
+1 -3
View File
@@ -39,7 +39,6 @@ extern const TOTP_ALGO TOTP_ALGO_SHA512;
/**
* @brief Generates a OTP key using the totp algorithm.
* @param algo hashing algorithm to be used
* @param digits desired TOTP code length
* @param plain_secret plain token secret
* @param plain_secret_length plain token secret length
* @param for_time the time the generated key will be created for
@@ -47,9 +46,8 @@ extern const TOTP_ALGO TOTP_ALGO_SHA512;
* @param interval token lifetime in seconds
* @return TOTP code if code was successfully generated; 0 otherwise
*/
uint32_t totp_at(
uint64_t totp_at(
TOTP_ALGO algo,
uint8_t digits,
const uint8_t* plain_secret,
size_t plain_secret_length,
uint64_t for_time,
+1 -1
View File
@@ -4,7 +4,7 @@
#include <gui/gui.h>
#include <dialogs/dialogs.h>
#include "../features_config.h"
#include "../lib/list/list.h"
#include <linked_list.h>
#include "../ui/totp_scenes_enum.h"
#include "notification_method.h"
#include "automation_method.h"
+38 -11
View File
@@ -1,11 +1,11 @@
#include <furi_hal.h>
#include "token_info.h"
#include "stdlib.h"
#include <furi_hal.h>
#include <base32.h>
#include <base64.h>
#include <memset_s.h>
#include <strnlen.h>
#include "common.h"
#include "../lib/base32/base32.h"
#include "../services/crypto/crypto.h"
#include "../lib/polyfills/memset_s.h"
#include "../lib/polyfills/strnlen.h"
TokenInfo* token_info_alloc() {
TokenInfo* tokenInfo = malloc(sizeof(TokenInfo));
@@ -26,15 +26,32 @@ void token_info_free(TokenInfo* token_info) {
bool token_info_set_secret(
TokenInfo* token_info,
const char* base32_token_secret,
const char* plain_token_secret,
size_t token_secret_length,
PlainTokenSecretEncoding plain_token_secret_encoding,
const uint8_t* iv) {
if(token_secret_length == 0) return false;
uint8_t* plain_secret;
size_t plain_secret_length;
size_t plain_secret_size;
if(plain_token_secret_encoding == PLAIN_TOKEN_ENCODING_BASE32) {
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) {
plain_secret_length = 0;
plain_secret = base64_decode(
(const uint8_t*)plain_token_secret,
token_secret_length,
&plain_secret_length,
&plain_secret_size);
furi_check(plain_secret != NULL);
} else {
return false;
}
uint8_t* plain_secret = malloc(token_secret_length);
furi_check(plain_secret != NULL);
int plain_secret_length =
base32_decode((const uint8_t*)base32_token_secret, plain_secret, token_secret_length);
bool result;
if(plain_secret_length > 0) {
token_info->token =
@@ -44,13 +61,16 @@ bool token_info_set_secret(
result = false;
}
memset_s(plain_secret, token_secret_length, 0, token_secret_length);
memset_s(plain_secret, plain_secret_size, 0, plain_secret_size);
free(plain_secret);
return result;
}
bool token_info_set_digits_from_int(TokenInfo* token_info, uint8_t digits) {
switch(digits) {
case 5:
token_info->digits = TOTP_5_DIGITS;
return true;
case 6:
token_info->digits = TOTP_6_DIGITS;
return true;
@@ -89,6 +109,11 @@ bool token_info_set_algo_from_str(TokenInfo* token_info, const FuriString* str)
return true;
}
if(furi_string_cmpi_str(str, TOTP_TOKEN_ALGO_STEAM_NAME) == 0) {
token_info->algo = STEAM;
return true;
}
return false;
}
@@ -100,6 +125,8 @@ char* token_info_get_algo_as_cstr(const TokenInfo* token_info) {
return TOTP_TOKEN_ALGO_SHA256_NAME;
case SHA512:
return TOTP_TOKEN_ALGO_SHA512_NAME;
case STEAM:
return TOTP_TOKEN_ALGO_STEAM_NAME;
default:
break;
}
+24 -2
View File
@@ -7,10 +7,14 @@
#define TOTP_TOKEN_DURATION_DEFAULT 30
#define TOTP_TOKEN_ALGO_SHA1_NAME "sha1"
#define TOTP_TOKEN_ALGO_STEAM_NAME "steam"
#define TOTP_TOKEN_ALGO_SHA256_NAME "sha256"
#define TOTP_TOKEN_ALGO_SHA512_NAME "sha512"
#define TOTP_TOKEN_MAX_LENGTH 255
#define PLAIN_TOKEN_ENCODING_BASE32_NAME "base32"
#define PLAIN_TOKEN_ENCODING_BASE64_NAME "base64"
#define TOTP_TOKEN_AUTOMATION_FEATURE_NONE_NAME "none"
#define TOTP_TOKEN_AUTOMATION_FEATURE_ENTER_AT_THE_END_NAME "enter"
#define TOTP_TOKEN_AUTOMATION_FEATURE_TAB_AT_THE_END_NAME "tab"
@@ -19,6 +23,7 @@
typedef uint8_t TokenHashAlgo;
typedef uint8_t TokenDigitsCount;
typedef uint8_t TokenAutomationFeature;
typedef uint8_t PlainTokenSecretEncoding;
/**
* @brief Hashing algorithm to be used to generate token
@@ -37,13 +42,23 @@ enum TokenHashAlgos {
/**
* @brief SHA512 hashing algorithm
*/
SHA512
SHA512,
/**
* @brief Algorithm used by Steam (Valve)
*/
STEAM
};
/**
* @brief Token digits count to be generated.
*/
enum TokenDigitsCounts {
/**
* @brief 6 digits
*/
TOTP_5_DIGITS = 5,
/**
* @brief 6 digits
*/
@@ -80,6 +95,11 @@ enum TokenAutomationFeatures {
TOKEN_AUTOMATION_FEATURE_TYPE_SLOWER = 0b100
};
enum PlainTokenSecretEncodings {
PLAIN_TOKEN_ENCODING_BASE32 = 0,
PLAIN_TOKEN_ENCODING_BASE64 = 1
};
#define TOTP_TOKEN_DIGITS_MAX_COUNT 8
/**
@@ -139,13 +159,15 @@ void token_info_free(TokenInfo* token_info);
* @param token_info instance where secret should be updated
* @param base32_token_secret plain token secret in Base32 format
* @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
* @return \c true if token successfully set; \c false otherwise
*/
bool token_info_set_secret(
TokenInfo* token_info,
const char* base32_token_secret,
const char* plain_token_secret,
size_t token_secret_length,
PlainTokenSecretEncoding plain_token_secret_encoding,
const uint8_t* iv);
/**
+24
View File
@@ -0,0 +1,24 @@
#pragma once
/* GENERATED BY https://github.com/pavius/the-dot-factory */
#include <stdint.h>
// This structure describes a single character's display information
typedef struct {
const uint8_t width; // width, in bits (or pixels), of the character
const uint16_t
offset; // offset of the character's bitmap, in bytes, into the the FONT_INFO's data array
} FONT_CHAR_INFO;
// Describes a single font
typedef struct {
const uint8_t height; // height, in pages (8 pixels), of the font's characters
const uint8_t startChar; // the first character in the font (e.g. in charInfo and data)
const uint8_t endChar; // the last character in the font
const uint8_t spacePixels; // number of pixels that a space character takes up
const FONT_CHAR_INFO* charInfo; // pointer to array of char information
const uint8_t* data; // pointer to generated array of character visual representation
} FONT_INFO;
@@ -0,0 +1,940 @@
#include "mode_nine.h"
/* GENERATED BY https://github.com/pavius/the-dot-factory */
/*
** Font data for ModeNine 15pt
*/
/* Character bitmaps for ModeNine 15pt */
const uint8_t modeNine_15ptBitmaps[] = {
/* @0 '-' (10 pixels wide) */
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0xFF,
0x03,
0xFF,
0x03,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
/* @28 '0' (10 pixels wide) */
0xFC,
0x00,
0xFE,
0x01,
0x87,
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
0x33,
0x03,
0x33,
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
0x87,
0x03,
0xFE,
0x01,
0xFC,
0x00,
/* @56 '1' (10 pixels wide) */
0x30,
0x00,
0x38,
0x00,
0x3C,
0x00,
0x3C,
0x00,
0x30,
0x00,
0x30,
0x00,
0x30,
0x00,
0x30,
0x00,
0x30,
0x00,
0x30,
0x00,
0x30,
0x00,
0x30,
0x00,
0xFC,
0x00,
0xFC,
0x00,
/* @84 '2' (10 pixels wide) */
0xFC,
0x00,
0xFE,
0x01,
0x87,
0x03,
0x03,
0x03,
0x00,
0x03,
0x80,
0x03,
0xFC,
0x01,
0xFE,
0x00,
0x07,
0x00,
0x03,
0x00,
0x03,
0x00,
0x03,
0x00,
0xFF,
0x03,
0xFF,
0x03,
/* @112 '3' (10 pixels wide) */
0xFF,
0x03,
0xFF,
0x03,
0x80,
0x03,
0xC0,
0x01,
0xE0,
0x00,
0x70,
0x00,
0xF8,
0x00,
0xFC,
0x01,
0x80,
0x03,
0x00,
0x03,
0x03,
0x03,
0x87,
0x03,
0xFE,
0x01,
0xFC,
0x00,
/* @140 '4' (10 pixels wide) */
0xE0,
0x00,
0xF0,
0x00,
0xF8,
0x00,
0xDC,
0x00,
0xCE,
0x00,
0xC7,
0x00,
0xC3,
0x00,
0xC3,
0x00,
0xFF,
0x03,
0xFF,
0x03,
0xC0,
0x00,
0xC0,
0x00,
0xC0,
0x00,
0xC0,
0x00,
/* @168 '5' (10 pixels wide) */
0xFF,
0x03,
0xFF,
0x03,
0x03,
0x00,
0x03,
0x00,
0xFF,
0x00,
0xFF,
0x01,
0x80,
0x03,
0x00,
0x03,
0x00,
0x03,
0x00,
0x03,
0x03,
0x03,
0x87,
0x03,
0xFE,
0x01,
0xFC,
0x00,
/* @196 '6' (10 pixels wide) */
0xF0,
0x00,
0xFC,
0x00,
0x0E,
0x00,
0x06,
0x00,
0x03,
0x00,
0x03,
0x00,
0xFF,
0x00,
0xFF,
0x01,
0x83,
0x03,
0x03,
0x03,
0x03,
0x03,
0x87,
0x03,
0xFE,
0x01,
0xFC,
0x00,
/* @224 '7' (10 pixels wide) */
0xFF,
0x03,
0xFF,
0x03,
0x00,
0x03,
0x80,
0x01,
0xC0,
0x01,
0xE0,
0x00,
0x30,
0x00,
0x18,
0x00,
0x1C,
0x00,
0x0C,
0x00,
0x0C,
0x00,
0x0C,
0x00,
0x0C,
0x00,
0x0C,
0x00,
/* @252 '8' (10 pixels wide) */
0xFC,
0x00,
0xFE,
0x01,
0x87,
0x03,
0x03,
0x03,
0x03,
0x03,
0x87,
0x03,
0xFE,
0x01,
0xFE,
0x01,
0x87,
0x03,
0x03,
0x03,
0x03,
0x03,
0x87,
0x03,
0xFE,
0x01,
0xFC,
0x00,
/* @280 '9' (10 pixels wide) */
0xFC,
0x00,
0xFE,
0x01,
0x87,
0x03,
0x03,
0x03,
0x03,
0x03,
0x07,
0x03,
0xFE,
0x03,
0xFC,
0x03,
0x00,
0x03,
0x00,
0x03,
0x80,
0x01,
0xC0,
0x01,
0xFC,
0x00,
0x3C,
0x00,
/* @308 'B' (10 pixels wide) */
0xFF,
0x00,
0xFF,
0x01,
0x83,
0x03,
0x03,
0x03,
0x03,
0x03,
0x83,
0x03,
0xFF,
0x01,
0xFF,
0x01,
0x83,
0x03,
0x03,
0x03,
0x03,
0x03,
0x83,
0x03,
0xFF,
0x01,
0xFF,
0x00,
/* @336 'C' (10 pixels wide) */
0xFC,
0x00,
0xFE,
0x01,
0x87,
0x03,
0x03,
0x03,
0x03,
0x00,
0x03,
0x00,
0x03,
0x00,
0x03,
0x00,
0x03,
0x00,
0x03,
0x00,
0x03,
0x03,
0x87,
0x03,
0xFE,
0x01,
0xFC,
0x00,
/* @364 'D' (10 pixels wide) */
0xFF,
0x00,
0xFF,
0x01,
0x83,
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
0x83,
0x03,
0xFF,
0x01,
0xFF,
0x00,
/* @392 'F' (10 pixels wide) */
0xFF,
0x03,
0xFF,
0x03,
0x03,
0x00,
0x03,
0x00,
0x03,
0x00,
0x03,
0x00,
0xFF,
0x00,
0xFF,
0x00,
0x03,
0x00,
0x03,
0x00,
0x03,
0x00,
0x03,
0x00,
0x03,
0x00,
0x03,
0x00,
/* @420 'G' (10 pixels wide) */
0xFC,
0x00,
0xFE,
0x01,
0x87,
0x03,
0x03,
0x03,
0x03,
0x00,
0x03,
0x00,
0x03,
0x00,
0x03,
0x00,
0xC3,
0x03,
0xC3,
0x03,
0x03,
0x03,
0x07,
0x03,
0xFE,
0x03,
0xFC,
0x03,
/* @448 'H' (10 pixels wide) */
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
0xFF,
0x03,
0xFF,
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
/* @476 'J' (10 pixels wide) */
0x00,
0x03,
0x00,
0x03,
0x00,
0x03,
0x00,
0x03,
0x00,
0x03,
0x00,
0x03,
0x00,
0x03,
0x00,
0x03,
0x00,
0x03,
0x00,
0x03,
0x03,
0x03,
0x87,
0x03,
0xFE,
0x01,
0xFC,
0x00,
/* @504 'K' (10 pixels wide) */
0x83,
0x03,
0xC3,
0x01,
0xE3,
0x00,
0x73,
0x00,
0x3B,
0x00,
0x1F,
0x00,
0x0F,
0x00,
0x0F,
0x00,
0x1F,
0x00,
0x3B,
0x00,
0x73,
0x00,
0xE3,
0x00,
0xC3,
0x01,
0x83,
0x03,
/* @532 'M' (10 pixels wide) */
0x03,
0x03,
0x87,
0x03,
0xCF,
0x03,
0xFF,
0x03,
0x7B,
0x03,
0x33,
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
/* @560 'N' (10 pixels wide) */
0x03,
0x03,
0x03,
0x03,
0x07,
0x03,
0x0F,
0x03,
0x1F,
0x03,
0x3B,
0x03,
0x73,
0x03,
0xE3,
0x03,
0xC3,
0x03,
0x83,
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
/* @588 'P' (10 pixels wide) */
0xFF,
0x00,
0xFF,
0x01,
0x83,
0x03,
0x03,
0x03,
0x03,
0x03,
0x83,
0x03,
0xFF,
0x01,
0xFF,
0x00,
0x03,
0x00,
0x03,
0x00,
0x03,
0x00,
0x03,
0x00,
0x03,
0x00,
0x03,
0x00,
/* @616 'Q' (10 pixels wide) */
0xFC,
0x00,
0xFE,
0x01,
0x87,
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
0x33,
0x03,
0x73,
0x03,
0xE7,
0x03,
0xFE,
0x01,
0xFC,
0x03,
/* @644 'R' (10 pixels wide) */
0xFF,
0x00,
0xFF,
0x01,
0x83,
0x03,
0x03,
0x03,
0x03,
0x03,
0x83,
0x03,
0xFF,
0x01,
0xFF,
0x00,
0x1F,
0x00,
0x3B,
0x00,
0x73,
0x00,
0xE3,
0x00,
0xC3,
0x01,
0x83,
0x03,
/* @672 'T' (10 pixels wide) */
0xFF,
0x03,
0xFF,
0x03,
0x30,
0x00,
0x30,
0x00,
0x30,
0x00,
0x30,
0x00,
0x30,
0x00,
0x30,
0x00,
0x30,
0x00,
0x30,
0x00,
0x30,
0x00,
0x30,
0x00,
0x30,
0x00,
0x30,
0x00,
/* @700 'V' (10 pixels wide) */
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
0x86,
0x01,
0x86,
0x01,
0xCC,
0x00,
0xCC,
0x00,
0x78,
0x00,
0x78,
0x00,
0x30,
0x00,
/* @728 'W' (10 pixels wide) */
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
0x33,
0x03,
0x33,
0x03,
0x33,
0x03,
0x33,
0x03,
0x33,
0x03,
0x33,
0x03,
0xFF,
0x03,
0xFE,
0x01,
/* @756 'X' (10 pixels wide) */
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
0x87,
0x03,
0xCE,
0x01,
0xFC,
0x00,
0xFC,
0x00,
0xCE,
0x01,
0x87,
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
/* @784 'Y' (10 pixels wide) */
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
0x03,
0x87,
0x03,
0xCE,
0x01,
0xFC,
0x00,
0x78,
0x00,
0x30,
0x00,
0x30,
0x00,
0x30,
0x00,
0x30,
0x00,
0x30,
0x00,
0x30,
0x00,
};
/* Character descriptors for ModeNine 15pt */
/* { [Char width in bits], [Offset into modeNine_15ptCharBitmaps in bytes] } */
const FONT_CHAR_INFO modeNine_15ptDescriptors[] = {
{10, 0}, /* - */
{0, 0}, /* . */
{0, 0}, /* / */
{10, 28}, /* 0 */
{10, 56}, /* 1 */
{10, 84}, /* 2 */
{10, 112}, /* 3 */
{10, 140}, /* 4 */
{10, 168}, /* 5 */
{10, 196}, /* 6 */
{10, 224}, /* 7 */
{10, 252}, /* 8 */
{10, 280}, /* 9 */
{0, 0}, /* : */
{0, 0}, /* ; */
{0, 0}, /* < */
{0, 0}, /* = */
{0, 0}, /* > */
{0, 0}, /* ? */
{0, 0}, /* @ */
{0, 0}, /* A */
{10, 308}, /* B */
{10, 336}, /* C */
{10, 364}, /* D */
{0, 0}, /* E */
{10, 392}, /* F */
{10, 420}, /* G */
{10, 448}, /* H */
{0, 0}, /* I */
{10, 476}, /* J */
{10, 504}, /* K */
{0, 0}, /* L */
{10, 532}, /* M */
{10, 560}, /* N */
{0, 0}, /* O */
{10, 588}, /* P */
{10, 616}, /* Q */
{10, 644}, /* R */
{0, 0}, /* S */
{10, 672}, /* T */
{0, 0}, /* U */
{10, 700}, /* V */
{10, 728}, /* W */
{10, 756}, /* X */
{10, 784}, /* Y */
};
/* Font information for ModeNine 15pt */
const FONT_INFO modeNine_15ptFontInfo = {
14, /* Character height */
'-', /* Start character */
'Y', /* End character */
2, /* Width, in pixels, of space character */
modeNine_15ptDescriptors, /* Character descriptor array */
modeNine_15ptBitmaps, /* Character bitmap array */
};
@@ -0,0 +1,9 @@
#pragma once
/* GENERATED BY https://github.com/pavius/the-dot-factory */
#include "../font_info.h"
#include <stdint.h>
/* Font data for ModeNine 15pt */
extern const FONT_INFO modeNine_15ptFontInfo;
@@ -4,17 +4,17 @@
#include "../../scene_director.h"
#include "totp_input_text.h"
#include "../../../types/token_info.h"
#include "../../../lib/list/list.h"
#include <linked_list.h>
#include "../../../services/config/config.h"
#include "../../ui_controls.h"
#include "../../common_dialogs.h"
#include "../../../lib/roll_value/roll_value.h"
#include <roll_value.h>
#include "../../../types/nullable.h"
#include "../generate_token/totp_scene_generate_token.h"
char* TOKEN_ALGO_LIST[] = {"SHA1", "SHA256", "SHA512"};
char* TOKEN_DIGITS_TEXT_LIST[] = {"6 digits", "8 digits"};
TokenDigitsCount TOKEN_DIGITS_VALUE_LIST[] = {TOTP_6_DIGITS, TOTP_8_DIGITS};
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};
typedef enum {
TokenNameTextBox,
@@ -95,6 +95,8 @@ void totp_scene_add_new_token_activate(
scene_state->screen_y_offset = 0;
scene_state->digits_count_index = 1;
scene_state->input_state = NULL;
scene_state->duration = TOTP_TOKEN_DURATION_DEFAULT;
scene_state->duration_text = furi_string_alloc();
@@ -216,10 +218,10 @@ bool totp_scene_add_new_token_handle_event(PluginEvent* const event, PluginState
break;
case InputKeyRight:
if(scene_state->selected_control == TokenAlgoSelect) {
totp_roll_value_uint8_t(&scene_state->algo, 1, SHA1, SHA512, RollOverflowBehaviorRoll);
totp_roll_value_uint8_t(&scene_state->algo, 1, SHA1, STEAM, RollOverflowBehaviorRoll);
} else if(scene_state->selected_control == TokenLengthSelect) {
totp_roll_value_uint8_t(
&scene_state->digits_count_index, 1, 0, 1, RollOverflowBehaviorRoll);
&scene_state->digits_count_index, 1, 0, 2, RollOverflowBehaviorRoll);
} else if(scene_state->selected_control == TokenDurationSelect) {
totp_roll_value_uint8_t(&scene_state->duration, 15, 15, 255, RollOverflowBehaviorStop);
update_duration_text(scene_state);
@@ -227,11 +229,10 @@ bool totp_scene_add_new_token_handle_event(PluginEvent* const event, PluginState
break;
case InputKeyLeft:
if(scene_state->selected_control == TokenAlgoSelect) {
totp_roll_value_uint8_t(
&scene_state->algo, -1, SHA1, SHA512, RollOverflowBehaviorRoll);
totp_roll_value_uint8_t(&scene_state->algo, -1, SHA1, STEAM, RollOverflowBehaviorRoll);
} else if(scene_state->selected_control == TokenLengthSelect) {
totp_roll_value_uint8_t(
&scene_state->digits_count_index, -1, 0, 1, RollOverflowBehaviorRoll);
&scene_state->digits_count_index, -1, 0, 2, RollOverflowBehaviorRoll);
} else if(scene_state->selected_control == TokenDurationSelect) {
totp_roll_value_uint8_t(
&scene_state->duration, -15, 15, 255, RollOverflowBehaviorStop);
@@ -268,6 +269,7 @@ bool totp_scene_add_new_token_handle_event(PluginEvent* const event, PluginState
tokenInfo,
scene_state->token_secret,
scene_state->token_secret_length,
PLAIN_TOKEN_ENCODING_BASE32,
&plugin_state->iv[0]);
if(token_secret_set) {
@@ -8,7 +8,7 @@
#include "../../constants.h"
#include "../../../services/config/config.h"
#include "../../../services/convert/convert.h"
#include "../../../lib/roll_value/roll_value.h"
#include <roll_value.h>
#include "../../../types/nullable.h"
#include "../../../features_config.h"
#ifdef TOTP_BADBT_TYPE_ENABLED
@@ -19,7 +19,9 @@
#ifdef TOTP_BADBT_TYPE_ENABLED
#include "../../../workers/bt_type_code/bt_type_code.h"
#endif
#include "../../fonts/mode-nine/mode_nine.h"
static const char* STEAM_ALGO_ALPHABET = "23456789BCDFGHJKMNPQRTVWXY";
static const uint8_t PROGRESS_BAR_MARGIN = 3;
static const uint8_t PROGRESS_BAR_HEIGHT = 4;
@@ -121,13 +123,21 @@ static const NotificationSequence*
return (NotificationSequence*)scene_state->notification_sequence_badusb;
}
static void int_token_to_str(uint32_t i_token_code, char* str, TokenDigitsCount len) {
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 {
for(int i = len - 1; i >= 0; i--) {
str[i] = CONVERT_DIGIT_TO_CHAR(i_token_code % 10);
i_token_code = i_token_code / 10;
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;
}
}
}
@@ -137,6 +147,7 @@ static void int_token_to_str(uint32_t i_token_code, char* str, TokenDigitsCount
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;
@@ -161,6 +172,27 @@ static void update_totp_params(PluginState* const plugin_state) {
}
}
static void draw_totp_code(Canvas* const canvas, const SceneState* const scene_state) {
uint8_t code_length = scene_state->current_token->digits;
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_y = SCREEN_HEIGHT_CENTER - (modeNine_15ptFontInfo.height >> 1);
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]);
offset_x += char_width + modeNine_15ptFontInfo.spacePixels;
}
}
void totp_scene_generate_token_init(const PluginState* plugin_state) {
UNUSED(plugin_state);
}
@@ -274,19 +306,19 @@ void totp_scene_generate_token_render(Canvas* const canvas, PluginState* plugin_
int_token_to_str(
totp_at(
get_totp_algo_impl(tokenInfo->algo),
tokenInfo->digits,
key,
key_length,
curr_ts,
plugin_state->timezone_offset,
tokenInfo->duration),
scene_state->last_code,
tokenInfo->digits);
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);
int_token_to_str(0, scene_state->last_code, tokenInfo->digits, tokenInfo->algo);
}
furi_mutex_release(scene_state->last_code_update_sync);
@@ -322,14 +354,7 @@ void totp_scene_generate_token_render(Canvas* const canvas, PluginState* plugin_
canvas_set_color(canvas, ColorBlack);
}
canvas_set_font(canvas, FontBigNumbers);
canvas_draw_str_aligned(
canvas,
SCREEN_WIDTH_CENTER,
SCREEN_HEIGHT_CENTER,
AlignCenter,
AlignCenter,
scene_state->last_code);
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;
@@ -6,13 +6,13 @@
#include "../../constants.h"
#include "../../scene_director.h"
#include "../../../services/config/config.h"
#include "../../../lib/list/list.h"
#include <linked_list.h>
#include "../../../types/token_info.h"
#include "../generate_token/totp_scene_generate_token.h"
#include "../add_new_token/totp_scene_add_new_token.h"
#include "../app_settings/totp_app_settings.h"
#include "../../../types/nullable.h"
#include "../../../lib/roll_value/roll_value.h"
#include <roll_value.h>
#define SCREEN_HEIGHT_THIRD (SCREEN_HEIGHT / 3)
#define SCREEN_HEIGHT_THIRD_CENTER (SCREEN_HEIGHT_THIRD >> 1)
@@ -1,5 +1,6 @@
#include "bt_type_code.h"
#include <furi_hal_bt_hid.h>
#include <bt/bt_service/bt_i.h>
#include <storage/storage.h>
#include "../../types/common.h"
#include "../../types/token_info.h"
@@ -11,6 +12,26 @@ static inline bool totp_type_code_worker_stop_requested() {
return furi_thread_flags_get() & TotpBtTypeCodeWorkerEventStop;
}
#if TOTP_TARGET_FIRMWARE == TOTP_FIRMWARE_XTREME
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) {
max_i = uid_size;
} else {
max_i = 6;
}
const uint8_t* uid = furi_hal_version_uid();
memcpy(mac, uid, max_i);
for(uint8_t i = max_i; i < 6; i++) {
mac[i] = 0;
}
mac[0] = 0b10;
}
#endif
static void totp_type_code_worker_type_code(TotpBtTypeCodeWorkerContext* context) {
uint8_t i = 0;
do {
@@ -30,7 +51,7 @@ static void totp_type_code_worker_type_code(TotpBtTypeCodeWorkerContext* context
}
static int32_t totp_type_code_worker_callback(void* context) {
furi_assert(context);
furi_check(context);
FuriMutex* context_mutex = furi_mutex_alloc(FuriMutexTypeNormal);
if(context_mutex == NULL) {
return 251;
@@ -74,7 +95,7 @@ void totp_bt_type_code_worker_start(
char* code_buf,
uint8_t code_buf_length,
FuriMutex* code_buf_update_sync) {
furi_assert(context != NULL);
furi_check(context != NULL);
context->string = code_buf;
context->string_length = code_buf_length;
context->string_sync = code_buf_update_sync;
@@ -87,7 +108,7 @@ void totp_bt_type_code_worker_start(
}
void totp_bt_type_code_worker_stop(TotpBtTypeCodeWorkerContext* context) {
furi_assert(context != NULL);
furi_check(context != NULL);
furi_thread_flags_set(furi_thread_get_id(context->thread), TotpBtTypeCodeWorkerEventStop);
furi_thread_join(context->thread);
furi_thread_free(context->thread);
@@ -98,7 +119,7 @@ void totp_bt_type_code_worker_notify(
TotpBtTypeCodeWorkerContext* context,
TotpBtTypeCodeWorkerEvent event,
uint8_t flags) {
furi_assert(context != NULL);
furi_check(context != NULL);
context->flags = flags;
furi_thread_flags_set(furi_thread_get_id(context->thread), event);
}
@@ -114,11 +135,33 @@ TotpBtTypeCodeWorkerContext* totp_bt_type_code_worker_init() {
furi_hal_bt_reinit();
furi_delay_ms(200);
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),
TOTP_BT_WORKER_BT_ADV_NAME_MAX_LEN);
memcpy(
&context->previous_bt_mac[0],
furi_hal_bt_get_profile_mac_addr(FuriHalBtProfileHidKeyboard),
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());
furi_hal_bt_set_profile_adv_name(FuriHalBtProfileHidKeyboard, new_name);
furi_hal_bt_set_profile_mac_addr(FuriHalBtProfileHidKeyboard, context->bt_mac);
#endif
if(!bt_set_profile(context->bt, BtProfileHidKeyboard)) {
FURI_LOG_E(LOGGING_TAG, "Failed to switch BT to keyboard HID profile");
}
furi_hal_bt_start_advertising();
#if TOTP_TARGET_FIRMWARE == TOTP_FIRMWARE_XTREME
bt_enable_peer_key_update(context->bt);
#endif
context->is_advertising = true;
bt_set_status_changed_callback(context->bt, connection_status_changed_callback, context);
@@ -126,7 +169,7 @@ TotpBtTypeCodeWorkerContext* totp_bt_type_code_worker_init() {
}
void totp_bt_type_code_worker_free(TotpBtTypeCodeWorkerContext* context) {
furi_assert(context != NULL);
furi_check(context != NULL);
if(context->thread != NULL) {
totp_bt_type_code_worker_stop(context);
@@ -142,6 +185,11 @@ void totp_bt_type_code_worker_free(TotpBtTypeCodeWorkerContext* context) {
furi_delay_ms(200);
bt_keys_storage_set_default_path(context->bt);
#if TOTP_TARGET_FIRMWARE == TOTP_FIRMWARE_XTREME
furi_hal_bt_set_profile_adv_name(FuriHalBtProfileHidKeyboard, context->previous_bt_name);
furi_hal_bt_set_profile_mac_addr(FuriHalBtProfileHidKeyboard, context->previous_bt_mac);
#endif
if(!bt_set_profile(context->bt, BtProfileSerial)) {
FURI_LOG_E(LOGGING_TAG, "Failed to switch BT to Serial profile");
}
@@ -4,6 +4,12 @@
#include <furi/furi.h>
#include <furi_hal.h>
#include <bt/bt_service/bt.h>
#include "../../features_config.h"
#if TOTP_TARGET_FIRMWARE == TOTP_FIRMWARE_XTREME
#define TOTP_BT_WORKER_BT_ADV_NAME_MAX_LEN 18
#define TOTP_BT_WORKER_BT_MAC_ADDRESS_LEN GAP_MAC_ADDR_SIZE
#endif
typedef uint8_t TotpBtTypeCodeWorkerEvent;
@@ -16,6 +22,11 @@ typedef struct {
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 + 1];
uint8_t previous_bt_mac[TOTP_BT_WORKER_BT_MAC_ADDRESS_LEN];
#endif
} TotpBtTypeCodeWorkerContext;
enum TotpBtTypeCodeWorkerEvents {
+23 -14
View File
@@ -3,17 +3,15 @@
#include <furi_hal.h>
#include "../../services/convert/convert.h"
static const uint8_t hid_number_keys[10] = {
HID_KEYBOARD_0,
HID_KEYBOARD_1,
HID_KEYBOARD_2,
HID_KEYBOARD_3,
HID_KEYBOARD_4,
HID_KEYBOARD_5,
HID_KEYBOARD_6,
HID_KEYBOARD_7,
HID_KEYBOARD_8,
HID_KEYBOARD_9};
static const uint8_t hid_number_keys[] = {
HID_KEYBOARD_0, HID_KEYBOARD_1, HID_KEYBOARD_2, HID_KEYBOARD_3, HID_KEYBOARD_4,
HID_KEYBOARD_5, HID_KEYBOARD_6, HID_KEYBOARD_7, HID_KEYBOARD_8, HID_KEYBOARD_9,
HID_KEYBOARD_A, HID_KEYBOARD_B, HID_KEYBOARD_C, HID_KEYBOARD_D, HID_KEYBOARD_E,
HID_KEYBOARD_F, HID_KEYBOARD_G, HID_KEYBOARD_H, HID_KEYBOARD_I, HID_KEYBOARD_J,
HID_KEYBOARD_K, HID_KEYBOARD_L, HID_KEYBOARD_M, HID_KEYBOARD_N, HID_KEYBOARD_O,
HID_KEYBOARD_P, HID_KEYBOARD_Q, HID_KEYBOARD_R, HID_KEYBOARD_S, HID_KEYBOARD_T,
HID_KEYBOARD_U, HID_KEYBOARD_V, HID_KEYBOARD_W, HID_KEYBOARD_X, HID_KEYBOARD_Y,
HID_KEYBOARD_Z};
static uint32_t get_keystroke_delay(TokenAutomationFeature features) {
if(features & TOKEN_AUTOMATION_FEATURE_TYPE_SLOWER) {
@@ -49,10 +47,18 @@ void totp_type_code_worker_execute_automation(
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 digit = CONVERT_CHAR_TO_DIGIT(string[i]);
if(digit > 9) break;
uint8_t hid_kb_key = hid_number_keys[digit];
uint8_t char_index = CONVERT_CHAR_TO_DIGIT(string[i]);
if(char_index > 9) {
char_index = string[i] - 0x41 + 10;
}
if(char_index > 35) break;
uint8_t hid_kb_key = hid_number_keys[char_index];
totp_type_code_worker_press_key(hid_kb_key, key_press_fn, key_release_fn, features);
furi_delay_ms(get_keystroke_delay(features));
i++;
@@ -68,4 +74,7 @@ void totp_type_code_worker_execute_automation(
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);
}
@@ -41,7 +41,7 @@ static void totp_type_code_worker_type_code(TotpUsbTypeCodeWorkerContext* contex
}
static int32_t totp_type_code_worker_callback(void* context) {
furi_assert(context);
furi_check(context);
FuriMutex* context_mutex = furi_mutex_alloc(FuriMutexTypeNormal);
if(context_mutex == NULL) {
return 251;
@@ -89,7 +89,7 @@ TotpUsbTypeCodeWorkerContext* totp_usb_type_code_worker_start(
}
void totp_usb_type_code_worker_stop(TotpUsbTypeCodeWorkerContext* context) {
furi_assert(context != NULL);
furi_check(context != NULL);
furi_thread_flags_set(furi_thread_get_id(context->thread), TotpUsbTypeCodeWorkerEventStop);
furi_thread_join(context->thread);
furi_thread_free(context->thread);
@@ -101,7 +101,7 @@ void totp_usb_type_code_worker_notify(
TotpUsbTypeCodeWorkerContext* context,
TotpUsbTypeCodeWorkerEvent event,
uint8_t flags) {
furi_assert(context != NULL);
furi_check(context != NULL);
context->flags = flags;
furi_thread_flags_set(furi_thread_get_id(context->thread), event);
}
@@ -111,11 +111,21 @@ void uart_terminal_scene_console_output_on_enter(void* context) {
uart_terminal_uart_set_handle_rx_data_cb(
app->uart, uart_terminal_console_output_handle_rx_data_cb); // setup callback for rx thread
// Send command with newline '\n'
// Send command with CR+LF or newline '\n'
if(app->is_command && app->selected_tx_string) {
uart_terminal_uart_tx(
(uint8_t*)(app->selected_tx_string), strlen(app->selected_tx_string));
uart_terminal_uart_tx((uint8_t*)("\n"), 1);
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);
} else {
uart_terminal_uart_tx(
(uint8_t*)(app->selected_tx_string), strlen(app->selected_tx_string));
uart_terminal_uart_tx((uint8_t*)("\n"), 1);
}
}
}
@@ -144,4 +154,4 @@ void uart_terminal_scene_console_output_on_exit(void* context) {
//if(app->is_command) {
// uart_terminal_uart_tx((uint8_t*)("exit\n"), strlen("exit\n"));
//}
}
}
@@ -31,6 +31,7 @@ const UART_TerminalItem items[NUM_MENU_ITEMS] = {
FOCUS_CONSOLE_TOGGLE,
NO_TIP},
{"Send command", {""}, 1, {""}, INPUT_ARGS, FOCUS_CONSOLE_END, NO_TIP},
{"Send AT command", {""}, 1, {"AT"}, INPUT_ARGS, FOCUS_CONSOLE_END, NO_TIP},
{"Fast cmd",
{"help", "uptime", "date", "df -h", "ps", "dmesg", "reboot", "poweroff"},
8,
@@ -25,7 +25,13 @@ void uart_terminal_scene_text_input_on_enter(void* context) {
// Setup view
UART_TextInput* text_input = app->text_input;
// Add help message to header
uart_text_input_set_header_text(text_input, "Send command to UART");
if(0 == strncmp("AT", app->selected_tx_string, strlen("AT"))) {
app->TERMINAL_MODE = 1;
uart_text_input_set_header_text(text_input, "Send AT command to UART");
} else {
app->TERMINAL_MODE = 0;
uart_text_input_set_header_text(text_input, "Send command to UART");
}
uart_text_input_set_result_callback(
text_input,
uart_terminal_scene_text_input_callback,
+2 -1
View File
@@ -12,7 +12,7 @@
#include <gui/modules/variable_item_list.h>
#include "uart_text_input.h"
#define NUM_MENU_ITEMS (4)
#define NUM_MENU_ITEMS (5)
#define UART_TERMINAL_TEXT_BOX_STORE_SIZE (4096)
#define UART_TERMINAL_TEXT_INPUT_STORE_SIZE (512)
@@ -40,6 +40,7 @@ struct UART_TerminalApp {
bool focus_console_start;
bool show_stopscan_tip;
int BAUDRATE;
int TERMINAL_MODE; //1=AT mode, 0=other mode
};
typedef enum {
+57 -11
View File
@@ -1,6 +1,7 @@
#include "uart_text_input.h"
#include <gui/elements.h>
#include "uart_terminal_icons.h"
#include "uart_terminal_app_i.h"
#include <furi.h>
struct UART_TextInput {
@@ -36,6 +37,8 @@ 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 mode_AT "Send AT command to UART"
#define ENTER_KEY '\r'
#define BACKSPACE_KEY '\b'
@@ -163,6 +166,47 @@ static bool char_is_lowercase(char letter) {
return (letter >= 0x61 && letter <= 0x7A);
}
static bool char_is_uppercase(char letter) {
return (letter >= 0x41 && letter <= 0x5A);
}
static char char_to_lowercase(const char letter) {
switch(letter) {
case ' ':
return 0x5f;
break;
case ')':
return 0x28;
break;
case '}':
return 0x7b;
break;
case ']':
return 0x5b;
break;
case '\\':
return 0x2f;
break;
case ':':
return 0x3b;
break;
case ',':
return 0x2e;
break;
case '?':
return 0x21;
break;
case '>':
return 0x3c;
break;
}
if(char_is_uppercase(letter)) {
return (letter + 0x20);
} else {
return letter;
}
}
static char char_to_uppercase(const char letter) {
switch(letter) {
case '_':
@@ -193,7 +237,7 @@ static char char_to_uppercase(const char letter) {
return 0x3e;
break;
}
if(isalpha(letter)) {
if(char_is_lowercase(letter)) {
return (letter - 0x20);
} else {
return letter;
@@ -209,7 +253,7 @@ static void uart_text_input_backspace_cb(UART_TextInputModel* model) {
static void uart_text_input_view_draw_callback(Canvas* canvas, void* _model) {
UART_TextInputModel* model = _model;
uint8_t text_length = model->text_buffer ? strlen(model->text_buffer) : 0;
//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;
@@ -291,15 +335,12 @@ static void uart_text_input_view_draw_callback(Canvas* canvas, void* _model) {
} else {
canvas_set_color(canvas, ColorBlack);
}
if(model->clear_default_text ||
(text_length == 0 && char_is_lowercase(keys[column].text))) {
if(0 == strcmp(model->header, mode_AT)) {
canvas_draw_glyph(
canvas,
keyboard_origin_x + keys[column].x,
keyboard_origin_y + keys[column].y,
//char_to_uppercase(keys[column].text));
keys[column].text);
char_to_uppercase(keys[column].text));
} else {
canvas_draw_glyph(
canvas,
@@ -372,10 +413,18 @@ static void uart_text_input_handle_ok(
char selected = get_selected_char(model);
uint8_t text_length = strlen(model->text_buffer);
if(shift) {
if(0 == strcmp(model->header, mode_AT)) {
selected = char_to_uppercase(selected);
}
if(shift) {
if(0 == strcmp(model->header, mode_AT)) {
selected = char_to_lowercase(selected);
} else {
selected = char_to_uppercase(selected);
}
}
if(selected == ENTER_KEY) {
if(model->validator_callback &&
(!model->validator_callback(
@@ -392,9 +441,6 @@ static void uart_text_input_handle_ok(
text_length = 0;
}
if(text_length < (model->text_buffer_size - 1)) {
if(text_length == 0 && char_is_lowercase(selected)) {
//selected = char_to_uppercase(selected);
}
model->text_buffer[text_length] = selected;
model->text_buffer[text_length + 1] = 0;
}
@@ -2,11 +2,15 @@
#define TAG "WSProtocolLaCrosse_TX141THBv2"
#define LACROSSE_TX141TH_BV2_BIT_COUNT 41
/*
* Help
* https://github.com/merbanan/rtl_433/blob/master/src/devices/lacrosse_tx141x.c
*
* iiii iiii | bkcc tttt | tttt tttt | hhhh hhhh | cccc cccc | u
* iiii iiii | bkcc tttt | tttt tttt | hhhh hhhh | cccc cccc | u - 41 bit
* or
* iiii iiii | bkcc tttt | tttt tttt | hhhh hhhh | cccc cccc | -40 bit
* - i: identification; changes on battery switch
* - c: lfsr_digest8_reflect;
* - u: unknown;
@@ -17,10 +21,10 @@
*/
static const SubGhzBlockConst ws_protocol_lacrosse_tx141thbv2_const = {
.te_short = 250,
.te_long = 500,
.te_short = 208,
.te_long = 417,
.te_delta = 120,
.min_count_bit_for_found = 41,
.min_count_bit_for_found = 40,
};
struct WSProtocolDecoderLaCrosse_TX141THBv2 {
@@ -102,14 +106,14 @@ void ws_protocol_decoder_lacrosse_tx141thbv2_reset(void* context) {
static bool
ws_protocol_lacrosse_tx141thbv2_check_crc(WSProtocolDecoderLaCrosse_TX141THBv2* instance) {
if(!instance->decoder.decode_data) return false;
uint8_t msg[] = {
instance->decoder.decode_data >> 33,
instance->decoder.decode_data >> 25,
instance->decoder.decode_data >> 17,
instance->decoder.decode_data >> 9};
uint64_t data = instance->decoder.decode_data;
if(instance->decoder.decode_count_bit == LACROSSE_TX141TH_BV2_BIT_COUNT) {
data >>= 1;
}
uint8_t msg[] = {data >> 32, data >> 24, data >> 16, data >> 8};
uint8_t crc = subghz_protocol_blocks_lfsr_digest8_reflect(msg, 4, 0x31, 0xF4);
return (crc == ((instance->decoder.decode_data >> 1) & 0xFF));
return (crc == (data & 0xFF));
}
/**
@@ -117,14 +121,43 @@ static bool
* @param instance Pointer to a WSBlockGeneric* instance
*/
static void ws_protocol_lacrosse_tx141thbv2_remote_controller(WSBlockGeneric* instance) {
instance->id = instance->data >> 33;
instance->battery_low = (instance->data >> 32) & 1;
instance->btn = (instance->data >> 31) & 1;
instance->channel = ((instance->data >> 29) & 0x03) + 1;
instance->temp = ((float)((instance->data >> 17) & 0x0FFF) - 500.0f) / 10.0f;
instance->humidity = (instance->data >> 9) & 0xFF;
uint64_t data = instance->data;
if(instance->data_count_bit == LACROSSE_TX141TH_BV2_BIT_COUNT) {
data >>= 1;
}
instance->id = data >> 32;
instance->battery_low = (data >> 31) & 1;
instance->btn = (data >> 30) & 1;
instance->channel = ((data >> 28) & 0x03) + 1;
instance->temp = ((float)((data >> 16) & 0x0FFF) - 500.0f) / 10.0f;
instance->humidity = (data >> 8) & 0xFF;
}
/**
* Analysis of received data
* @param instance Pointer to a WSBlockGeneric* instance
*/
static bool ws_protocol_decoder_lacrosse_tx141thbv2_add_bit(
WSProtocolDecoderLaCrosse_TX141THBv2* instance,
uint32_t te_last,
uint32_t te_current) {
furi_assert(instance);
bool ret = false;
if(DURATION_DIFF(
te_last + te_current,
ws_protocol_lacrosse_tx141thbv2_const.te_short +
ws_protocol_lacrosse_tx141thbv2_const.te_long) <
ws_protocol_lacrosse_tx141thbv2_const.te_delta * 2) {
if(te_last > te_current) {
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
} else {
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
}
ret = true;
}
return ret;
}
void ws_protocol_decoder_lacrosse_tx141thbv2_feed(void* context, bool level, uint32_t duration) {
furi_assert(context);
WSProtocolDecoderLaCrosse_TX141THBv2* instance = context;
@@ -132,7 +165,7 @@ void ws_protocol_decoder_lacrosse_tx141thbv2_feed(void* context, bool level, uin
switch(instance->decoder.parser_step) {
case LaCrosse_TX141THBv2DecoderStepReset:
if((level) &&
(DURATION_DIFF(duration, ws_protocol_lacrosse_tx141thbv2_const.te_short * 3) <
(DURATION_DIFF(duration, ws_protocol_lacrosse_tx141thbv2_const.te_short * 4) <
ws_protocol_lacrosse_tx141thbv2_const.te_delta * 2)) {
instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepCheckPreambule;
instance->decoder.te_last = duration;
@@ -146,33 +179,17 @@ void ws_protocol_decoder_lacrosse_tx141thbv2_feed(void* context, bool level, uin
} else {
if((DURATION_DIFF(
instance->decoder.te_last,
ws_protocol_lacrosse_tx141thbv2_const.te_short * 3) <
ws_protocol_lacrosse_tx141thbv2_const.te_short * 4) <
ws_protocol_lacrosse_tx141thbv2_const.te_delta * 2) &&
(DURATION_DIFF(duration, ws_protocol_lacrosse_tx141thbv2_const.te_short * 3) <
(DURATION_DIFF(duration, ws_protocol_lacrosse_tx141thbv2_const.te_short * 4) <
ws_protocol_lacrosse_tx141thbv2_const.te_delta * 2)) {
//Found preambule
instance->header_count++;
} else if(instance->header_count == 4) {
if((DURATION_DIFF(
instance->decoder.te_last,
ws_protocol_lacrosse_tx141thbv2_const.te_short) <
ws_protocol_lacrosse_tx141thbv2_const.te_delta) &&
(DURATION_DIFF(duration, ws_protocol_lacrosse_tx141thbv2_const.te_long) <
ws_protocol_lacrosse_tx141thbv2_const.te_delta)) {
instance->decoder.decode_data = 0;
instance->decoder.decode_count_bit = 0;
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepSaveDuration;
} else if(
(DURATION_DIFF(
instance->decoder.te_last,
ws_protocol_lacrosse_tx141thbv2_const.te_long) <
ws_protocol_lacrosse_tx141thbv2_const.te_delta) &&
(DURATION_DIFF(duration, ws_protocol_lacrosse_tx141thbv2_const.te_short) <
ws_protocol_lacrosse_tx141thbv2_const.te_delta)) {
instance->decoder.decode_data = 0;
instance->decoder.decode_count_bit = 0;
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
if(ws_protocol_decoder_lacrosse_tx141thbv2_add_bit(
instance, instance->decoder.te_last, duration)) {
instance->decoder.decode_data = instance->decoder.decode_data & 1;
instance->decoder.decode_count_bit = 1;
instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepSaveDuration;
} else {
instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepReset;
@@ -198,37 +215,26 @@ void ws_protocol_decoder_lacrosse_tx141thbv2_feed(void* context, bool level, uin
instance->decoder.te_last,
ws_protocol_lacrosse_tx141thbv2_const.te_short * 3) <
ws_protocol_lacrosse_tx141thbv2_const.te_delta * 2) &&
(DURATION_DIFF(duration, ws_protocol_lacrosse_tx141thbv2_const.te_short * 3) <
(DURATION_DIFF(duration, ws_protocol_lacrosse_tx141thbv2_const.te_short * 4) <
ws_protocol_lacrosse_tx141thbv2_const.te_delta * 2))) {
if((instance->decoder.decode_count_bit ==
ws_protocol_lacrosse_tx141thbv2_const.min_count_bit_for_found) &&
ws_protocol_lacrosse_tx141thbv2_check_crc(instance)) {
instance->generic.data = instance->decoder.decode_data;
instance->generic.data_count_bit = instance->decoder.decode_count_bit;
ws_protocol_lacrosse_tx141thbv2_remote_controller(&instance->generic);
if(instance->base.callback)
instance->base.callback(&instance->base, instance->base.context);
ws_protocol_lacrosse_tx141thbv2_const.min_count_bit_for_found) ||
(instance->decoder.decode_count_bit == LACROSSE_TX141TH_BV2_BIT_COUNT)) {
if(ws_protocol_lacrosse_tx141thbv2_check_crc(instance)) {
instance->generic.data = instance->decoder.decode_data;
instance->generic.data_count_bit = instance->decoder.decode_count_bit;
ws_protocol_lacrosse_tx141thbv2_remote_controller(&instance->generic);
if(instance->base.callback)
instance->base.callback(&instance->base, instance->base.context);
}
instance->decoder.decode_data = 0;
instance->decoder.decode_count_bit = 0;
instance->header_count = 1;
instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepCheckPreambule;
break;
}
instance->decoder.decode_data = 0;
instance->decoder.decode_count_bit = 0;
instance->header_count = 1;
instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepCheckPreambule;
break;
} else if(
(DURATION_DIFF(
instance->decoder.te_last, ws_protocol_lacrosse_tx141thbv2_const.te_short) <
ws_protocol_lacrosse_tx141thbv2_const.te_delta) &&
(DURATION_DIFF(duration, ws_protocol_lacrosse_tx141thbv2_const.te_long) <
ws_protocol_lacrosse_tx141thbv2_const.te_delta)) {
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepSaveDuration;
} else if(
(DURATION_DIFF(
instance->decoder.te_last, ws_protocol_lacrosse_tx141thbv2_const.te_long) <
ws_protocol_lacrosse_tx141thbv2_const.te_delta) &&
(DURATION_DIFF(duration, ws_protocol_lacrosse_tx141thbv2_const.te_short) <
ws_protocol_lacrosse_tx141thbv2_const.te_delta)) {
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
} else if(ws_protocol_decoder_lacrosse_tx141thbv2_add_bit(
instance, instance->decoder.te_last, duration)) {
instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepSaveDuration;
} else {
instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepReset;
+2 -2
View File
@@ -80,9 +80,9 @@ static void gpio_usb_uart_draw_callback(Canvas* canvas, void* _model) {
canvas_draw_icon(canvas, 48, 14, &I_ArrowUpEmpty_14x15);
if(model->rx_active)
canvas_draw_icon(canvas, 48, 34, &I_ArrowDownFilled_14x15);
canvas_draw_icon_ex(canvas, 48, 34, &I_ArrowUpFilled_14x15, IconRotation180);
else
canvas_draw_icon(canvas, 48, 34, &I_ArrowDownEmpty_14x15);
canvas_draw_icon_ex(canvas, 48, 34, &I_ArrowUpEmpty_14x15, IconRotation180);
}
static bool gpio_usb_uart_input_callback(InputEvent* event, void* context) {
@@ -35,6 +35,12 @@ typedef enum {
SubGhzSpeakerStateEnable,
} SubGhzSpeakerState;
/** SubGhzStarLineIgnore state */
typedef enum {
SubGhzStarLineIgnoreDisable,
SubGhzStarLineIgnoreEnable,
} SubGhzStarLineIgnoreState;
/** SubGhzRxKeyState state */
typedef enum {
SubGhzRxKeyStateIDLE,
@@ -147,6 +147,16 @@ void subghz_scene_receiver_on_enter(void* context) {
subghz_receiver_set_rx_callback(
subghz->txrx->receiver, subghz_scene_add_to_history_callback, subghz);
if(subghz->txrx->starline_state == SubGhzStarLineIgnoreEnable) {
SubGhzProtocolDecoderBase* protocoldecoderbase = NULL;
protocoldecoderbase =
subghz_receiver_search_decoder_base_by_name(subghz->txrx->receiver, "Star Line");
if(protocoldecoderbase) {
subghz_protocol_decoder_base_set_decoder_callback(
protocoldecoderbase, NULL, subghz->txrx->receiver);
}
}
subghz->state_notifications = SubGhzNotificationStateRx;
if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) {
subghz_rx_end(subghz);
@@ -6,6 +6,7 @@ enum SubGhzSettingIndex {
SubGhzSettingIndexHopping,
SubGhzSettingIndexModulation,
SubGhzSettingIndexBinRAW,
SubGhzSettingIndexIgnoreStarline,
SubGhzSettingIndexSound,
SubGhzSettingIndexLock,
SubGhzSettingIndexRAWThresholdRSSI,
@@ -68,6 +69,15 @@ 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] = {
"OFF",
"ON",
};
const uint32_t star_line_value[STAR_LINE_COUNT] = {
SubGhzStarLineIgnoreDisable,
SubGhzStarLineIgnoreEnable,
};
uint8_t subghz_scene_receiver_config_next_frequency(const uint32_t value, void* context) {
furi_assert(context);
@@ -217,6 +227,14 @@ static void subghz_scene_receiver_config_set_raw_threshold_rssi(VariableItem* it
subghz->txrx->raw_threshold_rssi = raw_threshold_rssi_value[index];
}
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];
}
static void subghz_scene_receiver_config_var_list_enter_callback(void* context, uint32_t index) {
furi_assert(context);
SubGhz* subghz = context;
@@ -291,6 +309,21 @@ void subghz_scene_receiver_config_on_enter(void* context) {
variable_item_set_current_value_text(item, bin_raw_text[value_index]);
}
if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) !=
SubGhzCustomEventManagerSet) {
item = variable_item_list_add(
subghz->variable_item_list,
"Ignore StarLine:",
STAR_LINE_COUNT,
subghz_scene_receiver_config_set_starline,
subghz);
value_index =
value_index_uint32(subghz->txrx->starline_state, star_line_value, STAR_LINE_COUNT);
variable_item_set_current_value_index(item, value_index);
variable_item_set_current_value_text(item, star_line_text[value_index]);
}
// Enable speaker, will send all incoming noises and signals to speaker so you can listen how your remote sounds like :)
item = variable_item_list_add(
subghz->variable_item_list,
@@ -558,7 +558,7 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) {
subghz,
"AM650",
433920000,
(key & 0x00FFFFFF) | 0x04000000,
(key & 0x000FFFFF) | 0x04700000,
0x2,
0x0021,
"AN-Motors")) {
+1
View File
@@ -73,6 +73,7 @@ struct SubGhzTxRx {
SubGhzTxRxState txrx_state;
SubGhzHopperState hopper_state;
SubGhzSpeakerState speaker_state;
SubGhzStarLineIgnoreState starline_state;
uint8_t hopper_timeout;
uint8_t hopper_idx_frequency;
SubGhzRxKeyState rx_key_state;
+2 -2
View File
@@ -36,7 +36,7 @@ static void desktop_loader_callback(const void* message, void* context) {
static void desktop_lock_icon_draw_callback(Canvas* canvas, void* context) {
UNUSED(context);
furi_assert(canvas);
canvas_draw_icon(canvas, 0, 0, &I_Lock_8x8);
canvas_draw_icon(canvas, 0, 0, &I_Lock_7x8);
}
static bool desktop_custom_event_callback(void* context, uint32_t event) {
@@ -216,7 +216,7 @@ Desktop* desktop_alloc() {
// Lock icon
desktop->lock_icon_viewport = view_port_alloc();
view_port_set_width(desktop->lock_icon_viewport, icon_get_width(&I_Lock_8x8));
view_port_set_width(desktop->lock_icon_viewport, icon_get_width(&I_Lock_7x8));
view_port_draw_callback_set(
desktop->lock_icon_viewport, desktop_lock_icon_draw_callback, desktop);
view_port_enabled_set(desktop->lock_icon_viewport, false);
@@ -115,16 +115,18 @@ static void desktop_view_pin_input_draw_cells(Canvas* canvas, DesktopViewPinInpu
} else {
switch(model->pin.data[i]) {
case InputKeyDown:
canvas_draw_icon(canvas, x + 3, y + 2, &I_Pin_arrow_down_7x9);
canvas_draw_icon_ex(
canvas, x + 3, y + 2, &I_Pin_arrow_up_7x9, IconRotation180);
break;
case InputKeyUp:
canvas_draw_icon(canvas, x + 3, y + 2, &I_Pin_arrow_up_7x9);
canvas_draw_icon_ex(canvas, x + 3, y + 2, &I_Pin_arrow_up_7x9, IconRotation0);
break;
case InputKeyLeft:
canvas_draw_icon(canvas, x + 2, y + 3, &I_Pin_arrow_left_9x7);
canvas_draw_icon_ex(
canvas, x + 2, y + 3, &I_Pin_arrow_up_7x9, IconRotation270);
break;
case InputKeyRight:
canvas_draw_icon(canvas, x + 2, y + 3, &I_Pin_arrow_right_9x7);
canvas_draw_icon_ex(canvas, x + 2, y + 3, &I_Pin_arrow_up_7x9, IconRotation90);
break;
default:
furi_assert(0);
@@ -147,7 +149,8 @@ static void desktop_view_pin_input_draw(Canvas* canvas, void* context) {
desktop_view_pin_input_draw_cells(canvas, model);
if((model->pin.length > 0) && !model->locked_input) {
canvas_draw_icon(canvas, 4, 53, &I_Pin_back_full_40x8);
canvas_draw_icon(canvas, 4, 53, &I_Pin_back_arrow_10x8);
canvas_draw_str(canvas, 16, 60, "= clear");
}
if(model->button_label && ((model->pin.length >= MIN_PIN_SIZE) || model->locked_input)) {
+131 -5
View File
@@ -236,7 +236,7 @@ void canvas_draw_bitmap(
y += canvas->offset_y;
uint8_t* bitmap_data = NULL;
compress_icon_decode(canvas->compress_icon, compressed_bitmap_data, &bitmap_data);
u8g2_DrawXBM(&canvas->fb, x, y, width, height, bitmap_data);
canvas_draw_u8g2_bitmap(&canvas->fb, x, y, width, height, bitmap_data, IconRotation0);
}
void canvas_draw_icon_animation(
@@ -252,13 +252,138 @@ void canvas_draw_icon_animation(
uint8_t* icon_data = NULL;
compress_icon_decode(
canvas->compress_icon, icon_animation_get_data(icon_animation), &icon_data);
u8g2_DrawXBM(
canvas_draw_u8g2_bitmap(
&canvas->fb,
x,
y,
icon_animation_get_width(icon_animation),
icon_animation_get_height(icon_animation),
icon_data);
icon_data,
IconRotation0);
}
static void canvas_draw_u8g2_bitmap_int(
u8g2_t* u8g2,
u8g2_uint_t x,
u8g2_uint_t y,
u8g2_uint_t w,
u8g2_uint_t h,
bool mirror,
bool rotation,
const uint8_t* bitmap) {
u8g2_uint_t blen;
blen = w;
blen += 7;
blen >>= 3;
if(rotation && !mirror) {
x += w + 1;
} else if(mirror && !rotation) {
y += h - 1;
}
while(h > 0) {
const uint8_t* b = bitmap;
uint16_t len = w;
uint16_t x0 = x;
uint16_t y0 = y;
uint8_t mask;
uint8_t color = u8g2->draw_color;
uint8_t ncolor = (color == 0 ? 1 : 0);
mask = 1;
while(len > 0) {
if(u8x8_pgm_read(b) & mask) {
u8g2->draw_color = color;
u8g2_DrawHVLine(u8g2, x0, y0, 1, 0);
} else if(u8g2->bitmap_transparency == 0) {
u8g2->draw_color = ncolor;
u8g2_DrawHVLine(u8g2, x0, y0, 1, 0);
}
if(rotation) {
y0++;
} else {
x0++;
}
mask <<= 1;
if(mask == 0) {
mask = 1;
b++;
}
len--;
}
u8g2->draw_color = color;
bitmap += blen;
if(mirror) {
if(rotation) {
x++;
} else {
y--;
}
} else {
if(rotation) {
x--;
} else {
y++;
}
}
h--;
}
}
void canvas_draw_u8g2_bitmap(
u8g2_t* u8g2,
u8g2_uint_t x,
u8g2_uint_t y,
u8g2_uint_t w,
u8g2_uint_t h,
const uint8_t* bitmap,
IconRotation rotation) {
u8g2_uint_t blen;
blen = w;
blen += 7;
blen >>= 3;
#ifdef U8G2_WITH_INTERSECTION
if(u8g2_IsIntersection(u8g2, x, y, x + w, y + h) == 0) return;
#endif /* U8G2_WITH_INTERSECTION */
switch(rotation) {
case IconRotation0:
canvas_draw_u8g2_bitmap_int(u8g2, x, y, w, h, 0, 0, bitmap);
break;
case IconRotation90:
canvas_draw_u8g2_bitmap_int(u8g2, x, y, w, h, 0, 1, bitmap);
break;
case IconRotation180:
canvas_draw_u8g2_bitmap_int(u8g2, x, y, w, h, 1, 0, bitmap);
break;
case IconRotation270:
canvas_draw_u8g2_bitmap_int(u8g2, x, y, w, h, 1, 1, bitmap);
break;
default:
break;
}
}
void canvas_draw_icon_ex(
Canvas* canvas,
uint8_t x,
uint8_t y,
const Icon* icon,
IconRotation rotation) {
furi_assert(canvas);
furi_assert(icon);
x += canvas->offset_x;
y += canvas->offset_y;
uint8_t* icon_data = NULL;
compress_icon_decode(canvas->compress_icon, icon_get_data(icon), &icon_data);
canvas_draw_u8g2_bitmap(
&canvas->fb, x, y, icon_get_width(icon), icon_get_height(icon), icon_data, rotation);
}
void canvas_draw_icon(Canvas* canvas, uint8_t x, uint8_t y, const Icon* icon) {
@@ -269,7 +394,8 @@ void canvas_draw_icon(Canvas* canvas, uint8_t x, uint8_t y, const Icon* icon) {
y += canvas->offset_y;
uint8_t* icon_data = NULL;
compress_icon_decode(canvas->compress_icon, icon_get_data(icon), &icon_data);
u8g2_DrawXBM(&canvas->fb, x, y, icon_get_width(icon), icon_get_height(icon), icon_data);
canvas_draw_u8g2_bitmap(
&canvas->fb, x, y, icon_get_width(icon), icon_get_height(icon), icon_data, IconRotation0);
}
void canvas_draw_dot(Canvas* canvas, uint8_t x, uint8_t y) {
@@ -379,7 +505,7 @@ void canvas_draw_xbm(
furi_assert(canvas);
x += canvas->offset_x;
y += canvas->offset_y;
u8g2_DrawXBM(&canvas->fb, x, y, w, h, bitmap);
canvas_draw_u8g2_bitmap(&canvas->fb, x, y, w, h, bitmap, IconRotation0);
}
void canvas_draw_glyph(Canvas* canvas, uint8_t x, uint8_t y, uint16_t ch) {
+32
View File
@@ -65,6 +65,22 @@ typedef struct {
uint8_t descender;
} CanvasFontParameters;
/** Icon flip */
typedef enum {
IconFlipNone,
IconFlipHorizontal,
IconFlipVertical,
IconFlipBoth,
} IconFlip;
/** Icon rotation */
typedef enum {
IconRotation0,
IconRotation90,
IconRotation180,
IconRotation270,
} IconRotation;
/** Canvas anonymous structure */
typedef struct Canvas Canvas;
@@ -218,6 +234,22 @@ void canvas_draw_bitmap(
uint8_t height,
const uint8_t* compressed_bitmap_data);
/** Draw icon at position defined by x,y with rotation and flip.
*
* @param canvas Canvas instance
* @param x x coordinate
* @param y y coordinate
* @param icon Icon instance
* @param flip IconFlip
* @param rotation IconRotation
*/
void canvas_draw_icon_ex(
Canvas* canvas,
uint8_t x,
uint8_t y,
const Icon* icon,
IconRotation rotation);
/** Draw animation at position defined by x,y.
*
* @param canvas Canvas instance
+19
View File
@@ -83,6 +83,25 @@ void canvas_set_orientation(Canvas* canvas, CanvasOrientation orientation);
*/
CanvasOrientation canvas_get_orientation(const Canvas* canvas);
/** Draw a u8g2 bitmap
*
* @param canvas Canvas instance
* @param x x coordinate
* @param y y coordinate
* @param width width
* @param height height
* @param bitmap bitmap
* @param rotation rotation
*/
void canvas_draw_u8g2_bitmap(
u8g2_t* u8g2,
uint8_t x,
uint8_t y,
uint8_t width,
uint8_t height,
const uint8_t* bitmap,
uint8_t rotation);
#ifdef __cplusplus
}
#endif
@@ -4,7 +4,7 @@
#include <flipper_application/api_hashtable/compilesort.hpp>
/* Generated table */
#include <symbols.h>
#include <firmware_api_table.h>
static_assert(!has_hash_collisions(elf_api_table), "Detected API method hash collision!");