mirror of
https://github.com/DarkFlippers/unleashed-firmware.git
synced 2025-12-13 13:09:49 +04:00
Compare commits
104 Commits
un1-7c54fc
...
un1-3a767c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
107d574c02 | ||
|
|
84204a3233 | ||
|
|
48691b2296 | ||
|
|
3a767c9c02 | ||
|
|
1a4d928e5c | ||
|
|
a8858e38b5 | ||
|
|
120b5d7c90 | ||
|
|
b7a6d18186 | ||
|
|
8d8481b17f | ||
|
|
ca8217b692 | ||
|
|
361895c689 | ||
|
|
789230458b | ||
|
|
6f77f537e3 | ||
|
|
b6254227b9 | ||
|
|
8323877120 | ||
|
|
f99c1a8c0a | ||
|
|
c41555b579 | ||
|
|
97b27261d5 | ||
|
|
1853359d78 | ||
|
|
ed2c607dd3 | ||
|
|
bd54c2b342 | ||
|
|
53aa5c71a0 | ||
|
|
a3932cfa6d | ||
|
|
1d787e6da8 | ||
|
|
10b0a611cf | ||
|
|
f537ccfe14 | ||
|
|
d37dbb29bf | ||
|
|
0ee4573a65 | ||
|
|
311b60f815 | ||
|
|
9ff35c9fc3 | ||
|
|
f14874b0e0 | ||
|
|
6c7611c57e | ||
|
|
4d388f4bde | ||
|
|
613c729025 | ||
|
|
079cadaeda | ||
|
|
0c0fb57410 | ||
|
|
e7a5d19f9c | ||
|
|
8e9043003f | ||
|
|
b67aaad6d5 | ||
|
|
e4c6158d65 | ||
|
|
23217f4a6a | ||
|
|
4fcb90928c | ||
|
|
68da151320 | ||
|
|
f70ec8f70e | ||
|
|
5e2a90c6f1 | ||
|
|
5272b81ae6 | ||
|
|
782593eafc | ||
|
|
d76ba20652 | ||
|
|
611b7e15ed | ||
|
|
274f17ed5a | ||
|
|
f09c5889dd | ||
|
|
39f936ce12 | ||
|
|
1350dcaf63 | ||
|
|
4629aee29c | ||
|
|
ffa3ff5e7c | ||
|
|
bacc8fa669 | ||
|
|
9c748860bb | ||
|
|
79c4b617c1 | ||
|
|
194727515b | ||
|
|
c714a32ea5 | ||
|
|
689da15346 | ||
|
|
1a4a6d4625 | ||
|
|
99a7d06f71 | ||
|
|
db4976c501 | ||
|
|
fc62762ce4 | ||
|
|
a6597da4a5 | ||
|
|
ab5bcd71f5 | ||
|
|
eb28dc2e20 | ||
|
|
92db5e1afe | ||
|
|
885bb0c730 | ||
|
|
71f4bd0a7c | ||
|
|
66e361714f | ||
|
|
9bd6d956ca | ||
|
|
293d5f722d | ||
|
|
04f522487e | ||
|
|
ab4bb55d0f | ||
|
|
64edddeabf | ||
|
|
a0819df874 | ||
|
|
24265204b3 | ||
|
|
e828d15fe3 | ||
|
|
6c2cf68077 | ||
|
|
f6d38939c2 | ||
|
|
f9d3d6bf5c | ||
|
|
33176220f6 | ||
|
|
b31e4a36a8 | ||
|
|
ecbe42744d | ||
|
|
7abc49ea21 | ||
|
|
5b6f2cb80d | ||
|
|
1f68cf09eb | ||
|
|
273871013e | ||
|
|
ce7b943793 | ||
|
|
7e20df7e93 | ||
|
|
9ec0835012 | ||
|
|
432782344a | ||
|
|
64ee9dd674 | ||
|
|
803422c18e | ||
|
|
545dabadb7 | ||
|
|
73c28437d6 | ||
|
|
71c27de8cc | ||
|
|
9eb9a44ee4 | ||
|
|
3aed4de1b4 | ||
|
|
33fbf268b7 | ||
|
|
36784f95c8 | ||
|
|
be942ef7b2 |
@@ -36,6 +36,7 @@ steps:
|
||||
- mkdir -p sd-card/nfc/assets
|
||||
- mkdir -p sd-card/infrared/assets
|
||||
- mkdir -p sd-card/unirf
|
||||
- mkdir -p sd-card/rfidfuzzer
|
||||
- mkdir -p sd-card/badusb/layouts
|
||||
- cp assets/resources/badusb/layouts/* sd-card/badusb/layouts/
|
||||
- cp assets/resources/subghz/assets/dangerous_settings sd-card/subghz/assets/dangerous_settings
|
||||
@@ -44,9 +45,11 @@ steps:
|
||||
- cp assets/resources/nfc/assets/mf_classic_dict.nfc sd-card/nfc/assets/mf_classic_dict.nfc
|
||||
- cp assets/resources/infrared/assets/tv.ir sd-card/infrared/assets/tv.ir
|
||||
- cp assets/resources/infrared/assets/ac.ir sd-card/infrared/assets/ac.ir
|
||||
- cp assets/resources/infrared/assets/fans.ir sd-card/infrared/assets/fans.ir
|
||||
- cp assets/resources/infrared/assets/projectors.ir sd-card/infrared/assets/projectors.ir
|
||||
- cp assets/resources/infrared/assets/audio.ir sd-card/infrared/assets/audio.ir
|
||||
- cp assets/resources/unirf/unirf_map_example.txt sd-card/unirf/unirf_map_example.txt
|
||||
- cp assets/resources/rfidfuzzer/example_uids.txt sd-card/rfidfuzzer/example_uids.txt
|
||||
- cp assets/resources/Manifest sd-card/Manifest
|
||||
- zip -r artifacts-default/sd-card-${DRONE_TAG}.zip sd-card
|
||||
- rm -rf sd-card
|
||||
@@ -115,9 +118,7 @@ steps:
|
||||
|
||||
[-Install via Web Updater-](https://my.flipp.dev/?url=https://unleashedflip.com/builds/flipper-z-f7-update-${DRONE_TAG}.tgz&channel=dev-cfw&version=${DRONE_TAG})"
|
||||
document:
|
||||
- artifacts-default/flipper-z-f7-full-${DRONE_TAG}.dfu
|
||||
- artifacts-default/flipper-z-f7-update-${DRONE_TAG}.zip
|
||||
- artifacts-default/sd-card-${DRONE_TAG}.zip
|
||||
- artifacts-default/flipper-z-f7-update-${DRONE_TAG}.tgz
|
||||
|
||||
- name: "Send discord notification"
|
||||
image: appleboy/drone-discord
|
||||
|
||||
24
CHANGELOG.md
24
CHANGELOG.md
@@ -1,24 +1,12 @@
|
||||
### New changes
|
||||
* New universal remote for projectors
|
||||
* OFW: New LF-RFID subsystem (New protocols, Animal tags support)
|
||||
* Updated universal remote assets (by @Amec0e)
|
||||
* Renamed UniRF Remix -> Sub-GHz Remote
|
||||
* Replaced Hex/Dec converter with Multi Converter plugin [(by theisolinearchip)](https://github.com/theisolinearchip/flipperzero_stuff)
|
||||
* New update screen, readme pictures (by @Svaarich)
|
||||
* Fixed crash if Center button is pressed on the "update success" screen via screensharing
|
||||
* Temporary disabled one log call in picopass plugin to fix crash/freeze on Read screen
|
||||
* OFW: Picopass load/info/delete
|
||||
* OFW: SubGhz: add protocol Magellen
|
||||
* OFW: Fix mifare ultralight/ntag unlock
|
||||
* OFW: Dolphin level thresholds update
|
||||
* OFW: Add MFC 1/4K 4/7bUID to "Add Manually"
|
||||
* OFW: Other fixes and changes
|
||||
* WAV Player, Arkanoid, TicTacToe, Barcode generator - plugins enabled and included in releases again
|
||||
* Debug apps disabled in release build
|
||||
* WAV Player moved to Games menu
|
||||
* OFW: Lib: update LFS to v2.5.0, lower update free page limit
|
||||
|
||||
**Note: Prefer installing using web updater or by self update package, all needed assets will be installed**
|
||||
**Note: To avoid issues prefer installing using web updater or by self update package, all needed assets will be installed**
|
||||
|
||||
**Build naming has been changed - all same as before but `cg - codegrabber` changed to `un - unleashed`**
|
||||
|
||||
Self-update package (update from microSD) - `flipper-z-f7-update-(version).zip`
|
||||
Self-update package (update from microSD) - `flipper-z-f7-update-(version).zip` or `.tgz` for iOS mobile app
|
||||
|
||||
DFU for update using qFlipper - `flipper-z-f7-full-(version).dfu`
|
||||
|
||||
|
||||
21
ReadMe.md
21
ReadMe.md
@@ -4,10 +4,10 @@
|
||||
</a>
|
||||
</h3>
|
||||
|
||||
Welcome to Flipper Zero's Custom Firmware repo!
|
||||
### Welcome to Flipper Zero Unleashed Firmware repo!
|
||||
Our goal is to make any features possible in this device without any limitations!
|
||||
|
||||
Please help us implement emulation for all subghz dynamic (rolling code) protocols and static code brute-force app!
|
||||
Please help us implement emulation for all subghz dynamic (rolling code) protocols and static code brute-force plugin!
|
||||
|
||||
<br>
|
||||
|
||||
@@ -31,21 +31,20 @@ Our Discord Community:
|
||||
* Extra SubGHz frequencies + extra Mifare Classic keys
|
||||
* Picopass/iClass plugin included in releases
|
||||
* Recompiled IR TV Universal Remote for ALL buttons
|
||||
* Universal A/C and Audio(soundbars, etc.) remote
|
||||
* Universal remote for Projectors
|
||||
* Universal remote for Projectors, Fans, A/Cs and Audio(soundbars, etc.)
|
||||
* BadUSB keyboard layouts
|
||||
* Customizable Flipper name
|
||||
* Other small fixes and changes throughout
|
||||
|
||||
See changelog in releases for latest updates!
|
||||
|
||||
### Current modified and new SubGhz protocols list:
|
||||
### Current modified and new SubGHz protocols list:
|
||||
- HCS101
|
||||
- An-Motors
|
||||
- CAME Atomo
|
||||
- FAAC SLH (Spa) [if cloning existing remote - external seed calculation required]
|
||||
- BFT Mitto [if cloning existing remote - external seed calculation required]
|
||||
- Keeloq (+ proper manufacturer codes selection) [Not ALL systems supported yet!]
|
||||
- FAAC SLH (Spa) [External seed calculation required]
|
||||
- BFT Mitto [External seed calculation required]
|
||||
- Keeloq [Not ALL systems supported yet!]
|
||||
- Nice Flor S
|
||||
- Security+ v1 & v2
|
||||
- Star Line
|
||||
@@ -56,8 +55,6 @@ See changelog in releases for latest updates!
|
||||
* DOGE: `D6R6gYgBn5LwTNmPyvAQR6bZ9EtGgFCpvv`
|
||||
* LTC: `ltc1q3ex4ejkl0xpx3znwrmth4lyuadr5qgv8tmq8z9`
|
||||
|
||||
**Big thanks to all sponsors!**
|
||||
|
||||
### Community apps included:
|
||||
|
||||
- ESP8266 Deauther plugin [(by SequoiaSan)](https://github.com/SequoiaSan/FlipperZero-Wifi-ESP8266-Deauther-Module)
|
||||
@@ -68,8 +65,8 @@ See changelog in releases for latest updates!
|
||||
- GPIO: Sentry Safe plugin [(by H4ckd4ddy)](https://github.com/H4ckd4ddy/flipperzero-sentry-safe-plugin)
|
||||
- ESP32: WiFi Marauder companion plugin [(by 0xchocolate)](https://github.com/0xchocolate/flipperzero-firmware-with-wifi-marauder-companion)
|
||||
- NRF24: Sniffer & MouseJacker (with changes) [(by mothball187)](https://github.com/mothball187/flipperzero-nrf24/tree/main/mousejacker)
|
||||
- Simple Clock (fixed) !! New version WIP, wait for updates !! [(Original by CompaqDisc)](https://gist.github.com/CompaqDisc/4e329c501bd03c1e801849b81f48ea61)
|
||||
- UniversalRF Remix (with changes)(only RAW subghz files) [(by ESurge)(Original UniversalRF by jimilinuxguy)](https://github.com/ESurge/flipperzero-firmware-unirfremix)
|
||||
- Simple Clock (fixed) [(Original by CompaqDisc)](https://gist.github.com/CompaqDisc/4e329c501bd03c1e801849b81f48ea61)
|
||||
- UniversalRF Remix / Sub-GHz Remote [(by ESurge)](https://github.com/ESurge/flipperzero-firmware-unirfremix)[(updated and all protocol support added by darmiel & xMasterX)](https://github.com/darmiel/flipper-playlist/tree/feat/unirf-protocols)
|
||||
- Tetris (with fixes) [(by jeffplang)](https://github.com/jeffplang/flipperzero-firmware/tree/tetris_game/applications/tetris_game)
|
||||
- Spectrum Analyzer (with changes) [(by jolcese)](https://github.com/jolcese/flipperzero-firmware/tree/spectrum/applications/spectrum_analyzer) - [Ultra Narrow mode & scan channels non-consecutively](https://github.com/theY4Kman/flipperzero-firmware/commits?author=theY4Kman)
|
||||
- Arkanoid (with fixes) [(by gotnull)](https://github.com/gotnull/flipperzero-firmware-wPlugins)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#
|
||||
# Main Fipper Build System entry point
|
||||
# Main Flipper Build System entry point
|
||||
#
|
||||
# This file is evaluated by scons (the build system) every time fbt is invoked.
|
||||
# Scons constructs all referenced environments & their targets' dependency
|
||||
@@ -15,7 +15,7 @@ DefaultEnvironment(tools=[])
|
||||
# Progress(["OwO\r", "owo\r", "uwu\r", "owo\r"], interval=15)
|
||||
|
||||
|
||||
# This environment is created only for loading options & validating file/dir existance
|
||||
# This environment is created only for loading options & validating file/dir existence
|
||||
fbt_variables = SConscript("site_scons/commandline.scons")
|
||||
cmd_environment = Environment(tools=[], variables=fbt_variables)
|
||||
Help(fbt_variables.GenerateHelpText(cmd_environment))
|
||||
|
||||
0
applications/bad_usb/views/bad_usb_view.h
Executable file → Normal file
0
applications/bad_usb/views/bad_usb_view.h
Executable file → Normal file
0
applications/bt/bt_debug_app/views/bt_carrier_test.c
Executable file → Normal file
0
applications/bt/bt_debug_app/views/bt_carrier_test.c
Executable file → Normal file
0
applications/bt/bt_debug_app/views/bt_test.c
Executable file → Normal file
0
applications/bt/bt_debug_app/views/bt_test.c
Executable file → Normal file
0
applications/bt/bt_debug_app/views/bt_test.h
Executable file → Normal file
0
applications/bt/bt_debug_app/views/bt_test.h
Executable file → Normal file
0
applications/bt/bt_hid_app/views/bt_hid_keynote.c
Executable file → Normal file
0
applications/bt/bt_hid_app/views/bt_hid_keynote.c
Executable file → Normal file
0
applications/bt/bt_hid_app/views/bt_hid_media.c
Executable file → Normal file
0
applications/bt/bt_hid_app/views/bt_hid_media.c
Executable file → Normal file
0
applications/bt/bt_settings_app/bt_settings_app.c
Executable file → Normal file
0
applications/bt/bt_settings_app/bt_settings_app.c
Executable file → Normal file
0
applications/bt/bt_settings_app/bt_settings_app.h
Executable file → Normal file
0
applications/bt/bt_settings_app/bt_settings_app.h
Executable file → Normal file
0
applications/bt/bt_settings_app/scenes/bt_settings_scene_config.h
Executable file → Normal file
0
applications/bt/bt_settings_app/scenes/bt_settings_scene_config.h
Executable file → Normal file
0
applications/bt/bt_settings_app/scenes/bt_settings_scene_forget_dev_confirm.c
Executable file → Normal file
0
applications/bt/bt_settings_app/scenes/bt_settings_scene_forget_dev_confirm.c
Executable file → Normal file
0
applications/bt/bt_settings_app/scenes/bt_settings_scene_forget_dev_success.c
Executable file → Normal file
0
applications/bt/bt_settings_app/scenes/bt_settings_scene_forget_dev_success.c
Executable file → Normal file
0
applications/bt/bt_settings_app/scenes/bt_settings_scene_start.c
Executable file → Normal file
0
applications/bt/bt_settings_app/scenes/bt_settings_scene_start.c
Executable file → Normal file
@@ -281,6 +281,9 @@ void cli_command_free(Cli* cli, string_t args, void* context) {
|
||||
printf("Total heap size: %d\r\n", memmgr_get_total_heap());
|
||||
printf("Minimum heap size: %d\r\n", memmgr_get_minimum_free_heap());
|
||||
printf("Maximum heap block: %d\r\n", memmgr_heap_get_max_free_block());
|
||||
|
||||
printf("Pool free: %d\r\n", memmgr_pool_get_free());
|
||||
printf("Maximum pool block: %d\r\n", memmgr_pool_get_max_block());
|
||||
}
|
||||
|
||||
void cli_command_free_blocks(Cli* cli, string_t args, void* context) {
|
||||
|
||||
0
applications/cli/cli_i.h
Executable file → Normal file
0
applications/cli/cli_i.h
Executable file → Normal file
8
applications/flipfrid/LICENSE.md
Normal file
8
applications/flipfrid/LICENSE.md
Normal file
@@ -0,0 +1,8 @@
|
||||
/*
|
||||
* ----------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* @G4N4P4T1 wrote this file. As long as you retain this notice you
|
||||
* can do whatever you want with this stuff. If we meet some day, and you think
|
||||
* this stuff is worth it, you can buy me a beer in return.
|
||||
* ----------------------------------------------------------------------------
|
||||
*/
|
||||
21
applications/flipfrid/README.md
Normal file
21
applications/flipfrid/README.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# Flipfrid
|
||||
|
||||
Basic EM4100 Fuzzer
|
||||
|
||||
## Why
|
||||
|
||||
Flipfrid is a simple Rfid fuzzer using EM4100 protocol (125khz).
|
||||
Objective is to provide a simple to use fuzzer to test readers by emulating various cards.
|
||||
|
||||
EM4100 cards use a 1 byte customer id and 4 bytes card id.
|
||||
|
||||
## How
|
||||
|
||||
There is 4 modes :
|
||||
- Default key loop over 16 factory/default keys and emulate each one after one ;
|
||||
- BF customer id. just an iteration from 0X00 to 0XFF on the first byte ;
|
||||
- Load Dump file : Load an existing EM4100 dump generated by Flipperzero, select an index and bruteforce from 0X00 to 0XFF;
|
||||
- Uids list: loop over a text file (one uid per line)
|
||||
|
||||
TODO :
|
||||
- blank screen on back press
|
||||
10
applications/flipfrid/application.fam
Normal file
10
applications/flipfrid/application.fam
Normal file
@@ -0,0 +1,10 @@
|
||||
App(
|
||||
appid="flipfrid",
|
||||
name="RFID Fuzzer",
|
||||
apptype=FlipperAppType.PLUGIN,
|
||||
entry_point="flipfrid_start",
|
||||
cdefines=["APP_FLIP_FRID"],
|
||||
requires=["gui"],
|
||||
stack_size=1 * 1024,
|
||||
order=29,
|
||||
)
|
||||
258
applications/flipfrid/flipfrid.c
Normal file
258
applications/flipfrid/flipfrid.c
Normal file
@@ -0,0 +1,258 @@
|
||||
#include "flipfrid.h"
|
||||
|
||||
#include "scene/flipfrid_scene_entrypoint.h"
|
||||
#include "scene/flipfrid_scene_load_file.h"
|
||||
#include "scene/flipfrid_scene_select_field.h"
|
||||
#include "scene/flipfrid_scene_run_attack.h"
|
||||
#include "scene/flipfrid_scene_load_custom_uids.h"
|
||||
|
||||
#define RFIDFUZZER_APP_FOLDER "/ext/rfidfuzzer"
|
||||
|
||||
static void flipfrid_draw_callback(Canvas* const canvas, void* ctx) {
|
||||
FlipFridState* flipfrid_state = (FlipFridState*)acquire_mutex((ValueMutex*)ctx, 100);
|
||||
|
||||
if(flipfrid_state == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Draw correct Canvas
|
||||
switch(flipfrid_state->current_scene) {
|
||||
case NoneScene:
|
||||
case SceneEntryPoint:
|
||||
flipfrid_scene_entrypoint_on_draw(canvas, flipfrid_state);
|
||||
break;
|
||||
case SceneSelectFile:
|
||||
flipfrid_scene_load_file_on_draw(canvas, flipfrid_state);
|
||||
break;
|
||||
case SceneSelectField:
|
||||
flipfrid_scene_select_field_on_draw(canvas, flipfrid_state);
|
||||
break;
|
||||
case SceneAttack:
|
||||
flipfrid_scene_run_attack_on_draw(canvas, flipfrid_state);
|
||||
break;
|
||||
case SceneLoadCustomUids:
|
||||
flipfrid_scene_load_custom_uids_on_draw(canvas, flipfrid_state);
|
||||
break;
|
||||
}
|
||||
|
||||
release_mutex((ValueMutex*)ctx, flipfrid_state);
|
||||
}
|
||||
|
||||
void flipfrid_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) {
|
||||
furi_assert(event_queue);
|
||||
|
||||
FlipFridEvent event = {
|
||||
.evt_type = EventTypeKey, .key = input_event->key, .input_type = input_event->type};
|
||||
furi_message_queue_put(event_queue, &event, 25);
|
||||
}
|
||||
|
||||
static void flipfrid_timer_callback(FuriMessageQueue* event_queue) {
|
||||
furi_assert(event_queue);
|
||||
FlipFridEvent event = {
|
||||
.evt_type = EventTypeTick, .key = InputKeyUp, .input_type = InputTypeRelease};
|
||||
furi_message_queue_put(event_queue, &event, 25);
|
||||
}
|
||||
|
||||
FlipFridState* flipfrid_alloc() {
|
||||
FlipFridState* flipfrid = malloc(sizeof(FlipFridState));
|
||||
string_init(flipfrid->notification_msg);
|
||||
string_init(flipfrid->attack_name);
|
||||
|
||||
flipfrid->previous_scene = NoneScene;
|
||||
flipfrid->current_scene = SceneEntryPoint;
|
||||
flipfrid->is_running = true;
|
||||
flipfrid->is_attacking = false;
|
||||
flipfrid->key_index = 0;
|
||||
flipfrid->menu_index = 0;
|
||||
|
||||
flipfrid->attack = FlipFridAttackDefaultValues;
|
||||
flipfrid->notify = furi_record_open(RECORD_NOTIFICATION);
|
||||
|
||||
flipfrid->data[0] = 0x00;
|
||||
flipfrid->data[1] = 0x00;
|
||||
flipfrid->data[2] = 0x00;
|
||||
flipfrid->data[3] = 0x00;
|
||||
flipfrid->data[4] = 0x00;
|
||||
|
||||
flipfrid->payload[0] = 0x00;
|
||||
flipfrid->payload[1] = 0x00;
|
||||
flipfrid->payload[2] = 0x00;
|
||||
flipfrid->payload[3] = 0x00;
|
||||
flipfrid->payload[4] = 0x00;
|
||||
|
||||
//Dialog
|
||||
flipfrid->dialogs = furi_record_open(RECORD_DIALOGS);
|
||||
|
||||
return flipfrid;
|
||||
}
|
||||
|
||||
void flipfrid_free(FlipFridState* flipfrid) {
|
||||
//Dialog
|
||||
furi_record_close(RECORD_DIALOGS);
|
||||
notification_message(flipfrid->notify, &sequence_blink_stop);
|
||||
|
||||
// Strings
|
||||
string_clear(flipfrid->notification_msg);
|
||||
string_clear(flipfrid->attack_name);
|
||||
|
||||
free(flipfrid->data);
|
||||
free(flipfrid->payload);
|
||||
|
||||
// The rest
|
||||
free(flipfrid);
|
||||
}
|
||||
|
||||
// ENTRYPOINT
|
||||
int32_t flipfrid_start(void* p) {
|
||||
UNUSED(p);
|
||||
// Input
|
||||
FURI_LOG_I(TAG, "Initializing input");
|
||||
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(FlipFridEvent));
|
||||
FlipFridState* flipfrid_state = flipfrid_alloc();
|
||||
ValueMutex flipfrid_state_mutex;
|
||||
|
||||
// Mutex
|
||||
FURI_LOG_I(TAG, "Initializing flipfrid mutex");
|
||||
if(!init_mutex(&flipfrid_state_mutex, flipfrid_state, sizeof(FlipFridState))) {
|
||||
FURI_LOG_E(TAG, "cannot create mutex\r\n");
|
||||
furi_message_queue_free(event_queue);
|
||||
furi_record_close(RECORD_NOTIFICATION);
|
||||
furi_record_close(RECORD_DIALOGS);
|
||||
free(flipfrid_state);
|
||||
return 255;
|
||||
}
|
||||
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
if(!storage_simply_mkdir(storage, RFIDFUZZER_APP_FOLDER)) {
|
||||
FURI_LOG_E(TAG, "Could not create folder %s", RFIDFUZZER_APP_FOLDER);
|
||||
}
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
|
||||
// Configure view port
|
||||
FURI_LOG_I(TAG, "Initializing viewport");
|
||||
ViewPort* view_port = view_port_alloc();
|
||||
view_port_draw_callback_set(view_port, flipfrid_draw_callback, &flipfrid_state_mutex);
|
||||
view_port_input_callback_set(view_port, flipfrid_input_callback, event_queue);
|
||||
|
||||
// Configure timer
|
||||
FURI_LOG_I(TAG, "Initializing timer");
|
||||
FuriTimer* timer =
|
||||
furi_timer_alloc(flipfrid_timer_callback, FuriTimerTypePeriodic, event_queue);
|
||||
furi_timer_start(timer, furi_kernel_get_tick_frequency() / 10); // 10 times per second
|
||||
|
||||
// Register view port in GUI
|
||||
FURI_LOG_I(TAG, "Initializing gui");
|
||||
Gui* gui = (Gui*)furi_record_open(RECORD_GUI);
|
||||
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
|
||||
|
||||
// Init values
|
||||
FlipFridEvent event;
|
||||
while(flipfrid_state->is_running) {
|
||||
// Get next event
|
||||
FuriStatus event_status = furi_message_queue_get(event_queue, &event, 25);
|
||||
if(event_status == FuriStatusOk) {
|
||||
if(event.evt_type == EventTypeKey) {
|
||||
//Handle event key
|
||||
switch(flipfrid_state->current_scene) {
|
||||
case NoneScene:
|
||||
case SceneEntryPoint:
|
||||
flipfrid_scene_entrypoint_on_event(event, flipfrid_state);
|
||||
break;
|
||||
case SceneSelectFile:
|
||||
flipfrid_scene_load_file_on_event(event, flipfrid_state);
|
||||
break;
|
||||
case SceneSelectField:
|
||||
flipfrid_scene_select_field_on_event(event, flipfrid_state);
|
||||
break;
|
||||
case SceneAttack:
|
||||
flipfrid_scene_run_attack_on_event(event, flipfrid_state);
|
||||
break;
|
||||
case SceneLoadCustomUids:
|
||||
flipfrid_scene_load_custom_uids_on_event(event, flipfrid_state);
|
||||
break;
|
||||
}
|
||||
|
||||
} else if(event.evt_type == EventTypeTick) {
|
||||
//Handle event tick
|
||||
if(flipfrid_state->current_scene != flipfrid_state->previous_scene) {
|
||||
// Trigger Exit Scene
|
||||
switch(flipfrid_state->previous_scene) {
|
||||
case SceneEntryPoint:
|
||||
flipfrid_scene_entrypoint_on_exit(flipfrid_state);
|
||||
break;
|
||||
case SceneSelectFile:
|
||||
flipfrid_scene_load_file_on_exit(flipfrid_state);
|
||||
break;
|
||||
case SceneSelectField:
|
||||
flipfrid_scene_select_field_on_exit(flipfrid_state);
|
||||
break;
|
||||
case SceneAttack:
|
||||
flipfrid_scene_run_attack_on_exit(flipfrid_state);
|
||||
break;
|
||||
case SceneLoadCustomUids:
|
||||
flipfrid_scene_load_custom_uids_on_exit(flipfrid_state);
|
||||
break;
|
||||
case NoneScene:
|
||||
break;
|
||||
}
|
||||
|
||||
// Trigger Entry Scene
|
||||
switch(flipfrid_state->current_scene) {
|
||||
case NoneScene:
|
||||
case SceneEntryPoint:
|
||||
flipfrid_scene_entrypoint_on_enter(flipfrid_state);
|
||||
break;
|
||||
case SceneSelectFile:
|
||||
flipfrid_scene_load_file_on_enter(flipfrid_state);
|
||||
break;
|
||||
case SceneSelectField:
|
||||
flipfrid_scene_select_field_on_enter(flipfrid_state);
|
||||
break;
|
||||
case SceneAttack:
|
||||
flipfrid_scene_run_attack_on_enter(flipfrid_state);
|
||||
break;
|
||||
case SceneLoadCustomUids:
|
||||
flipfrid_scene_load_custom_uids_on_enter(flipfrid_state);
|
||||
break;
|
||||
}
|
||||
flipfrid_state->previous_scene = flipfrid_state->current_scene;
|
||||
}
|
||||
|
||||
// Trigger Tick Scene
|
||||
switch(flipfrid_state->current_scene) {
|
||||
case NoneScene:
|
||||
case SceneEntryPoint:
|
||||
flipfrid_scene_entrypoint_on_tick(flipfrid_state);
|
||||
break;
|
||||
case SceneSelectFile:
|
||||
flipfrid_scene_load_file_on_tick(flipfrid_state);
|
||||
break;
|
||||
case SceneSelectField:
|
||||
flipfrid_scene_select_field_on_tick(flipfrid_state);
|
||||
break;
|
||||
case SceneAttack:
|
||||
flipfrid_scene_run_attack_on_tick(flipfrid_state);
|
||||
break;
|
||||
case SceneLoadCustomUids:
|
||||
flipfrid_scene_load_custom_uids_on_tick(flipfrid_state);
|
||||
break;
|
||||
}
|
||||
view_port_update(view_port);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
furi_timer_stop(timer);
|
||||
furi_timer_free(timer);
|
||||
|
||||
FURI_LOG_I(TAG, "Cleaning up");
|
||||
gui_remove_view_port(gui, view_port);
|
||||
view_port_free(view_port);
|
||||
furi_message_queue_free(event_queue);
|
||||
furi_record_close(RECORD_GUI);
|
||||
furi_record_close(RECORD_NOTIFICATION);
|
||||
flipfrid_free(flipfrid_state);
|
||||
|
||||
return 0;
|
||||
}
|
||||
76
applications/flipfrid/flipfrid.h
Normal file
76
applications/flipfrid/flipfrid.h
Normal file
@@ -0,0 +1,76 @@
|
||||
#pragma once
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include <input/input.h>
|
||||
#include <gui/gui.h>
|
||||
#include <gui/modules/submenu.h>
|
||||
#include "m-string.h"
|
||||
#include <dialogs/dialogs.h>
|
||||
#include <notification/notification.h>
|
||||
#include <notification/notification_messages.h>
|
||||
#include <toolbox/stream/stream.h>
|
||||
#include <flipper_format/flipper_format_i.h>
|
||||
|
||||
#include <toolbox/stream/stream.h>
|
||||
#include <toolbox/stream/string_stream.h>
|
||||
#include <toolbox/stream/file_stream.h>
|
||||
#include <toolbox/stream/buffered_file_stream.h>
|
||||
|
||||
#include <lib/lfrfid/lfrfid_worker.h>
|
||||
#include <lfrfid/protocols/lfrfid_protocols.h>
|
||||
|
||||
#define TAG "FlipFrid"
|
||||
|
||||
typedef enum {
|
||||
FlipFridAttackDefaultValues,
|
||||
FlipFridAttackBfCustomerId,
|
||||
FlipFridAttackLoadFile,
|
||||
FlipFridAttackLoadFileCustomUids,
|
||||
} FlipFridAttacks;
|
||||
|
||||
typedef enum {
|
||||
NoneScene,
|
||||
SceneEntryPoint,
|
||||
SceneSelectFile,
|
||||
SceneSelectField,
|
||||
SceneAttack,
|
||||
SceneLoadCustomUids,
|
||||
} FlipFridScene;
|
||||
|
||||
typedef enum {
|
||||
EventTypeTick,
|
||||
EventTypeKey,
|
||||
} EventType;
|
||||
|
||||
typedef struct {
|
||||
EventType evt_type;
|
||||
InputKey key;
|
||||
InputType input_type;
|
||||
} FlipFridEvent;
|
||||
|
||||
// STRUCTS
|
||||
typedef struct {
|
||||
bool is_running;
|
||||
bool is_attacking;
|
||||
FlipFridScene current_scene;
|
||||
FlipFridScene previous_scene;
|
||||
NotificationApp* notify;
|
||||
u_int8_t menu_index;
|
||||
|
||||
string_t data_str;
|
||||
uint8_t data[5];
|
||||
uint8_t payload[5];
|
||||
uint8_t attack_step;
|
||||
FlipFridAttacks attack;
|
||||
string_t attack_name;
|
||||
|
||||
DialogsApp* dialogs;
|
||||
string_t notification_msg;
|
||||
uint8_t key_index;
|
||||
LFRFIDWorker* worker;
|
||||
ProtocolDict* dict;
|
||||
ProtocolId protocol;
|
||||
|
||||
// Used for custom dictionnary
|
||||
Stream* uids_stream;
|
||||
} FlipFridState;
|
||||
123
applications/flipfrid/scene/flipfrid_scene_entrypoint.c
Normal file
123
applications/flipfrid/scene/flipfrid_scene_entrypoint.c
Normal file
@@ -0,0 +1,123 @@
|
||||
#include "flipfrid_scene_entrypoint.h"
|
||||
|
||||
string_t menu_items[4];
|
||||
|
||||
void flipfrid_scene_entrypoint_menu_callback(FlipFridState* context, uint32_t index) {
|
||||
switch(index) {
|
||||
case FlipFridAttackDefaultValues:
|
||||
context->attack = FlipFridAttackDefaultValues;
|
||||
context->current_scene = SceneAttack;
|
||||
string_set_str(context->attack_name, "Default Values");
|
||||
break;
|
||||
case FlipFridAttackBfCustomerId:
|
||||
context->attack = FlipFridAttackBfCustomerId;
|
||||
context->current_scene = SceneAttack;
|
||||
string_set_str(context->attack_name, "Bad Customer ID");
|
||||
break;
|
||||
case FlipFridAttackLoadFile:
|
||||
context->attack = FlipFridAttackLoadFile;
|
||||
context->current_scene = SceneSelectFile;
|
||||
string_set_str(context->attack_name, "Load File");
|
||||
break;
|
||||
case FlipFridAttackLoadFileCustomUids:
|
||||
context->attack = FlipFridAttackLoadFileCustomUids;
|
||||
context->current_scene = SceneLoadCustomUids;
|
||||
string_set_str(context->attack_name, "Load Custom UIDs");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void flipfrid_scene_entrypoint_on_enter(FlipFridState* context) {
|
||||
// Clear the previous payload
|
||||
context->payload[0] = 0x00;
|
||||
context->payload[1] = 0x00;
|
||||
context->payload[2] = 0x00;
|
||||
context->payload[3] = 0x00;
|
||||
context->payload[4] = 0x00;
|
||||
|
||||
context->menu_index = 0;
|
||||
for(uint32_t i = 0; i < 4; i++) {
|
||||
string_init(menu_items[i]);
|
||||
}
|
||||
|
||||
string_set(menu_items[0], "Default Values");
|
||||
string_set(menu_items[1], "BF Customer ID");
|
||||
string_set(menu_items[2], "Load File");
|
||||
string_set(menu_items[3], "Load uids from file");
|
||||
}
|
||||
|
||||
void flipfrid_scene_entrypoint_on_exit(FlipFridState* context) {
|
||||
UNUSED(context);
|
||||
for(uint32_t i = 0; i < 4; i++) {
|
||||
string_clear(menu_items[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void flipfrid_scene_entrypoint_on_tick(FlipFridState* context) {
|
||||
UNUSED(context);
|
||||
}
|
||||
|
||||
void flipfrid_scene_entrypoint_on_event(FlipFridEvent event, FlipFridState* context) {
|
||||
if(event.evt_type == EventTypeKey) {
|
||||
if(event.input_type == InputTypeShort) {
|
||||
switch(event.key) {
|
||||
case InputKeyDown:
|
||||
if(context->menu_index < FlipFridAttackLoadFileCustomUids) {
|
||||
context->menu_index++;
|
||||
}
|
||||
break;
|
||||
case InputKeyUp:
|
||||
if(context->menu_index > FlipFridAttackDefaultValues) {
|
||||
context->menu_index--;
|
||||
}
|
||||
break;
|
||||
case InputKeyLeft:
|
||||
case InputKeyRight:
|
||||
break;
|
||||
case InputKeyOk:
|
||||
flipfrid_scene_entrypoint_menu_callback(context, context->menu_index);
|
||||
break;
|
||||
case InputKeyBack:
|
||||
context->is_running = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void flipfrid_scene_entrypoint_on_draw(Canvas* canvas, FlipFridState* context) {
|
||||
canvas_clear(canvas);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
|
||||
// Title
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str_aligned(canvas, 64, 6, AlignCenter, AlignTop, "RFID Fuzzer");
|
||||
|
||||
if(context->menu_index > FlipFridAttackDefaultValues) {
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
canvas_draw_str_aligned(
|
||||
canvas,
|
||||
64,
|
||||
24,
|
||||
AlignCenter,
|
||||
AlignTop,
|
||||
string_get_cstr(menu_items[context->menu_index - 1]));
|
||||
}
|
||||
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str_aligned(
|
||||
canvas, 64, 36, AlignCenter, AlignTop, string_get_cstr(menu_items[context->menu_index]));
|
||||
|
||||
if(context->menu_index < FlipFridAttackLoadFileCustomUids) {
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
canvas_draw_str_aligned(
|
||||
canvas,
|
||||
64,
|
||||
48,
|
||||
AlignCenter,
|
||||
AlignTop,
|
||||
string_get_cstr(menu_items[context->menu_index + 1]));
|
||||
}
|
||||
}
|
||||
8
applications/flipfrid/scene/flipfrid_scene_entrypoint.h
Normal file
8
applications/flipfrid/scene/flipfrid_scene_entrypoint.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
#include "../flipfrid.h"
|
||||
|
||||
void flipfrid_scene_entrypoint_on_enter(FlipFridState* context);
|
||||
void flipfrid_scene_entrypoint_on_exit(FlipFridState* context);
|
||||
void flipfrid_scene_entrypoint_on_tick(FlipFridState* context);
|
||||
void flipfrid_scene_entrypoint_on_event(FlipFridEvent event, FlipFridState* context);
|
||||
void flipfrid_scene_entrypoint_on_draw(Canvas* canvas, FlipFridState* context);
|
||||
@@ -0,0 +1,79 @@
|
||||
#include "flipfrid_scene_load_custom_uids.h"
|
||||
#include "flipfrid_scene_run_attack.h"
|
||||
#include "flipfrid_scene_entrypoint.h"
|
||||
|
||||
#define LFRFID_UIDS_EXTENSION ".txt"
|
||||
#define RFIDFUZZER_APP_PATH_FOLDER "/ext/rfidfuzzer"
|
||||
|
||||
bool flipfrid_load_uids(FlipFridState* context, const char* file_path) {
|
||||
bool result = false;
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
context->uids_stream = buffered_file_stream_alloc(storage);
|
||||
result =
|
||||
buffered_file_stream_open(context->uids_stream, file_path, FSAM_READ, FSOM_OPEN_EXISTING);
|
||||
// Close if loading fails
|
||||
if(!result) {
|
||||
buffered_file_stream_close(context->uids_stream);
|
||||
return false;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool flipfrid_load_custom_uids_from_file(FlipFridState* context) {
|
||||
// Input events and views are managed by file_select
|
||||
string_t uid_path;
|
||||
string_init(uid_path);
|
||||
string_set_str(uid_path, RFIDFUZZER_APP_PATH_FOLDER);
|
||||
|
||||
bool res = dialog_file_browser_show(
|
||||
context->dialogs, uid_path, uid_path, LFRFID_UIDS_EXTENSION, true, &I_125_10px, false);
|
||||
|
||||
if(res) {
|
||||
res = flipfrid_load_uids(context, string_get_cstr(uid_path));
|
||||
}
|
||||
|
||||
string_clear(uid_path);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void flipfrid_scene_load_custom_uids_on_enter(FlipFridState* context) {
|
||||
if(flipfrid_load_custom_uids_from_file(context)) {
|
||||
// Force context loading
|
||||
flipfrid_scene_run_attack_on_enter(context);
|
||||
context->current_scene = SceneAttack;
|
||||
} else {
|
||||
flipfrid_scene_entrypoint_on_enter(context);
|
||||
context->current_scene = SceneEntryPoint;
|
||||
}
|
||||
}
|
||||
|
||||
void flipfrid_scene_load_custom_uids_on_exit(FlipFridState* context) {
|
||||
UNUSED(context);
|
||||
}
|
||||
|
||||
void flipfrid_scene_load_custom_uids_on_tick(FlipFridState* context) {
|
||||
UNUSED(context);
|
||||
}
|
||||
|
||||
void flipfrid_scene_load_custom_uids_on_event(FlipFridEvent event, FlipFridState* context) {
|
||||
if(event.evt_type == EventTypeKey) {
|
||||
if(event.input_type == InputTypeShort) {
|
||||
switch(event.key) {
|
||||
case InputKeyDown:
|
||||
case InputKeyUp:
|
||||
case InputKeyLeft:
|
||||
case InputKeyRight:
|
||||
case InputKeyOk:
|
||||
case InputKeyBack:
|
||||
context->current_scene = SceneEntryPoint;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void flipfrid_scene_load_custom_uids_on_draw(Canvas* canvas, FlipFridState* context) {
|
||||
UNUSED(context);
|
||||
UNUSED(canvas);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
#include "../flipfrid.h"
|
||||
|
||||
void flipfrid_scene_load_custom_uids_on_enter(FlipFridState* context);
|
||||
void flipfrid_scene_load_custom_uids_on_exit(FlipFridState* context);
|
||||
void flipfrid_scene_load_custom_uids_on_tick(FlipFridState* context);
|
||||
void flipfrid_scene_load_custom_uids_on_event(FlipFridEvent event, FlipFridState* context);
|
||||
void flipfrid_scene_load_custom_uids_on_draw(Canvas* canvas, FlipFridState* context);
|
||||
bool flipfrid_load_custom_uids_from_file(FlipFridState* context);
|
||||
146
applications/flipfrid/scene/flipfrid_scene_load_file.c
Normal file
146
applications/flipfrid/scene/flipfrid_scene_load_file.c
Normal file
@@ -0,0 +1,146 @@
|
||||
#include "flipfrid_scene_load_file.h"
|
||||
#include "flipfrid_scene_entrypoint.h"
|
||||
|
||||
#define LFRFID_APP_EXTENSION ".rfid"
|
||||
#define LFRFID_APP_PATH_FOLDER "/ext/lfrfid"
|
||||
|
||||
bool flipfrid_load(FlipFridState* context, const char* file_path) {
|
||||
bool result = false;
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
FlipperFormat* fff_data_file = flipper_format_file_alloc(storage);
|
||||
string_t temp_str;
|
||||
string_init(temp_str);
|
||||
do {
|
||||
if(!flipper_format_file_open_existing(fff_data_file, file_path)) {
|
||||
FURI_LOG_E(TAG, "Error open file %s", file_path);
|
||||
string_reset(context->notification_msg);
|
||||
string_set_str(context->notification_msg, "Error open file");
|
||||
break;
|
||||
}
|
||||
|
||||
// FileType
|
||||
if(!flipper_format_read_string(fff_data_file, "Filetype", temp_str)) {
|
||||
FURI_LOG_E(TAG, "Missing or incorrect Filetype");
|
||||
string_reset(context->notification_msg);
|
||||
string_set_str(context->notification_msg, "Missing or incorrect Filetypes");
|
||||
break;
|
||||
} else {
|
||||
FURI_LOG_I(TAG, "Filetype: %s", string_get_cstr(temp_str));
|
||||
}
|
||||
|
||||
// Key type
|
||||
if(!flipper_format_read_string(fff_data_file, "Key type", temp_str)) {
|
||||
FURI_LOG_E(TAG, "Missing or incorrect Key type");
|
||||
string_reset(context->notification_msg);
|
||||
string_set_str(context->notification_msg, "Missing or incorrect Key type");
|
||||
break;
|
||||
} else {
|
||||
FURI_LOG_I(TAG, "Key type: %s", string_get_cstr(temp_str));
|
||||
if(strcmp(string_get_cstr(temp_str), "EM4100") != 0) {
|
||||
FURI_LOG_E(TAG, "Unsupported Key type");
|
||||
string_reset(context->notification_msg);
|
||||
string_set_str(context->notification_msg, "Unsupported Key type");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Data
|
||||
if(!flipper_format_read_string(fff_data_file, "Data", context->data_str)) {
|
||||
FURI_LOG_E(TAG, "Missing or incorrect Data");
|
||||
string_reset(context->notification_msg);
|
||||
string_set_str(context->notification_msg, "Missing or incorrect Key");
|
||||
break;
|
||||
} else {
|
||||
FURI_LOG_I(TAG, "Key: %s", string_get_cstr(context->data_str));
|
||||
|
||||
// Check data size
|
||||
if(string_size(context->data_str) != 14) {
|
||||
FURI_LOG_E(TAG, "Incorrect Key length");
|
||||
string_reset(context->notification_msg);
|
||||
string_set_str(context->notification_msg, "Incorrect Key length");
|
||||
break;
|
||||
}
|
||||
// String to uint8_t
|
||||
for(uint8_t i = 0; i < 5; i++) {
|
||||
char temp_str2[3];
|
||||
temp_str2[0] = string_get_cstr(context->data_str)[i * 3];
|
||||
temp_str2[1] = string_get_cstr(context->data_str)[i * 3 + 1];
|
||||
temp_str2[2] = '\0';
|
||||
context->data[i] = (uint8_t)strtol(temp_str2, NULL, 16);
|
||||
}
|
||||
}
|
||||
|
||||
result = true;
|
||||
} while(0);
|
||||
string_clear(temp_str);
|
||||
flipper_format_free(fff_data_file);
|
||||
if(result) {
|
||||
FURI_LOG_I(TAG, "Loaded successfully");
|
||||
string_reset(context->notification_msg);
|
||||
string_set_str(context->notification_msg, "Source loaded.");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void flipfrid_scene_load_file_on_enter(FlipFridState* context) {
|
||||
if(flipfrid_load_protocol_from_file(context)) {
|
||||
context->current_scene = SceneSelectField;
|
||||
} else {
|
||||
flipfrid_scene_entrypoint_on_enter(context);
|
||||
context->current_scene = SceneEntryPoint;
|
||||
}
|
||||
}
|
||||
|
||||
void flipfrid_scene_load_file_on_exit(FlipFridState* context) {
|
||||
UNUSED(context);
|
||||
}
|
||||
|
||||
void flipfrid_scene_load_file_on_tick(FlipFridState* context) {
|
||||
UNUSED(context);
|
||||
}
|
||||
|
||||
void flipfrid_scene_load_file_on_event(FlipFridEvent event, FlipFridState* context) {
|
||||
if(event.evt_type == EventTypeKey) {
|
||||
if(event.input_type == InputTypeShort) {
|
||||
switch(event.key) {
|
||||
case InputKeyDown:
|
||||
case InputKeyUp:
|
||||
case InputKeyLeft:
|
||||
case InputKeyRight:
|
||||
case InputKeyOk:
|
||||
case InputKeyBack:
|
||||
context->current_scene = SceneEntryPoint;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void flipfrid_scene_load_file_on_draw(Canvas* canvas, FlipFridState* context) {
|
||||
UNUSED(context);
|
||||
UNUSED(canvas);
|
||||
}
|
||||
|
||||
bool flipfrid_load_protocol_from_file(FlipFridState* context) {
|
||||
string_t user_file_path;
|
||||
string_init(user_file_path);
|
||||
string_set_str(user_file_path, LFRFID_APP_PATH_FOLDER);
|
||||
|
||||
// Input events and views are managed by file_select
|
||||
bool res = dialog_file_browser_show(
|
||||
context->dialogs,
|
||||
user_file_path,
|
||||
user_file_path,
|
||||
LFRFID_APP_EXTENSION,
|
||||
true,
|
||||
&I_125_10px,
|
||||
true);
|
||||
|
||||
if(res) {
|
||||
res = flipfrid_load(context, string_get_cstr(user_file_path));
|
||||
}
|
||||
|
||||
string_clear(user_file_path);
|
||||
|
||||
return res;
|
||||
}
|
||||
9
applications/flipfrid/scene/flipfrid_scene_load_file.h
Normal file
9
applications/flipfrid/scene/flipfrid_scene_load_file.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
#include "../flipfrid.h"
|
||||
|
||||
void flipfrid_scene_load_file_on_enter(FlipFridState* context);
|
||||
void flipfrid_scene_load_file_on_exit(FlipFridState* context);
|
||||
void flipfrid_scene_load_file_on_tick(FlipFridState* context);
|
||||
void flipfrid_scene_load_file_on_event(FlipFridEvent event, FlipFridState* context);
|
||||
void flipfrid_scene_load_file_on_draw(Canvas* canvas, FlipFridState* context);
|
||||
bool flipfrid_load_protocol_from_file(FlipFridState* context);
|
||||
212
applications/flipfrid/scene/flipfrid_scene_run_attack.c
Normal file
212
applications/flipfrid/scene/flipfrid_scene_run_attack.c
Normal file
@@ -0,0 +1,212 @@
|
||||
#include "flipfrid_scene_run_attack.h"
|
||||
|
||||
uint8_t counter = 0;
|
||||
#define TIME_BETWEEN_CARDS 5
|
||||
uint8_t id_list[16][5] = {
|
||||
{0x00, 0x00, 0x00, 0x00, 0x00}, // Null bytes
|
||||
{0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, // Only FF
|
||||
{0x11, 0x11, 0x11, 0x11, 0x11}, // Only 11
|
||||
{0x22, 0x22, 0x22, 0x22, 0x22}, // Only 22
|
||||
{0x33, 0x33, 0x33, 0x33, 0x33}, // Only 33
|
||||
{0x44, 0x44, 0x44, 0x44, 0x44}, // Only 44
|
||||
{0x55, 0x55, 0x55, 0x55, 0x55}, // Only 55
|
||||
{0x66, 0x66, 0x66, 0x66, 0x66}, // Only 66
|
||||
{0x77, 0x77, 0x77, 0x77, 0x77}, // Only 77
|
||||
{0x88, 0x88, 0x88, 0x88, 0x88}, // Only 88
|
||||
{0x99, 0x99, 0x99, 0x99, 0x99}, // Only 99
|
||||
{0x12, 0x34, 0x56, 0x78, 0x9A}, // Incremental UID
|
||||
{0x04, 0xd0, 0x9b, 0x0d, 0x6a}, // From arha
|
||||
{0x34, 0x00, 0x29, 0x3d, 0x9e}, // From arha
|
||||
{0x04, 0xdf, 0x00, 0x00, 0x01}, // From arha
|
||||
{0xCA, 0xCA, 0xCA, 0xCA, 0xCA}, // From arha
|
||||
};
|
||||
|
||||
void flipfrid_scene_run_attack_on_enter(FlipFridState* context) {
|
||||
context->attack_step = 0;
|
||||
context->dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax);
|
||||
context->worker = lfrfid_worker_alloc(context->dict);
|
||||
context->protocol = protocol_dict_get_protocol_by_name(context->dict, "EM4100");
|
||||
}
|
||||
|
||||
void flipfrid_scene_run_attack_on_exit(FlipFridState* context) {
|
||||
lfrfid_worker_stop(context->worker);
|
||||
lfrfid_worker_stop_thread(context->worker);
|
||||
lfrfid_worker_free(context->worker);
|
||||
protocol_dict_free(context->dict);
|
||||
notification_message(context->notify, &sequence_blink_stop);
|
||||
}
|
||||
|
||||
void flipfrid_scene_run_attack_on_tick(FlipFridState* context) {
|
||||
if(context->is_attacking) {
|
||||
if(1 == counter) {
|
||||
protocol_dict_set_data(context->dict, context->protocol, context->payload, 5);
|
||||
lfrfid_worker_free(context->worker);
|
||||
context->worker = lfrfid_worker_alloc(context->dict);
|
||||
lfrfid_worker_start_thread(context->worker);
|
||||
lfrfid_worker_emulate_start(context->worker, context->protocol);
|
||||
} else if(0 == counter) {
|
||||
lfrfid_worker_stop(context->worker);
|
||||
lfrfid_worker_stop_thread(context->worker);
|
||||
switch(context->attack) {
|
||||
case FlipFridAttackDefaultValues:
|
||||
context->payload[0] = id_list[context->attack_step][0];
|
||||
context->payload[1] = id_list[context->attack_step][1];
|
||||
context->payload[2] = id_list[context->attack_step][2];
|
||||
context->payload[3] = id_list[context->attack_step][3];
|
||||
context->payload[4] = id_list[context->attack_step][4];
|
||||
|
||||
if(context->attack_step == 15) {
|
||||
context->attack_step = 0;
|
||||
counter = 0;
|
||||
context->is_attacking = false;
|
||||
notification_message(context->notify, &sequence_blink_stop);
|
||||
notification_message(context->notify, &sequence_single_vibro);
|
||||
|
||||
} else {
|
||||
context->attack_step++;
|
||||
}
|
||||
break;
|
||||
|
||||
case FlipFridAttackBfCustomerId:
|
||||
context->payload[0] = context->attack_step;
|
||||
context->payload[1] = 0x00;
|
||||
context->payload[2] = 0x00;
|
||||
context->payload[3] = 0x00;
|
||||
context->payload[4] = 0x00;
|
||||
|
||||
if(context->attack_step == 255) {
|
||||
context->attack_step = 0;
|
||||
counter = 0;
|
||||
context->is_attacking = false;
|
||||
notification_message(context->notify, &sequence_blink_stop);
|
||||
notification_message(context->notify, &sequence_single_vibro);
|
||||
} else {
|
||||
context->attack_step++;
|
||||
}
|
||||
break;
|
||||
case FlipFridAttackLoadFile:
|
||||
context->payload[0] = context->data[0];
|
||||
context->payload[1] = context->data[1];
|
||||
context->payload[2] = context->data[2];
|
||||
context->payload[3] = context->data[3];
|
||||
context->payload[4] = context->data[4];
|
||||
|
||||
context->payload[context->key_index] = context->attack_step;
|
||||
|
||||
if(context->attack_step == 255) {
|
||||
context->attack_step = 0;
|
||||
counter = 0;
|
||||
context->is_attacking = false;
|
||||
notification_message(context->notify, &sequence_blink_stop);
|
||||
notification_message(context->notify, &sequence_single_vibro);
|
||||
break;
|
||||
} else {
|
||||
context->attack_step++;
|
||||
}
|
||||
break;
|
||||
case FlipFridAttackLoadFileCustomUids:
|
||||
while(true) {
|
||||
string_reset(context->data_str);
|
||||
if(!stream_read_line(context->uids_stream, context->data_str)) {
|
||||
context->attack_step = 0;
|
||||
counter = 0;
|
||||
context->is_attacking = false;
|
||||
notification_message(context->notify, &sequence_blink_stop);
|
||||
notification_message(context->notify, &sequence_single_vibro);
|
||||
break;
|
||||
};
|
||||
if(string_get_char(context->data_str, 0) == '#') continue;
|
||||
if(string_size(context->data_str) != 11) continue;
|
||||
break;
|
||||
}
|
||||
FURI_LOG_D(TAG, string_get_cstr(context->data_str));
|
||||
|
||||
// string is valid, parse it in context->payload
|
||||
for(uint8_t i = 0; i < 5; i++) {
|
||||
char temp_str[3];
|
||||
temp_str[0] = string_get_cstr(context->data_str)[i * 2];
|
||||
temp_str[1] = string_get_cstr(context->data_str)[i * 2 + 1];
|
||||
temp_str[2] = '\0';
|
||||
context->payload[i] = (uint8_t)strtol(temp_str, NULL, 16);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(counter > TIME_BETWEEN_CARDS) {
|
||||
counter = 0;
|
||||
} else {
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void flipfrid_scene_run_attack_on_event(FlipFridEvent event, FlipFridState* context) {
|
||||
if(event.evt_type == EventTypeKey) {
|
||||
if(event.input_type == InputTypeShort) {
|
||||
switch(event.key) {
|
||||
case InputKeyDown:
|
||||
case InputKeyUp:
|
||||
case InputKeyLeft:
|
||||
case InputKeyRight:
|
||||
break;
|
||||
case InputKeyOk:
|
||||
counter = 0;
|
||||
if(!context->is_attacking) {
|
||||
notification_message(context->notify, &sequence_blink_start_blue);
|
||||
context->is_attacking = true;
|
||||
} else {
|
||||
context->is_attacking = false;
|
||||
notification_message(context->notify, &sequence_blink_stop);
|
||||
notification_message(context->notify, &sequence_single_vibro);
|
||||
}
|
||||
break;
|
||||
case InputKeyBack:
|
||||
if(context->attack == FlipFridAttackLoadFileCustomUids) {
|
||||
buffered_file_stream_close(context->uids_stream);
|
||||
}
|
||||
|
||||
context->attack_step = 0;
|
||||
context->is_attacking = false;
|
||||
string_reset(context->notification_msg);
|
||||
context->current_scene = SceneEntryPoint;
|
||||
notification_message(context->notify, &sequence_blink_stop);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void flipfrid_scene_run_attack_on_draw(Canvas* canvas, FlipFridState* context) {
|
||||
canvas_clear(canvas);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
|
||||
// Frame
|
||||
canvas_draw_frame(canvas, 0, 0, 128, 64);
|
||||
|
||||
// Title
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str_aligned(
|
||||
canvas, 64, 8, AlignCenter, AlignTop, string_get_cstr(context->attack_name));
|
||||
|
||||
char uid[16];
|
||||
snprintf(
|
||||
uid,
|
||||
sizeof(uid),
|
||||
"%02X:%02X:%02X:%02X:%02X",
|
||||
context->payload[0],
|
||||
context->payload[1],
|
||||
context->payload[2],
|
||||
context->payload[3],
|
||||
context->payload[4]);
|
||||
canvas_draw_str_aligned(canvas, 64, 24, AlignCenter, AlignTop, uid);
|
||||
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
char start_stop_msg[20];
|
||||
if(context->is_attacking) {
|
||||
snprintf(start_stop_msg, sizeof(start_stop_msg), " Press OK to stop ");
|
||||
} else {
|
||||
snprintf(start_stop_msg, sizeof(start_stop_msg), " Press OK to start ");
|
||||
}
|
||||
canvas_draw_str_aligned(canvas, 64, 44, AlignCenter, AlignTop, start_stop_msg);
|
||||
}
|
||||
8
applications/flipfrid/scene/flipfrid_scene_run_attack.h
Normal file
8
applications/flipfrid/scene/flipfrid_scene_run_attack.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
#include "../flipfrid.h"
|
||||
|
||||
void flipfrid_scene_run_attack_on_enter(FlipFridState* context);
|
||||
void flipfrid_scene_run_attack_on_exit(FlipFridState* context);
|
||||
void flipfrid_scene_run_attack_on_tick(FlipFridState* context);
|
||||
void flipfrid_scene_run_attack_on_event(FlipFridEvent event, FlipFridState* context);
|
||||
void flipfrid_scene_run_attack_on_draw(Canvas* canvas, FlipFridState* context);
|
||||
121
applications/flipfrid/scene/flipfrid_scene_select_field.c
Normal file
121
applications/flipfrid/scene/flipfrid_scene_select_field.c
Normal file
@@ -0,0 +1,121 @@
|
||||
#include "flipfrid_scene_select_field.h"
|
||||
|
||||
void flipfrid_center_displayed_key(FlipFridState* context, uint8_t index) {
|
||||
const char* key_cstr = string_get_cstr(context->data_str);
|
||||
uint8_t str_index = (index * 3);
|
||||
|
||||
char display_menu[17] = {
|
||||
'X', 'X', ' ', 'X', 'X', ' ', '<', 'X', 'X', '>', ' ', 'X', 'X', ' ', 'X', 'X', '\0'};
|
||||
|
||||
if(index > 1) {
|
||||
display_menu[0] = key_cstr[str_index - 6];
|
||||
display_menu[1] = key_cstr[str_index - 5];
|
||||
} else {
|
||||
display_menu[0] = ' ';
|
||||
display_menu[1] = ' ';
|
||||
}
|
||||
|
||||
if(index > 0) {
|
||||
display_menu[3] = key_cstr[str_index - 3];
|
||||
display_menu[4] = key_cstr[str_index - 2];
|
||||
} else {
|
||||
display_menu[3] = ' ';
|
||||
display_menu[4] = ' ';
|
||||
}
|
||||
|
||||
display_menu[7] = key_cstr[str_index];
|
||||
display_menu[8] = key_cstr[str_index + 1];
|
||||
|
||||
if((str_index + 4) <= (uint8_t)strlen(key_cstr)) {
|
||||
display_menu[11] = key_cstr[str_index + 3];
|
||||
display_menu[12] = key_cstr[str_index + 4];
|
||||
} else {
|
||||
display_menu[11] = ' ';
|
||||
display_menu[12] = ' ';
|
||||
}
|
||||
|
||||
if((str_index + 8) <= (uint8_t)strlen(key_cstr)) {
|
||||
display_menu[14] = key_cstr[str_index + 6];
|
||||
display_menu[15] = key_cstr[str_index + 7];
|
||||
} else {
|
||||
display_menu[14] = ' ';
|
||||
display_menu[15] = ' ';
|
||||
}
|
||||
|
||||
string_reset(context->notification_msg);
|
||||
string_set_str(context->notification_msg, display_menu);
|
||||
}
|
||||
|
||||
void flipfrid_scene_select_field_on_enter(FlipFridState* context) {
|
||||
string_clear(context->notification_msg);
|
||||
}
|
||||
|
||||
void flipfrid_scene_select_field_on_exit(FlipFridState* context) {
|
||||
UNUSED(context);
|
||||
}
|
||||
|
||||
void flipfrid_scene_select_field_on_tick(FlipFridState* context) {
|
||||
UNUSED(context);
|
||||
}
|
||||
|
||||
void flipfrid_scene_select_field_on_event(FlipFridEvent event, FlipFridState* context) {
|
||||
if(event.evt_type == EventTypeKey) {
|
||||
if(event.input_type == InputTypeShort) {
|
||||
const char* key_cstr = string_get_cstr(context->data_str);
|
||||
|
||||
// don't look, it's ugly but I'm a python dev so...
|
||||
uint8_t nb_bytes = 0;
|
||||
for(uint8_t i = 0; i < strlen(key_cstr); i++) {
|
||||
if(' ' == key_cstr[i]) {
|
||||
nb_bytes++;
|
||||
}
|
||||
}
|
||||
|
||||
switch(event.key) {
|
||||
case InputKeyDown:
|
||||
case InputKeyUp:
|
||||
break;
|
||||
case InputKeyLeft:
|
||||
if(context->key_index > 0) {
|
||||
context->key_index = context->key_index - 1;
|
||||
}
|
||||
break;
|
||||
case InputKeyRight:
|
||||
if(context->key_index < nb_bytes) {
|
||||
context->key_index = context->key_index + 1;
|
||||
}
|
||||
break;
|
||||
case InputKeyOk:
|
||||
string_reset(context->notification_msg);
|
||||
context->current_scene = SceneAttack;
|
||||
break;
|
||||
case InputKeyBack:
|
||||
string_reset(context->notification_msg);
|
||||
context->current_scene = SceneSelectFile;
|
||||
break;
|
||||
}
|
||||
FURI_LOG_D(TAG, "Position: %d/%d", context->key_index, nb_bytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void flipfrid_scene_select_field_on_draw(Canvas* canvas, FlipFridState* context) {
|
||||
canvas_clear(canvas);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
|
||||
// Frame
|
||||
canvas_draw_frame(canvas, 0, 0, 128, 64);
|
||||
|
||||
// Title
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str_aligned(canvas, 64, 10, AlignCenter, AlignTop, "Use < > to select byte.");
|
||||
|
||||
char msg_index[18];
|
||||
snprintf(msg_index, sizeof(msg_index), "Field index : %d", context->key_index);
|
||||
canvas_draw_str_aligned(canvas, 64, 26, AlignCenter, AlignTop, msg_index);
|
||||
|
||||
flipfrid_center_displayed_key(context, context->key_index);
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
canvas_draw_str_aligned(
|
||||
canvas, 64, 40, AlignCenter, AlignTop, string_get_cstr(context->notification_msg));
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
#include "../flipfrid.h"
|
||||
|
||||
void flipfrid_scene_select_field_on_enter(FlipFridState* context);
|
||||
void flipfrid_scene_select_field_on_exit(FlipFridState* context);
|
||||
void flipfrid_scene_select_field_on_tick(FlipFridState* context);
|
||||
void flipfrid_scene_select_field_on_event(FlipFridEvent event, FlipFridState* context);
|
||||
void flipfrid_scene_select_field_on_draw(Canvas* canvas, FlipFridState* context);
|
||||
void center_displayed_key(FlipFridState* context, uint8_t index);
|
||||
0
applications/gpio/views/gpio_test.c
Executable file → Normal file
0
applications/gpio/views/gpio_test.c
Executable file → Normal file
0
applications/gpio/views/gpio_test.h
Executable file → Normal file
0
applications/gpio/views/gpio_test.h
Executable file → Normal file
0
applications/gpio/views/gpio_usb_uart.h
Executable file → Normal file
0
applications/gpio/views/gpio_usb_uart.h
Executable file → Normal file
0
applications/gui/canvas.h
Executable file → Normal file
0
applications/gui/canvas.h
Executable file → Normal file
@@ -83,6 +83,15 @@ ButtonPanel* button_panel_alloc() {
|
||||
return button_panel;
|
||||
}
|
||||
|
||||
void button_panel_reset_selection(ButtonPanel* button_panel) {
|
||||
with_view_model(
|
||||
button_panel->view, (ButtonPanelModel * model) {
|
||||
model->selected_item_x = 0;
|
||||
model->selected_item_y = 0;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
void button_panel_reserve(ButtonPanel* button_panel, size_t reserve_x, size_t reserve_y) {
|
||||
furi_check(reserve_x > 0);
|
||||
furi_check(reserve_y > 0);
|
||||
|
||||
@@ -35,6 +35,12 @@ void button_panel_free(ButtonPanel* button_panel);
|
||||
*/
|
||||
void button_panel_reset(ButtonPanel* button_panel);
|
||||
|
||||
/** Resets selected_item_x and selected_item_y.
|
||||
*
|
||||
* @param button_panel ButtonPanel instance
|
||||
*/
|
||||
void button_panel_reset_selection(ButtonPanel* button_panel);
|
||||
|
||||
/** Reserve space for adding items.
|
||||
*
|
||||
* One does not simply use button_panel_add_item() without this function. It
|
||||
|
||||
0
applications/gui/modules/dialog_ex.c
Executable file → Normal file
0
applications/gui/modules/dialog_ex.c
Executable file → Normal file
0
applications/gui/modules/menu.h
Executable file → Normal file
0
applications/gui/modules/menu.h
Executable file → Normal file
0
applications/gui/modules/text_box.c
Executable file → Normal file
0
applications/gui/modules/text_box.c
Executable file → Normal file
0
applications/gui/modules/text_box.h
Executable file → Normal file
0
applications/gui/modules/text_box.h
Executable file → Normal file
0
applications/gui/modules/variable_item_list.h
Executable file → Normal file
0
applications/gui/modules/variable_item_list.h
Executable file → Normal file
0
applications/gui/modules/widget.h
Executable file → Normal file
0
applications/gui/modules/widget.h
Executable file → Normal file
0
applications/gui/modules/widget_elements/widget_element_frame.c
Executable file → Normal file
0
applications/gui/modules/widget_elements/widget_element_frame.c
Executable file → Normal file
0
applications/gui/modules/widget_elements/widget_element_i.h
Executable file → Normal file
0
applications/gui/modules/widget_elements/widget_element_i.h
Executable file → Normal file
0
applications/gui/modules/widget_elements/widget_element_string.c
Executable file → Normal file
0
applications/gui/modules/widget_elements/widget_element_string.c
Executable file → Normal file
0
applications/gui/scene_manager.c
Executable file → Normal file
0
applications/gui/scene_manager.c
Executable file → Normal file
0
applications/gui/scene_manager.h
Executable file → Normal file
0
applications/gui/scene_manager.h
Executable file → Normal file
0
applications/gui/scene_manager_i.h
Executable file → Normal file
0
applications/gui/scene_manager_i.h
Executable file → Normal file
0
applications/gui/view_dispatcher.h
Executable file → Normal file
0
applications/gui/view_dispatcher.h
Executable file → Normal file
@@ -35,6 +35,10 @@ InfraredBruteForce* infrared_brute_force_alloc() {
|
||||
return brute_force;
|
||||
}
|
||||
|
||||
void infrared_brute_force_clear_records(InfraredBruteForce* brute_force) {
|
||||
InfraredBruteForceRecordDict_reset(brute_force->records);
|
||||
}
|
||||
|
||||
void infrared_brute_force_free(InfraredBruteForce* brute_force) {
|
||||
furi_assert(!brute_force->ff);
|
||||
InfraredBruteForceRecordDict_clear(brute_force->records);
|
||||
|
||||
@@ -16,6 +16,7 @@ bool infrared_brute_force_start(
|
||||
bool infrared_brute_force_is_started(InfraredBruteForce* brute_force);
|
||||
void infrared_brute_force_stop(InfraredBruteForce* brute_force);
|
||||
bool infrared_brute_force_send_next(InfraredBruteForce* brute_force);
|
||||
void infrared_brute_force_clear_records(InfraredBruteForce* brute_force);
|
||||
void infrared_brute_force_add_record(
|
||||
InfraredBruteForce* brute_force,
|
||||
uint32_t index,
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
#include <infrared.h>
|
||||
#include <infrared_worker.h>
|
||||
#include <furi_hal_infrared.h>
|
||||
#include <flipper_format.h>
|
||||
#include <toolbox/args.h>
|
||||
|
||||
#include "infrared_signal.h"
|
||||
|
||||
@@ -10,6 +12,7 @@
|
||||
|
||||
static void infrared_cli_start_ir_rx(Cli* cli, string_t args);
|
||||
static void infrared_cli_start_ir_tx(Cli* cli, string_t args);
|
||||
static void infrared_cli_process_decode(Cli* cli, string_t args);
|
||||
|
||||
static const struct {
|
||||
const char* cmd;
|
||||
@@ -17,6 +20,7 @@ static const struct {
|
||||
} infrared_cli_commands[] = {
|
||||
{.cmd = "rx", .process_function = infrared_cli_start_ir_rx},
|
||||
{.cmd = "tx", .process_function = infrared_cli_start_ir_tx},
|
||||
{.cmd = "decode", .process_function = infrared_cli_process_decode},
|
||||
};
|
||||
|
||||
static void signal_received_callback(void* context, InfraredWorkerSignal* received_signal) {
|
||||
@@ -86,6 +90,7 @@ static void infrared_cli_print_usage(void) {
|
||||
"\tFrequency (%d - %d), Duty cycle (0 - 100), max 512 samples\r\n",
|
||||
INFRARED_MIN_FREQUENCY,
|
||||
INFRARED_MAX_FREQUENCY);
|
||||
printf("\tir decode <input_file> [<output_file>]\r\n");
|
||||
}
|
||||
|
||||
static bool infrared_cli_parse_message(const char* str, InfraredSignal* signal) {
|
||||
@@ -162,6 +167,160 @@ static void infrared_cli_start_ir_tx(Cli* cli, string_t args) {
|
||||
infrared_signal_free(signal);
|
||||
}
|
||||
|
||||
static bool
|
||||
infrared_cli_save_signal(InfraredSignal* signal, FlipperFormat* file, const char* name) {
|
||||
bool ret = infrared_signal_save(signal, file, name);
|
||||
if(!ret) {
|
||||
printf("Failed to save signal: \"%s\"\r\n", name);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool infrared_cli_decode_raw_signal(
|
||||
InfraredRawSignal* raw_signal,
|
||||
InfraredDecoderHandler* decoder,
|
||||
FlipperFormat* output_file,
|
||||
const char* signal_name) {
|
||||
InfraredSignal* signal = infrared_signal_alloc();
|
||||
bool ret = false, level = true, is_decoded = false;
|
||||
|
||||
size_t i;
|
||||
for(i = 0; i < raw_signal->timings_size; ++i) {
|
||||
// TODO: Any infrared_check_decoder_ready() magic?
|
||||
const InfraredMessage* message = infrared_decode(decoder, level, raw_signal->timings[i]);
|
||||
|
||||
if(message) {
|
||||
is_decoded = true;
|
||||
printf(
|
||||
"Protocol: %s address: 0x%lX command: 0x%lX %s\r\n",
|
||||
infrared_get_protocol_name(message->protocol),
|
||||
message->address,
|
||||
message->command,
|
||||
(message->repeat ? "R" : ""));
|
||||
if(output_file && !message->repeat) {
|
||||
infrared_signal_set_message(signal, message);
|
||||
if(!infrared_cli_save_signal(signal, output_file, signal_name)) break;
|
||||
}
|
||||
}
|
||||
|
||||
level = !level;
|
||||
}
|
||||
|
||||
if(i == raw_signal->timings_size) {
|
||||
if(!is_decoded && output_file) {
|
||||
infrared_signal_set_raw_signal(
|
||||
signal,
|
||||
raw_signal->timings,
|
||||
raw_signal->timings_size,
|
||||
raw_signal->frequency,
|
||||
raw_signal->duty_cycle);
|
||||
ret = infrared_cli_save_signal(signal, output_file, signal_name);
|
||||
} else {
|
||||
ret = true;
|
||||
}
|
||||
}
|
||||
|
||||
infrared_reset_decoder(decoder);
|
||||
infrared_signal_free(signal);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool infrared_cli_decode_file(FlipperFormat* input_file, FlipperFormat* output_file) {
|
||||
bool ret = false;
|
||||
|
||||
InfraredSignal* signal = infrared_signal_alloc();
|
||||
InfraredDecoderHandler* decoder = infrared_alloc_decoder();
|
||||
|
||||
string_t tmp;
|
||||
string_init(tmp);
|
||||
|
||||
while(infrared_signal_read(signal, input_file, tmp)) {
|
||||
ret = false;
|
||||
if(!infrared_signal_is_valid(signal)) {
|
||||
printf("Invalid signal\r\n");
|
||||
break;
|
||||
}
|
||||
if(!infrared_signal_is_raw(signal)) {
|
||||
if(output_file &&
|
||||
!infrared_cli_save_signal(signal, output_file, string_get_cstr(tmp))) {
|
||||
break;
|
||||
} else {
|
||||
printf("Skipping decoded signal\r\n");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
InfraredRawSignal* raw_signal = infrared_signal_get_raw_signal(signal);
|
||||
printf("Raw signal: %s, %u samples\r\n", string_get_cstr(tmp), raw_signal->timings_size);
|
||||
if(!infrared_cli_decode_raw_signal(raw_signal, decoder, output_file, string_get_cstr(tmp)))
|
||||
break;
|
||||
ret = true;
|
||||
}
|
||||
|
||||
infrared_free_decoder(decoder);
|
||||
infrared_signal_free(signal);
|
||||
string_clear(tmp);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void infrared_cli_process_decode(Cli* cli, string_t args) {
|
||||
UNUSED(cli);
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
FlipperFormat* input_file = flipper_format_buffered_file_alloc(storage);
|
||||
FlipperFormat* output_file = NULL;
|
||||
|
||||
uint32_t version;
|
||||
string_t tmp, header, input_path, output_path;
|
||||
string_init(tmp);
|
||||
string_init(header);
|
||||
string_init(input_path);
|
||||
string_init(output_path);
|
||||
|
||||
do {
|
||||
if(!args_read_probably_quoted_string_and_trim(args, input_path)) {
|
||||
printf("Wrong arguments.\r\n");
|
||||
infrared_cli_print_usage();
|
||||
break;
|
||||
}
|
||||
args_read_probably_quoted_string_and_trim(args, output_path);
|
||||
if(!flipper_format_buffered_file_open_existing(input_file, string_get_cstr(input_path))) {
|
||||
printf("Failed to open file for reading: \"%s\"\r\n", string_get_cstr(input_path));
|
||||
break;
|
||||
}
|
||||
if(!flipper_format_read_header(input_file, header, &version) ||
|
||||
(!string_start_with_str_p(header, "IR")) || version != 1) {
|
||||
printf("Invalid or corrupted input file: \"%s\"\r\n", string_get_cstr(input_path));
|
||||
break;
|
||||
}
|
||||
if(!string_empty_p(output_path)) {
|
||||
printf("Writing output to file: \"%s\"\r\n", string_get_cstr(output_path));
|
||||
output_file = flipper_format_file_alloc(storage);
|
||||
}
|
||||
if(output_file &&
|
||||
!flipper_format_file_open_always(output_file, string_get_cstr(output_path))) {
|
||||
printf("Failed to open file for writing: \"%s\"\r\n", string_get_cstr(output_path));
|
||||
break;
|
||||
}
|
||||
if(output_file && !flipper_format_write_header(output_file, header, version)) {
|
||||
printf("Failed to write to the output file: \"%s\"\r\n", string_get_cstr(output_path));
|
||||
break;
|
||||
}
|
||||
if(!infrared_cli_decode_file(input_file, output_file)) {
|
||||
break;
|
||||
}
|
||||
printf("File successfully decoded.\r\n");
|
||||
} while(false);
|
||||
|
||||
string_clear(tmp);
|
||||
string_clear(header);
|
||||
string_clear(input_path);
|
||||
string_clear(output_path);
|
||||
|
||||
flipper_format_free(input_file);
|
||||
if(output_file) flipper_format_free(output_file);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
}
|
||||
|
||||
static void infrared_cli_start_ir(Cli* cli, string_t args, void* context) {
|
||||
UNUSED(context);
|
||||
if(furi_hal_infrared_is_busy()) {
|
||||
@@ -169,18 +328,15 @@ static void infrared_cli_start_ir(Cli* cli, string_t args, void* context) {
|
||||
return;
|
||||
}
|
||||
|
||||
string_t command;
|
||||
string_init(command);
|
||||
args_read_string_and_trim(args, command);
|
||||
|
||||
size_t i = 0;
|
||||
for(; i < COUNT_OF(infrared_cli_commands); ++i) {
|
||||
size_t size = strlen(infrared_cli_commands[i].cmd);
|
||||
bool cmd_found = !strncmp(string_get_cstr(args), infrared_cli_commands[i].cmd, size);
|
||||
if(cmd_found) {
|
||||
if(string_size(args) == size) {
|
||||
break;
|
||||
}
|
||||
if(string_get_cstr(args)[size] == ' ') {
|
||||
string_right(args, size + 1);
|
||||
break;
|
||||
}
|
||||
size_t cmd_len = strlen(infrared_cli_commands[i].cmd);
|
||||
if(!strncmp(string_get_cstr(command), infrared_cli_commands[i].cmd, cmd_len)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,6 +345,8 @@ static void infrared_cli_start_ir(Cli* cli, string_t args, void* context) {
|
||||
} else {
|
||||
infrared_cli_print_usage();
|
||||
}
|
||||
|
||||
string_clear(command);
|
||||
}
|
||||
void infrared_on_system_start() {
|
||||
#ifdef SRV_CLI
|
||||
|
||||
@@ -33,6 +33,8 @@ static void infrared_scene_universal_common_hide_popup(Infrared* infrared) {
|
||||
|
||||
void infrared_scene_universal_common_on_enter(void* context) {
|
||||
Infrared* infrared = context;
|
||||
infrared_brute_force_clear_records(infrared->brute_force);
|
||||
button_panel_reset_selection(infrared->button_panel);
|
||||
view_stack_add_view(infrared->view_stack, button_panel_get_view(infrared->button_panel));
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ ADD_SCENE(infrared, remote_list, RemoteList)
|
||||
ADD_SCENE(infrared, universal, Universal)
|
||||
ADD_SCENE(infrared, universal_tv, UniversalTV)
|
||||
ADD_SCENE(infrared, universal_ac, UniversalAC)
|
||||
ADD_SCENE(infrared, universal_fan, UniversalFan)
|
||||
ADD_SCENE(infrared, universal_audio, UniversalAudio)
|
||||
ADD_SCENE(infrared, universal_projector, UniversalProjector)
|
||||
ADD_SCENE(infrared, debug, Debug)
|
||||
|
||||
@@ -25,7 +25,6 @@ bool infrared_scene_learn_on_event(void* context, SceneManagerEvent event) {
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == InfraredCustomEventTypeSignalReceived) {
|
||||
infrared_worker_rx_set_received_signal_callback(infrared->worker, NULL, NULL);
|
||||
infrared_play_notification_message(infrared, InfraredNotificationMessageSuccess);
|
||||
scene_manager_next_scene(infrared->scene_manager, InfraredSceneLearnSuccess);
|
||||
consumed = true;
|
||||
@@ -38,6 +37,7 @@ bool infrared_scene_learn_on_event(void* context, SceneManagerEvent event) {
|
||||
void infrared_scene_learn_on_exit(void* context) {
|
||||
Infrared* infrared = context;
|
||||
Popup* popup = infrared->popup;
|
||||
infrared_worker_rx_set_received_signal_callback(infrared->worker, NULL, NULL);
|
||||
infrared_worker_rx_stop(infrared->worker);
|
||||
infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStop);
|
||||
popup_set_icon(popup, 0, 0, NULL);
|
||||
|
||||
@@ -4,6 +4,7 @@ typedef enum {
|
||||
SubmenuIndexUniversalTV,
|
||||
SubmenuIndexUniversalAudio,
|
||||
SubmenuIndexUniversalProjector,
|
||||
SubmenuIndexUniversalFan,
|
||||
SubmenuIndexUniversalAirConditioner,
|
||||
} SubmenuIndex;
|
||||
|
||||
@@ -38,6 +39,13 @@ void infrared_scene_universal_on_enter(void* context) {
|
||||
infrared_scene_universal_submenu_callback,
|
||||
context);
|
||||
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
"Fans",
|
||||
SubmenuIndexUniversalFan,
|
||||
infrared_scene_universal_submenu_callback,
|
||||
context);
|
||||
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
"ACs",
|
||||
@@ -63,6 +71,9 @@ bool infrared_scene_universal_on_event(void* context, SceneManagerEvent event) {
|
||||
} else if(event.event == SubmenuIndexUniversalProjector) {
|
||||
scene_manager_next_scene(scene_manager, InfraredSceneUniversalProjector);
|
||||
consumed = true;
|
||||
} else if(event.event == SubmenuIndexUniversalFan) {
|
||||
scene_manager_next_scene(scene_manager, InfraredSceneUniversalFan);
|
||||
consumed = true;
|
||||
} else if(event.event == SubmenuIndexUniversalAirConditioner) {
|
||||
scene_manager_next_scene(scene_manager, InfraredSceneUniversalAC);
|
||||
consumed = true;
|
||||
|
||||
@@ -12,7 +12,7 @@ void infrared_scene_universal_ac_on_enter(void* context) {
|
||||
infrared_brute_force_set_db_filename(brute_force, EXT_PATH("infrared/assets/ac.ir"));
|
||||
|
||||
//TODO Improve A/C universal remote
|
||||
button_panel_reserve(button_panel, 2, 2);
|
||||
button_panel_reserve(button_panel, 2, 3);
|
||||
uint32_t i = 0;
|
||||
button_panel_add_item(
|
||||
button_panel,
|
||||
@@ -44,7 +44,7 @@ void infrared_scene_universal_ac_on_enter(void* context) {
|
||||
0,
|
||||
1,
|
||||
3,
|
||||
69,
|
||||
66,
|
||||
&I_Vol_up_25x27,
|
||||
&I_Vol_up_hvr_25x27,
|
||||
infrared_scene_universal_common_item_callback,
|
||||
@@ -56,15 +56,39 @@ void infrared_scene_universal_ac_on_enter(void* context) {
|
||||
1,
|
||||
1,
|
||||
36,
|
||||
69,
|
||||
66,
|
||||
&I_Vol_down_25x27,
|
||||
&I_Vol_down_hvr_25x27,
|
||||
infrared_scene_universal_common_item_callback,
|
||||
context);
|
||||
infrared_brute_force_add_record(brute_force, i++, "TEMP-");
|
||||
button_panel_add_item(
|
||||
button_panel,
|
||||
i,
|
||||
0,
|
||||
2,
|
||||
3,
|
||||
98,
|
||||
&I_Swing_25x27,
|
||||
&I_Swing_hvr_25x27,
|
||||
infrared_scene_universal_common_item_callback,
|
||||
context);
|
||||
infrared_brute_force_add_record(brute_force, i++, "SWING");
|
||||
button_panel_add_item(
|
||||
button_panel,
|
||||
i,
|
||||
1,
|
||||
2,
|
||||
36,
|
||||
98,
|
||||
&I_Timer_25x27,
|
||||
&I_Timer_hvr_25x27,
|
||||
infrared_scene_universal_common_item_callback,
|
||||
context);
|
||||
infrared_brute_force_add_record(brute_force, i++, "TIMER");
|
||||
|
||||
button_panel_add_label(button_panel, 6, 11, FontPrimary, "AC remote");
|
||||
button_panel_add_label(button_panel, 20, 66, FontSecondary, "Temp");
|
||||
button_panel_add_label(button_panel, 20, 63, FontSecondary, "Temp");
|
||||
button_panel_add_label(button_panel, 8, 23, FontSecondary, "Pwr");
|
||||
button_panel_add_label(button_panel, 40, 23, FontSecondary, "Mod");
|
||||
|
||||
|
||||
113
applications/infrared/scenes/infrared_scene_universal_fan.c
Normal file
113
applications/infrared/scenes/infrared_scene_universal_fan.c
Normal file
@@ -0,0 +1,113 @@
|
||||
#include "../infrared_i.h"
|
||||
|
||||
#include "common/infrared_scene_universal_common.h"
|
||||
|
||||
void infrared_scene_universal_fan_on_enter(void* context) {
|
||||
infrared_scene_universal_common_on_enter(context);
|
||||
|
||||
Infrared* infrared = context;
|
||||
ButtonPanel* button_panel = infrared->button_panel;
|
||||
InfraredBruteForce* brute_force = infrared->brute_force;
|
||||
|
||||
infrared_brute_force_set_db_filename(brute_force, EXT_PATH("infrared/assets/fans.ir"));
|
||||
|
||||
//TODO Improve Fan universal remote
|
||||
button_panel_reserve(button_panel, 2, 3);
|
||||
uint32_t i = 0;
|
||||
button_panel_add_item(
|
||||
button_panel,
|
||||
i,
|
||||
0,
|
||||
0,
|
||||
3,
|
||||
24,
|
||||
&I_Power_25x27,
|
||||
&I_Power_hvr_25x27,
|
||||
infrared_scene_universal_common_item_callback,
|
||||
context);
|
||||
infrared_brute_force_add_record(brute_force, i++, "POWER");
|
||||
button_panel_add_item(
|
||||
button_panel,
|
||||
i,
|
||||
1,
|
||||
0,
|
||||
36,
|
||||
24,
|
||||
&I_Mode_25x27,
|
||||
&I_Mode_hvr_25x27,
|
||||
infrared_scene_universal_common_item_callback,
|
||||
context);
|
||||
infrared_brute_force_add_record(brute_force, i++, "MODE");
|
||||
button_panel_add_item(
|
||||
button_panel,
|
||||
i,
|
||||
0,
|
||||
1,
|
||||
3,
|
||||
66,
|
||||
&I_Vol_up_25x27,
|
||||
&I_Vol_up_hvr_25x27,
|
||||
infrared_scene_universal_common_item_callback,
|
||||
context);
|
||||
infrared_brute_force_add_record(brute_force, i++, "SPEED+");
|
||||
button_panel_add_item(
|
||||
button_panel,
|
||||
i,
|
||||
1,
|
||||
1,
|
||||
36,
|
||||
66,
|
||||
&I_Vol_down_25x27,
|
||||
&I_Vol_down_hvr_25x27,
|
||||
infrared_scene_universal_common_item_callback,
|
||||
context);
|
||||
infrared_brute_force_add_record(brute_force, i++, "SPEED-");
|
||||
button_panel_add_item(
|
||||
button_panel,
|
||||
i,
|
||||
0,
|
||||
2,
|
||||
3,
|
||||
98,
|
||||
&I_Rotate_25x27,
|
||||
&I_Rotate_hvr_25x27,
|
||||
infrared_scene_universal_common_item_callback,
|
||||
context);
|
||||
infrared_brute_force_add_record(brute_force, i++, "ROTATE");
|
||||
button_panel_add_item(
|
||||
button_panel,
|
||||
i,
|
||||
1,
|
||||
2,
|
||||
36,
|
||||
98,
|
||||
&I_Timer_25x27,
|
||||
&I_Timer_hvr_25x27,
|
||||
infrared_scene_universal_common_item_callback,
|
||||
context);
|
||||
infrared_brute_force_add_record(brute_force, i++, "TIMER");
|
||||
|
||||
button_panel_add_label(button_panel, 5, 11, FontPrimary, "Fan remote");
|
||||
button_panel_add_label(button_panel, 20, 63, FontSecondary, "Speed");
|
||||
button_panel_add_label(button_panel, 8, 23, FontSecondary, "Pwr");
|
||||
button_panel_add_label(button_panel, 40, 23, FontSecondary, "Mod");
|
||||
|
||||
view_set_orientation(view_stack_get_view(infrared->view_stack), ViewOrientationVertical);
|
||||
view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewStack);
|
||||
|
||||
infrared_show_loading_popup(infrared, true);
|
||||
bool success = infrared_brute_force_calculate_messages(brute_force);
|
||||
infrared_show_loading_popup(infrared, false);
|
||||
|
||||
if(!success) {
|
||||
scene_manager_next_scene(infrared->scene_manager, InfraredSceneErrorDatabases);
|
||||
}
|
||||
}
|
||||
|
||||
bool infrared_scene_universal_fan_on_event(void* context, SceneManagerEvent event) {
|
||||
return infrared_scene_universal_common_on_event(context, event);
|
||||
}
|
||||
|
||||
void infrared_scene_universal_fan_on_exit(void* context) {
|
||||
infrared_scene_universal_common_on_exit(context);
|
||||
}
|
||||
@@ -9,7 +9,7 @@ typedef enum {
|
||||
void LfRfidAppSceneExtraActions::on_enter(LfRfidApp* app, bool need_restore) {
|
||||
auto submenu = app->view_controller.get<SubmenuVM>();
|
||||
|
||||
submenu->add_item("Read ASK (Animal, Ordinary Card)", SubmenuASK, submenu_callback, app);
|
||||
submenu->add_item("Read ASK (FDX,Regular)", SubmenuASK, submenu_callback, app);
|
||||
submenu->add_item("Read PSK (Indala)", SubmenuPSK, submenu_callback, app);
|
||||
|
||||
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
|
||||
|
||||
@@ -4,11 +4,18 @@ void LfRfidAppSceneSaveType::on_enter(LfRfidApp* app, bool need_restore) {
|
||||
auto submenu = app->view_controller.get<SubmenuVM>();
|
||||
|
||||
for(uint8_t i = 0; i < keys_count; i++) {
|
||||
string_init_printf(
|
||||
submenu_name[i],
|
||||
"%s %s",
|
||||
protocol_dict_get_manufacturer(app->dict, i),
|
||||
protocol_dict_get_name(app->dict, i));
|
||||
if(strcmp(
|
||||
protocol_dict_get_manufacturer(app->dict, i),
|
||||
protocol_dict_get_name(app->dict, i)) &&
|
||||
strcmp(protocol_dict_get_manufacturer(app->dict, i), "N/A")) {
|
||||
string_init_printf(
|
||||
submenu_name[i],
|
||||
"%s %s",
|
||||
protocol_dict_get_manufacturer(app->dict, i),
|
||||
protocol_dict_get_name(app->dict, i));
|
||||
} else {
|
||||
string_init_printf(submenu_name[i], "%s", protocol_dict_get_name(app->dict, i));
|
||||
}
|
||||
submenu->add_item(string_get_cstr(submenu_name[i]), i, submenu_callback, app);
|
||||
}
|
||||
|
||||
|
||||
@@ -35,6 +35,11 @@ void LfRfidAppSceneWrite::on_enter(LfRfidApp* app, bool /* need_restore */) {
|
||||
popup->set_icon(0, 3, &I_RFIDDolphinSend_97x61);
|
||||
|
||||
app->view_controller.switch_to<PopupVM>();
|
||||
|
||||
size_t size = protocol_dict_get_data_size(app->dict, app->protocol_id);
|
||||
app->old_key_data = (uint8_t*)malloc(size);
|
||||
protocol_dict_get_data(app->dict, app->protocol_id, app->old_key_data, size);
|
||||
|
||||
lfrfid_worker_start_thread(app->lfworker);
|
||||
lfrfid_worker_write_start(
|
||||
app->lfworker, (LFRFIDProtocol)app->protocol_id, lfrfid_write_callback, app);
|
||||
@@ -76,4 +81,8 @@ void LfRfidAppSceneWrite::on_exit(LfRfidApp* app) {
|
||||
app->view_controller.get<PopupVM>()->clean();
|
||||
lfrfid_worker_stop(app->lfworker);
|
||||
lfrfid_worker_stop_thread(app->lfworker);
|
||||
|
||||
size_t size = protocol_dict_get_data_size(app->dict, app->protocol_id);
|
||||
protocol_dict_set_data(app->dict, app->protocol_id, app->old_key_data, size);
|
||||
free(app->old_key_data);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ App(
|
||||
name="Basic services",
|
||||
apptype=FlipperAppType.METAPACKAGE,
|
||||
provides=[
|
||||
"rpc",
|
||||
"rpc_start",
|
||||
"bt",
|
||||
"desktop",
|
||||
"loader",
|
||||
@@ -48,6 +48,7 @@ App(
|
||||
"tetris_game",
|
||||
"arkanoid_game",
|
||||
"tictactoe_game",
|
||||
"wav_player",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -75,7 +76,7 @@ App(
|
||||
"wifi_marauder",
|
||||
"esp8266_deauth",
|
||||
"wifi_scanner",
|
||||
"wav_player",
|
||||
"multi_converter",
|
||||
"flipfrid",
|
||||
],
|
||||
)
|
||||
@@ -8,152 +8,158 @@
|
||||
#include "multi_converter_mode_select.h"
|
||||
|
||||
static void multi_converter_render_callback(Canvas* const canvas, void* ctx) {
|
||||
const MultiConverterState* multi_converter_state = acquire_mutex((ValueMutex*)ctx, 25);
|
||||
if(multi_converter_state == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
const MultiConverterState* multi_converter_state = acquire_mutex((ValueMutex*)ctx, 25);
|
||||
if(multi_converter_state == NULL) {
|
||||
return;
|
||||
}
|
||||
if(multi_converter_state->mode == ModeDisplay) {
|
||||
multi_converter_mode_display_draw(canvas, multi_converter_state);
|
||||
} else {
|
||||
multi_converter_mode_select_draw(canvas, multi_converter_state);
|
||||
}
|
||||
|
||||
if (multi_converter_state->mode == ModeDisplay) {
|
||||
multi_converter_mode_display_draw(canvas, multi_converter_state);
|
||||
} else {
|
||||
multi_converter_mode_select_draw(canvas, multi_converter_state);
|
||||
}
|
||||
|
||||
release_mutex((ValueMutex*)ctx, multi_converter_state);
|
||||
release_mutex((ValueMutex*)ctx, multi_converter_state);
|
||||
}
|
||||
|
||||
static void multi_converter_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) {
|
||||
furi_assert(event_queue);
|
||||
static void
|
||||
multi_converter_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) {
|
||||
furi_assert(event_queue);
|
||||
|
||||
MultiConverterEvent event = {.type = EventTypeKey, .input = *input_event};
|
||||
MultiConverterEvent event = {.type = EventTypeKey, .input = *input_event};
|
||||
furi_message_queue_put(event_queue, &event, FuriWaitForever);
|
||||
}
|
||||
|
||||
static void multi_converter_init(MultiConverterState* const multi_converter_state) {
|
||||
// initial default values
|
||||
// initial default values
|
||||
|
||||
multi_converter_state->buffer_orig[MULTI_CONVERTER_NUMBER_DIGITS] = '\0';
|
||||
multi_converter_state->buffer_dest[MULTI_CONVERTER_NUMBER_DIGITS] = '\0'; // null terminators
|
||||
multi_converter_state->buffer_orig[MULTI_CONVERTER_NUMBER_DIGITS] = '\0';
|
||||
multi_converter_state->buffer_dest[MULTI_CONVERTER_NUMBER_DIGITS] = '\0'; // null terminators
|
||||
|
||||
multi_converter_state->unit_type_orig = UnitTypeDec;
|
||||
multi_converter_state->unit_type_dest = UnitTypeHex;
|
||||
multi_converter_state->unit_type_orig = UnitTypeDec;
|
||||
multi_converter_state->unit_type_dest = UnitTypeHex;
|
||||
|
||||
multi_converter_state->keyboard_lock = 0;
|
||||
multi_converter_state->keyboard_lock = 0;
|
||||
|
||||
// init the display view
|
||||
multi_converter_mode_display_reset(multi_converter_state);
|
||||
// init the display view
|
||||
multi_converter_mode_display_reset(multi_converter_state);
|
||||
|
||||
// init the select view
|
||||
multi_converter_mode_select_reset(multi_converter_state);
|
||||
// init the select view
|
||||
multi_converter_mode_select_reset(multi_converter_state);
|
||||
|
||||
// set ModeDisplay as the current mode
|
||||
multi_converter_state->mode = ModeDisplay;
|
||||
// set ModeDisplay as the current mode
|
||||
multi_converter_state->mode = ModeDisplay;
|
||||
}
|
||||
|
||||
// main entry point
|
||||
int32_t multi_converter_app(void* p) {
|
||||
UNUSED(p);
|
||||
|
||||
// get event queue
|
||||
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(MultiConverterEvent));
|
||||
// get event queue
|
||||
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(MultiConverterEvent));
|
||||
|
||||
// allocate state
|
||||
MultiConverterState* multi_converter_state = malloc(sizeof(MultiConverterState));
|
||||
// allocate state
|
||||
MultiConverterState* multi_converter_state = malloc(sizeof(MultiConverterState));
|
||||
|
||||
// set mutex for plugin state (different threads can access it)
|
||||
ValueMutex state_mutex;
|
||||
if(!init_mutex(&state_mutex, multi_converter_state, sizeof(multi_converter_state))) {
|
||||
FURI_LOG_E("MultiConverter", "cannot create mutex\r\n");
|
||||
free(multi_converter_state);
|
||||
return 255;
|
||||
}
|
||||
// set mutex for plugin state (different threads can access it)
|
||||
ValueMutex state_mutex;
|
||||
if(!init_mutex(&state_mutex, multi_converter_state, sizeof(multi_converter_state))) {
|
||||
FURI_LOG_E("MultiConverter", "cannot create mutex\r\n");
|
||||
furi_message_queue_free(event_queue);
|
||||
free(multi_converter_state);
|
||||
return 255;
|
||||
}
|
||||
|
||||
// register callbacks for drawing and input processing
|
||||
ViewPort* view_port = view_port_alloc();
|
||||
view_port_draw_callback_set(view_port, multi_converter_render_callback, &state_mutex);
|
||||
view_port_input_callback_set(view_port, multi_converter_input_callback, event_queue);
|
||||
// register callbacks for drawing and input processing
|
||||
ViewPort* view_port = view_port_alloc();
|
||||
view_port_draw_callback_set(view_port, multi_converter_render_callback, &state_mutex);
|
||||
view_port_input_callback_set(view_port, multi_converter_input_callback, event_queue);
|
||||
|
||||
// open GUI and register view_port
|
||||
Gui* gui = furi_record_open("gui");
|
||||
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
|
||||
// open GUI and register view_port
|
||||
Gui* gui = furi_record_open("gui");
|
||||
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
|
||||
|
||||
multi_converter_init(multi_converter_state);
|
||||
multi_converter_init(multi_converter_state);
|
||||
|
||||
// main loop
|
||||
MultiConverterEvent event;
|
||||
for (bool processing = true; processing;) {
|
||||
FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
|
||||
MultiConverterState* multi_converter_state = (MultiConverterState*)acquire_mutex_block(&state_mutex);
|
||||
// main loop
|
||||
MultiConverterEvent event;
|
||||
for(bool processing = true; processing;) {
|
||||
FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
|
||||
MultiConverterState* multi_converter_state =
|
||||
(MultiConverterState*)acquire_mutex_block(&state_mutex);
|
||||
|
||||
if (event_status == FuriStatusOk) {
|
||||
// press events
|
||||
if (event.type == EventTypeKey && !multi_converter_state->keyboard_lock) {
|
||||
if (multi_converter_state->mode == ModeDisplay) {
|
||||
if(event_status == FuriStatusOk) {
|
||||
// press events
|
||||
if(event.type == EventTypeKey && !multi_converter_state->keyboard_lock) {
|
||||
if(multi_converter_state->mode == ModeDisplay) {
|
||||
if(event.input.key == InputKeyBack) {
|
||||
if(event.input.type == InputTypePress) processing = false;
|
||||
} else if(event.input.key == InputKeyOk) { // the "ok" press can be short or long
|
||||
MultiConverterModeTrigger t = None;
|
||||
|
||||
if (event.input.key == InputKeyBack) {
|
||||
if (event.input.type == InputTypePress) processing = false;
|
||||
} else if (event.input.key == InputKeyOk) { // the "ok" press can be short or long
|
||||
MultiConverterModeTrigger t = None;
|
||||
if(event.input.type == InputTypeLong)
|
||||
t = multi_converter_mode_display_ok(1, multi_converter_state);
|
||||
else if(event.input.type == InputTypeShort)
|
||||
t = multi_converter_mode_display_ok(0, multi_converter_state);
|
||||
|
||||
if (event.input.type == InputTypeLong) t = multi_converter_mode_display_ok(1, multi_converter_state);
|
||||
else if (event.input.type == InputTypeShort) t = multi_converter_mode_display_ok(0, multi_converter_state);
|
||||
if(t == Reset) {
|
||||
multi_converter_mode_select_reset(multi_converter_state);
|
||||
multi_converter_state->mode = ModeSelector;
|
||||
}
|
||||
} else {
|
||||
if(event.input.type == InputTypePress)
|
||||
multi_converter_mode_display_navigation(
|
||||
event.input.key, multi_converter_state);
|
||||
}
|
||||
|
||||
if (t == Reset) {
|
||||
multi_converter_mode_select_reset(multi_converter_state);
|
||||
multi_converter_state->mode = ModeSelector;
|
||||
}
|
||||
} else {
|
||||
if (event.input.type == InputTypePress) multi_converter_mode_display_navigation(event.input.key, multi_converter_state);
|
||||
}
|
||||
|
||||
} else { // ModeSelect
|
||||
if (event.input.type == InputTypePress) {
|
||||
switch (event.input.key) {
|
||||
default:
|
||||
break;
|
||||
case InputKeyBack:
|
||||
case InputKeyOk: {
|
||||
MultiConverterModeTrigger t = multi_converter_mode_select_exit(event.input.key == InputKeyOk ? 1 : 0, multi_converter_state);
|
||||
} else { // ModeSelect
|
||||
if(event.input.type == InputTypePress) {
|
||||
switch(event.input.key) {
|
||||
default:
|
||||
break;
|
||||
case InputKeyBack:
|
||||
case InputKeyOk: {
|
||||
MultiConverterModeTrigger t = multi_converter_mode_select_exit(
|
||||
event.input.key == InputKeyOk ? 1 : 0, multi_converter_state);
|
||||
|
||||
if (t == Reset) {
|
||||
multi_converter_mode_display_reset(multi_converter_state);
|
||||
} else if (t == Convert) {
|
||||
multi_converter_mode_display_convert(multi_converter_state);
|
||||
}
|
||||
if(t == Reset) {
|
||||
multi_converter_mode_display_reset(multi_converter_state);
|
||||
} else if(t == Convert) {
|
||||
multi_converter_mode_display_convert(multi_converter_state);
|
||||
}
|
||||
|
||||
multi_converter_state->keyboard_lock = 1;
|
||||
multi_converter_state->mode = ModeDisplay;
|
||||
break;
|
||||
}
|
||||
case InputKeyLeft:
|
||||
case InputKeyRight:
|
||||
multi_converter_mode_select_switch(multi_converter_state);
|
||||
break;
|
||||
case InputKeyUp:
|
||||
multi_converter_mode_select_change_unit(-1, multi_converter_state);
|
||||
break;
|
||||
case InputKeyDown:
|
||||
multi_converter_mode_select_change_unit(1, multi_converter_state);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (multi_converter_state->keyboard_lock) {
|
||||
multi_converter_state->keyboard_lock = 0;
|
||||
}
|
||||
} else {
|
||||
// event timeout
|
||||
}
|
||||
|
||||
view_port_update(view_port);
|
||||
release_mutex(&state_mutex, multi_converter_state);
|
||||
}
|
||||
multi_converter_state->keyboard_lock = 1;
|
||||
multi_converter_state->mode = ModeDisplay;
|
||||
break;
|
||||
}
|
||||
case InputKeyLeft:
|
||||
case InputKeyRight:
|
||||
multi_converter_mode_select_switch(multi_converter_state);
|
||||
break;
|
||||
case InputKeyUp:
|
||||
multi_converter_mode_select_change_unit(-1, multi_converter_state);
|
||||
break;
|
||||
case InputKeyDown:
|
||||
multi_converter_mode_select_change_unit(1, multi_converter_state);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if(multi_converter_state->keyboard_lock) {
|
||||
multi_converter_state->keyboard_lock = 0;
|
||||
}
|
||||
} else {
|
||||
// event timeout
|
||||
}
|
||||
|
||||
view_port_enabled_set(view_port, false);
|
||||
gui_remove_view_port(gui, view_port);
|
||||
furi_record_close("gui");
|
||||
view_port_free(view_port);
|
||||
view_port_update(view_port);
|
||||
release_mutex(&state_mutex, multi_converter_state);
|
||||
}
|
||||
|
||||
view_port_enabled_set(view_port, false);
|
||||
gui_remove_view_port(gui, view_port);
|
||||
furi_record_close("gui");
|
||||
view_port_free(view_port);
|
||||
furi_message_queue_free(event_queue);
|
||||
delete_mutex(&state_mutex);
|
||||
free(multi_converter_state);
|
||||
|
||||
@@ -1,82 +1,82 @@
|
||||
#pragma once
|
||||
|
||||
#define MULTI_CONVERTER_NUMBER_DIGITS 9
|
||||
#define MULTI_CONVERTER_NUMBER_DIGITS 9
|
||||
|
||||
typedef enum {
|
||||
EventTypeKey,
|
||||
EventTypeKey,
|
||||
} EventType;
|
||||
|
||||
typedef struct {
|
||||
InputEvent input;
|
||||
EventType type;
|
||||
InputEvent input;
|
||||
EventType type;
|
||||
} MultiConverterEvent;
|
||||
|
||||
typedef enum {
|
||||
ModeDisplay,
|
||||
ModeSelector,
|
||||
ModeDisplay,
|
||||
ModeSelector,
|
||||
} MultiConverterMode;
|
||||
|
||||
typedef enum {
|
||||
None,
|
||||
Reset,
|
||||
Convert,
|
||||
None,
|
||||
Reset,
|
||||
Convert,
|
||||
} MultiConverterModeTrigger;
|
||||
|
||||
// new units goes here, used as index to the main multi_converter_available_units array (multi_converter_units.h)
|
||||
typedef enum {
|
||||
UnitTypeDec,
|
||||
UnitTypeHex,
|
||||
UnitTypeBin,
|
||||
UnitTypeDec,
|
||||
UnitTypeHex,
|
||||
UnitTypeBin,
|
||||
|
||||
UnitTypeCelsius,
|
||||
UnitTypeFahernheit,
|
||||
UnitTypeKelvin,
|
||||
UnitTypeCelsius,
|
||||
UnitTypeFahernheit,
|
||||
UnitTypeKelvin,
|
||||
|
||||
UnitTypeKilometers,
|
||||
UnitTypeMeters,
|
||||
UnitTypeCentimeters,
|
||||
UnitTypeMiles,
|
||||
UnitTypeFeet,
|
||||
UnitTypeInches,
|
||||
UnitTypeKilometers,
|
||||
UnitTypeMeters,
|
||||
UnitTypeCentimeters,
|
||||
UnitTypeMiles,
|
||||
UnitTypeFeet,
|
||||
UnitTypeInches,
|
||||
|
||||
UnitTypeDegree,
|
||||
UnitTypeRadian,
|
||||
UnitTypeDegree,
|
||||
UnitTypeRadian,
|
||||
} MultiConverterUnitType;
|
||||
|
||||
typedef struct {
|
||||
MultiConverterUnitType selected_unit_type_orig;
|
||||
MultiConverterUnitType selected_unit_type_dest;
|
||||
uint8_t select_orig;
|
||||
MultiConverterUnitType selected_unit_type_orig;
|
||||
MultiConverterUnitType selected_unit_type_dest;
|
||||
uint8_t select_orig;
|
||||
} MultiConverterModeSelect;
|
||||
|
||||
typedef struct {
|
||||
uint8_t cursor; // cursor position when typing
|
||||
int8_t key; // hover key
|
||||
uint8_t comma; // comma already added? (only one comma allowed)
|
||||
uint8_t negative; // is negative?
|
||||
uint8_t cursor; // cursor position when typing
|
||||
int8_t key; // hover key
|
||||
uint8_t comma; // comma already added? (only one comma allowed)
|
||||
uint8_t negative; // is negative?
|
||||
} MultiConverterModeDisplay;
|
||||
|
||||
typedef struct MultiConverterUnit MultiConverterUnit;
|
||||
typedef struct MultiConverterState MultiConverterState;
|
||||
|
||||
struct MultiConverterUnit {
|
||||
uint8_t allow_comma;
|
||||
uint8_t allow_negative;
|
||||
uint8_t max_number_keys;
|
||||
char mini_name[4];
|
||||
char name[12];
|
||||
void (*convert_function)(MultiConverterState * const);
|
||||
uint8_t (*allowed_function)(MultiConverterUnitType);
|
||||
uint8_t allow_comma;
|
||||
uint8_t allow_negative;
|
||||
uint8_t max_number_keys;
|
||||
char mini_name[4];
|
||||
char name[12];
|
||||
void (*convert_function)(MultiConverterState* const);
|
||||
uint8_t (*allowed_function)(MultiConverterUnitType);
|
||||
};
|
||||
|
||||
struct MultiConverterState {
|
||||
char buffer_orig[MULTI_CONVERTER_NUMBER_DIGITS + 1];
|
||||
char buffer_dest[MULTI_CONVERTER_NUMBER_DIGITS + 1];
|
||||
MultiConverterUnitType unit_type_orig;
|
||||
MultiConverterUnitType unit_type_dest;
|
||||
MultiConverterMode mode;
|
||||
MultiConverterModeDisplay display;
|
||||
MultiConverterModeSelect select;
|
||||
uint8_t keyboard_lock; // used to create a small lock when switching from SELECT to DISPLAY modes
|
||||
// (debouncing, basically; otherwise it switch modes twice 'cause it's too fast!)
|
||||
char buffer_orig[MULTI_CONVERTER_NUMBER_DIGITS + 1];
|
||||
char buffer_dest[MULTI_CONVERTER_NUMBER_DIGITS + 1];
|
||||
MultiConverterUnitType unit_type_orig;
|
||||
MultiConverterUnitType unit_type_dest;
|
||||
MultiConverterMode mode;
|
||||
MultiConverterModeDisplay display;
|
||||
MultiConverterModeSelect select;
|
||||
uint8_t keyboard_lock; // used to create a small lock when switching from SELECT to DISPLAY modes
|
||||
// (debouncing, basically; otherwise it switch modes twice 'cause it's too fast!)
|
||||
};
|
||||
|
||||
@@ -1,284 +1,326 @@
|
||||
#include "multi_converter_mode_display.h"
|
||||
|
||||
#define MULTI_CONVERTER_DISPLAY_KEYS 18 // [0] to [F] + [BACK] + [SELECT]
|
||||
|
||||
#define MULTI_CONVERTER_DISPLAY_KEY_NEGATIVE 0 // long press
|
||||
#define MULTI_CONVERTER_DISPLAY_KEY_COMMA 1 // long press
|
||||
#define MULTI_CONVERTER_DISPLAY_KEY_DEL 16
|
||||
#define MULTI_CONVERTER_DISPLAY_KEY_SELECT 17
|
||||
#define MULTI_CONVERTER_DISPLAY_KEYS 18 // [0] to [F] + [BACK] + [SELECT]
|
||||
|
||||
#define MULTI_CONVERTER_DISPLAY_CHAR_COMMA '.'
|
||||
#define MULTI_CONVERTER_DISPLAY_CHAR_NEGATIVE '-'
|
||||
#define MULTI_CONVERTER_DISPLAY_CHAR_DEL '<'
|
||||
#define MULTI_CONVERTER_DISPLAY_CHAR_SELECT '#'
|
||||
#define MULTI_CONVERTER_DISPLAY_CHAR_BLANK ' '
|
||||
#define MULTI_CONVERTER_DISPLAY_KEY_NEGATIVE 0 // long press
|
||||
#define MULTI_CONVERTER_DISPLAY_KEY_COMMA 1 // long press
|
||||
#define MULTI_CONVERTER_DISPLAY_KEY_DEL 16
|
||||
#define MULTI_CONVERTER_DISPLAY_KEY_SELECT 17
|
||||
|
||||
#define MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN 3
|
||||
#define MULTI_CONVERTER_DISPLAY_KEY_CHAR_HEIGHT 8
|
||||
#define MULTI_CONVERTER_DISPLAY_CHAR_COMMA '.'
|
||||
#define MULTI_CONVERTER_DISPLAY_CHAR_NEGATIVE '-'
|
||||
#define MULTI_CONVERTER_DISPLAY_CHAR_DEL '<'
|
||||
#define MULTI_CONVERTER_DISPLAY_CHAR_SELECT '#'
|
||||
#define MULTI_CONVERTER_DISPLAY_CHAR_BLANK ' '
|
||||
|
||||
#define MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN 3
|
||||
#define MULTI_CONVERTER_DISPLAY_KEY_CHAR_HEIGHT 8
|
||||
|
||||
void multi_converter_mode_display_convert(MultiConverterState* const multi_converter_state) {
|
||||
// 1.- if origin == destination (in theory user won't be allowed to choose the same options, but it's kinda "valid"...)
|
||||
// just copy buffer_orig to buffer_dest and that's it
|
||||
|
||||
// 1.- if origin == destination (in theory user won't be allowed to choose the same options, but it's kinda "valid"...)
|
||||
// just copy buffer_orig to buffer_dest and that's it
|
||||
|
||||
if (multi_converter_state->unit_type_orig == multi_converter_state->unit_type_dest) {
|
||||
memcpy(multi_converter_state->buffer_dest, multi_converter_state->buffer_orig, MULTI_CONVERTER_NUMBER_DIGITS);
|
||||
return;
|
||||
}
|
||||
if(multi_converter_state->unit_type_orig == multi_converter_state->unit_type_dest) {
|
||||
memcpy(
|
||||
multi_converter_state->buffer_dest,
|
||||
multi_converter_state->buffer_orig,
|
||||
MULTI_CONVERTER_NUMBER_DIGITS);
|
||||
return;
|
||||
}
|
||||
|
||||
// 2.- origin_buffer has not null functions
|
||||
if (multi_converter_get_unit(multi_converter_state->unit_type_orig).convert_function == NULL || multi_converter_get_unit(multi_converter_state->unit_type_orig).allowed_function == NULL) return;
|
||||
// 2.- origin_buffer has not null functions
|
||||
if(multi_converter_get_unit(multi_converter_state->unit_type_orig).convert_function == NULL ||
|
||||
multi_converter_get_unit(multi_converter_state->unit_type_orig).allowed_function == NULL)
|
||||
return;
|
||||
|
||||
// 3.- valid destination type (using allowed_destinations function)
|
||||
if (!multi_converter_get_unit(multi_converter_state->unit_type_orig).allowed_function(multi_converter_state->unit_type_dest)) return;
|
||||
|
||||
multi_converter_get_unit(multi_converter_state->unit_type_orig).convert_function(multi_converter_state);
|
||||
// 3.- valid destination type (using allowed_destinations function)
|
||||
if(!multi_converter_get_unit(multi_converter_state->unit_type_orig)
|
||||
.allowed_function(multi_converter_state->unit_type_dest))
|
||||
return;
|
||||
|
||||
multi_converter_get_unit(multi_converter_state->unit_type_orig)
|
||||
.convert_function(multi_converter_state);
|
||||
}
|
||||
|
||||
void multi_converter_mode_display_draw(Canvas* const canvas, const MultiConverterState* multi_converter_state) {
|
||||
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
void multi_converter_mode_display_draw(
|
||||
Canvas* const canvas,
|
||||
const MultiConverterState* multi_converter_state) {
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
|
||||
// ORIGIN
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str(canvas, 2, 10, multi_converter_get_unit(multi_converter_state->unit_type_orig).mini_name);
|
||||
// ORIGIN
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str(
|
||||
canvas, 2, 10, multi_converter_get_unit(multi_converter_state->unit_type_orig).mini_name);
|
||||
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str(canvas, 2 + 30, 10, multi_converter_state->buffer_orig);
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str(canvas, 2 + 30, 10, multi_converter_state->buffer_orig);
|
||||
|
||||
// DESTINATION
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str(canvas, 2, 10 + 12, multi_converter_get_unit(multi_converter_state->unit_type_dest).mini_name);
|
||||
// DESTINATION
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str(
|
||||
canvas,
|
||||
2,
|
||||
10 + 12,
|
||||
multi_converter_get_unit(multi_converter_state->unit_type_dest).mini_name);
|
||||
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str(canvas, 2 + 30, 10 + 12, multi_converter_state->buffer_dest);
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str(canvas, 2 + 30, 10 + 12, multi_converter_state->buffer_dest);
|
||||
|
||||
// SEPARATOR_LINE
|
||||
canvas_draw_line(canvas, 2, 25, 128 - 3, 25);
|
||||
// SEPARATOR_LINE
|
||||
canvas_draw_line(canvas, 2, 25, 128 - 3, 25);
|
||||
|
||||
// KEYBOARD
|
||||
uint8_t _x = 5;
|
||||
uint8_t _y = 25 + 15; // line + 10
|
||||
// KEYBOARD
|
||||
uint8_t _x = 5;
|
||||
uint8_t _y = 25 + 15; // line + 10
|
||||
|
||||
for (int i = 0; i < MULTI_CONVERTER_DISPLAY_KEYS; i++) {
|
||||
for(int i = 0; i < MULTI_CONVERTER_DISPLAY_KEYS; i++) {
|
||||
char g;
|
||||
if(i < 10)
|
||||
g = (i + '0');
|
||||
else if(i < 16)
|
||||
g = ((i - 10) + 'A');
|
||||
else if(i == MULTI_CONVERTER_DISPLAY_KEY_DEL)
|
||||
g = MULTI_CONVERTER_DISPLAY_CHAR_DEL;
|
||||
else
|
||||
g = MULTI_CONVERTER_DISPLAY_CHAR_SELECT;
|
||||
|
||||
char g;
|
||||
if (i < 10) g = (i + '0');
|
||||
else if (i < 16) g = ((i - 10) + 'A');
|
||||
else if (i == MULTI_CONVERTER_DISPLAY_KEY_DEL) g = MULTI_CONVERTER_DISPLAY_CHAR_DEL;
|
||||
else g = MULTI_CONVERTER_DISPLAY_CHAR_SELECT;
|
||||
|
||||
uint8_t g_w = canvas_glyph_width(canvas, g);
|
||||
uint8_t g_w = canvas_glyph_width(canvas, g);
|
||||
|
||||
if (i < 16 && i > multi_converter_get_unit(multi_converter_state->unit_type_orig).max_number_keys-1) {
|
||||
// some units won't use the full [0] - [F] keyboard, in those situations just hide the char
|
||||
// (won't be selectable anyway, so no worries here; this is just about drawing stuff)
|
||||
g = MULTI_CONVERTER_DISPLAY_CHAR_BLANK;
|
||||
}
|
||||
if(i < 16 &&
|
||||
i > multi_converter_get_unit(multi_converter_state->unit_type_orig).max_number_keys -
|
||||
1) {
|
||||
// some units won't use the full [0] - [F] keyboard, in those situations just hide the char
|
||||
// (won't be selectable anyway, so no worries here; this is just about drawing stuff)
|
||||
g = MULTI_CONVERTER_DISPLAY_CHAR_BLANK;
|
||||
}
|
||||
|
||||
// currently hover key is highlighted
|
||||
if ((multi_converter_state->display).key == i) {
|
||||
canvas_draw_box(canvas,
|
||||
_x - MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN,
|
||||
_y - (MULTI_CONVERTER_DISPLAY_KEY_CHAR_HEIGHT + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN),
|
||||
MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN + g_w + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN,
|
||||
MULTI_CONVERTER_DISPLAY_KEY_CHAR_HEIGHT + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN * 2
|
||||
);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
} else {
|
||||
canvas_draw_frame(canvas,
|
||||
_x - MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN,
|
||||
_y - (MULTI_CONVERTER_DISPLAY_KEY_CHAR_HEIGHT + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN),
|
||||
MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN + g_w + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN,
|
||||
MULTI_CONVERTER_DISPLAY_KEY_CHAR_HEIGHT + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN * 2
|
||||
);
|
||||
}
|
||||
// currently hover key is highlighted
|
||||
if((multi_converter_state->display).key == i) {
|
||||
canvas_draw_box(
|
||||
canvas,
|
||||
_x - MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN,
|
||||
_y - (MULTI_CONVERTER_DISPLAY_KEY_CHAR_HEIGHT +
|
||||
MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN),
|
||||
MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN + g_w +
|
||||
MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN,
|
||||
MULTI_CONVERTER_DISPLAY_KEY_CHAR_HEIGHT +
|
||||
MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN * 2);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
} else {
|
||||
canvas_draw_frame(
|
||||
canvas,
|
||||
_x - MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN,
|
||||
_y - (MULTI_CONVERTER_DISPLAY_KEY_CHAR_HEIGHT +
|
||||
MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN),
|
||||
MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN + g_w +
|
||||
MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN,
|
||||
MULTI_CONVERTER_DISPLAY_KEY_CHAR_HEIGHT +
|
||||
MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN * 2);
|
||||
}
|
||||
|
||||
// draw key
|
||||
canvas_draw_glyph(canvas, _x, _y, g);
|
||||
// draw key
|
||||
canvas_draw_glyph(canvas, _x, _y, g);
|
||||
|
||||
// certain keys have long_press features, draw whatever they're using there too
|
||||
if (i == MULTI_CONVERTER_DISPLAY_KEY_NEGATIVE) {
|
||||
canvas_draw_box(canvas,
|
||||
_x + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN + g_w - 4,
|
||||
_y + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN - 2,
|
||||
4,
|
||||
2
|
||||
);
|
||||
} else if (i == MULTI_CONVERTER_DISPLAY_KEY_COMMA) {
|
||||
canvas_draw_box(canvas,
|
||||
_x + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN + g_w - 2,
|
||||
_y + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN - 2,
|
||||
2,
|
||||
2
|
||||
);
|
||||
}
|
||||
// certain keys have long_press features, draw whatever they're using there too
|
||||
if(i == MULTI_CONVERTER_DISPLAY_KEY_NEGATIVE) {
|
||||
canvas_draw_box(
|
||||
canvas,
|
||||
_x + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN + g_w - 4,
|
||||
_y + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN - 2,
|
||||
4,
|
||||
2);
|
||||
} else if(i == MULTI_CONVERTER_DISPLAY_KEY_COMMA) {
|
||||
canvas_draw_box(
|
||||
canvas,
|
||||
_x + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN + g_w - 2,
|
||||
_y + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN - 2,
|
||||
2,
|
||||
2);
|
||||
}
|
||||
|
||||
// back to black
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
|
||||
if (i < 8) {
|
||||
_x += g_w + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN * 2 + 2;
|
||||
} else if (i == 8) {
|
||||
_y += (MULTI_CONVERTER_DISPLAY_KEY_CHAR_HEIGHT + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN * 2) + 3;
|
||||
_x = 8; // some padding at the beginning on second line
|
||||
} else {
|
||||
_x += g_w + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN * 2 + 1;
|
||||
}
|
||||
}
|
||||
// back to black
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
|
||||
if(i < 8) {
|
||||
_x += g_w + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN * 2 + 2;
|
||||
} else if(i == 8) {
|
||||
_y += (MULTI_CONVERTER_DISPLAY_KEY_CHAR_HEIGHT +
|
||||
MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN * 2) +
|
||||
3;
|
||||
_x = 8; // some padding at the beginning on second line
|
||||
} else {
|
||||
_x += g_w + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN * 2 + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void multi_converter_mode_display_navigation(InputKey key, MultiConverterState* const multi_converter_state) {
|
||||
void multi_converter_mode_display_navigation(
|
||||
InputKey key,
|
||||
MultiConverterState* const multi_converter_state) {
|
||||
// first move to keyboard position, then check if the ORIGIN allows that specific key, if not jump to the "closest one"
|
||||
switch(key) {
|
||||
default:
|
||||
break;
|
||||
|
||||
// first move to keyboard position, then check if the ORIGIN allows that specific key, if not jump to the "closest one"
|
||||
switch (key) {
|
||||
case InputKeyUp:
|
||||
case InputKeyDown:
|
||||
if((multi_converter_state->display).key >= 9)
|
||||
(multi_converter_state->display).key -= 9;
|
||||
else
|
||||
(multi_converter_state->display).key += 9;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
case InputKeyLeft:
|
||||
case InputKeyRight:
|
||||
|
||||
case InputKeyUp:
|
||||
case InputKeyDown:
|
||||
if ((multi_converter_state->display).key >= 9) (multi_converter_state->display).key -= 9;
|
||||
else (multi_converter_state->display).key += 9;
|
||||
break;
|
||||
(multi_converter_state->display).key += (key == InputKeyLeft ? -1 : 1);
|
||||
|
||||
case InputKeyLeft:
|
||||
case InputKeyRight:
|
||||
if((multi_converter_state->display).key > MULTI_CONVERTER_DISPLAY_KEYS - 1)
|
||||
(multi_converter_state->display).key = 0;
|
||||
else if((multi_converter_state->display).key < 0)
|
||||
(multi_converter_state->display).key = MULTI_CONVERTER_DISPLAY_KEYS - 1;
|
||||
break;
|
||||
}
|
||||
|
||||
(multi_converter_state->display).key += (key == InputKeyLeft ? -1 : 1);
|
||||
// if destination key is disabled by max_number_keys, move to the closest one
|
||||
// (this could be improved with more accurate keys movements, probably...)
|
||||
if(multi_converter_get_unit(multi_converter_state->unit_type_orig).max_number_keys >= 16)
|
||||
return; // weird, since this means "do not show any number on the keyboard, but just in case..."
|
||||
|
||||
if ((multi_converter_state->display).key > MULTI_CONVERTER_DISPLAY_KEYS-1) (multi_converter_state->display).key = 0;
|
||||
else if ((multi_converter_state->display).key < 0) (multi_converter_state->display).key = MULTI_CONVERTER_DISPLAY_KEYS-1;
|
||||
break;
|
||||
}
|
||||
|
||||
// if destination key is disabled by max_number_keys, move to the closest one
|
||||
// (this could be improved with more accurate keys movements, probably...)
|
||||
if (multi_converter_get_unit(multi_converter_state->unit_type_orig).max_number_keys >= 16) return; // weird, since this means "do not show any number on the keyboard, but just in case..."
|
||||
|
||||
int8_t i = -1;
|
||||
if (key == InputKeyRight || key == InputKeyDown) i = 1;
|
||||
|
||||
while ((multi_converter_state->display).key < 16 && (multi_converter_state->display).key > multi_converter_get_unit(multi_converter_state->unit_type_orig).max_number_keys-1) {
|
||||
(multi_converter_state->display).key += i;
|
||||
if ((multi_converter_state->display).key > MULTI_CONVERTER_DISPLAY_KEYS-1) (multi_converter_state->display).key = 0;
|
||||
else if ((multi_converter_state->display).key < 0) (multi_converter_state->display).key = MULTI_CONVERTER_DISPLAY_KEYS-1;
|
||||
}
|
||||
int8_t i = -1;
|
||||
if(key == InputKeyRight || key == InputKeyDown) i = 1;
|
||||
|
||||
while((multi_converter_state->display).key < 16 &&
|
||||
(multi_converter_state->display).key >
|
||||
multi_converter_get_unit(multi_converter_state->unit_type_orig).max_number_keys -
|
||||
1) {
|
||||
(multi_converter_state->display).key += i;
|
||||
if((multi_converter_state->display).key > MULTI_CONVERTER_DISPLAY_KEYS - 1)
|
||||
(multi_converter_state->display).key = 0;
|
||||
else if((multi_converter_state->display).key < 0)
|
||||
(multi_converter_state->display).key = MULTI_CONVERTER_DISPLAY_KEYS - 1;
|
||||
}
|
||||
}
|
||||
|
||||
void multi_converter_mode_display_reset(MultiConverterState* const multi_converter_state) {
|
||||
// clean the buffers
|
||||
for(int i = 0; i < MULTI_CONVERTER_NUMBER_DIGITS; i++) {
|
||||
multi_converter_state->buffer_orig[i] = MULTI_CONVERTER_DISPLAY_CHAR_BLANK;
|
||||
multi_converter_state->buffer_dest[i] = MULTI_CONVERTER_DISPLAY_CHAR_BLANK;
|
||||
}
|
||||
|
||||
// clean the buffers
|
||||
for (int i = 0; i < MULTI_CONVERTER_NUMBER_DIGITS; i++) {
|
||||
multi_converter_state->buffer_orig[i] = MULTI_CONVERTER_DISPLAY_CHAR_BLANK;
|
||||
multi_converter_state->buffer_dest[i] = MULTI_CONVERTER_DISPLAY_CHAR_BLANK;
|
||||
}
|
||||
|
||||
// reset the display flags and index
|
||||
multi_converter_state->display.cursor = 0;
|
||||
multi_converter_state->display.key = 0;
|
||||
multi_converter_state->display.comma = 0;
|
||||
multi_converter_state->display.negative = 0;
|
||||
// reset the display flags and index
|
||||
multi_converter_state->display.cursor = 0;
|
||||
multi_converter_state->display.key = 0;
|
||||
multi_converter_state->display.comma = 0;
|
||||
multi_converter_state->display.negative = 0;
|
||||
}
|
||||
|
||||
void multi_converter_mode_display_toggle_negative(MultiConverterState* const multi_converter_state) {
|
||||
if (multi_converter_get_unit(multi_converter_state->unit_type_orig).allow_negative) {
|
||||
|
||||
if (!(multi_converter_state->display).negative) {
|
||||
// shift origin buffer one to right + add the "-" sign (last digit will be lost)
|
||||
for (int i = MULTI_CONVERTER_NUMBER_DIGITS-1; i > 0; i--) {
|
||||
// we could avoid the blanks, but nevermind
|
||||
multi_converter_state->buffer_orig[i] = multi_converter_state->buffer_orig[i-1];
|
||||
}
|
||||
multi_converter_state->buffer_orig[0] = MULTI_CONVERTER_DISPLAY_CHAR_NEGATIVE;
|
||||
void multi_converter_mode_display_toggle_negative(
|
||||
MultiConverterState* const multi_converter_state) {
|
||||
if(multi_converter_get_unit(multi_converter_state->unit_type_orig).allow_negative) {
|
||||
if(!(multi_converter_state->display).negative) {
|
||||
// shift origin buffer one to right + add the "-" sign (last digit will be lost)
|
||||
for(int i = MULTI_CONVERTER_NUMBER_DIGITS - 1; i > 0; i--) {
|
||||
// we could avoid the blanks, but nevermind
|
||||
multi_converter_state->buffer_orig[i] = multi_converter_state->buffer_orig[i - 1];
|
||||
}
|
||||
multi_converter_state->buffer_orig[0] = MULTI_CONVERTER_DISPLAY_CHAR_NEGATIVE;
|
||||
|
||||
// only increment cursor if we're not out of bound
|
||||
if ((multi_converter_state->display).cursor < MULTI_CONVERTER_NUMBER_DIGITS) (multi_converter_state->display).cursor++;
|
||||
} else {
|
||||
// shift origin buffer one to left, append ' ' on the end
|
||||
for (int i = 0; i < MULTI_CONVERTER_NUMBER_DIGITS-1; i++) {
|
||||
if (multi_converter_state->buffer_orig[i] == MULTI_CONVERTER_DISPLAY_CHAR_BLANK) break;
|
||||
// only increment cursor if we're not out of bound
|
||||
if((multi_converter_state->display).cursor < MULTI_CONVERTER_NUMBER_DIGITS)
|
||||
(multi_converter_state->display).cursor++;
|
||||
} else {
|
||||
// shift origin buffer one to left, append ' ' on the end
|
||||
for(int i = 0; i < MULTI_CONVERTER_NUMBER_DIGITS - 1; i++) {
|
||||
if(multi_converter_state->buffer_orig[i] == MULTI_CONVERTER_DISPLAY_CHAR_BLANK)
|
||||
break;
|
||||
|
||||
multi_converter_state->buffer_orig[i] = multi_converter_state->buffer_orig[i+1];
|
||||
}
|
||||
multi_converter_state->buffer_orig[MULTI_CONVERTER_NUMBER_DIGITS-1] = MULTI_CONVERTER_DISPLAY_CHAR_BLANK;
|
||||
multi_converter_state->buffer_orig[i] = multi_converter_state->buffer_orig[i + 1];
|
||||
}
|
||||
multi_converter_state->buffer_orig[MULTI_CONVERTER_NUMBER_DIGITS - 1] =
|
||||
MULTI_CONVERTER_DISPLAY_CHAR_BLANK;
|
||||
|
||||
(multi_converter_state->display).cursor--;
|
||||
}
|
||||
(multi_converter_state->display).cursor--;
|
||||
}
|
||||
|
||||
// toggle flag
|
||||
(multi_converter_state->display).negative ^= 1;
|
||||
}
|
||||
// toggle flag
|
||||
(multi_converter_state->display).negative ^= 1;
|
||||
}
|
||||
}
|
||||
|
||||
void multi_converter_mode_display_add_comma(MultiConverterState* const multi_converter_state) {
|
||||
if (
|
||||
!multi_converter_get_unit(multi_converter_state->unit_type_orig).allow_comma ||
|
||||
(multi_converter_state->display).comma ||
|
||||
!(multi_converter_state->display).cursor ||
|
||||
((multi_converter_state->display).cursor == (MULTI_CONVERTER_NUMBER_DIGITS - 1))
|
||||
) return; // maybe not allowerd; or one comma already in place; also cannot add commas as first or last chars
|
||||
if(!multi_converter_get_unit(multi_converter_state->unit_type_orig).allow_comma ||
|
||||
(multi_converter_state->display).comma || !(multi_converter_state->display).cursor ||
|
||||
((multi_converter_state->display).cursor == (MULTI_CONVERTER_NUMBER_DIGITS - 1)))
|
||||
return; // maybe not allowerd; or one comma already in place; also cannot add commas as first or last chars
|
||||
|
||||
// set flag to one
|
||||
(multi_converter_state->display).comma = 1;
|
||||
// set flag to one
|
||||
(multi_converter_state->display).comma = 1;
|
||||
|
||||
multi_converter_state->buffer_orig[(multi_converter_state->display).cursor] = MULTI_CONVERTER_DISPLAY_CHAR_COMMA;
|
||||
(multi_converter_state->display).cursor++;
|
||||
multi_converter_state->buffer_orig[(multi_converter_state->display).cursor] =
|
||||
MULTI_CONVERTER_DISPLAY_CHAR_COMMA;
|
||||
(multi_converter_state->display).cursor++;
|
||||
}
|
||||
|
||||
void multi_converter_mode_display_add_number(MultiConverterState* const multi_converter_state) {
|
||||
if ((multi_converter_state->display).key > multi_converter_get_unit(multi_converter_state->unit_type_orig).max_number_keys-1) return;
|
||||
if((multi_converter_state->display).key >
|
||||
multi_converter_get_unit(multi_converter_state->unit_type_orig).max_number_keys - 1)
|
||||
return;
|
||||
|
||||
if ((multi_converter_state->display).key < 10) {
|
||||
multi_converter_state->buffer_orig[(multi_converter_state->display).cursor] = (multi_converter_state->display).key + '0';
|
||||
} else {
|
||||
multi_converter_state->buffer_orig[(multi_converter_state->display).cursor] = ((multi_converter_state->display).key - 10) + 'A';
|
||||
}
|
||||
if((multi_converter_state->display).key < 10) {
|
||||
multi_converter_state->buffer_orig[(multi_converter_state->display).cursor] =
|
||||
(multi_converter_state->display).key + '0';
|
||||
} else {
|
||||
multi_converter_state->buffer_orig[(multi_converter_state->display).cursor] =
|
||||
((multi_converter_state->display).key - 10) + 'A';
|
||||
}
|
||||
|
||||
(multi_converter_state->display).cursor++;
|
||||
(multi_converter_state->display).cursor++;
|
||||
}
|
||||
|
||||
MultiConverterModeTrigger multi_converter_mode_display_ok(uint8_t long_press, MultiConverterState* const multi_converter_state) {
|
||||
MultiConverterModeTrigger multi_converter_mode_display_ok(
|
||||
uint8_t long_press,
|
||||
MultiConverterState* const multi_converter_state) {
|
||||
if((multi_converter_state->display).key < MULTI_CONVERTER_DISPLAY_KEY_DEL) {
|
||||
if((multi_converter_state->display).cursor >= MULTI_CONVERTER_NUMBER_DIGITS)
|
||||
return None; // limit reached, ignore
|
||||
|
||||
if ((multi_converter_state->display).key < MULTI_CONVERTER_DISPLAY_KEY_DEL) {
|
||||
if ((multi_converter_state->display).cursor >= MULTI_CONVERTER_NUMBER_DIGITS) return None; // limit reached, ignore
|
||||
// long press on 0 toggle NEGATIVE if allowed, on 1 adds COMMA if allowed
|
||||
if(long_press) {
|
||||
if((multi_converter_state->display).key == MULTI_CONVERTER_DISPLAY_KEY_NEGATIVE) {
|
||||
// toggle negative
|
||||
multi_converter_mode_display_toggle_negative(multi_converter_state);
|
||||
} else if((multi_converter_state->display).key == MULTI_CONVERTER_DISPLAY_KEY_COMMA) {
|
||||
// add comma
|
||||
multi_converter_mode_display_add_comma(multi_converter_state);
|
||||
}
|
||||
|
||||
// long press on 0 toggle NEGATIVE if allowed, on 1 adds COMMA if allowed
|
||||
if (long_press) {
|
||||
} else {
|
||||
// regular keys
|
||||
multi_converter_mode_display_add_number(multi_converter_state);
|
||||
}
|
||||
|
||||
if ((multi_converter_state->display).key == MULTI_CONVERTER_DISPLAY_KEY_NEGATIVE) {
|
||||
// toggle negative
|
||||
multi_converter_mode_display_toggle_negative(multi_converter_state);
|
||||
} else if ((multi_converter_state->display).key == MULTI_CONVERTER_DISPLAY_KEY_COMMA) {
|
||||
// add comma
|
||||
multi_converter_mode_display_add_comma(multi_converter_state);
|
||||
}
|
||||
multi_converter_mode_display_convert(multi_converter_state);
|
||||
|
||||
} else {
|
||||
// regular keys
|
||||
multi_converter_mode_display_add_number(multi_converter_state);
|
||||
}
|
||||
} else if((multi_converter_state->display).key == MULTI_CONVERTER_DISPLAY_KEY_DEL) {
|
||||
if((multi_converter_state->display).cursor > 0) (multi_converter_state->display).cursor--;
|
||||
|
||||
multi_converter_mode_display_convert(multi_converter_state);
|
||||
if(multi_converter_state->buffer_orig[(multi_converter_state->display).cursor] ==
|
||||
MULTI_CONVERTER_DISPLAY_CHAR_COMMA)
|
||||
(multi_converter_state->display).comma = 0;
|
||||
if(multi_converter_state->buffer_orig[(multi_converter_state->display).cursor] ==
|
||||
MULTI_CONVERTER_DISPLAY_CHAR_NEGATIVE)
|
||||
(multi_converter_state->display).negative = 0;
|
||||
|
||||
} else if ((multi_converter_state->display).key == MULTI_CONVERTER_DISPLAY_KEY_DEL) {
|
||||
if ((multi_converter_state->display).cursor > 0) (multi_converter_state->display).cursor--;
|
||||
multi_converter_state->buffer_orig[(multi_converter_state->display).cursor] =
|
||||
MULTI_CONVERTER_DISPLAY_CHAR_BLANK;
|
||||
|
||||
if (multi_converter_state->buffer_orig[(multi_converter_state->display).cursor] == MULTI_CONVERTER_DISPLAY_CHAR_COMMA) (multi_converter_state->display).comma = 0;
|
||||
if (multi_converter_state->buffer_orig[(multi_converter_state->display).cursor] == MULTI_CONVERTER_DISPLAY_CHAR_NEGATIVE) (multi_converter_state->display).negative = 0;
|
||||
multi_converter_mode_display_convert(multi_converter_state);
|
||||
|
||||
multi_converter_state->buffer_orig[(multi_converter_state->display).cursor] = MULTI_CONVERTER_DISPLAY_CHAR_BLANK;
|
||||
|
||||
multi_converter_mode_display_convert(multi_converter_state);
|
||||
|
||||
} else { // MULTI_CONVERTER_DISPLAY_KEY_SELECT
|
||||
return Reset;
|
||||
}
|
||||
|
||||
return None;
|
||||
} else { // MULTI_CONVERTER_DISPLAY_KEY_SELECT
|
||||
return Reset;
|
||||
}
|
||||
|
||||
return None;
|
||||
}
|
||||
@@ -16,12 +16,16 @@ void multi_converter_mode_display_convert(MultiConverterState* const multi_conve
|
||||
//
|
||||
// draw the main DISPLAY view with the current multi_converter_state values
|
||||
//
|
||||
void multi_converter_mode_display_draw(Canvas* const canvas, const MultiConverterState* multi_converter_state);
|
||||
void multi_converter_mode_display_draw(
|
||||
Canvas* const canvas,
|
||||
const MultiConverterState* multi_converter_state);
|
||||
|
||||
//
|
||||
// keyboard navigation on DISPLAY mode (NAVIGATION only, no BACK nor OK - InputKey guaranteed to be left/right/up/down)
|
||||
//
|
||||
void multi_converter_mode_display_navigation(InputKey key, MultiConverterState* const multi_converter_state);
|
||||
void multi_converter_mode_display_navigation(
|
||||
InputKey key,
|
||||
MultiConverterState* const multi_converter_state);
|
||||
|
||||
//
|
||||
// reset the DISPLAY mode with the current units, cleaning the buffers and different flags;
|
||||
@@ -52,4 +56,6 @@ void multi_converter_mode_display_add_number(MultiConverterState* const multi_co
|
||||
// handle the OK action when selecting a specific key on the keyboard (add a number, a symbol, change mode...)
|
||||
// returns a ModeTrigger enum value: may or may not let to a mode change on the main loop (WON'T change the mode here)
|
||||
//
|
||||
MultiConverterModeTrigger multi_converter_mode_display_ok(uint8_t long_press, MultiConverterState* const multi_converter_state);
|
||||
MultiConverterModeTrigger multi_converter_mode_display_ok(
|
||||
uint8_t long_press,
|
||||
MultiConverterState* const multi_converter_state);
|
||||
@@ -1,160 +1,210 @@
|
||||
#include "multi_converter_mode_select.h"
|
||||
|
||||
#define MULTI_CONVERTER_LIST_ENTRIES_COUNT 3
|
||||
#define MULTI_CONVERTER_LIST_ENTRIES_COUNT 3
|
||||
|
||||
#define MULTI_CONVERTER_INFO_STRING_FROM "FROM:"
|
||||
#define MULTI_CONVERTER_INFO_STRING_TO "TO:"
|
||||
#define MULTI_CONVERTER_INFO_STRING_OK "OK: Change"
|
||||
#define MULTI_CONVERTER_INFO_STRING_BACK "BACK: Cancel"
|
||||
#define MULTI_CONVERTER_INFO_STRING_FROM "FROM:"
|
||||
#define MULTI_CONVERTER_INFO_STRING_TO "TO:"
|
||||
#define MULTI_CONVERTER_INFO_STRING_OK "OK: Change"
|
||||
#define MULTI_CONVERTER_INFO_STRING_BACK "BACK: Cancel"
|
||||
|
||||
void multi_converter_mode_select_draw_destination_offset(uint8_t x, uint8_t y, int8_t d, Canvas* const canvas, const MultiConverterState* multi_converter_state) {
|
||||
int i = 1;
|
||||
while (i < MULTI_CONVERTER_AVAILABLE_UNITS) { // in case there's no match, to avoid an endless loop (in theory shouldn't happen, but...)
|
||||
int ut = multi_converter_get_unit_type_offset((multi_converter_state->select).selected_unit_type_dest, i * d);
|
||||
if (
|
||||
multi_converter_available_units[(multi_converter_state->select).selected_unit_type_orig].allowed_function(ut) &&
|
||||
(multi_converter_state->select).selected_unit_type_orig != ut
|
||||
) {
|
||||
canvas_draw_str(canvas, x, y, multi_converter_available_units[ut].name);
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
void multi_converter_mode_select_draw_destination_offset(
|
||||
uint8_t x,
|
||||
uint8_t y,
|
||||
int8_t d,
|
||||
Canvas* const canvas,
|
||||
const MultiConverterState* multi_converter_state) {
|
||||
int i = 1;
|
||||
while(
|
||||
i <
|
||||
MULTI_CONVERTER_AVAILABLE_UNITS) { // in case there's no match, to avoid an endless loop (in theory shouldn't happen, but...)
|
||||
int ut = multi_converter_get_unit_type_offset(
|
||||
(multi_converter_state->select).selected_unit_type_dest, i * d);
|
||||
if(multi_converter_available_units[(multi_converter_state->select).selected_unit_type_orig]
|
||||
.allowed_function(ut) &&
|
||||
(multi_converter_state->select).selected_unit_type_orig != ut) {
|
||||
canvas_draw_str(canvas, x, y, multi_converter_available_units[ut].name);
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
void multi_converter_mode_select_draw_selected_unit(uint8_t x, uint8_t y, MultiConverterUnitType unit_type, Canvas* const canvas) {
|
||||
canvas_draw_box(canvas, x - 2 , y - 10, canvas_string_width(canvas, multi_converter_available_units[unit_type].name) + 4, 13);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
canvas_draw_str(canvas, x, y, multi_converter_available_units[unit_type].name);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
void multi_converter_mode_select_draw_selected_unit(
|
||||
uint8_t x,
|
||||
uint8_t y,
|
||||
MultiConverterUnitType unit_type,
|
||||
Canvas* const canvas) {
|
||||
canvas_draw_box(
|
||||
canvas,
|
||||
x - 2,
|
||||
y - 10,
|
||||
canvas_string_width(canvas, multi_converter_available_units[unit_type].name) + 4,
|
||||
13);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
canvas_draw_str(canvas, x, y, multi_converter_available_units[unit_type].name);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
}
|
||||
|
||||
void multi_converter_mode_select_draw(Canvas* const canvas, const MultiConverterState* multi_converter_state) {
|
||||
void multi_converter_mode_select_draw(
|
||||
Canvas* const canvas,
|
||||
const MultiConverterState* multi_converter_state) {
|
||||
int y = 10;
|
||||
int x = 10;
|
||||
|
||||
int y = 10;
|
||||
int x = 10;
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
// FROM
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str(canvas, x, y, MULTI_CONVERTER_INFO_STRING_FROM);
|
||||
|
||||
// FROM
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str(canvas, x, y, MULTI_CONVERTER_INFO_STRING_FROM);
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
// offset -1
|
||||
y += 12;
|
||||
|
||||
// offset -1
|
||||
y += 12;
|
||||
canvas_draw_str(
|
||||
canvas,
|
||||
x,
|
||||
y,
|
||||
multi_converter_available_units[multi_converter_get_unit_type_offset(
|
||||
(multi_converter_state->select).selected_unit_type_orig,
|
||||
-1)]
|
||||
.name);
|
||||
|
||||
canvas_draw_str(canvas, x, y, multi_converter_available_units[ multi_converter_get_unit_type_offset((multi_converter_state->select).selected_unit_type_orig, -1) ].name);
|
||||
// current selected element
|
||||
y += 12;
|
||||
|
||||
// current selected element
|
||||
y += 12;
|
||||
multi_converter_mode_select_draw_selected_unit(
|
||||
x, y, (multi_converter_state->select).selected_unit_type_orig, canvas);
|
||||
|
||||
multi_converter_mode_select_draw_selected_unit(x, y, (multi_converter_state->select).selected_unit_type_orig, canvas);
|
||||
if((multi_converter_state->select).select_orig) canvas_draw_str(canvas, x - 6, y, ">");
|
||||
|
||||
if ((multi_converter_state->select).select_orig) canvas_draw_str(canvas, x - 6, y, ">");
|
||||
// offset +1
|
||||
y += 12;
|
||||
|
||||
// offset +1
|
||||
y += 12;
|
||||
canvas_draw_str(
|
||||
canvas,
|
||||
x,
|
||||
y,
|
||||
multi_converter_available_units[multi_converter_get_unit_type_offset(
|
||||
(multi_converter_state->select).selected_unit_type_orig,
|
||||
1)]
|
||||
.name);
|
||||
|
||||
canvas_draw_str(canvas, x, y, multi_converter_available_units[ multi_converter_get_unit_type_offset((multi_converter_state->select).selected_unit_type_orig, 1) ].name);
|
||||
// TO
|
||||
y = 10;
|
||||
x = 70;
|
||||
|
||||
// TO
|
||||
y = 10;
|
||||
x = 70;
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str(canvas, x, y, MULTI_CONVERTER_INFO_STRING_TO);
|
||||
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str(canvas, x, y, MULTI_CONVERTER_INFO_STRING_TO);
|
||||
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
|
||||
// offset -1: go back from current selected destination and find the first one valid (even if it's itself)
|
||||
y += 12;
|
||||
// offset -1: go back from current selected destination and find the first one valid (even if it's itself)
|
||||
y += 12;
|
||||
|
||||
multi_converter_mode_select_draw_destination_offset(x, y, -1, canvas, multi_converter_state);
|
||||
multi_converter_mode_select_draw_destination_offset(x, y, -1, canvas, multi_converter_state);
|
||||
|
||||
// current selected element
|
||||
y += 12;
|
||||
// current selected element
|
||||
y += 12;
|
||||
|
||||
multi_converter_mode_select_draw_selected_unit(x, y, (multi_converter_state->select).selected_unit_type_dest, canvas);
|
||||
multi_converter_mode_select_draw_selected_unit(
|
||||
x, y, (multi_converter_state->select).selected_unit_type_dest, canvas);
|
||||
|
||||
if (!(multi_converter_state->select).select_orig) canvas_draw_str(canvas, x - 6, y, ">");
|
||||
if(!(multi_converter_state->select).select_orig) canvas_draw_str(canvas, x - 6, y, ">");
|
||||
|
||||
// offset +1: same but on the opposite direction
|
||||
y += 12;
|
||||
// offset +1: same but on the opposite direction
|
||||
y += 12;
|
||||
|
||||
multi_converter_mode_select_draw_destination_offset(x, y, 1, canvas, multi_converter_state);
|
||||
multi_converter_mode_select_draw_destination_offset(x, y, 1, canvas, multi_converter_state);
|
||||
|
||||
// OK / CANCEL
|
||||
// OK / CANCEL
|
||||
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
canvas_draw_box(canvas, 0, 64 - 12, canvas_string_width(canvas, MULTI_CONVERTER_INFO_STRING_OK) + 4, 12);
|
||||
canvas_draw_box(canvas, 128 - 4 - canvas_string_width(canvas, MULTI_CONVERTER_INFO_STRING_BACK), 64 - 12, canvas_string_width(canvas, "BACK: Cancel") + 4, 12);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
canvas_draw_box(
|
||||
canvas, 0, 64 - 12, canvas_string_width(canvas, MULTI_CONVERTER_INFO_STRING_OK) + 4, 12);
|
||||
canvas_draw_box(
|
||||
canvas,
|
||||
128 - 4 - canvas_string_width(canvas, MULTI_CONVERTER_INFO_STRING_BACK),
|
||||
64 - 12,
|
||||
canvas_string_width(canvas, "BACK: Cancel") + 4,
|
||||
12);
|
||||
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
canvas_draw_str(canvas, 2, 64 - 3, MULTI_CONVERTER_INFO_STRING_OK);
|
||||
canvas_draw_str(canvas, 128 - 2 - canvas_string_width(canvas, MULTI_CONVERTER_INFO_STRING_BACK), 64 - 3, MULTI_CONVERTER_INFO_STRING_BACK);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
canvas_draw_str(canvas, 2, 64 - 3, MULTI_CONVERTER_INFO_STRING_OK);
|
||||
canvas_draw_str(
|
||||
canvas,
|
||||
128 - 2 - canvas_string_width(canvas, MULTI_CONVERTER_INFO_STRING_BACK),
|
||||
64 - 3,
|
||||
MULTI_CONVERTER_INFO_STRING_BACK);
|
||||
}
|
||||
|
||||
void multi_converter_mode_select_reset(MultiConverterState* const multi_converter_state) {
|
||||
// initial pre-selected values are equal to the current selected values
|
||||
(multi_converter_state->select).selected_unit_type_orig =
|
||||
multi_converter_state->unit_type_orig;
|
||||
(multi_converter_state->select).selected_unit_type_dest =
|
||||
multi_converter_state->unit_type_dest;
|
||||
|
||||
// initial pre-selected values are equal to the current selected values
|
||||
(multi_converter_state->select).selected_unit_type_orig = multi_converter_state->unit_type_orig;
|
||||
(multi_converter_state->select).selected_unit_type_dest = multi_converter_state->unit_type_dest;
|
||||
|
||||
(multi_converter_state->select).select_orig = 1;
|
||||
(multi_converter_state->select).select_orig = 1;
|
||||
}
|
||||
|
||||
MultiConverterModeTrigger multi_converter_mode_select_exit(uint8_t save_changes, MultiConverterState* const multi_converter_state) {
|
||||
if (save_changes) {
|
||||
MultiConverterModeTrigger multi_converter_mode_select_exit(
|
||||
uint8_t save_changes,
|
||||
MultiConverterState* const multi_converter_state) {
|
||||
if(save_changes) {
|
||||
multi_converter_state->unit_type_dest =
|
||||
(multi_converter_state->select).selected_unit_type_dest;
|
||||
|
||||
multi_converter_state->unit_type_dest = (multi_converter_state->select).selected_unit_type_dest;
|
||||
if(multi_converter_state->unit_type_orig ==
|
||||
(multi_converter_state->select).selected_unit_type_orig) {
|
||||
// if the ORIGIN unit didn't changed, just trigger the convert
|
||||
|
||||
if (multi_converter_state->unit_type_orig == (multi_converter_state->select).selected_unit_type_orig) {
|
||||
// if the ORIGIN unit didn't changed, just trigger the convert
|
||||
return Convert;
|
||||
} else {
|
||||
multi_converter_state->unit_type_orig =
|
||||
(multi_converter_state->select).selected_unit_type_orig;
|
||||
multi_converter_state->unit_type_dest =
|
||||
(multi_converter_state->select).selected_unit_type_dest;
|
||||
|
||||
return Convert;
|
||||
} else {
|
||||
multi_converter_state->unit_type_orig = (multi_converter_state->select).selected_unit_type_orig;
|
||||
multi_converter_state->unit_type_dest = (multi_converter_state->select).selected_unit_type_dest;
|
||||
return Reset;
|
||||
}
|
||||
}
|
||||
|
||||
return Reset;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return None;
|
||||
return None;
|
||||
}
|
||||
|
||||
void multi_converter_mode_select_switch(MultiConverterState* const multi_converter_state) {
|
||||
(multi_converter_state->select).select_orig ^= 1;
|
||||
(multi_converter_state->select).select_orig ^= 1;
|
||||
}
|
||||
|
||||
void multi_converter_mode_select_change_unit(int8_t direction, MultiConverterState* const multi_converter_state) {
|
||||
void multi_converter_mode_select_change_unit(
|
||||
int8_t direction,
|
||||
MultiConverterState* const multi_converter_state) {
|
||||
MultiConverterUnitType d;
|
||||
if((multi_converter_state->select).select_orig) {
|
||||
(multi_converter_state->select).selected_unit_type_orig =
|
||||
multi_converter_get_unit_type_offset(
|
||||
(multi_converter_state->select).selected_unit_type_orig, direction);
|
||||
d = (multi_converter_state->select).selected_unit_type_dest;
|
||||
} else {
|
||||
d = ((multi_converter_state->select).selected_unit_type_dest + direction) %
|
||||
MULTI_CONVERTER_AVAILABLE_UNITS;
|
||||
}
|
||||
|
||||
MultiConverterUnitType d;
|
||||
if ((multi_converter_state->select).select_orig) {
|
||||
(multi_converter_state->select).selected_unit_type_orig = multi_converter_get_unit_type_offset((multi_converter_state->select).selected_unit_type_orig, direction);
|
||||
d = (multi_converter_state->select).selected_unit_type_dest;
|
||||
} else {
|
||||
d = ((multi_converter_state->select).selected_unit_type_dest + direction) % MULTI_CONVERTER_AVAILABLE_UNITS;
|
||||
}
|
||||
|
||||
// check each unit with the ORIGIN allowed_function() to make sure we're selecting a valid DESTINATION
|
||||
// (when changing the ORIGIN unit the DIRECTION in which we'll switch the DESTINATION will be the SAME);
|
||||
// also notice that ORIGIN must be DIFFERENT than DESTINATION
|
||||
int i = 0;
|
||||
while (i < MULTI_CONVERTER_AVAILABLE_UNITS) {
|
||||
if (
|
||||
multi_converter_available_units[(multi_converter_state->select).selected_unit_type_orig].allowed_function(d) &&
|
||||
(multi_converter_state->select).selected_unit_type_orig != d
|
||||
) {
|
||||
(multi_converter_state->select).selected_unit_type_dest = d;
|
||||
break;
|
||||
}
|
||||
|
||||
d = multi_converter_get_unit_type_offset(d, direction);
|
||||
i++;
|
||||
}
|
||||
// check each unit with the ORIGIN allowed_function() to make sure we're selecting a valid DESTINATION
|
||||
// (when changing the ORIGIN unit the DIRECTION in which we'll switch the DESTINATION will be the SAME);
|
||||
// also notice that ORIGIN must be DIFFERENT than DESTINATION
|
||||
int i = 0;
|
||||
while(i < MULTI_CONVERTER_AVAILABLE_UNITS) {
|
||||
if(multi_converter_available_units[(multi_converter_state->select).selected_unit_type_orig]
|
||||
.allowed_function(d) &&
|
||||
(multi_converter_state->select).selected_unit_type_orig != d) {
|
||||
(multi_converter_state->select).selected_unit_type_dest = d;
|
||||
break;
|
||||
}
|
||||
|
||||
d = multi_converter_get_unit_type_offset(d, direction);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,14 +10,25 @@
|
||||
//
|
||||
// aux draw function for units offsets and draw stuff
|
||||
//
|
||||
void multi_converter_mode_select_draw_destination_offset(uint8_t x, uint8_t y, int8_t d, Canvas* const canvas, const MultiConverterState* multi_converter_state);
|
||||
void multi_converter_mode_select_draw_destination_offset(
|
||||
uint8_t x,
|
||||
uint8_t y,
|
||||
int8_t d,
|
||||
Canvas* const canvas,
|
||||
const MultiConverterState* multi_converter_state);
|
||||
|
||||
void multi_converter_mode_select_draw_selected_unit(uint8_t x, uint8_t y, MultiConverterUnitType unit_type, Canvas* const canvas);
|
||||
void multi_converter_mode_select_draw_selected_unit(
|
||||
uint8_t x,
|
||||
uint8_t y,
|
||||
MultiConverterUnitType unit_type,
|
||||
Canvas* const canvas);
|
||||
|
||||
//
|
||||
// draw the main SELECT view with the current multi_converter_state values
|
||||
//
|
||||
void multi_converter_mode_select_draw(Canvas* const canvas, const MultiConverterState* multi_converter_state);
|
||||
void multi_converter_mode_select_draw(
|
||||
Canvas* const canvas,
|
||||
const MultiConverterState* multi_converter_state);
|
||||
|
||||
//
|
||||
// reset the SELECT mode view, showing as "pre-selected" the current working units
|
||||
@@ -33,11 +44,13 @@ void multi_converter_mode_select_reset(MultiConverterState* const multi_converte
|
||||
// prevent weird behaviours, so for now we're trusting the selected_unit_orig/dest_type values)
|
||||
//
|
||||
// returns an enum code MultiConverterDisplayTrigger based on doing nothing (cancel), triggering the display
|
||||
// convert method or reseting the whole display mode (when fully changing the units)
|
||||
// convert method or reseting the whole display mode (when fully changing the units)
|
||||
//
|
||||
// notice the MODE CHANGE itself is not done here but in the main loop (outside the call) via the ModeTrigger enum element
|
||||
//
|
||||
MultiConverterModeTrigger multi_converter_mode_select_exit(uint8_t save_changes, MultiConverterState* const multi_converter_state);
|
||||
MultiConverterModeTrigger multi_converter_mode_select_exit(
|
||||
uint8_t save_changes,
|
||||
MultiConverterState* const multi_converter_state);
|
||||
|
||||
//
|
||||
// switch between selecting the ORIGIN or the DESTINATION unit on DISPLAY mode (since there're only
|
||||
@@ -48,11 +61,13 @@ void multi_converter_mode_select_switch(MultiConverterState* const multi_convert
|
||||
//
|
||||
// change the selected unit on SELECTED mode, using the select_orig flag to check if we're switching the
|
||||
// ORIGIN or the DESTINATION unit; the DIRECTION (up or down to travel the array) is set as a param
|
||||
//
|
||||
//
|
||||
// when switching the ORIGIN one, reset the DESTINATION to the first valid unit (if the current one is not
|
||||
// valid anymore); when switching the DESTINATION one, an allowed_function() check is performed in order to
|
||||
// properly set a valid destination unit.
|
||||
//
|
||||
// (notice the draw step also perform which units are valid to display, so no worries about that here)
|
||||
//
|
||||
void multi_converter_mode_select_change_unit(int8_t direction, MultiConverterState* const multi_converter_state);
|
||||
void multi_converter_mode_select_change_unit(
|
||||
int8_t direction,
|
||||
MultiConverterState* const multi_converter_state);
|
||||
|
||||
@@ -1,124 +1,126 @@
|
||||
#include "multi_converter_units.h"
|
||||
|
||||
#define MULTI_CONVERTER_CHAR_OVERFLOW '#'
|
||||
#define MULTI_CONVERTER_MAX_SUPORTED_INT 999999999
|
||||
#define MULTI_CONVERTER_CHAR_OVERFLOW '#'
|
||||
#define MULTI_CONVERTER_MAX_SUPORTED_INT 999999999
|
||||
|
||||
#define multi_converter_unit_set_overflow(b) for (int _i = 0; _i < MULTI_CONVERTER_NUMBER_DIGITS; _i++) b[_i] = MULTI_CONVERTER_CHAR_OVERFLOW;
|
||||
#define multi_converter_unit_set_overflow(b) \
|
||||
for(int _i = 0; _i < MULTI_CONVERTER_NUMBER_DIGITS; _i++) \
|
||||
b[_i] = MULTI_CONVERTER_CHAR_OVERFLOW;
|
||||
|
||||
//
|
||||
// DEC / HEX / BIN conversion
|
||||
//
|
||||
//
|
||||
void multi_converter_unit_dec_hex_bin_convert(MultiConverterState* const multi_converter_state) {
|
||||
char dest[MULTI_CONVERTER_NUMBER_DIGITS];
|
||||
|
||||
char dest[MULTI_CONVERTER_NUMBER_DIGITS];
|
||||
int i = 0;
|
||||
uint8_t overflow = 0;
|
||||
|
||||
int i = 0;
|
||||
uint8_t overflow = 0;
|
||||
|
||||
int a = 0;
|
||||
int r = 0;
|
||||
uint8_t f = 1;
|
||||
int a = 0;
|
||||
int r = 0;
|
||||
uint8_t f = 1;
|
||||
|
||||
switch(multi_converter_state->unit_type_orig) {
|
||||
default:
|
||||
break;
|
||||
case UnitTypeDec: {
|
||||
a = atoi(multi_converter_state->buffer_orig);
|
||||
f = (multi_converter_state->unit_type_dest == UnitTypeHex ? 16 : 2);
|
||||
switch(multi_converter_state->unit_type_orig) {
|
||||
default:
|
||||
break;
|
||||
case UnitTypeDec: {
|
||||
a = atoi(multi_converter_state->buffer_orig);
|
||||
f = (multi_converter_state->unit_type_dest == UnitTypeHex ? 16 : 2);
|
||||
|
||||
break;
|
||||
}
|
||||
case UnitTypeHex:
|
||||
a = strtol(multi_converter_state->buffer_orig, NULL, 16);
|
||||
f = (multi_converter_state->unit_type_dest == UnitTypeDec ? 10 : 2);
|
||||
break;
|
||||
}
|
||||
case UnitTypeHex:
|
||||
a = strtol(multi_converter_state->buffer_orig, NULL, 16);
|
||||
f = (multi_converter_state->unit_type_dest == UnitTypeDec ? 10 : 2);
|
||||
|
||||
break;
|
||||
case UnitTypeBin:
|
||||
a = strtol(multi_converter_state->buffer_orig, NULL, 2);
|
||||
f = (multi_converter_state->unit_type_dest == UnitTypeDec ? 10 : 16);
|
||||
break;
|
||||
case UnitTypeBin:
|
||||
a = strtol(multi_converter_state->buffer_orig, NULL, 2);
|
||||
f = (multi_converter_state->unit_type_dest == UnitTypeDec ? 10 : 16);
|
||||
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
while (a > 0) {
|
||||
r = a % f;
|
||||
dest[i] = r + (r < 10 ? '0' : ('A' - 10) );
|
||||
a /= f;
|
||||
if (i++ >= MULTI_CONVERTER_NUMBER_DIGITS) {
|
||||
overflow = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (overflow) {
|
||||
multi_converter_unit_set_overflow(multi_converter_state->buffer_dest);
|
||||
} else {
|
||||
// copy DEST (reversed) to destination and append empty chars at the end
|
||||
for (int j = 0; j < MULTI_CONVERTER_NUMBER_DIGITS; j++) {
|
||||
if (i >= 1) multi_converter_state->buffer_dest[j] = dest[--i];
|
||||
else multi_converter_state->buffer_dest[j] = ' ';
|
||||
}
|
||||
}
|
||||
while(a > 0) {
|
||||
r = a % f;
|
||||
dest[i] = r + (r < 10 ? '0' : ('A' - 10));
|
||||
a /= f;
|
||||
if(i++ >= MULTI_CONVERTER_NUMBER_DIGITS) {
|
||||
overflow = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(overflow) {
|
||||
multi_converter_unit_set_overflow(multi_converter_state->buffer_dest);
|
||||
} else {
|
||||
// copy DEST (reversed) to destination and append empty chars at the end
|
||||
for(int j = 0; j < MULTI_CONVERTER_NUMBER_DIGITS; j++) {
|
||||
if(i >= 1)
|
||||
multi_converter_state->buffer_dest[j] = dest[--i];
|
||||
else
|
||||
multi_converter_state->buffer_dest[j] = ' ';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t multi_converter_unit_dec_hex_bin_allowed(MultiConverterUnitType unit_type) {
|
||||
return (unit_type == UnitTypeDec || unit_type == UnitTypeHex || unit_type == UnitTypeBin);
|
||||
return (unit_type == UnitTypeDec || unit_type == UnitTypeHex || unit_type == UnitTypeBin);
|
||||
}
|
||||
|
||||
//
|
||||
// CEL / FAR / KEL
|
||||
//
|
||||
void multi_converter_unit_temperature_convert(MultiConverterState* const multi_converter_state) {
|
||||
double a = strtof(multi_converter_state->buffer_orig, NULL);
|
||||
uint8_t overflow = 0;
|
||||
|
||||
double a = strtof(multi_converter_state->buffer_orig, NULL);
|
||||
uint8_t overflow = 0;
|
||||
switch(multi_converter_state->unit_type_orig) {
|
||||
default:
|
||||
break;
|
||||
case UnitTypeCelsius:
|
||||
if(multi_converter_state->unit_type_dest == UnitTypeFahernheit) {
|
||||
// celsius to fahrenheit
|
||||
a = (a * ((double)1.8)) + 32;
|
||||
} else { // UnitTypeKelvin
|
||||
a += ((double)273.15);
|
||||
}
|
||||
|
||||
switch(multi_converter_state->unit_type_orig) {
|
||||
default:
|
||||
break;
|
||||
case UnitTypeCelsius:
|
||||
if (multi_converter_state->unit_type_dest == UnitTypeFahernheit) {
|
||||
// celsius to fahrenheit
|
||||
a = (a * ((double) 1.8)) + 32;
|
||||
} else { // UnitTypeKelvin
|
||||
a += ((double) 273.15);
|
||||
}
|
||||
break;
|
||||
case UnitTypeFahernheit:
|
||||
// fahrenheit to celsius, always
|
||||
a = (a - 32) / ((double)1.8);
|
||||
if(multi_converter_state->unit_type_dest == UnitTypeKelvin) {
|
||||
// if kelvin, add
|
||||
a += ((double)273.15);
|
||||
}
|
||||
|
||||
break;
|
||||
case UnitTypeFahernheit:
|
||||
// fahrenheit to celsius, always
|
||||
a = (a - 32) / ((double) 1.8);
|
||||
if (multi_converter_state->unit_type_dest == UnitTypeKelvin) {
|
||||
// if kelvin, add
|
||||
a += ((double) 273.15);
|
||||
}
|
||||
break;
|
||||
case UnitTypeKelvin:
|
||||
// kelvin to celsius, always
|
||||
a -= ((double)273.15);
|
||||
if(multi_converter_state->unit_type_dest == UnitTypeFahernheit) {
|
||||
// if fahernheit, convert
|
||||
a = (a * ((double)1.8)) + 32;
|
||||
}
|
||||
|
||||
break;
|
||||
case UnitTypeKelvin:
|
||||
// kelvin to celsius, always
|
||||
a -= ((double) 273.15);
|
||||
if (multi_converter_state->unit_type_dest == UnitTypeFahernheit) {
|
||||
// if fahernheit, convert
|
||||
a = (a * ((double) 1.8)) + 32;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (overflow) {
|
||||
multi_converter_unit_set_overflow(multi_converter_state->buffer_dest);
|
||||
} else {
|
||||
|
||||
int ret = snprintf(multi_converter_state->buffer_dest, MULTI_CONVERTER_NUMBER_DIGITS + 1, "%.3lf", a);
|
||||
|
||||
if (ret < 0) multi_converter_unit_set_overflow(multi_converter_state->buffer_dest);
|
||||
}
|
||||
if(overflow) {
|
||||
multi_converter_unit_set_overflow(multi_converter_state->buffer_dest);
|
||||
} else {
|
||||
int ret = snprintf(
|
||||
multi_converter_state->buffer_dest, MULTI_CONVERTER_NUMBER_DIGITS + 1, "%.3lf", a);
|
||||
|
||||
if(ret < 0) multi_converter_unit_set_overflow(multi_converter_state->buffer_dest);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t multi_converter_unit_temperature_allowed(MultiConverterUnitType unit_type) {
|
||||
return (unit_type == UnitTypeCelsius || unit_type == UnitTypeFahernheit || unit_type == UnitTypeKelvin);
|
||||
return (
|
||||
unit_type == UnitTypeCelsius || unit_type == UnitTypeFahernheit ||
|
||||
unit_type == UnitTypeKelvin);
|
||||
}
|
||||
|
||||
//
|
||||
@@ -126,73 +128,102 @@ uint8_t multi_converter_unit_temperature_allowed(MultiConverterUnitType unit_typ
|
||||
//
|
||||
|
||||
void multi_converter_unit_distance_convert(MultiConverterState* const multi_converter_state) {
|
||||
double a = strtof(multi_converter_state->buffer_orig, NULL);
|
||||
uint8_t overflow = 0;
|
||||
double a = strtof(multi_converter_state->buffer_orig, NULL);
|
||||
uint8_t overflow = 0;
|
||||
|
||||
switch(multi_converter_state->unit_type_orig) {
|
||||
default:
|
||||
break;
|
||||
case UnitTypeKilometers:
|
||||
if (multi_converter_state->unit_type_dest == UnitTypeMeters) a *= ((double) 1000);
|
||||
else if (multi_converter_state->unit_type_dest == UnitTypeCentimeters) a *= ((double) 100000);
|
||||
else if (multi_converter_state->unit_type_dest == UnitTypeMiles) a *= ((double) 0.6213711);
|
||||
else if (multi_converter_state->unit_type_dest == UnitTypeFeet) a *= ((double) 3280.839895013);
|
||||
else if (multi_converter_state->unit_type_dest == UnitTypeInches) a *= ((double) 39370.078740157);
|
||||
break;
|
||||
case UnitTypeMeters:
|
||||
if (multi_converter_state->unit_type_dest == UnitTypeKilometers) a /= ((double) 1000);
|
||||
else if (multi_converter_state->unit_type_dest == UnitTypeCentimeters) a *= ((double) 100);
|
||||
else if (multi_converter_state->unit_type_dest == UnitTypeMiles) a *= ((double) 0.0006213711);
|
||||
else if (multi_converter_state->unit_type_dest == UnitTypeFeet) a *= ((double) 3.280839895013);
|
||||
else if (multi_converter_state->unit_type_dest == UnitTypeInches) a *= ((double) 39.370078740157);
|
||||
break;
|
||||
case UnitTypeCentimeters:
|
||||
if (multi_converter_state->unit_type_dest == UnitTypeKilometers) a /= ((double) 100000);
|
||||
else if (multi_converter_state->unit_type_dest == UnitTypeMeters) a /= ((double) 100);
|
||||
else if (multi_converter_state->unit_type_dest == UnitTypeMiles) a *= ((double) 0.000006213711);
|
||||
else if (multi_converter_state->unit_type_dest == UnitTypeFeet) a *= ((double) 0.03280839895013);
|
||||
else if (multi_converter_state->unit_type_dest == UnitTypeInches) a *= ((double) 0.39370078740157);
|
||||
break;
|
||||
switch(multi_converter_state->unit_type_orig) {
|
||||
default:
|
||||
break;
|
||||
case UnitTypeKilometers:
|
||||
if(multi_converter_state->unit_type_dest == UnitTypeMeters)
|
||||
a *= ((double)1000);
|
||||
else if(multi_converter_state->unit_type_dest == UnitTypeCentimeters)
|
||||
a *= ((double)100000);
|
||||
else if(multi_converter_state->unit_type_dest == UnitTypeMiles)
|
||||
a *= ((double)0.6213711);
|
||||
else if(multi_converter_state->unit_type_dest == UnitTypeFeet)
|
||||
a *= ((double)3280.839895013);
|
||||
else if(multi_converter_state->unit_type_dest == UnitTypeInches)
|
||||
a *= ((double)39370.078740157);
|
||||
break;
|
||||
case UnitTypeMeters:
|
||||
if(multi_converter_state->unit_type_dest == UnitTypeKilometers)
|
||||
a /= ((double)1000);
|
||||
else if(multi_converter_state->unit_type_dest == UnitTypeCentimeters)
|
||||
a *= ((double)100);
|
||||
else if(multi_converter_state->unit_type_dest == UnitTypeMiles)
|
||||
a *= ((double)0.0006213711);
|
||||
else if(multi_converter_state->unit_type_dest == UnitTypeFeet)
|
||||
a *= ((double)3.280839895013);
|
||||
else if(multi_converter_state->unit_type_dest == UnitTypeInches)
|
||||
a *= ((double)39.370078740157);
|
||||
break;
|
||||
case UnitTypeCentimeters:
|
||||
if(multi_converter_state->unit_type_dest == UnitTypeKilometers)
|
||||
a /= ((double)100000);
|
||||
else if(multi_converter_state->unit_type_dest == UnitTypeMeters)
|
||||
a /= ((double)100);
|
||||
else if(multi_converter_state->unit_type_dest == UnitTypeMiles)
|
||||
a *= ((double)0.000006213711);
|
||||
else if(multi_converter_state->unit_type_dest == UnitTypeFeet)
|
||||
a *= ((double)0.03280839895013);
|
||||
else if(multi_converter_state->unit_type_dest == UnitTypeInches)
|
||||
a *= ((double)0.39370078740157);
|
||||
break;
|
||||
|
||||
case UnitTypeMiles:
|
||||
if (multi_converter_state->unit_type_dest == UnitTypeKilometers) a *= ((double) 1.609344);
|
||||
else if (multi_converter_state->unit_type_dest == UnitTypeMeters) a *= ((double) 1609.344);
|
||||
else if (multi_converter_state->unit_type_dest == UnitTypeCentimeters) a *= ((double) 160934.4);
|
||||
else if (multi_converter_state->unit_type_dest == UnitTypeFeet) a *= ((double) 5280);
|
||||
else if (multi_converter_state->unit_type_dest == UnitTypeInches) a *= ((double) 63360);
|
||||
break;
|
||||
case UnitTypeFeet:
|
||||
if (multi_converter_state->unit_type_dest == UnitTypeKilometers) a *= ((double) 0.0003048);
|
||||
else if (multi_converter_state->unit_type_dest == UnitTypeMeters) a *= ((double) 0.3048);
|
||||
else if (multi_converter_state->unit_type_dest == UnitTypeCentimeters) a *= ((double) 30.48);
|
||||
else if (multi_converter_state->unit_type_dest == UnitTypeMiles) a *= ((double) 0.000189393939394);
|
||||
else if (multi_converter_state->unit_type_dest == UnitTypeInches) a *= ((double) 12);
|
||||
break;
|
||||
case UnitTypeInches:
|
||||
if (multi_converter_state->unit_type_dest == UnitTypeKilometers) a *= ((double) 0.0000254);
|
||||
else if (multi_converter_state->unit_type_dest == UnitTypeMeters) a *= ((double) 0.0254);
|
||||
else if (multi_converter_state->unit_type_dest == UnitTypeCentimeters) a *= ((double) 2.54);
|
||||
else if (multi_converter_state->unit_type_dest == UnitTypeMiles) a *= ((double) 0.0000157828282828);
|
||||
else if (multi_converter_state->unit_type_dest == UnitTypeFeet) a *= ((double) 0.0833333333333);
|
||||
break;
|
||||
case UnitTypeMiles:
|
||||
if(multi_converter_state->unit_type_dest == UnitTypeKilometers)
|
||||
a *= ((double)1.609344);
|
||||
else if(multi_converter_state->unit_type_dest == UnitTypeMeters)
|
||||
a *= ((double)1609.344);
|
||||
else if(multi_converter_state->unit_type_dest == UnitTypeCentimeters)
|
||||
a *= ((double)160934.4);
|
||||
else if(multi_converter_state->unit_type_dest == UnitTypeFeet)
|
||||
a *= ((double)5280);
|
||||
else if(multi_converter_state->unit_type_dest == UnitTypeInches)
|
||||
a *= ((double)63360);
|
||||
break;
|
||||
case UnitTypeFeet:
|
||||
if(multi_converter_state->unit_type_dest == UnitTypeKilometers)
|
||||
a *= ((double)0.0003048);
|
||||
else if(multi_converter_state->unit_type_dest == UnitTypeMeters)
|
||||
a *= ((double)0.3048);
|
||||
else if(multi_converter_state->unit_type_dest == UnitTypeCentimeters)
|
||||
a *= ((double)30.48);
|
||||
else if(multi_converter_state->unit_type_dest == UnitTypeMiles)
|
||||
a *= ((double)0.000189393939394);
|
||||
else if(multi_converter_state->unit_type_dest == UnitTypeInches)
|
||||
a *= ((double)12);
|
||||
break;
|
||||
case UnitTypeInches:
|
||||
if(multi_converter_state->unit_type_dest == UnitTypeKilometers)
|
||||
a *= ((double)0.0000254);
|
||||
else if(multi_converter_state->unit_type_dest == UnitTypeMeters)
|
||||
a *= ((double)0.0254);
|
||||
else if(multi_converter_state->unit_type_dest == UnitTypeCentimeters)
|
||||
a *= ((double)2.54);
|
||||
else if(multi_converter_state->unit_type_dest == UnitTypeMiles)
|
||||
a *= ((double)0.0000157828282828);
|
||||
else if(multi_converter_state->unit_type_dest == UnitTypeFeet)
|
||||
a *= ((double)0.0833333333333);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
if(overflow) {
|
||||
multi_converter_unit_set_overflow(multi_converter_state->buffer_dest);
|
||||
} else {
|
||||
int ret = snprintf(
|
||||
multi_converter_state->buffer_dest, MULTI_CONVERTER_NUMBER_DIGITS + 1, "%lf", a);
|
||||
|
||||
if (overflow) {
|
||||
multi_converter_unit_set_overflow(multi_converter_state->buffer_dest);
|
||||
} else {
|
||||
|
||||
int ret = snprintf(multi_converter_state->buffer_dest, MULTI_CONVERTER_NUMBER_DIGITS + 1, "%lf", a);
|
||||
|
||||
if (ret < 0) multi_converter_unit_set_overflow(multi_converter_state->buffer_dest);
|
||||
}
|
||||
if(ret < 0) multi_converter_unit_set_overflow(multi_converter_state->buffer_dest);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t multi_converter_unit_distance_allowed(MultiConverterUnitType unit_type) {
|
||||
return (
|
||||
unit_type == UnitTypeKilometers || unit_type == UnitTypeMeters || unit_type == UnitTypeCentimeters ||
|
||||
unit_type == UnitTypeMiles || unit_type == UnitTypeFeet || unit_type == UnitTypeInches
|
||||
);
|
||||
return (
|
||||
unit_type == UnitTypeKilometers || unit_type == UnitTypeMeters ||
|
||||
unit_type == UnitTypeCentimeters || unit_type == UnitTypeMiles ||
|
||||
unit_type == UnitTypeFeet || unit_type == UnitTypeInches);
|
||||
}
|
||||
|
||||
//
|
||||
@@ -200,31 +231,31 @@ uint8_t multi_converter_unit_distance_allowed(MultiConverterUnitType unit_type)
|
||||
//
|
||||
|
||||
void multi_converter_unit_angle_convert(MultiConverterState* const multi_converter_state) {
|
||||
double a = strtof(multi_converter_state->buffer_orig, NULL);
|
||||
uint8_t overflow = 0;
|
||||
double a = strtof(multi_converter_state->buffer_orig, NULL);
|
||||
uint8_t overflow = 0;
|
||||
|
||||
switch(multi_converter_state->unit_type_orig) {
|
||||
default:
|
||||
break;
|
||||
case UnitTypeDegree:
|
||||
if (multi_converter_state->unit_type_dest == UnitTypeRadian) a *= ((double) 0.0174532925199);
|
||||
break;
|
||||
switch(multi_converter_state->unit_type_orig) {
|
||||
default:
|
||||
break;
|
||||
case UnitTypeDegree:
|
||||
if(multi_converter_state->unit_type_dest == UnitTypeRadian) a *= ((double)0.0174532925199);
|
||||
break;
|
||||
|
||||
case UnitTypeRadian:
|
||||
if (multi_converter_state->unit_type_dest == UnitTypeDegree) a *= ((double) 57.2957795131);
|
||||
break;
|
||||
}
|
||||
case UnitTypeRadian:
|
||||
if(multi_converter_state->unit_type_dest == UnitTypeDegree) a *= ((double)57.2957795131);
|
||||
break;
|
||||
}
|
||||
|
||||
if (overflow) {
|
||||
multi_converter_unit_set_overflow(multi_converter_state->buffer_dest);
|
||||
} else {
|
||||
if(overflow) {
|
||||
multi_converter_unit_set_overflow(multi_converter_state->buffer_dest);
|
||||
} else {
|
||||
int ret = snprintf(
|
||||
multi_converter_state->buffer_dest, MULTI_CONVERTER_NUMBER_DIGITS + 1, "%lf", a);
|
||||
|
||||
int ret = snprintf(multi_converter_state->buffer_dest, MULTI_CONVERTER_NUMBER_DIGITS + 1, "%lf", a);
|
||||
|
||||
if (ret < 0) multi_converter_unit_set_overflow(multi_converter_state->buffer_dest);
|
||||
}
|
||||
if(ret < 0) multi_converter_unit_set_overflow(multi_converter_state->buffer_dest);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t multi_converter_unit_angle_allowed(MultiConverterUnitType unit_type) {
|
||||
return (unit_type == UnitTypeDegree || unit_type == UnitTypeRadian);
|
||||
return (unit_type == UnitTypeDegree || unit_type == UnitTypeRadian);
|
||||
}
|
||||
@@ -8,7 +8,9 @@
|
||||
#define MULTI_CONVERTER_AVAILABLE_UNITS 14
|
||||
|
||||
#define multi_converter_get_unit(unit_type) multi_converter_available_units[unit_type]
|
||||
#define multi_converter_get_unit_type_offset(unit_type, offset) (((unit_type + offset) % MULTI_CONVERTER_AVAILABLE_UNITS + MULTI_CONVERTER_AVAILABLE_UNITS) % MULTI_CONVERTER_AVAILABLE_UNITS)
|
||||
#define multi_converter_get_unit_type_offset(unit_type, offset) \
|
||||
(((unit_type + offset) % MULTI_CONVERTER_AVAILABLE_UNITS + MULTI_CONVERTER_AVAILABLE_UNITS) % \
|
||||
MULTI_CONVERTER_AVAILABLE_UNITS)
|
||||
// the modulo operation will fail with extremely large values on the units array
|
||||
|
||||
// DEC / HEX / BIN
|
||||
@@ -31,41 +33,139 @@ uint8_t multi_converter_unit_angle_allowed(MultiConverterUnitType unit_type);
|
||||
// each unit is made of comma? + negative? + keyboard_length + mini_name + name + convert function + allowed function
|
||||
// (setting functions as NULL will cause convert / select options to be ignored)
|
||||
//
|
||||
static const MultiConverterUnit multi_converter_unit_dec = { 0, 0, 10, "DEC\0", "Decimal\0", multi_converter_unit_dec_hex_bin_convert, multi_converter_unit_dec_hex_bin_allowed };
|
||||
static const MultiConverterUnit multi_converter_unit_hex = { 0, 0, 16, "HEX\0", "Hexadecimal\0", multi_converter_unit_dec_hex_bin_convert, multi_converter_unit_dec_hex_bin_allowed };
|
||||
static const MultiConverterUnit multi_converter_unit_bin = { 0, 0, 2, "BIN\0", "Binary\0", multi_converter_unit_dec_hex_bin_convert, multi_converter_unit_dec_hex_bin_allowed };
|
||||
static const MultiConverterUnit multi_converter_unit_dec = {
|
||||
0,
|
||||
0,
|
||||
10,
|
||||
"DEC\0",
|
||||
"Decimal\0",
|
||||
multi_converter_unit_dec_hex_bin_convert,
|
||||
multi_converter_unit_dec_hex_bin_allowed};
|
||||
static const MultiConverterUnit multi_converter_unit_hex = {
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
"HEX\0",
|
||||
"Hexadecimal\0",
|
||||
multi_converter_unit_dec_hex_bin_convert,
|
||||
multi_converter_unit_dec_hex_bin_allowed};
|
||||
static const MultiConverterUnit multi_converter_unit_bin = {
|
||||
0,
|
||||
0,
|
||||
2,
|
||||
"BIN\0",
|
||||
"Binary\0",
|
||||
multi_converter_unit_dec_hex_bin_convert,
|
||||
multi_converter_unit_dec_hex_bin_allowed};
|
||||
|
||||
static const MultiConverterUnit multi_converter_unit_cel = { 1, 1, 10, "CEL\0", "Celsius\0", multi_converter_unit_temperature_convert, multi_converter_unit_temperature_allowed };
|
||||
static const MultiConverterUnit multi_converter_unit_far = { 1, 1, 10, "FAR\0", "Fahernheit\0", multi_converter_unit_temperature_convert, multi_converter_unit_temperature_allowed };
|
||||
static const MultiConverterUnit multi_converter_unit_kel = { 1, 1, 10, "KEL\0", "Kelvin\0", multi_converter_unit_temperature_convert, multi_converter_unit_temperature_allowed };
|
||||
static const MultiConverterUnit multi_converter_unit_cel = {
|
||||
1,
|
||||
1,
|
||||
10,
|
||||
"CEL\0",
|
||||
"Celsius\0",
|
||||
multi_converter_unit_temperature_convert,
|
||||
multi_converter_unit_temperature_allowed};
|
||||
static const MultiConverterUnit multi_converter_unit_far = {
|
||||
1,
|
||||
1,
|
||||
10,
|
||||
"FAR\0",
|
||||
"Fahernheit\0",
|
||||
multi_converter_unit_temperature_convert,
|
||||
multi_converter_unit_temperature_allowed};
|
||||
static const MultiConverterUnit multi_converter_unit_kel = {
|
||||
1,
|
||||
1,
|
||||
10,
|
||||
"KEL\0",
|
||||
"Kelvin\0",
|
||||
multi_converter_unit_temperature_convert,
|
||||
multi_converter_unit_temperature_allowed};
|
||||
|
||||
static const MultiConverterUnit multi_converter_unit_km = { 1, 0, 10, "KM\0", "Kilometers\0", multi_converter_unit_distance_convert, multi_converter_unit_distance_allowed };
|
||||
static const MultiConverterUnit multi_converter_unit_m = { 1, 0, 10, "M\0", "Meters\0", multi_converter_unit_distance_convert, multi_converter_unit_distance_allowed };
|
||||
static const MultiConverterUnit multi_converter_unit_cm = { 1, 0, 10, "CM\0", "Centimeters\0", multi_converter_unit_distance_convert, multi_converter_unit_distance_allowed };
|
||||
static const MultiConverterUnit multi_converter_unit_mi = { 1, 0, 10, "MI\0", "Miles\0", multi_converter_unit_distance_convert, multi_converter_unit_distance_allowed };
|
||||
static const MultiConverterUnit multi_converter_unit_ft = { 1, 0, 10, "FT\0", "Feet\0", multi_converter_unit_distance_convert, multi_converter_unit_distance_allowed };
|
||||
static const MultiConverterUnit multi_converter_unit_in = { 1, 0, 10, " \"\0", "Inches\0", multi_converter_unit_distance_convert, multi_converter_unit_distance_allowed };
|
||||
static const MultiConverterUnit multi_converter_unit_km = {
|
||||
1,
|
||||
0,
|
||||
10,
|
||||
"KM\0",
|
||||
"Kilometers\0",
|
||||
multi_converter_unit_distance_convert,
|
||||
multi_converter_unit_distance_allowed};
|
||||
static const MultiConverterUnit multi_converter_unit_m = {
|
||||
1,
|
||||
0,
|
||||
10,
|
||||
"M\0",
|
||||
"Meters\0",
|
||||
multi_converter_unit_distance_convert,
|
||||
multi_converter_unit_distance_allowed};
|
||||
static const MultiConverterUnit multi_converter_unit_cm = {
|
||||
1,
|
||||
0,
|
||||
10,
|
||||
"CM\0",
|
||||
"Centimeters\0",
|
||||
multi_converter_unit_distance_convert,
|
||||
multi_converter_unit_distance_allowed};
|
||||
static const MultiConverterUnit multi_converter_unit_mi = {
|
||||
1,
|
||||
0,
|
||||
10,
|
||||
"MI\0",
|
||||
"Miles\0",
|
||||
multi_converter_unit_distance_convert,
|
||||
multi_converter_unit_distance_allowed};
|
||||
static const MultiConverterUnit multi_converter_unit_ft = {
|
||||
1,
|
||||
0,
|
||||
10,
|
||||
"FT\0",
|
||||
"Feet\0",
|
||||
multi_converter_unit_distance_convert,
|
||||
multi_converter_unit_distance_allowed};
|
||||
static const MultiConverterUnit multi_converter_unit_in = {
|
||||
1,
|
||||
0,
|
||||
10,
|
||||
" \"\0",
|
||||
"Inches\0",
|
||||
multi_converter_unit_distance_convert,
|
||||
multi_converter_unit_distance_allowed};
|
||||
|
||||
static const MultiConverterUnit multi_converter_unit_deg = { 1, 0, 10, "DEG\0", "Degree\0", multi_converter_unit_angle_convert, multi_converter_unit_angle_allowed };
|
||||
static const MultiConverterUnit multi_converter_unit_rad = { 1, 0, 10, "RAD\0", "Radian\0", multi_converter_unit_angle_convert, multi_converter_unit_angle_allowed };
|
||||
static const MultiConverterUnit multi_converter_unit_deg = {
|
||||
1,
|
||||
0,
|
||||
10,
|
||||
"DEG\0",
|
||||
"Degree\0",
|
||||
multi_converter_unit_angle_convert,
|
||||
multi_converter_unit_angle_allowed};
|
||||
static const MultiConverterUnit multi_converter_unit_rad = {
|
||||
1,
|
||||
0,
|
||||
10,
|
||||
"RAD\0",
|
||||
"Radian\0",
|
||||
multi_converter_unit_angle_convert,
|
||||
multi_converter_unit_angle_allowed};
|
||||
|
||||
// index order set by the MultiConverterUnitType enum element (multi_converter_definitions.h)
|
||||
static const MultiConverterUnit multi_converter_available_units[MULTI_CONVERTER_AVAILABLE_UNITS] = {
|
||||
[UnitTypeDec] = multi_converter_unit_dec,
|
||||
[UnitTypeHex] = multi_converter_unit_hex,
|
||||
[UnitTypeBin] = multi_converter_unit_bin,
|
||||
[UnitTypeDec] = multi_converter_unit_dec,
|
||||
[UnitTypeHex] = multi_converter_unit_hex,
|
||||
[UnitTypeBin] = multi_converter_unit_bin,
|
||||
|
||||
[UnitTypeCelsius] = multi_converter_unit_cel,
|
||||
[UnitTypeFahernheit] = multi_converter_unit_far,
|
||||
[UnitTypeKelvin] = multi_converter_unit_kel,
|
||||
[UnitTypeCelsius] = multi_converter_unit_cel,
|
||||
[UnitTypeFahernheit] = multi_converter_unit_far,
|
||||
[UnitTypeKelvin] = multi_converter_unit_kel,
|
||||
|
||||
[UnitTypeKilometers] = multi_converter_unit_km,
|
||||
[UnitTypeMeters] = multi_converter_unit_m,
|
||||
[UnitTypeCentimeters] = multi_converter_unit_cm,
|
||||
[UnitTypeMiles] = multi_converter_unit_mi,
|
||||
[UnitTypeFeet] = multi_converter_unit_ft,
|
||||
[UnitTypeInches] = multi_converter_unit_in,
|
||||
[UnitTypeKilometers] = multi_converter_unit_km,
|
||||
[UnitTypeMeters] = multi_converter_unit_m,
|
||||
[UnitTypeCentimeters] = multi_converter_unit_cm,
|
||||
[UnitTypeMiles] = multi_converter_unit_mi,
|
||||
[UnitTypeFeet] = multi_converter_unit_ft,
|
||||
[UnitTypeInches] = multi_converter_unit_in,
|
||||
|
||||
[UnitTypeDegree] = multi_converter_unit_deg,
|
||||
[UnitTypeRadian] = multi_converter_unit_rad,
|
||||
[UnitTypeDegree] = multi_converter_unit_deg,
|
||||
[UnitTypeRadian] = multi_converter_unit_rad,
|
||||
};
|
||||
@@ -1,7 +1,7 @@
|
||||
App(
|
||||
appid="music_player",
|
||||
name="Music Player",
|
||||
apptype=FlipperAppType.PLUGIN,
|
||||
apptype=FlipperAppType.GAME,
|
||||
entry_point="music_player_app",
|
||||
cdefines=["APP_MUSIC_PLAYER"],
|
||||
requires=[
|
||||
@@ -10,7 +10,7 @@ App(
|
||||
],
|
||||
provides=["music_player_start"],
|
||||
stack_size=2 * 1024,
|
||||
order=20,
|
||||
order=45,
|
||||
)
|
||||
|
||||
App(
|
||||
|
||||
0
applications/nfc/helpers/nfc_emv_parser.c
Executable file → Normal file
0
applications/nfc/helpers/nfc_emv_parser.c
Executable file → Normal file
0
applications/nfc/helpers/nfc_emv_parser.h
Executable file → Normal file
0
applications/nfc/helpers/nfc_emv_parser.h
Executable file → Normal file
@@ -254,7 +254,7 @@ static void
|
||||
session_register_page = 234;
|
||||
break;
|
||||
default:
|
||||
furi_assert(false);
|
||||
furi_crash("Unknown MFUL");
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -94,6 +94,11 @@ Nfc* nfc_alloc() {
|
||||
view_dispatcher_add_view(
|
||||
nfc->view_dispatcher, NfcViewDictAttack, dict_attack_get_view(nfc->dict_attack));
|
||||
|
||||
// Detect Reader
|
||||
nfc->detect_reader = detect_reader_alloc();
|
||||
view_dispatcher_add_view(
|
||||
nfc->view_dispatcher, NfcViewDetectReader, detect_reader_get_view(nfc->detect_reader));
|
||||
|
||||
// Generator
|
||||
nfc->generator = NULL;
|
||||
|
||||
@@ -158,6 +163,10 @@ void nfc_free(Nfc* nfc) {
|
||||
view_dispatcher_remove_view(nfc->view_dispatcher, NfcViewDictAttack);
|
||||
dict_attack_free(nfc->dict_attack);
|
||||
|
||||
// Detect Reader
|
||||
view_dispatcher_remove_view(nfc->view_dispatcher, NfcViewDetectReader);
|
||||
detect_reader_free(nfc->detect_reader);
|
||||
|
||||
// Worker
|
||||
nfc_worker_stop(nfc->worker);
|
||||
nfc_worker_free(nfc->worker);
|
||||
|
||||
0
applications/nfc/nfc_cli.c
Executable file → Normal file
0
applications/nfc/nfc_cli.c
Executable file → Normal file
@@ -28,6 +28,7 @@
|
||||
#include <lib/nfc/parsers/nfc_supported_card.h>
|
||||
|
||||
#include "views/dict_attack.h"
|
||||
#include "views/detect_reader.h"
|
||||
|
||||
#include <nfc/scenes/nfc_scene.h>
|
||||
#include <nfc/helpers/nfc_custom_event.h>
|
||||
@@ -71,6 +72,7 @@ struct Nfc {
|
||||
TextBox* text_box;
|
||||
Widget* widget;
|
||||
DictAttack* dict_attack;
|
||||
DetectReader* detect_reader;
|
||||
|
||||
const NfcGenerator* generator;
|
||||
};
|
||||
@@ -85,6 +87,7 @@ typedef enum {
|
||||
NfcViewTextBox,
|
||||
NfcViewWidget,
|
||||
NfcViewDictAttack,
|
||||
NfcViewDetectReader,
|
||||
} NfcView;
|
||||
|
||||
Nfc* nfc_alloc();
|
||||
|
||||
0
applications/nfc/scenes/nfc_scene.c
Executable file → Normal file
0
applications/nfc/scenes/nfc_scene.c
Executable file → Normal file
2
applications/nfc/scenes/nfc_scene_config.h
Executable file → Normal file
2
applications/nfc/scenes/nfc_scene_config.h
Executable file → Normal file
@@ -48,4 +48,6 @@ ADD_SCENE(nfc, rpc, Rpc)
|
||||
ADD_SCENE(nfc, exit_confirm, ExitConfirm)
|
||||
ADD_SCENE(nfc, retry_confirm, RetryConfirm)
|
||||
ADD_SCENE(nfc, detect_reader, DetectReader)
|
||||
ADD_SCENE(nfc, mfkey_nonces_info, MfkeyNoncesInfo)
|
||||
ADD_SCENE(nfc, mfkey_complete, MfkeyComplete)
|
||||
ADD_SCENE(nfc, nfc_data_info, NfcDataInfo)
|
||||
|
||||
0
applications/nfc/scenes/nfc_scene_delete.c
Executable file → Normal file
0
applications/nfc/scenes/nfc_scene_delete.c
Executable file → Normal file
0
applications/nfc/scenes/nfc_scene_delete_success.c
Executable file → Normal file
0
applications/nfc/scenes/nfc_scene_delete_success.c
Executable file → Normal file
@@ -1,126 +1,48 @@
|
||||
#include "../nfc_i.h"
|
||||
#include <dolphin/dolphin.h>
|
||||
|
||||
#define NFC_SCENE_DETECT_READER_LOG_SIZE_MAX (200)
|
||||
|
||||
enum {
|
||||
NfcSceneDetectReaderStateWidget,
|
||||
NfcSceneDetectReaderStateTextBox,
|
||||
};
|
||||
|
||||
bool nfc_detect_reader_worker_callback(NfcWorkerEvent event, void* context) {
|
||||
UNUSED(event);
|
||||
furi_assert(context);
|
||||
Nfc* nfc = context;
|
||||
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventWorkerExit);
|
||||
view_dispatcher_send_custom_event(nfc->view_dispatcher, event);
|
||||
return true;
|
||||
}
|
||||
|
||||
void nfc_scene_detect_reader_widget_callback(GuiButtonType result, InputType type, void* context) {
|
||||
furi_assert(context);
|
||||
Nfc* nfc = context;
|
||||
if(type == InputTypeShort) {
|
||||
view_dispatcher_send_custom_event(nfc->view_dispatcher, result);
|
||||
}
|
||||
}
|
||||
|
||||
void nfc_detect_reader_textbox_callback(void* context) {
|
||||
void nfc_scene_detect_reader_callback(void* context) {
|
||||
furi_assert(context);
|
||||
Nfc* nfc = context;
|
||||
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit);
|
||||
}
|
||||
|
||||
// Add widget with device name or inform that data received
|
||||
static void nfc_scene_detect_reader_widget_config(Nfc* nfc, bool data_received) {
|
||||
Widget* widget = nfc->widget;
|
||||
widget_reset(widget);
|
||||
|
||||
widget_add_icon_element(widget, 0, 14, &I_Reader_detect);
|
||||
widget_add_string_element(
|
||||
widget, 64, 3, AlignCenter, AlignTop, FontSecondary, "Hold Near Reader");
|
||||
widget_add_string_element(widget, 55, 22, AlignLeft, AlignTop, FontPrimary, "Emulating...");
|
||||
|
||||
if(data_received) {
|
||||
widget_add_button_element(
|
||||
widget, GuiButtonTypeCenter, "Log", nfc_scene_detect_reader_widget_callback, nfc);
|
||||
}
|
||||
}
|
||||
|
||||
void nfc_scene_detect_reader_on_enter(void* context) {
|
||||
Nfc* nfc = context;
|
||||
DOLPHIN_DEED(DolphinDeedNfcEmulate);
|
||||
FuriHalNfcDevData nfc_params = {
|
||||
.uid = {0x36, 0x9C, 0xe7, 0xb1, 0x0A, 0xC1, 0x34},
|
||||
.uid_len = 7,
|
||||
.atqa = {0x44, 0x00},
|
||||
.sak = 0x08,
|
||||
.type = FuriHalNfcTypeA,
|
||||
};
|
||||
nfc->dev->dev_data.nfc_data = nfc_params;
|
||||
|
||||
// Setup Widget
|
||||
nfc_scene_detect_reader_widget_config(nfc, false);
|
||||
// Setup TextBox
|
||||
TextBox* text_box = nfc->text_box;
|
||||
text_box_set_font(text_box, TextBoxFontHex);
|
||||
text_box_set_focus(text_box, TextBoxFocusEnd);
|
||||
string_reset(nfc->text_box_store);
|
||||
|
||||
// Set Widget state and view
|
||||
scene_manager_set_scene_state(
|
||||
nfc->scene_manager, NfcSceneDetectReader, NfcSceneDetectReaderStateWidget);
|
||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
|
||||
// Start worker
|
||||
memset(&nfc->dev->dev_data.reader_data, 0, sizeof(NfcReaderRequestData));
|
||||
detect_reader_set_callback(nfc->detect_reader, nfc_scene_detect_reader_callback, nfc);
|
||||
nfc_worker_start(
|
||||
nfc->worker,
|
||||
NfcWorkerStateUidEmulate,
|
||||
NfcWorkerStateAnalyzeReader,
|
||||
&nfc->dev->dev_data,
|
||||
nfc_detect_reader_worker_callback,
|
||||
nfc);
|
||||
|
||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDetectReader);
|
||||
|
||||
nfc_blink_start(nfc);
|
||||
}
|
||||
|
||||
bool nfc_scene_detect_reader_on_event(void* context, SceneManagerEvent event) {
|
||||
Nfc* nfc = context;
|
||||
NfcReaderRequestData* reader_data = &nfc->dev->dev_data.reader_data;
|
||||
uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneDetectReader);
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == NfcCustomEventWorkerExit) {
|
||||
// Add data button to widget if data is received for the first time
|
||||
if(!string_size(nfc->text_box_store)) {
|
||||
nfc_scene_detect_reader_widget_config(nfc, true);
|
||||
}
|
||||
// Update TextBox data
|
||||
if(string_size(nfc->text_box_store) < NFC_SCENE_DETECT_READER_LOG_SIZE_MAX) {
|
||||
string_cat_printf(nfc->text_box_store, "R:");
|
||||
for(uint16_t i = 0; i < reader_data->size; i++) {
|
||||
string_cat_printf(nfc->text_box_store, " %02X", reader_data->data[i]);
|
||||
}
|
||||
string_push_back(nfc->text_box_store, '\n');
|
||||
text_box_set_text(nfc->text_box, string_get_cstr(nfc->text_box_store));
|
||||
}
|
||||
memset(reader_data, 0, sizeof(NfcReaderRequestData));
|
||||
if(event.event == NfcCustomEventViewExit) {
|
||||
nfc_worker_stop(nfc->worker);
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfkeyNoncesInfo);
|
||||
consumed = true;
|
||||
} else if(event.event == GuiButtonTypeCenter && state == NfcSceneDetectReaderStateWidget) {
|
||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox);
|
||||
scene_manager_set_scene_state(
|
||||
nfc->scene_manager, NfcSceneDetectReader, NfcSceneDetectReaderStateTextBox);
|
||||
consumed = true;
|
||||
} else if(event.event == NfcCustomEventViewExit && state == NfcSceneDetectReaderStateTextBox) {
|
||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
|
||||
scene_manager_set_scene_state(
|
||||
nfc->scene_manager, NfcSceneDetectReader, NfcSceneDetectReaderStateWidget);
|
||||
consumed = true;
|
||||
}
|
||||
} else if(event.type == SceneManagerEventTypeBack) {
|
||||
if(state == NfcSceneDetectReaderStateTextBox) {
|
||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
|
||||
scene_manager_set_scene_state(
|
||||
nfc->scene_manager, NfcSceneDetectReader, NfcSceneDetectReaderStateWidget);
|
||||
} else if(event.event == NfcWorkerEventDetectReaderMfkeyCollected) {
|
||||
detect_reader_inc_nonce_cnt(nfc->detect_reader);
|
||||
consumed = true;
|
||||
}
|
||||
}
|
||||
@@ -135,9 +57,7 @@ void nfc_scene_detect_reader_on_exit(void* context) {
|
||||
nfc_worker_stop(nfc->worker);
|
||||
|
||||
// Clear view
|
||||
widget_reset(nfc->widget);
|
||||
text_box_reset(nfc->text_box);
|
||||
string_reset(nfc->text_box_store);
|
||||
detect_reader_reset(nfc->detect_reader);
|
||||
|
||||
nfc_blink_stop(nfc);
|
||||
}
|
||||
|
||||
0
applications/nfc/scenes/nfc_scene_emulate_uid.c
Executable file → Normal file
0
applications/nfc/scenes/nfc_scene_emulate_uid.c
Executable file → Normal file
0
applications/nfc/scenes/nfc_scene_file_select.c
Executable file → Normal file
0
applications/nfc/scenes/nfc_scene_file_select.c
Executable file → Normal file
0
applications/nfc/scenes/nfc_scene_mf_ultralight_emulate.c
Executable file → Normal file
0
applications/nfc/scenes/nfc_scene_mf_ultralight_emulate.c
Executable file → Normal file
49
applications/nfc/scenes/nfc_scene_mfkey_complete.c
Normal file
49
applications/nfc/scenes/nfc_scene_mfkey_complete.c
Normal file
@@ -0,0 +1,49 @@
|
||||
#include "../nfc_i.h"
|
||||
|
||||
void nfc_scene_mfkey_complete_callback(GuiButtonType result, InputType type, void* context) {
|
||||
Nfc* nfc = context;
|
||||
if(type == InputTypeShort) {
|
||||
view_dispatcher_send_custom_event(nfc->view_dispatcher, result);
|
||||
}
|
||||
}
|
||||
|
||||
void nfc_scene_mfkey_complete_on_enter(void* context) {
|
||||
Nfc* nfc = context;
|
||||
|
||||
widget_add_string_element(nfc->widget, 64, 0, AlignCenter, AlignTop, FontPrimary, "Complete!");
|
||||
widget_add_string_multiline_element(
|
||||
nfc->widget,
|
||||
64,
|
||||
32,
|
||||
AlignCenter,
|
||||
AlignCenter,
|
||||
FontSecondary,
|
||||
"Now use mfkey32v2\nto extract keys");
|
||||
widget_add_button_element(
|
||||
nfc->widget, GuiButtonTypeCenter, "OK", nfc_scene_mfkey_complete_callback, nfc);
|
||||
|
||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
|
||||
}
|
||||
|
||||
bool nfc_scene_mfkey_complete_on_event(void* context, SceneManagerEvent event) {
|
||||
Nfc* nfc = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == GuiButtonTypeCenter) {
|
||||
consumed = scene_manager_search_and_switch_to_previous_scene(
|
||||
nfc->scene_manager, NfcSceneStart);
|
||||
}
|
||||
} else if(event.event == SceneManagerEventTypeBack) {
|
||||
consumed =
|
||||
scene_manager_search_and_switch_to_previous_scene(nfc->scene_manager, NfcSceneStart);
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void nfc_scene_mfkey_complete_on_exit(void* context) {
|
||||
Nfc* nfc = context;
|
||||
|
||||
widget_reset(nfc->widget);
|
||||
}
|
||||
55
applications/nfc/scenes/nfc_scene_mfkey_nonces_info.c
Normal file
55
applications/nfc/scenes/nfc_scene_mfkey_nonces_info.c
Normal file
@@ -0,0 +1,55 @@
|
||||
#include "../nfc_i.h"
|
||||
#include <lib/nfc/helpers/mfkey32.h>
|
||||
|
||||
void nfc_scene_mfkey_nonces_info_callback(GuiButtonType result, InputType type, void* context) {
|
||||
Nfc* nfc = context;
|
||||
if(type == InputTypeShort) {
|
||||
view_dispatcher_send_custom_event(nfc->view_dispatcher, result);
|
||||
}
|
||||
}
|
||||
|
||||
void nfc_scene_mfkey_nonces_info_on_enter(void* context) {
|
||||
Nfc* nfc = context;
|
||||
|
||||
string_t temp_str;
|
||||
string_init(temp_str);
|
||||
|
||||
uint16_t nonces_saved = mfkey32_get_auth_sectors(temp_str);
|
||||
widget_add_text_scroll_element(nfc->widget, 0, 22, 128, 42, string_get_cstr(temp_str));
|
||||
string_printf(temp_str, "Nonces saved %d", nonces_saved);
|
||||
widget_add_string_element(
|
||||
nfc->widget, 0, 0, AlignLeft, AlignTop, FontPrimary, string_get_cstr(temp_str));
|
||||
widget_add_string_element(
|
||||
nfc->widget, 0, 12, AlignLeft, AlignTop, FontSecondary, "Authenticated sectors:");
|
||||
|
||||
widget_add_button_element(
|
||||
nfc->widget, GuiButtonTypeRight, "Next", nfc_scene_mfkey_nonces_info_callback, nfc);
|
||||
|
||||
string_clear(temp_str);
|
||||
|
||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
|
||||
}
|
||||
|
||||
bool nfc_scene_mfkey_nonces_info_on_event(void* context, SceneManagerEvent event) {
|
||||
Nfc* nfc = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == GuiButtonTypeRight) {
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfkeyComplete);
|
||||
consumed = true;
|
||||
}
|
||||
} else if(event.type == SceneManagerEventTypeBack) {
|
||||
consumed =
|
||||
scene_manager_search_and_switch_to_previous_scene(nfc->scene_manager, NfcSceneStart);
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void nfc_scene_mfkey_nonces_info_on_exit(void* context) {
|
||||
Nfc* nfc = context;
|
||||
|
||||
// Clear view
|
||||
widget_reset(nfc->widget);
|
||||
}
|
||||
0
applications/nfc/scenes/nfc_scene_read_card_success.c
Executable file → Normal file
0
applications/nfc/scenes/nfc_scene_read_card_success.c
Executable file → Normal file
0
applications/nfc/scenes/nfc_scene_set_atqa.c
Executable file → Normal file
0
applications/nfc/scenes/nfc_scene_set_atqa.c
Executable file → Normal file
0
applications/nfc/scenes/nfc_scene_set_sak.c
Executable file → Normal file
0
applications/nfc/scenes/nfc_scene_set_sak.c
Executable file → Normal file
0
applications/nfc/scenes/nfc_scene_set_uid.c
Executable file → Normal file
0
applications/nfc/scenes/nfc_scene_set_uid.c
Executable file → Normal file
@@ -49,7 +49,12 @@ bool nfc_scene_start_on_event(void* context, SceneManagerEvent event) {
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneRead);
|
||||
consumed = true;
|
||||
} else if(event.event == SubmenuIndexDetectReader) {
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneDetectReader);
|
||||
bool sd_exist = storage_sd_status(nfc->dev->storage) == FSE_OK;
|
||||
if(sd_exist) {
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneDetectReader);
|
||||
} else {
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneDictNotFound);
|
||||
}
|
||||
consumed = true;
|
||||
} else if(event.event == SubmenuIndexSaved) {
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneFileSelect);
|
||||
@@ -61,7 +66,6 @@ bool nfc_scene_start_on_event(void* context, SceneManagerEvent event) {
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneSetType);
|
||||
consumed = true;
|
||||
} else if(event.event == SubmenuIndexDebug) {
|
||||
scene_manager_set_scene_state(nfc->scene_manager, NfcSceneStart, SubmenuIndexDebug);
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneDebug);
|
||||
consumed = true;
|
||||
}
|
||||
|
||||
115
applications/nfc/views/detect_reader.c
Normal file
115
applications/nfc/views/detect_reader.c
Normal file
@@ -0,0 +1,115 @@
|
||||
#include "detect_reader.h"
|
||||
|
||||
#include <gui/elements.h>
|
||||
|
||||
struct DetectReader {
|
||||
View* view;
|
||||
DetectReaderDoneCallback callback;
|
||||
void* context;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
uint16_t nonces;
|
||||
} DetectReaderViewModel;
|
||||
|
||||
static void detect_reader_draw_callback(Canvas* canvas, void* model) {
|
||||
DetectReaderViewModel* m = model;
|
||||
char text[32] = {};
|
||||
|
||||
snprintf(text, sizeof(text), "Tap the reader several times");
|
||||
canvas_draw_str_aligned(canvas, 64, 0, AlignCenter, AlignTop, "Tap the reader several times");
|
||||
|
||||
if(m->nonces == 0) {
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str_aligned(canvas, 52, 22, AlignLeft, AlignTop, "Emulating...");
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
canvas_draw_str_aligned(canvas, 52, 35, AlignLeft, AlignTop, "MIFARE Classic");
|
||||
canvas_draw_icon(canvas, 0, 13, &I_Tap_reader_36x38);
|
||||
} else {
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str_aligned(canvas, 54, 22, AlignLeft, AlignTop, "Collecting...");
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
snprintf(text, sizeof(text), "Nonces: %d", m->nonces);
|
||||
canvas_draw_str_aligned(canvas, 54, 35, AlignLeft, AlignTop, text);
|
||||
elements_button_right(canvas, "Next");
|
||||
canvas_draw_icon(canvas, 6, 15, &I_ArrowC_1_36x36);
|
||||
}
|
||||
}
|
||||
|
||||
static bool detect_reader_input_callback(InputEvent* event, void* context) {
|
||||
DetectReader* detect_reader = context;
|
||||
furi_assert(detect_reader->callback);
|
||||
bool consumed = false;
|
||||
|
||||
uint8_t nonces = 0;
|
||||
with_view_model(
|
||||
detect_reader->view, (DetectReaderViewModel * model) {
|
||||
nonces = model->nonces;
|
||||
return false;
|
||||
});
|
||||
|
||||
if(event->type == InputTypeShort) {
|
||||
if(event->key == InputKeyRight) {
|
||||
if(nonces > 0) {
|
||||
detect_reader->callback(detect_reader->context);
|
||||
consumed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
DetectReader* detect_reader_alloc() {
|
||||
DetectReader* detect_reader = malloc(sizeof(DetectReader));
|
||||
detect_reader->view = view_alloc();
|
||||
view_allocate_model(detect_reader->view, ViewModelTypeLocking, sizeof(DetectReaderViewModel));
|
||||
view_set_draw_callback(detect_reader->view, detect_reader_draw_callback);
|
||||
view_set_input_callback(detect_reader->view, detect_reader_input_callback);
|
||||
view_set_context(detect_reader->view, detect_reader);
|
||||
|
||||
return detect_reader;
|
||||
}
|
||||
|
||||
void detect_reader_free(DetectReader* detect_reader) {
|
||||
furi_assert(detect_reader);
|
||||
|
||||
view_free(detect_reader->view);
|
||||
free(detect_reader);
|
||||
}
|
||||
|
||||
void detect_reader_reset(DetectReader* detect_reader) {
|
||||
furi_assert(detect_reader);
|
||||
|
||||
with_view_model(
|
||||
detect_reader->view, (DetectReaderViewModel * model) {
|
||||
model->nonces = 0;
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
View* detect_reader_get_view(DetectReader* detect_reader) {
|
||||
furi_assert(detect_reader);
|
||||
|
||||
return detect_reader->view;
|
||||
}
|
||||
|
||||
void detect_reader_set_callback(
|
||||
DetectReader* detect_reader,
|
||||
DetectReaderDoneCallback callback,
|
||||
void* context) {
|
||||
furi_assert(detect_reader);
|
||||
furi_assert(callback);
|
||||
|
||||
detect_reader->callback = callback;
|
||||
detect_reader->context = context;
|
||||
}
|
||||
|
||||
void detect_reader_inc_nonce_cnt(DetectReader* detect_reader) {
|
||||
furi_assert(detect_reader);
|
||||
with_view_model(
|
||||
detect_reader->view, (DetectReaderViewModel * model) {
|
||||
model->nonces++;
|
||||
return false;
|
||||
});
|
||||
}
|
||||
23
applications/nfc/views/detect_reader.h
Normal file
23
applications/nfc/views/detect_reader.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <gui/view.h>
|
||||
#include <gui/modules/widget.h>
|
||||
|
||||
typedef struct DetectReader DetectReader;
|
||||
|
||||
typedef void (*DetectReaderDoneCallback)(void* context);
|
||||
|
||||
DetectReader* detect_reader_alloc();
|
||||
|
||||
void detect_reader_free(DetectReader* detect_reader);
|
||||
|
||||
void detect_reader_reset(DetectReader* detect_reader);
|
||||
|
||||
View* detect_reader_get_view(DetectReader* detect_reader);
|
||||
|
||||
void detect_reader_set_callback(
|
||||
DetectReader* detect_reader,
|
||||
DetectReaderDoneCallback callback,
|
||||
void* context);
|
||||
|
||||
void detect_reader_inc_nonce_cnt(DetectReader* detect_reader);
|
||||
@@ -9,8 +9,10 @@
|
||||
#include <toolbox/stream/file_stream.h>
|
||||
|
||||
#define LOGITECH_MAX_CHANNEL 85
|
||||
#define COUNT_THRESHOLD 4
|
||||
#define SAMPLE_TIME 20000
|
||||
#define COUNT_THRESHOLD 2
|
||||
#define DEFAULT_SAMPLE_TIME 8000
|
||||
#define MAX_ADDRS 100
|
||||
#define MAX_CONFIRMED 32
|
||||
|
||||
#define NRFSNIFF_APP_PATH_FOLDER "/ext/nrfsniff"
|
||||
#define NRFSNIFF_APP_FILENAME "addresses.txt"
|
||||
@@ -32,31 +34,36 @@ typedef struct {
|
||||
} PluginState;
|
||||
|
||||
char rate_text_fmt[] = "Transfer rate: %dMbps";
|
||||
char sample_text_fmt[] = "Sample Time: %d ms";
|
||||
char channel_text_fmt[] = "Channel: %d";
|
||||
char preamble_text_fmt[] = "Preamble: %02X";
|
||||
char sniff_text_fmt[] = "Sniffing: %s Found: %d";
|
||||
char addresses_header_text[] = "Address,rate";
|
||||
char sniffed_address_fmt[] = "%s,%d";
|
||||
char rate_text[46];
|
||||
char channel_text[42];
|
||||
char channel_text[14];
|
||||
char sample_text[32];
|
||||
char preamble_text[14];
|
||||
char sniff_text[38];
|
||||
char sniffed_address[14];
|
||||
|
||||
uint8_t target_channel = 0;
|
||||
uint32_t found_count = 0;
|
||||
uint32_t sample_time = DEFAULT_SAMPLE_TIME;
|
||||
uint8_t target_rate = 8; // rate can be either 8 (2Mbps) or 0 (1Mbps)
|
||||
uint8_t target_preamble[] = {0xAA, 0x00};
|
||||
uint8_t sniffing_state = false;
|
||||
char top_address[12];
|
||||
|
||||
uint8_t candidates[100][5] = {0}; // top 100 recurring addresses
|
||||
uint32_t counts[100];
|
||||
uint8_t total_candidates = 0;
|
||||
uint8_t last_cleanup_idx = 101; // avoid replacing the last replaced addr
|
||||
uint8_t candidates[MAX_ADDRS][5] = {0}; // last 100 sniffed addresses
|
||||
uint32_t counts[MAX_ADDRS];
|
||||
uint8_t confirmed[MAX_CONFIRMED][5] = {0}; // first 32 confirmed addresses
|
||||
uint8_t confirmed_idx = 0;
|
||||
uint32_t total_candidates = 0;
|
||||
uint32_t candidate_idx = 0;
|
||||
|
||||
static int get_addr_index(uint8_t* addr, uint8_t addr_size) {
|
||||
for(int i = 0; i < total_candidates; i++) {
|
||||
for(uint32_t i = 0; i < total_candidates; i++) {
|
||||
uint8_t* arr_item = candidates[i];
|
||||
if(!memcmp(arr_item, addr, addr_size)) return i;
|
||||
}
|
||||
@@ -64,32 +71,10 @@ static int get_addr_index(uint8_t* addr, uint8_t addr_size) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
static uint32_t get_addr_count(uint8_t* addr, uint8_t addr_size)
|
||||
{
|
||||
return counts[get_addr_index(addr, addr_size)];
|
||||
}
|
||||
*/
|
||||
|
||||
static uint8_t get_lowest_idx() {
|
||||
uint32_t lowest = 10000;
|
||||
uint8_t lowest_idx = 0;
|
||||
for(uint8_t i = 0; i < total_candidates; i++) {
|
||||
if(i == last_cleanup_idx) continue;
|
||||
|
||||
if(counts[i] < lowest) {
|
||||
lowest = counts[i];
|
||||
lowest_idx = i;
|
||||
}
|
||||
}
|
||||
last_cleanup_idx = lowest_idx;
|
||||
return lowest_idx;
|
||||
}
|
||||
|
||||
static uint8_t get_highest_idx() {
|
||||
static int get_highest_idx() {
|
||||
uint32_t highest = 0;
|
||||
uint8_t highest_idx = 0;
|
||||
for(uint8_t i = 0; i < total_candidates; i++) {
|
||||
int highest_idx = 0;
|
||||
for(uint32_t i = 0; i < total_candidates; i++) {
|
||||
if(counts[i] > highest) {
|
||||
highest = counts[i];
|
||||
highest_idx = i;
|
||||
@@ -99,15 +84,14 @@ static uint8_t get_highest_idx() {
|
||||
return highest_idx;
|
||||
}
|
||||
|
||||
// if array is full, start over from beginning
|
||||
static void insert_addr(uint8_t* addr, uint8_t addr_size) {
|
||||
uint8_t idx = total_candidates;
|
||||
if(total_candidates > 99) {
|
||||
// replace addr with lowest count
|
||||
idx = get_lowest_idx();
|
||||
}
|
||||
memcpy(candidates[idx], addr, addr_size);
|
||||
counts[idx] = 1;
|
||||
if(total_candidates < 100) total_candidates++;
|
||||
if(candidate_idx >= MAX_ADDRS) candidate_idx = 0;
|
||||
|
||||
memcpy(candidates[candidate_idx], addr, addr_size);
|
||||
counts[candidate_idx] = 1;
|
||||
if(total_candidates < MAX_ADDRS) total_candidates++;
|
||||
candidate_idx++;
|
||||
}
|
||||
|
||||
static void render_callback(Canvas* const canvas, void* ctx) {
|
||||
@@ -128,13 +112,15 @@ static void render_callback(Canvas* const canvas, void* ctx) {
|
||||
|
||||
snprintf(rate_text, sizeof(rate_text), rate_text_fmt, (int)rate);
|
||||
snprintf(channel_text, sizeof(channel_text), channel_text_fmt, (int)target_channel);
|
||||
snprintf(preamble_text, sizeof(preamble_text), preamble_text_fmt, target_preamble[0]);
|
||||
snprintf(sample_text, sizeof(sample_text), sample_text_fmt, (int)sample_time);
|
||||
//snprintf(preamble_text, sizeof(preamble_text), preamble_text_fmt, target_preamble[0]);
|
||||
snprintf(sniff_text, sizeof(sniff_text), sniff_text_fmt, sniffing, found_count);
|
||||
snprintf(
|
||||
sniffed_address, sizeof(sniffed_address), sniffed_address_fmt, top_address, (int)rate);
|
||||
canvas_draw_str_aligned(canvas, 10, 10, AlignLeft, AlignBottom, rate_text);
|
||||
canvas_draw_str_aligned(canvas, 10, 20, AlignLeft, AlignBottom, channel_text);
|
||||
canvas_draw_str_aligned(canvas, 10, 30, AlignLeft, AlignBottom, preamble_text);
|
||||
canvas_draw_str_aligned(canvas, 10, 20, AlignLeft, AlignBottom, sample_text);
|
||||
canvas_draw_str_aligned(canvas, 10, 30, AlignLeft, AlignBottom, channel_text);
|
||||
//canvas_draw_str_aligned(canvas, 10, 30, AlignLeft, AlignBottom, preamble_text);
|
||||
canvas_draw_str_aligned(canvas, 10, 40, AlignLeft, AlignBottom, sniff_text);
|
||||
canvas_draw_str_aligned(canvas, 30, 50, AlignLeft, AlignBottom, addresses_header_text);
|
||||
canvas_draw_str_aligned(canvas, 30, 60, AlignLeft, AlignBottom, sniffed_address);
|
||||
@@ -256,12 +242,24 @@ void alt_address(uint8_t* addr, uint8_t* altaddr) {
|
||||
for(int i = 0; i < 5; i++) altaddr[i] = tmpaddr[4 - i];
|
||||
}
|
||||
|
||||
static bool previously_confirmed(uint8_t* addr) {
|
||||
bool found = false;
|
||||
for(int i = 0; i < MAX_CONFIRMED; i++) {
|
||||
if(!memcmp(confirmed[i], addr, 5)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
static void wrap_up(Storage* storage, NotificationApp* notification) {
|
||||
uint8_t ch;
|
||||
uint8_t addr[5];
|
||||
uint8_t altaddr[5];
|
||||
char trying[12];
|
||||
uint8_t idx;
|
||||
int idx;
|
||||
uint8_t rate = 0;
|
||||
if(target_rate == 8) rate = 2;
|
||||
|
||||
@@ -291,14 +289,24 @@ static void wrap_up(Storage* storage, NotificationApp* notification) {
|
||||
hexlify(addr, 5, top_address);
|
||||
save_addr_to_file(storage, addr, 5, notification);
|
||||
found_count++;
|
||||
if(confirmed_idx < MAX_CONFIRMED) memcpy(confirmed[confirmed_idx++], addr, 5);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void start_sniffing() {
|
||||
static void clear_cache() {
|
||||
found_count = 0;
|
||||
confirmed_idx = 0;
|
||||
candidate_idx = 0;
|
||||
target_channel = 2;
|
||||
total_candidates = 0;
|
||||
memset(candidates, 0, sizeof(candidates));
|
||||
memset(counts, 0, sizeof(counts));
|
||||
memset(confirmed, 0, sizeof(confirmed));
|
||||
}
|
||||
|
||||
static void start_sniffing() {
|
||||
nrf24_init_promisc_mode(nrf24_HANDLE, target_channel, target_rate);
|
||||
}
|
||||
|
||||
@@ -311,8 +319,8 @@ int32_t nrfsniff_app(void* p) {
|
||||
PluginState* plugin_state = malloc(sizeof(PluginState));
|
||||
ValueMutex state_mutex;
|
||||
if(!init_mutex(&state_mutex, plugin_state, sizeof(PluginState))) {
|
||||
FURI_LOG_E(TAG, "cannot create mutex\r\n");
|
||||
furi_message_queue_free(event_queue);
|
||||
FURI_LOG_E(TAG, "cannot create mutex\r\n");
|
||||
free(plugin_state);
|
||||
return 255;
|
||||
}
|
||||
@@ -366,18 +374,20 @@ int32_t nrfsniff_app(void* p) {
|
||||
break;
|
||||
case InputKeyRight:
|
||||
// increment channel
|
||||
if(!sniffing_state && target_channel <= LOGITECH_MAX_CHANNEL)
|
||||
target_channel++;
|
||||
//if(!sniffing_state && target_channel <= LOGITECH_MAX_CHANNEL)
|
||||
// target_channel++;
|
||||
sample_time += 500;
|
||||
break;
|
||||
case InputKeyLeft:
|
||||
// decrement channel
|
||||
if(!sniffing_state && target_channel > 0) target_channel--;
|
||||
//if(!sniffing_state && target_channel > 0) target_channel--;
|
||||
if(sample_time > 500) sample_time -= 500;
|
||||
break;
|
||||
case InputKeyOk:
|
||||
// toggle sniffing
|
||||
sniffing_state = !sniffing_state;
|
||||
if(sniffing_state) {
|
||||
found_count = 0;
|
||||
clear_cache();
|
||||
start_sniffing();
|
||||
start = furi_get_tick();
|
||||
} else
|
||||
@@ -398,22 +408,26 @@ int32_t nrfsniff_app(void* p) {
|
||||
if(nrf24_sniff_address(nrf24_HANDLE, 5, address)) {
|
||||
int idx;
|
||||
uint8_t* top_addr;
|
||||
idx = get_addr_index(address, 5);
|
||||
if(idx == -1)
|
||||
insert_addr(address, 5);
|
||||
else
|
||||
counts[idx]++;
|
||||
if(!previously_confirmed(address)) {
|
||||
idx = get_addr_index(address, 5);
|
||||
if(idx == -1)
|
||||
insert_addr(address, 5);
|
||||
else
|
||||
counts[idx]++;
|
||||
|
||||
top_addr = candidates[get_highest_idx()];
|
||||
hexlify(top_addr, 5, top_address);
|
||||
top_addr = candidates[get_highest_idx()];
|
||||
hexlify(top_addr, 5, top_address);
|
||||
}
|
||||
}
|
||||
|
||||
if(furi_get_tick() - start >= SAMPLE_TIME) {
|
||||
wrap_up(storage, notification);
|
||||
if(furi_get_tick() - start >= sample_time) {
|
||||
target_channel++;
|
||||
if(target_channel > LOGITECH_MAX_CHANNEL) target_channel = 2;
|
||||
{
|
||||
wrap_up(storage, notification);
|
||||
start_sniffing();
|
||||
}
|
||||
|
||||
start_sniffing();
|
||||
start = furi_get_tick();
|
||||
}
|
||||
}
|
||||
@@ -422,6 +436,10 @@ int32_t nrfsniff_app(void* p) {
|
||||
release_mutex(&state_mutex, plugin_state);
|
||||
}
|
||||
|
||||
clear_cache();
|
||||
sample_time = DEFAULT_SAMPLE_TIME;
|
||||
target_rate = 8; // rate can be either 8 (2Mbps) or 0 (1Mbps)
|
||||
sniffing_state = false;
|
||||
furi_hal_spi_release(nrf24_HANDLE);
|
||||
view_port_enabled_set(view_port, false);
|
||||
gui_remove_view_port(gui, view_port);
|
||||
@@ -432,4 +450,4 @@ int32_t nrfsniff_app(void* p) {
|
||||
furi_message_queue_free(event_queue);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,12 +190,87 @@ ReturnCode picopass_read_card(PicopassBlock* AA1) {
|
||||
return ERR_NONE;
|
||||
}
|
||||
|
||||
ReturnCode picopass_write_card(PicopassBlock* AA1) {
|
||||
rfalPicoPassIdentifyRes idRes;
|
||||
rfalPicoPassSelectRes selRes;
|
||||
rfalPicoPassReadCheckRes rcRes;
|
||||
rfalPicoPassCheckRes chkRes;
|
||||
|
||||
ReturnCode err;
|
||||
|
||||
uint8_t div_key[8] = {0};
|
||||
uint8_t mac[4] = {0};
|
||||
uint8_t ccnr[12] = {0};
|
||||
|
||||
err = rfalPicoPassPollerIdentify(&idRes);
|
||||
if(err != ERR_NONE) {
|
||||
FURI_LOG_E(TAG, "rfalPicoPassPollerIdentify error %d", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = rfalPicoPassPollerSelect(idRes.CSN, &selRes);
|
||||
if(err != ERR_NONE) {
|
||||
FURI_LOG_E(TAG, "rfalPicoPassPollerSelect error %d", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = rfalPicoPassPollerReadCheck(&rcRes);
|
||||
if(err != ERR_NONE) {
|
||||
FURI_LOG_E(TAG, "rfalPicoPassPollerReadCheck error %d", err);
|
||||
return err;
|
||||
}
|
||||
memcpy(ccnr, rcRes.CCNR, sizeof(rcRes.CCNR)); // last 4 bytes left 0
|
||||
|
||||
loclass_diversifyKey(selRes.CSN, picopass_iclass_key, div_key);
|
||||
loclass_opt_doReaderMAC(ccnr, div_key, mac);
|
||||
|
||||
err = rfalPicoPassPollerCheck(mac, &chkRes);
|
||||
if(err != ERR_NONE) {
|
||||
FURI_LOG_E(TAG, "rfalPicoPassPollerCheck error %d", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
for(size_t i = 6; i < 10; i++) {
|
||||
FURI_LOG_D(TAG, "rfalPicoPassPollerWriteBlock %d", i);
|
||||
uint8_t data[9] = {0};
|
||||
data[0] = i;
|
||||
memcpy(data + 1, AA1[i].data, RFAL_PICOPASS_MAX_BLOCK_LEN);
|
||||
loclass_doMAC_N(data, sizeof(data), div_key, mac);
|
||||
FURI_LOG_D(
|
||||
TAG,
|
||||
"loclass_doMAC_N %d %02x%02x%02x%02x%02x%02x%02x%02x %02x%02x%02x%02x",
|
||||
i,
|
||||
data[1],
|
||||
data[2],
|
||||
data[3],
|
||||
data[4],
|
||||
data[5],
|
||||
data[6],
|
||||
data[7],
|
||||
data[8],
|
||||
mac[0],
|
||||
mac[1],
|
||||
mac[2],
|
||||
mac[3]);
|
||||
|
||||
err = rfalPicoPassPollerWriteBlock(i, AA1[i].data, mac);
|
||||
if(err != ERR_NONE) {
|
||||
FURI_LOG_E(TAG, "rfalPicoPassPollerWriteBlock error %d", err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return ERR_NONE;
|
||||
}
|
||||
|
||||
int32_t picopass_worker_task(void* context) {
|
||||
PicopassWorker* picopass_worker = context;
|
||||
|
||||
picopass_worker_enable_field();
|
||||
if(picopass_worker->state == PicopassWorkerStateDetect) {
|
||||
picopass_worker_detect(picopass_worker);
|
||||
} else if(picopass_worker->state == PicopassWorkerStateWrite) {
|
||||
picopass_worker_write(picopass_worker);
|
||||
}
|
||||
picopass_worker_disable_field(ERR_NONE);
|
||||
|
||||
@@ -212,27 +287,60 @@ void picopass_worker_detect(PicopassWorker* picopass_worker) {
|
||||
PicopassPacs* pacs = &dev_data->pacs;
|
||||
ReturnCode err;
|
||||
|
||||
PicopassWorkerEvent nextState = PicopassWorkerEventSuccess;
|
||||
|
||||
while(picopass_worker->state == PicopassWorkerStateDetect) {
|
||||
if(picopass_detect_card(1000) == ERR_NONE) {
|
||||
// Process first found device
|
||||
err = picopass_read_card(AA1);
|
||||
if(err != ERR_NONE) {
|
||||
FURI_LOG_E(TAG, "picopass_read_card error %d", err);
|
||||
nextState = PicopassWorkerEventFail;
|
||||
}
|
||||
|
||||
err = picopass_device_parse_credential(AA1, pacs);
|
||||
if(nextState == PicopassWorkerEventSuccess) {
|
||||
err = picopass_device_parse_credential(AA1, pacs);
|
||||
}
|
||||
if(err != ERR_NONE) {
|
||||
FURI_LOG_E(TAG, "picopass_device_parse_credential error %d", err);
|
||||
nextState = PicopassWorkerEventFail;
|
||||
}
|
||||
|
||||
err = picopass_device_parse_wiegand(pacs->credential, &pacs->record);
|
||||
if(nextState == PicopassWorkerEventSuccess) {
|
||||
err = picopass_device_parse_wiegand(pacs->credential, &pacs->record);
|
||||
}
|
||||
if(err != ERR_NONE) {
|
||||
FURI_LOG_E(TAG, "picopass_device_parse_wiegand error %d", err);
|
||||
nextState = PicopassWorkerEventFail;
|
||||
}
|
||||
|
||||
// Notify caller and exit
|
||||
if(picopass_worker->callback) {
|
||||
picopass_worker->callback(PicopassWorkerEventSuccess, picopass_worker->context);
|
||||
picopass_worker->callback(nextState, picopass_worker->context);
|
||||
}
|
||||
break;
|
||||
}
|
||||
furi_delay_ms(100);
|
||||
}
|
||||
}
|
||||
|
||||
void picopass_worker_write(PicopassWorker* picopass_worker) {
|
||||
PicopassDeviceData* dev_data = picopass_worker->dev_data;
|
||||
PicopassBlock* AA1 = dev_data->AA1;
|
||||
ReturnCode err;
|
||||
PicopassWorkerEvent nextState = PicopassWorkerEventSuccess;
|
||||
|
||||
while(picopass_worker->state == PicopassWorkerStateWrite) {
|
||||
if(picopass_detect_card(1000) == ERR_NONE) {
|
||||
err = picopass_write_card(AA1);
|
||||
if(err != ERR_NONE) {
|
||||
FURI_LOG_E(TAG, "picopass_write_card error %d", err);
|
||||
nextState = PicopassWorkerEventFail;
|
||||
}
|
||||
|
||||
// Notify caller and exit
|
||||
if(picopass_worker->callback) {
|
||||
picopass_worker->callback(nextState, picopass_worker->context);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
1
applications/picopass/picopass_worker.h
Executable file → Normal file
1
applications/picopass/picopass_worker.h
Executable file → Normal file
@@ -11,6 +11,7 @@ typedef enum {
|
||||
PicopassWorkerStateReady,
|
||||
// Main worker states
|
||||
PicopassWorkerStateDetect,
|
||||
PicopassWorkerStateWrite,
|
||||
// Transition
|
||||
PicopassWorkerStateStop,
|
||||
} PicopassWorkerState;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user