Merge remote-tracking branch 'origin/release-candidate' into release
9
.github/workflows/build.yml
vendored
@ -14,8 +14,11 @@ jobs:
|
||||
|
||||
- name: 'Decontaminate previous build leftovers'
|
||||
run: |
|
||||
git submodule status \
|
||||
|| git checkout `git rev-list --max-parents=0 HEAD | tail -n 1`
|
||||
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
|
||||
@ -44,7 +47,7 @@ jobs:
|
||||
|
||||
- name: 'Generate branch suffix'
|
||||
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'
|
||||
uses: ./.github/actions/docker
|
||||
|
||||
15
.github/workflows/lint_c.yml
vendored
@ -12,16 +12,19 @@ jobs:
|
||||
- name: 'Cleanup workspace'
|
||||
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'
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: 'Checkout submodules: update'
|
||||
run: git submodule update --init --recursive
|
||||
|
||||
- name: 'Checkout submodules: sync'
|
||||
run: git submodule sync
|
||||
submodules: true
|
||||
|
||||
- name: 'Docker cache'
|
||||
uses: satackey/action-docker-layer-caching@v0.0.11
|
||||
|
||||
24
.github/workflows/lint_python.yml
vendored
@ -10,6 +10,24 @@ jobs:
|
||||
lint_python:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-python@v2
|
||||
- uses: psf/black@20.8b1
|
||||
- name: 'Cleanup workspace'
|
||||
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'
|
||||
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
|
||||
|
||||
@ -39,6 +39,7 @@ int32_t lfrfid_debug_app(void* p);
|
||||
int32_t storage_app(void* p);
|
||||
int32_t storage_app_test(void* p);
|
||||
int32_t dialogs_app(void* p);
|
||||
int32_t power_observer(void* p);
|
||||
|
||||
// On system start hooks declaration
|
||||
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},
|
||||
#endif
|
||||
|
||||
#ifdef SRV_POWER_OBSERVER
|
||||
{.app = power_observer, .name = "power_observer", .stack_size = 1024, .icon = &A_Plugins_14},
|
||||
#endif
|
||||
|
||||
#ifdef SRV_BT
|
||||
{.app = bt_task, .name = "bt_task", .stack_size = 1024, .icon = &A_Plugins_14},
|
||||
#endif
|
||||
@ -139,7 +144,7 @@ const FlipperApplication FLIPPER_SERVICES[] = {
|
||||
#endif
|
||||
|
||||
#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
|
||||
|
||||
#ifdef SRV_ACCESSOR
|
||||
@ -268,7 +273,7 @@ const FlipperApplication FLIPPER_DEBUG_APPS[] = {
|
||||
#endif
|
||||
|
||||
#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
|
||||
|
||||
#ifdef APP_ACCESSOR
|
||||
|
||||
@ -20,6 +20,7 @@ SRV_DOLPHIN = 1
|
||||
SRV_NOTIFICATION = 1
|
||||
SRV_STORAGE = 1
|
||||
SRV_DIALOGS = 1
|
||||
SRV_POWER_OBSERVER = 1
|
||||
|
||||
# Main Apps
|
||||
APP_IRDA = 1
|
||||
@ -59,6 +60,12 @@ SRV_CLI = 1
|
||||
CFLAGS += -DSRV_POWER
|
||||
endif
|
||||
|
||||
SRV_POWER_OBSERVER ?= 0
|
||||
ifeq ($(SRV_POWER_OBSERVER), 1)
|
||||
SRV_POWER = 1
|
||||
CFLAGS += -DSRV_POWER_OBSERVER
|
||||
endif
|
||||
|
||||
SRV_BT ?= 0
|
||||
ifeq ($(SRV_BT), 1)
|
||||
SRV_CLI = 1
|
||||
|
||||
@ -297,7 +297,8 @@ static void archive_enter_text_input(ArchiveApp* archive) {
|
||||
archive_text_input_callback,
|
||||
archive,
|
||||
archive->browser.text_input_buffer,
|
||||
MAX_NAME_LEN);
|
||||
MAX_NAME_LEN,
|
||||
false);
|
||||
|
||||
view_dispatcher_switch_to_view(archive->view_dispatcher, ArchiveViewTextInput);
|
||||
}
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
#include "cli_i.h"
|
||||
#include "cli_commands.h"
|
||||
|
||||
#include <version.h>
|
||||
#include <api-hal-version.h>
|
||||
#include <loader/loader.h>
|
||||
|
||||
|
||||
@ -94,23 +94,30 @@ void cli_command_help(Cli* cli, string_t args, void* context) {
|
||||
(void)args;
|
||||
printf("Commands we have:");
|
||||
|
||||
// Get the middle element
|
||||
CliCommandTree_it_t it_mid;
|
||||
uint8_t cmd_num = CliCommandTree_size(cli->commands);
|
||||
uint8_t i = cmd_num / 2 + cmd_num % 2;
|
||||
for(CliCommandTree_it(it_mid, cli->commands); i; --i, CliCommandTree_next(it_mid))
|
||||
;
|
||||
// Command count
|
||||
const size_t commands_count = CliCommandTree_size(cli->commands);
|
||||
const size_t commands_count_mid = commands_count / 2 + commands_count % 2;
|
||||
|
||||
// Use 2 iterators from start and middle to show 2 columns
|
||||
CliCommandTree_it_t it_i;
|
||||
CliCommandTree_it_t it_j;
|
||||
for(CliCommandTree_it(it_i, cli->commands), CliCommandTree_it_set(it_j, it_mid);
|
||||
!CliCommandTree_it_equal_p(it_i, it_mid);
|
||||
CliCommandTree_next(it_i), CliCommandTree_next(it_j)) {
|
||||
CliCommandTree_itref_t* ref = CliCommandTree_ref(it_i);
|
||||
CliCommandTree_it_t it_left;
|
||||
CliCommandTree_it(it_left, cli->commands);
|
||||
CliCommandTree_it_t it_right;
|
||||
CliCommandTree_it(it_right, cli->commands);
|
||||
for(size_t i = 0; i < commands_count_mid; i++) CliCommandTree_next(it_right);
|
||||
|
||||
// Iterate throw tree
|
||||
for(size_t i = 0; i < commands_count_mid; i++) {
|
||||
printf("\r\n");
|
||||
printf("%-30s", string_get_cstr(ref->key_ptr[0]));
|
||||
ref = CliCommandTree_ref(it_j);
|
||||
printf(string_get_cstr(ref->key_ptr[0]));
|
||||
// Left Column
|
||||
if(!CliCommandTree_end_p(it_left)) {
|
||||
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) {
|
||||
|
||||
@ -3,7 +3,6 @@
|
||||
#include <gui/gui.h>
|
||||
#include <gui/elements.h>
|
||||
#include <api-hal.h>
|
||||
#include <version.h>
|
||||
#include <api-hal-version.h>
|
||||
|
||||
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",
|
||||
my_name ? my_name : "Unknown",
|
||||
",\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);
|
||||
} else if(m->page == 6) {
|
||||
canvas_draw_icon(canvas, 0, height - 48, &I_DolphinFirstStart6_58x54);
|
||||
|
||||
@ -1,15 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
static const char* emotes_list[] = {
|
||||
"(O_o)", "(!_?)", "(^_^)", "(*__*)", "(@_@)", "(X_x)", "(>_<)", "(^ ^)", "(^_^)",
|
||||
"(-_-)", "(~_~)", "(#^.^#)", "(^ ^)", "(^.^)", "(-.-)", "zZzZ", "(^_-)", "(^_-)",
|
||||
"(+_+)", "(+o+)", "(' ')", "('-')", "('.')", "('_')", "(* > *)", "(o o)", "(^_^)",
|
||||
"(^O^)", "(^o^)", "(^o^)", "(._.)", "(_^_)", "('_')", "('_;)", "(T_T)", "(;_;)",
|
||||
"(ー_ー)", "(-.-)", "(^o^)", "(-_-)", "(=_=)", "(=^ ^=)", "(. .)", "(._.)", "( ^m^)",
|
||||
"(?_?)", "(*^_^*)", "(^<^)", "(^.^)", "(^·^)", "(^.^)", "(^_^.)", "(^_^)", "(^^)",
|
||||
"(^J^)", "(*^.^*)", "(#^.^#)", "(~o~)", "(^o^)", "(-o-)", "(^. ^)", "(^o^)", "(*^0^*)",
|
||||
"(*_*)", "(~ o ~)", "(~_~)", "(p_-)", "d[-_-]b", "(^0_0^)", "- ^ -"};
|
||||
|
||||
static const char* dialogues_list[] = {
|
||||
"Let's hack!\n\nbla bla bla\nbla bla..",
|
||||
// temp
|
||||
const char* console_emotes[] = {
|
||||
"Run it, m8",
|
||||
"Lets GOOOO",
|
||||
"Click it, buddy",
|
||||
"I wanna play",
|
||||
"Wtf is this?",
|
||||
"Just do it",
|
||||
"JUST DO IT!",
|
||||
};
|
||||
|
||||
@ -1,49 +1,38 @@
|
||||
#include <gui/elements.h>
|
||||
#include "applications.h"
|
||||
#include "items_i.h"
|
||||
#include "emotes.h"
|
||||
#include <gui/icon_i.h>
|
||||
|
||||
const Item TV = {
|
||||
.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 = {
|
||||
const Item Food = {
|
||||
.layer = 4,
|
||||
.timeout = 100,
|
||||
.x = 250,
|
||||
.y = 34,
|
||||
.icon = &I_Sofa_40x13,
|
||||
.action_name = "Sit",
|
||||
.draw = NULL,
|
||||
.callback = sofa_sit};
|
||||
.pos =
|
||||
{
|
||||
.x = 0,
|
||||
.y = 90,
|
||||
},
|
||||
.width = 60,
|
||||
.height = 50,
|
||||
.draw = food_redraw,
|
||||
.callback = food_callback};
|
||||
|
||||
const Item PC = {
|
||||
const Item Console = {
|
||||
.layer = 4,
|
||||
.timeout = 100,
|
||||
.x = 400,
|
||||
.y = 10,
|
||||
.icon = &I_PC_22x29,
|
||||
.action_name = "Use",
|
||||
.draw = NULL,
|
||||
.callback = pc_callback};
|
||||
.pos =
|
||||
{
|
||||
.x = 357,
|
||||
.y = 190,
|
||||
},
|
||||
.width = 40,
|
||||
.height = 20,
|
||||
.draw = console_redraw,
|
||||
.callback = console_callback};
|
||||
|
||||
const Item* Home[ITEMS_NUM] = {&TV, &Sofa, &Painting, &PC};
|
||||
const Item** Scenes[1] = {*&Home};
|
||||
const Item* Home[] = {&Food, &Console};
|
||||
|
||||
const Item** Scenes[] = {Home};
|
||||
|
||||
const Item** get_scene(SceneState* state) {
|
||||
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);
|
||||
}
|
||||
|
||||
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) {
|
||||
furi_assert(state);
|
||||
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) {
|
||||
furi_assert(state);
|
||||
uint8_t item = 0;
|
||||
bool found = false;
|
||||
const Item** current = get_scene(state);
|
||||
while(item < ITEMS_NUM) {
|
||||
int32_t rel =
|
||||
while(item < ItemsEnumTotal) {
|
||||
int32_t rel_x =
|
||||
(DOLPHIN_CENTER + DOLPHIN_WIDTH / 2 -
|
||||
(current[item]->x - state->player_global.x) * PARALLAX(current[item]->layer));
|
||||
if(abs(rel) <= DOLPHIN_WIDTH / 2) {
|
||||
(current[item]->pos.x - state->player_global.x) * PARALLAX(current[item]->layer));
|
||||
|
||||
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;
|
||||
break;
|
||||
}
|
||||
@ -96,50 +132,102 @@ const Item* is_nearby(SceneState* state) {
|
||||
return found ? current[item] : NULL;
|
||||
}
|
||||
|
||||
void draw_tv(Canvas* canvas, void* state) {
|
||||
furi_assert(state);
|
||||
SceneState* s = state;
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
canvas_draw_box(
|
||||
canvas, (TV.x + 3 - s->player_global.x) * PARALLAX(TV.layer), TV.y + 4, 16, 20);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
canvas_set_bitmap_mode(canvas, true);
|
||||
}
|
||||
void food_redraw(Canvas* canvas, void* s) {
|
||||
furi_assert(s);
|
||||
SceneState* state = s;
|
||||
|
||||
const Icon* food_frames[] = {
|
||||
&I_food1_61x98,
|
||||
&I_food2_61x98,
|
||||
&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, ((TV.x - 5) - s->player_global.x) * PARALLAX(TV.layer), TV.y - 2, &I_FX_Bang_32x6);
|
||||
canvas_set_bitmap_mode(canvas, false);
|
||||
if(s->action_timeout < TV.timeout - 2) {
|
||||
elements_multiline_text_framed(canvas, 80, 24, "Bang!");
|
||||
canvas,
|
||||
(Food.pos.x - state->player_global.x) * PARALLAX(Food.layer),
|
||||
Food.pos.y - state->player_global.y,
|
||||
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) {
|
||||
furi_assert(state);
|
||||
SceneState* s = state;
|
||||
// temp fix pos
|
||||
s->player_global.x = 154;
|
||||
s->dolphin_gfx = &A_FX_Sitting_40x27;
|
||||
s->dolphin_gfx_b = &I_FX_SittingB_40x27;
|
||||
}
|
||||
void console_redraw(Canvas* canvas, void* s) {
|
||||
furi_assert(s);
|
||||
SceneState* state = s;
|
||||
|
||||
void inspect_painting(Canvas* canvas, void* state) {
|
||||
furi_assert(state);
|
||||
SceneState* s = state;
|
||||
if(s->use_pending) {
|
||||
dolphin_scene_start_app(s, &FLIPPER_SCENE_APPS[0]);
|
||||
const Icon* console[] = {
|
||||
&I_Console_74x67_0,
|
||||
&I_Console_74x67_1,
|
||||
&I_Console_74x67_2,
|
||||
&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) {
|
||||
furi_assert(state);
|
||||
SceneState* s = state;
|
||||
if(s->use_pending) {
|
||||
dolphin_scene_start_app(s, &FLIPPER_SCENE_APPS[1]);
|
||||
void console_callback(Canvas* canvas, void* s) {
|
||||
furi_assert(s);
|
||||
SceneState* state = s;
|
||||
if(state->use_pending) {
|
||||
dolphin_scene_start_app(state, &FLIPPER_SCENE_APPS[1]);
|
||||
}
|
||||
}
|
||||
@ -1,8 +1,14 @@
|
||||
#pragma once
|
||||
#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** get_scene(SceneState* state);
|
||||
const void scene_activate_item_callback(SceneState* state, Canvas* canvas);
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
#pragma once
|
||||
#include "items.h"
|
||||
|
||||
void smash_tv(Canvas* canvas, void* state);
|
||||
void draw_tv(Canvas* canvas, void* state);
|
||||
void sofa_sit(Canvas* canvas, void* state);
|
||||
void inspect_painting(Canvas* canvas, void* state);
|
||||
void pc_callback(Canvas* canvas, void* state);
|
||||
void food_redraw(Canvas* canvas, void* state);
|
||||
void food_callback(Canvas* canvas, void* state);
|
||||
|
||||
void console_redraw(Canvas* canvas, void* state);
|
||||
void console_callback(Canvas* canvas, void* state);
|
||||
|
||||
276
applications/dolphin/scenes/assets/meta.h
Normal 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},
|
||||
};
|
||||
@ -52,7 +52,7 @@ void scene_alloc() {
|
||||
|
||||
// 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->view_port = view_port_alloc();
|
||||
scene_app_gui->timer =
|
||||
@ -62,10 +62,18 @@ void scene_alloc() {
|
||||
SceneState* scene_state = furi_alloc(sizeof(SceneState));
|
||||
scene_state->player.y = DOLPHIN_DEFAULT_Y;
|
||||
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.y = scene_state->player.y;
|
||||
|
||||
// scene_state->debug = true;
|
||||
scene_state_mutex = furi_alloc(sizeof(ValueMutex));
|
||||
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_input_callback_set(
|
||||
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);
|
||||
printf("scene_alloc: complete\r\n");
|
||||
}
|
||||
|
||||
@ -16,26 +16,28 @@
|
||||
// player
|
||||
#define DOLPHIN_WIDTH 32
|
||||
#define DOLPHIN_HEIGHT 32
|
||||
#define DOLPHIN_CENTER (SCREEN_WIDTH / 2 - DOLPHIN_WIDTH / 2)
|
||||
#define SPEED_X 2
|
||||
#define ACTIONS_NUM 5
|
||||
#define DOLPHIN_DEFAULT_Y 20
|
||||
#define DOLPHIN_CENTER (SCREEN_WIDTH / 2 - DOLPHIN_WIDTH)
|
||||
#define SPEED_X 4
|
||||
#define SPEED_Y 4
|
||||
#define ACTIONS_NUM 4
|
||||
#define DOLPHIN_DEFAULT_Y 2
|
||||
#define MAX_FRAMES 3
|
||||
|
||||
// world
|
||||
#define WORLD_WIDTH 2048
|
||||
#define WORLD_HEIGHT 64
|
||||
#define WORLD_WIDTH 256
|
||||
#define WORLD_HEIGHT 192
|
||||
|
||||
#define LAYERS 8
|
||||
#define SCENE_ZOOM 9
|
||||
#define DOLPHIN_LAYER 6
|
||||
#define PARALLAX_MOD 7
|
||||
#define PARALLAX(layer) layer / PARALLAX_MOD - layer
|
||||
|
||||
#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[] =
|
||||
{[SLEEP] = 300, [IDLE] = 100, [WALK] = 100, [EMOTE] = 50, [INTERACT] = 10, [MINDCONTROL] = 50};
|
||||
{[IDLE] = 100, [EMOTE] = 50, [INTERACT] = 10, [MINDCONTROL] = 50};
|
||||
|
||||
typedef enum {
|
||||
EventTypeTick,
|
||||
@ -64,47 +66,60 @@ typedef struct {
|
||||
typedef struct {
|
||||
uint8_t layer;
|
||||
uint16_t timeout;
|
||||
int32_t x;
|
||||
int32_t y;
|
||||
const Icon* icon;
|
||||
char action_name[16];
|
||||
Vec2 pos;
|
||||
|
||||
uint8_t width;
|
||||
uint8_t height;
|
||||
|
||||
void (*draw)(Canvas* canvas, void* model);
|
||||
void (*callback)(Canvas* canvas, void* model);
|
||||
} 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 {
|
||||
///
|
||||
Vec2 player;
|
||||
Vec2 player_global;
|
||||
Vec2 player_v;
|
||||
Vec2 screen;
|
||||
|
||||
const Icon* dolphin_gfx;
|
||||
const Icon* dolphin_gfx_b; // temp
|
||||
FrameDirectionEnum frame_group;
|
||||
FrameDirectionEnum last_group;
|
||||
FrameDirectionEnum frame_pending;
|
||||
FrameDirectionEnum frame_type;
|
||||
|
||||
bool player_flipped;
|
||||
const DolphinFrame* current_frame;
|
||||
|
||||
bool transition;
|
||||
bool transition_pending;
|
||||
bool use_pending;
|
||||
// dolphin_scene_debug
|
||||
bool debug;
|
||||
|
||||
uint8_t player_anim;
|
||||
uint8_t scene_id;
|
||||
uint8_t frame_idx;
|
||||
|
||||
uint8_t scene_id;
|
||||
uint8_t emote_id;
|
||||
uint8_t previous_emote;
|
||||
|
||||
uint8_t dialogue_id;
|
||||
uint8_t previous_dialogue;
|
||||
|
||||
uint32_t action_timeout;
|
||||
uint8_t poi;
|
||||
|
||||
uint8_t action;
|
||||
uint8_t next_action;
|
||||
uint8_t prev_action;
|
||||
|
||||
int8_t zoom_v;
|
||||
uint8_t scene_zoom;
|
||||
uint8_t action_timeout;
|
||||
uint8_t dialog_progress;
|
||||
|
||||
FuriThread* scene_app_thread;
|
||||
|
||||
@ -6,43 +6,31 @@ void dolphin_scene_handle_user_input(SceneState* state, InputEvent* input) {
|
||||
furi_assert(state);
|
||||
furi_assert(input);
|
||||
|
||||
// dolphin_scene_debug
|
||||
if(input->type == InputTypeShort) {
|
||||
if(input->key == InputKeyUp) {
|
||||
state->debug = !state->debug;
|
||||
}
|
||||
}
|
||||
// toggle mind control on any user interaction
|
||||
state->last_group = state->frame_group;
|
||||
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(input->type == InputTypePress) {
|
||||
if(input->key == InputKeyRight) {
|
||||
state->player_flipped = false;
|
||||
state->player_v.y = 0;
|
||||
state->player_v.x = SPEED_X;
|
||||
} else if(input->key == InputKeyLeft) {
|
||||
state->player_flipped = true;
|
||||
state->player_v.y = 0;
|
||||
state->player_v.x = -SPEED_X;
|
||||
}
|
||||
} else if(input->type == InputTypeRelease) {
|
||||
if(input->key == InputKeyRight || input->key == InputKeyLeft) {
|
||||
} else if(input->key == InputKeyUp) {
|
||||
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) {
|
||||
if(input->key == InputKeyOk) {
|
||||
state->prev_action = MINDCONTROL;
|
||||
@ -59,13 +47,14 @@ void dolphin_scene_coordinates(SceneState* state, uint32_t dt) {
|
||||
|
||||
// global pos
|
||||
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
|
||||
state->scene_zoom = CLAMP(state->scene_zoom + state->zoom_v, SCENE_ZOOM, 0);
|
||||
state->player.x = CLAMP(state->player.x - (state->zoom_v * (SPEED_X * 2)), DOLPHIN_CENTER, 0);
|
||||
state->player.y = CLAMP(state->player.y - (state->zoom_v * SPEED_X / 2), DOLPHIN_DEFAULT_Y, 3);
|
||||
|
||||
//center screen
|
||||
state->screen.x = state->player_global.x - state->player.x;
|
||||
state->player_anim = (state->player_global.x / 10) % 2;
|
||||
// nudge camera postition
|
||||
if(state->player_global.x > 170) {
|
||||
state->player.x =
|
||||
CLAMP(state->player.x - state->player_v.x / 2, DOLPHIN_CENTER, -DOLPHIN_WIDTH / 2);
|
||||
} else if(state->player_global.x < 70) {
|
||||
state->player.x =
|
||||
CLAMP(state->player.x - state->player_v.x / 2, DOLPHIN_WIDTH * 2, DOLPHIN_CENTER);
|
||||
}
|
||||
}
|
||||
@ -1,74 +1,32 @@
|
||||
#include <furi.h>
|
||||
#include "scene.h"
|
||||
#include "assets/emotes.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;
|
||||
}
|
||||
#include "assets/items.h"
|
||||
|
||||
static void scene_proceed_action(SceneState* state) {
|
||||
furi_assert(state);
|
||||
|
||||
state->prev_action = state->action;
|
||||
state->action = (state->prev_action != state->next_action) ?
|
||||
state->next_action :
|
||||
roll_new(state->next_action, ACTIONS_NUM);
|
||||
state->action = roll_new(state->prev_action, ACTIONS_NUM);
|
||||
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) {
|
||||
furi_assert(state);
|
||||
if(state->action == MINDCONTROL && state->player_v.x != 0) {
|
||||
state->action_timeout = default_timeout[state->action];
|
||||
if(state->action == MINDCONTROL) {
|
||||
if(state->player_v.x != 0 || state->player_v.y != 0) {
|
||||
state->action_timeout = default_timeout[state->action];
|
||||
}
|
||||
}
|
||||
|
||||
if(state->action_timeout > 0) {
|
||||
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) {
|
||||
furi_assert(state);
|
||||
scene_action_handler(state);
|
||||
UNUSED(dialogues_list);
|
||||
|
||||
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:
|
||||
if(state->action_timeout == 0) {
|
||||
if(state->prev_action == MINDCONTROL) {
|
||||
@ -78,20 +36,9 @@ void dolphin_scene_update_state(SceneState* state, uint32_t t, uint32_t dt) {
|
||||
}
|
||||
}
|
||||
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:
|
||||
|
||||
if(state->action_timeout == 0) {
|
||||
scene_proceed_action(state);
|
||||
}
|
||||
|
||||
@ -1,101 +1,46 @@
|
||||
#include <furi.h>
|
||||
#include "scene.h"
|
||||
#include "assets/emotes.h"
|
||||
#include "assets/items.h"
|
||||
#include "assets/meta.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) {
|
||||
furi_assert(state);
|
||||
furi_assert(canvas);
|
||||
char buf[32];
|
||||
|
||||
const Item* near = is_nearby(state);
|
||||
if(near) {
|
||||
int32_t hint_pos_x = (near->x - state->player_global.x) * PARALLAX(near->layer) + 25;
|
||||
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);
|
||||
if(state->player_v.x < 0) {
|
||||
state->frame_pending = DirLeft;
|
||||
} else if(state->player_v.x > 0) {
|
||||
state->frame_pending = DirRight;
|
||||
} else if(state->player_v.y < 0) {
|
||||
state->frame_pending = DirUp;
|
||||
} else if(state->player_v.y > 0) {
|
||||
state->frame_pending = DirDown;
|
||||
}
|
||||
}
|
||||
state->transition_pending = state->frame_group != state->frame_pending;
|
||||
|
||||
static void scene_draw_current_emote(SceneState* state, Canvas* canvas) {
|
||||
furi_assert(state);
|
||||
furi_assert(canvas);
|
||||
elements_multiline_text_framed(canvas, 80, 20, (char*)emotes_list[state->emote_id]);
|
||||
}
|
||||
if(*&frames[state->frame_group][state->frame_type]->frames[state->frame_idx].f) {
|
||||
state->current_frame = *&frames[state->frame_group][state->frame_type];
|
||||
}
|
||||
|
||||
static void scene_draw_sleep_emote(SceneState* state, Canvas* canvas) {
|
||||
furi_assert(state);
|
||||
furi_assert(canvas);
|
||||
uint8_t total = state->current_frame->frames[2].f == NULL ? 2 : 3;
|
||||
|
||||
char dialog_str[] = "zZzZ..";
|
||||
// 2do - sofa x pos getter
|
||||
if(state->player_global.x == 154 && 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, 80, 20, dialog_str);
|
||||
}
|
||||
if(state->transition_pending && !state->frame_idx) {
|
||||
state->transition_pending = false;
|
||||
state->transition = true;
|
||||
}
|
||||
|
||||
if(state->transition) {
|
||||
state->frame_type = state->frame_pending;
|
||||
state->frame_group = state->last_group;
|
||||
state->transition = !(state->frame_idx == total - 1);
|
||||
} else {
|
||||
state->dialog_progress = 0;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
state->frame_group = state->frame_type;
|
||||
}
|
||||
|
||||
elements_multiline_text_framed(canvas, 68, 16, buf);
|
||||
}
|
||||
state->player_anim++;
|
||||
|
||||
/*
|
||||
static void draw_idle_emote(SceneState* state, Canvas* canvas){
|
||||
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;
|
||||
if(!(state->player_anim % speed_mod)) {
|
||||
state->frame_idx = (state->frame_idx + 1) % total;
|
||||
}
|
||||
}
|
||||
|
||||
@ -103,44 +48,24 @@ void dolphin_scene_render_dolphin(SceneState* state, Canvas* canvas) {
|
||||
furi_assert(state);
|
||||
furi_assert(canvas);
|
||||
|
||||
if(state->scene_zoom == SCENE_ZOOM) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
dolphin_scene_transition_handler(state);
|
||||
|
||||
canvas_set_bitmap_mode(canvas, true);
|
||||
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_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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
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) {
|
||||
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);
|
||||
|
||||
for(uint8_t l = 0; l < LAYERS; l++) {
|
||||
if(state->scene_zoom < SCENE_ZOOM) {
|
||||
for(uint8_t i = 0; i < ITEMS_NUM; i++) {
|
||||
int32_t item_pos = (current_scene[i]->x - state->player_global.x);
|
||||
if(item_screen_bounds(item_pos)) {
|
||||
if(current_scene[i]->draw) current_scene[i]->draw(canvas, state);
|
||||
for(uint8_t i = 0; i < ItemsEnumTotal; i++) {
|
||||
int32_t item_pos_X = (current_scene[i]->pos.x - state->player_global.x);
|
||||
int32_t item_pos_Y = (current_scene[i]->pos.y - state->player_global.y);
|
||||
|
||||
if(l == current_scene[i]->layer) {
|
||||
canvas_draw_icon(
|
||||
canvas,
|
||||
item_pos * PARALLAX(l),
|
||||
current_scene[i]->y,
|
||||
current_scene[i]->icon);
|
||||
canvas_set_bitmap_mode(canvas, false);
|
||||
if(item_screen_bounds_x(item_pos_X) && item_screen_bounds_y(item_pos_Y)) {
|
||||
if(l == current_scene[i]->layer) {
|
||||
if(current_scene[i]->draw) {
|
||||
current_scene[i]->draw(canvas, state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(l == 0) canvas_draw_line(canvas, 0, 42, 128, 42);
|
||||
}
|
||||
|
||||
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) {
|
||||
sprintf(
|
||||
buf,
|
||||
"x:%ld>%d %ld %s",
|
||||
state->player_global.x,
|
||||
state->poi,
|
||||
state->action_timeout,
|
||||
action_str[state->action]);
|
||||
"%d:%d %d/%dP%dL%d T%d-%d",
|
||||
state->frame_idx,
|
||||
state->current_frame->frames[2].f == NULL ? 2 : 3,
|
||||
state->frame_group,
|
||||
state->frame_type,
|
||||
state->frame_pending,
|
||||
state->last_group,
|
||||
state->transition_pending,
|
||||
state->transition);
|
||||
canvas_draw_str(canvas, 0, 13, buf);
|
||||
}
|
||||
|
||||
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);
|
||||
if(state->action == INTERACT) scene_activate_item_callback(state, canvas);
|
||||
}
|
||||
@ -192,7 +192,8 @@ int32_t gui_test(void* param) {
|
||||
text_input_callback,
|
||||
gui_tester,
|
||||
text_input_text,
|
||||
text_input_text_len);
|
||||
text_input_text_len,
|
||||
false);
|
||||
text_input_set_header_text(gui_tester->text_input, "Name the key");
|
||||
|
||||
const uint8_t byte_input_bytes_len = 16;
|
||||
|
||||
23
applications/gui/modules/text_input.c
Normal file → Executable file
@ -16,6 +16,7 @@ typedef struct {
|
||||
const char* header;
|
||||
char* text_buffer;
|
||||
size_t text_buffer_size;
|
||||
bool clear_default_text;
|
||||
|
||||
TextInputCallback callback;
|
||||
void* callback_context;
|
||||
@ -128,7 +129,7 @@ static const char char_to_uppercase(const char letter) {
|
||||
}
|
||||
|
||||
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) {
|
||||
model->text_buffer[text_length - 1] = 0;
|
||||
}
|
||||
@ -158,11 +159,16 @@ static void text_input_view_draw_callback(Canvas* canvas, void* _model) {
|
||||
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 + 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);
|
||||
|
||||
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) {
|
||||
text_input_backspace_cb(model);
|
||||
} 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)) {
|
||||
selected = char_to_uppercase(selected);
|
||||
}
|
||||
model->text_buffer[text_length] = selected;
|
||||
model->text_buffer[text_length + 1] = 0;
|
||||
}
|
||||
model->clear_default_text = false;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
@ -365,6 +375,7 @@ TextInput* text_input_alloc() {
|
||||
model->header = "";
|
||||
model->selected_row = 0;
|
||||
model->selected_column = 0;
|
||||
model->clear_default_text = false;
|
||||
return true;
|
||||
});
|
||||
|
||||
@ -387,13 +398,15 @@ void text_input_set_result_callback(
|
||||
TextInputCallback callback,
|
||||
void* callback_context,
|
||||
char* text_buffer,
|
||||
size_t text_buffer_size) {
|
||||
size_t text_buffer_size,
|
||||
bool clear_default_text) {
|
||||
with_view_model(
|
||||
text_input->view, (TextInputModel * model) {
|
||||
model->callback = callback;
|
||||
model->callback_context = callback_context;
|
||||
model->text_buffer = text_buffer;
|
||||
model->text_buffer_size = text_buffer_size;
|
||||
model->clear_default_text = clear_default_text;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
@ -39,13 +39,15 @@ View* text_input_get_view(TextInput* text_input);
|
||||
* @param callback_context - callback context
|
||||
* @param text_buffer - pointer to YOUR text buffer, that we going to modify
|
||||
* @param text_buffer_size - YOUR text buffer size in bytes. Max string length will be text_buffer_size - 1.
|
||||
* @param clear_default_text - clear text from text_buffer on first OK event
|
||||
*/
|
||||
void text_input_set_result_callback(
|
||||
TextInput* text_input,
|
||||
TextInputCallback callback,
|
||||
void* callback_context,
|
||||
char* text_buffer,
|
||||
size_t text_buffer_size);
|
||||
size_t text_buffer_size,
|
||||
bool clear_default_text);
|
||||
|
||||
/**
|
||||
* @brief Set text input header text
|
||||
|
||||
@ -53,11 +53,11 @@ bool scene_manager_handle_custom_event(SceneManager* scene_manager, uint32_t cus
|
||||
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);
|
||||
|
||||
SceneManagerEvent event = {
|
||||
.type = SceneManagerEventTypeNavigation,
|
||||
.type = SceneManagerEventTypeBack,
|
||||
};
|
||||
uint32_t scene_id = *SceneManagerIdStack_back(scene_manager->scene_id_stack);
|
||||
bool consumed =
|
||||
@ -109,7 +109,9 @@ bool scene_manager_previous_scene(SceneManager* scene_manager) {
|
||||
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);
|
||||
|
||||
uint32_t prev_scene_id = 0;
|
||||
@ -137,3 +139,45 @@ bool scene_manager_search_previous_scene(SceneManager* scene_manager, uint32_t s
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@ -11,7 +11,7 @@ extern "C" {
|
||||
*/
|
||||
typedef enum {
|
||||
SceneManagerEventTypeCustom,
|
||||
SceneManagerEventTypeNavigation,
|
||||
SceneManagerEventTypeBack,
|
||||
SceneManagerEventTypeTick,
|
||||
} 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);
|
||||
|
||||
/** Navigation event handler
|
||||
* Calls Scene event handler with Navigation event parameter
|
||||
/** Back event handler
|
||||
* Calls Scene event handler with Back event parameter
|
||||
* @param scene_manager SceneManager instance
|
||||
* @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
|
||||
* 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);
|
||||
|
||||
/** Search previous Scene by ID
|
||||
/** Search 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_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
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
#include <callback-connector.h>
|
||||
#include <m-string.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_extension = ".ibtn";
|
||||
@ -40,9 +40,6 @@ iButtonApp::iButtonApp()
|
||||
: notification{"notification"} {
|
||||
api_hal_power_insomnia_enter();
|
||||
key_worker = new KeyWorker(&ibutton_gpio);
|
||||
|
||||
// we need random
|
||||
srand(DWT->CYCCNT);
|
||||
}
|
||||
|
||||
iButtonApp::~iButtonApp() {
|
||||
@ -186,38 +183,6 @@ uint8_t iButtonApp::get_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
|
||||
bool iButtonApp::save_key(const char* key_name) {
|
||||
// Create ibutton directory if necessary
|
||||
|
||||
@ -90,8 +90,6 @@ public:
|
||||
char* get_file_name();
|
||||
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 load_key();
|
||||
bool load_key(const char* key_name);
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
#include <api-hal.h>
|
||||
#include <stdarg.h>
|
||||
#include <cli/cli.h>
|
||||
#include <args.h>
|
||||
#include <lib/toolbox/args.h>
|
||||
|
||||
#include "helpers/key-info.h"
|
||||
#include "helpers/key-worker.h"
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
#include "../ibutton-event.h"
|
||||
#include "../ibutton-key.h"
|
||||
#include <callback-connector.h>
|
||||
#include <lib/toolbox/random_name.h>
|
||||
|
||||
void iButtonSceneSaveName::on_enter(iButtonApp* app) {
|
||||
iButtonAppViewManager* view_manager = app->get_view_manager();
|
||||
@ -12,16 +13,17 @@ void iButtonSceneSaveName::on_enter(iButtonApp* app) {
|
||||
|
||||
iButtonKey* key = app->get_key();
|
||||
const char* key_name = key->get_name();
|
||||
bool key_name_empty = !strcmp(key_name, "");
|
||||
|
||||
if(strcmp(key_name, "") == 0) {
|
||||
app->generate_random_name(app->get_text_store(), app->get_text_store_size());
|
||||
if(key_name_empty) {
|
||||
set_random_name(app->get_text_store(), app->get_text_store_size());
|
||||
} else {
|
||||
app->set_text_store("%s", key_name);
|
||||
}
|
||||
|
||||
text_input_set_header_text(text_input, "Name the key");
|
||||
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);
|
||||
}
|
||||
@ -47,7 +49,7 @@ bool iButtonSceneSaveName::on_event(iButtonApp* app, iButtonEvent* event) {
|
||||
void iButtonSceneSaveName::on_exit(iButtonApp* app) {
|
||||
TextInput* text_input = app->get_view_manager()->get_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) {
|
||||
|
||||
@ -10,6 +10,7 @@
|
||||
#include <string>
|
||||
#include <m-string.h>
|
||||
#include <irda_transmit.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
static void signal_received_callback(void* context, IrdaWorkerSignal* 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) {
|
||||
if(api_hal_irda_rx_irq_is_busy()) {
|
||||
if(api_hal_irda_is_busy()) {
|
||||
printf("IRDA is busy. Exit.");
|
||||
return;
|
||||
}
|
||||
@ -105,7 +106,7 @@ static bool parse_signal_raw(
|
||||
uint32_t* timings,
|
||||
uint32_t* timings_cnt,
|
||||
float* duty_cycle,
|
||||
float* frequency) {
|
||||
uint32_t* frequency) {
|
||||
char frequency_str[10];
|
||||
char duty_cycle_str[10];
|
||||
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) {
|
||||
if(api_hal_irda_rx_irq_is_busy()) {
|
||||
if(api_hal_irda_is_busy()) {
|
||||
printf("IRDA is busy. Exit.");
|
||||
return;
|
||||
}
|
||||
|
||||
IrdaMessage message;
|
||||
const char* str = string_get_cstr(args);
|
||||
float frequency;
|
||||
uint32_t frequency;
|
||||
float duty_cycle;
|
||||
uint32_t* timings = (uint32_t*)furi_alloc(sizeof(uint32_t) * 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)) {
|
||||
irda_send(&message, 1);
|
||||
} 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 {
|
||||
printf("Wrong arguments.\r\n");
|
||||
irda_cli_print_usage();
|
||||
|
||||
@ -20,7 +20,8 @@ void IrdaAppSceneEditRename::on_enter(IrdaApp* app) {
|
||||
IrdaApp::text_input_callback,
|
||||
app,
|
||||
app->get_text_store(0),
|
||||
app->get_text_store_size());
|
||||
app->get_text_store_size(),
|
||||
false);
|
||||
|
||||
view_manager->switch_to(IrdaAppViewManager::ViewType::TextInput);
|
||||
}
|
||||
|
||||
@ -26,7 +26,8 @@ void IrdaAppSceneLearnEnterName::on_enter(IrdaApp* app) {
|
||||
IrdaApp::text_input_callback,
|
||||
app,
|
||||
app->get_text_store(0),
|
||||
app->get_text_store_size());
|
||||
app->get_text_store_size(),
|
||||
false);
|
||||
|
||||
view_manager->switch_to(IrdaAppViewManager::ViewType::TextInput);
|
||||
}
|
||||
|
||||
15
applications/lfrfid/helpers/decoder-gpio-out.cpp
Normal 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);
|
||||
}
|
||||
14
applications/lfrfid/helpers/decoder-gpio-out.h
Normal 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();
|
||||
};
|
||||
@ -2,18 +2,24 @@
|
||||
#include <api-hal.h>
|
||||
|
||||
constexpr uint32_t clocks_in_us = 64;
|
||||
|
||||
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;
|
||||
constexpr uint32_t us_per_bit = 255;
|
||||
|
||||
bool DecoderIndala::read(uint8_t* data, uint8_t data_size) {
|
||||
bool result = false;
|
||||
|
||||
if(ready) {
|
||||
result = true;
|
||||
printf("IND %02X %02X %02X\r\n", facility, (uint8_t)(number >> 8), (uint8_t)number);
|
||||
ready = false;
|
||||
if(cursed_data_valid) {
|
||||
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;
|
||||
@ -22,149 +28,49 @@ bool DecoderIndala::read(uint8_t* data, uint8_t data_size) {
|
||||
void DecoderIndala::process_front(bool polarity, uint32_t time) {
|
||||
if(ready) return;
|
||||
|
||||
if(polarity == false) {
|
||||
last_pulse_time = time;
|
||||
} else {
|
||||
last_pulse_time += time;
|
||||
pulse_count++;
|
||||
process_internal(polarity, time, &raw_data);
|
||||
if(ready) return;
|
||||
|
||||
if(last_pulse_time > min_time_us && last_pulse_time < max_time_us) {
|
||||
if(last_pulse_time > mid_time_us) {
|
||||
bool last_data = !(readed_data & 1);
|
||||
pulse_count = 0;
|
||||
readed_data = (readed_data << 1) | last_data;
|
||||
verify();
|
||||
} else if((pulse_count % 16) == 0) {
|
||||
bool last_data = readed_data & 1;
|
||||
pulse_count = 0;
|
||||
readed_data = (readed_data << 1) | last_data;
|
||||
verify();
|
||||
if(polarity) {
|
||||
time = time + 110;
|
||||
} else {
|
||||
time = time - 110;
|
||||
}
|
||||
|
||||
process_internal(!polarity, time, &cursed_raw_data);
|
||||
if(ready) {
|
||||
cursed_data_valid = true;
|
||||
}
|
||||
}
|
||||
|
||||
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() {
|
||||
reset_state();
|
||||
}
|
||||
|
||||
void DecoderIndala::reset_state() {
|
||||
}
|
||||
|
||||
void DecoderIndala::verify() {
|
||||
// verify inverse
|
||||
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;
|
||||
raw_data = 0;
|
||||
cursed_raw_data = 0;
|
||||
ready = false;
|
||||
cursed_data_valid = false;
|
||||
}
|
||||
@ -2,27 +2,24 @@
|
||||
#include <stdint.h>
|
||||
#include <limits.h>
|
||||
#include <atomic>
|
||||
#include "protocols/protocol-indala-40134.h"
|
||||
|
||||
class DecoderIndala {
|
||||
public:
|
||||
bool read(uint8_t* data, uint8_t data_size);
|
||||
void process_front(bool polarity, uint32_t time);
|
||||
|
||||
void process_internal(bool polarity, uint32_t time, uint64_t* data);
|
||||
|
||||
DecoderIndala();
|
||||
|
||||
private:
|
||||
void reset_state();
|
||||
|
||||
void verify();
|
||||
void verify_inner();
|
||||
|
||||
uint32_t last_pulse_time = 0;
|
||||
uint32_t pulse_count = 0;
|
||||
uint32_t overall_pulse_count = 0;
|
||||
|
||||
uint64_t readed_data = 0;
|
||||
uint64_t raw_data;
|
||||
uint64_t cursed_raw_data;
|
||||
|
||||
std::atomic<bool> ready;
|
||||
uint8_t facility = 0;
|
||||
uint16_t number = 0;
|
||||
std::atomic<bool> cursed_data_valid;
|
||||
ProtocolIndala40134 indala;
|
||||
};
|
||||
@ -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);
|
||||
|
||||
card_data_index = 0;
|
||||
bit_index = 0;
|
||||
}
|
||||
|
||||
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) {
|
||||
// hid 0 is 6 cycles by 8 clocks
|
||||
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;
|
||||
uint8_t bit = (card_data[card_data_index / 32] >> (31 - (card_data_index % 32))) & 1;
|
||||
|
||||
bool bit = (card_data[card_data_index / 32] >> (31 - (card_data_index % 32))) & 1;
|
||||
|
||||
*polarity = true;
|
||||
if(bit) {
|
||||
*period = hid_1_period;
|
||||
*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;
|
||||
}
|
||||
bool advance = fsk->next(bit, period);
|
||||
if(advance) {
|
||||
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;
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
#include "encoder-generic.h"
|
||||
#include "osc-fsk.h"
|
||||
|
||||
class EncoderHID_H10301 : public EncoderGeneric {
|
||||
public:
|
||||
@ -10,15 +11,16 @@ public:
|
||||
* @param data_size must be 3
|
||||
*/
|
||||
void init(const uint8_t* data, const uint8_t data_size) final;
|
||||
|
||||
void get_next(bool* polarity, uint16_t* period, uint16_t* pulse) final;
|
||||
EncoderHID_H10301();
|
||||
~EncoderHID_H10301();
|
||||
|
||||
private:
|
||||
static const uint8_t card_data_max = 3;
|
||||
uint32_t card_data[card_data_max];
|
||||
uint8_t card_data_index;
|
||||
uint8_t bit_index;
|
||||
|
||||
void write_bit(bool bit, uint8_t position);
|
||||
void write_raw_bit(bool bit, uint8_t position);
|
||||
|
||||
OscFSK* fsk;
|
||||
};
|
||||
20
applications/lfrfid/helpers/osc-fsk.cpp
Normal 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;
|
||||
}
|
||||
30
applications/lfrfid/helpers/osc-fsk.h
Normal 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;
|
||||
};
|
||||
@ -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() {
|
||||
return sizeof(Indala40134CardData);
|
||||
}
|
||||
@ -110,6 +115,46 @@ void ProtocolIndala40134::encode(
|
||||
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(
|
||||
const uint8_t* encoded_data,
|
||||
const uint8_t encoded_data_size,
|
||||
@ -117,15 +162,76 @@ void ProtocolIndala40134::decode(
|
||||
const uint8_t decoded_data_size) {
|
||||
furi_check(decoded_data_size >= get_decoded_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(
|
||||
const uint8_t* encoded_data,
|
||||
const uint8_t encoded_data_size) {
|
||||
furi_check(encoded_data_size >= get_encoded_data_size());
|
||||
// TODO implement decoding
|
||||
furi_check(0);
|
||||
return false;
|
||||
bool can_be_decoded = 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;
|
||||
}
|
||||
@ -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');
|
||||
}
|
||||
@ -1,4 +0,0 @@
|
||||
#pragma once
|
||||
#include "stdint.h"
|
||||
|
||||
void rfid_generate_random_name(char* name, uint8_t max_name_size);
|
||||
@ -17,19 +17,47 @@ struct RfidReaderAccessor {
|
||||
|
||||
void RfidReader::decode(bool polarity) {
|
||||
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) {
|
||||
case Type::Normal:
|
||||
decoder_em.process_front(polarity, current_dwt_value - last_dwt_value);
|
||||
decoder_hid26.process_front(polarity, current_dwt_value - last_dwt_value);
|
||||
//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;
|
||||
decoder_em.process_front(polarity, period);
|
||||
decoder_hid26.process_front(polarity, period);
|
||||
break;
|
||||
case Type::Indala:
|
||||
decoder_em.process_front(polarity, period);
|
||||
decoder_hid26.process_front(polarity, period);
|
||||
decoder_indala.process_front(polarity, period);
|
||||
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) {
|
||||
@ -45,47 +73,103 @@ static void comparator_trigger_callback(void* hcomp, void* comp_ctx) {
|
||||
RfidReader::RfidReader() {
|
||||
}
|
||||
|
||||
void RfidReader::start(Type _type) {
|
||||
type = _type;
|
||||
void RfidReader::start() {
|
||||
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) {
|
||||
case Type::Normal:
|
||||
start_timer();
|
||||
start();
|
||||
break;
|
||||
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;
|
||||
}
|
||||
|
||||
start_comparator();
|
||||
}
|
||||
|
||||
void RfidReader::stop() {
|
||||
stop_gpio();
|
||||
stop_timer();
|
||||
api_hal_rfid_pins_reset();
|
||||
api_hal_rfid_tim_read_stop();
|
||||
api_hal_rfid_tim_reset();
|
||||
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 something_readed = false;
|
||||
|
||||
// reading
|
||||
if(decoder_em.read(data, data_size)) {
|
||||
*type = LfrfidKeyType::KeyEM4100;
|
||||
result = true;
|
||||
*_type = LfrfidKeyType::KeyEM4100;
|
||||
something_readed = true;
|
||||
}
|
||||
|
||||
if(decoder_hid26.read(data, data_size)) {
|
||||
*type = LfrfidKeyType::KeyH10301;
|
||||
result = true;
|
||||
*_type = LfrfidKeyType::KeyH10301;
|
||||
something_readed = true;
|
||||
}
|
||||
|
||||
//decoder_indala.read(NULL, 0);
|
||||
//decoder_analyzer.read(NULL, 0);
|
||||
if(decoder_indala.read(data, data_size)) {
|
||||
*_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;
|
||||
}
|
||||
|
||||
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) {
|
||||
api_interrupt_add(comparator_trigger_callback, InterruptTypeComparatorTrigger, this);
|
||||
last_dwt_value = DWT->CYCCNT;
|
||||
@ -93,7 +177,7 @@ void RfidReader::start_comparator(void) {
|
||||
hcomp1.Init.InputMinus = COMP_INPUT_MINUS_1_2VREFINT;
|
||||
hcomp1.Init.InputPlus = COMP_INPUT_PLUS_IO1;
|
||||
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.Mode = COMP_POWERMODE_MEDIUMSPEED;
|
||||
hcomp1.Init.WindowMode = COMP_WINDOWMODE_DISABLE;
|
||||
@ -105,30 +189,7 @@ void RfidReader::start_comparator(void) {
|
||||
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) {
|
||||
HAL_COMP_Stop(&hcomp1);
|
||||
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();
|
||||
}
|
||||
@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
#include "decoder-analyzer.h"
|
||||
//#include "decoder-analyzer.h"
|
||||
#include "decoder-gpio-out.h"
|
||||
#include "decoder-emmarine.h"
|
||||
#include "decoder-hid26.h"
|
||||
#include "decoder-indala.h"
|
||||
@ -13,14 +14,19 @@ public:
|
||||
};
|
||||
|
||||
RfidReader();
|
||||
void start(Type type);
|
||||
void start();
|
||||
void start_forced(RfidReader::Type type);
|
||||
void stop();
|
||||
bool read(LfrfidKeyType* type, uint8_t* data, uint8_t data_size);
|
||||
|
||||
bool detect();
|
||||
bool any_read();
|
||||
|
||||
private:
|
||||
friend struct RfidReaderAccessor;
|
||||
|
||||
//DecoderAnalyzer decoder_analyzer;
|
||||
//DecoderGpioOut decoder_gpio_out;
|
||||
DecoderEMMarine decoder_em;
|
||||
DecoderHID26 decoder_hid26;
|
||||
DecoderIndala decoder_indala;
|
||||
@ -28,14 +34,20 @@ private:
|
||||
uint32_t last_dwt_value;
|
||||
|
||||
void start_comparator(void);
|
||||
void start_timer(void);
|
||||
void start_timer_indala(void);
|
||||
void start_gpio(void);
|
||||
void stop_comparator(void);
|
||||
void stop_timer(void);
|
||||
void stop_gpio(void);
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
@ -7,7 +7,7 @@ RfidWorker::~RfidWorker() {
|
||||
}
|
||||
|
||||
void RfidWorker::start_read() {
|
||||
reader.start(RfidReader::Type::Normal);
|
||||
reader.start();
|
||||
}
|
||||
|
||||
bool RfidWorker::read() {
|
||||
@ -25,6 +25,14 @@ bool RfidWorker::read() {
|
||||
return result;
|
||||
}
|
||||
|
||||
bool RfidWorker::detect() {
|
||||
return reader.detect();
|
||||
}
|
||||
|
||||
bool RfidWorker::any_read() {
|
||||
return reader.any_read();
|
||||
}
|
||||
|
||||
void RfidWorker::stop_read() {
|
||||
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_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));
|
||||
}
|
||||
|
||||
@ -59,26 +67,37 @@ void RfidWorker::stop_emulate() {
|
||||
}
|
||||
|
||||
void RfidWorker::sq_write() {
|
||||
// TODO expand this
|
||||
switch(key.get_type()) {
|
||||
case LfrfidKeyType::KeyEM4100:
|
||||
writer.start();
|
||||
writer.write_em(key.get_data());
|
||||
writer.stop();
|
||||
break;
|
||||
case LfrfidKeyType::KeyH10301:
|
||||
writer.start();
|
||||
writer.write_hid(key.get_data());
|
||||
writer.stop();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
for(size_t i = 0; i < 5; i++) {
|
||||
switch(key.get_type()) {
|
||||
case LfrfidKeyType::KeyEM4100:
|
||||
writer.start();
|
||||
writer.write_em(key.get_data());
|
||||
writer.stop();
|
||||
break;
|
||||
case LfrfidKeyType::KeyH10301:
|
||||
writer.start();
|
||||
writer.write_hid(key.get_data());
|
||||
writer.stop();
|
||||
break;
|
||||
case LfrfidKeyType::KeyI40134:
|
||||
writer.start();
|
||||
writer.write_indala(key.get_data());
|
||||
writer.stop();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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() {
|
||||
@ -88,7 +107,11 @@ void RfidWorker::sq_write_validate() {
|
||||
|
||||
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(memcmp(data, key.get_data(), key.get_type_data_count()) == 0) {
|
||||
write_result = WriteResult::Ok;
|
||||
@ -99,10 +122,6 @@ void RfidWorker::sq_write_validate() {
|
||||
} else {
|
||||
validate_counts++;
|
||||
}
|
||||
|
||||
if(validate_counts > 5) {
|
||||
write_result = WriteResult::NotWritable;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -13,6 +13,8 @@ public:
|
||||
|
||||
void start_read();
|
||||
bool read();
|
||||
bool detect();
|
||||
bool any_read();
|
||||
void stop_read();
|
||||
|
||||
enum class WriteResult : uint8_t {
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
#include <api-hal.h>
|
||||
#include "protocols/protocol-emmarin.h"
|
||||
#include "protocols/protocol-hid-h10301.h"
|
||||
#include "protocols/protocol-indala-40134.h"
|
||||
|
||||
extern COMP_HandleTypeDef hcomp1;
|
||||
|
||||
@ -115,7 +116,7 @@ void RfidWriter::write_em(const uint8_t em_data[5]) {
|
||||
ProtocolEMMarin em_card;
|
||||
uint64_t em_encoded_data;
|
||||
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();
|
||||
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();
|
||||
__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();
|
||||
}
|
||||
|
||||
@ -9,6 +9,7 @@ public:
|
||||
void stop();
|
||||
void write_em(const uint8_t em_data[5]);
|
||||
void write_hid(const uint8_t hid_data[3]);
|
||||
void write_indala(const uint8_t indala_data[3]);
|
||||
|
||||
private:
|
||||
void write_gap(uint32_t gap_time);
|
||||
|
||||
@ -17,7 +17,7 @@
|
||||
#include "scene/lfrfid-app-scene-delete-success.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_extension = ".rfid";
|
||||
@ -27,9 +27,6 @@ LfRfidApp::LfRfidApp()
|
||||
, notification{"notification"}
|
||||
, text_store(40) {
|
||||
api_hal_power_insomnia_enter();
|
||||
|
||||
// we need random
|
||||
srand(DWT->CYCCNT);
|
||||
}
|
||||
|
||||
LfRfidApp::~LfRfidApp() {
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
#include <api-hal.h>
|
||||
#include <stdarg.h>
|
||||
#include <cli/cli.h>
|
||||
#include <args.h>
|
||||
#include <lib/toolbox/args.h>
|
||||
|
||||
#include "helpers/rfid-reader.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) {
|
||||
RfidReader reader;
|
||||
reader.start(RfidReader::Type::Normal);
|
||||
reader.start();
|
||||
|
||||
static const uint8_t data_size = LFRFID_KEY_SIZE;
|
||||
uint8_t data[data_size] = {0};
|
||||
|
||||
@ -55,6 +55,7 @@ void LfRfidAppSceneReadSuccess::on_enter(LfRfidApp* app, bool need_restore) {
|
||||
string_get_cstr(string[2]), 68, 47, AlignLeft, AlignBottom, FontSecondary);
|
||||
break;
|
||||
case LfrfidKeyType::KeyH10301:
|
||||
case LfrfidKeyType::KeyI40134:
|
||||
line_1_text->set_text("HEX:", 65, 23, 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);
|
||||
@ -73,9 +74,6 @@ void LfRfidAppSceneReadSuccess::on_enter(LfRfidApp* app, bool need_restore) {
|
||||
line_3_value->set_text(
|
||||
string_get_cstr(string[2]), 68, 47, AlignLeft, AlignBottom, FontSecondary);
|
||||
break;
|
||||
case LfrfidKeyType::KeyI40134:
|
||||
//TODO implement when we can read Indala
|
||||
break;
|
||||
}
|
||||
|
||||
app->view_controller.switch_to<ContainerVM>();
|
||||
|
||||
@ -18,7 +18,13 @@ bool LfRfidAppSceneRead::on_event(LfRfidApp* app, LfRfidApp::Event* event) {
|
||||
notification_message(app->notification, &sequence_success);
|
||||
app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::ReadSuccess);
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
13
applications/lfrfid/scene/lfrfid-app-scene-save-name.cpp
Normal file → Executable file
@ -1,11 +1,12 @@
|
||||
#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) {
|
||||
const char* key_name = app->worker.key.get_name();
|
||||
|
||||
if(strcmp(key_name, "") == 0) {
|
||||
rfid_generate_random_name(app->text_store.text, app->text_store.text_size);
|
||||
bool key_name_empty = !strcmp(key_name, "");
|
||||
if(key_name_empty) {
|
||||
set_random_name(app->text_store.text, app->text_store.text_size);
|
||||
} else {
|
||||
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_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>();
|
||||
}
|
||||
|
||||
7
applications/nfc/nfc.c
Normal file → Executable 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);
|
||||
}
|
||||
|
||||
bool nfc_navigation_event_callback(void* context) {
|
||||
bool nfc_back_event_callback(void* context) {
|
||||
furi_assert(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) {
|
||||
@ -28,8 +28,7 @@ Nfc* nfc_alloc() {
|
||||
view_dispatcher_enable_queue(nfc->view_dispatcher);
|
||||
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_navigation_event_callback(
|
||||
nfc->view_dispatcher, nfc_navigation_event_callback);
|
||||
view_dispatcher_set_navigation_event_callback(nfc->view_dispatcher, nfc_back_event_callback);
|
||||
view_dispatcher_set_tick_event_callback(nfc->view_dispatcher, nfc_tick_event_callback, 100);
|
||||
|
||||
// Open GUI record
|
||||
|
||||
@ -19,7 +19,6 @@ void nfc_cli_detect(Cli* cli, string_t args, void* context) {
|
||||
rfalNfcDevice* dev_list;
|
||||
uint8_t dev_cnt = 0;
|
||||
bool cmd_exit = false;
|
||||
api_hal_nfc_init();
|
||||
api_hal_nfc_exit_sleep();
|
||||
printf("Detecting nfc...\r\nPress Ctrl+C to abort\r\n");
|
||||
while(!cmd_exit) {
|
||||
@ -51,7 +50,6 @@ void nfc_cli_emulate(Cli* cli, string_t args, void* context) {
|
||||
return;
|
||||
}
|
||||
|
||||
api_hal_nfc_init();
|
||||
api_hal_nfc_exit_sleep();
|
||||
printf("Emulating NFC-A Type: T2T UID: CF72D440 SAK: 20 ATQA: 00/04\r\n");
|
||||
printf("Press Ctrl+C to abort\r\n");
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
#include "nfc_device_i.h"
|
||||
|
||||
#include <file-worker.h>
|
||||
#include <path.h>
|
||||
#include <hex.h>
|
||||
#include <lib/toolbox/path.h>
|
||||
#include <lib/toolbox/hex.h>
|
||||
|
||||
#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++) {
|
||||
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);
|
||||
}
|
||||
|
||||
@ -236,6 +240,14 @@ bool nfc_device_parse_bank_card_string(NfcDevice* dev, string_t bank_card_string
|
||||
break;
|
||||
}
|
||||
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);
|
||||
|
||||
return parsed;
|
||||
|
||||
@ -42,7 +42,7 @@ typedef struct {
|
||||
uint16_t aid_len;
|
||||
uint8_t number[8];
|
||||
uint8_t exp_mon;
|
||||
uint16_t exp_year;
|
||||
uint8_t exp_year;
|
||||
char cardholder[32];
|
||||
} NfcEmvData;
|
||||
|
||||
|
||||
@ -15,9 +15,7 @@ NfcWorker* nfc_worker_alloc() {
|
||||
nfc_worker->callback = NULL;
|
||||
nfc_worker->context = NULL;
|
||||
// Initialize rfal
|
||||
nfc_worker->error = api_hal_nfc_init();
|
||||
if(nfc_worker->error == ERR_NONE) {
|
||||
api_hal_nfc_start_sleep();
|
||||
if(!api_hal_nfc_is_busy()) {
|
||||
nfc_worker_change_state(nfc_worker, NfcWorkerStateReady);
|
||||
} else {
|
||||
nfc_worker_change_state(nfc_worker, NfcWorkerStateBroken);
|
||||
@ -35,10 +33,6 @@ NfcWorkerState nfc_worker_get_state(NfcWorker* nfc_worker) {
|
||||
return nfc_worker->state;
|
||||
}
|
||||
|
||||
ReturnCode nfc_worker_get_error(NfcWorker* nfc_worker) {
|
||||
return nfc_worker->error;
|
||||
}
|
||||
|
||||
void nfc_worker_start(
|
||||
NfcWorker* nfc_worker,
|
||||
NfcWorkerState state,
|
||||
@ -87,8 +81,8 @@ void nfc_worker_task(void* context) {
|
||||
nfc_worker_read_emv_app(nfc_worker);
|
||||
} else if(nfc_worker->state == NfcWorkerStateReadEMV) {
|
||||
nfc_worker_read_emv(nfc_worker);
|
||||
} else if(nfc_worker->state == NfcWorkerStateEmulateEMV) {
|
||||
nfc_worker_emulate_emv(nfc_worker);
|
||||
} else if(nfc_worker->state == NfcWorkerStateEmulateApdu) {
|
||||
nfc_worker_emulate_apdu(nfc_worker);
|
||||
} else if(nfc_worker->state == NfcWorkerStateReadMifareUl) {
|
||||
nfc_worker_read_mifare_ul(nfc_worker);
|
||||
} else if(nfc_worker->state == NfcWorkerStateEmulateMifareUl) {
|
||||
@ -330,6 +324,10 @@ void nfc_worker_read_emv(NfcWorker* nfc_worker) {
|
||||
result->emv_data.number,
|
||||
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
|
||||
if(nfc_worker->callback) {
|
||||
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;
|
||||
uint8_t tx_buff[255] = {};
|
||||
uint16_t tx_len = 0;
|
||||
@ -368,9 +366,47 @@ void nfc_worker_emulate_emv(NfcWorker* nfc_worker) {
|
||||
.device = NfcDeviceNfca,
|
||||
.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) {
|
||||
if(api_hal_nfc_listen(params.uid, params.uid_len, params.atqa, params.sak, 100)) {
|
||||
while(nfc_worker->state == NfcWorkerStateEmulateApdu) {
|
||||
if(api_hal_nfc_listen(params.uid, params.uid_len, params.atqa, params.sak, 300)) {
|
||||
FURI_LOG_I(NFC_WORKER_TAG, "POS terminal detected");
|
||||
// Read data from POS terminal
|
||||
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);
|
||||
err = api_hal_nfc_data_exchange(tx_buff, tx_len, &rx_buff, &rx_len, false);
|
||||
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();
|
||||
} else {
|
||||
|
||||
@ -14,7 +14,7 @@ typedef enum {
|
||||
NfcWorkerStateEmulate,
|
||||
NfcWorkerStateReadEMVApp,
|
||||
NfcWorkerStateReadEMV,
|
||||
NfcWorkerStateEmulateEMV,
|
||||
NfcWorkerStateEmulateApdu,
|
||||
NfcWorkerStateField,
|
||||
NfcWorkerStateReadMifareUl,
|
||||
NfcWorkerStateEmulateMifareUl,
|
||||
@ -28,8 +28,6 @@ NfcWorker* nfc_worker_alloc();
|
||||
|
||||
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_start(
|
||||
|
||||
@ -26,7 +26,6 @@ struct NfcWorker {
|
||||
void* context;
|
||||
|
||||
NfcWorkerState state;
|
||||
ReturnCode error;
|
||||
};
|
||||
|
||||
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_emulate_emv(NfcWorker* nfc_worker);
|
||||
void nfc_worker_emulate_apdu(NfcWorker* nfc_worker);
|
||||
|
||||
void nfc_worker_detect(NfcWorker* nfc_worker);
|
||||
|
||||
|
||||
@ -70,8 +70,9 @@ const bool nfc_scene_card_menu_on_event(void* context, SceneManagerEvent event)
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName);
|
||||
return true;
|
||||
}
|
||||
} else if(event.type == SceneManagerEventTypeNavigation) {
|
||||
return scene_manager_search_previous_scene(nfc->scene_manager, NfcSceneStart);
|
||||
} else if(event.type == SceneManagerEventTypeBack) {
|
||||
return scene_manager_search_and_switch_to_previous_scene(
|
||||
nfc->scene_manager, NfcSceneStart);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
@ -25,3 +25,4 @@ ADD_SCENE(nfc, delete_success, DeleteSuccess)
|
||||
ADD_SCENE(nfc, run_emv_app_confirm, RunEmvAppConfirm)
|
||||
ADD_SCENE(nfc, read_emv_data, ReadEmvData)
|
||||
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
@ -75,7 +75,8 @@ const bool nfc_scene_delete_on_event(void* context, SceneManagerEvent event) {
|
||||
if(nfc_device_delete(&nfc->dev)) {
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneDeleteSuccess);
|
||||
} else {
|
||||
scene_manager_search_previous_scene(nfc->scene_manager, NfcSceneStart);
|
||||
scene_manager_search_and_switch_to_previous_scene(
|
||||
nfc->scene_manager, NfcSceneStart);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
3
applications/nfc/scenes/nfc_scene_delete_success.c
Normal file → Executable file
@ -26,7 +26,8 @@ const bool nfc_scene_delete_success_on_event(void* context, SceneManagerEvent ev
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
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;
|
||||
|
||||
38
applications/nfc/scenes/nfc_scene_emulate_apdu_sequence.c
Normal 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);
|
||||
}
|
||||
@ -6,7 +6,7 @@ const void nfc_scene_file_select_on_enter(void* context) {
|
||||
if(nfc_file_select(&nfc->dev)) {
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneSavedMenu);
|
||||
} 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
@ -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);
|
||||
return true;
|
||||
}
|
||||
} else if(event.type == SceneManagerEventTypeNavigation) {
|
||||
return scene_manager_search_previous_scene(nfc->scene_manager, NfcSceneStart);
|
||||
} else if(event.type == SceneManagerEventTypeBack) {
|
||||
return scene_manager_search_and_switch_to_previous_scene(
|
||||
nfc->scene_manager, NfcSceneStart);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
@ -59,6 +59,12 @@ void nfc_scene_read_emv_data_success_on_enter(void* context) {
|
||||
char sak_str[16];
|
||||
snprintf(sak_str, sizeof(sak_str), "SAK: %02X", nfc_data->sak);
|
||||
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);
|
||||
}
|
||||
@ -68,15 +74,16 @@ const bool nfc_scene_read_emv_data_success_on_event(void* context, SceneManagerE
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == GuiButtonTypeLeft) {
|
||||
return scene_manager_search_previous_scene(
|
||||
return scene_manager_search_and_switch_to_previous_scene(
|
||||
nfc->scene_manager, NfcSceneReadEmvAppSuccess);
|
||||
} else if(event.event == GuiButtonTypeRight) {
|
||||
nfc->dev.format = NfcDeviceSaveFormatBankCard;
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName);
|
||||
return true;
|
||||
}
|
||||
} else if(event.type == SceneManagerEventTypeNavigation) {
|
||||
return scene_manager_search_previous_scene(nfc->scene_manager, NfcSceneReadEmvAppSuccess);
|
||||
} else if(event.type == SceneManagerEventTypeBack) {
|
||||
return scene_manager_search_and_switch_to_previous_scene(
|
||||
nfc->scene_manager, NfcSceneReadEmvAppSuccess);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
#include "../nfc_i.h"
|
||||
#include <lib/toolbox/random_name.h>
|
||||
|
||||
#define SCENE_SAVE_NAME_CUSTOM_EVENT (0UL)
|
||||
|
||||
@ -13,17 +14,21 @@ const void nfc_scene_save_name_on_enter(void* context) {
|
||||
|
||||
// Setup view
|
||||
TextInput* text_input = nfc->text_input;
|
||||
if(nfc->dev.dev_name) {
|
||||
nfc_device_delete(&nfc->dev);
|
||||
bool dev_name_empty = false;
|
||||
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_result_callback(
|
||||
text_input,
|
||||
nfc_scene_save_name_text_input_callback,
|
||||
nfc,
|
||||
nfc->text_store,
|
||||
sizeof(nfc->text_store));
|
||||
NFC_DEV_NAME_MAX_LEN,
|
||||
dev_name_empty);
|
||||
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.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));
|
||||
if(nfc_device_save(&nfc->dev, nfc->text_store)) {
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveSuccess);
|
||||
return true;
|
||||
} 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
|
||||
text_input_set_header_text(nfc->text_input, NULL);
|
||||
text_input_set_result_callback(nfc->text_input, NULL, NULL, NULL, 0, false);
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
Nfc* nfc = (Nfc*)context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
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) {
|
||||
|
||||
@ -5,6 +5,7 @@ enum SubmenuIndex {
|
||||
SubmenuIndexRunScript,
|
||||
SubmenuIndexSaved,
|
||||
SubmenuIndexAddManualy,
|
||||
SubmenuIndexDebug,
|
||||
};
|
||||
|
||||
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_add_item(
|
||||
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, 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) {
|
||||
Nfc* nfc = (Nfc*)context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == SubmenuIndexRead) {
|
||||
scene_manager_set_scene_state(nfc->scene_manager, NfcSceneStart, SubmenuIndexRead);
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneReadCard);
|
||||
return true;
|
||||
consumed = true;
|
||||
} else if(event.event == SubmenuIndexRunScript) {
|
||||
scene_manager_set_scene_state(
|
||||
nfc->scene_manager, NfcSceneStart, SubmenuIndexRunScript);
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneScriptsMenu);
|
||||
return true;
|
||||
consumed = true;
|
||||
} else if(event.event == SubmenuIndexSaved) {
|
||||
scene_manager_set_scene_state(nfc->scene_manager, NfcSceneStart, SubmenuIndexSaved);
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneFileSelect);
|
||||
return true;
|
||||
consumed = true;
|
||||
} else if(event.event == SubmenuIndexAddManualy) {
|
||||
scene_manager_set_scene_state(
|
||||
nfc->scene_manager, NfcSceneStart, SubmenuIndexAddManualy);
|
||||
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) {
|
||||
|
||||
@ -49,10 +49,10 @@ void bank_card_set_number(BankCard* bank_card, uint8_t* number) {
|
||||
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);
|
||||
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(
|
||||
bank_card->widget, 122, 54, AlignRight, AlignBottom, FontSecondary, exp_date_str);
|
||||
}
|
||||
|
||||
@ -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_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);
|
||||
|
||||
@ -74,7 +74,7 @@ uint8_t float_value_index(const float value, const float values[], uint8_t value
|
||||
const float epsilon = 0.01f;
|
||||
float last_value = values[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)) {
|
||||
index = i;
|
||||
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) {
|
||||
float last_value = values[0];
|
||||
int64_t last_value = INT64_MIN;
|
||||
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])) {
|
||||
index = i;
|
||||
break;
|
||||
|
||||
33
applications/power-observer/power-observer.c
Normal 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;
|
||||
}
|
||||
@ -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) {
|
||||
cli_add_command(cli, "poweroff", CliCommandFlagParallelSafe, power_cli_poweroff, 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, "power_info", CliCommandFlagParallelSafe, power_cli_info, power);
|
||||
cli_add_command(cli, "power_otg", CliCommandFlagParallelSafe, power_cli_otg, power);
|
||||
cli_add_command(cli, "power_ext", CliCommandFlagParallelSafe, power_cli_ext, power);
|
||||
}
|
||||
|
||||
9
applications/storage-settings/scenes/storage-settings-scene-ejected.c
Normal file → Executable file
@ -40,12 +40,13 @@ bool storage_settings_scene_unmounted_on_event(void* context, SceneManagerEvent
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
switch(event.event) {
|
||||
case DialogExResultLeft:
|
||||
consumed =
|
||||
scene_manager_search_previous_scene(app->scene_manager, StorageSettingsStart);
|
||||
consumed = scene_manager_search_and_switch_to_previous_scene(
|
||||
app->scene_manager, StorageSettingsStart);
|
||||
break;
|
||||
}
|
||||
} else if(event.type == SceneManagerEventTypeNavigation) {
|
||||
consumed = scene_manager_search_previous_scene(app->scene_manager, StorageSettingsStart);
|
||||
} else if(event.type == SceneManagerEventTypeBack) {
|
||||
consumed = scene_manager_search_and_switch_to_previous_scene(
|
||||
app->scene_manager, StorageSettingsStart);
|
||||
}
|
||||
|
||||
return consumed;
|
||||
|
||||
9
applications/storage-settings/scenes/storage-settings-scene-formatting.c
Normal file → Executable file
@ -60,12 +60,13 @@ bool storage_settings_scene_formatting_on_event(void* context, SceneManagerEvent
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
switch(event.event) {
|
||||
case DialogExResultLeft:
|
||||
consumed =
|
||||
scene_manager_search_previous_scene(app->scene_manager, StorageSettingsStart);
|
||||
consumed = scene_manager_search_and_switch_to_previous_scene(
|
||||
app->scene_manager, StorageSettingsStart);
|
||||
break;
|
||||
}
|
||||
} else if(event.type == SceneManagerEventTypeNavigation) {
|
||||
consumed = scene_manager_search_previous_scene(app->scene_manager, StorageSettingsStart);
|
||||
} else if(event.type == SceneManagerEventTypeBack) {
|
||||
consumed = scene_manager_search_and_switch_to_previous_scene(
|
||||
app->scene_manager, StorageSettingsStart);
|
||||
}
|
||||
|
||||
return consumed;
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
static bool storage_settings_navigation_event_callback(void* context) {
|
||||
static bool storage_settings_back_event_callback(void* context) {
|
||||
furi_assert(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() {
|
||||
@ -29,7 +29,7 @@ static StorageSettings* storage_settings_alloc() {
|
||||
view_dispatcher_set_custom_event_callback(
|
||||
app->view_dispatcher, storage_settings_custom_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);
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
#include <furi.h>
|
||||
#include <cli/cli.h>
|
||||
#include <args.h>
|
||||
#include <lib/toolbox/args.h>
|
||||
#include <storage/storage.h>
|
||||
#include <storage/storage-sd-api.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");
|
||||
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("\tmkdir\t - creates a new directory\r\n");
|
||||
};
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
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) {
|
||||
string_t cmd;
|
||||
string_t path;
|
||||
@ -342,6 +354,11 @@ void storage_cli(Cli* cli, string_t args, void* context) {
|
||||
break;
|
||||
}
|
||||
|
||||
if(string_cmp_str(cmd, "mkdir") == 0) {
|
||||
storage_cli_mkdir(cli, path);
|
||||
break;
|
||||
}
|
||||
|
||||
storage_cli_print_usage();
|
||||
} while(false);
|
||||
|
||||
|
||||
@ -10,67 +10,67 @@ extern const Icon A_MDWRB_32x32;
|
||||
extern const Icon A_MDWR_32x32;
|
||||
extern const Icon A_WatchingTV_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_ibutt_10px;
|
||||
extern const Icon I_ir_10px;
|
||||
extern const Icon I_Nfc_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_ButtonCenter_7x7;
|
||||
extern const Icon I_ButtonLeftSmall_3x5;
|
||||
extern const Icon I_ButtonLeft_4x7;
|
||||
extern const Icon I_ble_10px;
|
||||
extern const Icon I_125_10px;
|
||||
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_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_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_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_BigBurger_24x24;
|
||||
extern const Icon I_FX_Bang_32x6;
|
||||
extern const Icon I_FX_SittingB_40x27;
|
||||
extern const Icon I_DoorLeft_70x55;
|
||||
extern const Icon I_DolphinFirstStart3_57x48;
|
||||
extern const Icon I_PassportBottom_128x17;
|
||||
extern const Icon I_DoorLeft_8x56;
|
||||
extern const Icon I_DoorLocked_10x56;
|
||||
extern const Icon I_DoorRight_70x55;
|
||||
extern const Icon I_DoorRight_8x56;
|
||||
extern const Icon I_LockPopup_100x49;
|
||||
extern const Icon I_PassportBottom_128x17;
|
||||
extern const Icon I_DoorLeft_70x55;
|
||||
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_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_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_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_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_KeyBackspaceSelected_16x9;
|
||||
extern const Icon I_KeyBackspace_16x9;
|
||||
extern const Icon I_KeySaveSelected_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_Bluetooth_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_U2F_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_passport_bad1_43x45;
|
||||
extern const Icon I_passport_bad2_43x45;
|
||||
extern const Icon I_passport_bad3_43x45;
|
||||
extern const Icon I_EMV_Chip_14x11;
|
||||
extern const Icon I_passport_happy1_43x45;
|
||||
extern const Icon I_passport_happy2_43x45;
|
||||
extern const Icon I_passport_happy3_43x45;
|
||||
extern const Icon I_passport_okay1_43x45;
|
||||
extern const Icon I_passport_bad3_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_BatteryBody_52x28;
|
||||
extern const Icon I_Battery_16x16;
|
||||
extern const Icon I_passport_bad1_43x45;
|
||||
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_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_FaceNormal_29x14;
|
||||
extern const Icon I_Health_16x16;
|
||||
extern const Icon I_Temperature_16x16;
|
||||
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_Battery_16x16;
|
||||
extern const Icon I_FaceConfused_29x14;
|
||||
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_Home_painting_17x20;
|
||||
extern const Icon I_PC_22x29;
|
||||
extern const Icon I_Sofa_40x13;
|
||||
extern const Icon I_TV_20x20;
|
||||
extern const Icon I_TV_20x24;
|
||||
extern const Icon I_WalkL1_32x32;
|
||||
extern const Icon I_WalkL2_32x32;
|
||||
extern const Icon I_WalkLB1_32x32;
|
||||
extern const Icon I_WalkLB2_32x32;
|
||||
extern const Icon I_WalkR1_32x32;
|
||||
extern const Icon I_WalkR2_32x32;
|
||||
extern const Icon I_WalkRB1_32x32;
|
||||
extern const Icon I_WalkRB2_32x32;
|
||||
extern const Icon I_Background_128x11;
|
||||
extern const Icon I_Background_128x8;
|
||||
extern const Icon I_SDError_43x35;
|
||||
extern const Icon I_Console_74x67_4;
|
||||
extern const Icon I_Console_74x67_5;
|
||||
extern const Icon I_Console_74x67_7;
|
||||
extern const Icon I_Console_74x67_6;
|
||||
extern const Icon I_Console_74x67_2;
|
||||
extern const Icon I_Console_74x67_3;
|
||||
extern const Icon I_Console_74x67_1;
|
||||
extern const Icon I_Console_74x67_0;
|
||||
extern const Icon I_Console_74x67_8;
|
||||
extern const Icon I_food8_61x98;
|
||||
extern const Icon I_food5_61x98;
|
||||
extern const Icon I_food3_61x98;
|
||||
extern const Icon I_food9_61x98;
|
||||
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_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_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_SDcardFail_11x8;
|
||||
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_DolphinNice_96x59;
|
||||
extern const Icon I_DolphinWait_61x59;
|
||||
extern const Icon I_DolphinExcited_64x63;
|
||||
extern const Icon I_iButtonDolphinSuccess_109x60;
|
||||
extern const Icon I_iButtonDolphinVerySuccess_108x52;
|
||||
extern const Icon I_iButtonKey_49x44;
|
||||
extern const Icon I_DolphinNice_96x59;
|
||||
extern const Icon I_DolphinWait_61x59;
|
||||
|
||||
|
Before Width: | Height: | Size: 522 B |
BIN
assets/icons/Dolphin/DolphinFirstStart5_54x49.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 520 B After Width: | Height: | Size: 1.3 KiB |
BIN
assets/icons/Scenes/Console_74x67/Console_74x67_0.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
assets/icons/Scenes/Console_74x67/Console_74x67_1.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
assets/icons/Scenes/Console_74x67/Console_74x67_2.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
assets/icons/Scenes/Console_74x67/Console_74x67_3.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
assets/icons/Scenes/Console_74x67/Console_74x67_4.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
assets/icons/Scenes/Console_74x67/Console_74x67_5.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
assets/icons/Scenes/Console_74x67/Console_74x67_6.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
assets/icons/Scenes/Console_74x67/Console_74x67_7.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
assets/icons/Scenes/Console_74x67/Console_74x67_8.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
assets/icons/Scenes/Food/food10_61x98.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
assets/icons/Scenes/Food/food11_61x98.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
assets/icons/Scenes/Food/food12_61x98.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
assets/icons/Scenes/Food/food1_61x98.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
assets/icons/Scenes/Food/food2_61x98.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
assets/icons/Scenes/Food/food3_61x98.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |