Merge remote-tracking branch 'origin/release-candidate' into release

This commit is contained in:
Aleksandr Kutuzov 2021-08-12 00:18:56 +03:00
commit 073c87f37d
253 changed files with 3203 additions and 1511 deletions

View File

@ -14,8 +14,11 @@ jobs:
- name: 'Decontaminate previous build leftovers' - name: 'Decontaminate previous build leftovers'
run: | run: |
git submodule status \ if [ -d .git ]
|| git checkout `git rev-list --max-parents=0 HEAD | tail -n 1` then
git submodule status \
|| git checkout `git rev-list --max-parents=0 HEAD | tail -n 1`
fi
- name: 'Checkout code' - name: 'Checkout code'
uses: actions/checkout@v2 uses: actions/checkout@v2
@ -44,7 +47,7 @@ jobs:
- name: 'Generate branch suffix' - name: 'Generate branch suffix'
if: startsWith(github.ref, 'refs/tags/') != true if: startsWith(github.ref, 'refs/tags/') != true
run: echo "SUFFIX=$(date +'%Y-%m-%d')-$(git rev-parse --short HEAD)" >> $GITHUB_ENV run: echo "SUFFIX=$(git rev-parse --abbrev-ref HEAD)-$(date +'%d%m%Y')-$(git rev-parse --short HEAD)" >> $GITHUB_ENV
- name: 'Build bootloader in docker' - name: 'Build bootloader in docker'
uses: ./.github/actions/docker uses: ./.github/actions/docker

View File

@ -12,16 +12,19 @@ jobs:
- name: 'Cleanup workspace' - name: 'Cleanup workspace'
uses: AutoModality/action-clean@v1 uses: AutoModality/action-clean@v1
- name: 'Decontaminate previous build leftovers'
run: |
if [ -d .git ]
then
git submodule status \
|| git checkout `git rev-list --max-parents=0 HEAD | tail -n 1`
fi
- name: 'Checkout code' - name: 'Checkout code'
uses: actions/checkout@v2 uses: actions/checkout@v2
with: with:
fetch-depth: 0 fetch-depth: 0
submodules: true
- name: 'Checkout submodules: update'
run: git submodule update --init --recursive
- name: 'Checkout submodules: sync'
run: git submodule sync
- name: 'Docker cache' - name: 'Docker cache'
uses: satackey/action-docker-layer-caching@v0.0.11 uses: satackey/action-docker-layer-caching@v0.0.11

View File

@ -10,6 +10,24 @@ jobs:
lint_python: lint_python:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - name: 'Cleanup workspace'
- uses: actions/setup-python@v2 uses: AutoModality/action-clean@v1
- uses: psf/black@20.8b1
- name: 'Decontaminate previous build leftovers'
run: |
if [ -d .git ]
then
git submodule status \
|| git checkout `git rev-list --max-parents=0 HEAD | tail -n 1`
fi
- name: 'Checkout code'
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: 'Setup python'
uses: actions/setup-python@v2
- name: 'Check python code with black'
uses: psf/black@20.8b1

View File

@ -39,6 +39,7 @@ int32_t lfrfid_debug_app(void* p);
int32_t storage_app(void* p); int32_t storage_app(void* p);
int32_t storage_app_test(void* p); int32_t storage_app_test(void* p);
int32_t dialogs_app(void* p); int32_t dialogs_app(void* p);
int32_t power_observer(void* p);
// On system start hooks declaration // On system start hooks declaration
void irda_cli_init(); void irda_cli_init();
@ -91,6 +92,10 @@ const FlipperApplication FLIPPER_SERVICES[] = {
{.app = power_task, .name = "power_task", .stack_size = 1024, .icon = &A_Plugins_14}, {.app = power_task, .name = "power_task", .stack_size = 1024, .icon = &A_Plugins_14},
#endif #endif
#ifdef SRV_POWER_OBSERVER
{.app = power_observer, .name = "power_observer", .stack_size = 1024, .icon = &A_Plugins_14},
#endif
#ifdef SRV_BT #ifdef SRV_BT
{.app = bt_task, .name = "bt_task", .stack_size = 1024, .icon = &A_Plugins_14}, {.app = bt_task, .name = "bt_task", .stack_size = 1024, .icon = &A_Plugins_14},
#endif #endif
@ -139,7 +144,7 @@ const FlipperApplication FLIPPER_SERVICES[] = {
#endif #endif
#ifdef SRV_KEYPAD_TEST #ifdef SRV_KEYPAD_TEST
{.app = keypad_test, .name = "keypad_test", .icon = &A_Plugins_14}, {.app = keypad_test, .name = "keypad_test", .stack_size = 1024, .icon = &A_Plugins_14},
#endif #endif
#ifdef SRV_ACCESSOR #ifdef SRV_ACCESSOR
@ -268,7 +273,7 @@ const FlipperApplication FLIPPER_DEBUG_APPS[] = {
#endif #endif
#ifdef APP_KEYPAD_TEST #ifdef APP_KEYPAD_TEST
{.app = keypad_test, .name = "keypad_test", .icon = &A_Plugins_14}, {.app = keypad_test, .name = "keypad_test", .stack_size = 1024, .icon = &A_Plugins_14},
#endif #endif
#ifdef APP_ACCESSOR #ifdef APP_ACCESSOR

View File

@ -20,6 +20,7 @@ SRV_DOLPHIN = 1
SRV_NOTIFICATION = 1 SRV_NOTIFICATION = 1
SRV_STORAGE = 1 SRV_STORAGE = 1
SRV_DIALOGS = 1 SRV_DIALOGS = 1
SRV_POWER_OBSERVER = 1
# Main Apps # Main Apps
APP_IRDA = 1 APP_IRDA = 1
@ -59,6 +60,12 @@ SRV_CLI = 1
CFLAGS += -DSRV_POWER CFLAGS += -DSRV_POWER
endif endif
SRV_POWER_OBSERVER ?= 0
ifeq ($(SRV_POWER_OBSERVER), 1)
SRV_POWER = 1
CFLAGS += -DSRV_POWER_OBSERVER
endif
SRV_BT ?= 0 SRV_BT ?= 0
ifeq ($(SRV_BT), 1) ifeq ($(SRV_BT), 1)
SRV_CLI = 1 SRV_CLI = 1

View File

@ -297,7 +297,8 @@ static void archive_enter_text_input(ArchiveApp* archive) {
archive_text_input_callback, archive_text_input_callback,
archive, archive,
archive->browser.text_input_buffer, archive->browser.text_input_buffer,
MAX_NAME_LEN); MAX_NAME_LEN,
false);
view_dispatcher_switch_to_view(archive->view_dispatcher, ArchiveViewTextInput); view_dispatcher_switch_to_view(archive->view_dispatcher, ArchiveViewTextInput);
} }

View File

@ -1,7 +1,6 @@
#include "cli_i.h" #include "cli_i.h"
#include "cli_commands.h" #include "cli_commands.h"
#include <version.h>
#include <api-hal-version.h> #include <api-hal-version.h>
#include <loader/loader.h> #include <loader/loader.h>

View File

@ -94,23 +94,30 @@ void cli_command_help(Cli* cli, string_t args, void* context) {
(void)args; (void)args;
printf("Commands we have:"); printf("Commands we have:");
// Get the middle element // Command count
CliCommandTree_it_t it_mid; const size_t commands_count = CliCommandTree_size(cli->commands);
uint8_t cmd_num = CliCommandTree_size(cli->commands); const size_t commands_count_mid = commands_count / 2 + commands_count % 2;
uint8_t i = cmd_num / 2 + cmd_num % 2;
for(CliCommandTree_it(it_mid, cli->commands); i; --i, CliCommandTree_next(it_mid))
;
// Use 2 iterators from start and middle to show 2 columns // Use 2 iterators from start and middle to show 2 columns
CliCommandTree_it_t it_i; CliCommandTree_it_t it_left;
CliCommandTree_it_t it_j; CliCommandTree_it(it_left, cli->commands);
for(CliCommandTree_it(it_i, cli->commands), CliCommandTree_it_set(it_j, it_mid); CliCommandTree_it_t it_right;
!CliCommandTree_it_equal_p(it_i, it_mid); CliCommandTree_it(it_right, cli->commands);
CliCommandTree_next(it_i), CliCommandTree_next(it_j)) { for(size_t i = 0; i < commands_count_mid; i++) CliCommandTree_next(it_right);
CliCommandTree_itref_t* ref = CliCommandTree_ref(it_i);
// Iterate throw tree
for(size_t i = 0; i < commands_count_mid; i++) {
printf("\r\n"); printf("\r\n");
printf("%-30s", string_get_cstr(ref->key_ptr[0])); // Left Column
ref = CliCommandTree_ref(it_j); if(!CliCommandTree_end_p(it_left)) {
printf(string_get_cstr(ref->key_ptr[0])); printf("%-30s", string_get_cstr(*CliCommandTree_ref(it_left)->key_ptr));
CliCommandTree_next(it_left);
}
// Right Column
if(!CliCommandTree_end_p(it_right)) {
printf(string_get_cstr(*CliCommandTree_ref(it_right)->key_ptr));
CliCommandTree_next(it_right);
}
}; };
if(string_size(args) > 0) { if(string_size(args) > 0) {

View File

@ -3,7 +3,6 @@
#include <gui/gui.h> #include <gui/gui.h>
#include <gui/elements.h> #include <gui/elements.h>
#include <api-hal.h> #include <api-hal.h>
#include <version.h>
#include <api-hal-version.h> #include <api-hal-version.h>
static char* Lockmenu_Items[3] = {"Lock", "Set PIN", "DUMB mode"}; static char* Lockmenu_Items[3] = {"Lock", "Set PIN", "DUMB mode"};
@ -40,7 +39,7 @@ void dolphin_view_first_start_draw(Canvas* canvas, void* model) {
"I am", "I am",
my_name ? my_name : "Unknown", my_name ? my_name : "Unknown",
",\ncyberdolphin\nliving in your\npocket >"); ",\ncyberdolphin\nliving in your\npocket >");
canvas_draw_icon(canvas, 0, height - 48, &I_DolphinFirstStart5_45x53); canvas_draw_icon(canvas, 0, height - 48, &I_DolphinFirstStart5_54x49);
elements_multiline_text_framed(canvas, 60, 17, buf); elements_multiline_text_framed(canvas, 60, 17, buf);
} else if(m->page == 6) { } else if(m->page == 6) {
canvas_draw_icon(canvas, 0, height - 48, &I_DolphinFirstStart6_58x54); canvas_draw_icon(canvas, 0, height - 48, &I_DolphinFirstStart6_58x54);

View File

@ -1,15 +1,12 @@
#pragma once #pragma once
static const char* emotes_list[] = { // temp
"(O_o)", "(!_?)", "(^_^)", "(*__*)", "(@_@)", "(X_x)", "(>_<)", "(^ ^)", "(^_^)", const char* console_emotes[] = {
"(-_-)", "(~_~)", "(#^.^#)", "(^ ^)", "(^.^)", "(-.-)", "zZzZ", "(^_-)", "(^_-)", "Run it, m8",
"(+_+)", "(+o+)", "(' ')", "('-')", "('.')", "('_')", "(* > *)", "(o o)", "(^_^)", "Lets GOOOO",
"(^O^)", "(^o^)", "(^o^)", "(._.)", "(_^_)", "('_')", "('_;)", "(T_T)", "(;_;)", "Click it, buddy",
"(ー_ー)", "(-.-)", "(^o^)", "(-_-)", "(=_=)", "(=^ ^=)", "(. .)", "(._.)", "( ^m^)", "I wanna play",
"(?_?)", "(*^_^*)", "(^<^)", "(^.^)", "(^·^)", "(^.^)", "(^_^.)", "(^_^)", "(^^)", "Wtf is this?",
"(^J^)", "(*^.^*)", "(#^.^#)", "(~o~)", "(^o^)", "(-o-)", "(^. ^)", "(^o^)", "(*^0^*)", "Just do it",
"(*_*)", "(~ o ~)", "(~_~)", "(p_-)", "d[-_-]b", "(^0_0^)", "- ^ -"}; "JUST DO IT!",
static const char* dialogues_list[] = {
"Let's hack!\n\nbla bla bla\nbla bla..",
}; };

View File

@ -1,49 +1,38 @@
#include <gui/elements.h> #include <gui/elements.h>
#include "applications.h" #include "applications.h"
#include "items_i.h" #include "items_i.h"
#include "emotes.h"
#include <gui/icon_i.h>
const Item TV = { const Item Food = {
.layer = 7,
.timeout = 10,
.x = 160,
.y = 34,
.icon = &I_TV_20x24,
.action_name = "Use",
.draw = draw_tv,
.callback = smash_tv};
const Item Painting = {
.layer = 3,
.timeout = 20,
.x = 160,
.y = 10,
.icon = &I_Home_painting_17x20,
.action_name = "Inspect",
.draw = NULL,
.callback = inspect_painting};
const Item Sofa = {
.layer = 4, .layer = 4,
.timeout = 100, .timeout = 100,
.x = 250, .pos =
.y = 34, {
.icon = &I_Sofa_40x13, .x = 0,
.action_name = "Sit", .y = 90,
.draw = NULL, },
.callback = sofa_sit}; .width = 60,
.height = 50,
.draw = food_redraw,
.callback = food_callback};
const Item PC = { const Item Console = {
.layer = 4, .layer = 4,
.timeout = 100, .timeout = 100,
.x = 400, .pos =
.y = 10, {
.icon = &I_PC_22x29, .x = 357,
.action_name = "Use", .y = 190,
.draw = NULL, },
.callback = pc_callback}; .width = 40,
.height = 20,
.draw = console_redraw,
.callback = console_callback};
const Item* Home[ITEMS_NUM] = {&TV, &Sofa, &Painting, &PC}; const Item* Home[] = {&Food, &Console};
const Item** Scenes[1] = {*&Home};
const Item** Scenes[] = {Home};
const Item** get_scene(SceneState* state) { const Item** get_scene(SceneState* state) {
return Scenes[state->scene_id]; return Scenes[state->scene_id];
@ -64,6 +53,37 @@ static void dolphin_scene_start_app(SceneState* state, const FlipperApplication*
furi_thread_start(state->scene_app_thread); furi_thread_start(state->scene_app_thread);
} }
uint16_t roll_new(uint16_t prev, uint16_t max) {
uint16_t val = 999;
while(val != prev) {
val = random() % max;
break;
}
return val;
}
static void dolphin_scene_type_text(
Canvas* canvas,
SceneState* state,
uint8_t x,
uint8_t y,
const char* text) {
char dialog_str[64];
char buf[64];
strcpy(dialog_str, (char*)text);
if(state->dialog_progress <= strlen(dialog_str)) {
if(HAL_GetTick() / 10 % 2 == 0) state->dialog_progress++;
dialog_str[state->dialog_progress] = '\0';
snprintf(buf, state->dialog_progress, dialog_str);
} else {
snprintf(buf, 64, dialog_str);
}
canvas_draw_str_aligned(canvas, x, y, AlignCenter, AlignCenter, buf);
}
const void scene_activate_item_callback(SceneState* state, Canvas* canvas) { const void scene_activate_item_callback(SceneState* state, Canvas* canvas) {
furi_assert(state); furi_assert(state);
furi_assert(canvas); furi_assert(canvas);
@ -78,16 +98,32 @@ const void scene_activate_item_callback(SceneState* state, Canvas* canvas) {
} }
} }
const Vec2 item_get_pos(SceneState* state, ItemsEnum item) {
const Item** current = get_scene(state);
Vec2 rel_pos = {0, 0};
rel_pos.x = DOLPHIN_WIDTH / 2 + (current[item]->pos.x * PARALLAX(current[item]->layer));
rel_pos.y = DOLPHIN_WIDTH / 4 + (current[item]->pos.y * PARALLAX(current[item]->layer));
return rel_pos;
}
const Item* is_nearby(SceneState* state) { const Item* is_nearby(SceneState* state) {
furi_assert(state); furi_assert(state);
uint8_t item = 0; uint8_t item = 0;
bool found = false; bool found = false;
const Item** current = get_scene(state); const Item** current = get_scene(state);
while(item < ITEMS_NUM) { while(item < ItemsEnumTotal) {
int32_t rel = int32_t rel_x =
(DOLPHIN_CENTER + DOLPHIN_WIDTH / 2 - (DOLPHIN_CENTER + DOLPHIN_WIDTH / 2 -
(current[item]->x - state->player_global.x) * PARALLAX(current[item]->layer)); (current[item]->pos.x - state->player_global.x) * PARALLAX(current[item]->layer));
if(abs(rel) <= DOLPHIN_WIDTH / 2) {
uint8_t item_height = current[item]->height;
uint8_t item_width = current[item]->width;
int32_t rel_y = current[item]->pos.y - state->player_global.y;
if(abs(rel_x) <= item_width && abs(rel_y) <= item_height) {
found = !found; found = !found;
break; break;
} }
@ -96,50 +132,102 @@ const Item* is_nearby(SceneState* state) {
return found ? current[item] : NULL; return found ? current[item] : NULL;
} }
void draw_tv(Canvas* canvas, void* state) { void food_redraw(Canvas* canvas, void* s) {
furi_assert(state); furi_assert(s);
SceneState* s = state; SceneState* state = s;
canvas_set_color(canvas, ColorWhite);
canvas_draw_box( const Icon* food_frames[] = {
canvas, (TV.x + 3 - s->player_global.x) * PARALLAX(TV.layer), TV.y + 4, 16, 20); &I_food1_61x98,
canvas_set_color(canvas, ColorBlack); &I_food2_61x98,
canvas_set_bitmap_mode(canvas, true); &I_food3_61x98,
} &I_food4_61x98,
&I_food5_61x98,
&I_food6_61x98,
&I_food7_61x98,
&I_food8_61x98,
&I_food9_61x98,
&I_food10_61x98,
&I_food11_61x98,
&I_food12_61x98,
};
uint8_t frame = ((HAL_GetTick() / 200) % SIZEOF_ARRAY(food_frames));
if(is_nearby(state) && (state->player_global.y > Food.pos.y)) {
dolphin_scene_type_text(
canvas,
state,
(Food.pos.x - state->player_global.x) * PARALLAX(Food.layer) + 90,
state->screen.y + 8,
console_emotes[state->emote_id]);
} else {
state->dialog_progress = 0;
state->emote_id = roll_new(state->previous_emote, SIZEOF_ARRAY(console_emotes));
}
void smash_tv(Canvas* canvas, void* state) {
furi_assert(state);
SceneState* s = state;
s->player_flipped = true;
canvas_set_bitmap_mode(canvas, true);
canvas_draw_icon( canvas_draw_icon(
canvas, ((TV.x - 5) - s->player_global.x) * PARALLAX(TV.layer), TV.y - 2, &I_FX_Bang_32x6); canvas,
canvas_set_bitmap_mode(canvas, false); (Food.pos.x - state->player_global.x) * PARALLAX(Food.layer),
if(s->action_timeout < TV.timeout - 2) { Food.pos.y - state->player_global.y,
elements_multiline_text_framed(canvas, 80, 24, "Bang!"); food_frames[frame]);
canvas_set_bitmap_mode(canvas, true);
}
void food_callback(Canvas* canvas, void* s) {
furi_assert(s);
SceneState* state = s;
if(state->use_pending) {
dolphin_scene_start_app(state, &FLIPPER_SCENE_APPS[1]);
} }
} }
void sofa_sit(Canvas* canvas, void* state) { void console_redraw(Canvas* canvas, void* s) {
furi_assert(state); furi_assert(s);
SceneState* s = state; SceneState* state = s;
// temp fix pos
s->player_global.x = 154;
s->dolphin_gfx = &A_FX_Sitting_40x27;
s->dolphin_gfx_b = &I_FX_SittingB_40x27;
}
void inspect_painting(Canvas* canvas, void* state) { const Icon* console[] = {
furi_assert(state); &I_Console_74x67_0,
SceneState* s = state; &I_Console_74x67_1,
if(s->use_pending) { &I_Console_74x67_2,
dolphin_scene_start_app(s, &FLIPPER_SCENE_APPS[0]); &I_Console_74x67_3,
&I_Console_74x67_4,
&I_Console_74x67_5,
&I_Console_74x67_6,
&I_Console_74x67_7,
&I_Console_74x67_8,
};
uint8_t frame = ((HAL_GetTick() / 100) % SIZEOF_ARRAY(console));
canvas_draw_icon(
canvas,
(Console.pos.x - state->player_global.x) * PARALLAX(Console.layer),
Console.pos.y - state->player_global.y,
console[frame]);
canvas_set_bitmap_mode(canvas, true);
if(is_nearby(state)) {
dolphin_scene_type_text(
canvas,
state,
(Console.pos.x - state->player_global.x) * PARALLAX(Console.layer) - 25,
Console.pos.y - state->player_global.y + 14,
console_emotes[state->emote_id]);
} else {
state->dialog_progress = 0;
state->emote_id = roll_new(state->previous_emote, SIZEOF_ARRAY(console_emotes));
} }
} }
void pc_callback(Canvas* canvas, void* state) { void console_callback(Canvas* canvas, void* s) {
furi_assert(state); furi_assert(s);
SceneState* s = state; SceneState* state = s;
if(s->use_pending) { if(state->use_pending) {
dolphin_scene_start_app(s, &FLIPPER_SCENE_APPS[1]); dolphin_scene_start_app(state, &FLIPPER_SCENE_APPS[1]);
} }
} }

View File

@ -1,8 +1,14 @@
#pragma once #pragma once
#include "dolphin/scenes/scene.h" #include "dolphin/scenes/scene.h"
#define ITEMS_NUM 4 typedef enum {
ItemsFood,
ItemsConsole,
ItemsEnumTotal,
} ItemsEnum;
uint16_t roll_new(uint16_t prev, uint16_t max);
const Vec2 item_get_pos(SceneState* state, ItemsEnum item);
const Item* is_nearby(SceneState* state); const Item* is_nearby(SceneState* state);
const Item** get_scene(SceneState* state); const Item** get_scene(SceneState* state);
const void scene_activate_item_callback(SceneState* state, Canvas* canvas); const void scene_activate_item_callback(SceneState* state, Canvas* canvas);

View File

@ -1,8 +1,8 @@
#pragma once #pragma once
#include "items.h" #include "items.h"
void smash_tv(Canvas* canvas, void* state); void food_redraw(Canvas* canvas, void* state);
void draw_tv(Canvas* canvas, void* state); void food_callback(Canvas* canvas, void* state);
void sofa_sit(Canvas* canvas, void* state);
void inspect_painting(Canvas* canvas, void* state); void console_redraw(Canvas* canvas, void* state);
void pc_callback(Canvas* canvas, void* state); void console_callback(Canvas* canvas, void* state);

View File

@ -0,0 +1,276 @@
#pragma once
#include "dolphin/scenes/scene.h"
const DolphinFrame up = {
.frames =
{
{
.f = &I_up1_73x61,
.b = &I_black_up1_73x61,
},
{
.f = &I_up2_73x61,
.b = &I_black_up2_73x61,
},
},
.total = 2,
};
const DolphinFrame up_down = {
.frames =
{
{
.f = &I_updown1_73x61,
.b = &I_black_updown1_73x61,
},
{
.f = &I_updown2_73x61,
.b = &I_black_updown2_73x61,
},
{
.f = &I_updown3_73x61,
.b = &I_black_updown3_73x61,
},
},
.total = 3,
};
const DolphinFrame up_right = {
.frames =
{
{
.f = &I_upright1_73x61,
.b = &I_black_upright1_73x61,
},
{
.f = &I_upright2_73x61,
.b = &I_black_upright2_73x61,
},
},
.total = 2,
};
const DolphinFrame up_left = {
.frames =
{
{
.f = &I_upleft1_73x61,
.b = &I_black_upleft1_73x61,
},
{
.f = &I_upleft2_73x61,
.b = &I_black_upleft2_73x61,
},
},
.total = 2,
};
const DolphinFrame right = {
.frames =
{
{
.f = &I_right1_73x61,
.b = &I_black_right1_73x61,
},
{
.f = &I_right2_73x61,
.b = &I_black_right2_73x61,
},
{
.f = &I_right3_73x61,
.b = &I_black_right3_73x61,
},
},
.total = 3,
};
const DolphinFrame right_up = {
.frames =
{
{
.f = &I_rightup1_73x61,
.b = &I_black_rightup1_73x61,
},
{
.f = &I_rightup2_73x61,
.b = &I_black_rightup2_73x61,
},
},
.total = 2,
};
const DolphinFrame right_down = {
.frames =
{
{
.f = &I_rightdown1_73x61,
.b = &I_black_rightdown1_73x61,
},
{
.f = &I_rightdown2_73x61,
.b = &I_black_rightdown2_73x61,
},
},
.total = 2,
};
const DolphinFrame right_left = {
.frames =
{
{
.f = &I_rightleft1_73x61,
.b = &I_black_rightleft1_73x61,
},
{
.f = &I_rightleft2_73x61,
.b = &I_black_rightleft2_73x61,
},
},
.total = 2,
};
const DolphinFrame down = {
.frames =
{
{
.f = &I_down1_73x61,
.b = &I_black_down1_73x61,
},
{
.f = &I_down2_73x61,
.b = &I_black_down2_73x61,
},
},
.total = 2,
};
const DolphinFrame down_up = {
.frames =
{
{
.f = &I_downup1_73x61,
.b = &I_black_downup1_73x61,
},
{
.f = &I_downup2_73x61,
.b = &I_black_downup2_73x61,
},
{
.f = &I_downup3_73x61,
.b = &I_black_downup3_73x61,
},
},
.total = 3,
};
const DolphinFrame down_left = {
.frames =
{
{
.f = &I_downleft1_73x61,
.b = &I_black_downleft1_73x61,
},
{
.f = &I_downleft2_73x61,
.b = &I_black_downleft2_73x61,
},
{
.f = &I_downleft3_73x61,
.b = &I_black_downleft3_73x61,
},
},
.total = 3,
};
const DolphinFrame down_right = {
.frames =
{
{
.f = &I_downright1_73x61,
.b = &I_black_downright1_73x61,
},
{
.f = &I_downright2_73x61,
.b = &I_black_downright2_73x61,
},
{
.f = &I_downright3_73x61,
.b = &I_black_downright3_73x61,
},
},
.total = 3,
};
const DolphinFrame left = {
.frames =
{
{
.f = &I_left1_73x61,
.b = &I_black_left1_73x61,
},
{
.f = &I_left2_73x61,
.b = &I_black_left2_73x61,
},
{
.f = &I_left3_73x61,
.b = &I_black_left3_73x61,
},
},
.total = 3,
};
const DolphinFrame left_up = {
.frames =
{
{
.f = &I_leftup1_73x61,
.b = &I_black_leftup1_73x61,
},
{
.f = &I_leftup2_73x61,
.b = &I_black_leftup2_73x61,
},
},
.total = 2,
};
const DolphinFrame left_down = {
.frames =
{
{
.f = &I_leftdown1_73x61,
.b = &I_black_leftdown1_73x61,
},
{
.f = &I_leftdown2_73x61,
.b = &I_black_leftdown2_73x61,
},
},
.total = 2,
};
const DolphinFrame left_right = {
.frames =
{
{
.f = &I_rightleft1_73x61,
.b = &I_black_rightleft1_73x61,
},
{
.f = &I_rightleft2_73x61,
.b = &I_black_rightleft2_73x61,
},
},
.total = 2,
};
const DolphinFrame* frames[4][4] = {
[DirUp] = {[DirUp] = &up, [DirRight] = &up_right, [DirDown] = &up_down, [DirLeft] = &up_left},
[DirRight] =
{[DirUp] = &right_up, [DirRight] = &right, [DirDown] = &right_down, [DirLeft] = &right_left},
[DirDown] =
{[DirUp] = &down_up, [DirRight] = &down_right, [DirDown] = &down, [DirLeft] = &down_left},
[DirLeft] =
{[DirUp] = &left_up, [DirRight] = &left_right, [DirDown] = &left_down, [DirLeft] = &left},
};

View File

@ -52,7 +52,7 @@ void scene_alloc() {
// SceneAppGui // SceneAppGui
scene_app_gui = furi_alloc(sizeof(SceneAppGui)); scene_app_gui = furi_alloc(sizeof(SceneAppGui));
scene_app_gui->mqueue = osMessageQueueNew(2, sizeof(AppEvent), NULL); scene_app_gui->mqueue = osMessageQueueNew(8, sizeof(AppEvent), NULL);
scene_app_gui->gui = furi_record_open("gui"); scene_app_gui->gui = furi_record_open("gui");
scene_app_gui->view_port = view_port_alloc(); scene_app_gui->view_port = view_port_alloc();
scene_app_gui->timer = scene_app_gui->timer =
@ -62,10 +62,18 @@ void scene_alloc() {
SceneState* scene_state = furi_alloc(sizeof(SceneState)); SceneState* scene_state = furi_alloc(sizeof(SceneState));
scene_state->player.y = DOLPHIN_DEFAULT_Y; scene_state->player.y = DOLPHIN_DEFAULT_Y;
scene_state->player.x = DOLPHIN_CENTER; scene_state->player.x = DOLPHIN_CENTER;
scene_state->player_global.x = random() % WORLD_WIDTH / 4;
scene_state->player_global.x = 160;
scene_state->player_global.y = WORLD_HEIGHT;
scene_state->frame_group = DirRight;
scene_state->frame_type = DirRight;
scene_state->frame_pending = DirRight;
scene_state->last_group = DirRight;
scene_state->screen.x = scene_state->player.x; scene_state->screen.x = scene_state->player.x;
scene_state->screen.y = scene_state->player.y; scene_state->screen.y = scene_state->player.y;
// scene_state->debug = true;
scene_state_mutex = furi_alloc(sizeof(ValueMutex)); scene_state_mutex = furi_alloc(sizeof(ValueMutex));
furi_check(init_mutex(scene_state_mutex, scene_state, sizeof(SceneState))); furi_check(init_mutex(scene_state_mutex, scene_state, sizeof(SceneState)));
@ -73,7 +81,7 @@ void scene_alloc() {
view_port_draw_callback_set(scene_app_gui->view_port, dolphin_scene_redraw, scene_state_mutex); view_port_draw_callback_set(scene_app_gui->view_port, dolphin_scene_redraw, scene_state_mutex);
view_port_input_callback_set( view_port_input_callback_set(
scene_app_gui->view_port, scene_engine_input_callback, scene_app_gui->mqueue); scene_app_gui->view_port, scene_engine_input_callback, scene_app_gui->mqueue);
gui_add_view_port(scene_app_gui->gui, scene_app_gui->view_port, GuiLayerMain); gui_add_view_port(scene_app_gui->gui, scene_app_gui->view_port, GuiLayerFullscreen);
view_port_enabled_set(scene_app_gui->view_port, true); view_port_enabled_set(scene_app_gui->view_port, true);
printf("scene_alloc: complete\r\n"); printf("scene_alloc: complete\r\n");
} }

View File

@ -16,26 +16,28 @@
// player // player
#define DOLPHIN_WIDTH 32 #define DOLPHIN_WIDTH 32
#define DOLPHIN_HEIGHT 32 #define DOLPHIN_HEIGHT 32
#define DOLPHIN_CENTER (SCREEN_WIDTH / 2 - DOLPHIN_WIDTH / 2) #define DOLPHIN_CENTER (SCREEN_WIDTH / 2 - DOLPHIN_WIDTH)
#define SPEED_X 2 #define SPEED_X 4
#define ACTIONS_NUM 5 #define SPEED_Y 4
#define DOLPHIN_DEFAULT_Y 20 #define ACTIONS_NUM 4
#define DOLPHIN_DEFAULT_Y 2
#define MAX_FRAMES 3
// world // world
#define WORLD_WIDTH 2048 #define WORLD_WIDTH 256
#define WORLD_HEIGHT 64 #define WORLD_HEIGHT 192
#define LAYERS 8 #define LAYERS 8
#define SCENE_ZOOM 9
#define DOLPHIN_LAYER 6 #define DOLPHIN_LAYER 6
#define PARALLAX_MOD 7 #define PARALLAX_MOD 7
#define PARALLAX(layer) layer / PARALLAX_MOD - layer #define PARALLAX(layer) layer / PARALLAX_MOD - layer
#define DIALOG_PROGRESS 250 #define DIALOG_PROGRESS 250
enum Actions { SLEEP = 0, IDLE, WALK, EMOTE, INTERACT, MINDCONTROL }; enum Actions { IDLE = 0, EMOTE, INTERACT, MINDCONTROL };
static const uint16_t default_timeout[] = static const uint16_t default_timeout[] =
{[SLEEP] = 300, [IDLE] = 100, [WALK] = 100, [EMOTE] = 50, [INTERACT] = 10, [MINDCONTROL] = 50}; {[IDLE] = 100, [EMOTE] = 50, [INTERACT] = 10, [MINDCONTROL] = 50};
typedef enum { typedef enum {
EventTypeTick, EventTypeTick,
@ -64,47 +66,60 @@ typedef struct {
typedef struct { typedef struct {
uint8_t layer; uint8_t layer;
uint16_t timeout; uint16_t timeout;
int32_t x; Vec2 pos;
int32_t y;
const Icon* icon; uint8_t width;
char action_name[16]; uint8_t height;
void (*draw)(Canvas* canvas, void* model); void (*draw)(Canvas* canvas, void* model);
void (*callback)(Canvas* canvas, void* model); void (*callback)(Canvas* canvas, void* model);
} Item; } Item;
typedef enum {
DirUp = 0,
DirRight,
DirDown,
DirLeft,
} FrameDirectionEnum;
typedef struct {
const Icon* f;
const Icon* b;
} DolphinGfxAsset;
typedef struct {
const DolphinGfxAsset frames[MAX_FRAMES];
const uint8_t total;
} DolphinFrame;
typedef struct { typedef struct {
///
Vec2 player; Vec2 player;
Vec2 player_global; Vec2 player_global;
Vec2 player_v; Vec2 player_v;
Vec2 screen; Vec2 screen;
const Icon* dolphin_gfx; FrameDirectionEnum frame_group;
const Icon* dolphin_gfx_b; // temp FrameDirectionEnum last_group;
FrameDirectionEnum frame_pending;
FrameDirectionEnum frame_type;
bool player_flipped; const DolphinFrame* current_frame;
bool transition;
bool transition_pending;
bool use_pending; bool use_pending;
// dolphin_scene_debug
bool debug; bool debug;
uint8_t player_anim; uint8_t player_anim;
uint8_t scene_id; uint8_t frame_idx;
uint8_t scene_id;
uint8_t emote_id; uint8_t emote_id;
uint8_t previous_emote; uint8_t previous_emote;
uint8_t dialogue_id;
uint8_t previous_dialogue;
uint32_t action_timeout;
uint8_t poi;
uint8_t action; uint8_t action;
uint8_t next_action;
uint8_t prev_action; uint8_t prev_action;
uint8_t action_timeout;
int8_t zoom_v;
uint8_t scene_zoom;
uint8_t dialog_progress; uint8_t dialog_progress;
FuriThread* scene_app_thread; FuriThread* scene_app_thread;

View File

@ -6,43 +6,31 @@ void dolphin_scene_handle_user_input(SceneState* state, InputEvent* input) {
furi_assert(state); furi_assert(state);
furi_assert(input); furi_assert(input);
// dolphin_scene_debug state->last_group = state->frame_group;
if(input->type == InputTypeShort) {
if(input->key == InputKeyUp) {
state->debug = !state->debug;
}
}
// toggle mind control on any user interaction
if(input->type == InputTypePress) { if(input->type == InputTypePress) {
if(input->key == InputKeyLeft || input->key == InputKeyRight || input->key == InputKeyOk) { state->action = MINDCONTROL;
state->action = MINDCONTROL;
}
} }
// zoom poc for tests
if(input->type == InputTypePress) {
if(input->key == InputKeyDown) {
state->zoom_v = SPEED_X;
}
} else if(input->type == InputTypeRelease) {
if(input->key == InputKeyDown) {
state->zoom_v = -SPEED_X * 2;
state->dialog_progress = 0;
}
}
// mind control
if(state->action == MINDCONTROL) { if(state->action == MINDCONTROL) {
if(input->type == InputTypePress) { if(input->type == InputTypePress) {
if(input->key == InputKeyRight) { if(input->key == InputKeyRight) {
state->player_flipped = false; state->player_v.y = 0;
state->player_v.x = SPEED_X; state->player_v.x = SPEED_X;
} else if(input->key == InputKeyLeft) { } else if(input->key == InputKeyLeft) {
state->player_flipped = true; state->player_v.y = 0;
state->player_v.x = -SPEED_X; state->player_v.x = -SPEED_X;
} } else if(input->key == InputKeyUp) {
} else if(input->type == InputTypeRelease) {
if(input->key == InputKeyRight || input->key == InputKeyLeft) {
state->player_v.x = 0; state->player_v.x = 0;
state->player_v.y = -SPEED_Y;
} else if(input->key == InputKeyDown) {
state->player_v.x = 0;
state->player_v.y = SPEED_Y;
} }
}
if(input->type == InputTypeRelease) {
state->player_v.x = 0;
state->player_v.y = 0;
} else if(input->type == InputTypeShort) { } else if(input->type == InputTypeShort) {
if(input->key == InputKeyOk) { if(input->key == InputKeyOk) {
state->prev_action = MINDCONTROL; state->prev_action = MINDCONTROL;
@ -59,13 +47,14 @@ void dolphin_scene_coordinates(SceneState* state, uint32_t dt) {
// global pos // global pos
state->player_global.x = CLAMP(state->player_global.x + state->player_v.x, WORLD_WIDTH, 0); state->player_global.x = CLAMP(state->player_global.x + state->player_v.x, WORLD_WIDTH, 0);
state->player_global.y = CLAMP(state->player_global.y + state->player_v.y, WORLD_HEIGHT, 0);
// zoom handlers // nudge camera postition
state->scene_zoom = CLAMP(state->scene_zoom + state->zoom_v, SCENE_ZOOM, 0); if(state->player_global.x > 170) {
state->player.x = CLAMP(state->player.x - (state->zoom_v * (SPEED_X * 2)), DOLPHIN_CENTER, 0); state->player.x =
state->player.y = CLAMP(state->player.y - (state->zoom_v * SPEED_X / 2), DOLPHIN_DEFAULT_Y, 3); CLAMP(state->player.x - state->player_v.x / 2, DOLPHIN_CENTER, -DOLPHIN_WIDTH / 2);
} else if(state->player_global.x < 70) {
//center screen state->player.x =
state->screen.x = state->player_global.x - state->player.x; CLAMP(state->player.x - state->player_v.x / 2, DOLPHIN_WIDTH * 2, DOLPHIN_CENTER);
state->player_anim = (state->player_global.x / 10) % 2; }
} }

View File

@ -1,74 +1,32 @@
#include <furi.h> #include <furi.h>
#include "scene.h" #include "scene.h"
#include "assets/emotes.h" #include "assets/items.h"
static uint16_t roll_new(uint16_t prev, uint16_t max) {
uint16_t val = 999;
while(val != prev) {
val = random() % max;
break;
}
return val;
}
static void scene_proceed_action(SceneState* state) { static void scene_proceed_action(SceneState* state) {
furi_assert(state); furi_assert(state);
state->prev_action = state->action; state->prev_action = state->action;
state->action = (state->prev_action != state->next_action) ? state->action = roll_new(state->prev_action, ACTIONS_NUM);
state->next_action :
roll_new(state->next_action, ACTIONS_NUM);
state->action_timeout = default_timeout[state->action]; state->action_timeout = default_timeout[state->action];
} }
static void scene_dolphin_go_to_poi(SceneState* state) {
furi_assert(state);
if(state->player_global.x < state->poi) {
state->player_flipped = false;
state->player_v.x = SPEED_X / 2;
} else if(state->player_global.x > state->poi) {
state->player_flipped = true;
state->player_v.x = -SPEED_X / 2;
}
}
static void scene_action_handler(SceneState* state) { static void scene_action_handler(SceneState* state) {
furi_assert(state); furi_assert(state);
if(state->action == MINDCONTROL && state->player_v.x != 0) { if(state->action == MINDCONTROL) {
state->action_timeout = default_timeout[state->action]; if(state->player_v.x != 0 || state->player_v.y != 0) {
state->action_timeout = default_timeout[state->action];
}
} }
if(state->action_timeout > 0) { if(state->action_timeout > 0) {
state->action_timeout--; state->action_timeout--;
} else {
if(random() % 1000 > 500) {
state->next_action = roll_new(state->prev_action, ACTIONS_NUM);
state->poi = roll_new(state->player_global.x, WORLD_WIDTH / 4);
}
} }
} }
void dolphin_scene_update_state(SceneState* state, uint32_t t, uint32_t dt) { void dolphin_scene_update_state(SceneState* state, uint32_t t, uint32_t dt) {
furi_assert(state); furi_assert(state);
scene_action_handler(state); scene_action_handler(state);
UNUSED(dialogues_list);
switch(state->action) { switch(state->action) {
case WALK:
if(state->player_global.x == state->poi) {
state->player_v.x = 0;
scene_proceed_action(state);
} else {
scene_dolphin_go_to_poi(state);
}
break;
case EMOTE:
state->player_flipped = false;
if(state->action_timeout == 0) {
scene_proceed_action(state);
state->emote_id = roll_new(state->previous_emote, SIZEOF_ARRAY(emotes_list));
break;
}
case INTERACT: case INTERACT:
if(state->action_timeout == 0) { if(state->action_timeout == 0) {
if(state->prev_action == MINDCONTROL) { if(state->prev_action == MINDCONTROL) {
@ -78,20 +36,9 @@ void dolphin_scene_update_state(SceneState* state, uint32_t t, uint32_t dt) {
} }
} }
break; break;
case SLEEP:
if(state->poi != 154) { // temp
state->poi = 154;
} else if(state->player_global.x != state->poi) {
scene_dolphin_go_to_poi(state);
} else {
state->player_v.x = 0;
if(state->action_timeout == 0) {
state->poi = roll_new(state->player_global.x, WORLD_WIDTH / 4);
scene_proceed_action(state);
}
break;
}
default: default:
if(state->action_timeout == 0) { if(state->action_timeout == 0) {
scene_proceed_action(state); scene_proceed_action(state);
} }

View File

@ -1,101 +1,46 @@
#include <furi.h> #include <furi.h>
#include "scene.h" #include "scene.h"
#include "assets/emotes.h"
#include "assets/items.h" #include "assets/items.h"
#include "assets/meta.h"
#include <gui/elements.h> #include <gui/elements.h>
const char* action_str[] = {"Sleep", "Idle", "Walk", "Emote", "Use", "MC"}; void dolphin_scene_transition_handler(SceneState* state) {
uint8_t speed_mod = (state->player_v.x || state->player_v.y || state->transition) ? 6 : 10;
static void scene_draw_hint(SceneState* state, Canvas* canvas, bool glitching) { if(state->player_v.x < 0) {
furi_assert(state); state->frame_pending = DirLeft;
furi_assert(canvas); } else if(state->player_v.x > 0) {
char buf[32]; state->frame_pending = DirRight;
} else if(state->player_v.y < 0) {
const Item* near = is_nearby(state); state->frame_pending = DirUp;
if(near) { } else if(state->player_v.y > 0) {
int32_t hint_pos_x = (near->x - state->player_global.x) * PARALLAX(near->layer) + 25; state->frame_pending = DirDown;
int8_t hint_pos_y = near->y < 15 ? near->y + 4 : near->y - 16;
strcpy(buf, near->action_name);
if(glitching) {
for(size_t g = 0; g != state->action_timeout; g++) {
buf[(g * 23) % strlen(buf)] = ' ' + (random() % g * 17) % ('z' - ' ');
}
}
canvas_draw_str(canvas, hint_pos_x, hint_pos_y, buf);
} }
} state->transition_pending = state->frame_group != state->frame_pending;
static void scene_draw_current_emote(SceneState* state, Canvas* canvas) { if(*&frames[state->frame_group][state->frame_type]->frames[state->frame_idx].f) {
furi_assert(state); state->current_frame = *&frames[state->frame_group][state->frame_type];
furi_assert(canvas); }
elements_multiline_text_framed(canvas, 80, 20, (char*)emotes_list[state->emote_id]);
}
static void scene_draw_sleep_emote(SceneState* state, Canvas* canvas) { uint8_t total = state->current_frame->frames[2].f == NULL ? 2 : 3;
furi_assert(state);
furi_assert(canvas);
char dialog_str[] = "zZzZ.."; if(state->transition_pending && !state->frame_idx) {
// 2do - sofa x pos getter state->transition_pending = false;
if(state->player_global.x == 154 && state->action_timeout % 100 < 50) { state->transition = true;
if(state->dialog_progress < strlen(dialog_str)) { }
if(state->action_timeout % 10 == 0) state->dialog_progress++;
dialog_str[state->dialog_progress + 1] = '\0';
canvas_draw_str(canvas, 80, 20, dialog_str);
}
if(state->transition) {
state->frame_type = state->frame_pending;
state->frame_group = state->last_group;
state->transition = !(state->frame_idx == total - 1);
} else { } else {
state->dialog_progress = 0; state->frame_group = state->frame_type;
}
}
static void scene_draw_dialog(SceneState* state, Canvas* canvas) {
furi_assert(state);
furi_assert(canvas);
char dialog_str[64];
char buf[64];
strcpy(dialog_str, (char*)dialogues_list[state->dialogue_id]);
if(state->dialog_progress <= strlen(dialog_str)) {
if(state->action_timeout % 2 == 0) state->dialog_progress++;
dialog_str[state->dialog_progress] = '\0';
snprintf(buf, state->dialog_progress, dialog_str);
} else {
snprintf(buf, 64, dialog_str);
} }
elements_multiline_text_framed(canvas, 68, 16, buf); state->player_anim++;
}
/* if(!(state->player_anim % speed_mod)) {
static void draw_idle_emote(SceneState* state, Canvas* canvas){ state->frame_idx = (state->frame_idx + 1) % total;
if(state->action_timeout % 50 < 40 && state->prev_action == MINDCONTROL){
elements_multiline_text_framed(canvas, 68, 16, "WUT?!");
}
}
*/
static void draw_idle_emote(SceneState* state, Canvas* canvas) {
furi_assert(state);
furi_assert(canvas);
char dialog_str[] = "...";
if(state->action_timeout % 100 < 50) {
if(state->dialog_progress < strlen(dialog_str)) {
if(state->action_timeout % 10 == 0) state->dialog_progress++;
dialog_str[state->dialog_progress + 1] = '\0';
canvas_draw_str(canvas, 70, 15, dialog_str);
}
} else {
state->dialog_progress = 0;
} }
} }
@ -103,44 +48,24 @@ void dolphin_scene_render_dolphin(SceneState* state, Canvas* canvas) {
furi_assert(state); furi_assert(state);
furi_assert(canvas); furi_assert(canvas);
if(state->scene_zoom == SCENE_ZOOM) { dolphin_scene_transition_handler(state);
state->dolphin_gfx = &I_DolphinExcited_64x63;
} else if(state->action == SLEEP && state->player_global.x == 154) { // 2do - sofa x pos getter
state->dolphin_gfx = &A_FX_Sitting_40x27;
state->dolphin_gfx_b = &I_FX_SittingB_40x27;
} else if(state->action != INTERACT) {
if(state->player_v.x < 0 || state->player_flipped) {
if(state->player_anim == 0) {
state->dolphin_gfx = &I_WalkL1_32x32;
state->dolphin_gfx_b = &I_WalkLB1_32x32;
} else {
state->dolphin_gfx = &I_WalkL2_32x32;
state->dolphin_gfx_b = &I_WalkLB2_32x32;
}
} else if(state->player_v.x > 0 || !state->player_flipped) {
if(state->player_anim == 0) {
state->dolphin_gfx = &I_WalkR1_32x32;
state->dolphin_gfx_b = &I_WalkRB1_32x32;
} else {
state->dolphin_gfx = &I_WalkR2_32x32;
state->dolphin_gfx_b = &I_WalkRB2_32x32;
}
}
}
canvas_set_bitmap_mode(canvas, true); canvas_set_bitmap_mode(canvas, true);
canvas_set_color(canvas, ColorWhite); canvas_set_color(canvas, ColorWhite);
canvas_draw_icon(canvas, state->player.x, state->player.y, state->dolphin_gfx_b); canvas_draw_icon(
canvas, state->player.x, state->player.y, state->current_frame->frames[state->frame_idx].b);
canvas_set_color(canvas, ColorBlack); canvas_set_color(canvas, ColorBlack);
canvas_draw_icon(canvas, state->player.x, state->player.y, state->dolphin_gfx); canvas_draw_icon(
canvas, state->player.x, state->player.y, state->current_frame->frames[state->frame_idx].f);
canvas_set_bitmap_mode(canvas, false); canvas_set_bitmap_mode(canvas, false);
} }
static bool item_screen_bounds(int32_t pos) { static bool item_screen_bounds_x(int32_t pos) {
return pos > -SCREEN_WIDTH && pos < (SCREEN_WIDTH * 2); return pos > -SCREEN_WIDTH && pos < (SCREEN_WIDTH * 2);
} }
static bool item_screen_bounds_y(int32_t pos) {
return pos > -SCREEN_HEIGHT * 2 && pos < (SCREEN_HEIGHT * 2);
}
void dolphin_scene_render(SceneState* state, Canvas* canvas, uint32_t t) { void dolphin_scene_render(SceneState* state, Canvas* canvas, uint32_t t) {
furi_assert(state); furi_assert(state);
@ -151,24 +76,17 @@ void dolphin_scene_render(SceneState* state, Canvas* canvas, uint32_t t) {
const Item** current_scene = get_scene(state); const Item** current_scene = get_scene(state);
for(uint8_t l = 0; l < LAYERS; l++) { for(uint8_t l = 0; l < LAYERS; l++) {
if(state->scene_zoom < SCENE_ZOOM) { for(uint8_t i = 0; i < ItemsEnumTotal; i++) {
for(uint8_t i = 0; i < ITEMS_NUM; i++) { int32_t item_pos_X = (current_scene[i]->pos.x - state->player_global.x);
int32_t item_pos = (current_scene[i]->x - state->player_global.x); int32_t item_pos_Y = (current_scene[i]->pos.y - state->player_global.y);
if(item_screen_bounds(item_pos)) {
if(current_scene[i]->draw) current_scene[i]->draw(canvas, state);
if(l == current_scene[i]->layer) { if(item_screen_bounds_x(item_pos_X) && item_screen_bounds_y(item_pos_Y)) {
canvas_draw_icon( if(l == current_scene[i]->layer) {
canvas, if(current_scene[i]->draw) {
item_pos * PARALLAX(l), current_scene[i]->draw(canvas, state);
current_scene[i]->y,
current_scene[i]->icon);
canvas_set_bitmap_mode(canvas, false);
} }
} }
} }
if(l == 0) canvas_draw_line(canvas, 0, 42, 128, 42);
} }
if(l == DOLPHIN_LAYER) dolphin_scene_render_dolphin(state, canvas); if(l == DOLPHIN_LAYER) dolphin_scene_render_dolphin(state, canvas);
@ -188,24 +106,16 @@ void dolphin_scene_render_state(SceneState* state, Canvas* canvas) {
if(state->debug) { if(state->debug) {
sprintf( sprintf(
buf, buf,
"x:%ld>%d %ld %s", "%d:%d %d/%dP%dL%d T%d-%d",
state->player_global.x, state->frame_idx,
state->poi, state->current_frame->frames[2].f == NULL ? 2 : 3,
state->action_timeout, state->frame_group,
action_str[state->action]); state->frame_type,
state->frame_pending,
state->last_group,
state->transition_pending,
state->transition);
canvas_draw_str(canvas, 0, 13, buf); canvas_draw_str(canvas, 0, 13, buf);
} }
if(state->action == INTERACT) scene_activate_item_callback(state, canvas);
if(state->scene_zoom == SCENE_ZOOM)
scene_draw_dialog(state, canvas);
else if(state->action == EMOTE)
scene_draw_current_emote(state, canvas);
else if(state->action == MINDCONTROL)
scene_draw_hint(state, canvas, state->action_timeout > 45);
else if(state->action == INTERACT)
scene_activate_item_callback(state, canvas);
else if(state->action == SLEEP)
scene_draw_sleep_emote(state, canvas);
else if(state->action == IDLE)
draw_idle_emote(state, canvas);
} }

View File

@ -192,7 +192,8 @@ int32_t gui_test(void* param) {
text_input_callback, text_input_callback,
gui_tester, gui_tester,
text_input_text, text_input_text,
text_input_text_len); text_input_text_len,
false);
text_input_set_header_text(gui_tester->text_input, "Name the key"); text_input_set_header_text(gui_tester->text_input, "Name the key");
const uint8_t byte_input_bytes_len = 16; const uint8_t byte_input_bytes_len = 16;

23
applications/gui/modules/text_input.c Normal file → Executable file
View File

@ -16,6 +16,7 @@ typedef struct {
const char* header; const char* header;
char* text_buffer; char* text_buffer;
size_t text_buffer_size; size_t text_buffer_size;
bool clear_default_text;
TextInputCallback callback; TextInputCallback callback;
void* callback_context; void* callback_context;
@ -128,7 +129,7 @@ static const char char_to_uppercase(const char letter) {
} }
static void text_input_backspace_cb(TextInputModel* model) { static void text_input_backspace_cb(TextInputModel* model) {
uint8_t text_length = strlen(model->text_buffer); uint8_t text_length = model->clear_default_text ? 1 : strlen(model->text_buffer);
if(text_length > 0) { if(text_length > 0) {
model->text_buffer[text_length - 1] = 0; model->text_buffer[text_length - 1] = 0;
} }
@ -158,11 +159,16 @@ static void text_input_view_draw_callback(Canvas* canvas, void* _model) {
text++; text++;
} }
if(model->clear_default_text) {
elements_slightly_rounded_box(
canvas, start_pos - 1, 14, canvas_string_width(canvas, text) + 2, 10);
canvas_set_color(canvas, ColorWhite);
} else {
canvas_draw_str(canvas, start_pos + canvas_string_width(canvas, text) + 1, 22, "|");
canvas_draw_str(canvas, start_pos + canvas_string_width(canvas, text) + 2, 22, "|");
}
canvas_draw_str(canvas, start_pos, 22, text); canvas_draw_str(canvas, start_pos, 22, text);
canvas_draw_str(canvas, start_pos + canvas_string_width(canvas, text) + 1, 22, "|");
canvas_draw_str(canvas, start_pos + canvas_string_width(canvas, text) + 2, 22, "|");
canvas_set_font(canvas, FontKeyboard); canvas_set_font(canvas, FontKeyboard);
for(uint8_t row = 0; row <= keyboard_row_count; row++) { for(uint8_t row = 0; row <= keyboard_row_count; row++) {
@ -295,12 +301,16 @@ static void text_input_handle_ok(TextInput* text_input) {
} else if(selected == BACKSPACE_KEY) { } else if(selected == BACKSPACE_KEY) {
text_input_backspace_cb(model); text_input_backspace_cb(model);
} else if(text_length < (model->text_buffer_size - 1)) { } else if(text_length < (model->text_buffer_size - 1)) {
if(model->clear_default_text) {
text_length = 0;
}
if(text_length == 0 && char_is_lowercase(selected)) { if(text_length == 0 && char_is_lowercase(selected)) {
selected = char_to_uppercase(selected); selected = char_to_uppercase(selected);
} }
model->text_buffer[text_length] = selected; model->text_buffer[text_length] = selected;
model->text_buffer[text_length + 1] = 0; model->text_buffer[text_length + 1] = 0;
} }
model->clear_default_text = false;
return true; return true;
}); });
} }
@ -365,6 +375,7 @@ TextInput* text_input_alloc() {
model->header = ""; model->header = "";
model->selected_row = 0; model->selected_row = 0;
model->selected_column = 0; model->selected_column = 0;
model->clear_default_text = false;
return true; return true;
}); });
@ -387,13 +398,15 @@ void text_input_set_result_callback(
TextInputCallback callback, TextInputCallback callback,
void* callback_context, void* callback_context,
char* text_buffer, char* text_buffer,
size_t text_buffer_size) { size_t text_buffer_size,
bool clear_default_text) {
with_view_model( with_view_model(
text_input->view, (TextInputModel * model) { text_input->view, (TextInputModel * model) {
model->callback = callback; model->callback = callback;
model->callback_context = callback_context; model->callback_context = callback_context;
model->text_buffer = text_buffer; model->text_buffer = text_buffer;
model->text_buffer_size = text_buffer_size; model->text_buffer_size = text_buffer_size;
model->clear_default_text = clear_default_text;
return true; return true;
}); });
} }

View File

@ -39,13 +39,15 @@ View* text_input_get_view(TextInput* text_input);
* @param callback_context - callback context * @param callback_context - callback context
* @param text_buffer - pointer to YOUR text buffer, that we going to modify * @param text_buffer - pointer to YOUR text buffer, that we going to modify
* @param text_buffer_size - YOUR text buffer size in bytes. Max string length will be text_buffer_size - 1. * @param text_buffer_size - YOUR text buffer size in bytes. Max string length will be text_buffer_size - 1.
* @param clear_default_text - clear text from text_buffer on first OK event
*/ */
void text_input_set_result_callback( void text_input_set_result_callback(
TextInput* text_input, TextInput* text_input,
TextInputCallback callback, TextInputCallback callback,
void* callback_context, void* callback_context,
char* text_buffer, char* text_buffer,
size_t text_buffer_size); size_t text_buffer_size,
bool clear_default_text);
/** /**
* @brief Set text input header text * @brief Set text input header text

View File

@ -53,11 +53,11 @@ bool scene_manager_handle_custom_event(SceneManager* scene_manager, uint32_t cus
scene_manager->context, event); scene_manager->context, event);
} }
bool scene_manager_handle_navigation_event(SceneManager* scene_manager) { bool scene_manager_handle_back_event(SceneManager* scene_manager) {
furi_assert(scene_manager); furi_assert(scene_manager);
SceneManagerEvent event = { SceneManagerEvent event = {
.type = SceneManagerEventTypeNavigation, .type = SceneManagerEventTypeBack,
}; };
uint32_t scene_id = *SceneManagerIdStack_back(scene_manager->scene_id_stack); uint32_t scene_id = *SceneManagerIdStack_back(scene_manager->scene_id_stack);
bool consumed = bool consumed =
@ -109,7 +109,9 @@ bool scene_manager_previous_scene(SceneManager* scene_manager) {
return true; return true;
} }
bool scene_manager_search_previous_scene(SceneManager* scene_manager, uint32_t scene_id) { bool scene_manager_search_and_switch_to_previous_scene(
SceneManager* scene_manager,
uint32_t scene_id) {
furi_assert(scene_manager); furi_assert(scene_manager);
uint32_t prev_scene_id = 0; uint32_t prev_scene_id = 0;
@ -137,3 +139,45 @@ bool scene_manager_search_previous_scene(SceneManager* scene_manager, uint32_t s
return true; return true;
} }
bool scene_manager_has_previous_scene(SceneManager* scene_manager, uint32_t scene_id) {
furi_assert(scene_manager);
bool scene_found = false;
uint32_t prev_scene_id;
SceneManagerIdStack_it_t scene_it;
SceneManagerIdStack_it_last(scene_it, scene_manager->scene_id_stack);
// Perform search in scene stack
while(!scene_found) {
SceneManagerIdStack_previous(scene_it);
if(SceneManagerIdStack_end_p(scene_it)) {
break;
}
prev_scene_id = *SceneManagerIdStack_ref(scene_it);
if(prev_scene_id == scene_id) {
scene_found = true;
}
}
return scene_found;
}
bool scene_manager_search_and_switch_to_another_scene(
SceneManager* scene_manager,
uint32_t scene_id) {
furi_assert(scene_manager);
furi_assert(scene_id < scene_manager->scene_handlers->scene_num);
uint32_t cur_scene_id = *SceneManagerIdStack_back(scene_manager->scene_id_stack);
SceneManagerIdStack_it_t scene_it;
SceneManagerIdStack_it(scene_it, scene_manager->scene_id_stack);
SceneManagerIdStack_next(scene_it);
// Remove all scene id from navigation stack until first scene
SceneManagerIdStack_pop_until(scene_manager->scene_id_stack, scene_it);
// Add next scene
SceneManagerIdStack_push_back(scene_manager->scene_id_stack, scene_id);
scene_manager->scene_handlers->on_exit_handlers[cur_scene_id](scene_manager->context);
scene_manager->scene_handlers->on_enter_handlers[scene_id](scene_manager->context);
return true;
}

View File

@ -11,7 +11,7 @@ extern "C" {
*/ */
typedef enum { typedef enum {
SceneManagerEventTypeCustom, SceneManagerEventTypeCustom,
SceneManagerEventTypeNavigation, SceneManagerEventTypeBack,
SceneManagerEventTypeTick, SceneManagerEventTypeTick,
} SceneManagerEventType; } SceneManagerEventType;
@ -78,12 +78,12 @@ void scene_manager_free(SceneManager* scene_manager);
*/ */
bool scene_manager_handle_custom_event(SceneManager* scene_manager, uint32_t custom_event); bool scene_manager_handle_custom_event(SceneManager* scene_manager, uint32_t custom_event);
/** Navigation event handler /** Back event handler
* Calls Scene event handler with Navigation event parameter * Calls Scene event handler with Back event parameter
* @param scene_manager SceneManager instance * @param scene_manager SceneManager instance
* @return true if event was consumed, false otherwise * @return true if event was consumed, false otherwise
*/ */
bool scene_manager_handle_navigation_event(SceneManager* scene_manager); bool scene_manager_handle_back_event(SceneManager* scene_manager);
/** Tick event handler /** Tick event handler
* Calls Scene event handler with Tick event parameter * Calls Scene event handler with Tick event parameter
@ -104,12 +104,30 @@ void scene_manager_next_scene(SceneManager* scene_manager, uint32_t next_scene_i
*/ */
bool scene_manager_previous_scene(SceneManager* scene_manager); bool scene_manager_previous_scene(SceneManager* scene_manager);
/** Search previous Scene by ID /** Search previous Scene
* @param scene_manager SceneManager instance * @param scene_manager SceneManager instance
* @param scene_id Scene ID * @param scene_id Scene ID
* @return true if previous scene was found, false otherwise * @return true if previous scene was found, false otherwise
*/ */
bool scene_manager_search_previous_scene(SceneManager* scene_manager, uint32_t scene_id); bool scene_manager_has_previous_scene(SceneManager* scene_manager, uint32_t scene_id);
/** Search and switch to previous Scene
* @param scene_manager SceneManager instance
* @param scene_id Scene ID
* @return true if previous scene was found, false otherwise
*/
bool scene_manager_search_and_switch_to_previous_scene(
SceneManager* scene_manager,
uint32_t scene_id);
/** Clear Scene stack and switch to another Scene
* @param scene_manager SceneManager instance
* @param scene_id Scene ID
* @return true if previous scene was found, false otherwise
*/
bool scene_manager_search_and_switch_to_another_scene(
SceneManager* scene_manager,
uint32_t scene_id);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -3,7 +3,7 @@
#include <callback-connector.h> #include <callback-connector.h>
#include <m-string.h> #include <m-string.h>
#include <file-worker-cpp.h> #include <file-worker-cpp.h>
#include <path.h> #include <lib/toolbox/path.h>
const char* iButtonApp::app_folder = "/any/ibutton"; const char* iButtonApp::app_folder = "/any/ibutton";
const char* iButtonApp::app_extension = ".ibtn"; const char* iButtonApp::app_extension = ".ibtn";
@ -40,9 +40,6 @@ iButtonApp::iButtonApp()
: notification{"notification"} { : notification{"notification"} {
api_hal_power_insomnia_enter(); api_hal_power_insomnia_enter();
key_worker = new KeyWorker(&ibutton_gpio); key_worker = new KeyWorker(&ibutton_gpio);
// we need random
srand(DWT->CYCCNT);
} }
iButtonApp::~iButtonApp() { iButtonApp::~iButtonApp() {
@ -186,38 +183,6 @@ uint8_t iButtonApp::get_text_store_size() {
return text_store_size; return text_store_size;
} }
void iButtonApp::generate_random_name(char* name, uint8_t max_name_size) {
const uint8_t prefix_size = 9;
const char* prefix[prefix_size] = {
"ancient",
"hollow",
"strange",
"disappeared",
"unknown",
"unthinkable",
"unnamable",
"nameless",
"my",
};
const uint8_t suffix_size = 8;
const char* suffix[suffix_size] = {
"door",
"entrance",
"doorway",
"entry",
"portal",
"entree",
"opening",
"crack",
};
sniprintf(
name, max_name_size, "%s_%s", prefix[rand() % prefix_size], suffix[rand() % suffix_size]);
// to upper
name[0] = name[0] - 0x20;
}
// file managment // file managment
bool iButtonApp::save_key(const char* key_name) { bool iButtonApp::save_key(const char* key_name) {
// Create ibutton directory if necessary // Create ibutton directory if necessary

View File

@ -90,8 +90,6 @@ public:
char* get_file_name(); char* get_file_name();
uint8_t get_file_name_size(); uint8_t get_file_name_size();
void generate_random_name(char* name, uint8_t max_name_size);
bool save_key(const char* key_name); bool save_key(const char* key_name);
bool load_key(); bool load_key();
bool load_key(const char* key_name); bool load_key(const char* key_name);

View File

@ -2,7 +2,7 @@
#include <api-hal.h> #include <api-hal.h>
#include <stdarg.h> #include <stdarg.h>
#include <cli/cli.h> #include <cli/cli.h>
#include <args.h> #include <lib/toolbox/args.h>
#include "helpers/key-info.h" #include "helpers/key-info.h"
#include "helpers/key-worker.h" #include "helpers/key-worker.h"

View File

@ -4,6 +4,7 @@
#include "../ibutton-event.h" #include "../ibutton-event.h"
#include "../ibutton-key.h" #include "../ibutton-key.h"
#include <callback-connector.h> #include <callback-connector.h>
#include <lib/toolbox/random_name.h>
void iButtonSceneSaveName::on_enter(iButtonApp* app) { void iButtonSceneSaveName::on_enter(iButtonApp* app) {
iButtonAppViewManager* view_manager = app->get_view_manager(); iButtonAppViewManager* view_manager = app->get_view_manager();
@ -12,16 +13,17 @@ void iButtonSceneSaveName::on_enter(iButtonApp* app) {
iButtonKey* key = app->get_key(); iButtonKey* key = app->get_key();
const char* key_name = key->get_name(); const char* key_name = key->get_name();
bool key_name_empty = !strcmp(key_name, "");
if(strcmp(key_name, "") == 0) { if(key_name_empty) {
app->generate_random_name(app->get_text_store(), app->get_text_store_size()); set_random_name(app->get_text_store(), app->get_text_store_size());
} else { } else {
app->set_text_store("%s", key_name); app->set_text_store("%s", key_name);
} }
text_input_set_header_text(text_input, "Name the key"); text_input_set_header_text(text_input, "Name the key");
text_input_set_result_callback( text_input_set_result_callback(
text_input, callback, app, app->get_text_store(), IBUTTON_KEY_NAME_SIZE); text_input, callback, app, app->get_text_store(), IBUTTON_KEY_NAME_SIZE, key_name_empty);
view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewTextInput); view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewTextInput);
} }
@ -47,7 +49,7 @@ bool iButtonSceneSaveName::on_event(iButtonApp* app, iButtonEvent* event) {
void iButtonSceneSaveName::on_exit(iButtonApp* app) { void iButtonSceneSaveName::on_exit(iButtonApp* app) {
TextInput* text_input = app->get_view_manager()->get_text_input(); TextInput* text_input = app->get_view_manager()->get_text_input();
text_input_set_header_text(text_input, ""); text_input_set_header_text(text_input, "");
text_input_set_result_callback(text_input, NULL, NULL, NULL, 0); text_input_set_result_callback(text_input, NULL, NULL, NULL, 0, false);
} }
void iButtonSceneSaveName::text_input_callback(void* context) { void iButtonSceneSaveName::text_input_callback(void* context) {

View File

@ -10,6 +10,7 @@
#include <string> #include <string>
#include <m-string.h> #include <m-string.h>
#include <irda_transmit.h> #include <irda_transmit.h>
#include <sys/types.h>
static void signal_received_callback(void* context, IrdaWorkerSignal* received_signal) { static void signal_received_callback(void* context, IrdaWorkerSignal* received_signal) {
furi_assert(received_signal); furi_assert(received_signal);
@ -47,7 +48,7 @@ static void signal_received_callback(void* context, IrdaWorkerSignal* received_s
} }
static void irda_cli_start_ir_rx(Cli* cli, string_t args, void* context) { static void irda_cli_start_ir_rx(Cli* cli, string_t args, void* context) {
if(api_hal_irda_rx_irq_is_busy()) { if(api_hal_irda_is_busy()) {
printf("IRDA is busy. Exit."); printf("IRDA is busy. Exit.");
return; return;
} }
@ -105,7 +106,7 @@ static bool parse_signal_raw(
uint32_t* timings, uint32_t* timings,
uint32_t* timings_cnt, uint32_t* timings_cnt,
float* duty_cycle, float* duty_cycle,
float* frequency) { uint32_t* frequency) {
char frequency_str[10]; char frequency_str[10];
char duty_cycle_str[10]; char duty_cycle_str[10];
int parsed = sscanf(str, "RAW F:%9s DC:%9s", frequency_str, duty_cycle_str); int parsed = sscanf(str, "RAW F:%9s DC:%9s", frequency_str, duty_cycle_str);
@ -141,14 +142,14 @@ static bool parse_signal_raw(
} }
static void irda_cli_start_ir_tx(Cli* cli, string_t args, void* context) { static void irda_cli_start_ir_tx(Cli* cli, string_t args, void* context) {
if(api_hal_irda_rx_irq_is_busy()) { if(api_hal_irda_is_busy()) {
printf("IRDA is busy. Exit."); printf("IRDA is busy. Exit.");
return; return;
} }
IrdaMessage message; IrdaMessage message;
const char* str = string_get_cstr(args); const char* str = string_get_cstr(args);
float frequency; uint32_t frequency;
float duty_cycle; float duty_cycle;
uint32_t* timings = (uint32_t*)furi_alloc(sizeof(uint32_t) * 1000); uint32_t* timings = (uint32_t*)furi_alloc(sizeof(uint32_t) * 1000);
uint32_t timings_cnt = 1000; uint32_t timings_cnt = 1000;
@ -156,7 +157,7 @@ static void irda_cli_start_ir_tx(Cli* cli, string_t args, void* context) {
if(parse_message(str, &message)) { if(parse_message(str, &message)) {
irda_send(&message, 1); irda_send(&message, 1);
} else if(parse_signal_raw(str, timings, &timings_cnt, &duty_cycle, &frequency)) { } else if(parse_signal_raw(str, timings, &timings_cnt, &duty_cycle, &frequency)) {
irda_send_raw_ext(timings, timings_cnt, true, duty_cycle, frequency); irda_send_raw_ext(timings, timings_cnt, true, frequency, duty_cycle);
} else { } else {
printf("Wrong arguments.\r\n"); printf("Wrong arguments.\r\n");
irda_cli_print_usage(); irda_cli_print_usage();

View File

@ -20,7 +20,8 @@ void IrdaAppSceneEditRename::on_enter(IrdaApp* app) {
IrdaApp::text_input_callback, IrdaApp::text_input_callback,
app, app,
app->get_text_store(0), app->get_text_store(0),
app->get_text_store_size()); app->get_text_store_size(),
false);
view_manager->switch_to(IrdaAppViewManager::ViewType::TextInput); view_manager->switch_to(IrdaAppViewManager::ViewType::TextInput);
} }

View File

@ -26,7 +26,8 @@ void IrdaAppSceneLearnEnterName::on_enter(IrdaApp* app) {
IrdaApp::text_input_callback, IrdaApp::text_input_callback,
app, app,
app->get_text_store(0), app->get_text_store(0),
app->get_text_store_size()); app->get_text_store_size(),
false);
view_manager->switch_to(IrdaAppViewManager::ViewType::TextInput); view_manager->switch_to(IrdaAppViewManager::ViewType::TextInput);
} }

View File

@ -0,0 +1,15 @@
#include "decoder-gpio-out.h"
#include <furi.h>
#include <api-hal.h>
void DecoderGpioOut::process_front(bool polarity, uint32_t time) {
hal_gpio_write(&gpio_ext_pa7, polarity);
}
DecoderGpioOut::DecoderGpioOut() {
hal_gpio_init_simple(&gpio_ext_pa7, GpioModeOutputPushPull);
}
DecoderGpioOut::~DecoderGpioOut() {
hal_gpio_init_simple(&gpio_ext_pa7, GpioModeAnalog);
}

View File

@ -0,0 +1,14 @@
#pragma once
#include <stdint.h>
#include <atomic>
class DecoderGpioOut {
public:
void process_front(bool polarity, uint32_t time);
DecoderGpioOut();
~DecoderGpioOut();
private:
void reset_state();
};

View File

@ -2,18 +2,24 @@
#include <api-hal.h> #include <api-hal.h>
constexpr uint32_t clocks_in_us = 64; constexpr uint32_t clocks_in_us = 64;
constexpr uint32_t us_per_bit = 255;
constexpr uint32_t min_time_us = 25 * clocks_in_us;
constexpr uint32_t mid_time_us = 45 * clocks_in_us;
constexpr uint32_t max_time_us = 90 * clocks_in_us;
bool DecoderIndala::read(uint8_t* data, uint8_t data_size) { bool DecoderIndala::read(uint8_t* data, uint8_t data_size) {
bool result = false; bool result = false;
if(ready) { if(ready) {
result = true; result = true;
printf("IND %02X %02X %02X\r\n", facility, (uint8_t)(number >> 8), (uint8_t)number); if(cursed_data_valid) {
ready = false; indala.decode(
reinterpret_cast<const uint8_t*>(&cursed_raw_data),
sizeof(uint64_t),
data,
data_size);
} else {
indala.decode(
reinterpret_cast<const uint8_t*>(&raw_data), sizeof(uint64_t), data, data_size);
}
reset_state();
} }
return result; return result;
@ -22,149 +28,49 @@ bool DecoderIndala::read(uint8_t* data, uint8_t data_size) {
void DecoderIndala::process_front(bool polarity, uint32_t time) { void DecoderIndala::process_front(bool polarity, uint32_t time) {
if(ready) return; if(ready) return;
if(polarity == false) { process_internal(polarity, time, &raw_data);
last_pulse_time = time; if(ready) return;
} else {
last_pulse_time += time;
pulse_count++;
if(last_pulse_time > min_time_us && last_pulse_time < max_time_us) { if(polarity) {
if(last_pulse_time > mid_time_us) { time = time + 110;
bool last_data = !(readed_data & 1); } else {
pulse_count = 0; time = time - 110;
readed_data = (readed_data << 1) | last_data; }
verify();
} else if((pulse_count % 16) == 0) { process_internal(!polarity, time, &cursed_raw_data);
bool last_data = readed_data & 1; if(ready) {
pulse_count = 0; cursed_data_valid = true;
readed_data = (readed_data << 1) | last_data; }
verify(); }
void DecoderIndala::process_internal(bool polarity, uint32_t time, uint64_t* data) {
time /= clocks_in_us;
time += (us_per_bit / 2);
uint32_t bit_count = (time / us_per_bit);
if(bit_count < 64) {
for(uint32_t i = 0; i < bit_count; i++) {
*data = (*data << 1) | polarity;
if((*data >> 32) == 0xa0000000ULL) {
if(indala.can_be_decoded(
reinterpret_cast<const uint8_t*>(data), sizeof(uint64_t))) {
ready = true;
break;
}
} }
} }
} }
} }
DecoderIndala::DecoderIndala() { DecoderIndala::DecoderIndala() {
reset_state();
} }
void DecoderIndala::reset_state() { void DecoderIndala::reset_state() {
} raw_data = 0;
cursed_raw_data = 0;
void DecoderIndala::verify() { ready = false;
// verify inverse cursed_data_valid = false;
readed_data = ~readed_data; }
verify_inner();
// verify normal
readed_data = ~readed_data;
verify_inner();
}
typedef union {
uint64_t raw;
struct __attribute__((packed)) {
uint8_t static0 : 3;
uint8_t checksum : 2;
uint8_t static1 : 2;
uint8_t y14 : 1;
uint8_t x8 : 1;
uint8_t x1 : 1;
uint8_t y13 : 1;
uint8_t static2 : 1;
uint8_t y12 : 1;
uint8_t x6 : 1;
uint8_t y5 : 1;
uint8_t y8 : 1;
uint8_t y15 : 1;
uint8_t x2 : 1;
uint8_t x5 : 1;
uint8_t x4 : 1;
uint8_t y9 : 1;
uint8_t y2 : 1;
uint8_t x3 : 1;
uint8_t y3 : 1;
uint8_t y1 : 1;
uint8_t y16 : 1;
uint8_t y4 : 1;
uint8_t x7 : 1;
uint8_t p2 : 1;
uint8_t y11 : 1;
uint8_t y6 : 1;
uint8_t y7 : 1;
uint8_t p1 : 1;
uint8_t y10 : 1;
uint32_t preamble : 30;
};
} IndalaFormat;
void DecoderIndala::verify_inner() {
IndalaFormat id;
id.raw = readed_data;
// preamble
//if((data >> 34) != 0b000000000000000000000000000001) return;
if(id.preamble != 1) return;
// static data bits
//if((data & 0b100001100111) != 0b101) return;
if(id.static2 != 0 && id.static1 != 0 && id.static0 != 0b101) return;
// Indala checksum
uint8_t sum_to_check = id.y2 + id.y4 + id.y7 + id.y8 + id.y10 + id.y11 + id.y14 + id.y16;
if(sum_to_check % 2 == 0) {
if(id.checksum != 0b10) return;
} else {
if(id.checksum != 0b01) return;
}
// read facility number
facility = (id.x1 << 7) + (id.x2 << 6) + (id.x3 << 5) + (id.x4 << 4) + (id.x5 << 3) +
(id.x6 << 2) + (id.x7 << 1) + (id.x8 << 0);
// read serial number
number = (id.y1 << 15) + (id.y2 << 14) + (id.y3 << 13) + (id.y4 << 12) + (id.y5 << 11) +
(id.y6 << 10) + (id.y7 << 9) + (id.y8 << 8) + (id.y9 << 7) + (id.y10 << 6) +
(id.y11 << 5) + (id.y12 << 4) + (id.y13 << 3) + (id.y14 << 2) + (id.y15 << 1) +
(id.y16 << 0);
// Wiegand checksum left
sum_to_check = 0;
for(int8_t i = 0; i < 8; i--) {
if((facility >> i) & 1) {
sum_to_check += 1;
}
}
for(int8_t i = 0; i < 4; i--) {
if((number >> i) & 1) {
sum_to_check += 1;
}
}
if(id.p1) {
sum_to_check += 1;
}
if((sum_to_check % 2) == 1) return;
// Wiegand checksum right
sum_to_check = 0;
for(int8_t i = 0; i < 12; i--) {
if((number >> (i + 4)) & 1) {
sum_to_check += 1;
}
}
if(id.p2) {
sum_to_check += 1;
}
if((sum_to_check % 2) != 1) return;
ready = true;
}

View File

@ -2,27 +2,24 @@
#include <stdint.h> #include <stdint.h>
#include <limits.h> #include <limits.h>
#include <atomic> #include <atomic>
#include "protocols/protocol-indala-40134.h"
class DecoderIndala { class DecoderIndala {
public: public:
bool read(uint8_t* data, uint8_t data_size); bool read(uint8_t* data, uint8_t data_size);
void process_front(bool polarity, uint32_t time); void process_front(bool polarity, uint32_t time);
void process_internal(bool polarity, uint32_t time, uint64_t* data);
DecoderIndala(); DecoderIndala();
private: private:
void reset_state(); void reset_state();
void verify(); uint64_t raw_data;
void verify_inner(); uint64_t cursed_raw_data;
uint32_t last_pulse_time = 0;
uint32_t pulse_count = 0;
uint32_t overall_pulse_count = 0;
uint64_t readed_data = 0;
std::atomic<bool> ready; std::atomic<bool> ready;
uint8_t facility = 0; std::atomic<bool> cursed_data_valid;
uint16_t number = 0; ProtocolIndala40134 indala;
}; };

View File

@ -7,7 +7,6 @@ void EncoderHID_H10301::init(const uint8_t* data, const uint8_t data_size) {
hid.encode(data, data_size, reinterpret_cast<uint8_t*>(&card_data), sizeof(card_data) * 3); hid.encode(data, data_size, reinterpret_cast<uint8_t*>(&card_data), sizeof(card_data) * 3);
card_data_index = 0; card_data_index = 0;
bit_index = 0;
} }
void EncoderHID_H10301::write_bit(bool bit, uint8_t position) { void EncoderHID_H10301::write_bit(bool bit, uint8_t position) {
@ -24,39 +23,24 @@ void EncoderHID_H10301::write_raw_bit(bool bit, uint8_t position) {
} }
void EncoderHID_H10301::get_next(bool* polarity, uint16_t* period, uint16_t* pulse) { void EncoderHID_H10301::get_next(bool* polarity, uint16_t* period, uint16_t* pulse) {
// hid 0 is 6 cycles by 8 clocks uint8_t bit = (card_data[card_data_index / 32] >> (31 - (card_data_index % 32))) & 1;
const uint8_t hid_0_period = 8;
const uint8_t hid_0_count = 6;
// hid 1 is 5 cycles by 10 clocks
const uint8_t hid_1_period = 10;
const uint8_t hid_1_count = 5;
bool bit = (card_data[card_data_index / 32] >> (31 - (card_data_index % 32))) & 1; bool advance = fsk->next(bit, period);
if(advance) {
*polarity = true; card_data_index++;
if(bit) { if(card_data_index >= (32 * card_data_max)) {
*period = hid_1_period; card_data_index = 0;
*pulse = hid_1_period / 2;
bit_index++;
if(bit_index >= hid_1_count) {
bit_index = 0;
card_data_index++;
if(card_data_index >= (32 * card_data_max)) {
card_data_index = 0;
}
}
} else {
*period = hid_0_period;
*pulse = hid_0_period / 2;
bit_index++;
if(bit_index >= hid_0_count) {
bit_index = 0;
card_data_index++;
if(card_data_index >= (32 * card_data_max)) {
card_data_index = 0;
}
} }
} }
*polarity = true;
*pulse = *period / 2;
}
EncoderHID_H10301::EncoderHID_H10301() {
fsk = new OscFSK(8, 10, 50);
}
EncoderHID_H10301::~EncoderHID_H10301() {
delete fsk;
} }

View File

@ -1,5 +1,6 @@
#pragma once #pragma once
#include "encoder-generic.h" #include "encoder-generic.h"
#include "osc-fsk.h"
class EncoderHID_H10301 : public EncoderGeneric { class EncoderHID_H10301 : public EncoderGeneric {
public: public:
@ -10,15 +11,16 @@ public:
* @param data_size must be 3 * @param data_size must be 3
*/ */
void init(const uint8_t* data, const uint8_t data_size) final; void init(const uint8_t* data, const uint8_t data_size) final;
void get_next(bool* polarity, uint16_t* period, uint16_t* pulse) final; void get_next(bool* polarity, uint16_t* period, uint16_t* pulse) final;
EncoderHID_H10301();
~EncoderHID_H10301();
private: private:
static const uint8_t card_data_max = 3; static const uint8_t card_data_max = 3;
uint32_t card_data[card_data_max]; uint32_t card_data[card_data_max];
uint8_t card_data_index; uint8_t card_data_index;
uint8_t bit_index;
void write_bit(bool bit, uint8_t position); void write_bit(bool bit, uint8_t position);
void write_raw_bit(bool bit, uint8_t position); void write_raw_bit(bool bit, uint8_t position);
OscFSK* fsk;
}; };

View File

@ -0,0 +1,20 @@
#include "osc-fsk.h"
OscFSK::OscFSK(uint16_t _freq_low, uint16_t _freq_hi, uint16_t _osc_phase_max)
: freq{_freq_low, _freq_hi}
, osc_phase_max(_osc_phase_max) {
osc_phase_current = 0;
}
bool OscFSK::next(bool bit, uint16_t* period) {
bool advance = false;
*period = freq[bit];
osc_phase_current += *period;
if(osc_phase_current > osc_phase_max) {
advance = true;
osc_phase_current -= osc_phase_max;
}
return advance;
}

View File

@ -0,0 +1,30 @@
#pragma once
#include <stdint.h>
/**
* This code tries to fit the periods into a given number of cycles (phases) by taking cycles from the next cycle of periods.
*/
class OscFSK {
public:
/**
* Get next period
* @param bit bit value
* @param period return period
* @return bool whether to advance to the next bit
*/
bool next(bool bit, uint16_t* period);
/**
* FSK ocillator constructor
*
* @param freq_low bit 0 freq
* @param freq_hi bit 1 freq
* @param osc_phase_max max oscillator phase
*/
OscFSK(uint16_t freq_low, uint16_t freq_hi, uint16_t osc_phase_max);
private:
const uint16_t freq[2];
const uint16_t osc_phase_max;
int32_t osc_phase_current;
};

View File

@ -12,6 +12,11 @@ static void set_bit(bool bit, uint8_t position, Indala40134CardData* card_data)
} }
} }
static bool get_bit(uint8_t position, const Indala40134CardData* card_data) {
position = (sizeof(Indala40134CardData) * 8) - 1 - position;
return (*card_data >> position) & 1;
}
uint8_t ProtocolIndala40134::get_encoded_data_size() { uint8_t ProtocolIndala40134::get_encoded_data_size() {
return sizeof(Indala40134CardData); return sizeof(Indala40134CardData);
} }
@ -110,6 +115,46 @@ void ProtocolIndala40134::encode(
memcpy(encoded_data, &card_data, get_encoded_data_size()); memcpy(encoded_data, &card_data, get_encoded_data_size());
} }
// factory code
static uint8_t get_fc(const Indala40134CardData* card_data) {
uint8_t fc = 0;
fc = fc << 1 | get_bit(57, card_data);
fc = fc << 1 | get_bit(49, card_data);
fc = fc << 1 | get_bit(44, card_data);
fc = fc << 1 | get_bit(47, card_data);
fc = fc << 1 | get_bit(48, card_data);
fc = fc << 1 | get_bit(53, card_data);
fc = fc << 1 | get_bit(39, card_data);
fc = fc << 1 | get_bit(58, card_data);
return fc;
}
// card number
static uint16_t get_cn(const Indala40134CardData* card_data) {
uint16_t cn = 0;
cn = cn << 1 | get_bit(42, card_data);
cn = cn << 1 | get_bit(45, card_data);
cn = cn << 1 | get_bit(43, card_data);
cn = cn << 1 | get_bit(40, card_data);
cn = cn << 1 | get_bit(52, card_data);
cn = cn << 1 | get_bit(36, card_data);
cn = cn << 1 | get_bit(35, card_data);
cn = cn << 1 | get_bit(51, card_data);
cn = cn << 1 | get_bit(46, card_data);
cn = cn << 1 | get_bit(33, card_data);
cn = cn << 1 | get_bit(37, card_data);
cn = cn << 1 | get_bit(54, card_data);
cn = cn << 1 | get_bit(56, card_data);
cn = cn << 1 | get_bit(59, card_data);
cn = cn << 1 | get_bit(50, card_data);
cn = cn << 1 | get_bit(41, card_data);
return cn;
}
void ProtocolIndala40134::decode( void ProtocolIndala40134::decode(
const uint8_t* encoded_data, const uint8_t* encoded_data,
const uint8_t encoded_data_size, const uint8_t encoded_data_size,
@ -117,15 +162,76 @@ void ProtocolIndala40134::decode(
const uint8_t decoded_data_size) { const uint8_t decoded_data_size) {
furi_check(decoded_data_size >= get_decoded_data_size()); furi_check(decoded_data_size >= get_decoded_data_size());
furi_check(encoded_data_size >= get_encoded_data_size()); furi_check(encoded_data_size >= get_encoded_data_size());
// TODO implement decoding
furi_check(0); const Indala40134CardData* card_data =
reinterpret_cast<const Indala40134CardData*>(encoded_data);
uint8_t fc = get_fc(card_data);
uint16_t card = get_cn(card_data);
decoded_data[0] = fc;
decoded_data[1] = card >> 8;
decoded_data[2] = card;
} }
bool ProtocolIndala40134::can_be_decoded( bool ProtocolIndala40134::can_be_decoded(
const uint8_t* encoded_data, const uint8_t* encoded_data,
const uint8_t encoded_data_size) { const uint8_t encoded_data_size) {
furi_check(encoded_data_size >= get_encoded_data_size()); furi_check(encoded_data_size >= get_encoded_data_size());
// TODO implement decoding bool can_be_decoded = false;
furi_check(0);
return false; const Indala40134CardData* card_data =
reinterpret_cast<const Indala40134CardData*>(encoded_data);
do {
// preambula
if((*card_data >> 32) != 0xa0000000UL) break;
// data
const uint32_t fc_and_card = get_fc(card_data) << 16 | get_cn(card_data);
// checksum
const uint8_t checksum = get_bit(62, card_data) << 1 | get_bit(63, card_data);
uint8_t checksum_sum = 0;
checksum_sum += ((fc_and_card >> 14) & 1);
checksum_sum += ((fc_and_card >> 12) & 1);
checksum_sum += ((fc_and_card >> 9) & 1);
checksum_sum += ((fc_and_card >> 8) & 1);
checksum_sum += ((fc_and_card >> 6) & 1);
checksum_sum += ((fc_and_card >> 5) & 1);
checksum_sum += ((fc_and_card >> 2) & 1);
checksum_sum += ((fc_and_card >> 0) & 1);
checksum_sum = checksum_sum & 0b1;
if(checksum_sum == 1 && checksum == 0b01) {
} else if(checksum_sum == 0 && checksum == 0b10) {
} else {
break;
}
// wiegand parity bits
// even parity sum calculation (high 12 bits of data)
const bool even_parity = get_bit(34, card_data);
uint8_t even_parity_sum = 0;
for(int8_t i = 12; i < 24; i++) {
if(((fc_and_card >> i) & 1) == 1) {
even_parity_sum++;
}
}
if(even_parity_sum % 2 != even_parity) break;
// odd parity sum calculation (low 12 bits of data)
const bool odd_parity = get_bit(38, card_data);
uint8_t odd_parity_sum = 1;
for(int8_t i = 0; i < 12; i++) {
if(((fc_and_card >> i) & 1) == 1) {
odd_parity_sum++;
}
}
if(odd_parity_sum % 2 != odd_parity) break;
can_be_decoded = true;
} while(false);
return can_be_decoded;
} }

View File

@ -1,35 +0,0 @@
#include "rfid-name-generator.h"
#include <stdio.h>
#include <stdlib.h>
void rfid_generate_random_name(char* name, uint8_t max_name_size) {
const uint8_t prefix_size = 9;
const char* prefix[prefix_size] = {
"good",
"nice",
"best",
"some",
"strange",
"working",
"that",
"forgettable",
"easy",
};
const uint8_t suffix_size = 7;
const char* suffix[suffix_size] = {
"pass",
"card",
"key",
"fob",
"permit",
"pass",
"one",
};
sniprintf(
name, max_name_size, "%s_%s", prefix[rand() % prefix_size], suffix[rand() % suffix_size]);
// to upper
name[0] = name[0] - ('a' - 'A');
}

View File

@ -1,4 +0,0 @@
#pragma once
#include "stdint.h"
void rfid_generate_random_name(char* name, uint8_t max_name_size);

View File

@ -17,19 +17,47 @@ struct RfidReaderAccessor {
void RfidReader::decode(bool polarity) { void RfidReader::decode(bool polarity) {
uint32_t current_dwt_value = DWT->CYCCNT; uint32_t current_dwt_value = DWT->CYCCNT;
uint32_t period = current_dwt_value - last_dwt_value;
last_dwt_value = current_dwt_value;
//decoder_gpio_out.process_front(polarity, period);
switch(type) { switch(type) {
case Type::Normal: case Type::Normal:
decoder_em.process_front(polarity, current_dwt_value - last_dwt_value); decoder_em.process_front(polarity, period);
decoder_hid26.process_front(polarity, current_dwt_value - last_dwt_value); decoder_hid26.process_front(polarity, period);
//decoder_indala.process_front(polarity, current_dwt_value - last_dwt_value);
//decoder_analyzer.process_front(polarity, current_dwt_value - last_dwt_value);
last_dwt_value = current_dwt_value;
break; break;
case Type::Indala: case Type::Indala:
decoder_em.process_front(polarity, period);
decoder_hid26.process_front(polarity, period);
decoder_indala.process_front(polarity, period);
break; break;
} }
detect_ticks++;
}
bool RfidReader::switch_timer_elapsed() {
const uint32_t seconds_to_switch = osKernelGetTickFreq() * 2.0f;
return (osKernelGetTickCount() - switch_os_tick_last) > seconds_to_switch;
}
void RfidReader::switch_timer_reset() {
switch_os_tick_last = osKernelGetTickCount();
}
void RfidReader::switch_mode() {
switch(type) {
case Type::Normal:
type = Type::Indala;
api_hal_rfid_change_read_config(62500.0f, 0.25f);
break;
case Type::Indala:
type = Type::Normal;
api_hal_rfid_change_read_config(125000.0f, 0.5f);
break;
}
switch_timer_reset();
} }
static void comparator_trigger_callback(void* hcomp, void* comp_ctx) { static void comparator_trigger_callback(void* hcomp, void* comp_ctx) {
@ -45,47 +73,103 @@ static void comparator_trigger_callback(void* hcomp, void* comp_ctx) {
RfidReader::RfidReader() { RfidReader::RfidReader() {
} }
void RfidReader::start(Type _type) { void RfidReader::start() {
type = _type; type = Type::Normal;
start_gpio(); api_hal_rfid_pins_read();
api_hal_rfid_tim_read(125000, 0.5);
api_hal_rfid_tim_read_start();
start_comparator();
switch_timer_reset();
last_readed_count = 0;
}
void RfidReader::start_forced(RfidReader::Type _type) {
type = _type;
switch(type) { switch(type) {
case Type::Normal: case Type::Normal:
start_timer(); start();
break; break;
case Type::Indala: case Type::Indala:
start_timer_indala(); api_hal_rfid_pins_read();
api_hal_rfid_tim_read(62500.0f, 0.25f);
api_hal_rfid_tim_read_start();
start_comparator();
switch_timer_reset();
last_readed_count = 0;
break; break;
} }
start_comparator();
} }
void RfidReader::stop() { void RfidReader::stop() {
stop_gpio(); api_hal_rfid_pins_reset();
stop_timer(); api_hal_rfid_tim_read_stop();
api_hal_rfid_tim_reset();
stop_comparator(); stop_comparator();
} }
bool RfidReader::read(LfrfidKeyType* type, uint8_t* data, uint8_t data_size) { bool RfidReader::read(LfrfidKeyType* _type, uint8_t* data, uint8_t data_size) {
bool result = false; bool result = false;
bool something_readed = false;
// reading
if(decoder_em.read(data, data_size)) { if(decoder_em.read(data, data_size)) {
*type = LfrfidKeyType::KeyEM4100; *_type = LfrfidKeyType::KeyEM4100;
result = true; something_readed = true;
} }
if(decoder_hid26.read(data, data_size)) { if(decoder_hid26.read(data, data_size)) {
*type = LfrfidKeyType::KeyH10301; *_type = LfrfidKeyType::KeyH10301;
result = true; something_readed = true;
} }
//decoder_indala.read(NULL, 0); if(decoder_indala.read(data, data_size)) {
//decoder_analyzer.read(NULL, 0); *_type = LfrfidKeyType::KeyI40134;
something_readed = true;
}
// validation
if(something_readed) {
switch_timer_reset();
if(last_readed_type == *_type && memcmp(last_readed_data, data, data_size) == 0) {
last_readed_count = last_readed_count + 1;
if(last_readed_count > 2) {
result = true;
}
} else {
last_readed_type = *_type;
memcpy(last_readed_data, data, data_size);
last_readed_count = 0;
}
}
// mode switching
if(switch_timer_elapsed()) {
switch_mode();
last_readed_count = 0;
}
return result; return result;
} }
bool RfidReader::detect() {
bool detected = false;
if(detect_ticks > 10) {
detected = true;
}
detect_ticks = 0;
return detected;
}
bool RfidReader::any_read() {
return last_readed_count > 0;
}
void RfidReader::start_comparator(void) { void RfidReader::start_comparator(void) {
api_interrupt_add(comparator_trigger_callback, InterruptTypeComparatorTrigger, this); api_interrupt_add(comparator_trigger_callback, InterruptTypeComparatorTrigger, this);
last_dwt_value = DWT->CYCCNT; last_dwt_value = DWT->CYCCNT;
@ -93,7 +177,7 @@ void RfidReader::start_comparator(void) {
hcomp1.Init.InputMinus = COMP_INPUT_MINUS_1_2VREFINT; hcomp1.Init.InputMinus = COMP_INPUT_MINUS_1_2VREFINT;
hcomp1.Init.InputPlus = COMP_INPUT_PLUS_IO1; hcomp1.Init.InputPlus = COMP_INPUT_PLUS_IO1;
hcomp1.Init.OutputPol = COMP_OUTPUTPOL_NONINVERTED; hcomp1.Init.OutputPol = COMP_OUTPUTPOL_NONINVERTED;
hcomp1.Init.Hysteresis = COMP_HYSTERESIS_LOW; hcomp1.Init.Hysteresis = COMP_HYSTERESIS_HIGH;
hcomp1.Init.BlankingSrce = COMP_BLANKINGSRC_NONE; hcomp1.Init.BlankingSrce = COMP_BLANKINGSRC_NONE;
hcomp1.Init.Mode = COMP_POWERMODE_MEDIUMSPEED; hcomp1.Init.Mode = COMP_POWERMODE_MEDIUMSPEED;
hcomp1.Init.WindowMode = COMP_WINDOWMODE_DISABLE; hcomp1.Init.WindowMode = COMP_WINDOWMODE_DISABLE;
@ -105,30 +189,7 @@ void RfidReader::start_comparator(void) {
HAL_COMP_Start(&hcomp1); HAL_COMP_Start(&hcomp1);
} }
void RfidReader::start_timer(void) {
api_hal_rfid_tim_read(125000, 0.5);
api_hal_rfid_tim_read_start();
}
void RfidReader::start_timer_indala(void) {
api_hal_rfid_tim_read(62500, 0.25);
api_hal_rfid_tim_read_start();
}
void RfidReader::start_gpio(void) {
api_hal_rfid_pins_read();
}
void RfidReader::stop_comparator(void) { void RfidReader::stop_comparator(void) {
HAL_COMP_Stop(&hcomp1); HAL_COMP_Stop(&hcomp1);
api_interrupt_remove(comparator_trigger_callback, InterruptTypeComparatorTrigger); api_interrupt_remove(comparator_trigger_callback, InterruptTypeComparatorTrigger);
}
void RfidReader::stop_timer(void) {
api_hal_rfid_tim_read_stop();
api_hal_rfid_tim_reset();
}
void RfidReader::stop_gpio(void) {
api_hal_rfid_pins_reset();
} }

View File

@ -1,5 +1,6 @@
#pragma once #pragma once
#include "decoder-analyzer.h" //#include "decoder-analyzer.h"
#include "decoder-gpio-out.h"
#include "decoder-emmarine.h" #include "decoder-emmarine.h"
#include "decoder-hid26.h" #include "decoder-hid26.h"
#include "decoder-indala.h" #include "decoder-indala.h"
@ -13,14 +14,19 @@ public:
}; };
RfidReader(); RfidReader();
void start(Type type); void start();
void start_forced(RfidReader::Type type);
void stop(); void stop();
bool read(LfrfidKeyType* type, uint8_t* data, uint8_t data_size); bool read(LfrfidKeyType* type, uint8_t* data, uint8_t data_size);
bool detect();
bool any_read();
private: private:
friend struct RfidReaderAccessor; friend struct RfidReaderAccessor;
//DecoderAnalyzer decoder_analyzer; //DecoderAnalyzer decoder_analyzer;
//DecoderGpioOut decoder_gpio_out;
DecoderEMMarine decoder_em; DecoderEMMarine decoder_em;
DecoderHID26 decoder_hid26; DecoderHID26 decoder_hid26;
DecoderIndala decoder_indala; DecoderIndala decoder_indala;
@ -28,14 +34,20 @@ private:
uint32_t last_dwt_value; uint32_t last_dwt_value;
void start_comparator(void); void start_comparator(void);
void start_timer(void);
void start_timer_indala(void);
void start_gpio(void);
void stop_comparator(void); void stop_comparator(void);
void stop_timer(void);
void stop_gpio(void);
void decode(bool polarity); void decode(bool polarity);
uint32_t detect_ticks;
uint32_t switch_os_tick_last;
bool switch_timer_elapsed();
void switch_timer_reset();
void switch_mode();
LfrfidKeyType last_readed_type;
uint8_t last_readed_data[LFRFID_KEY_SIZE];
uint8_t last_readed_count;
Type type = Type::Normal; Type type = Type::Normal;
}; };

View File

@ -7,7 +7,7 @@ RfidWorker::~RfidWorker() {
} }
void RfidWorker::start_read() { void RfidWorker::start_read() {
reader.start(RfidReader::Type::Normal); reader.start();
} }
bool RfidWorker::read() { bool RfidWorker::read() {
@ -25,6 +25,14 @@ bool RfidWorker::read() {
return result; return result;
} }
bool RfidWorker::detect() {
return reader.detect();
}
bool RfidWorker::any_read() {
return reader.any_read();
}
void RfidWorker::stop_read() { void RfidWorker::stop_read() {
reader.stop(); reader.stop();
} }
@ -36,7 +44,7 @@ void RfidWorker::start_write() {
write_sequence->do_every_tick(1, std::bind(&RfidWorker::sq_write, this)); write_sequence->do_every_tick(1, std::bind(&RfidWorker::sq_write, this));
write_sequence->do_after_tick(2, std::bind(&RfidWorker::sq_write_start_validate, this)); write_sequence->do_after_tick(2, std::bind(&RfidWorker::sq_write_start_validate, this));
write_sequence->do_after_tick(15, std::bind(&RfidWorker::sq_write_validate, this)); write_sequence->do_every_tick(30, std::bind(&RfidWorker::sq_write_validate, this));
write_sequence->do_every_tick(1, std::bind(&RfidWorker::sq_write_stop_validate, this)); write_sequence->do_every_tick(1, std::bind(&RfidWorker::sq_write_stop_validate, this));
} }
@ -59,26 +67,37 @@ void RfidWorker::stop_emulate() {
} }
void RfidWorker::sq_write() { void RfidWorker::sq_write() {
// TODO expand this for(size_t i = 0; i < 5; i++) {
switch(key.get_type()) { switch(key.get_type()) {
case LfrfidKeyType::KeyEM4100: case LfrfidKeyType::KeyEM4100:
writer.start(); writer.start();
writer.write_em(key.get_data()); writer.write_em(key.get_data());
writer.stop(); writer.stop();
break; break;
case LfrfidKeyType::KeyH10301: case LfrfidKeyType::KeyH10301:
writer.start(); writer.start();
writer.write_hid(key.get_data()); writer.write_hid(key.get_data());
writer.stop(); writer.stop();
break; break;
case LfrfidKeyType::KeyI40134:
default: writer.start();
break; writer.write_indala(key.get_data());
writer.stop();
break;
}
} }
} }
void RfidWorker::sq_write_start_validate() { void RfidWorker::sq_write_start_validate() {
reader.start(RfidReader::Type::Normal); switch(key.get_type()) {
case LfrfidKeyType::KeyEM4100:
case LfrfidKeyType::KeyH10301:
reader.start_forced(RfidReader::Type::Normal);
break;
case LfrfidKeyType::KeyI40134:
reader.start_forced(RfidReader::Type::Indala);
break;
}
} }
void RfidWorker::sq_write_validate() { void RfidWorker::sq_write_validate() {
@ -88,7 +107,11 @@ void RfidWorker::sq_write_validate() {
bool result = reader.read(&type, data, data_size); bool result = reader.read(&type, data, data_size);
if(result) { if(result && (write_result != WriteResult::Ok)) {
if(validate_counts > (5 * 60)) {
write_result = WriteResult::NotWritable;
}
if(type == key.get_type()) { if(type == key.get_type()) {
if(memcmp(data, key.get_data(), key.get_type_data_count()) == 0) { if(memcmp(data, key.get_data(), key.get_type_data_count()) == 0) {
write_result = WriteResult::Ok; write_result = WriteResult::Ok;
@ -99,10 +122,6 @@ void RfidWorker::sq_write_validate() {
} else { } else {
validate_counts++; validate_counts++;
} }
if(validate_counts > 5) {
write_result = WriteResult::NotWritable;
}
}; };
} }

View File

@ -13,6 +13,8 @@ public:
void start_read(); void start_read();
bool read(); bool read();
bool detect();
bool any_read();
void stop_read(); void stop_read();
enum class WriteResult : uint8_t { enum class WriteResult : uint8_t {

View File

@ -2,6 +2,7 @@
#include <api-hal.h> #include <api-hal.h>
#include "protocols/protocol-emmarin.h" #include "protocols/protocol-emmarin.h"
#include "protocols/protocol-hid-h10301.h" #include "protocols/protocol-hid-h10301.h"
#include "protocols/protocol-indala-40134.h"
extern COMP_HandleTypeDef hcomp1; extern COMP_HandleTypeDef hcomp1;
@ -115,7 +116,7 @@ void RfidWriter::write_em(const uint8_t em_data[5]) {
ProtocolEMMarin em_card; ProtocolEMMarin em_card;
uint64_t em_encoded_data; uint64_t em_encoded_data;
em_card.encode(em_data, 5, reinterpret_cast<uint8_t*>(&em_encoded_data), sizeof(uint64_t)); em_card.encode(em_data, 5, reinterpret_cast<uint8_t*>(&em_encoded_data), sizeof(uint64_t));
const uint32_t em_config_block_data = 0b01100000000101001000000001000000; const uint32_t em_config_block_data = 0b00000000000101001000000001000000;
__disable_irq(); __disable_irq();
write_block(0, 0, false, em_config_block_data); write_block(0, 0, false, em_config_block_data);
@ -140,3 +141,19 @@ void RfidWriter::write_hid(const uint8_t hid_data[3]) {
write_reset(); write_reset();
__enable_irq(); __enable_irq();
} }
void RfidWriter::write_indala(const uint8_t indala_data[3]) {
ProtocolIndala40134 indala_card;
uint32_t card_data[2];
indala_card.encode(
indala_data, 3, reinterpret_cast<uint8_t*>(&card_data), sizeof(card_data) * 2);
const uint32_t indala_config_block_data = 0b00000000000010000001000001000000;
__disable_irq();
write_block(0, 0, false, indala_config_block_data);
write_block(0, 1, false, card_data[0]);
write_block(0, 2, false, card_data[1]);
write_reset();
__enable_irq();
}

View File

@ -9,6 +9,7 @@ public:
void stop(); void stop();
void write_em(const uint8_t em_data[5]); void write_em(const uint8_t em_data[5]);
void write_hid(const uint8_t hid_data[3]); void write_hid(const uint8_t hid_data[3]);
void write_indala(const uint8_t indala_data[3]);
private: private:
void write_gap(uint32_t gap_time); void write_gap(uint32_t gap_time);

View File

@ -17,7 +17,7 @@
#include "scene/lfrfid-app-scene-delete-success.h" #include "scene/lfrfid-app-scene-delete-success.h"
#include <file-worker-cpp.h> #include <file-worker-cpp.h>
#include <path.h> #include <lib/toolbox/path.h>
const char* LfRfidApp::app_folder = "/any/lfrfid"; const char* LfRfidApp::app_folder = "/any/lfrfid";
const char* LfRfidApp::app_extension = ".rfid"; const char* LfRfidApp::app_extension = ".rfid";
@ -27,9 +27,6 @@ LfRfidApp::LfRfidApp()
, notification{"notification"} , notification{"notification"}
, text_store(40) { , text_store(40) {
api_hal_power_insomnia_enter(); api_hal_power_insomnia_enter();
// we need random
srand(DWT->CYCCNT);
} }
LfRfidApp::~LfRfidApp() { LfRfidApp::~LfRfidApp() {

View File

@ -2,7 +2,7 @@
#include <api-hal.h> #include <api-hal.h>
#include <stdarg.h> #include <stdarg.h>
#include <cli/cli.h> #include <cli/cli.h>
#include <args.h> #include <lib/toolbox/args.h>
#include "helpers/rfid-reader.h" #include "helpers/rfid-reader.h"
#include "helpers/rfid-timer-emulator.h" #include "helpers/rfid-timer-emulator.h"
@ -46,7 +46,7 @@ bool lfrfid_cli_get_key_type(string_t data, LfrfidKeyType* type) {
void lfrfid_cli_read(Cli* cli) { void lfrfid_cli_read(Cli* cli) {
RfidReader reader; RfidReader reader;
reader.start(RfidReader::Type::Normal); reader.start();
static const uint8_t data_size = LFRFID_KEY_SIZE; static const uint8_t data_size = LFRFID_KEY_SIZE;
uint8_t data[data_size] = {0}; uint8_t data[data_size] = {0};

View File

@ -55,6 +55,7 @@ void LfRfidAppSceneReadSuccess::on_enter(LfRfidApp* app, bool need_restore) {
string_get_cstr(string[2]), 68, 47, AlignLeft, AlignBottom, FontSecondary); string_get_cstr(string[2]), 68, 47, AlignLeft, AlignBottom, FontSecondary);
break; break;
case LfrfidKeyType::KeyH10301: case LfrfidKeyType::KeyH10301:
case LfrfidKeyType::KeyI40134:
line_1_text->set_text("HEX:", 65, 23, AlignRight, AlignBottom, FontSecondary); line_1_text->set_text("HEX:", 65, 23, AlignRight, AlignBottom, FontSecondary);
line_2_text->set_text("FC:", 65, 35, AlignRight, AlignBottom, FontSecondary); line_2_text->set_text("FC:", 65, 35, AlignRight, AlignBottom, FontSecondary);
line_3_text->set_text("Card:", 65, 47, AlignRight, AlignBottom, FontSecondary); line_3_text->set_text("Card:", 65, 47, AlignRight, AlignBottom, FontSecondary);
@ -73,9 +74,6 @@ void LfRfidAppSceneReadSuccess::on_enter(LfRfidApp* app, bool need_restore) {
line_3_value->set_text( line_3_value->set_text(
string_get_cstr(string[2]), 68, 47, AlignLeft, AlignBottom, FontSecondary); string_get_cstr(string[2]), 68, 47, AlignLeft, AlignBottom, FontSecondary);
break; break;
case LfrfidKeyType::KeyI40134:
//TODO implement when we can read Indala
break;
} }
app->view_controller.switch_to<ContainerVM>(); app->view_controller.switch_to<ContainerVM>();

View File

@ -18,7 +18,13 @@ bool LfRfidAppSceneRead::on_event(LfRfidApp* app, LfRfidApp::Event* event) {
notification_message(app->notification, &sequence_success); notification_message(app->notification, &sequence_success);
app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::ReadSuccess); app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::ReadSuccess);
} else { } else {
notification_message(app->notification, &sequence_blink_red_10); if(app->worker.any_read()) {
notification_message(app->notification, &sequence_blink_green_10);
} else if(app->worker.detect()) {
notification_message(app->notification, &sequence_blink_blue_10);
} else {
notification_message(app->notification, &sequence_blink_red_10);
}
} }
} }

View File

@ -1,11 +1,12 @@
#include "lfrfid-app-scene-save-name.h" #include "lfrfid-app-scene-save-name.h"
#include "../helpers/rfid-name-generator.h" #include <lib/toolbox/random_name.h>
void LfRfidAppSceneSaveName::on_enter(LfRfidApp* app, bool need_restore) { void LfRfidAppSceneSaveName::on_enter(LfRfidApp* app, bool need_restore) {
const char* key_name = app->worker.key.get_name(); const char* key_name = app->worker.key.get_name();
if(strcmp(key_name, "") == 0) { bool key_name_empty = !strcmp(key_name, "");
rfid_generate_random_name(app->text_store.text, app->text_store.text_size); if(key_name_empty) {
set_random_name(app->text_store.text, app->text_store.text_size);
} else { } else {
app->text_store.set("%s", key_name); app->text_store.set("%s", key_name);
} }
@ -14,7 +15,11 @@ void LfRfidAppSceneSaveName::on_enter(LfRfidApp* app, bool need_restore) {
text_input->set_header_text("Name the card"); text_input->set_header_text("Name the card");
text_input->set_result_callback( text_input->set_result_callback(
save_callback, app, app->text_store.text, app->worker.key.get_name_length()); save_callback,
app,
app->text_store.text,
app->worker.key.get_name_length(),
key_name_empty);
app->view_controller.switch_to<TextInputVM>(); app->view_controller.switch_to<TextInputVM>();
} }

7
applications/nfc/nfc.c Normal file → Executable file
View File

@ -7,10 +7,10 @@ bool nfc_custom_event_callback(void* context, uint32_t event) {
return scene_manager_handle_custom_event(nfc->scene_manager, event); return scene_manager_handle_custom_event(nfc->scene_manager, event);
} }
bool nfc_navigation_event_callback(void* context) { bool nfc_back_event_callback(void* context) {
furi_assert(context); furi_assert(context);
Nfc* nfc = (Nfc*)context; Nfc* nfc = (Nfc*)context;
return scene_manager_handle_navigation_event(nfc->scene_manager); return scene_manager_handle_back_event(nfc->scene_manager);
} }
void nfc_tick_event_callback(void* context) { void nfc_tick_event_callback(void* context) {
@ -28,8 +28,7 @@ Nfc* nfc_alloc() {
view_dispatcher_enable_queue(nfc->view_dispatcher); view_dispatcher_enable_queue(nfc->view_dispatcher);
view_dispatcher_set_event_callback_context(nfc->view_dispatcher, nfc); view_dispatcher_set_event_callback_context(nfc->view_dispatcher, nfc);
view_dispatcher_set_custom_event_callback(nfc->view_dispatcher, nfc_custom_event_callback); view_dispatcher_set_custom_event_callback(nfc->view_dispatcher, nfc_custom_event_callback);
view_dispatcher_set_navigation_event_callback( view_dispatcher_set_navigation_event_callback(nfc->view_dispatcher, nfc_back_event_callback);
nfc->view_dispatcher, nfc_navigation_event_callback);
view_dispatcher_set_tick_event_callback(nfc->view_dispatcher, nfc_tick_event_callback, 100); view_dispatcher_set_tick_event_callback(nfc->view_dispatcher, nfc_tick_event_callback, 100);
// Open GUI record // Open GUI record

View File

@ -19,7 +19,6 @@ void nfc_cli_detect(Cli* cli, string_t args, void* context) {
rfalNfcDevice* dev_list; rfalNfcDevice* dev_list;
uint8_t dev_cnt = 0; uint8_t dev_cnt = 0;
bool cmd_exit = false; bool cmd_exit = false;
api_hal_nfc_init();
api_hal_nfc_exit_sleep(); api_hal_nfc_exit_sleep();
printf("Detecting nfc...\r\nPress Ctrl+C to abort\r\n"); printf("Detecting nfc...\r\nPress Ctrl+C to abort\r\n");
while(!cmd_exit) { while(!cmd_exit) {
@ -51,7 +50,6 @@ void nfc_cli_emulate(Cli* cli, string_t args, void* context) {
return; return;
} }
api_hal_nfc_init();
api_hal_nfc_exit_sleep(); api_hal_nfc_exit_sleep();
printf("Emulating NFC-A Type: T2T UID: CF72D440 SAK: 20 ATQA: 00/04\r\n"); printf("Emulating NFC-A Type: T2T UID: CF72D440 SAK: 20 ATQA: 00/04\r\n");
printf("Press Ctrl+C to abort\r\n"); printf("Press Ctrl+C to abort\r\n");

View File

@ -1,8 +1,8 @@
#include "nfc_device_i.h" #include "nfc_device_i.h"
#include <file-worker.h> #include <file-worker.h>
#include <path.h> #include <lib/toolbox/path.h>
#include <hex.h> #include <lib/toolbox/hex.h>
#define NFC_DEVICE_MAX_DATA_LEN 14 #define NFC_DEVICE_MAX_DATA_LEN 14
@ -203,6 +203,10 @@ uint16_t nfc_device_prepare_bank_card_string(NfcDevice* dev, string_t bank_card_
for(uint8_t i = 0; i < sizeof(data->number); i++) { for(uint8_t i = 0; i < sizeof(data->number); i++) {
string_cat_printf(bank_card_string, " %02X", data->number[i]); string_cat_printf(bank_card_string, " %02X", data->number[i]);
} }
if(data->exp_mon) {
string_cat_printf(
bank_card_string, "\nExp date: %02X/%02X", data->exp_mon, data->exp_year);
}
return string_size(bank_card_string); return string_size(bank_card_string);
} }
@ -236,6 +240,14 @@ bool nfc_device_parse_bank_card_string(NfcDevice* dev, string_t bank_card_string
break; break;
} }
parsed = true; parsed = true;
// Check expiration date presence
ws = string_search_str(bank_card_string, "Exp date: ");
if(ws != STRING_FAILURE) {
// strlen("Exp date: ") = 10
string_right(bank_card_string, 10);
nfc_device_read_hex(bank_card_string, &data->exp_mon, 1);
nfc_device_read_hex(bank_card_string, &data->exp_year, 1);
}
} while(0); } while(0);
return parsed; return parsed;

View File

@ -42,7 +42,7 @@ typedef struct {
uint16_t aid_len; uint16_t aid_len;
uint8_t number[8]; uint8_t number[8];
uint8_t exp_mon; uint8_t exp_mon;
uint16_t exp_year; uint8_t exp_year;
char cardholder[32]; char cardholder[32];
} NfcEmvData; } NfcEmvData;

View File

@ -15,9 +15,7 @@ NfcWorker* nfc_worker_alloc() {
nfc_worker->callback = NULL; nfc_worker->callback = NULL;
nfc_worker->context = NULL; nfc_worker->context = NULL;
// Initialize rfal // Initialize rfal
nfc_worker->error = api_hal_nfc_init(); if(!api_hal_nfc_is_busy()) {
if(nfc_worker->error == ERR_NONE) {
api_hal_nfc_start_sleep();
nfc_worker_change_state(nfc_worker, NfcWorkerStateReady); nfc_worker_change_state(nfc_worker, NfcWorkerStateReady);
} else { } else {
nfc_worker_change_state(nfc_worker, NfcWorkerStateBroken); nfc_worker_change_state(nfc_worker, NfcWorkerStateBroken);
@ -35,10 +33,6 @@ NfcWorkerState nfc_worker_get_state(NfcWorker* nfc_worker) {
return nfc_worker->state; return nfc_worker->state;
} }
ReturnCode nfc_worker_get_error(NfcWorker* nfc_worker) {
return nfc_worker->error;
}
void nfc_worker_start( void nfc_worker_start(
NfcWorker* nfc_worker, NfcWorker* nfc_worker,
NfcWorkerState state, NfcWorkerState state,
@ -87,8 +81,8 @@ void nfc_worker_task(void* context) {
nfc_worker_read_emv_app(nfc_worker); nfc_worker_read_emv_app(nfc_worker);
} else if(nfc_worker->state == NfcWorkerStateReadEMV) { } else if(nfc_worker->state == NfcWorkerStateReadEMV) {
nfc_worker_read_emv(nfc_worker); nfc_worker_read_emv(nfc_worker);
} else if(nfc_worker->state == NfcWorkerStateEmulateEMV) { } else if(nfc_worker->state == NfcWorkerStateEmulateApdu) {
nfc_worker_emulate_emv(nfc_worker); nfc_worker_emulate_apdu(nfc_worker);
} else if(nfc_worker->state == NfcWorkerStateReadMifareUl) { } else if(nfc_worker->state == NfcWorkerStateReadMifareUl) {
nfc_worker_read_mifare_ul(nfc_worker); nfc_worker_read_mifare_ul(nfc_worker);
} else if(nfc_worker->state == NfcWorkerStateEmulateMifareUl) { } else if(nfc_worker->state == NfcWorkerStateEmulateMifareUl) {
@ -330,6 +324,10 @@ void nfc_worker_read_emv(NfcWorker* nfc_worker) {
result->emv_data.number, result->emv_data.number,
emv_app.card_number, emv_app.card_number,
sizeof(emv_app.card_number)); sizeof(emv_app.card_number));
if(emv_app.exp_month) {
result->emv_data.exp_mon = emv_app.exp_month;
result->emv_data.exp_year = emv_app.exp_year;
}
// Notify caller and exit // Notify caller and exit
if(nfc_worker->callback) { if(nfc_worker->callback) {
nfc_worker->callback(nfc_worker->context); nfc_worker->callback(nfc_worker->context);
@ -354,7 +352,7 @@ void nfc_worker_read_emv(NfcWorker* nfc_worker) {
} }
} }
void nfc_worker_emulate_emv(NfcWorker* nfc_worker) { void nfc_worker_emulate_apdu(NfcWorker* nfc_worker) {
ReturnCode err; ReturnCode err;
uint8_t tx_buff[255] = {}; uint8_t tx_buff[255] = {};
uint16_t tx_len = 0; uint16_t tx_len = 0;
@ -368,9 +366,47 @@ void nfc_worker_emulate_emv(NfcWorker* nfc_worker) {
.device = NfcDeviceNfca, .device = NfcDeviceNfca,
.protocol = NfcDeviceProtocolEMV, .protocol = NfcDeviceProtocolEMV,
}; };
// Test RX data
const uint8_t debug_rx[] = {
0xba, 0x0b, 0xba, 0xba, 0x20, 0x00, 0x02, 0x28, 0xde, 0xad, 0xbe, 0xef, 0x00, 0xca, 0xca,
0xca, 0xfe, 0xfa, 0xce, 0x14, 0x88, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88,
0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0xba,
0x0b, 0xba, 0xba, 0x20, 0x00, 0x02, 0x28, 0xde, 0xad, 0xbe, 0xef, 0x00, 0xca, 0xca, 0xca,
0xfe, 0xfa, 0xce, 0x14, 0x88, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0xba, 0x0b,
0xba, 0xba, 0x20, 0x00, 0x02, 0x28, 0xde, 0xad, 0xbe, 0xef, 0x00, 0xca, 0xca, 0xca, 0xfe,
0xfa, 0xce, 0x14, 0x88, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa,
0xbb, 0xcc, 0xdd, 0xee, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0xba, 0x0b, 0xba,
0xba, 0x20, 0x00, 0x02, 0x28, 0xde, 0xad, 0xbe, 0xef, 0x00, 0xca, 0xca, 0xca, 0xfe, 0xfa,
0xce, 0x14, 0x88, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb,
0xcc, 0xdd, 0xee, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0xba, 0x0b, 0xba, 0xba,
0x20, 0x00, 0x02, 0x28, 0xde, 0xad, 0xbe, 0xef, 0x00, 0xca, 0xca, 0xca, 0xfe, 0xfa, 0xce,
0x14, 0x88, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc,
0xdd, 0xee, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0xba, 0x0b, 0xba, 0xba, 0x20,
0x00, 0x02, 0x28, 0xde, 0xad, 0xbe, 0xef, 0x00, 0xca, 0xca, 0xca, 0xfe, 0xfa, 0xce, 0x14,
0x88, 0x00};
// Test TX data
const uint8_t debug_tx[] = {
0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xff, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32,
0x10, 0x14, 0x88, 0x02, 0x28, 0x00, 0x00, 0xca, 0xca, 0x00, 0xc0, 0xc0, 0x00, 0xde, 0xad,
0xbe, 0xef, 0xce, 0xee, 0xec, 0xca, 0xfe, 0xba, 0xba, 0xb0, 0xb0, 0xac, 0xdc, 0x11, 0x12,
0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xff, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10,
0x14, 0x88, 0x02, 0x28, 0x00, 0x00, 0xca, 0xca, 0x00, 0xc0, 0xc0, 0x00, 0xde, 0xad, 0xbe,
0xef, 0xce, 0xee, 0xec, 0xca, 0xfe, 0xba, 0xba, 0xb0, 0xb0, 0xac, 0xdc, 0x11, 0x12, 0x34,
0x56, 0x78, 0x9a, 0xbc, 0xde, 0xff, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0x14,
0x88, 0x02, 0x28, 0x00, 0x00, 0xca, 0xca, 0x00, 0xc0, 0xc0, 0x00, 0xde, 0xad, 0xbe, 0xef,
0xce, 0xee, 0xec, 0xca, 0xfe, 0xba, 0xba, 0xb0, 0xb0, 0xac, 0xdc, 0x11, 0x12, 0x34, 0x56,
0x78, 0x9a, 0xbc, 0xde, 0xff, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0x14, 0x88,
0x02, 0x28, 0x00, 0x00, 0xca, 0xca, 0x00, 0xc0, 0xc0, 0x00, 0xde, 0xad, 0xbe, 0xef, 0xce,
0xee, 0xec, 0xca, 0xfe, 0xba, 0xba, 0xb0, 0xb0, 0xac, 0xdc, 0x11, 0x12, 0x34, 0x56, 0x78,
0x9a, 0xbc, 0xde, 0xff, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0x14, 0x88, 0x02,
0x28, 0x00, 0x00, 0xca, 0xca, 0x00, 0xc0, 0xc0, 0x00, 0xde, 0xad, 0xbe, 0xef, 0xce, 0xee,
0xec, 0xca, 0xfe, 0xba, 0xba, 0xb0, 0xb0, 0xac, 0xdc, 0x11, 0x12, 0x34, 0x56, 0x78, 0x9a,
0xbc, 0xde, 0xff, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0x14, 0x88, 0x02, 0x28,
0x00, 0x00};
while(nfc_worker->state == NfcWorkerStateEmulateEMV) { while(nfc_worker->state == NfcWorkerStateEmulateApdu) {
if(api_hal_nfc_listen(params.uid, params.uid_len, params.atqa, params.sak, 100)) { if(api_hal_nfc_listen(params.uid, params.uid_len, params.atqa, params.sak, 300)) {
FURI_LOG_I(NFC_WORKER_TAG, "POS terminal detected"); FURI_LOG_I(NFC_WORKER_TAG, "POS terminal detected");
// Read data from POS terminal // Read data from POS terminal
err = api_hal_nfc_data_exchange(NULL, 0, &rx_buff, &rx_len, false); err = api_hal_nfc_data_exchange(NULL, 0, &rx_buff, &rx_len, false);
@ -407,7 +443,23 @@ void nfc_worker_emulate_emv(NfcWorker* nfc_worker) {
tx_len = emv_get_proc_opt_ans(tx_buff); tx_len = emv_get_proc_opt_ans(tx_buff);
err = api_hal_nfc_data_exchange(tx_buff, tx_len, &rx_buff, &rx_len, false); err = api_hal_nfc_data_exchange(tx_buff, tx_len, &rx_buff, &rx_len, false);
if(err == ERR_NONE) { if(err == ERR_NONE) {
FURI_LOG_I(NFC_WORKER_TAG, "Received PDOL"); FURI_LOG_I(NFC_WORKER_TAG, "Transive PDOL ANS");
} else {
FURI_LOG_E(NFC_WORKER_TAG, "Error in 4rd data exchange: Transive PDOL ANS");
api_hal_nfc_deactivate();
continue;
}
if(*rx_len != sizeof(debug_rx) || memcmp(rx_buff, debug_rx, sizeof(debug_rx))) {
FURI_LOG_E(NFC_WORKER_TAG, "Failed long message test");
} else {
FURI_LOG_I(NFC_WORKER_TAG, "Correct debug message received");
tx_len = sizeof(debug_tx);
err = api_hal_nfc_data_exchange(
(uint8_t*)debug_tx, tx_len, &rx_buff, &rx_len, false);
if(err == ERR_NONE) {
FURI_LOG_I(NFC_WORKER_TAG, "Transive Debug message");
}
} }
api_hal_nfc_deactivate(); api_hal_nfc_deactivate();
} else { } else {

View File

@ -14,7 +14,7 @@ typedef enum {
NfcWorkerStateEmulate, NfcWorkerStateEmulate,
NfcWorkerStateReadEMVApp, NfcWorkerStateReadEMVApp,
NfcWorkerStateReadEMV, NfcWorkerStateReadEMV,
NfcWorkerStateEmulateEMV, NfcWorkerStateEmulateApdu,
NfcWorkerStateField, NfcWorkerStateField,
NfcWorkerStateReadMifareUl, NfcWorkerStateReadMifareUl,
NfcWorkerStateEmulateMifareUl, NfcWorkerStateEmulateMifareUl,
@ -28,8 +28,6 @@ NfcWorker* nfc_worker_alloc();
NfcWorkerState nfc_worker_get_state(NfcWorker* nfc_worker); NfcWorkerState nfc_worker_get_state(NfcWorker* nfc_worker);
ReturnCode nfc_worker_get_error(NfcWorker* nfc_worker);
void nfc_worker_free(NfcWorker* nfc_worker); void nfc_worker_free(NfcWorker* nfc_worker);
void nfc_worker_start( void nfc_worker_start(

View File

@ -26,7 +26,6 @@ struct NfcWorker {
void* context; void* context;
NfcWorkerState state; NfcWorkerState state;
ReturnCode error;
}; };
void nfc_worker_change_state(NfcWorker* nfc_worker, NfcWorkerState state); void nfc_worker_change_state(NfcWorker* nfc_worker, NfcWorkerState state);
@ -37,7 +36,7 @@ void nfc_worker_read_emv_app(NfcWorker* nfc_worker);
void nfc_worker_read_emv(NfcWorker* nfc_worker); void nfc_worker_read_emv(NfcWorker* nfc_worker);
void nfc_worker_emulate_emv(NfcWorker* nfc_worker); void nfc_worker_emulate_apdu(NfcWorker* nfc_worker);
void nfc_worker_detect(NfcWorker* nfc_worker); void nfc_worker_detect(NfcWorker* nfc_worker);

View File

@ -70,8 +70,9 @@ const bool nfc_scene_card_menu_on_event(void* context, SceneManagerEvent event)
scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName);
return true; return true;
} }
} else if(event.type == SceneManagerEventTypeNavigation) { } else if(event.type == SceneManagerEventTypeBack) {
return scene_manager_search_previous_scene(nfc->scene_manager, NfcSceneStart); return scene_manager_search_and_switch_to_previous_scene(
nfc->scene_manager, NfcSceneStart);
} }
return false; return false;

View File

@ -25,3 +25,4 @@ ADD_SCENE(nfc, delete_success, DeleteSuccess)
ADD_SCENE(nfc, run_emv_app_confirm, RunEmvAppConfirm) ADD_SCENE(nfc, run_emv_app_confirm, RunEmvAppConfirm)
ADD_SCENE(nfc, read_emv_data, ReadEmvData) ADD_SCENE(nfc, read_emv_data, ReadEmvData)
ADD_SCENE(nfc, read_emv_data_success, ReadEmvDataSuccess) ADD_SCENE(nfc, read_emv_data_success, ReadEmvDataSuccess)
ADD_SCENE(nfc, emulate_apdu_sequence, EmulateApduSequence)

3
applications/nfc/scenes/nfc_scene_delete.c Normal file → Executable file
View File

@ -75,7 +75,8 @@ const bool nfc_scene_delete_on_event(void* context, SceneManagerEvent event) {
if(nfc_device_delete(&nfc->dev)) { if(nfc_device_delete(&nfc->dev)) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneDeleteSuccess); scene_manager_next_scene(nfc->scene_manager, NfcSceneDeleteSuccess);
} else { } else {
scene_manager_search_previous_scene(nfc->scene_manager, NfcSceneStart); scene_manager_search_and_switch_to_previous_scene(
nfc->scene_manager, NfcSceneStart);
} }
return true; return true;
} }

3
applications/nfc/scenes/nfc_scene_delete_success.c Normal file → Executable file
View File

@ -26,7 +26,8 @@ const bool nfc_scene_delete_success_on_event(void* context, SceneManagerEvent ev
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SCENE_SAVE_SUCCESS_CUSTOM_EVENT) { if(event.event == SCENE_SAVE_SUCCESS_CUSTOM_EVENT) {
return scene_manager_search_previous_scene(nfc->scene_manager, NfcSceneStart); return scene_manager_search_and_switch_to_previous_scene(
nfc->scene_manager, NfcSceneStart);
} }
} }
return false; return false;

View File

@ -0,0 +1,38 @@
#include "../nfc_i.h"
const void nfc_scene_emulate_apdu_sequence_on_enter(void* context) {
Nfc* nfc = (Nfc*)context;
// Setup view
Popup* popup = nfc->popup;
popup_set_header(popup, "Run APDU reader", 64, 31, AlignCenter, AlignTop);
// Setup and start worker
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup);
nfc_worker_start(nfc->worker, NfcWorkerStateEmulateApdu, &nfc->dev.dev_data, NULL, nfc);
}
const bool nfc_scene_emulate_apdu_sequence_on_event(void* context, SceneManagerEvent event) {
Nfc* nfc = (Nfc*)context;
if(event.type == SceneManagerEventTypeTick) {
notification_message(nfc->notifications, &sequence_blink_blue_10);
return true;
}
return false;
}
const void nfc_scene_emulate_apdu_sequence_on_exit(void* context) {
Nfc* nfc = (Nfc*)context;
// Stop worker
nfc_worker_stop(nfc->worker);
// Clear view
Popup* popup = nfc->popup;
popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom);
popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop);
popup_set_icon(popup, 0, 0, NULL);
}

View File

@ -6,7 +6,7 @@ const void nfc_scene_file_select_on_enter(void* context) {
if(nfc_file_select(&nfc->dev)) { if(nfc_file_select(&nfc->dev)) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneSavedMenu); scene_manager_next_scene(nfc->scene_manager, NfcSceneSavedMenu);
} else { } else {
scene_manager_search_previous_scene(nfc->scene_manager, NfcSceneStart); scene_manager_search_and_switch_to_previous_scene(nfc->scene_manager, NfcSceneStart);
} }
} }

5
applications/nfc/scenes/nfc_scene_mifare_ul_menu.c Normal file → Executable file
View File

@ -41,8 +41,9 @@ const bool nfc_scene_mifare_ul_menu_on_event(void* context, SceneManagerEvent ev
scene_manager_next_scene(nfc->scene_manager, NfcSceneNotImplemented); scene_manager_next_scene(nfc->scene_manager, NfcSceneNotImplemented);
return true; return true;
} }
} else if(event.type == SceneManagerEventTypeNavigation) { } else if(event.type == SceneManagerEventTypeBack) {
return scene_manager_search_previous_scene(nfc->scene_manager, NfcSceneStart); return scene_manager_search_and_switch_to_previous_scene(
nfc->scene_manager, NfcSceneStart);
} }
return false; return false;

View File

@ -59,6 +59,12 @@ void nfc_scene_read_emv_data_success_on_enter(void* context) {
char sak_str[16]; char sak_str[16];
snprintf(sak_str, sizeof(sak_str), "SAK: %02X", nfc_data->sak); snprintf(sak_str, sizeof(sak_str), "SAK: %02X", nfc_data->sak);
widget_add_string_element(nfc->widget, 121, 42, AlignRight, AlignTop, FontSecondary, sak_str); widget_add_string_element(nfc->widget, 121, 42, AlignRight, AlignTop, FontSecondary, sak_str);
if(emv_data->exp_mon) {
char exp_str[16];
snprintf(
exp_str, sizeof(exp_str), "Exp: %02X/%02X", emv_data->exp_mon, emv_data->exp_year);
widget_add_string_element(nfc->widget, 7, 32, AlignLeft, AlignTop, FontSecondary, exp_str);
}
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
} }
@ -68,15 +74,16 @@ const bool nfc_scene_read_emv_data_success_on_event(void* context, SceneManagerE
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
if(event.event == GuiButtonTypeLeft) { if(event.event == GuiButtonTypeLeft) {
return scene_manager_search_previous_scene( return scene_manager_search_and_switch_to_previous_scene(
nfc->scene_manager, NfcSceneReadEmvAppSuccess); nfc->scene_manager, NfcSceneReadEmvAppSuccess);
} else if(event.event == GuiButtonTypeRight) { } else if(event.event == GuiButtonTypeRight) {
nfc->dev.format = NfcDeviceSaveFormatBankCard; nfc->dev.format = NfcDeviceSaveFormatBankCard;
scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName);
return true; return true;
} }
} else if(event.type == SceneManagerEventTypeNavigation) { } else if(event.type == SceneManagerEventTypeBack) {
return scene_manager_search_previous_scene(nfc->scene_manager, NfcSceneReadEmvAppSuccess); return scene_manager_search_and_switch_to_previous_scene(
nfc->scene_manager, NfcSceneReadEmvAppSuccess);
} }
return false; return false;
} }

View File

@ -1,4 +1,5 @@
#include "../nfc_i.h" #include "../nfc_i.h"
#include <lib/toolbox/random_name.h>
#define SCENE_SAVE_NAME_CUSTOM_EVENT (0UL) #define SCENE_SAVE_NAME_CUSTOM_EVENT (0UL)
@ -13,17 +14,21 @@ const void nfc_scene_save_name_on_enter(void* context) {
// Setup view // Setup view
TextInput* text_input = nfc->text_input; TextInput* text_input = nfc->text_input;
if(nfc->dev.dev_name) { bool dev_name_empty = false;
nfc_device_delete(&nfc->dev); if(!strcmp(nfc->dev.dev_name, "")) {
set_random_name(nfc->text_store, sizeof(nfc->text_store));
dev_name_empty = true;
} else {
nfc_text_store_set(nfc, nfc->dev.dev_name);
} }
nfc_text_store_set(nfc, nfc->dev.dev_name);
text_input_set_header_text(text_input, "Name the card"); text_input_set_header_text(text_input, "Name the card");
text_input_set_result_callback( text_input_set_result_callback(
text_input, text_input,
nfc_scene_save_name_text_input_callback, nfc_scene_save_name_text_input_callback,
nfc, nfc,
nfc->text_store, nfc->text_store,
sizeof(nfc->text_store)); NFC_DEV_NAME_MAX_LEN,
dev_name_empty);
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextInput); view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextInput);
} }
@ -32,12 +37,16 @@ const bool nfc_scene_save_name_on_event(void* context, SceneManagerEvent event)
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SCENE_SAVE_NAME_CUSTOM_EVENT) { if(event.event == SCENE_SAVE_NAME_CUSTOM_EVENT) {
if(nfc->dev.dev_name) {
nfc_device_delete(&nfc->dev);
}
memcpy(&nfc->dev.dev_name, nfc->text_store, strlen(nfc->text_store)); memcpy(&nfc->dev.dev_name, nfc->text_store, strlen(nfc->text_store));
if(nfc_device_save(&nfc->dev, nfc->text_store)) { if(nfc_device_save(&nfc->dev, nfc->text_store)) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveSuccess); scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveSuccess);
return true; return true;
} else { } else {
return scene_manager_search_previous_scene(nfc->scene_manager, NfcSceneStart); return scene_manager_search_and_switch_to_previous_scene(
nfc->scene_manager, NfcSceneStart);
} }
} }
} }
@ -49,4 +58,5 @@ const void nfc_scene_save_name_on_exit(void* context) {
// Clear view // Clear view
text_input_set_header_text(nfc->text_input, NULL); text_input_set_header_text(nfc->text_input, NULL);
text_input_set_result_callback(nfc->text_input, NULL, NULL, NULL, 0, false);
} }

View File

@ -23,13 +23,23 @@ const void nfc_scene_save_success_on_enter(void* context) {
const bool nfc_scene_save_success_on_event(void* context, SceneManagerEvent event) { const bool nfc_scene_save_success_on_event(void* context, SceneManagerEvent event) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = (Nfc*)context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SCENE_SAVE_SUCCESS_CUSTOM_EVENT) { if(event.event == SCENE_SAVE_SUCCESS_CUSTOM_EVENT) {
return scene_manager_search_previous_scene(nfc->scene_manager, NfcSceneStart); if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneCardMenu)) {
consumed = scene_manager_search_and_switch_to_previous_scene(
nfc->scene_manager, NfcSceneCardMenu);
} else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) {
consumed = scene_manager_search_and_switch_to_another_scene(
nfc->scene_manager, NfcSceneFileSelect);
} else {
consumed = scene_manager_search_and_switch_to_previous_scene(
nfc->scene_manager, NfcSceneStart);
}
} }
} }
return false; return consumed;
} }
const void nfc_scene_save_success_on_exit(void* context) { const void nfc_scene_save_success_on_exit(void* context) {

View File

@ -5,6 +5,7 @@ enum SubmenuIndex {
SubmenuIndexRunScript, SubmenuIndexRunScript,
SubmenuIndexSaved, SubmenuIndexSaved,
SubmenuIndexAddManualy, SubmenuIndexAddManualy,
SubmenuIndexDebug,
}; };
void nfc_scene_start_submenu_callback(void* context, uint32_t index) { void nfc_scene_start_submenu_callback(void* context, uint32_t index) {
@ -29,6 +30,7 @@ const void nfc_scene_start_on_enter(void* context) {
submenu, "Saved cards", SubmenuIndexSaved, nfc_scene_start_submenu_callback, nfc); submenu, "Saved cards", SubmenuIndexSaved, nfc_scene_start_submenu_callback, nfc);
submenu_add_item( submenu_add_item(
submenu, "Add manually", SubmenuIndexAddManualy, nfc_scene_start_submenu_callback, nfc); submenu, "Add manually", SubmenuIndexAddManualy, nfc_scene_start_submenu_callback, nfc);
submenu_add_item(submenu, "Debug", SubmenuIndexDebug, nfc_scene_start_submenu_callback, nfc);
submenu_set_selected_item( submenu_set_selected_item(
submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneStart)); submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneStart));
@ -38,29 +40,34 @@ const void nfc_scene_start_on_enter(void* context) {
const bool nfc_scene_start_on_event(void* context, SceneManagerEvent event) { const bool nfc_scene_start_on_event(void* context, SceneManagerEvent event) {
Nfc* nfc = (Nfc*)context; Nfc* nfc = (Nfc*)context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SubmenuIndexRead) { if(event.event == SubmenuIndexRead) {
scene_manager_set_scene_state(nfc->scene_manager, NfcSceneStart, SubmenuIndexRead); scene_manager_set_scene_state(nfc->scene_manager, NfcSceneStart, SubmenuIndexRead);
scene_manager_next_scene(nfc->scene_manager, NfcSceneReadCard); scene_manager_next_scene(nfc->scene_manager, NfcSceneReadCard);
return true; consumed = true;
} else if(event.event == SubmenuIndexRunScript) { } else if(event.event == SubmenuIndexRunScript) {
scene_manager_set_scene_state( scene_manager_set_scene_state(
nfc->scene_manager, NfcSceneStart, SubmenuIndexRunScript); nfc->scene_manager, NfcSceneStart, SubmenuIndexRunScript);
scene_manager_next_scene(nfc->scene_manager, NfcSceneScriptsMenu); scene_manager_next_scene(nfc->scene_manager, NfcSceneScriptsMenu);
return true; consumed = true;
} else if(event.event == SubmenuIndexSaved) { } else if(event.event == SubmenuIndexSaved) {
scene_manager_set_scene_state(nfc->scene_manager, NfcSceneStart, SubmenuIndexSaved); scene_manager_set_scene_state(nfc->scene_manager, NfcSceneStart, SubmenuIndexSaved);
scene_manager_next_scene(nfc->scene_manager, NfcSceneFileSelect); scene_manager_next_scene(nfc->scene_manager, NfcSceneFileSelect);
return true; consumed = true;
} else if(event.event == SubmenuIndexAddManualy) { } else if(event.event == SubmenuIndexAddManualy) {
scene_manager_set_scene_state( scene_manager_set_scene_state(
nfc->scene_manager, NfcSceneStart, SubmenuIndexAddManualy); nfc->scene_manager, NfcSceneStart, SubmenuIndexAddManualy);
scene_manager_next_scene(nfc->scene_manager, NfcSceneSetType); scene_manager_next_scene(nfc->scene_manager, NfcSceneSetType);
return true; consumed = true;
} else if(event.event == SubmenuIndexDebug) {
scene_manager_set_scene_state(nfc->scene_manager, NfcSceneStart, SubmenuIndexDebug);
scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateApduSequence);
consumed = true;
} }
} }
return false; return consumed;
} }
const void nfc_scene_start_on_exit(void* context) { const void nfc_scene_start_on_exit(void* context) {

View File

@ -49,10 +49,10 @@ void bank_card_set_number(BankCard* bank_card, uint8_t* number) {
string_clear(num_str); string_clear(num_str);
} }
void bank_card_set_exp_date(BankCard* bank_card, uint8_t mon, uint16_t year) { void bank_card_set_exp_date(BankCard* bank_card, uint8_t mon, uint8_t year) {
furi_assert(bank_card); furi_assert(bank_card);
char exp_date_str[16]; char exp_date_str[16];
snprintf(exp_date_str, sizeof(exp_date_str), "Exp: %02d/%02d", mon, year % 100); snprintf(exp_date_str, sizeof(exp_date_str), "Exp: %02X/%02X", mon, year);
widget_add_string_element( widget_add_string_element(
bank_card->widget, 122, 54, AlignRight, AlignBottom, FontSecondary, exp_date_str); bank_card->widget, 122, 54, AlignRight, AlignBottom, FontSecondary, exp_date_str);
} }

View File

@ -18,6 +18,6 @@ void bank_card_set_name(BankCard* bank_card, char* name);
void bank_card_set_number(BankCard* bank_card, uint8_t* number); void bank_card_set_number(BankCard* bank_card, uint8_t* number);
void bank_card_set_exp_date(BankCard* bank_card, uint8_t mon, uint16_t year); void bank_card_set_exp_date(BankCard* bank_card, uint8_t mon, uint8_t year);
void bank_card_set_cardholder_name(BankCard* bank_card, char* name); void bank_card_set_cardholder_name(BankCard* bank_card, char* name);

View File

@ -74,7 +74,7 @@ uint8_t float_value_index(const float value, const float values[], uint8_t value
const float epsilon = 0.01f; const float epsilon = 0.01f;
float last_value = values[0]; float last_value = values[0];
uint8_t index = 0; uint8_t index = 0;
for(uint8_t i = 1; i < values_count; i++) { for(uint8_t i = 0; i < values_count; i++) {
if((value >= last_value - epsilon) && (value <= values[i] + epsilon)) { if((value >= last_value - epsilon) && (value <= values[i] + epsilon)) {
index = i; index = i;
break; break;
@ -85,9 +85,9 @@ uint8_t float_value_index(const float value, const float values[], uint8_t value
} }
uint8_t uint32_value_index(const uint32_t value, const uint32_t values[], uint8_t values_count) { uint8_t uint32_value_index(const uint32_t value, const uint32_t values[], uint8_t values_count) {
float last_value = values[0]; int64_t last_value = INT64_MIN;
uint8_t index = 0; uint8_t index = 0;
for(uint8_t i = 1; i < values_count; i++) { for(uint8_t i = 0; i < values_count; i++) {
if((value >= last_value) && (value <= values[i])) { if((value >= last_value) && (value <= values[i])) {
index = i; index = i;
break; break;

View File

@ -0,0 +1,33 @@
#include <furi.h>
#include <api-hal.h>
#include <notification/notification-messages.h>
const NotificationMessage message_green_110 = {
.type = NotificationMessageTypeLedGreen,
.data.led.value = 110,
};
static const NotificationSequence sequence_overconsumption = {
&message_green_110,
&message_red_255,
&message_delay_100,
NULL,
};
int32_t power_observer(void* p) {
NotificationApp* notifications = furi_record_open("notification");
const float overconsumption_limit = 0.03f;
while(true) {
float current = -api_hal_power_get_battery_current(ApiHalPowerICFuelGauge);
if(current >= overconsumption_limit) {
notification_message_block(notifications, &sequence_overconsumption);
}
delay(1000);
}
return 0;
}

View File

@ -43,6 +43,16 @@ void power_cli_otg(Cli* cli, string_t args, void* context) {
} }
} }
void power_cli_ext(Cli* cli, string_t args, void* context) {
if(!string_cmp(args, "0")) {
api_hal_power_disable_external_3_3v();
} else if(!string_cmp(args, "1")) {
api_hal_power_enable_external_3_3v();
} else {
cli_print_usage("power_ext", "<1|0>", string_get_cstr(args));
}
}
void power_cli_init(Cli* cli, Power* power) { void power_cli_init(Cli* cli, Power* power) {
cli_add_command(cli, "poweroff", CliCommandFlagParallelSafe, power_cli_poweroff, power); cli_add_command(cli, "poweroff", CliCommandFlagParallelSafe, power_cli_poweroff, power);
cli_add_command(cli, "reboot", CliCommandFlagParallelSafe, power_cli_reboot, power); cli_add_command(cli, "reboot", CliCommandFlagParallelSafe, power_cli_reboot, power);
@ -51,4 +61,5 @@ void power_cli_init(Cli* cli, Power* power) {
cli_add_command(cli, "dfu", CliCommandFlagParallelSafe, power_cli_dfu, power); cli_add_command(cli, "dfu", CliCommandFlagParallelSafe, power_cli_dfu, power);
cli_add_command(cli, "power_info", CliCommandFlagParallelSafe, power_cli_info, power); cli_add_command(cli, "power_info", CliCommandFlagParallelSafe, power_cli_info, power);
cli_add_command(cli, "power_otg", CliCommandFlagParallelSafe, power_cli_otg, power); cli_add_command(cli, "power_otg", CliCommandFlagParallelSafe, power_cli_otg, power);
cli_add_command(cli, "power_ext", CliCommandFlagParallelSafe, power_cli_ext, power);
} }

View File

@ -40,12 +40,13 @@ bool storage_settings_scene_unmounted_on_event(void* context, SceneManagerEvent
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
switch(event.event) { switch(event.event) {
case DialogExResultLeft: case DialogExResultLeft:
consumed = consumed = scene_manager_search_and_switch_to_previous_scene(
scene_manager_search_previous_scene(app->scene_manager, StorageSettingsStart); app->scene_manager, StorageSettingsStart);
break; break;
} }
} else if(event.type == SceneManagerEventTypeNavigation) { } else if(event.type == SceneManagerEventTypeBack) {
consumed = scene_manager_search_previous_scene(app->scene_manager, StorageSettingsStart); consumed = scene_manager_search_and_switch_to_previous_scene(
app->scene_manager, StorageSettingsStart);
} }
return consumed; return consumed;

View File

@ -60,12 +60,13 @@ bool storage_settings_scene_formatting_on_event(void* context, SceneManagerEvent
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
switch(event.event) { switch(event.event) {
case DialogExResultLeft: case DialogExResultLeft:
consumed = consumed = scene_manager_search_and_switch_to_previous_scene(
scene_manager_search_previous_scene(app->scene_manager, StorageSettingsStart); app->scene_manager, StorageSettingsStart);
break; break;
} }
} else if(event.type == SceneManagerEventTypeNavigation) { } else if(event.type == SceneManagerEventTypeBack) {
consumed = scene_manager_search_previous_scene(app->scene_manager, StorageSettingsStart); consumed = scene_manager_search_and_switch_to_previous_scene(
app->scene_manager, StorageSettingsStart);
} }
return consumed; return consumed;

View File

@ -6,10 +6,10 @@ static bool storage_settings_custom_event_callback(void* context, uint32_t event
return scene_manager_handle_custom_event(app->scene_manager, event); return scene_manager_handle_custom_event(app->scene_manager, event);
} }
static bool storage_settings_navigation_event_callback(void* context) { static bool storage_settings_back_event_callback(void* context) {
furi_assert(context); furi_assert(context);
StorageSettings* app = context; StorageSettings* app = context;
return scene_manager_handle_navigation_event(app->scene_manager); return scene_manager_handle_back_event(app->scene_manager);
} }
static StorageSettings* storage_settings_alloc() { static StorageSettings* storage_settings_alloc() {
@ -29,7 +29,7 @@ static StorageSettings* storage_settings_alloc() {
view_dispatcher_set_custom_event_callback( view_dispatcher_set_custom_event_callback(
app->view_dispatcher, storage_settings_custom_event_callback); app->view_dispatcher, storage_settings_custom_event_callback);
view_dispatcher_set_navigation_event_callback( view_dispatcher_set_navigation_event_callback(
app->view_dispatcher, storage_settings_navigation_event_callback); app->view_dispatcher, storage_settings_back_event_callback);
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);

View File

@ -1,6 +1,6 @@
#include <furi.h> #include <furi.h>
#include <cli/cli.h> #include <cli/cli.h>
#include <args.h> #include <lib/toolbox/args.h>
#include <storage/storage.h> #include <storage/storage.h>
#include <storage/storage-sd-api.h> #include <storage/storage-sd-api.h>
#include <api-hal-version.h> #include <api-hal-version.h>
@ -30,6 +30,7 @@ void storage_cli_print_usage() {
"\twrite\t - read data from cli and append it to file, <args> should contain how many bytes you want to write\r\n"); "\twrite\t - read data from cli and append it to file, <args> should contain how many bytes you want to write\r\n");
printf("\tcopy\t - copy file to new file, <args> must contain new path\r\n"); printf("\tcopy\t - copy file to new file, <args> must contain new path\r\n");
printf("\trename\t - move file to new file, <args> must contain new path\r\n"); printf("\trename\t - move file to new file, <args> must contain new path\r\n");
printf("\tmkdir\t - creates a new directory\r\n");
}; };
void storage_cli_print_error(FS_Error error) { void storage_cli_print_error(FS_Error error) {
@ -285,6 +286,17 @@ void storage_cli_rename(Cli* cli, string_t old_path, string_t args) {
furi_record_close("storage"); furi_record_close("storage");
} }
void storage_cli_mkdir(Cli* cli, string_t path) {
Storage* api = furi_record_open("storage");
FS_Error error = storage_common_mkdir(api, string_get_cstr(path));
if(error != FSE_OK) {
storage_cli_print_error(error);
}
furi_record_close("storage");
}
void storage_cli(Cli* cli, string_t args, void* context) { void storage_cli(Cli* cli, string_t args, void* context) {
string_t cmd; string_t cmd;
string_t path; string_t path;
@ -342,6 +354,11 @@ void storage_cli(Cli* cli, string_t args, void* context) {
break; break;
} }
if(string_cmp_str(cmd, "mkdir") == 0) {
storage_cli_mkdir(cli, path);
break;
}
storage_cli_print_usage(); storage_cli_print_usage();
} while(false); } while(false);

File diff suppressed because one or more lines are too long

View File

@ -10,67 +10,67 @@ extern const Icon A_MDWRB_32x32;
extern const Icon A_MDWR_32x32; extern const Icon A_MDWR_32x32;
extern const Icon A_WatchingTV_128x64; extern const Icon A_WatchingTV_128x64;
extern const Icon A_Wink_128x64; extern const Icon A_Wink_128x64;
extern const Icon I_125_10px;
extern const Icon I_ble_10px;
extern const Icon I_dir_10px; extern const Icon I_dir_10px;
extern const Icon I_ibutt_10px;
extern const Icon I_ir_10px;
extern const Icon I_Nfc_10px; extern const Icon I_Nfc_10px;
extern const Icon I_sub1_10px; extern const Icon I_sub1_10px;
extern const Icon I_ir_10px;
extern const Icon I_ibutt_10px;
extern const Icon I_unknown_10px; extern const Icon I_unknown_10px;
extern const Icon I_ButtonCenter_7x7; extern const Icon I_ble_10px;
extern const Icon I_ButtonLeftSmall_3x5; extern const Icon I_125_10px;
extern const Icon I_ButtonLeft_4x7;
extern const Icon I_ButtonRightSmall_3x5; extern const Icon I_ButtonRightSmall_3x5;
extern const Icon I_ButtonLeft_4x7;
extern const Icon I_ButtonLeftSmall_3x5;
extern const Icon I_ButtonRight_4x7; extern const Icon I_ButtonRight_4x7;
extern const Icon I_BigBurger_24x24; extern const Icon I_ButtonCenter_7x7;
extern const Icon I_FX_SittingB_40x27;
extern const Icon I_BigGames_24x24; extern const Icon I_BigGames_24x24;
extern const Icon I_BigProfile_24x24; extern const Icon I_BigProfile_24x24;
extern const Icon I_DolphinFirstStart0_70x53;
extern const Icon I_DolphinFirstStart1_59x53;
extern const Icon I_DolphinFirstStart2_59x51;
extern const Icon I_DolphinFirstStart3_57x48;
extern const Icon I_DolphinFirstStart4_67x53;
extern const Icon I_DolphinFirstStart5_45x53;
extern const Icon I_DolphinFirstStart6_58x54;
extern const Icon I_DolphinFirstStart7_61x51;
extern const Icon I_DolphinFirstStart8_56x51;
extern const Icon I_DolphinOkay_41x43; extern const Icon I_DolphinOkay_41x43;
extern const Icon I_DolphinFirstStart4_67x53;
extern const Icon I_DolphinFirstStart2_59x51;
extern const Icon I_DolphinFirstStart5_54x49;
extern const Icon I_DolphinFirstStart0_70x53;
extern const Icon I_DolphinFirstStart6_58x54;
extern const Icon I_DolphinFirstStart1_59x53;
extern const Icon I_DolphinFirstStart8_56x51;
extern const Icon I_DolphinFirstStart7_61x51;
extern const Icon I_Flipper_young_80x60; extern const Icon I_Flipper_young_80x60;
extern const Icon I_BigBurger_24x24;
extern const Icon I_FX_Bang_32x6; extern const Icon I_FX_Bang_32x6;
extern const Icon I_FX_SittingB_40x27; extern const Icon I_DolphinFirstStart3_57x48;
extern const Icon I_DoorLeft_70x55; extern const Icon I_PassportBottom_128x17;
extern const Icon I_DoorLeft_8x56; extern const Icon I_DoorLeft_8x56;
extern const Icon I_DoorLocked_10x56; extern const Icon I_DoorLocked_10x56;
extern const Icon I_DoorRight_70x55;
extern const Icon I_DoorRight_8x56; extern const Icon I_DoorRight_8x56;
extern const Icon I_LockPopup_100x49; extern const Icon I_DoorLeft_70x55;
extern const Icon I_PassportBottom_128x17;
extern const Icon I_PassportLeft_6x47; extern const Icon I_PassportLeft_6x47;
extern const Icon I_Back_15x10; extern const Icon I_DoorRight_70x55;
extern const Icon I_LockPopup_100x49;
extern const Icon I_Mute_25x27;
extern const Icon I_IrdaArrowUp_4x8;
extern const Icon I_Up_hvr_25x27;
extern const Icon I_Mute_hvr_25x27;
extern const Icon I_Vol_down_25x27;
extern const Icon I_Down_25x27; extern const Icon I_Down_25x27;
extern const Icon I_Power_hvr_25x27;
extern const Icon I_IrdaLearnShort_128x31;
extern const Icon I_IrdaArrowDown_4x8;
extern const Icon I_Vol_down_hvr_25x27;
extern const Icon I_IrdaLearn_128x64;
extern const Icon I_Down_hvr_25x27; extern const Icon I_Down_hvr_25x27;
extern const Icon I_Fill_marker_7x7; extern const Icon I_Fill_marker_7x7;
extern const Icon I_IrdaArrowDown_4x8;
extern const Icon I_IrdaArrowUp_4x8;
extern const Icon I_IrdaLearnShort_128x31;
extern const Icon I_IrdaLearn_128x64;
extern const Icon I_IrdaSendShort_128x34;
extern const Icon I_IrdaSend_128x64;
extern const Icon I_Mute_25x27;
extern const Icon I_Mute_hvr_25x27;
extern const Icon I_Power_25x27; extern const Icon I_Power_25x27;
extern const Icon I_Power_hvr_25x27;
extern const Icon I_Up_25x27;
extern const Icon I_Up_hvr_25x27;
extern const Icon I_Vol_down_25x27;
extern const Icon I_Vol_down_hvr_25x27;
extern const Icon I_Vol_up_25x27; extern const Icon I_Vol_up_25x27;
extern const Icon I_Up_25x27;
extern const Icon I_Back_15x10;
extern const Icon I_IrdaSend_128x64;
extern const Icon I_IrdaSendShort_128x34;
extern const Icon I_Vol_up_hvr_25x27; extern const Icon I_Vol_up_hvr_25x27;
extern const Icon I_KeyBackspaceSelected_16x9;
extern const Icon I_KeyBackspace_16x9;
extern const Icon I_KeySaveSelected_24x11;
extern const Icon I_KeySave_24x11; extern const Icon I_KeySave_24x11;
extern const Icon I_KeyBackspaceSelected_16x9;
extern const Icon I_KeySaveSelected_24x11;
extern const Icon I_KeyBackspace_16x9;
extern const Icon A_125khz_14; extern const Icon A_125khz_14;
extern const Icon A_Bluetooth_14; extern const Icon A_Bluetooth_14;
extern const Icon A_FileManager_14; extern const Icon A_FileManager_14;
@ -86,61 +86,141 @@ extern const Icon A_Sub1ghz_14;
extern const Icon A_Tamagotchi_14; extern const Icon A_Tamagotchi_14;
extern const Icon A_U2F_14; extern const Icon A_U2F_14;
extern const Icon A_iButton_14; extern const Icon A_iButton_14;
extern const Icon I_EMV_Chip_14x11;
extern const Icon I_Medium_chip_22x21; extern const Icon I_Medium_chip_22x21;
extern const Icon I_passport_bad1_43x45; extern const Icon I_EMV_Chip_14x11;
extern const Icon I_passport_bad2_43x45;
extern const Icon I_passport_bad3_43x45;
extern const Icon I_passport_happy1_43x45; extern const Icon I_passport_happy1_43x45;
extern const Icon I_passport_happy2_43x45; extern const Icon I_passport_bad3_43x45;
extern const Icon I_passport_happy3_43x45;
extern const Icon I_passport_okay1_43x45;
extern const Icon I_passport_okay2_43x45; extern const Icon I_passport_okay2_43x45;
extern const Icon I_passport_bad2_43x45;
extern const Icon I_passport_okay3_43x45; extern const Icon I_passport_okay3_43x45;
extern const Icon I_BatteryBody_52x28; extern const Icon I_passport_bad1_43x45;
extern const Icon I_Battery_16x16; extern const Icon I_passport_happy3_43x45;
extern const Icon I_passport_happy2_43x45;
extern const Icon I_passport_okay1_43x45;
extern const Icon I_Health_16x16;
extern const Icon I_FaceCharging_29x14; extern const Icon I_FaceCharging_29x14;
extern const Icon I_FaceConfused_29x14; extern const Icon I_BatteryBody_52x28;
extern const Icon I_Voltage_16x16;
extern const Icon I_Temperature_16x16;
extern const Icon I_FaceNopower_29x14; extern const Icon I_FaceNopower_29x14;
extern const Icon I_FaceNormal_29x14; extern const Icon I_FaceNormal_29x14;
extern const Icon I_Health_16x16; extern const Icon I_Battery_16x16;
extern const Icon I_Temperature_16x16; extern const Icon I_FaceConfused_29x14;
extern const Icon I_Voltage_16x16;
extern const Icon I_RFIDBigChip_37x36;
extern const Icon I_RFIDDolphinReceive_97x61;
extern const Icon I_RFIDDolphinSend_97x61;
extern const Icon I_RFIDDolphinSuccess_108x57; extern const Icon I_RFIDDolphinSuccess_108x57;
extern const Icon I_SDError_43x35; extern const Icon I_RFIDBigChip_37x36;
extern const Icon I_RFIDDolphinSend_97x61;
extern const Icon I_RFIDDolphinReceive_97x61;
extern const Icon I_SDQuestion_35x43; extern const Icon I_SDQuestion_35x43;
extern const Icon I_Home_painting_17x20; extern const Icon I_SDError_43x35;
extern const Icon I_PC_22x29; extern const Icon I_Console_74x67_4;
extern const Icon I_Sofa_40x13; extern const Icon I_Console_74x67_5;
extern const Icon I_TV_20x20; extern const Icon I_Console_74x67_7;
extern const Icon I_TV_20x24; extern const Icon I_Console_74x67_6;
extern const Icon I_WalkL1_32x32; extern const Icon I_Console_74x67_2;
extern const Icon I_WalkL2_32x32; extern const Icon I_Console_74x67_3;
extern const Icon I_WalkLB1_32x32; extern const Icon I_Console_74x67_1;
extern const Icon I_WalkLB2_32x32; extern const Icon I_Console_74x67_0;
extern const Icon I_WalkR1_32x32; extern const Icon I_Console_74x67_8;
extern const Icon I_WalkR2_32x32; extern const Icon I_food8_61x98;
extern const Icon I_WalkRB1_32x32; extern const Icon I_food5_61x98;
extern const Icon I_WalkRB2_32x32; extern const Icon I_food3_61x98;
extern const Icon I_Background_128x11; extern const Icon I_food9_61x98;
extern const Icon I_Background_128x8; extern const Icon I_food12_61x98;
extern const Icon I_food4_61x98;
extern const Icon I_food2_61x98;
extern const Icon I_food7_61x98;
extern const Icon I_food11_61x98;
extern const Icon I_food1_61x98;
extern const Icon I_food6_61x98;
extern const Icon I_food10_61x98;
extern const Icon I_rightdown2_73x61;
extern const Icon I_black_upright2_73x61;
extern const Icon I_black_leftup1_73x61;
extern const Icon I_black_upleft1_73x61;
extern const Icon I_black_down2_73x61;
extern const Icon I_downleft3_73x61;
extern const Icon I_down2_73x61;
extern const Icon I_black_downright2_73x61;
extern const Icon I_black_downleft2_73x61;
extern const Icon I_up2_73x61;
extern const Icon I_right1_73x61;
extern const Icon I_black_up1_73x61;
extern const Icon I_upright1_73x61;
extern const Icon I_black_rightleft2_73x61;
extern const Icon I_black_right3_73x61;
extern const Icon I_upleft2_73x61;
extern const Icon I_black_downup3_73x61;
extern const Icon I_black_updown3_73x61;
extern const Icon I_leftup2_73x61;
extern const Icon I_leftdown2_73x61;
extern const Icon I_downup1_73x61;
extern const Icon I_updown1_73x61;
extern const Icon I_rightup1_73x61;
extern const Icon I_black_rightdown1_73x61;
extern const Icon I_downleft2_73x61;
extern const Icon I_downright1_73x61;
extern const Icon I_black_downright3_73x61;
extern const Icon I_black_downleft3_73x61;
extern const Icon I_rightleft1_73x61;
extern const Icon I_black_right2_73x61;
extern const Icon I_black_rightup2_73x61;
extern const Icon I_black_downup2_73x61;
extern const Icon I_black_updown2_73x61;
extern const Icon I_black_left1_73x61;
extern const Icon I_black_leftdown2_73x61;
extern const Icon I_left1_73x61;
extern const Icon I_rightup2_73x61;
extern const Icon I_black_rightdown2_73x61;
extern const Icon I_downup2_73x61;
extern const Icon I_updown2_73x61;
extern const Icon I_right3_73x61;
extern const Icon I_downright2_73x61;
extern const Icon I_downleft1_73x61;
extern const Icon I_black_rightup1_73x61;
extern const Icon I_black_right1_73x61;
extern const Icon I_rightleft2_73x61;
extern const Icon I_left2_73x61;
extern const Icon I_black_left2_73x61;
extern const Icon I_black_downup1_73x61;
extern const Icon I_black_updown1_73x61;
extern const Icon I_black_leftdown1_73x61;
extern const Icon I_downup3_73x61;
extern const Icon I_updown3_73x61;
extern const Icon I_black_leftup2_73x61;
extern const Icon I_black_upright1_73x61;
extern const Icon I_rightdown1_73x61;
extern const Icon I_right2_73x61;
extern const Icon I_black_downleft1_73x61;
extern const Icon I_down1_73x61;
extern const Icon I_black_downright1_73x61;
extern const Icon I_up1_73x61;
extern const Icon I_downright3_73x61;
extern const Icon I_black_down1_73x61;
extern const Icon I_black_upleft2_73x61;
extern const Icon I_upleft1_73x61;
extern const Icon I_black_rightleft1_73x61;
extern const Icon I_black_up2_73x61;
extern const Icon I_upright2_73x61;
extern const Icon I_leftdown1_73x61;
extern const Icon I_left3_73x61;
extern const Icon I_leftup1_73x61;
extern const Icon I_black_left3_73x61;
extern const Icon I_BadUsb_9x8; extern const Icon I_BadUsb_9x8;
extern const Icon I_Battery_19x8;
extern const Icon I_Battery_26x8;
extern const Icon I_Bluetooth_5x8;
extern const Icon I_Lock_8x8;
extern const Icon I_PlaceholderL_11x13;
extern const Icon I_PlaceholderR_30x13; extern const Icon I_PlaceholderR_30x13;
extern const Icon I_SDcardFail_11x8; extern const Icon I_Background_128x8;
extern const Icon I_Lock_8x8;
extern const Icon I_Battery_26x8;
extern const Icon I_PlaceholderL_11x13;
extern const Icon I_Battery_19x8;
extern const Icon I_SDcardMounted_11x8; extern const Icon I_SDcardMounted_11x8;
extern const Icon I_SDcardFail_11x8;
extern const Icon I_USBConnected_15x8; extern const Icon I_USBConnected_15x8;
extern const Icon I_DolphinExcited_64x63; extern const Icon I_Bluetooth_5x8;
extern const Icon I_Background_128x11;
extern const Icon I_DolphinMafia_115x62; extern const Icon I_DolphinMafia_115x62;
extern const Icon I_DolphinNice_96x59; extern const Icon I_DolphinExcited_64x63;
extern const Icon I_DolphinWait_61x59;
extern const Icon I_iButtonDolphinSuccess_109x60; extern const Icon I_iButtonDolphinSuccess_109x60;
extern const Icon I_iButtonDolphinVerySuccess_108x52; extern const Icon I_iButtonDolphinVerySuccess_108x52;
extern const Icon I_iButtonKey_49x44; extern const Icon I_iButtonKey_49x44;
extern const Icon I_DolphinNice_96x59;
extern const Icon I_DolphinWait_61x59;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 522 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 520 B

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Some files were not shown because too many files have changed in this diff Show More