Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
cc6a3d7
SimpleArray attached to FelicaData
zinongli Jul 28, 2025
5048614
tx rx done. response parsing done (in log)
zinongli Jul 29, 2025
4bf5b70
dynamic vector as buffer. rendering begin
zinongli Jul 29, 2025
a643849
On screen render for directory tree
zinongli Jul 29, 2025
2ac690f
flags in render to indicate is_public_readable
zinongli Jul 29, 2025
fbdba17
beautify render flags
zinongli Jul 29, 2025
70bd40b
format
zinongli Jul 29, 2025
14f2136
offload dynamic vector into individual files
zinongli Jul 29, 2025
e46dadb
saving. exposed dir tree writing for double use
zinongli Jul 29, 2025
7207d14
save: additional formatting
zinongli Jul 29, 2025
bb9411a
save: clean up and some additional notes
zinongli Jul 29, 2025
21e922b
load done
zinongli Jul 29, 2025
0b9b425
delete unnecessary debug log
zinongli Jul 29, 2025
1a4e658
Load: safer way to handle backward compatibility
zinongli Jul 29, 2025
20d87ff
format
zinongli Jul 30, 2025
6f5512d
handle block reading according to IC type
zinongli Jul 30, 2025
57a4872
read block content works. rendering begin
zinongli Jul 30, 2025
d0ca21c
Render Refactor: dir & dump view from submenu
zinongli Jul 30, 2025
ee5056e
Render: show IC type name
zinongli Jul 30, 2025
ee3f7bf
IC parsing function cleanup
zinongli Jul 30, 2025
84e42ea
Revert "IC parsing function cleanup"
zinongli Jul 30, 2025
b0ff1a2
Load: Standard dump. Fully backward compatible
zinongli Jul 30, 2025
85d1911
format
zinongli Jul 30, 2025
6a1ed0b
sync API version
zinongli Jul 30, 2025
c95fdbe
Merge pull request #1 from zinongli/felica_unencrypted_block_dump
zinongli Jul 30, 2025
94cf452
format saved file
zinongli Jul 30, 2025
b7e7f28
delete unused variable
zinongli Jul 30, 2025
035cf1b
clean ups
zinongli Jul 30, 2025
1b97dac
IC type addition
zinongli Jul 31, 2025
d5cffa9
correction
zinongli Jul 31, 2025
f83f15c
beautify attribute parsing
zinongli Jul 31, 2025
de6c33c
correction
zinongli Jul 31, 2025
3538ac1
Lite save: delete extra line
zinongli Aug 1, 2025
7f28dd5
correction: FeliCa link in Lite-S mode
zinongli Aug 2, 2025
a541cd8
format
zinongli Aug 2, 2025
2ef7fca
Save: simplify printing
zinongli Aug 2, 2025
9f3e58c
update IC type parsing
zinongli Aug 4, 2025
037e5d5
conform to api standard: const resp ptr to ptr
zinongli Aug 13, 2025
116ce1b
disambiguate workflow type vs ic type
zinongli Aug 13, 2025
145aad5
beautify on device render
zinongli Sep 24, 2025
f4b61cb
Merge branch 'dev' into felica_service_dump
zinongli Sep 24, 2025
a054125
Merge branch 'dev' into felica_service_dump
zinongli Sep 25, 2025
d5a2825
reject dynamic_vector, embrace m-array
zinongli Sep 25, 2025
2aa19d8
Merge branch 'dev' into felica_service_dump
zinongli Sep 25, 2025
0f64aa2
lint
zinongli Sep 25, 2025
6f00312
Merge branch 'felica_service_dump' of https://github.com/zinongli/fli…
zinongli Sep 25, 2025
42877ac
use full variable name
zinongli Sep 25, 2025
fb73cf8
Merge remote-tracking branch 'origin/dev' into felica_service_dump
hedger Sep 29, 2025
9076d14
partial fix: poller context's data proper init
zinongli Sep 30, 2025
bdb7de9
edit unit test dump IC code
zinongli Sep 30, 2025
3efbab4
Merge branch 'dev' into felica_service_dump
zinongli Sep 30, 2025
e6dc353
unit test felica dump PMm correction
zinongli Sep 30, 2025
01becd2
Merge branch 'dev' into felica_service_dump
hedger Oct 1, 2025
564a193
Fixes for static analysis warnings
hedger Oct 1, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ UID: 29 9F FA 53 AB 75 87 6E
# FeliCa specific data
Data format version: 1
Manufacture id: 29 9F FA 53 AB 75 87 6E
Manufacture parameter: 57 4E 10 2A 94 16 BC 8E
Manufacture parameter: 00 F1 00 00 00 01 43 00
Blocks total: 28
Blocks read: 28
Block 0: 00 00 DE AD BE AF 00 00 00 00 00 00 00 00 DE AD BE AF
Expand Down
13 changes: 2 additions & 11 deletions applications/main/nfc/helpers/protocol_support/felica/felica.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,17 +39,8 @@ static bool nfc_scene_info_on_event_felica(NfcApp* instance, SceneManagerEvent e
}

static void nfc_scene_more_info_on_enter_felica(NfcApp* instance) {
const NfcDevice* device = instance->nfc_device;
const FelicaData* data = nfc_device_get_data(device, NfcProtocolFelica);

FuriString* temp_str = furi_string_alloc();

nfc_render_felica_dump(data, temp_str);

widget_add_text_scroll_element(
instance->widget, 0, 0, 128, 64, furi_string_get_cstr(temp_str));

furi_string_free(temp_str);
// Jump to advanced scene right away
scene_manager_next_scene(instance->scene_manager, NfcSceneFelicaMoreInfo);
}

static NfcCommand nfc_scene_read_poller_callback_felica(NfcGenericEvent event, void* context) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,16 @@ void nfc_render_felica_blocks_count(
const FelicaData* data,
FuriString* str,
bool render_auth_notification) {
furi_string_cat_printf(str, "\nBlocks Read: %u/%u", data->blocks_read, data->blocks_total);
if(render_auth_notification && data->blocks_read != data->blocks_total) {
furi_string_cat_printf(str, "\nAuth-protected blocks!");
if(data->workflow_type == FelicaLite) {
furi_string_cat_printf(str, "Blocks: %u\n", data->blocks_total);

furi_string_cat_printf(str, "\nBlocks Read: %u/%u", data->blocks_read, data->blocks_total);
if(render_auth_notification && data->blocks_read != data->blocks_total) {
furi_string_cat_printf(str, "\nAuth-protected blocks!");
}
} else if(data->workflow_type == FelicaStandard) {
furi_string_cat_printf(
str, "Public blocks Read: %lu", simple_array_get_count(data->public_blocks));
}
}

Expand All @@ -32,6 +39,11 @@ void nfc_render_felica_info(
furi_string_cat_printf(str, "Tech: JIS X 6319-4,\nISO 18092 [NFC-F]\n");
}

FuriString* ic_type_str = furi_string_alloc();
felica_get_ic_name(data, ic_type_str);
furi_string_cat_printf(str, "IC Type:\n%s\n", furi_string_get_cstr(ic_type_str));
furi_string_free(ic_type_str);

nfc_render_felica_idm(data, format_type, str);

if(format_type == NfcProtocolFormatTypeFull) {
Expand All @@ -40,6 +52,14 @@ void nfc_render_felica_info(
furi_string_cat_printf(str, "%02X ", data->pmm.data[i]);
}
}

furi_string_cat_printf(str, "\n");
furi_string_cat_printf(
str,
"Services found: %lu \nAreas found: %lu\n",
simple_array_get_count(data->services),
simple_array_get_count(data->areas));

nfc_render_felica_blocks_count(data, str, true);
}

Expand All @@ -59,13 +79,18 @@ static void nfc_render_felica_block_name(

static void nfc_render_felica_block_data(const FelicaBlock* block, FuriString* str) {
furi_string_cat_printf(str, "\nSF1=%02X; SF2=%02X\n", block->SF1, block->SF2);
for(size_t j = 0; j < FELICA_DATA_BLOCK_SIZE; j++) {
if((j != 0) && (j % 8 == 0)) furi_string_cat_printf(str, "\n");
furi_string_cat_printf(str, "%02X ", block->data[j]);
for(size_t i = 0; i < FELICA_DATA_BLOCK_SIZE; i += 2) {
furi_string_cat_printf(str, "%02X%02X ", block->data[i], block->data[i + 1]);
}
furi_string_cat_printf(str, "\n");
}

static void nfc_render_felica_block_data_simple(const FelicaBlock* block, FuriString* str) {
for(size_t i = 0; i < FELICA_DATA_BLOCK_SIZE; i += 2) {
furi_string_cat_printf(str, "%02X%02X ", block->data[i], block->data[i + 1]);
}
}

static void nfc_render_felica_block(
const FelicaBlock* block,
FuriString* str,
Expand All @@ -76,8 +101,13 @@ static void nfc_render_felica_block(
nfc_render_felica_block_data(block, str);
}

void nfc_render_felica_dump(const FelicaData* data, FuriString* str) {
void nfc_more_info_render_felica_lite_dump(const FelicaData* data, FuriString* str) {
FuriString* name = furi_string_alloc();

furi_string_cat_printf(str, "\e#Blocks read:\n");

furi_string_cat_printf(str, "Blocks: %u\n", data->blocks_total);

for(size_t i = 0; i < 14; i++) {
furi_string_printf(name, "S_PAD%d", i);
uint8_t suf_cnt = 18;
Expand Down Expand Up @@ -105,3 +135,70 @@ void nfc_render_felica_dump(const FelicaData* data, FuriString* str) {
nfc_render_felica_block(&data->data.fs.state, str, "STATE", 20, 21);
nfc_render_felica_block(&data->data.fs.crc_check, str, "CRC_CHCK", 15, 17);
}

void nfc_more_info_render_felica_dir(const FelicaData* data, FuriString* str) {
const size_t area_count = simple_array_get_count(data->areas);
const size_t service_count = simple_array_get_count(data->services);

furi_string_cat_printf(str, "\e#Directory Tree:\n");

if(area_count == 0 || service_count == 0) {
furi_string_cat_printf(str, "No services or areas found.\n");
} else {
furi_string_cat_printf(
str, "%zu areas found.\n%zu services found.\n\n", area_count, service_count);
furi_string_cat_printf(
str, "::: ... are readable services\n||| ... are locked services\n");
}
felica_write_directory_tree(data, str);
}

void nfc_more_info_render_felica_blocks(
const FelicaData* data,
FuriString* str,
const uint16_t service_code_key) {
furi_string_cat_printf(str, "\n");
if(data->workflow_type == FelicaLite) {
furi_string_cat_printf(str, "Blocks: %u\n", data->blocks_total);
FuriString* name = furi_string_alloc();

for(size_t i = 0; i < 14; i++) {
furi_string_printf(name, "S_PAD%d", i);
uint8_t suf_cnt = 18;
if(i == 1) {
suf_cnt = 19;
} else if((i == 10) || (i == 12) || (i == 13)) {
suf_cnt = 16;
}
nfc_render_felica_block(
&data->data.fs.spad[i], str, furi_string_get_cstr(name), 20, suf_cnt);
}
furi_string_free(name);
nfc_render_felica_block(&data->data.fs.reg, str, "REG", 23, 23);
nfc_render_felica_block(&data->data.fs.rc, str, "RC", 25, 25);
nfc_render_felica_block(&data->data.fs.mac, str, "MAC", 23, 23);
nfc_render_felica_block(&data->data.fs.id, str, "ID", 25, 25);
nfc_render_felica_block(&data->data.fs.d_id, str, "D_ID", 22, 24);
nfc_render_felica_block(&data->data.fs.ser_c, str, "SER_C", 20, 21);
nfc_render_felica_block(&data->data.fs.sys_c, str, "SYS_C", 20, 21);
nfc_render_felica_block(&data->data.fs.ckv, str, "CKV", 23, 23);
nfc_render_felica_block(&data->data.fs.ck, str, "CK", 25, 25);
nfc_render_felica_block(&data->data.fs.mc, str, "MC", 25, 24);
nfc_render_felica_block(&data->data.fs.wcnt, str, "WCNT", 22, 20);
nfc_render_felica_block(&data->data.fs.mac_a, str, "MAC_A", 20, 20);
nfc_render_felica_block(&data->data.fs.state, str, "STATE", 20, 21);
nfc_render_felica_block(&data->data.fs.crc_check, str, "CRC_CHCK", 15, 17);

} else if(data->workflow_type == FelicaStandard) {
uint32_t public_blocks_count = simple_array_get_count(data->public_blocks);
for(size_t i = 0; i < public_blocks_count; i++) {
FelicaPublicBlock* public_block = simple_array_get(data->public_blocks, i);
if(public_block->service_code != service_code_key) {
continue; // Skip blocks not matching the requested service code
}
furi_string_cat_printf(str, "-----Block 0x%02X-----\n", public_block->block_idx);
nfc_render_felica_block_data_simple(&public_block->block, str);
furi_string_cat_printf(str, "\n");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,16 @@ void nfc_render_felica_info(
NfcProtocolFormatType format_type,
FuriString* str);

void nfc_render_felica_dump(const FelicaData* data, FuriString* str);
void nfc_more_info_render_felica_lite_dump(const FelicaData* data, FuriString* str);

void nfc_render_felica_idm(
const FelicaData* data,
NfcProtocolFormatType format_type,
FuriString* str);

void nfc_more_info_render_felica_dir(const FelicaData* data, FuriString* str);

void nfc_more_info_render_felica_blocks(
const FelicaData* data,
FuriString* str,
const uint16_t service_code_key);
2 changes: 2 additions & 0 deletions applications/main/nfc/scenes/nfc_scene_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,6 @@ ADD_SCENE(nfc, slix_key_input, SlixKeyInput)
ADD_SCENE(nfc, slix_unlock, SlixUnlock)
ADD_SCENE(nfc, slix_unlock_success, SlixUnlockSuccess)

ADD_SCENE(nfc, felica_more_info, FelicaMoreInfo)

ADD_SCENE(nfc, generate_info, GenerateInfo)
151 changes: 151 additions & 0 deletions applications/main/nfc/scenes/nfc_scene_felica_more_info.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
#include "../nfc_app_i.h"

#include "../helpers/protocol_support/nfc_protocol_support_gui_common.h"
#include "../helpers/protocol_support/felica/felica_render.h"

enum {
FelicaMoreInfoStateMenu,
FelicaMoreInfoStateItem, // MUST be last, states >= this correspond with submenu index
};

enum SubmenuIndex {
SubmenuIndexDirectory,
SubmenuIndexDynamic, // dynamic indices start here
};

void nfc_scene_felica_more_info_on_enter(void* context) {
NfcApp* nfc = context;
Submenu* submenu = nfc->submenu;

const uint32_t state =
scene_manager_get_scene_state(nfc->scene_manager, NfcSceneFelicaMoreInfo);
const FelicaData* data = nfc_device_get_data(nfc->nfc_device, NfcProtocolFelica);

submenu_add_item(
submenu,
"Directory",
SubmenuIndexDirectory,
nfc_protocol_support_common_submenu_callback,
nfc);

FuriString* label = furi_string_alloc();

switch(data->workflow_type) {
case FelicaLite:
furi_string_printf(label, "All blocks");
submenu_add_item(
submenu,
furi_string_get_cstr(label),
SubmenuIndexDynamic,
nfc_protocol_support_common_submenu_callback,
nfc);
break;
case FelicaStandard:
for(uint32_t i = 0; i < simple_array_get_count(data->services); ++i) {
const FelicaService* service = simple_array_cget(data->services, i);
bool is_public = (service->attr & FELICA_SERVICE_ATTRIBUTE_UNAUTH_READ) == 1;
if(!is_public) {
continue;
}
furi_string_printf(label, "Readable serv %04X", service->code);
submenu_add_item(
submenu,
furi_string_get_cstr(label),
i + SubmenuIndexDynamic,
nfc_protocol_support_common_submenu_callback,
nfc);
}
break;
default:
break;
}

furi_string_free(label);

if(state >= FelicaMoreInfoStateItem) {
submenu_set_selected_item(
nfc->submenu, state - FelicaMoreInfoStateItem + SubmenuIndexDynamic);
scene_manager_set_scene_state(
nfc->scene_manager, NfcSceneFelicaMoreInfo, FelicaMoreInfoStateMenu);
}

view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu);
}

bool nfc_scene_felica_more_info_on_event(void* context, SceneManagerEvent event) {
NfcApp* nfc = context;
bool consumed = false;

const uint32_t state =
scene_manager_get_scene_state(nfc->scene_manager, NfcSceneFelicaMoreInfo);
const FelicaData* data = nfc_device_get_data(nfc->nfc_device, NfcProtocolFelica);

if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SubmenuIndexDirectory) {
FuriString* temp_str = furi_string_alloc();
nfc_more_info_render_felica_dir(data, temp_str);

widget_add_text_scroll_element(
nfc->widget, 0, 0, 128, 64, furi_string_get_cstr(temp_str));
furi_string_free(temp_str);

view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
scene_manager_set_scene_state(
nfc->scene_manager,
NfcSceneFelicaMoreInfo,
FelicaMoreInfoStateItem + SubmenuIndexDirectory);
consumed = true;
} else {
const uint16_t service_ind = event.event - 1; // offset the three enums above

text_box_reset(nfc->text_box);
furi_string_reset(nfc->text_box_store);

switch(data->workflow_type) {
case FelicaLite:
nfc_more_info_render_felica_lite_dump(data, nfc->text_box_store);
break;
case FelicaStandard:
const FelicaService* service = simple_array_cget(data->services, service_ind);
furi_string_cat_printf(nfc->text_box_store, "Service 0x%04X\n", service->code);
nfc_more_info_render_felica_blocks(data, nfc->text_box_store, service->code);
break;
default:
furi_string_set_str(nfc->text_box_store, "IC type not implemented yet");
break;
}
text_box_set_font(nfc->text_box, TextBoxFontHex);
text_box_set_text(nfc->text_box, furi_string_get_cstr(nfc->text_box_store));
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox);
scene_manager_set_scene_state(
nfc->scene_manager, NfcSceneFelicaMoreInfo, FelicaMoreInfoStateItem + event.event);
consumed = true;
}
} else if(event.type == SceneManagerEventTypeBack) {
if(state >= FelicaMoreInfoStateItem) {
widget_reset(nfc->widget);
text_box_reset(nfc->text_box);
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu);
scene_manager_set_scene_state(
nfc->scene_manager, NfcSceneFelicaMoreInfo, FelicaMoreInfoStateMenu);
} else {
widget_reset(nfc->widget);
text_box_reset(nfc->text_box);
// Return directly to the Info scene
scene_manager_search_and_switch_to_previous_scene(nfc->scene_manager, NfcSceneInfo);
}
consumed = true;
}

return consumed;
}

void nfc_scene_felica_more_info_on_exit(void* context) {
NfcApp* nfc = context;

// Clear views
widget_reset(nfc->widget);
text_box_reset(nfc->text_box);
furi_string_reset(nfc->text_box_store);
submenu_reset(nfc->submenu);
}
Loading
Loading