diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
index a9c2e81cc2..6c0847dce7 100644
--- a/.github/FUNDING.yml
+++ b/.github/FUNDING.yml
@@ -1,2 +1,2 @@
ko_fi: willyjl
-custom: ["https://bunq.me/WillyJL", "https://paypal.me/willyjl1"]
+custom: ["https://paypal.me/willyjl1"]
diff --git a/.github/assets/icon.png b/.github/assets/icon.png
new file mode 100644
index 0000000000..e6b46af58a
Binary files /dev/null and b/.github/assets/icon.png differ
diff --git a/.github/workflow_data/release.md b/.github/workflow_data/release.md
index f556bfb57f..8402dd1083 100644
--- a/.github/workflow_data/release.md
+++ b/.github/workflow_data/release.md
@@ -8,24 +8,14 @@
**Check the [install guide](https://github.com/Next-Flip/Momentum-Firmware#install) if you're not sure, or [join our Discord](https://discord.gg/momentum) if you have questions or encounter issues!**
## ❤️ Support
-If you enjoy the firmware, please consider donating to the team, or sharing it with others! :D
+If you enjoy the firmware please __**spread the word!**__ And if you really love it, maybe consider donating to the team? :D
> **[Ko-fi](https://ko-fi.com/willyjl)**: One-off or Recurring, No signup required
-> **[Bank transfer](https://bunq.me/WillyJL)**: One-off, No signup required
-
> **[PayPal](https://paypal.me/willyjl1)**: One-off, Signup required
-> **BCH**: `1EnCi1HF8Jw6m2dWSUwHLbCRbVBCQSyDKm`
-
-> **ETH**: `0x90b8284c3eba44108427e3148ff8efa0ae7a61a8`
-
> **BTC**: `1EnCi1HF8Jw6m2dWSUwHLbCRbVBCQSyDKm`
-> **SHIB**: `0x90b8284c3eba44108427e3148ff8efa0ae7a61a8`
-
-> **DOGE**: `DNUdUqtmWaAiJ6yoV6hRNEh6Nn1Tg4Aorr`
-
**Thank you <3**
## 🚀 Changelog
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 329dfdc0d0..d054252155 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -54,14 +54,14 @@ jobs:
- name: "Make tgz, zip and sdk"
run: bash .github/workflow_data/package.sh
- - name: Send devbuild webhook
- if: "github.event_name == 'push' && github.ref_name == 'dev' && !contains(github.event.head_commit.message, '--nobuild')"
- env:
- NC_HOST: "https://cloud.cynthialabs.net/"
- NC_USERAGENT: "${{ secrets.NC_USERAGENT }}"
- NC_USER: "${{ secrets.NC_USER }}"
- NC_PASS: "${{ secrets.NC_PASS }}"
- BUILD_WEBHOOK: ${{ secrets.BUILD_WEBHOOK }}
- run: |
- python -m pip install pyncclient
- python .github/workflow_data/devbuild.py
+ # - name: Send devbuild webhook
+ # if: "github.event_name == 'push' && github.ref_name == 'dev' && !contains(github.event.head_commit.message, '--nobuild')"
+ # env:
+ # NC_HOST: "https://cloud.cynthialabs.net/"
+ # NC_USERAGENT: "${{ secrets.NC_USERAGENT }}"
+ # NC_USER: "${{ secrets.NC_USER }}"
+ # NC_PASS: "${{ secrets.NC_PASS }}"
+ # BUILD_WEBHOOK: ${{ secrets.BUILD_WEBHOOK }}
+ # run: |
+ # python -m pip install pyncclient
+ # python .github/workflow_data/devbuild.py
diff --git a/ReadMe.md b/ReadMe.md
index 6d64ebf9bd..81098f41f4 100644
--- a/ReadMe.md
+++ b/ReadMe.md
@@ -7,7 +7,7 @@
Install | Features | Discord | Donate
-This custom firmware is based on the [Official Firmware](https://github.com/flipperdevices/flipperzero-firmware), and features most of the awesome features from [Unleashed](https://github.com/DarkFlippers/unleashed-firmware). It is a direct continuation of the Xtreme firmware, built by the same (and only) developers who made that project special, until being unfairly thrown out.
+This custom firmware is based on the [Official Firmware](https://github.com/flipperdevices/flipperzero-firmware), and includes most of the awesome features from [Unleashed](https://github.com/DarkFlippers/unleashed-firmware). It is a direct continuation of the Xtreme firmware, built by the same (and only) developers who made that project special.
-----
@@ -83,7 +83,7 @@ In USB mode it also enables additional functionality to spoof the manufacturer a
There are too many to name them all, this is a **non-comprehensive** list of the **most notable from an end-user perspective**. For a more detailed list, you can read through the [**changelogs**](https://github.com/Next-Flip/Momentum-Firmware/releases) and commits/code. Also, you can find a **feature comparison with other firmwares** on [our website](https://momentum-fw.dev/#features).
-Note that this repo is always updated with the great work from our friends at [Unleashed](https://github.com/DarkFlippers/unleashed-firmware) and the latest changes from [OFW](https://github.com/flipperdevices/flipperzero-firmware). Below are mentioned only **our** changes that we can actually be credited for, so make sure to check their fantastic additions aswell. And a huge thank you to both teams!
+Note that this repo is always updated with the great work from our friends at [Unleashed](https://github.com/DarkFlippers/unleashed-firmware) and the latest changes from [OFW](https://github.com/flipperdevices/flipperzero-firmware). Below are mentioned only **our** changes that we can actually be credited for, so make sure to check their fantastic additions aswell. And a huge thank you to both teams!
```txt
[Added]
@@ -192,22 +192,12 @@ $ ./fbt launch APPSRC=your_appid
## ❤️ Support
-If you enjoy the firmware, please consider donating to the team, or sharing it with others! :D
+If you enjoy the firmware please __**spread the word!**__ And if you really love it, maybe consider donating to the team? :D
> **[Ko-fi](https://ko-fi.com/willyjl)**: One-off or Recurring, No signup required
-> **[Bank transfer](https://bunq.me/WillyJL)**: One-off, No signup required
-
> **[PayPal](https://paypal.me/willyjl1)**: One-off, Signup required
-> **BCH**: `1EnCi1HF8Jw6m2dWSUwHLbCRbVBCQSyDKm`
-
-> **ETH**: `0x90b8284c3eba44108427e3148ff8efa0ae7a61a8`
-
> **BTC**: `1EnCi1HF8Jw6m2dWSUwHLbCRbVBCQSyDKm`
-> **SHIB**: `0x90b8284c3eba44108427e3148ff8efa0ae7a61a8`
-
-> **DOGE**: `DNUdUqtmWaAiJ6yoV6hRNEh6Nn1Tg4Aorr`
-
**Thank you <3**
diff --git a/SConstruct b/SConstruct
index 8053777747..2c1d62a795 100644
--- a/SConstruct
+++ b/SConstruct
@@ -66,6 +66,7 @@ if GetOption("fullenv") or any(
# Target for self-update package
dist_basic_arguments = [
+ "${ARGS}",
"--bundlever",
"${UPDATE_VERSION_STRING}",
]
@@ -182,6 +183,7 @@ fap_deploy = distenv.PhonyTarget(
"send",
"${SOURCE}",
"/ext/apps",
+ "${ARGS}",
]
]
),
@@ -208,7 +210,7 @@ distenv.Alias("jflash", firmware_jflash)
distenv.PhonyTarget(
"gdb_trace_all",
- "$GDB $GDBOPTS $SOURCES $GDBFLASH",
+ [["${GDB}", "${GDBOPTS}", "${SOURCES}", "${GDBFLASH}"]],
source=firmware_env["FW_ELF"],
GDBOPTS="${GDBOPTS_BASE}",
GDBREMOTE="${OPENOCD_GDB_PIPE}",
@@ -272,19 +274,35 @@ distenv.PhonyTarget(
# Just start OpenOCD
distenv.PhonyTarget(
"openocd",
- "${OPENOCDCOM}",
+ [["${OPENOCDCOM}", "${ARGS}"]],
)
# Linter
distenv.PhonyTarget(
"lint",
- [["${PYTHON3}", "${FBT_SCRIPT_DIR}/lint.py", "check", "${LINT_SOURCES}"]],
+ [
+ [
+ "${PYTHON3}",
+ "${FBT_SCRIPT_DIR}/lint.py",
+ "check",
+ "${LINT_SOURCES}",
+ "${ARGS}",
+ ]
+ ],
LINT_SOURCES=[n.srcnode() for n in firmware_env["LINT_SOURCES"]],
)
distenv.PhonyTarget(
"format",
- [["${PYTHON3}", "${FBT_SCRIPT_DIR}/lint.py", "format", "${LINT_SOURCES}"]],
+ [
+ [
+ "${PYTHON3}",
+ "${FBT_SCRIPT_DIR}/lint.py",
+ "format",
+ "${LINT_SOURCES}",
+ "${ARGS}",
+ ]
+ ],
LINT_SOURCES=[n.srcnode() for n in firmware_env["LINT_SOURCES"]],
)
@@ -307,7 +325,16 @@ firmware_env.Append(
)
-black_commandline = "@${PYTHON3} -m black ${PY_BLACK_ARGS} ${PY_LINT_SOURCES}"
+black_commandline = [
+ [
+ "@${PYTHON3}",
+ "-m",
+ "black",
+ "${PY_BLACK_ARGS}",
+ "${PY_LINT_SOURCES}",
+ "${ARGS}",
+ ]
+]
black_base_args = [
"--include",
'"(\\.scons|\\.py|SConscript|SConstruct|\\.fam)$"',
@@ -333,12 +360,28 @@ distenv.PhonyTarget(
# Start Flipper CLI via PySerial's miniterm
distenv.PhonyTarget(
- "cli", [["${PYTHON3}", "${FBT_SCRIPT_DIR}/serial_cli.py", "-p", "${FLIP_PORT}"]]
+ "cli",
+ [
+ [
+ "${PYTHON3}",
+ "${FBT_SCRIPT_DIR}/serial_cli.py",
+ "-p",
+ "${FLIP_PORT}",
+ "${ARGS}",
+ ]
+ ],
)
-# Update WiFi devboard firmware
+# Update WiFi devboard firmware with release channel
distenv.PhonyTarget(
- "devboard_flash", [["${PYTHON3}", "${FBT_SCRIPT_DIR}/wifi_board.py"]]
+ "devboard_flash",
+ [
+ [
+ "${PYTHON3}",
+ "${FBT_SCRIPT_DIR}/wifi_board.py",
+ "${ARGS}",
+ ]
+ ],
)
@@ -353,7 +396,7 @@ distenv.PhonyTarget(
distenv.PhonyTarget(
"get_stlink",
distenv.Action(
- lambda **kw: distenv.GetDevices(),
+ lambda **_: distenv.GetDevices(),
None,
),
)
diff --git a/applications/external b/applications/external
index dae7906c1a..762688f712 160000
--- a/applications/external
+++ b/applications/external
@@ -1 +1 @@
-Subproject commit dae7906c1abb4fd6d4198669a84d92dd1902032b
+Subproject commit 762688f7127324da6ef38a0d22c04a05c44fae71
diff --git a/applications/main/archive/scenes/archive_scene_delete.c b/applications/main/archive/scenes/archive_scene_delete.c
index 7dac0a9d3d..217e4a490e 100644
--- a/applications/main/archive/scenes/archive_scene_delete.c
+++ b/applications/main/archive/scenes/archive_scene_delete.c
@@ -25,6 +25,12 @@ void archive_scene_delete_on_enter(void* context) {
filename = furi_string_alloc();
ArchiveFile_t* current = archive_get_current_file(app->browser);
+
+ FuriString* filename_no_ext = furi_string_alloc();
+ path_extract_filename(current->path, filename_no_ext, true);
+ strlcpy(app->text_store, furi_string_get_cstr(filename_no_ext), MAX_NAME_LEN);
+ furi_string_free(filename_no_ext);
+
path_extract_filename(current->path, filename, false);
char delete_str[64];
diff --git a/applications/main/bad_kb/helpers/ducky_script.c b/applications/main/bad_kb/helpers/ducky_script.c
index b94ab73b3d..e7f41b3ed3 100644
--- a/applications/main/bad_kb/helpers/ducky_script.c
+++ b/applications/main/bad_kb/helpers/ducky_script.c
@@ -382,6 +382,7 @@ static void ducky_script_preload(BadKbScript* bad_kb, File* script_file) {
app->has_usb_id = strncmp(line_tmp, ducky_cmd_id, strlen(ducky_cmd_id)) == 0;
app->has_bt_id = strncmp(line_tmp, ducky_cmd_bt_id, strlen(ducky_cmd_bt_id)) == 0;
+ // Auto-switch to mode chosen with ID/BT_ID, can override manually in config screen
if(app->has_usb_id) {
app->is_bt = false;
app->set_usb_id = ducky_set_usb_id(bad_kb, &line_tmp[strlen(ducky_cmd_id) + 1]);
diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config.c b/applications/main/bad_kb/scenes/bad_kb_scene_config.c
index ca5ec64000..3083e86960 100644
--- a/applications/main/bad_kb/scenes/bad_kb_scene_config.c
+++ b/applications/main/bad_kb/scenes/bad_kb_scene_config.c
@@ -52,11 +52,6 @@ void bad_kb_scene_config_on_enter(void* context) {
var_item_list, "Connection", 2, bad_kb_scene_config_connection_callback, bad_kb);
variable_item_set_current_value_index(item, bad_kb->is_bt);
variable_item_set_current_value_text(item, bad_kb->is_bt ? "BT" : "USB");
- if(bad_kb->has_usb_id) {
- variable_item_set_locked(item, true, "Script has\nID cmd!\nLocked to\nUSB Mode!");
- } else if(bad_kb->has_bt_id) {
- variable_item_set_locked(item, true, "Script has\nBT_ID cmd!\nLocked to\nBT Mode!");
- }
if(bad_kb->is_bt) {
item = variable_item_list_add(
@@ -65,43 +60,24 @@ void bad_kb_scene_config_on_enter(void* context) {
variable_item_set_current_value_text(item, bad_kb->bt_remember ? "ON" : "OFF");
item = variable_item_list_add(var_item_list, "BT Device Name", 0, NULL, bad_kb);
- if(bad_kb->set_bt_id) {
- variable_item_set_locked(item, true, "Script has\nBT_ID cmd!\nLocked to\nset Name!");
- }
item = variable_item_list_add(var_item_list, "BT MAC Address", 0, NULL, bad_kb);
if(bad_kb->bt_remember) {
variable_item_set_locked(item, true, "Remember\nmust be Off!");
- } else if(bad_kb->set_bt_id) {
- variable_item_set_locked(item, true, "Script has\nBT_ID cmd!\nLocked to\nset MAC!");
}
item = variable_item_list_add(var_item_list, "Randomize BT MAC", 0, NULL, bad_kb);
if(bad_kb->bt_remember) {
variable_item_set_locked(item, true, "Remember\nmust be Off!");
- } else if(bad_kb->set_bt_id) {
- variable_item_set_locked(item, true, "Script has\nBT_ID cmd!\nLocked to\nset MAC!");
}
} else {
item = variable_item_list_add(var_item_list, "USB Manufacturer", 0, NULL, bad_kb);
- if(bad_kb->set_usb_id) {
- variable_item_set_locked(item, true, "Script has\nID cmd!\nLocked to\nset Mname!");
- }
item = variable_item_list_add(var_item_list, "USB Product Name", 0, NULL, bad_kb);
- if(bad_kb->set_usb_id) {
- variable_item_set_locked(item, true, "Script has\nID cmd!\nLocked to\nset Pname!");
- }
item = variable_item_list_add(var_item_list, "USB VID and PID", 0, NULL, bad_kb);
- if(bad_kb->set_usb_id) {
- variable_item_set_locked(item, true, "Script has\nID cmd!\nLocked to\nset IDs!");
- }
item = variable_item_list_add(var_item_list, "Randomize USB VID:PID", 0, NULL, bad_kb);
- if(bad_kb->set_usb_id) {
- variable_item_set_locked(item, true, "Script has\nID cmd!\nLocked to\nset IDs!");
- }
}
variable_item_list_set_enter_callback(
@@ -142,7 +118,15 @@ bool bad_kb_scene_config_on_event(void* context, SceneManagerEvent event) {
scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigBtMac);
break;
case VarItemListIndexBtRandomizeMac:
+ // Set user config and remember
furi_hal_random_fill_buf(bad_kb->config.ble.mac, sizeof(bad_kb->config.ble.mac));
+ // Apply to ID config so its temporarily overridden
+ if(bad_kb->set_bt_id) {
+ memcpy(
+ bad_kb->id_config.ble.mac,
+ bad_kb->config.ble.mac,
+ sizeof(bad_kb->id_config.ble.mac));
+ }
bad_kb_config_refresh(bad_kb);
break;
default:
@@ -166,8 +150,14 @@ bool bad_kb_scene_config_on_event(void* context, SceneManagerEvent event) {
case VarItemListIndexUsbRandomizeVidPid:
furi_hal_random_fill_buf(
(void*)bad_kb->usb_vidpid_buf, sizeof(bad_kb->usb_vidpid_buf));
+ // Set user config and remember
bad_kb->config.usb.vid = bad_kb->usb_vidpid_buf[0];
bad_kb->config.usb.pid = bad_kb->usb_vidpid_buf[1];
+ // Apply to ID config so its temporarily overridden
+ if(bad_kb->set_usb_id) {
+ bad_kb->id_config.usb.vid = bad_kb->config.usb.vid;
+ bad_kb->id_config.usb.pid = bad_kb->config.usb.pid;
+ }
bad_kb_config_refresh(bad_kb);
break;
default:
diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config_bt_mac.c b/applications/main/bad_kb/scenes/bad_kb_scene_config_bt_mac.c
index 5439c0bc4c..ea9849448e 100644
--- a/applications/main/bad_kb/scenes/bad_kb_scene_config_bt_mac.c
+++ b/applications/main/bad_kb/scenes/bad_kb_scene_config_bt_mac.c
@@ -10,7 +10,10 @@ void bad_kb_scene_config_bt_mac_on_enter(void* context) {
BadKbApp* bad_kb = context;
ByteInput* byte_input = bad_kb->byte_input;
- memcpy(bad_kb->bt_mac_buf, bad_kb->config.ble.mac, sizeof(bad_kb->bt_mac_buf));
+ memcpy(
+ bad_kb->bt_mac_buf,
+ bad_kb->set_bt_id ? bad_kb->id_config.ble.mac : bad_kb->config.ble.mac,
+ sizeof(bad_kb->bt_mac_buf));
furi_hal_bt_reverse_mac_addr(bad_kb->bt_mac_buf);
byte_input_set_header_text(byte_input, "Set BT MAC address");
@@ -33,7 +36,15 @@ bool bad_kb_scene_config_bt_mac_on_event(void* context, SceneManagerEvent event)
consumed = true;
if(event.event == BadKbAppCustomEventByteInputDone) {
furi_hal_bt_reverse_mac_addr(bad_kb->bt_mac_buf);
+ // Set user config and remember
memcpy(bad_kb->config.ble.mac, bad_kb->bt_mac_buf, sizeof(bad_kb->config.ble.mac));
+ // Apply to ID config so its temporarily overridden
+ if(bad_kb->set_bt_id) {
+ memcpy(
+ bad_kb->id_config.ble.mac,
+ bad_kb->bt_mac_buf,
+ sizeof(bad_kb->id_config.ble.mac));
+ }
bad_kb_config_refresh(bad_kb);
}
scene_manager_previous_scene(bad_kb->scene_manager);
diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config_bt_name.c b/applications/main/bad_kb/scenes/bad_kb_scene_config_bt_name.c
index 04a02aed40..67a92c6bf1 100644
--- a/applications/main/bad_kb/scenes/bad_kb_scene_config_bt_name.c
+++ b/applications/main/bad_kb/scenes/bad_kb_scene_config_bt_name.c
@@ -10,7 +10,10 @@ void bad_kb_scene_config_bt_name_on_enter(void* context) {
BadKbApp* bad_kb = context;
TextInput* text_input = bad_kb->text_input;
- strlcpy(bad_kb->bt_name_buf, bad_kb->config.ble.name, sizeof(bad_kb->bt_name_buf));
+ strlcpy(
+ bad_kb->bt_name_buf,
+ bad_kb->set_bt_id ? bad_kb->id_config.ble.name : bad_kb->config.ble.name,
+ sizeof(bad_kb->bt_name_buf));
text_input_set_header_text(text_input, "Set BT device name");
text_input_set_result_callback(
@@ -31,7 +34,15 @@ bool bad_kb_scene_config_bt_name_on_event(void* context, SceneManagerEvent event
if(event.type == SceneManagerEventTypeCustom) {
consumed = true;
if(event.event == BadKbAppCustomEventTextInputDone) {
+ // Set user config and remember
strlcpy(bad_kb->config.ble.name, bad_kb->bt_name_buf, sizeof(bad_kb->config.ble.name));
+ // Apply to ID config so its temporarily overridden
+ if(bad_kb->set_bt_id) {
+ strlcpy(
+ bad_kb->id_config.ble.name,
+ bad_kb->bt_name_buf,
+ sizeof(bad_kb->id_config.ble.name));
+ }
bad_kb_config_refresh(bad_kb);
}
scene_manager_previous_scene(bad_kb->scene_manager);
diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config_usb_name.c b/applications/main/bad_kb/scenes/bad_kb_scene_config_usb_name.c
index c7dd7a2fa3..0cd9da1c86 100644
--- a/applications/main/bad_kb/scenes/bad_kb_scene_config_usb_name.c
+++ b/applications/main/bad_kb/scenes/bad_kb_scene_config_usb_name.c
@@ -11,10 +11,16 @@ void bad_kb_scene_config_usb_name_on_enter(void* context) {
TextInput* text_input = bad_kb->text_input;
if(scene_manager_get_scene_state(bad_kb->scene_manager, BadKbSceneConfigUsbName)) {
- strlcpy(bad_kb->usb_name_buf, bad_kb->config.usb.manuf, sizeof(bad_kb->usb_name_buf));
+ strlcpy(
+ bad_kb->usb_name_buf,
+ bad_kb->set_usb_id ? bad_kb->id_config.usb.manuf : bad_kb->config.usb.manuf,
+ sizeof(bad_kb->usb_name_buf));
text_input_set_header_text(text_input, "Set USB manufacturer name");
} else {
- strlcpy(bad_kb->usb_name_buf, bad_kb->config.usb.product, sizeof(bad_kb->usb_name_buf));
+ strlcpy(
+ bad_kb->usb_name_buf,
+ bad_kb->set_usb_id ? bad_kb->id_config.usb.product : bad_kb->config.usb.product,
+ sizeof(bad_kb->usb_name_buf));
text_input_set_header_text(text_input, "Set USB product name");
}
@@ -37,15 +43,31 @@ bool bad_kb_scene_config_usb_name_on_event(void* context, SceneManagerEvent even
consumed = true;
if(event.event == BadKbAppCustomEventTextInputDone) {
if(scene_manager_get_scene_state(bad_kb->scene_manager, BadKbSceneConfigUsbName)) {
+ // Set user config and remember
strlcpy(
bad_kb->config.usb.manuf,
bad_kb->usb_name_buf,
- sizeof(bad_kb->config.usb.product));
+ sizeof(bad_kb->config.usb.manuf));
+ // Apply to ID config so its temporarily overridden
+ if(bad_kb->set_usb_id) {
+ strlcpy(
+ bad_kb->id_config.usb.manuf,
+ bad_kb->usb_name_buf,
+ sizeof(bad_kb->id_config.usb.manuf));
+ }
} else {
+ // Set user config and remember
strlcpy(
bad_kb->config.usb.product,
bad_kb->usb_name_buf,
sizeof(bad_kb->config.usb.product));
+ // Apply to ID config so its temporarily overridden
+ if(bad_kb->set_usb_id) {
+ strlcpy(
+ bad_kb->id_config.usb.product,
+ bad_kb->usb_name_buf,
+ sizeof(bad_kb->id_config.usb.product));
+ }
}
bad_kb_config_refresh(bad_kb);
}
diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config_usb_vidpid.c b/applications/main/bad_kb/scenes/bad_kb_scene_config_usb_vidpid.c
index 33f899f410..43b1314655 100644
--- a/applications/main/bad_kb/scenes/bad_kb_scene_config_usb_vidpid.c
+++ b/applications/main/bad_kb/scenes/bad_kb_scene_config_usb_vidpid.c
@@ -10,8 +10,13 @@ void bad_kb_scene_config_usb_vidpid_on_enter(void* context) {
BadKbApp* bad_kb = context;
ByteInput* byte_input = bad_kb->byte_input;
- bad_kb->usb_vidpid_buf[0] = __REVSH(bad_kb->config.usb.vid);
- bad_kb->usb_vidpid_buf[1] = __REVSH(bad_kb->config.usb.pid);
+ if(bad_kb->set_usb_id) {
+ bad_kb->usb_vidpid_buf[0] = __REVSH(bad_kb->id_config.usb.vid);
+ bad_kb->usb_vidpid_buf[1] = __REVSH(bad_kb->id_config.usb.pid);
+ } else {
+ bad_kb->usb_vidpid_buf[0] = __REVSH(bad_kb->config.usb.vid);
+ bad_kb->usb_vidpid_buf[1] = __REVSH(bad_kb->config.usb.pid);
+ }
byte_input_set_header_text(byte_input, "Set USB VID:PID");
byte_input_set_result_callback(
@@ -32,8 +37,14 @@ bool bad_kb_scene_config_usb_vidpid_on_event(void* context, SceneManagerEvent ev
if(event.type == SceneManagerEventTypeCustom) {
consumed = true;
if(event.event == BadKbAppCustomEventByteInputDone) {
+ // Set user config and remember
bad_kb->config.usb.vid = __REVSH(bad_kb->usb_vidpid_buf[0]);
bad_kb->config.usb.pid = __REVSH(bad_kb->usb_vidpid_buf[1]);
+ // Apply to ID config so its temporarily overridden
+ if(bad_kb->set_usb_id) {
+ bad_kb->id_config.usb.vid = bad_kb->config.usb.vid;
+ bad_kb->id_config.usb.pid = bad_kb->config.usb.pid;
+ }
bad_kb_config_refresh(bad_kb);
}
scene_manager_previous_scene(bad_kb->scene_manager);
diff --git a/applications/main/momentum_app/scenes/momentum_app_scene_protocols_gpio.c b/applications/main/momentum_app/scenes/momentum_app_scene_protocols_gpio.c
index 061a2b3641..ae561ae6ed 100644
--- a/applications/main/momentum_app/scenes/momentum_app_scene_protocols_gpio.c
+++ b/applications/main/momentum_app/scenes/momentum_app_scene_protocols_gpio.c
@@ -58,18 +58,6 @@ static void momentum_app_scene_protocols_gpio_nmea_channel_changed(VariableItem*
app->save_settings = true;
}
-static void momentum_app_scene_protocols_gpio_general_channel_changed(VariableItem* item) {
- MomentumApp* app = variable_item_get_context(item);
- momentum_settings.uart_general_channel = variable_item_get_current_value_index(item) == 0 ?
- FuriHalSerialIdUsart :
- FuriHalSerialIdLpuart;
- variable_item_set_current_value_text(
- item,
- momentum_settings.uart_general_channel == FuriHalSerialIdUsart ? UART_DEFAULT :
- UART_EXTRA);
- app->save_settings = true;
-}
-
void momentum_app_scene_protocols_gpio_on_enter(void* context) {
MomentumApp* app = context;
VariableItemList* var_item_list = app->var_item_list;
@@ -113,18 +101,6 @@ void momentum_app_scene_protocols_gpio_on_enter(void* context) {
item,
momentum_settings.uart_nmea_channel == FuriHalSerialIdUsart ? UART_DEFAULT : UART_EXTRA);
- item = variable_item_list_add(
- var_item_list,
- "General UART",
- 2,
- momentum_app_scene_protocols_gpio_general_channel_changed,
- app);
- variable_item_set_current_value_index(item, momentum_settings.uart_general_channel);
- variable_item_set_current_value_text(
- item,
- momentum_settings.uart_general_channel == FuriHalSerialIdUsart ? UART_DEFAULT :
- UART_EXTRA);
-
variable_item_list_set_enter_callback(
var_item_list, momentum_app_scene_protocols_gpio_var_item_list_callback, app);
diff --git a/applications/main/nfc/api/mosgortrans/mosgortrans_util.c b/applications/main/nfc/api/mosgortrans/mosgortrans_util.c
new file mode 100644
index 0000000000..f2484a2af5
--- /dev/null
+++ b/applications/main/nfc/api/mosgortrans/mosgortrans_util.c
@@ -0,0 +1,1308 @@
+#include "mosgortrans_util.h"
+
+#define TAG "Mosgortrans"
+
+void from_days_to_datetime(uint32_t days, DateTime* datetime, uint16_t start_year) {
+ uint32_t timestamp = days * 24 * 60 * 60;
+ DateTime start_datetime = {0};
+ start_datetime.year = start_year - 1;
+ start_datetime.month = 12;
+ start_datetime.day = 31;
+ timestamp += datetime_datetime_to_timestamp(&start_datetime);
+ datetime_timestamp_to_datetime(timestamp, datetime);
+}
+
+void from_minutes_to_datetime(uint32_t minutes, DateTime* datetime, uint16_t start_year) {
+ uint32_t timestamp = minutes * 60;
+ DateTime start_datetime = {0};
+ start_datetime.year = start_year - 1;
+ start_datetime.month = 12;
+ start_datetime.day = 31;
+ timestamp += datetime_datetime_to_timestamp(&start_datetime);
+ datetime_timestamp_to_datetime(timestamp, datetime);
+}
+
+void from_seconds_to_datetime(uint32_t seconds, DateTime* datetime, uint16_t start_year) {
+ uint32_t timestamp = seconds;
+ DateTime start_datetime = {0};
+ start_datetime.year = start_year - 1;
+ start_datetime.month = 12;
+ start_datetime.day = 31;
+ timestamp += datetime_datetime_to_timestamp(&start_datetime);
+ datetime_timestamp_to_datetime(timestamp, datetime);
+}
+
+typedef struct {
+ uint16_t view; //101
+ uint16_t type; //102
+ uint8_t layout; //111
+ uint8_t layout2; //112
+ uint16_t blank_type; //121
+ uint16_t type_of_extended; //122
+ uint8_t extended; //123
+ uint8_t benefit_code; //124
+ uint32_t number; //201
+ uint16_t use_before_date; //202
+ uint16_t use_before_date2; //202.2
+ uint16_t use_with_date; //205
+ uint8_t requires_activation; //301
+ uint16_t activate_during; //302
+ uint16_t extension_counter; //304
+ uint8_t blocked; //303
+ uint32_t valid_from_date; //311
+ uint16_t valid_to_date; //312
+ uint8_t valid_for_days; //313
+ uint32_t valid_for_minutes; //314
+ uint16_t valid_for_time; //316
+ uint16_t valid_for_time2; //316.2
+ uint32_t valid_to_time; //317
+ uint16_t remaining_trips; //321
+ uint8_t remaining_trips1; //321.1
+ uint32_t remaining_funds; //322
+ uint16_t total_trips; //331
+ uint16_t start_trip_date; //402
+ uint16_t start_trip_time; //403
+ uint32_t start_trip_neg_minutes; //404
+ uint32_t start_trip_minutes; //405
+ uint8_t start_trip_seconds; //406
+ uint8_t minutes_pass; //412
+ uint8_t passage_5_minutes; //413
+ uint8_t metro_ride_with; //414
+ uint8_t transport_type; //421
+ uint8_t transport_type_flag; //421.0
+ uint8_t transport_type1; //421.1
+ uint8_t transport_type2; //421.2
+ uint8_t transport_type3; //421.3
+ uint8_t transport_type4; //421.4
+ uint16_t validator; //422
+ uint8_t validator1; //422.1
+ uint16_t validator2; //422.2
+ uint16_t route; //424
+ uint8_t passage_in_metro; //431
+ uint8_t transfer_in_metro; //432
+ uint8_t passages_ground_transport; //433
+ uint8_t fare_trip; //441
+ uint16_t crc16; //501.1
+ uint16_t crc16_2; //501.2
+ uint32_t hash; //502
+ uint16_t hash1; //502.1
+ uint32_t hash2; //502.2
+ uint8_t geozone_a; //GeoZoneA
+ uint8_t geozone_b; //GeoZoneB
+ uint8_t company; //Company
+ uint8_t units; //Units
+ uint64_t rfu1; //rfu1
+ uint16_t rfu2; //rfu2
+ uint32_t rfu3; //rfu3
+ uint8_t rfu4; //rfu4
+ uint8_t rfu5; //rfu5
+ uint8_t write_enabled; //write_enabled
+ uint32_t tech_code; //TechCode
+ uint8_t interval; //Interval
+ uint16_t app_code1; //AppCode1
+ uint16_t app_code2; //AppCode2
+ uint16_t app_code3; //AppCode3
+ uint16_t app_code4; //AppCode4
+ uint16_t type1; //Type1
+ uint16_t type2; //Type2
+ uint16_t type3; //Type3
+ uint16_t type4; //Type4
+ uint8_t zoo; //zoo
+} BlockData;
+
+void parse_layout_2(BlockData* data_block, const MfClassicBlock* block) {
+ data_block->view = bit_lib_get_bits_16(block->data, 0x00, 10); //101
+ data_block->type = bit_lib_get_bits_16(block->data, 0x0A, 10); //102
+ data_block->number = bit_lib_get_bits_32(block->data, 0x14, 32); //201
+ data_block->layout = bit_lib_get_bits(block->data, 0x34, 4); //111
+ data_block->use_before_date = bit_lib_get_bits_16(block->data, 0x38, 16); //202
+ data_block->benefit_code = bit_lib_get_bits(block->data, 0x48, 8); //124
+ data_block->rfu1 = bit_lib_get_bits_32(block->data, 0x50, 32); //rfu1
+ data_block->crc16 = bit_lib_get_bits_16(block->data, 0x70, 16); //501.1
+ data_block->blocked = bit_lib_get_bits(block->data, 0x80, 1); //303
+ data_block->start_trip_time = bit_lib_get_bits_16(block->data, 0x81, 12); //403
+ data_block->start_trip_date = bit_lib_get_bits_16(block->data, 0x8D, 16); //402
+ data_block->valid_from_date = bit_lib_get_bits_16(block->data, 0x9D, 16); //311
+ data_block->valid_to_date = bit_lib_get_bits_16(block->data, 0xAD, 16); //312
+ data_block->start_trip_seconds = bit_lib_get_bits(block->data, 0xDB, 6); //406
+ data_block->transport_type1 = bit_lib_get_bits(block->data, 0xC3, 2); //421.1
+ data_block->transport_type2 = bit_lib_get_bits(block->data, 0xC5, 2); //421.2
+ data_block->transport_type3 = bit_lib_get_bits(block->data, 0xC7, 2); //421.3
+ data_block->transport_type4 = bit_lib_get_bits(block->data, 0xC9, 2); //421.4
+ data_block->use_with_date = bit_lib_get_bits_16(block->data, 0xBD, 16); //205
+ data_block->route = bit_lib_get_bits(block->data, 0xCD, 1); //424
+ data_block->validator1 = bit_lib_get_bits_16(block->data, 0xCE, 15); //422.1
+ data_block->validator = bit_lib_get_bits_16(block->data, 0xCD, 16);
+ data_block->total_trips = bit_lib_get_bits_16(block->data, 0xDD, 16); //331
+ data_block->write_enabled = bit_lib_get_bits(block->data, 0xED, 1); //write_enabled
+ data_block->rfu2 = bit_lib_get_bits(block->data, 0xEE, 2); //rfu2
+ data_block->crc16_2 = bit_lib_get_bits_16(block->data, 0xF0, 16); //501.2
+}
+
+void parse_layout_6(BlockData* data_block, const MfClassicBlock* block) {
+ data_block->view = bit_lib_get_bits_16(block->data, 0x00, 10); //101
+ data_block->type = bit_lib_get_bits_16(block->data, 0x0A, 10); //102
+ data_block->number = bit_lib_get_bits_32(block->data, 0x14, 32); //201
+ data_block->layout = bit_lib_get_bits(block->data, 0x34, 4); //111
+ data_block->use_before_date = bit_lib_get_bits_16(block->data, 0x38, 16); //202
+ data_block->geozone_a = bit_lib_get_bits(block->data, 0x48, 4); //GeoZoneA
+ data_block->geozone_b = bit_lib_get_bits(block->data, 0x4C, 4); //GeoZoneB
+ data_block->blank_type = bit_lib_get_bits_16(block->data, 0x50, 10); //121
+ data_block->type_of_extended = bit_lib_get_bits_16(block->data, 0x5A, 10); //122
+ data_block->rfu1 = bit_lib_get_bits_16(block->data, 0x64, 12); //rfu1
+ data_block->crc16 = bit_lib_get_bits_16(block->data, 0x70, 16); //501.1
+ data_block->blocked = bit_lib_get_bits(block->data, 0x80, 1); //303
+ data_block->start_trip_time = bit_lib_get_bits_16(block->data, 0x81, 12); //403
+ data_block->start_trip_date = bit_lib_get_bits_16(block->data, 0x8D, 16); //402
+ data_block->valid_from_date = bit_lib_get_bits_16(block->data, 0x9D, 16); //311
+ data_block->valid_to_date = bit_lib_get_bits_16(block->data, 0xAD, 16); //312
+ data_block->company = bit_lib_get_bits(block->data, 0xBD, 4); //Company
+ data_block->validator1 = bit_lib_get_bits(block->data, 0xC1, 4); //422.1
+ data_block->remaining_trips = bit_lib_get_bits_16(block->data, 0xC5, 10); //321
+ data_block->units = bit_lib_get_bits(block->data, 0xCF, 6); //Units
+ data_block->validator2 = bit_lib_get_bits_16(block->data, 0xD5, 10); //422.2
+ data_block->total_trips = bit_lib_get_bits_16(block->data, 0xDF, 16); //331
+ data_block->extended = bit_lib_get_bits(block->data, 0xEF, 1); //123
+ data_block->crc16_2 = bit_lib_get_bits_16(block->data, 0xF0, 16); //501.2
+}
+
+void parse_layout_8(BlockData* data_block, const MfClassicBlock* block) {
+ data_block->view = bit_lib_get_bits_16(block->data, 0x00, 10); //101
+ data_block->type = bit_lib_get_bits_16(block->data, 0x0A, 10); //102
+ data_block->number = bit_lib_get_bits_32(block->data, 0x14, 32); //201
+ data_block->layout = bit_lib_get_bits(block->data, 0x34, 4); //111
+ data_block->use_before_date = bit_lib_get_bits_16(block->data, 0x38, 16); //202
+ data_block->rfu1 = bit_lib_get_bits_64(block->data, 0x48, 56); //rfu1
+ data_block->valid_from_date = bit_lib_get_bits_16(block->data, 0x80, 16); //311
+ data_block->valid_for_days = bit_lib_get_bits(block->data, 0x90, 8); //313
+ data_block->requires_activation = bit_lib_get_bits(block->data, 0x98, 1); //301
+ data_block->rfu2 = bit_lib_get_bits(block->data, 0x99, 7); //rfu2
+ data_block->remaining_trips1 = bit_lib_get_bits(block->data, 0xA0, 8); //321.1
+ data_block->remaining_trips = bit_lib_get_bits(block->data, 0xA8, 8); //321
+ data_block->validator1 = bit_lib_get_bits(block->data, 0xB0, 2); //422.1
+ data_block->validator = bit_lib_get_bits_16(block->data, 0xB1, 15); //422
+ data_block->hash = bit_lib_get_bits_32(block->data, 0xC0, 32); //502
+ data_block->rfu3 = bit_lib_get_bits_32(block->data, 0xE0, 32); //rfu3
+}
+
+void parse_layout_A(BlockData* data_block, const MfClassicBlock* block) {
+ data_block->view = bit_lib_get_bits_16(block->data, 0x00, 10); //101
+ data_block->type = bit_lib_get_bits_16(block->data, 0x0A, 10); //102
+ data_block->number = bit_lib_get_bits_32(block->data, 0x14, 32); //201
+ data_block->layout = bit_lib_get_bits(block->data, 0x34, 4); //111
+ data_block->valid_from_date = bit_lib_get_bits_16(block->data, 0x40, 12); //311
+ data_block->valid_for_minutes = bit_lib_get_bits_32(block->data, 0x4C, 19); //314
+ data_block->requires_activation = bit_lib_get_bits(block->data, 0x5F, 1); //301
+ data_block->start_trip_minutes = bit_lib_get_bits_32(block->data, 0x60, 19); //405
+ data_block->minutes_pass = bit_lib_get_bits(block->data, 0x77, 7); //412
+ data_block->transport_type_flag = bit_lib_get_bits(block->data, 0x7E, 2); //421.0
+ data_block->remaining_trips = bit_lib_get_bits(block->data, 0x80, 8); //321
+ data_block->validator = bit_lib_get_bits_16(block->data, 0x88, 16); //422
+ data_block->transport_type1 = bit_lib_get_bits(block->data, 0x98, 2); //421.1
+ data_block->transport_type2 = bit_lib_get_bits(block->data, 0x9A, 2); //421.2
+ data_block->transport_type3 = bit_lib_get_bits(block->data, 0x9C, 2); //421.3
+ data_block->transport_type4 = bit_lib_get_bits(block->data, 0x9E, 2); //421.4
+ data_block->hash = bit_lib_get_bits_32(block->data, 0xC0, 32); //502
+}
+
+void parse_layout_C(BlockData* data_block, const MfClassicBlock* block) {
+ data_block->view = bit_lib_get_bits_16(block->data, 0x00, 10); //101
+ data_block->type = bit_lib_get_bits_16(block->data, 0x0A, 10); //102
+ data_block->number = bit_lib_get_bits_32(block->data, 0x14, 32); //201
+ data_block->layout = bit_lib_get_bits(block->data, 0x34, 4); //111
+ data_block->use_before_date = bit_lib_get_bits_16(block->data, 0x38, 16); //202
+ data_block->rfu1 = bit_lib_get_bits_64(block->data, 0x48, 56); //rfu1
+ data_block->valid_from_date = bit_lib_get_bits_16(block->data, 0x80, 16); //311
+ data_block->valid_for_days = bit_lib_get_bits(block->data, 0x90, 8); //313
+ data_block->requires_activation = bit_lib_get_bits(block->data, 0x98, 1); //301
+ data_block->rfu2 = bit_lib_get_bits_16(block->data, 0x99, 13); //rfu2
+ data_block->remaining_trips = bit_lib_get_bits_16(block->data, 0xA6, 10); //321
+ data_block->validator = bit_lib_get_bits_16(block->data, 0xB0, 16); //422
+ data_block->hash = bit_lib_get_bits_32(block->data, 0xC0, 32); //502
+ data_block->start_trip_date = bit_lib_get_bits_16(block->data, 0xE0, 16); //402
+ data_block->start_trip_time = bit_lib_get_bits_16(block->data, 0xF0, 11); //403
+ data_block->transport_type = bit_lib_get_bits(block->data, 0xFB, 2); //421
+ data_block->rfu3 = bit_lib_get_bits(block->data, 0xFD, 2); //rfu3
+ data_block->transfer_in_metro = bit_lib_get_bits(block->data, 0xFF, 1); //432
+}
+
+void parse_layout_D(BlockData* data_block, const MfClassicBlock* block) {
+ data_block->view = bit_lib_get_bits_16(block->data, 0x00, 10); //101
+ data_block->type = bit_lib_get_bits_16(block->data, 0x0A, 10); //102
+ data_block->number = bit_lib_get_bits_32(block->data, 0x14, 32); //201
+ data_block->layout = bit_lib_get_bits(block->data, 0x34, 4); //111
+ data_block->rfu1 = bit_lib_get_bits(block->data, 0x38, 8); //rfu1
+ data_block->use_before_date = bit_lib_get_bits_16(block->data, 0x40, 16); //202
+ data_block->valid_for_time = bit_lib_get_bits_16(block->data, 0x50, 11); //316
+ data_block->rfu2 = bit_lib_get_bits(block->data, 0x5B, 5); //rfu2
+ data_block->use_before_date2 = bit_lib_get_bits_16(block->data, 0x60, 16); //202.2
+ data_block->valid_for_time2 = bit_lib_get_bits_16(block->data, 0x70, 11); //316.2
+ data_block->rfu3 = bit_lib_get_bits(block->data, 0x7B, 5); //rfu3
+ data_block->valid_from_date = bit_lib_get_bits_16(block->data, 0x80, 16); //311
+ data_block->valid_for_days = bit_lib_get_bits(block->data, 0x90, 8); //313
+ data_block->requires_activation = bit_lib_get_bits(block->data, 0x98, 1); //301
+ data_block->rfu4 = bit_lib_get_bits(block->data, 0x99, 2); //rfu4
+ data_block->passage_5_minutes = bit_lib_get_bits(block->data, 0x9B, 5); //413
+ data_block->transport_type1 = bit_lib_get_bits(block->data, 0xA0, 2); //421.1
+ data_block->passage_in_metro = bit_lib_get_bits(block->data, 0xA2, 1); //431
+ data_block->passages_ground_transport = bit_lib_get_bits(block->data, 0xA3, 3); //433
+ data_block->remaining_trips = bit_lib_get_bits_16(block->data, 0xA6, 10); //321
+ data_block->validator = bit_lib_get_bits_16(block->data, 0xB0, 16); //422
+ data_block->hash = bit_lib_get_bits_32(block->data, 0xC0, 32); //502
+ data_block->start_trip_date = bit_lib_get_bits_16(block->data, 0xE0, 16); //402
+ data_block->start_trip_time = bit_lib_get_bits_16(block->data, 0xF0, 11); //403
+ data_block->transport_type2 = bit_lib_get_bits(block->data, 0xFB, 2); //421.2
+ data_block->rfu5 = bit_lib_get_bits(block->data, 0xFD, 2); //rfu5
+ data_block->transfer_in_metro = bit_lib_get_bits(block->data, 0xFF, 1); //432
+}
+
+void parse_layout_E1(BlockData* data_block, const MfClassicBlock* block) {
+ data_block->view = bit_lib_get_bits_16(block->data, 0x00, 10); //101
+ data_block->type = bit_lib_get_bits_16(block->data, 0x0A, 10); //102
+ data_block->number = bit_lib_get_bits_32(block->data, 0x14, 32); //201
+ data_block->layout = bit_lib_get_bits(block->data, 0x34, 4); //111
+ data_block->layout2 = bit_lib_get_bits(block->data, 0x38, 5); //112
+ data_block->use_before_date = bit_lib_get_bits_16(block->data, 0x3D, 16); //202
+ data_block->blank_type = bit_lib_get_bits_16(block->data, 0x4D, 10); //121
+ data_block->validator = bit_lib_get_bits_16(block->data, 0x80, 16); //422
+ data_block->start_trip_date = bit_lib_get_bits_16(block->data, 0x90, 16); //402
+ data_block->start_trip_time = bit_lib_get_bits_16(block->data, 0xA0, 11); //403
+ data_block->transport_type1 = bit_lib_get_bits(block->data, 0xAB, 2); //421.1
+ data_block->transport_type2 = bit_lib_get_bits(block->data, 0xAD, 2); //421.2
+ data_block->transfer_in_metro = bit_lib_get_bits(block->data, 0xB1, 1); //432
+ data_block->passage_in_metro = bit_lib_get_bits(block->data, 0xB2, 1); //431
+ data_block->passages_ground_transport = bit_lib_get_bits(block->data, 0xB3, 3); //433
+ data_block->minutes_pass = bit_lib_get_bits(block->data, 0xB9, 8); //412
+ data_block->remaining_funds = bit_lib_get_bits_32(block->data, 0xC4, 19); //322
+ data_block->fare_trip = bit_lib_get_bits(block->data, 0xD7, 2); //441
+ data_block->blocked = bit_lib_get_bits(block->data, 0x9D, 1); //303
+ data_block->zoo = bit_lib_get_bits(block->data, 0xDA, 1); //zoo
+ data_block->hash = bit_lib_get_bits_32(block->data, 0xE0, 32); //502
+}
+
+void parse_layout_E2(BlockData* data_block, const MfClassicBlock* block) {
+ data_block->view = bit_lib_get_bits_16(block->data, 0x00, 10); //101
+ data_block->type = bit_lib_get_bits_16(block->data, 0x0A, 10); //102
+ data_block->number = bit_lib_get_bits_32(block->data, 0x14, 32); //201
+ data_block->layout = bit_lib_get_bits(block->data, 0x34, 4); //111
+ data_block->layout2 = bit_lib_get_bits(block->data, 0x38, 5); //112
+ data_block->type_of_extended = bit_lib_get_bits_16(block->data, 0x3D, 10); //122
+ data_block->use_before_date = bit_lib_get_bits_16(block->data, 0x47, 16); //202
+ data_block->blank_type = bit_lib_get_bits_16(block->data, 0x57, 10); //121
+ data_block->valid_from_date = bit_lib_get_bits_16(block->data, 0x61, 16); //311
+ data_block->activate_during = bit_lib_get_bits_16(block->data, 0x71, 9); //302
+ data_block->valid_for_minutes = bit_lib_get_bits_32(block->data, 0x83, 20); //314
+ data_block->minutes_pass = bit_lib_get_bits(block->data, 0x9A, 8); //412
+ data_block->transport_type = bit_lib_get_bits(block->data, 0xA3, 2); //421
+ data_block->passage_in_metro = bit_lib_get_bits(block->data, 0xA5, 1); //431
+ data_block->transfer_in_metro = bit_lib_get_bits(block->data, 0xA6, 1); //432
+ data_block->remaining_trips = bit_lib_get_bits_16(block->data, 0xA7, 10); //321
+ data_block->validator = bit_lib_get_bits_16(block->data, 0xB1, 16); //422
+ data_block->start_trip_neg_minutes = bit_lib_get_bits_32(block->data, 0xC4, 20); //404
+ data_block->requires_activation = bit_lib_get_bits(block->data, 0xD8, 1); //301
+ data_block->blocked = bit_lib_get_bits(block->data, 0xD9, 1); //303
+ data_block->extended = bit_lib_get_bits(block->data, 0xDA, 1); //123
+ data_block->hash = bit_lib_get_bits_32(block->data, 0xE0, 32); //502
+}
+
+void parse_layout_E3(BlockData* data_block, const MfClassicBlock* block) {
+ data_block->view = bit_lib_get_bits_16(block->data, 0x00, 10); //101
+ data_block->type = bit_lib_get_bits_16(block->data, 0x0A, 10); //102
+ data_block->number = bit_lib_get_bits_32(block->data, 0x14, 32); //201
+ data_block->layout = bit_lib_get_bits(block->data, 0x34, 4); //111
+ data_block->layout2 = bit_lib_get_bits(block->data, 0x38, 5); //112
+ data_block->use_before_date = bit_lib_get_bits_16(block->data, 61, 16); //202
+ data_block->blank_type = bit_lib_get_bits_16(block->data, 0x4D, 10); //121
+ data_block->remaining_funds = bit_lib_get_bits_32(block->data, 0xBC, 22); //322
+ data_block->hash = bit_lib_get_bits_32(block->data, 224, 32); //502
+ data_block->validator = bit_lib_get_bits_16(block->data, 0x80, 16); //422
+ data_block->start_trip_minutes = bit_lib_get_bits_32(block->data, 0x90, 23); //405
+ data_block->fare_trip = bit_lib_get_bits(block->data, 0xD2, 2); //441
+ data_block->minutes_pass = bit_lib_get_bits(block->data, 0xAB, 7); //412
+ data_block->transport_type_flag = bit_lib_get_bits(block->data, 0xB2, 2); //421.0
+ data_block->transport_type1 = bit_lib_get_bits(block->data, 0xB4, 2); //421.1
+ data_block->transport_type2 = bit_lib_get_bits(block->data, 0xB6, 2); //421.2
+ data_block->transport_type3 = bit_lib_get_bits(block->data, 0xB8, 2); //421.3
+ data_block->transport_type4 = bit_lib_get_bits(block->data, 0xBA, 2); //421.4
+ data_block->blocked = bit_lib_get_bits(block->data, 0xD4, 1); //303
+}
+
+void parse_layout_E4(BlockData* data_block, const MfClassicBlock* block) {
+ data_block->view = bit_lib_get_bits_16(block->data, 0x00, 10); //101
+ data_block->type = bit_lib_get_bits_16(block->data, 0x0A, 10); //102
+ data_block->number = bit_lib_get_bits_32(block->data, 0x14, 32); //201
+ data_block->layout = bit_lib_get_bits(block->data, 0x34, 4); //111
+ data_block->layout2 = bit_lib_get_bits(block->data, 0x38, 5); //112
+ data_block->type_of_extended = bit_lib_get_bits_16(block->data, 0x3D, 10); //122
+ data_block->use_before_date = bit_lib_get_bits_16(block->data, 0x47, 13); //202
+ data_block->blank_type = bit_lib_get_bits_16(block->data, 0x54, 10); //121
+ data_block->valid_from_date = bit_lib_get_bits_16(block->data, 0x5E, 13); //311
+ data_block->activate_during = bit_lib_get_bits_16(block->data, 0x6B, 9); //302
+ data_block->extension_counter = bit_lib_get_bits_16(block->data, 0x74, 10); //304
+ data_block->valid_for_minutes = bit_lib_get_bits_32(block->data, 0x80, 20); //314
+ data_block->minutes_pass = bit_lib_get_bits(block->data, 0x98, 7); //412
+ data_block->transport_type_flag = bit_lib_get_bits(block->data, 0x9F, 2); //421.0
+ data_block->transport_type1 = bit_lib_get_bits(block->data, 0xA1, 2); //421.1
+ data_block->transport_type2 = bit_lib_get_bits(block->data, 0xA3, 2); //421.2
+ data_block->transport_type3 = bit_lib_get_bits(block->data, 0xA5, 2); //421.3
+ data_block->transport_type4 = bit_lib_get_bits(block->data, 0xA7, 2); //421.4
+ data_block->remaining_trips = bit_lib_get_bits_16(block->data, 0xA9, 10); //321
+ data_block->validator = bit_lib_get_bits_16(block->data, 0xB3, 16); //422
+ data_block->start_trip_neg_minutes = bit_lib_get_bits_32(block->data, 0xC3, 20); //404
+ data_block->requires_activation = bit_lib_get_bits(block->data, 0xD7, 1); //301
+ data_block->blocked = bit_lib_get_bits(block->data, 0xD8, 1); //303
+ data_block->extended = bit_lib_get_bits(block->data, 0xD9, 1); //123
+ data_block->hash = bit_lib_get_bits_32(block->data, 0xE0, 32); //502
+}
+
+void parse_layout_E5(BlockData* data_block, const MfClassicBlock* block) {
+ data_block->view = bit_lib_get_bits_16(block->data, 0x00, 10); //101
+ data_block->type = bit_lib_get_bits_16(block->data, 0x0A, 10); //102
+ data_block->number = bit_lib_get_bits_32(block->data, 0x14, 32); //201
+ data_block->layout = bit_lib_get_bits(block->data, 0x34, 4); //111
+ data_block->layout2 = bit_lib_get_bits(block->data, 0x38, 5); //112
+ data_block->use_before_date = bit_lib_get_bits_16(block->data, 0x3D, 13); //202
+ data_block->blank_type = bit_lib_get_bits_16(block->data, 0x4A, 10); //121
+ data_block->valid_to_time = bit_lib_get_bits_32(block->data, 0x54, 23); //317
+ data_block->extension_counter = bit_lib_get_bits_16(block->data, 0x6B, 10); //304
+ data_block->start_trip_minutes = bit_lib_get_bits_32(block->data, 0x80, 23); //405
+ data_block->metro_ride_with = bit_lib_get_bits(block->data, 0x97, 7); //414
+ data_block->minutes_pass = bit_lib_get_bits(block->data, 0x9E, 7); //412
+ data_block->remaining_funds = bit_lib_get_bits_32(block->data, 0xA7, 19); //322
+ data_block->validator = bit_lib_get_bits_16(block->data, 0xBA, 16); //422
+ data_block->blocked = bit_lib_get_bits(block->data, 0xCA, 1); //303
+ data_block->route = bit_lib_get_bits_16(block->data, 0xCC, 12); //424
+ data_block->passages_ground_transport = bit_lib_get_bits(block->data, 0xD8, 7); //433
+ data_block->hash = bit_lib_get_bits_32(block->data, 0xE0, 32); //502
+}
+
+void parse_layout_E6(BlockData* data_block, const MfClassicBlock* block) {
+ data_block->view = bit_lib_get_bits_16(block->data, 0x00, 10); //101
+ data_block->type = bit_lib_get_bits_16(block->data, 0x0A, 10); //102
+ data_block->number = bit_lib_get_bits_32(block->data, 0x14, 32); //201
+ data_block->layout = bit_lib_get_bits(block->data, 0x34, 4); //111
+ data_block->layout2 = bit_lib_get_bits(block->data, 0x38, 5); //112
+ data_block->type_of_extended = bit_lib_get_bits_16(block->data, 0x3D, 10); //122
+ data_block->use_before_date = bit_lib_get_bits_16(block->data, 0x47, 13); //202
+ data_block->blank_type = bit_lib_get_bits_16(block->data, 0x54, 10); //121
+ data_block->valid_from_date = bit_lib_get_bits_32(block->data, 0x5E, 23); //311
+ data_block->extension_counter = bit_lib_get_bits_16(block->data, 0x75, 10); //304
+ data_block->valid_for_minutes = bit_lib_get_bits_32(block->data, 0x80, 20); //314
+ data_block->start_trip_neg_minutes = bit_lib_get_bits_32(block->data, 0x94, 20); //404
+ data_block->metro_ride_with = bit_lib_get_bits(block->data, 0xA8, 7); //414
+ data_block->minutes_pass = bit_lib_get_bits(block->data, 0xAF, 7); //412
+ data_block->remaining_trips = bit_lib_get_bits(block->data, 0xB6, 7); //321
+ data_block->validator = bit_lib_get_bits_16(block->data, 0xBD, 16); //422
+ data_block->blocked = bit_lib_get_bits(block->data, 0xCD, 1); //303
+ data_block->extended = bit_lib_get_bits(block->data, 0xCE, 1); //123
+ data_block->route = bit_lib_get_bits_16(block->data, 0xD4, 12); //424
+ data_block->hash = bit_lib_get_bits_32(block->data, 0xE0, 32); //502
+}
+
+void parse_layout_FCB(BlockData* data_block, const MfClassicBlock* block) {
+ data_block->view = bit_lib_get_bits_16(block->data, 0x00, 10); //101
+ data_block->type = bit_lib_get_bits_16(block->data, 0x0A, 10); //102
+ data_block->number = bit_lib_get_bits_32(block->data, 0x14, 32); //201
+ data_block->layout = bit_lib_get_bits(block->data, 0x34, 4); //111
+ data_block->tech_code = bit_lib_get_bits_32(block->data, 0x38, 10); //tech_code
+ data_block->valid_from_date = bit_lib_get_bits_16(block->data, 0x42, 16); //311
+ data_block->valid_to_date = bit_lib_get_bits_16(block->data, 0x52, 16); //312
+ data_block->interval = bit_lib_get_bits(block->data, 0x62, 4); //interval
+ data_block->app_code1 = bit_lib_get_bits_16(block->data, 0x66, 10); //app_code1
+ data_block->hash1 = bit_lib_get_bits_16(block->data, 0x70, 16); //502.1
+ data_block->type1 = bit_lib_get_bits_16(block->data, 0x80, 10); //type1
+ data_block->app_code2 = bit_lib_get_bits_16(block->data, 0x8A, 10); //app_code2
+ data_block->type2 = bit_lib_get_bits_16(block->data, 0x94, 10); //type2
+ data_block->app_code3 = bit_lib_get_bits_16(block->data, 0x9E, 10); //app_code3
+ data_block->type3 = bit_lib_get_bits_16(block->data, 0xA8, 10); //type3
+ data_block->app_code4 = bit_lib_get_bits_16(block->data, 0xB2, 10); //app_code4
+ data_block->type4 = bit_lib_get_bits_16(block->data, 0xBC, 10); //type4
+ data_block->hash2 = bit_lib_get_bits_32(block->data, 0xE0, 32); //502.2
+}
+
+void parse_layout_F0B(BlockData* data_block, const MfClassicBlock* block) {
+ data_block->view = bit_lib_get_bits_16(block->data, 0x00, 10); //101
+ data_block->type = bit_lib_get_bits_16(block->data, 0x0A, 10); //102
+ data_block->number = bit_lib_get_bits_32(block->data, 0x14, 32); //201
+ data_block->layout = bit_lib_get_bits(block->data, 0x34, 4); //111
+ data_block->tech_code = bit_lib_get_bits_32(block->data, 0x38, 10); //tech_code
+ data_block->valid_from_date = bit_lib_get_bits_16(block->data, 0x42, 16); //311
+ data_block->valid_to_date = bit_lib_get_bits_16(block->data, 0x52, 16); //312
+ data_block->hash1 = bit_lib_get_bits_32(block->data, 0x70, 16); //502.1
+}
+
+void parse_transport_type(BlockData* data_block, FuriString* transport) {
+ switch(data_block->transport_type_flag) {
+ case 1:
+ uint8_t transport_type =
+ (data_block->transport_type1 || data_block->transport_type2 ||
+ data_block->transport_type3 || data_block->transport_type4);
+ switch(transport_type) {
+ case 1:
+ furi_string_cat(transport, "Metro");
+ break;
+ case 2:
+ furi_string_cat(transport, "Monorail");
+ break;
+ case 3:
+ furi_string_cat(transport, "MCC");
+ break;
+ default:
+ furi_string_cat(transport, "Unknown");
+ break;
+ }
+ break;
+ case 2:
+ furi_string_cat(transport, "Ground");
+ break;
+ default:
+ furi_string_cat(transport, "");
+ break;
+ }
+}
+
+bool mosgortrans_parse_transport_block(const MfClassicBlock* block, FuriString* result) {
+ BlockData data_block = {};
+ const uint16_t valid_departments[] = {0x106, 0x108, 0x10A, 0x10E, 0x110, 0x117};
+ uint16_t transport_departament = bit_lib_get_bits_16(block->data, 0, 10);
+ if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
+ furi_string_cat_printf(result, "Transport departament: %x\n", transport_departament);
+ }
+ bool departament_valid = false;
+ for(uint8_t i = 0; i < 6; i++) {
+ if(transport_departament == valid_departments[i]) {
+ departament_valid = true;
+ break;
+ }
+ }
+ if(!departament_valid) {
+ return false;
+ }
+ FURI_LOG_D(TAG, "Transport departament: %x", transport_departament);
+ uint16_t layout_type = bit_lib_get_bits_16(block->data, 52, 4);
+ if(layout_type == 0xE) {
+ layout_type = bit_lib_get_bits_16(block->data, 52, 9);
+ } else if(layout_type == 0xF) {
+ layout_type = bit_lib_get_bits_16(block->data, 52, 14);
+ }
+ if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
+ furi_string_cat_printf(result, "Layout: %x\n", layout_type);
+ }
+ FURI_LOG_D(TAG, "Layout type %x", layout_type);
+ switch(layout_type) {
+ case 0x02: {
+ parse_layout_2(&data_block, block);
+ //number
+ furi_string_cat_printf(result, "Number: %010lu\n", data_block.number);
+ //use_before_date
+ DateTime card_use_before_date_s = {0};
+ from_days_to_datetime(data_block.use_before_date, &card_use_before_date_s, 1992);
+ furi_string_cat_printf(
+ result,
+ "Use before: %02d.%02d.%04d\n",
+ card_use_before_date_s.day,
+ card_use_before_date_s.month,
+ card_use_before_date_s.year);
+
+ if(data_block.valid_from_date == 0 || data_block.valid_to_date == 0) {
+ furi_string_cat(result, "\e#No ticket\n");
+ return true;
+ }
+ //remaining_trips
+ furi_string_cat_printf(result, "Trips: %d\n", data_block.total_trips);
+ //valid_from_date
+ DateTime card_valid_from_date_s = {0};
+ from_days_to_datetime(data_block.valid_from_date, &card_valid_from_date_s, 1992);
+ furi_string_cat_printf(
+ result,
+ "Valid from: %02d.%02d.%04d\n",
+ card_valid_from_date_s.day,
+ card_valid_from_date_s.month,
+ card_valid_from_date_s.year);
+ //valid_to_date
+ DateTime card_valid_to_date_s = {0};
+ from_days_to_datetime(data_block.valid_to_date, &card_valid_to_date_s, 1992);
+ furi_string_cat_printf(
+ result,
+ "Valid to: %02d.%02d.%04d\n",
+ card_valid_to_date_s.day,
+ card_valid_to_date_s.month,
+ card_valid_to_date_s.year);
+ //trip_number
+ furi_string_cat_printf(result, "Trips: %d\n", data_block.total_trips);
+ //trip_from
+ DateTime card_start_trip_minutes_s = {0};
+ from_seconds_to_datetime(
+ data_block.start_trip_date * 24 * 60 * 60 + data_block.start_trip_time * 60 +
+ data_block.start_trip_seconds,
+ &card_start_trip_minutes_s,
+ 1992);
+ furi_string_cat_printf(
+ result,
+ "Trip from: %02d.%02d.%04d %02d:%02d",
+ card_start_trip_minutes_s.day,
+ card_start_trip_minutes_s.month,
+ card_start_trip_minutes_s.year,
+ card_start_trip_minutes_s.hour,
+ card_start_trip_minutes_s.minute);
+ break;
+ }
+ case 0x06: {
+ parse_layout_6(&data_block, block);
+ //number
+ furi_string_cat_printf(result, "Number: %010lu\n", data_block.number);
+ //use_before_date
+ DateTime card_use_before_date_s = {0};
+ from_days_to_datetime(data_block.use_before_date, &card_use_before_date_s, 1992);
+ furi_string_cat_printf(
+ result,
+ "Use before: %02d.%02d.%04d\n",
+ card_use_before_date_s.day,
+ card_use_before_date_s.month,
+ card_use_before_date_s.year);
+ //remaining_trips
+ furi_string_cat_printf(result, "Trips left: %d\n", data_block.remaining_trips);
+ //valid_from_date
+ DateTime card_valid_from_date_s = {0};
+ from_days_to_datetime(data_block.valid_from_date, &card_valid_from_date_s, 1992);
+ furi_string_cat_printf(
+ result,
+ "Valid from: %02d.%02d.%04d\n",
+ card_valid_from_date_s.day,
+ card_valid_from_date_s.month,
+ card_valid_from_date_s.year);
+ //valid_to_date
+ DateTime card_valid_to_date_s = {0};
+ from_days_to_datetime(data_block.valid_to_date, &card_valid_to_date_s, 1992);
+ furi_string_cat_printf(
+ result,
+ "Valid to: %02d.%02d.%04d\n",
+ card_valid_to_date_s.day,
+ card_valid_to_date_s.month,
+ card_valid_to_date_s.year);
+ //trip_number
+ furi_string_cat_printf(result, "Trips: %d\n", data_block.total_trips);
+ //trip_from
+ DateTime card_start_trip_minutes_s = {0};
+ from_minutes_to_datetime(
+ (data_block.start_trip_date) * 24 * 60 + data_block.start_trip_time,
+ &card_start_trip_minutes_s,
+ 1992);
+ furi_string_cat_printf(
+ result,
+ "Trip from: %02d.%02d.%04d %02d:%02d\n",
+ card_start_trip_minutes_s.day,
+ card_start_trip_minutes_s.month,
+ card_start_trip_minutes_s.year,
+ card_start_trip_minutes_s.hour,
+ card_start_trip_minutes_s.minute);
+ //validator
+ furi_string_cat_printf(
+ result, "Validator: %05d", data_block.validator1 * 1024 + data_block.validator2);
+ break;
+ }
+ case 0x08: {
+ parse_layout_8(&data_block, block);
+ //number
+ furi_string_cat_printf(result, "Number: %010lu\n", data_block.number);
+ //use_before_date
+ DateTime card_use_before_date_s = {0};
+ from_days_to_datetime(data_block.use_before_date, &card_use_before_date_s, 1992);
+ //remaining_trips
+ furi_string_cat_printf(result, "Trips left: %d\n", data_block.remaining_trips);
+ //valid_from_date
+ DateTime card_valid_from_date_s = {0};
+ from_days_to_datetime(data_block.valid_from_date, &card_valid_from_date_s, 1992);
+ furi_string_cat_printf(
+ result,
+ "Valid from: %02d.%02d.%04d\n",
+ card_valid_from_date_s.day,
+ card_valid_from_date_s.month,
+ card_valid_from_date_s.year);
+ //valid_to_date
+ DateTime card_valid_to_date_s = {0};
+ from_days_to_datetime(
+ data_block.valid_from_date + data_block.valid_for_days, &card_valid_to_date_s, 1992);
+ furi_string_cat_printf(
+ result,
+ "Valid to: %02d.%02d.%04d\n",
+ card_valid_to_date_s.day,
+ card_valid_to_date_s.month,
+ card_valid_to_date_s.year);
+ break;
+ }
+ case 0x0A: {
+ parse_layout_A(&data_block, block);
+ //number
+ furi_string_cat_printf(result, "Number: %010lu\n", data_block.number);
+ //use_before_date
+ DateTime card_use_before_date_s = {0};
+ from_days_to_datetime(data_block.use_before_date, &card_use_before_date_s, 2016);
+ furi_string_cat_printf(
+ result,
+ "Use before: %02d.%02d.%04d\n",
+ card_use_before_date_s.day,
+ card_use_before_date_s.month,
+ card_use_before_date_s.year);
+ //remaining_trips
+ furi_string_cat_printf(result, "Trips left: %d\n", data_block.remaining_trips);
+ //valid_from_date
+ DateTime card_valid_from_date_s = {0};
+ from_days_to_datetime(data_block.valid_from_date, &card_valid_from_date_s, 2016);
+ furi_string_cat_printf(
+ result,
+ "Valid from: %02d.%02d.%04d\n",
+ card_valid_from_date_s.day,
+ card_valid_from_date_s.month,
+ card_valid_from_date_s.year);
+ //valid_to_date
+ DateTime card_valid_to_date_s = {0};
+ from_minutes_to_datetime(
+ data_block.valid_from_date * 24 * 60 + data_block.valid_for_minutes - 1,
+ &card_valid_to_date_s,
+ 2016);
+ furi_string_cat_printf(
+ result,
+ "Valid to: %02d.%02d.%04d\n",
+ card_valid_to_date_s.day,
+ card_valid_to_date_s.month,
+ card_valid_to_date_s.year);
+ //trip_from
+ if(data_block.start_trip_minutes) {
+ DateTime card_start_trip_minutes_s = {0};
+ from_minutes_to_datetime(
+ data_block.valid_from_date * 24 * 60 + data_block.start_trip_minutes,
+ &card_start_trip_minutes_s,
+ 2016);
+ furi_string_cat_printf(
+ result,
+ "Trip from: %02d.%02d.%04d %02d:%02d\n",
+ card_start_trip_minutes_s.day,
+ card_start_trip_minutes_s.month,
+ card_start_trip_minutes_s.year,
+ card_start_trip_minutes_s.hour,
+ card_start_trip_minutes_s.minute);
+ }
+ //trip_switch
+ if(data_block.minutes_pass) {
+ DateTime card_start_switch_trip_minutes_s = {0};
+ from_minutes_to_datetime(
+ data_block.valid_from_date * 24 * 60 + data_block.start_trip_minutes +
+ data_block.minutes_pass,
+ &card_start_switch_trip_minutes_s,
+ 2016);
+ furi_string_cat_printf(
+ result,
+ "Trip switch: %02d.%02d.%04d %02d:%02d\n",
+ card_start_switch_trip_minutes_s.day,
+ card_start_switch_trip_minutes_s.month,
+ card_start_switch_trip_minutes_s.year,
+ card_start_switch_trip_minutes_s.hour,
+ card_start_switch_trip_minutes_s.minute);
+ }
+ //transport
+ FuriString* transport = furi_string_alloc();
+ parse_transport_type(&data_block, transport);
+ furi_string_cat_printf(result, "Transport: %s\n", furi_string_get_cstr(transport));
+ //validator
+ if(data_block.validator) {
+ furi_string_cat_printf(result, "Validator: %05d\n", data_block.validator);
+ }
+ furi_string_free(transport);
+ break;
+ }
+ case 0x0C: {
+ parse_layout_C(&data_block, block);
+ //number
+ furi_string_cat_printf(result, "Number: %010lu\n", data_block.number);
+ //use_before_date
+ DateTime card_use_before_date_s = {0};
+ from_days_to_datetime(data_block.use_before_date, &card_use_before_date_s, 1992);
+ //remaining_trips
+ furi_string_cat_printf(result, "Trips left: %d\n", data_block.remaining_trips);
+ //valid_from_date
+ DateTime card_valid_from_date_s = {0};
+ from_days_to_datetime(data_block.valid_from_date, &card_valid_from_date_s, 1992);
+ furi_string_cat_printf(
+ result,
+ "Valid from: %02d.%02d.%04d\n",
+ card_valid_from_date_s.day,
+ card_valid_from_date_s.month,
+ card_valid_from_date_s.year);
+ //valid_to_date
+ DateTime card_valid_to_date_s = {0};
+ from_days_to_datetime(
+ data_block.valid_from_date + data_block.valid_for_days, &card_valid_to_date_s, 1992);
+ furi_string_cat_printf(
+ result,
+ "Valid to: %02d.%02d.%04d\n",
+ card_valid_to_date_s.day,
+ card_valid_to_date_s.month,
+ card_valid_to_date_s.year);
+ //remaining_trips
+ furi_string_cat_printf(result, "Trips left: %d\n", data_block.remaining_trips);
+ //trip_from
+ if(data_block.start_trip_date) {
+ DateTime card_start_trip_minutes_s = {0};
+ from_minutes_to_datetime(
+ data_block.start_trip_date * 24 * 60 + data_block.start_trip_time,
+ &card_start_trip_minutes_s,
+ 1992);
+ }
+ //validator
+ if(data_block.validator) {
+ furi_string_cat_printf(result, "Validator: %05d", data_block.validator);
+ }
+ break;
+ }
+ case 0x0D: {
+ parse_layout_D(&data_block, block);
+ //number
+ furi_string_cat_printf(result, "Number: %010lu\n", data_block.number);
+ //use_before_date
+ DateTime card_use_before_date_s = {0};
+ from_days_to_datetime(data_block.use_before_date, &card_use_before_date_s, 1992);
+ furi_string_cat_printf(
+ result,
+ "Use before: %02d.%02d.%04d\n",
+ card_use_before_date_s.day,
+ card_use_before_date_s.month,
+ card_use_before_date_s.year);
+ //remaining_trips
+ furi_string_cat_printf(result, "Trips left: %d\n", data_block.remaining_trips);
+ //valid_from_date
+ DateTime card_valid_from_date_s = {0};
+ from_days_to_datetime(data_block.valid_from_date, &card_valid_from_date_s, 1992);
+ furi_string_cat_printf(
+ result,
+ "Valid from: %02d.%02d.%04d\n",
+ card_valid_from_date_s.day,
+ card_valid_from_date_s.month,
+ card_valid_from_date_s.year);
+ //valid_to_date
+ DateTime card_valid_to_date_s = {0};
+ from_days_to_datetime(
+ data_block.valid_from_date + data_block.valid_for_days, &card_valid_to_date_s, 1992);
+ furi_string_cat_printf(
+ result,
+ "Valid to: %02d.%02d.%04d\n",
+ card_valid_to_date_s.day,
+ card_valid_to_date_s.month,
+ card_valid_to_date_s.year);
+ //trip_from
+ if(data_block.start_trip_date) {
+ DateTime card_start_trip_minutes_s = {0};
+ from_minutes_to_datetime(
+ data_block.start_trip_date * 24 * 60 + data_block.start_trip_time,
+ &card_start_trip_minutes_s,
+ 1992);
+ }
+ //trip_switch
+ if(data_block.passage_5_minutes) {
+ DateTime card_start_switch_trip_minutes_s = {0};
+ from_minutes_to_datetime(
+ data_block.start_trip_date * 24 * 60 + data_block.start_trip_time +
+ data_block.passage_5_minutes,
+ &card_start_switch_trip_minutes_s,
+ 1992);
+ }
+ //validator
+ if(data_block.validator) {
+ furi_string_cat_printf(result, "Validator: %05d", data_block.validator);
+ }
+ break;
+ }
+ case 0xE1:
+ case 0x1C1: {
+ parse_layout_E1(&data_block, block);
+ //number
+ furi_string_cat_printf(result, "Number: %010lu\n", data_block.number);
+ //use_before_date
+ DateTime card_use_before_date_s = {0};
+ from_days_to_datetime(data_block.use_before_date, &card_use_before_date_s, 1992);
+ furi_string_cat_printf(
+ result,
+ "Use before: %02d.%02d.%04d\n",
+ card_use_before_date_s.day,
+ card_use_before_date_s.month,
+ card_use_before_date_s.year);
+ //remaining_funds
+ furi_string_cat_printf(result, "Balance: %ld rub\n", data_block.remaining_funds / 100);
+ //trip_from
+ if(data_block.start_trip_date) {
+ DateTime card_start_trip_minutes_s = {0};
+ from_minutes_to_datetime(
+ data_block.start_trip_date * 24 * 60 + data_block.start_trip_time,
+ &card_start_trip_minutes_s,
+ 1992);
+ furi_string_cat_printf(
+ result,
+ "Trip from: %02d.%02d.%04d %02d:%02d\n",
+ card_start_trip_minutes_s.day,
+ card_start_trip_minutes_s.month,
+ card_start_trip_minutes_s.year,
+ card_start_trip_minutes_s.hour,
+ card_start_trip_minutes_s.minute);
+ }
+ //transport
+ FuriString* transport = furi_string_alloc();
+ switch(data_block.transport_type1) {
+ case 1:
+ switch(data_block.transport_type2) {
+ case 1:
+ furi_string_cat(transport, "Metro");
+ break;
+ case 2:
+ furi_string_cat(transport, "Monorail");
+ break;
+ default:
+ furi_string_cat(transport, "Unknown");
+ break;
+ }
+ break;
+ case 2:
+ furi_string_cat(transport, "Ground");
+ break;
+ case 3:
+ furi_string_cat(transport, "MCC");
+ break;
+ default:
+ furi_string_cat(transport, "");
+ break;
+ }
+ furi_string_cat_printf(result, "Transport: %s\n", furi_string_get_cstr(transport));
+ //validator
+ if(data_block.validator) {
+ furi_string_cat_printf(result, "Validator: %05d", data_block.validator);
+ }
+ furi_string_free(transport);
+ break;
+ }
+ case 0xE2:
+ case 0x1C2: {
+ parse_layout_E2(&data_block, block);
+ //number
+ furi_string_cat_printf(result, "Number: %010lu\n", data_block.number);
+ //use_before_date
+ DateTime card_use_before_date_s = {0};
+ from_days_to_datetime(data_block.use_before_date, &card_use_before_date_s, 1992);
+ furi_string_cat_printf(
+ result,
+ "Use before: %02d.%02d.%04d\n",
+ card_use_before_date_s.day,
+ card_use_before_date_s.month,
+ card_use_before_date_s.year);
+ //remaining_trips
+ furi_string_cat_printf(result, "Trips left: %d\n", data_block.remaining_trips);
+ //valid_from_date
+ DateTime card_valid_from_date_s = {0};
+ from_days_to_datetime(data_block.valid_from_date, &card_valid_from_date_s, 1992);
+ furi_string_cat_printf(
+ result,
+ "Valid from: %02d.%02d.%04d\n",
+ card_valid_from_date_s.day,
+ card_valid_from_date_s.month,
+ card_valid_from_date_s.year);
+ //valid_to_date
+ if(data_block.activate_during) {
+ DateTime card_valid_to_date_s = {0};
+ from_days_to_datetime(
+ data_block.valid_from_date + data_block.activate_during,
+ &card_valid_to_date_s,
+ 1992);
+ furi_string_cat_printf(
+ result,
+ "Valid to: %02d.%02d.%04d\n",
+ card_valid_to_date_s.day,
+ card_valid_to_date_s.month,
+ card_valid_to_date_s.year);
+ } else {
+ DateTime card_valid_to_date_s = {0};
+ from_minutes_to_datetime(
+ data_block.valid_from_date * 24 * 60 + data_block.valid_for_minutes - 1,
+ &card_valid_to_date_s,
+ 1992);
+ furi_string_cat_printf(
+ result,
+ "Valid to: %02d.%02d.%04d\n",
+ card_valid_to_date_s.day,
+ card_valid_to_date_s.month,
+ card_valid_to_date_s.year);
+ }
+ //trip_from
+ if(data_block.start_trip_neg_minutes) {
+ DateTime card_start_trip_minutes_s = {0};
+ from_minutes_to_datetime(
+ data_block.valid_to_date * 24 * 60 + data_block.valid_for_minutes -
+ data_block.start_trip_neg_minutes,
+ &card_start_trip_minutes_s,
+ 1992); //-time
+ furi_string_cat_printf(
+ result,
+ "Trip from: %02d.%02d.%04d %02d:%02d\n",
+ card_start_trip_minutes_s.day,
+ card_start_trip_minutes_s.month,
+ card_start_trip_minutes_s.year,
+ card_start_trip_minutes_s.hour,
+ card_start_trip_minutes_s.minute);
+ }
+ //trip_switch
+ if(data_block.minutes_pass) {
+ DateTime card_start_switch_trip_minutes_s = {0};
+ from_minutes_to_datetime(
+ data_block.valid_from_date * 24 * 60 + data_block.valid_for_minutes -
+ data_block.start_trip_neg_minutes + data_block.minutes_pass,
+ &card_start_switch_trip_minutes_s,
+ 1992);
+ furi_string_cat_printf(
+ result,
+ "Trip switch: %02d.%02d.%04d %02d:%02d\n",
+ card_start_switch_trip_minutes_s.day,
+ card_start_switch_trip_minutes_s.month,
+ card_start_switch_trip_minutes_s.year,
+ card_start_switch_trip_minutes_s.hour,
+ card_start_switch_trip_minutes_s.minute);
+ }
+ //transport
+ FuriString* transport = furi_string_alloc();
+ switch(data_block.transport_type) {
+ case 1:
+ furi_string_cat(transport, "Metro");
+ break;
+ case 2:
+ furi_string_cat(transport, "Monorail");
+ break;
+ case 3:
+ furi_string_cat(transport, "Ground");
+ break;
+ default:
+ furi_string_cat(transport, "Unknown");
+ break;
+ }
+ //validator
+ if(data_block.validator) {
+ furi_string_cat_printf(result, "Validator: %05d", data_block.validator);
+ }
+ furi_string_free(transport);
+ break;
+ }
+ case 0xE3:
+ case 0x1C3: {
+ parse_layout_E3(&data_block, block);
+ // number
+ furi_string_cat_printf(result, "Number: %010lu\n", data_block.number);
+ // use_before_date
+ DateTime card_use_before_date_s = {0};
+ from_days_to_datetime(data_block.use_before_date, &card_use_before_date_s, 1992);
+ furi_string_cat_printf(
+ result,
+ "Use before: %02d.%02d.%04d\n",
+ card_use_before_date_s.day,
+ card_use_before_date_s.month,
+ card_use_before_date_s.year);
+ // remaining_funds
+ furi_string_cat_printf(result, "Balance: %lu rub\n", data_block.remaining_funds);
+ // start_trip_minutes
+ DateTime card_start_trip_minutes_s = {0};
+ from_minutes_to_datetime(data_block.start_trip_minutes, &card_start_trip_minutes_s, 2016);
+ furi_string_cat_printf(
+ result,
+ "Trip from: %02d.%02d.%04d %02d:%02d\n",
+ card_start_trip_minutes_s.day,
+ card_start_trip_minutes_s.month,
+ card_start_trip_minutes_s.year,
+ card_start_trip_minutes_s.hour,
+ card_start_trip_minutes_s.minute);
+ // transport
+ FuriString* transport = furi_string_alloc();
+ parse_transport_type(&data_block, transport);
+ furi_string_cat_printf(result, "Transport: %s\n", furi_string_get_cstr(transport));
+ // validator
+ furi_string_cat_printf(result, "Validator: %05d\n", data_block.validator);
+ // fare
+ FuriString* fare = furi_string_alloc();
+ switch(data_block.fare_trip) {
+ case 0:
+ furi_string_cat(fare, "");
+ break;
+ case 1:
+ furi_string_cat(fare, "Single");
+ break;
+ case 2:
+ furi_string_cat(fare, "90 minutes");
+ break;
+ default:
+ furi_string_cat(fare, "Unknown");
+ break;
+ }
+ furi_string_cat_printf(result, "Fare: %s", furi_string_get_cstr(fare));
+ furi_string_free(fare);
+ furi_string_free(transport);
+ break;
+ }
+ case 0xE4:
+ case 0x1C4: {
+ parse_layout_E4(&data_block, block);
+
+ // number
+ furi_string_cat_printf(result, "Number: %010lu\n", data_block.number);
+ // use_before_date
+ DateTime card_use_before_date_s = {0};
+ from_days_to_datetime(data_block.use_before_date, &card_use_before_date_s, 2016);
+ furi_string_cat_printf(
+ result,
+ "Use before: %02d.%02d.%04d\n",
+ card_use_before_date_s.day,
+ card_use_before_date_s.month,
+ card_use_before_date_s.year);
+ // remaining_funds
+ furi_string_cat_printf(result, "Balance: %lu rub\n", data_block.remaining_funds);
+ // valid_from_date
+ DateTime card_use_from_date_s = {0};
+ from_days_to_datetime(data_block.valid_from_date, &card_use_from_date_s, 2016);
+ furi_string_cat_printf(
+ result,
+ "Valid from: %02d.%02d.%04d\n",
+ card_use_from_date_s.day,
+ card_use_from_date_s.month,
+ card_use_from_date_s.year);
+ // valid_to_date
+ DateTime card_use_to_date_s = {0};
+ if(data_block.requires_activation) {
+ from_days_to_datetime(
+ data_block.valid_from_date + data_block.activate_during,
+ &card_use_to_date_s,
+ 2016);
+ } else {
+ from_minutes_to_datetime(
+ data_block.valid_from_date * 24 * 60 + data_block.valid_for_minutes - 1,
+ &card_use_to_date_s,
+ 2016);
+ }
+
+ furi_string_cat_printf(
+ result,
+ "Valid to: %02d.%02d.%04d\n",
+ card_use_to_date_s.day,
+ card_use_to_date_s.month,
+ card_use_to_date_s.year);
+ // trip_number
+ // furi_string_cat_printf(result, "Trips left: %d", data_block.remaining_trips);
+ // trip_from
+ DateTime card_start_trip_minutes_s = {0};
+ from_minutes_to_datetime(
+ data_block.valid_from_date * 24 * 60 + data_block.valid_for_minutes -
+ data_block.start_trip_neg_minutes,
+ &card_start_trip_minutes_s,
+ 2016);
+ //transport
+ FuriString* transport = furi_string_alloc();
+ parse_transport_type(&data_block, transport);
+ furi_string_cat_printf(result, "Transport: %s\n", furi_string_get_cstr(transport));
+ // validator
+ if(data_block.validator) {
+ furi_string_cat_printf(result, "Validator: %05d", data_block.validator);
+ }
+ furi_string_free(transport);
+ break;
+ }
+ case 0xE5:
+ case 0x1C5: {
+ parse_layout_E5(&data_block, block);
+ //number
+ furi_string_cat_printf(result, "Number: %010lu\n", data_block.number);
+ //use_before_date
+ DateTime card_use_before_date_s = {0};
+ from_days_to_datetime(data_block.use_before_date, &card_use_before_date_s, 2019);
+ furi_string_cat_printf(
+ result,
+ "Use before: %02d.%02d.%04d\n",
+ card_use_before_date_s.day,
+ card_use_before_date_s.month,
+ card_use_before_date_s.year);
+ //remaining_funds
+ furi_string_cat_printf(result, "Balance: %ld rub\n", data_block.remaining_funds / 100);
+ //start_trip_minutes
+ if(data_block.start_trip_minutes) {
+ DateTime card_start_trip_minutes_s = {0};
+ from_minutes_to_datetime(
+ data_block.start_trip_minutes, &card_start_trip_minutes_s, 2019);
+ furi_string_cat_printf(
+ result,
+ "Trip from: %02d.%02d.%04d %02d:%02d\n",
+ card_start_trip_minutes_s.day,
+ card_start_trip_minutes_s.month,
+ card_start_trip_minutes_s.year,
+ card_start_trip_minutes_s.hour,
+ card_start_trip_minutes_s.minute);
+ }
+ //start_m_trip_minutes
+ if(data_block.metro_ride_with) {
+ DateTime card_start_m_trip_minutes_s = {0};
+ from_minutes_to_datetime(
+ data_block.start_trip_minutes + data_block.metro_ride_with,
+ &card_start_m_trip_minutes_s,
+ 2019);
+ furi_string_cat_printf(
+ result,
+ "(M) from: %02d.%02d.%04d %02d:%02d\n",
+ card_start_m_trip_minutes_s.day,
+ card_start_m_trip_minutes_s.month,
+ card_start_m_trip_minutes_s.year,
+ card_start_m_trip_minutes_s.hour,
+ card_start_m_trip_minutes_s.minute);
+ }
+ if(data_block.minutes_pass) {
+ DateTime card_start_change_trip_minutes_s = {0};
+ from_minutes_to_datetime(
+ data_block.start_trip_minutes + data_block.minutes_pass,
+ &card_start_change_trip_minutes_s,
+ 2019);
+ furi_string_cat_printf(
+ result,
+ "Trip edit: %02d.%02d.%04d %02d:%02d\n",
+ card_start_change_trip_minutes_s.day,
+ card_start_change_trip_minutes_s.month,
+ card_start_change_trip_minutes_s.year,
+ card_start_change_trip_minutes_s.hour,
+ card_start_change_trip_minutes_s.minute);
+ }
+ //transport
+ //validator
+ if(data_block.validator) {
+ furi_string_cat_printf(result, "Validator: %05d", data_block.validator);
+ }
+ break;
+ }
+ case 0xE6:
+ case 0x1C6: {
+ parse_layout_E6(&data_block, block);
+ //number
+ furi_string_cat_printf(result, "Number: %010lu\n", data_block.number);
+ //use_before_date
+ DateTime card_use_before_date_s = {0};
+ from_days_to_datetime(data_block.use_before_date, &card_use_before_date_s, 2019);
+ furi_string_cat_printf(
+ result,
+ "Use before: %02d.%02d.%04d\n",
+ card_use_before_date_s.day,
+ card_use_before_date_s.month,
+ card_use_before_date_s.year);
+ //remaining_trips
+ furi_string_cat_printf(result, "Trips left: %d\n", data_block.remaining_trips);
+ //valid_from_date
+ DateTime card_use_from_date_s = {0};
+ from_minutes_to_datetime(data_block.valid_from_date, &card_use_from_date_s, 2019);
+ furi_string_cat_printf(
+ result,
+ "Valid from: %02d.%02d.%04d\n",
+ card_use_from_date_s.day,
+ card_use_from_date_s.month,
+ card_use_from_date_s.year);
+ //valid_to_date
+ DateTime card_use_to_date_s = {0};
+ from_minutes_to_datetime(
+ data_block.valid_from_date + data_block.valid_for_minutes - 1,
+ &card_use_to_date_s,
+ 2019);
+ furi_string_cat_printf(
+ result,
+ "Valid to: %02d.%02d.%04d\n",
+ card_use_to_date_s.day,
+ card_use_to_date_s.month,
+ card_use_to_date_s.year);
+ //start_trip_minutes
+ if(data_block.start_trip_neg_minutes) {
+ DateTime card_start_trip_minutes_s = {0};
+ from_minutes_to_datetime(
+ data_block.valid_from_date + data_block.valid_for_minutes -
+ data_block.start_trip_neg_minutes,
+ &card_start_trip_minutes_s,
+ 2019); //-time
+ furi_string_cat_printf(
+ result,
+ "Trip from: %02d.%02d.%04d %02d:%02d\n",
+ card_start_trip_minutes_s.day,
+ card_start_trip_minutes_s.month,
+ card_start_trip_minutes_s.year,
+ card_start_trip_minutes_s.hour,
+ card_start_trip_minutes_s.minute);
+ }
+ //start_trip_m_minutes
+ if(data_block.metro_ride_with) {
+ DateTime card_start_trip_m_minutes_s = {0};
+ from_minutes_to_datetime(
+ data_block.valid_from_date + data_block.valid_for_minutes -
+ data_block.start_trip_neg_minutes + data_block.metro_ride_with,
+ &card_start_trip_m_minutes_s,
+ 2019);
+ furi_string_cat_printf(
+ result,
+ "(M) from: %02d.%02d.%04d %02d:%02d\n",
+ card_start_trip_m_minutes_s.day,
+ card_start_trip_m_minutes_s.month,
+ card_start_trip_m_minutes_s.year,
+ card_start_trip_m_minutes_s.hour,
+ card_start_trip_m_minutes_s.minute);
+ }
+ //transport
+ //validator
+ if(data_block.validator) {
+ furi_string_cat_printf(result, "Validator: %05d", data_block.validator);
+ }
+ break;
+ }
+ case 0x3CCB: {
+ parse_layout_FCB(&data_block, block);
+ //number
+ furi_string_cat_printf(result, "Number: %010lu\n", data_block.number);
+ //valid_from_date
+ DateTime card_use_from_date_s = {0};
+ from_days_to_datetime(data_block.valid_from_date, &card_use_from_date_s, 1992);
+ furi_string_cat_printf(
+ result,
+ "Valid from: %02d.%02d.%04d\n",
+ card_use_from_date_s.day,
+ card_use_from_date_s.month,
+ card_use_from_date_s.year);
+ //valid_to_date
+ DateTime card_use_to_date_s = {0};
+ from_days_to_datetime(data_block.valid_to_date, &card_use_to_date_s, 1992);
+ furi_string_cat_printf(
+ result,
+ "Valid to: %02d.%02d.%04d",
+ card_use_to_date_s.day,
+ card_use_to_date_s.month,
+ card_use_to_date_s.year);
+ break;
+ }
+ case 0x3C0B: {
+ //number
+ furi_string_cat_printf(result, "Number: %010lu\n", data_block.number);
+ //valid_from_date
+ DateTime card_use_from_date_s = {0};
+ from_days_to_datetime(data_block.valid_from_date, &card_use_from_date_s, 1992);
+ furi_string_cat_printf(
+ result,
+ "Valid from: %02d.%02d.%04d\n",
+ card_use_from_date_s.day,
+ card_use_from_date_s.month,
+ card_use_from_date_s.year);
+ //valid_to_date
+ DateTime card_use_to_date_s = {0};
+ from_days_to_datetime(data_block.valid_to_date, &card_use_to_date_s, 1992);
+ furi_string_cat_printf(
+ result,
+ "Valid to: %02d.%02d.%04d",
+ card_use_to_date_s.day,
+ card_use_to_date_s.month,
+ card_use_to_date_s.year);
+ break;
+ }
+ default:
+ result = NULL;
+ return false;
+ }
+
+ return true;
+}
diff --git a/applications/main/nfc/api/mosgortrans/mosgortrans_util.h b/applications/main/nfc/api/mosgortrans/mosgortrans_util.h
new file mode 100644
index 0000000000..e5da8ddeb4
--- /dev/null
+++ b/applications/main/nfc/api/mosgortrans/mosgortrans_util.h
@@ -0,0 +1,17 @@
+#pragma once
+
+#include
+#include
+#include
+#include
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+bool mosgortrans_parse_transport_block(const MfClassicBlock* block, FuriString* result);
+
+#ifdef __cplusplus
+}
+#endif
\ No newline at end of file
diff --git a/applications/main/nfc/api/nfc_app_api_table_i.h b/applications/main/nfc/api/nfc_app_api_table_i.h
index 08bff072ea..bf0e926ee6 100644
--- a/applications/main/nfc/api/nfc_app_api_table_i.h
+++ b/applications/main/nfc/api/nfc_app_api_table_i.h
@@ -1,4 +1,5 @@
#include "gallagher/gallagher_util.h"
+#include "mosgortrans/mosgortrans_util.h"
/*
* A list of app's private functions and objects to expose for plugins.
@@ -10,4 +11,8 @@ static constexpr auto nfc_app_api_table = sort(create_array_t(
gallagher_deobfuscate_and_parse_credential,
void,
(GallagherCredential * credential, const uint8_t* cardholder_data_obfuscated)),
- API_VARIABLE(GALLAGHER_CARDAX_ASCII, const uint8_t*)));
+ API_VARIABLE(GALLAGHER_CARDAX_ASCII, const uint8_t*),
+ API_METHOD(
+ mosgortrans_parse_transport_block,
+ bool,
+ (const MfClassicBlock* block, FuriString* result))));
diff --git a/applications/main/nfc/application.fam b/applications/main/nfc/application.fam
index 44d45ff206..0468e4bfb4 100644
--- a/applications/main/nfc/application.fam
+++ b/applications/main/nfc/application.fam
@@ -146,6 +146,15 @@ App(
sources=["plugins/supported_cards/aime.c"],
)
+App(
+ appid="bip_parser",
+ apptype=FlipperAppType.PLUGIN,
+ entry_point="bip_plugin_ep",
+ targets=["f7"],
+ requires=["nfc"],
+ sources=["plugins/supported_cards/bip.c"],
+)
+
App(
appid="saflok_parser",
apptype=FlipperAppType.PLUGIN,
diff --git a/applications/main/nfc/plugins/supported_cards/bip.c b/applications/main/nfc/plugins/supported_cards/bip.c
new file mode 100644
index 0000000000..f6fed6774b
--- /dev/null
+++ b/applications/main/nfc/plugins/supported_cards/bip.c
@@ -0,0 +1,368 @@
+#include "nfc_supported_card_plugin.h"
+
+#include
+#include
+#include
+#include
+
+#define TAG "Bip"
+
+#define SECTOR_BLOCK_OFFSET(sector, block) (((sector) * 4) + (block))
+
+static const uint64_t bip_keys_a[] = {
+ 0x3a42f33af429,
+ 0x6338a371c0ed,
+ 0xf124c2578ad0,
+ 0x32ac3b90ac13,
+ 0x4ad1e273eaf1,
+ 0xe2c42591368a,
+ 0x2a3c347a1200,
+ 0x16f3d5ab1139,
+ 0x937a4fff3011,
+ 0x35c3d2caee88,
+ 0x693143f10368,
+ 0xa3f97428dd01,
+ 0x63f17a449af0,
+ 0xc4652c54261c,
+ 0xd49e2826664f,
+ 0x3df14c8000a1,
+};
+
+static const uint64_t bip_keys_b[] = {
+ 0x1fc235ac1309,
+ 0x243f160918d1,
+ 0x9afc42372af1,
+ 0x682d401abb09,
+ 0x067db45454a9,
+ 0x15fc4c7613fe,
+ 0x68d30288910a,
+ 0xf59a36a2546d,
+ 0x64e3c10394c2,
+ 0xb736412614af,
+ 0x324f5df65310,
+ 0x643fb6de2217,
+ 0x82f435dedf01,
+ 0x0263de1278f3,
+ 0x51284c3686a6,
+ 0x6a470d54127c,
+};
+
+bool bip_verify(Nfc* nfc) {
+ bool verified = true;
+
+ const uint8_t verify_sector = 0;
+ uint8_t block_num = mf_classic_get_first_block_num_of_sector(verify_sector);
+ FURI_LOG_D(TAG, "Verifying sector %u", verify_sector);
+
+ MfClassicKey key_a_0 = {};
+ bit_lib_num_to_bytes_be(bip_keys_a[0], COUNT_OF(key_a_0.data), key_a_0.data);
+
+ MfClassicAuthContext auth_ctx = {};
+ MfClassicError error =
+ mf_classic_poller_sync_auth(nfc, block_num, &key_a_0, MfClassicKeyTypeA, &auth_ctx);
+
+ if(error == MfClassicErrorNotPresent) {
+ FURI_LOG_D(TAG, "Failed to read block %u: %d", block_num, error);
+ verified = false;
+ }
+
+ return verified;
+}
+
+static bool bip_read(Nfc* nfc, NfcDevice* device) {
+ furi_assert(nfc);
+ furi_assert(device);
+
+ bool is_read = false;
+
+ MfClassicData* data = mf_classic_alloc();
+ nfc_device_copy_data(device, NfcProtocolMfClassic, data);
+
+ do {
+ MfClassicType type = MfClassicType1k;
+ MfClassicError error = mf_classic_poller_sync_detect_type(nfc, &type);
+ if(error == MfClassicErrorNotPresent) {
+ FURI_LOG_W(TAG, "Card not MIFARE Classic 1k");
+ break;
+ }
+
+ data->type = type;
+ MfClassicDeviceKeys keys = {};
+ for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) {
+ bit_lib_num_to_bytes_be(bip_keys_a[i], sizeof(MfClassicKey), keys.key_a[i].data);
+ FURI_BIT_SET(keys.key_a_mask, i);
+ bit_lib_num_to_bytes_be(bip_keys_b[i], sizeof(MfClassicKey), keys.key_b[i].data);
+ FURI_BIT_SET(keys.key_b_mask, i);
+ }
+
+ error = mf_classic_poller_sync_read(nfc, &keys, data);
+ if(error == MfClassicErrorNotPresent) {
+ FURI_LOG_W(TAG, "Failed to read data. Bad keys?");
+ break;
+ }
+
+ nfc_device_set_data(device, NfcProtocolMfClassic, data);
+
+ is_read = true;
+ } while(false);
+
+ mf_classic_free(data);
+
+ return is_read;
+}
+
+typedef struct {
+ uint16_t year;
+ uint8_t month;
+ uint8_t day;
+ uint8_t hour;
+ uint8_t minute;
+ uint8_t second;
+} BipTimestamp;
+
+static void parse_bip_timestamp(const MfClassicBlock* block, BipTimestamp* timestamp) {
+ furi_assert(block);
+ furi_assert(timestamp);
+
+ timestamp->day = (((block->data[1] << 8) + block->data[0]) >> 6) & 0x1f;
+ timestamp->month = (((block->data[1] << 8) + block->data[0]) >> 11) & 0xf;
+ timestamp->year = 2000 + ((((block->data[2] << 8) + block->data[1]) >> 7) & 0x1f);
+ timestamp->hour = (((block->data[3] << 8) + block->data[2]) >> 4) & 0x1f;
+ timestamp->minute = (((block->data[3] << 8) + block->data[2]) >> 9) & 0x3f;
+ timestamp->second = (((block->data[4] << 8) + block->data[3]) >> 7) & 0x3f;
+}
+
+static int compare_bip_timestamp(const BipTimestamp* t1, const BipTimestamp* t2) {
+ furi_assert(t1);
+ furi_assert(t2);
+ if(t1->year != t2->year) {
+ return t1->year - t2->year;
+ }
+ if(t1->month != t2->month) {
+ return t1->month - t2->month;
+ }
+ if(t1->day != t2->day) {
+ return t1->day - t2->day;
+ }
+ if(t1->hour != t2->hour) {
+ return t1->hour - t2->hour;
+ }
+ if(t1->minute != t2->minute) {
+ return t1->minute - t2->minute;
+ }
+ if(t1->second != t2->second) {
+ return t1->second - t2->second;
+ }
+ return 0;
+}
+
+static void print_bip_timestamp(const BipTimestamp* timestamp, FuriString* str) {
+ furi_assert(timestamp);
+ furi_assert(str);
+ furi_string_cat_printf(
+ str,
+ "%04u-%02u-%02u %02u:%02u:%02u",
+ timestamp->year,
+ timestamp->month,
+ timestamp->day,
+ timestamp->hour,
+ timestamp->minute,
+ timestamp->second);
+}
+
+static bool is_bip_block_empty(const MfClassicBlock* block) {
+ furi_assert(block);
+ // check if all but last byte are zero (last is checksum)
+ for(size_t i = 0; i < sizeof(block->data) - 1; i++) {
+ if(block->data[i] != 0) {
+ return false;
+ }
+ }
+ return true;
+}
+
+static void parse_uint16_le(const uint8_t* data, uint16_t* value) {
+ furi_assert(data);
+ furi_assert(value);
+
+ *value = (data[0]) | (data[1] << 8);
+}
+
+static void parse_uint32_le(const uint8_t* data, uint32_t* value) {
+ furi_assert(data);
+ furi_assert(value);
+
+ *value = (data[0]) | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
+}
+
+static void parse_uint16_txn_amount(const uint8_t* data, uint16_t* value) {
+ furi_assert(data);
+ furi_assert(value);
+
+ parse_uint16_le(data, value);
+ *value = *value >> 2;
+}
+
+typedef struct {
+ BipTimestamp timestamp;
+ uint16_t amount;
+} BipTransaction;
+
+static bool bip_parse(const NfcDevice* device, FuriString* parsed_data) {
+ furi_assert(device);
+ furi_assert(parsed_data);
+
+ bool parsed = true;
+
+ struct {
+ uint32_t card_id;
+ uint16_t balance;
+ uint16_t flags;
+ BipTimestamp trip_time_window;
+ BipTransaction top_ups[3];
+ BipTransaction charges[3];
+ } bip_data = {
+ .card_id = 0,
+ .balance = 0,
+ .flags = 0,
+ .trip_time_window = {0, 0, 0, 0, 0, 0},
+ .top_ups =
+ {
+ {{0, 0, 0, 0, 0, 0}, 0},
+ {{0, 0, 0, 0, 0, 0}, 0},
+ {{0, 0, 0, 0, 0, 0}, 0},
+ },
+ .charges =
+ {
+ {{0, 0, 0, 0, 0, 0}, 0},
+ {{0, 0, 0, 0, 0, 0}, 0},
+ {{0, 0, 0, 0, 0, 0}, 0},
+ },
+ };
+
+ const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic);
+
+ do {
+ // verify first sector keys
+ MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, 0);
+ uint64_t key = bit_lib_bytes_to_num_be(sec_tr->key_a.data, 6);
+ if(key != bip_keys_a[0]) {
+ parsed = false;
+ break;
+ }
+ key = bit_lib_bytes_to_num_be(sec_tr->key_b.data, 6);
+ if(key != bip_keys_b[0]) {
+ parsed = false;
+ break;
+ }
+
+ // Get Card ID, little-endian 4 bytes at sector 0 block 1, bytes 4-7
+ parse_uint32_le(&data->block[SECTOR_BLOCK_OFFSET(0, 1)].data[4], &bip_data.card_id);
+
+ // Get balance, little-endian 2 bytes at sector 8 block 1, bytes 0-1
+ parse_uint16_le(&data->block[SECTOR_BLOCK_OFFSET(8, 1)].data[0], &bip_data.balance);
+
+ // Get balance flags (negative balance, etc.), little-endian 2 bytes at sector 8 block 1, bytes 2-3
+ parse_uint16_le(&data->block[SECTOR_BLOCK_OFFSET(8, 1)].data[2], &bip_data.flags);
+
+ // Get trip time window, proprietary format, at sector 5 block 1, bytes 0-7
+ parse_bip_timestamp(&data->block[SECTOR_BLOCK_OFFSET(5, 1)], &bip_data.trip_time_window);
+
+ // Last 3 top-ups: sector 10, ring-buffer of 3 blocks, timestamp in bytes 0-7, amount in bytes 9-10
+ for(size_t i = 0; i < 3; i++) {
+ if(is_bip_block_empty(&data->block[SECTOR_BLOCK_OFFSET(10, i)])) {
+ continue;
+ }
+ BipTransaction* top_up = &bip_data.top_ups[i];
+ parse_bip_timestamp(&data->block[SECTOR_BLOCK_OFFSET(10, i)], &top_up->timestamp);
+ parse_uint16_txn_amount(
+ &data->block[SECTOR_BLOCK_OFFSET(10, i)].data[9], &top_up->amount);
+ }
+
+ // Last 3 charges (i.e. trips), sector 11, ring-buffer of 3 blocks, timestamp in bytes 0-7, amount in bytes 10-11
+ for(size_t i = 0; i < 3; i++) {
+ if(is_bip_block_empty(&data->block[SECTOR_BLOCK_OFFSET(11, i)])) {
+ continue;
+ }
+ BipTransaction* charge = &bip_data.charges[i];
+ parse_bip_timestamp(&data->block[SECTOR_BLOCK_OFFSET(11, i)], &charge->timestamp);
+ parse_uint16_txn_amount(
+ &data->block[SECTOR_BLOCK_OFFSET(11, i)].data[10], &charge->amount);
+ }
+
+ // All data is now parsed and stored in bip_data, now print it
+
+ // Print basic info
+ furi_string_printf(
+ parsed_data,
+ "\e#Tarjeta Bip!\n"
+ "Card Number: %lu\n"
+ "Balance: $%hu (flags %hu)\n"
+ "Current Trip Window Ends:\n @",
+ bip_data.card_id,
+ bip_data.balance,
+ bip_data.flags);
+
+ print_bip_timestamp(&bip_data.trip_time_window, parsed_data);
+
+ // Find newest top-up
+ size_t newest_top_up = 0;
+ for(size_t i = 1; i < 3; i++) {
+ const BipTimestamp* newest = &bip_data.top_ups[newest_top_up].timestamp;
+ const BipTimestamp* current = &bip_data.top_ups[i].timestamp;
+ if(compare_bip_timestamp(current, newest) > 0) {
+ newest_top_up = i;
+ }
+ }
+
+ // Print top-ups, newest first
+ furi_string_cat_printf(parsed_data, "\n\e#Last Top-ups");
+ for(size_t i = 0; i < 3; i++) {
+ const BipTransaction* top_up = &bip_data.top_ups[(3u + newest_top_up - i) % 3];
+ furi_string_cat_printf(parsed_data, "\n+$%d\n @", top_up->amount);
+ print_bip_timestamp(&top_up->timestamp, parsed_data);
+ }
+
+ // Find newest charge
+ size_t newest_charge = 0;
+ for(size_t i = 1; i < 3; i++) {
+ const BipTimestamp* newest = &bip_data.charges[newest_charge].timestamp;
+ const BipTimestamp* current = &bip_data.charges[i].timestamp;
+ if(compare_bip_timestamp(current, newest) > 0) {
+ newest_charge = i;
+ }
+ }
+
+ // Print charges
+ furi_string_cat_printf(parsed_data, "\n\e#Last Charges (Trips)");
+ for(size_t i = 0; i < 3; i++) {
+ const BipTransaction* charge = &bip_data.charges[(3u + newest_charge - i) % 3];
+ furi_string_cat_printf(parsed_data, "\n-$%d\n @", charge->amount);
+ print_bip_timestamp(&charge->timestamp, parsed_data);
+ }
+
+ parsed = true;
+ } while(false);
+
+ return parsed;
+}
+
+/* Actual implementation of app<>plugin interface */
+static const NfcSupportedCardsPlugin bip_plugin = {
+ .protocol = NfcProtocolMfClassic,
+ .verify = bip_verify,
+ .read = bip_read,
+ .parse = bip_parse,
+};
+
+/* Plugin descriptor to comply with basic plugin specification */
+static const FlipperAppPluginDescriptor bip_plugin_descriptor = {
+ .appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID,
+ .ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION,
+ .entry_point = &bip_plugin,
+};
+
+/* Plugin entry point - must return a pointer to const descriptor */
+const FlipperAppPluginDescriptor* bip_plugin_ep() {
+ return &bip_plugin_descriptor;
+}
diff --git a/applications/main/nfc/plugins/supported_cards/troika.c b/applications/main/nfc/plugins/supported_cards/troika.c
index 06da5424c8..fc17c7df9b 100644
--- a/applications/main/nfc/plugins/supported_cards/troika.c
+++ b/applications/main/nfc/plugins/supported_cards/troika.c
@@ -2,6 +2,7 @@
#include
#include
+#include "../../api/mosgortrans/mosgortrans_util.h"
#include
#include
@@ -38,1003 +39,48 @@ static const MfClassicKeyPair troika_1k_keys[] = {
};
static const MfClassicKeyPair troika_4k_keys[] = {
- {.a = 0xa0a1a2a3a4a5, .b = 0xfbf225dc5d58}, {.a = 0xa82607b01c0d, .b = 0x2910989b6880},
- {.a = 0x2aa05ed1856f, .b = 0xeaac88e5dc99}, {.a = 0x2aa05ed1856f, .b = 0xeaac88e5dc99},
- {.a = 0x73068f118c13, .b = 0x2b7f3253fac5}, {.a = 0xfbc2793d540b, .b = 0xd3a297dc2698},
- {.a = 0x2aa05ed1856f, .b = 0xeaac88e5dc99}, {.a = 0xae3d65a3dad4, .b = 0x0f1c63013dbb},
- {.a = 0xa73f5dc1d333, .b = 0xe35173494a81}, {.a = 0x69a32f1c2f19, .b = 0x6b8bd9860763},
- {.a = 0x9becdf3d9273, .b = 0xf8493407799d}, {.a = 0x08b386463229, .b = 0x5efbaecef46b},
- {.a = 0xcd4c61c26e3d, .b = 0x31c7610de3b0}, {.a = 0xa82607b01c0d, .b = 0x2910989b6880},
- {.a = 0x0e8f64340ba4, .b = 0x4acec1205d75}, {.a = 0x2aa05ed1856f, .b = 0xeaac88e5dc99},
- {.a = 0x6b02733bb6ec, .b = 0x7038cd25c408}, {.a = 0x403d706ba880, .b = 0xb39d19a280df},
- {.a = 0xc11f4597efb5, .b = 0x70d901648cb9}, {.a = 0x0db520c78c1c, .b = 0x73e5b9d9d3a4},
- {.a = 0x3ebce0925b2f, .b = 0x372cc880f216}, {.a = 0x16a27af45407, .b = 0x9868925175ba},
- {.a = 0xaba208516740, .b = 0xce26ecb95252}, {.a = 0xcd64e567abcd, .b = 0x8f79c4fd8a01},
- {.a = 0x764cd061f1e6, .b = 0xa74332f74994}, {.a = 0x1cc219e9fec1, .b = 0xb90de525ceb6},
- {.a = 0x2fe3cb83ea43, .b = 0xfba88f109b32}, {.a = 0x07894ffec1d6, .b = 0xefcb0e689db3},
- {.a = 0x04c297b91308, .b = 0xc8454c154cb5}, {.a = 0x7a38e3511a38, .b = 0xab16584c972a},
- {.a = 0x7545df809202, .b = 0xecf751084a80}, {.a = 0x5125974cd391, .b = 0xd3eafb5df46d},
- {.a = 0x7a86aa203788, .b = 0xe41242278ca2}, {.a = 0xafcef64c9913, .b = 0x9db96dca4324},
- {.a = 0x04eaa462f70b, .b = 0xac17b93e2fae}, {.a = 0xe734c210f27e, .b = 0x29ba8c3e9fda},
- {.a = 0xd5524f591eed, .b = 0x5daf42861b4d}, {.a = 0xe4821a377b75, .b = 0xe8709e486465},
- {.a = 0x518dc6eea089, .b = 0x97c64ac98ca4}, {.a = 0xbb52f8cce07f, .b = 0x6b6119752c70},
+ {.a = 0xEC29806D9738, .b = 0xFBF225DC5D58}, //1
+ {.a = 0xA0A1A2A3A4A5, .b = 0x7DE02A7F6025}, //2
+ {.a = 0x2AA05ED1856F, .b = 0xEAAC88E5DC99}, //3
+ {.a = 0x2AA05ED1856F, .b = 0xEAAC88E5DC99}, //4
+ {.a = 0x73068F118C13, .b = 0x2B7F3253FAC5}, //5
+ {.a = 0xFBC2793D540B, .b = 0xD3A297DC2698}, //6
+ {.a = 0x2AA05ED1856F, .b = 0xEAAC88E5DC99}, //7
+ {.a = 0xAE3D65A3DAD4, .b = 0x0F1C63013DBA}, //8
+ {.a = 0xA73F5DC1D333, .b = 0xE35173494A81}, //9
+ {.a = 0x69A32F1C2F19, .b = 0x6B8BD9860763}, //10
+ {.a = 0x9BECDF3D9273, .b = 0xF8493407799D}, //11
+ {.a = 0x08B386463229, .b = 0x5EFBAECEF46B}, //12
+ {.a = 0xCD4C61C26E3D, .b = 0x31C7610DE3B0}, //13
+ {.a = 0xA82607B01C0D, .b = 0x2910989B6880}, //14
+ {.a = 0x0E8F64340BA4, .b = 0x4ACEC1205D75}, //15
+ {.a = 0x2AA05ED1856F, .b = 0xEAAC88E5DC99}, //16
+ {.a = 0x6B02733BB6EC, .b = 0x7038CD25C408}, //17
+ {.a = 0x403D706BA880, .b = 0xB39D19A280DF}, //18
+ {.a = 0xC11F4597EFB5, .b = 0x70D901648CB9}, //19
+ {.a = 0x0DB520C78C1C, .b = 0x73E5B9D9D3A4}, //20
+ {.a = 0x3EBCE0925B2F, .b = 0x372CC880F216}, //21
+ {.a = 0x16A27AF45407, .b = 0x9868925175BA}, //22
+ {.a = 0xABA208516740, .b = 0xCE26ECB95252}, //23
+ {.a = 0xCD64E567ABCD, .b = 0x8F79C4FD8A01}, //24
+ {.a = 0x764CD061F1E6, .b = 0xA74332F74994}, //25
+ {.a = 0x1CC219E9FEC1, .b = 0xB90DE525CEB6}, //26
+ {.a = 0x2FE3CB83EA43, .b = 0xFBA88F109B32}, //27
+ {.a = 0x07894FFEC1D6, .b = 0xEFCB0E689DB3}, //28
+ {.a = 0x04C297B91308, .b = 0xC8454C154CB5}, //29
+ {.a = 0x7A38E3511A38, .b = 0xAB16584C972A}, //30
+ {.a = 0x7545DF809202, .b = 0xECF751084A80}, //31
+ {.a = 0x5125974CD391, .b = 0xD3EAFB5DF46D}, //32
+ {.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF}, //33
+ {.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF}, //34
+ {.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF}, //35
+ {.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF}, //36
+ {.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF}, //37
+ {.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF}, //38
+ {.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF}, //39
+ {.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF}, //40
};
-void from_days_to_datetime(uint16_t days, DateTime* datetime, uint16_t start_year) {
- uint32_t timestamp = days * 24 * 60 * 60;
- DateTime start_datetime = {0};
- start_datetime.year = start_year - 1;
- start_datetime.month = 12;
- start_datetime.day = 31;
- timestamp += datetime_datetime_to_timestamp(&start_datetime);
- datetime_timestamp_to_datetime(timestamp, datetime);
-}
-
-void from_minutes_to_datetime(uint32_t minutes, DateTime* datetime, uint16_t start_year) {
- uint32_t timestamp = minutes * 60;
- DateTime start_datetime = {0};
- start_datetime.year = start_year - 1;
- start_datetime.month = 12;
- start_datetime.day = 31;
- timestamp += datetime_datetime_to_timestamp(&start_datetime);
- datetime_timestamp_to_datetime(timestamp, datetime);
-}
-
-bool parse_transport_block(const MfClassicBlock* block, FuriString* result) {
- uint16_t transport_departament = bit_lib_get_bits_16(block->data, 0, 10);
-
- FURI_LOG_I(TAG, "Transport departament: %x", transport_departament);
-
- uint16_t layout_type = bit_lib_get_bits_16(block->data, 52, 4);
- if(layout_type == 0xE) {
- layout_type = bit_lib_get_bits_16(block->data, 52, 9);
- } else if(layout_type == 0xF) {
- layout_type = bit_lib_get_bits_16(block->data, 52, 14);
- }
-
- FURI_LOG_I(TAG, "Layout type %x", layout_type);
-
- uint16_t card_view = 0;
- uint16_t card_type = 0;
- uint32_t card_number = 0;
- uint8_t card_layout = 0;
- uint8_t card_layout2 = 0;
- uint16_t card_use_before_date = 0;
- uint16_t card_blank_type = 0;
- uint32_t card_start_trip_minutes = 0;
- uint8_t card_minutes_pass = 0;
- uint32_t card_remaining_funds = 0;
- uint16_t card_validator = 0;
- uint8_t card_blocked = 0;
- uint32_t card_hash = 0;
-
- switch(layout_type) {
- case 0x02: {
- card_view = bit_lib_get_bits_16(block->data, 0, 10); //101
- card_type = bit_lib_get_bits_16(block->data, 10, 10); //102
- card_number = bit_lib_get_bits_32(block->data, 20, 32); //201
- card_layout = bit_lib_get_bits(block->data, 52, 4); //111
- card_use_before_date = bit_lib_get_bits_16(block->data, 56, 16); //202
- uint8_t card_benefit_code = bit_lib_get_bits(block->data, 72, 8); //124
- uint32_t card_rfu1 = bit_lib_get_bits_32(block->data, 80, 32); //rfu1
- uint16_t card_crc16 = bit_lib_get_bits_16(block->data, 112, 16); //501.1
- card_blocked = bit_lib_get_bits(block->data, 128, 1); //303
- uint16_t card_start_trip_time = bit_lib_get_bits_16(block->data, 177, 12); //403
- uint16_t card_start_trip_date = bit_lib_get_bits_16(block->data, 189, 16); //402
- uint16_t card_valid_from_date = bit_lib_get_bits_16(block->data, 157, 16); //311
- uint16_t card_valid_by_date = bit_lib_get_bits_16(block->data, 173, 16); //312
- uint8_t card_start_trip_seconds = bit_lib_get_bits(block->data, 189, 6); //406
- uint8_t card_transport_type1 = bit_lib_get_bits(block->data, 180, 2); //421.1
- uint8_t card_transport_type2 = bit_lib_get_bits(block->data, 182, 2); //421.2
- uint8_t card_transport_type3 = bit_lib_get_bits(block->data, 184, 2); //421.3
- uint8_t card_transport_type4 = bit_lib_get_bits(block->data, 186, 2); //421.4
- uint16_t card_use_with_date = bit_lib_get_bits_16(block->data, 189, 16); //205
- uint8_t card_route = bit_lib_get_bits(block->data, 205, 1); //424
- uint16_t card_validator1 = bit_lib_get_bits_16(block->data, 206, 15); //422.1
- card_validator = bit_lib_get_bits_16(block->data, 205, 16); //422
- uint16_t card_total_trips = bit_lib_get_bits_16(block->data, 221, 16); //331
- uint8_t card_write_enabled = bit_lib_get_bits(block->data, 237, 1); //write_enabled
- uint8_t card_rfu2 = bit_lib_get_bits(block->data, 238, 2); //rfu2
- uint16_t card_crc16_2 = bit_lib_get_bits_16(block->data, 240, 16); //501.2
-
- FURI_LOG_D(
- TAG,
- "%x %x %lx %x %x %lx %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x",
- card_view,
- card_type,
- card_number,
- card_use_before_date,
- card_benefit_code,
- card_rfu1,
- card_crc16,
- card_blocked,
- card_start_trip_time,
- card_start_trip_date,
- card_valid_from_date,
- card_valid_by_date,
- card_start_trip_seconds,
- card_transport_type1,
- card_transport_type2,
- card_transport_type3,
- card_transport_type4,
- card_use_with_date,
- card_route,
- card_validator1,
- card_validator,
- card_total_trips,
- card_write_enabled,
- card_rfu2,
- card_crc16_2);
- if(card_valid_by_date == 0) {
- return false;
- }
- DateTime card_use_before_date_s = {0};
- from_days_to_datetime(card_valid_by_date, &card_use_before_date_s, 1992);
-
- DateTime card_start_trip_minutes_s = {0};
- from_minutes_to_datetime(
- (card_start_trip_date) * 24 * 60 + card_start_trip_time,
- &card_start_trip_minutes_s,
- 1992);
- furi_string_printf(
- result,
- "Number: %010lu\nValid for: %02d.%02d.%04d\nTrips: %d\nTrip from: %02d.%02d.%04d %02d:%02d\nValidator: %05d",
- card_number,
- card_use_before_date_s.day,
- card_use_before_date_s.month,
- card_use_before_date_s.year,
- card_total_trips,
- card_start_trip_minutes_s.day,
- card_start_trip_minutes_s.month,
- card_start_trip_minutes_s.year,
- card_start_trip_minutes_s.hour,
- card_start_trip_minutes_s.minute,
- card_validator);
- break;
- }
- case 0x06: {
- card_view = bit_lib_get_bits_16(block->data, 0, 10); //101
- card_type = bit_lib_get_bits_16(block->data, 10, 10); //102
- card_number = bit_lib_get_bits_32(block->data, 20, 32); //201
- card_layout = bit_lib_get_bits(block->data, 52, 4); //111
- card_use_before_date = bit_lib_get_bits_16(block->data, 56, 16); //202
- uint8_t card_geozone_a = bit_lib_get_bits(block->data, 72, 4); //GeoZoneA
- uint8_t card_geozone_b = bit_lib_get_bits(block->data, 76, 4); //GeoZoneB
- card_blank_type = bit_lib_get_bits_16(block->data, 80, 10); //121.
- uint16_t card_type_of_extended = bit_lib_get_bits_16(block->data, 90, 10); //122
- uint32_t card_rfu1 = bit_lib_get_bits_16(block->data, 100, 12); //rfu1
- uint16_t card_crc16 = bit_lib_get_bits_16(block->data, 112, 16); //501.1
- card_blocked = bit_lib_get_bits(block->data, 128, 1); //303
- uint16_t card_start_trip_time = bit_lib_get_bits_16(block->data, 129, 12); //403
- uint16_t card_start_trip_date = bit_lib_get_bits_16(block->data, 141, 16); //402
- uint16_t card_valid_from_date = bit_lib_get_bits_16(block->data, 157, 16); //311
- uint16_t card_valid_by_date = bit_lib_get_bits_16(block->data, 173, 16); //312
- uint16_t card_company = bit_lib_get_bits(block->data, 189, 4); //Company
- uint8_t card_validator1 = bit_lib_get_bits(block->data, 193, 4); //422.1
- uint16_t card_remaining_trips = bit_lib_get_bits_16(block->data, 197, 10); //321
- uint8_t card_units = bit_lib_get_bits(block->data, 207, 6); //Units
- uint16_t card_validator2 = bit_lib_get_bits_16(block->data, 213, 10); //422.2
- uint16_t card_total_trips = bit_lib_get_bits_16(block->data, 223, 16); //331
- uint8_t card_extended = bit_lib_get_bits(block->data, 239, 1); //123
- uint16_t card_crc16_2 = bit_lib_get_bits_16(block->data, 240, 16); //501.2
-
- FURI_LOG_D(
- TAG,
- "%x %x %lx %x %x %x %x %x %lx %x %x %x %x %x %x %x %x %x %x %x %x %x %x",
- card_view,
- card_type,
- card_number,
- card_use_before_date,
- card_geozone_a,
- card_geozone_b,
- card_blank_type,
- card_type_of_extended,
- card_rfu1,
- card_crc16,
- card_blocked,
- card_start_trip_time,
- card_start_trip_date,
- card_valid_from_date,
- card_valid_by_date,
- card_company,
- card_validator1,
- card_remaining_trips,
- card_units,
- card_validator2,
- card_total_trips,
- card_extended,
- card_crc16_2);
- card_validator = card_validator1 * 1024 + card_validator2;
- DateTime card_use_before_date_s = {0};
- from_days_to_datetime(card_valid_by_date, &card_use_before_date_s, 1992);
-
- DateTime card_start_trip_minutes_s = {0};
- from_minutes_to_datetime(
- (card_start_trip_date) * 24 * 60 + card_start_trip_time,
- &card_start_trip_minutes_s,
- 1992);
- furi_string_printf(
- result,
- "Number: %010lu\nValid for: %02d.%02d.%04d\nTrips left: %d of %d\nTrip from: %02d.%02d.%04d %02d:%02d\nValidator: %05d",
- card_number,
- card_use_before_date_s.day,
- card_use_before_date_s.month,
- card_use_before_date_s.year,
- card_remaining_trips,
- card_total_trips,
- card_start_trip_minutes_s.day,
- card_start_trip_minutes_s.month,
- card_start_trip_minutes_s.year,
- card_start_trip_minutes_s.hour,
- card_start_trip_minutes_s.minute,
- card_validator);
- break;
- }
- case 0x08: {
- card_view = bit_lib_get_bits_16(block->data, 0, 10); //101
- card_type = bit_lib_get_bits_16(block->data, 10, 10); //102
- card_number = bit_lib_get_bits_32(block->data, 20, 32); //201
- card_layout = bit_lib_get_bits(block->data, 52, 4); //111
- card_use_before_date = bit_lib_get_bits_16(block->data, 56, 16); //202
- uint64_t card_rfu1 = bit_lib_get_bits_64(block->data, 72, 56); //rfu1
- uint16_t card_valid_from_date = bit_lib_get_bits_16(block->data, 128, 16); //311
- uint8_t card_valid_for_days = bit_lib_get_bits(block->data, 144, 8); //313
- uint8_t card_requires_activation = bit_lib_get_bits(block->data, 152, 1); //301
- uint8_t card_rfu2 = bit_lib_get_bits(block->data, 153, 7); //rfu2
- uint8_t card_remaining_trips1 = bit_lib_get_bits(block->data, 160, 8); //321.1
- uint8_t card_remaining_trips = bit_lib_get_bits(block->data, 168, 8); //321
- uint8_t card_validator1 = bit_lib_get_bits(block->data, 193, 2); //422.1
- uint16_t card_validator = bit_lib_get_bits_16(block->data, 177, 15); //422
- card_hash = bit_lib_get_bits_32(block->data, 192, 32); //502
- uint32_t card_rfu3 = bit_lib_get_bits_32(block->data, 224, 32); //rfu3
-
- FURI_LOG_D(
- TAG,
- "%x %x %lx %x %llx %x %x %x %x %x %x %x %x %lx %x %lx",
- card_view,
- card_type,
- card_number,
- card_use_before_date,
- card_rfu1,
- card_valid_from_date,
- card_valid_for_days,
- card_requires_activation,
- card_rfu2,
- card_remaining_trips1,
- card_remaining_trips,
- card_validator1,
- card_validator,
- card_hash,
- card_valid_from_date,
- card_rfu3);
- DateTime card_use_before_date_s = {0};
- from_days_to_datetime(card_use_before_date, &card_use_before_date_s, 1992);
-
- furi_string_printf(
- result,
- "Number: %010lu\nValid for: %02d.%02d.%04d\nTrips left: %d\nValidator: %05d",
- card_number,
- card_use_before_date_s.day,
- card_use_before_date_s.month,
- card_use_before_date_s.year,
- card_remaining_trips,
- card_validator);
- break;
- }
- case 0x0A: {
- card_view = bit_lib_get_bits_16(block->data, 0, 10); //101
- card_type = bit_lib_get_bits_16(block->data, 10, 10); //102
- card_number = bit_lib_get_bits_32(block->data, 20, 32); //201
- card_layout = bit_lib_get_bits(block->data, 52, 4); //111
- uint16_t card_valid_from_date = bit_lib_get_bits_16(block->data, 64, 12); //311
- uint32_t card_valid_for_minutes = bit_lib_get_bits_32(block->data, 76, 19); //314
- uint8_t card_requires_activation = bit_lib_get_bits(block->data, 95, 1); //301
- card_start_trip_minutes = bit_lib_get_bits_32(block->data, 96, 19); //405
- card_minutes_pass = bit_lib_get_bits(block->data, 119, 7); //412
- uint8_t card_transport_type_flag = bit_lib_get_bits(block->data, 126, 2); //421.0
- uint8_t card_remaining_trips = bit_lib_get_bits(block->data, 128, 8); //321
- uint16_t card_validator = bit_lib_get_bits_16(block->data, 136, 16); //422
- uint8_t card_transport_type1 = bit_lib_get_bits(block->data, 152, 2); //421.1
- uint8_t card_transport_type2 = bit_lib_get_bits(block->data, 154, 2); //421.2
- uint8_t card_transport_type3 = bit_lib_get_bits(block->data, 156, 2); //421.3
- uint8_t card_transport_type4 = bit_lib_get_bits(block->data, 158, 2); //421.4
- card_hash = bit_lib_get_bits_32(block->data, 192, 32); //502
-
- FURI_LOG_D(
- TAG,
- "%x %x %lx %x %x %lx %x %lx %x %x %x %x %x %x %x %x %lx",
- card_view,
- card_type,
- card_number,
- card_use_before_date,
- card_valid_from_date,
- card_valid_for_minutes,
- card_requires_activation,
- card_start_trip_minutes,
- card_minutes_pass,
- card_transport_type_flag,
- card_remaining_trips,
- card_validator,
- card_transport_type1,
- card_transport_type2,
- card_transport_type3,
- card_transport_type4,
- card_hash);
- DateTime card_use_before_date_s = {0};
- from_days_to_datetime(card_use_before_date, &card_use_before_date_s, 2016);
-
- DateTime card_start_trip_minutes_s = {0};
- from_minutes_to_datetime(card_start_trip_minutes, &card_start_trip_minutes_s, 2016);
- furi_string_printf(
- result,
- "Number: %010lu\nValid for: %02d.%02d.%04d\nTrip from: %02d.%02d.%04d %02d:%02d\nTrips left: %d\nValidator: %05d",
- card_number,
- card_use_before_date_s.day,
- card_use_before_date_s.month,
- card_use_before_date_s.year,
- card_start_trip_minutes_s.day,
- card_start_trip_minutes_s.month,
- card_start_trip_minutes_s.year,
- card_start_trip_minutes_s.hour,
- card_start_trip_minutes_s.minute,
- card_remaining_trips,
- card_validator);
- break;
- }
- case 0x0C: {
- card_view = bit_lib_get_bits_16(block->data, 0, 10); //101
- card_type = bit_lib_get_bits_16(block->data, 10, 10); //102
- card_number = bit_lib_get_bits_32(block->data, 20, 32); //201
- card_layout = bit_lib_get_bits(block->data, 52, 4); //111
- card_use_before_date = bit_lib_get_bits_16(block->data, 56, 16); //202
- uint64_t card_rfu1 = bit_lib_get_bits_64(block->data, 72, 56); //rfu1
- uint16_t card_valid_from_date = bit_lib_get_bits_16(block->data, 128, 16); //311
- uint8_t card_valid_for_days = bit_lib_get_bits(block->data, 144, 8); //313
- uint8_t card_requires_activation = bit_lib_get_bits(block->data, 152, 1); //301
- uint16_t card_rfu2 = bit_lib_get_bits_16(block->data, 153, 13); //rfu2
- uint16_t card_remaining_trips = bit_lib_get_bits_16(block->data, 166, 10); //321
- uint16_t card_validator = bit_lib_get_bits_16(block->data, 176, 16); //422
- card_hash = bit_lib_get_bits_32(block->data, 192, 32); //502
- uint16_t card_start_trip_date = bit_lib_get_bits_16(block->data, 224, 16); //402
- uint16_t card_start_trip_time = bit_lib_get_bits_16(block->data, 240, 11); //403
- uint8_t card_transport_type = bit_lib_get_bits(block->data, 251, 2); //421
- uint8_t card_rfu3 = bit_lib_get_bits(block->data, 253, 2); //rfu3
- uint8_t card_transfer_in_metro = bit_lib_get_bits(block->data, 255, 1); //432
-
- FURI_LOG_D(
- TAG,
- "%x %x %lx %x %llx %x %x %x %x %x %x %x %x %x %x %x",
- card_view,
- card_type,
- card_number,
- card_use_before_date,
- card_rfu1,
- card_valid_from_date,
- card_valid_for_days,
- card_requires_activation,
- card_rfu2,
- card_remaining_trips,
- card_validator,
- card_start_trip_date,
- card_start_trip_time,
- card_transport_type,
- card_rfu3,
- card_transfer_in_metro);
- DateTime card_use_before_date_s = {0};
- from_days_to_datetime(card_use_before_date, &card_use_before_date_s, 1992);
- DateTime card_start_trip_minutes_s = {0};
- from_minutes_to_datetime(
- (card_start_trip_date) * 24 * 60 + card_start_trip_time,
- &card_start_trip_minutes_s,
- 1992);
- furi_string_printf(
- result,
- "Number: %010lu\nValid for: %02d.%02d.%04d\nTrip from: %02d.%02d.%04d %02d:%02d\nTrips left: %d\nValidator: %05d",
- card_number,
- card_use_before_date_s.day,
- card_use_before_date_s.month,
- card_use_before_date_s.year,
- card_start_trip_minutes_s.day,
- card_start_trip_minutes_s.month,
- card_start_trip_minutes_s.year,
- card_start_trip_minutes_s.hour,
- card_start_trip_minutes_s.minute,
- card_remaining_trips,
- card_validator);
- break;
- }
- case 0x0D: {
- card_view = bit_lib_get_bits_16(block->data, 0, 10); //101
- card_type = bit_lib_get_bits_16(block->data, 10, 10); //102
- card_number = bit_lib_get_bits_32(block->data, 20, 32); //201
- card_layout = bit_lib_get_bits(block->data, 52, 4); //111
- uint8_t card_rfu1 = bit_lib_get_bits(block->data, 56, 8); //rfu1
- card_use_before_date = bit_lib_get_bits_16(block->data, 64, 16); //202
- uint16_t card_valid_for_time = bit_lib_get_bits_16(block->data, 80, 11); //316
- uint8_t card_rfu2 = bit_lib_get_bits(block->data, 91, 5); //rfu2
- uint16_t card_use_before_date2 = bit_lib_get_bits_16(block->data, 96, 16); //202.2
- uint16_t card_valid_for_time2 = bit_lib_get_bits_16(block->data, 123, 11); //316.2
- uint8_t card_rfu3 = bit_lib_get_bits(block->data, 123, 5); //rfu3
- uint16_t card_valid_from_date = bit_lib_get_bits_16(block->data, 128, 16); //311
- uint8_t card_valid_for_days = bit_lib_get_bits(block->data, 144, 8); //313
- uint8_t card_requires_activation = bit_lib_get_bits(block->data, 152, 1); //301
- uint8_t card_rfu4 = bit_lib_get_bits(block->data, 153, 2); //rfu4
- uint8_t card_passage_5_minutes = bit_lib_get_bits(block->data, 155, 5); //413
- uint8_t card_transport_type1 = bit_lib_get_bits(block->data, 160, 2); //421.1
- uint8_t card_passage_in_metro = bit_lib_get_bits(block->data, 162, 1); //431
- uint8_t card_passages_ground_transport = bit_lib_get_bits(block->data, 163, 3); //433
- uint16_t card_remaining_trips = bit_lib_get_bits_16(block->data, 166, 10); //321
- uint16_t card_validator = bit_lib_get_bits_16(block->data, 176, 16); //422
- card_hash = bit_lib_get_bits_32(block->data, 192, 32); //502
- uint16_t card_start_trip_date = bit_lib_get_bits_16(block->data, 224, 16); //402
- uint16_t card_start_trip_time = bit_lib_get_bits_16(block->data, 240, 11); //403
- uint8_t card_transport_type2 = bit_lib_get_bits(block->data, 251, 2); //421.2
- uint8_t card_rfu5 = bit_lib_get_bits(block->data, 253, 2); //rfu5
- uint8_t card_transfer_in_metro = bit_lib_get_bits(block->data, 255, 1); //432
-
- FURI_LOG_D(
- TAG,
- "%x %x %lx %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x",
- card_view,
- card_type,
- card_number,
- card_layout,
- card_rfu1,
- card_use_before_date,
- card_valid_for_time,
- card_rfu2,
- card_use_before_date2,
- card_valid_for_time2,
- card_rfu3,
- card_valid_from_date,
- card_valid_for_days,
- card_requires_activation,
- card_rfu4,
- card_passage_5_minutes,
- card_transport_type1,
- card_passage_in_metro,
- card_passages_ground_transport,
- card_remaining_trips,
- card_validator,
- card_start_trip_date,
- card_start_trip_time,
- card_transport_type2,
- card_rfu5,
- card_transfer_in_metro);
- DateTime card_use_before_date_s = {0};
- from_days_to_datetime(card_use_before_date, &card_use_before_date_s, 1992);
- DateTime card_start_trip_minutes_s = {0};
- from_minutes_to_datetime(
- (card_start_trip_date) * 24 * 60 + card_start_trip_time,
- &card_start_trip_minutes_s,
- 1992);
- furi_string_printf(
- result,
- "Number: %010lu\nValid for: %02d.%02d.%04d\nTrip from: %02d.%02d.%04d %02d:%02d\nTrips left: %d\nValidator: %05d",
- card_number,
- card_use_before_date_s.day,
- card_use_before_date_s.month,
- card_use_before_date_s.year,
- card_start_trip_minutes_s.day,
- card_start_trip_minutes_s.month,
- card_start_trip_minutes_s.year,
- card_start_trip_minutes_s.hour,
- card_start_trip_minutes_s.minute,
- card_remaining_trips,
- card_validator);
- break;
- }
- case 0x1C1: {
- card_view = bit_lib_get_bits_16(block->data, 0, 10); //101
- card_type = bit_lib_get_bits_16(block->data, 10, 10); //102
- card_number = bit_lib_get_bits_32(block->data, 20, 32); //201
- card_layout = bit_lib_get_bits(block->data, 52, 4); //111
- card_layout2 = bit_lib_get_bits(block->data, 56, 5); //112
- card_use_before_date = bit_lib_get_bits_16(block->data, 61, 16); //202.
- card_blank_type = bit_lib_get_bits_16(block->data, 77, 10); //121.
- card_validator = bit_lib_get_bits_16(block->data, 128, 16); //422
- uint16_t card_start_trip_date = bit_lib_get_bits_16(block->data, 144, 16); //402
- uint16_t card_start_trip_time = bit_lib_get_bits_16(block->data, 160, 11); //403
- uint8_t card_transport_type1 = bit_lib_get_bits(block->data, 171, 2); //421.1
- uint8_t card_transport_type2 = bit_lib_get_bits(block->data, 173, 2); //421.2
- uint8_t card_transfer_in_metro = bit_lib_get_bits(block->data, 177, 1); //432
- uint8_t card_passage_in_metro = bit_lib_get_bits(block->data, 178, 1); //431
- uint8_t card_passages_ground_transport = bit_lib_get_bits(block->data, 179, 3); //433
- card_minutes_pass = bit_lib_get_bits(block->data, 185, 8); //412.
- card_remaining_funds = bit_lib_get_bits_32(block->data, 196, 19) / 100; //322
- uint8_t card_fare_trip = bit_lib_get_bits(block->data, 215, 2); //441
- card_blocked = bit_lib_get_bits(block->data, 202, 1); //303
- uint8_t card_zoo = bit_lib_get_bits(block->data, 218, 1); //zoo
- card_hash = bit_lib_get_bits_32(block->data, 224, 32); //502
-
- FURI_LOG_D(
- TAG,
- "%x %x %lx %x %x %x %x %x %x %x %x %x %x %x %x %x %lx %x %x %x %lx",
- card_view,
- card_type,
- card_number,
- card_layout,
- card_layout2,
- card_use_before_date,
- card_blank_type,
- card_validator,
- card_start_trip_date,
- card_start_trip_time,
- card_transport_type1,
- card_transport_type2,
- card_transfer_in_metro,
- card_passage_in_metro,
- card_passages_ground_transport,
- card_minutes_pass,
- card_remaining_funds,
- card_fare_trip,
- card_blocked,
- card_zoo,
- card_hash);
- DateTime card_use_before_date_s = {0};
- from_days_to_datetime(card_use_before_date, &card_use_before_date_s, 1992);
-
- DateTime card_start_trip_minutes_s = {0};
- from_minutes_to_datetime(card_start_trip_minutes, &card_start_trip_minutes_s, 1992);
- furi_string_printf(
- result,
- "Number: %010lu\nValid for: %02d.%02d.%04d\nTrip from: %02d.%02d.%04d %02d:%02d\nValidator: %05d",
- card_number,
- card_use_before_date_s.day,
- card_use_before_date_s.month,
- card_use_before_date_s.year,
- card_start_trip_minutes_s.day,
- card_start_trip_minutes_s.month,
- card_start_trip_minutes_s.year,
- card_start_trip_minutes_s.hour,
- card_start_trip_minutes_s.minute,
- card_validator);
- break;
- }
- case 0x1C2: {
- card_view = bit_lib_get_bits_16(block->data, 0, 10); //101
- card_type = bit_lib_get_bits_16(block->data, 10, 10); //102
- card_number = bit_lib_get_bits_32(block->data, 20, 32); //201
- card_layout = bit_lib_get_bits(block->data, 52, 4); //111
- card_layout2 = bit_lib_get_bits(block->data, 56, 5); //112
- uint16_t card_type_of_extended = bit_lib_get_bits_16(block->data, 61, 10); //122
- card_use_before_date = bit_lib_get_bits_16(block->data, 71, 16); //202.
- card_blank_type = bit_lib_get_bits_16(block->data, 87, 10); //121.
- uint16_t card_valid_to_date = bit_lib_get_bits_16(block->data, 97, 16); //311
- uint16_t card_activate_during = bit_lib_get_bits_16(block->data, 113, 9); //302
- uint32_t card_valid_for_minutes = bit_lib_get_bits_32(block->data, 131, 20); //314
- card_minutes_pass = bit_lib_get_bits(block->data, 154, 8); //412.
- uint8_t card_transport_type = bit_lib_get_bits(block->data, 163, 2); //421
- uint8_t card_passage_in_metro = bit_lib_get_bits(block->data, 165, 1); //431
- uint8_t card_transfer_in_metro = bit_lib_get_bits(block->data, 166, 1); //432
- uint16_t card_remaining_trips = bit_lib_get_bits_16(block->data, 167, 10); //321
- card_validator = bit_lib_get_bits_16(block->data, 177, 16); //422
- uint32_t card_start_trip_neg_minutes = bit_lib_get_bits_32(block->data, 196, 20); //404
- uint8_t card_requires_activation = bit_lib_get_bits(block->data, 216, 1); //301
- card_blocked = bit_lib_get_bits(block->data, 217, 1); //303
- uint8_t card_extended = bit_lib_get_bits(block->data, 218, 1); //123
- card_hash = bit_lib_get_bits_32(block->data, 224, 32); //502
-
- FURI_LOG_D(
- TAG,
- "%x %x %lx %x %x %x %x %x %x %x %lx %x %x %x %x %x %x %lx %x %x %x %lx",
- card_view,
- card_type,
- card_number,
- card_layout,
- card_layout2,
- card_type_of_extended,
- card_use_before_date,
- card_blank_type,
- card_valid_to_date,
- card_activate_during,
- card_valid_for_minutes,
- card_minutes_pass,
- card_transport_type,
- card_passage_in_metro,
- card_transfer_in_metro,
- card_remaining_trips,
- card_validator,
- card_start_trip_neg_minutes,
- card_requires_activation,
- card_blocked,
- card_extended,
- card_hash);
- DateTime card_use_before_date_s = {0};
- from_days_to_datetime(card_use_before_date, &card_use_before_date_s, 2016);
-
- DateTime card_start_trip_minutes_s = {0};
- from_minutes_to_datetime(
- (card_valid_to_date) * 24 * 60 + card_valid_for_minutes - card_start_trip_neg_minutes,
- &card_start_trip_minutes_s,
- 2016); //-time
- furi_string_printf(
- result,
- "Number: %010lu\nValid for: %02d.%02d.%04d\nTrip from: %02d.%02d.%04d %02d:%02d\nValidator: %05d",
- card_number,
- card_use_before_date_s.day,
- card_use_before_date_s.month,
- card_use_before_date_s.year,
- card_start_trip_minutes_s.day,
- card_start_trip_minutes_s.month,
- card_start_trip_minutes_s.year,
- card_start_trip_minutes_s.hour,
- card_start_trip_minutes_s.minute,
- card_validator);
- break;
- }
- case 0x1C3: {
- card_view = bit_lib_get_bits_16(block->data, 0, 10); //101
- card_type = bit_lib_get_bits_16(block->data, 10, 10); //102
- card_number = bit_lib_get_bits_32(block->data, 20, 32); //201
- card_layout = bit_lib_get_bits(block->data, 52, 4); //111
- card_layout2 = bit_lib_get_bits(block->data, 56, 5); //112
- card_use_before_date = bit_lib_get_bits_16(block->data, 61, 16); //202
- card_blank_type = bit_lib_get_bits_16(block->data, 77, 10); //121
- card_remaining_funds = bit_lib_get_bits_32(block->data, 188, 22) / 100; //322
- card_hash = bit_lib_get_bits_32(block->data, 224, 32); //502
- card_validator = bit_lib_get_bits_16(block->data, 128, 16); //422
- card_start_trip_minutes = bit_lib_get_bits_32(block->data, 144, 23); //405
- uint8_t card_fare_trip = bit_lib_get_bits(block->data, 210, 2); //441
- card_minutes_pass = bit_lib_get_bits(block->data, 171, 7); //412
- uint8_t card_transport_type_flag = bit_lib_get_bits(block->data, 178, 2); //421.0
- uint8_t card_transport_type1 = bit_lib_get_bits(block->data, 180, 2); //421.1
- uint8_t card_transport_type2 = bit_lib_get_bits(block->data, 182, 2); //421.2
- uint8_t card_transport_type3 = bit_lib_get_bits(block->data, 184, 2); //421.3
- uint8_t card_transport_type4 = bit_lib_get_bits(block->data, 186, 2); //421.4
- card_blocked = bit_lib_get_bits(block->data, 212, 1); //303
- FURI_LOG_D(
- TAG,
- "Card view: %x, type: %x, number: %lx, layout: %x, layout2: %x, use before date: %x, blank type: %x, remaining funds: %lx, hash: %lx, validator: %x, start trip minutes: %lx, fare trip: %x, minutes pass: %x, transport type flag: %x, transport type1: %x, transport type2: %x, transport type3: %x, transport type4: %x, blocked: %x",
- card_view,
- card_type,
- card_number,
- card_layout,
- card_layout2,
- card_use_before_date,
- card_blank_type,
- card_remaining_funds,
- card_hash,
- card_validator,
- card_start_trip_minutes,
- card_fare_trip,
- card_minutes_pass,
- card_transport_type_flag,
- card_transport_type1,
- card_transport_type2,
- card_transport_type3,
- card_transport_type4,
- card_blocked);
- DateTime card_use_before_date_s = {0};
- from_days_to_datetime(card_use_before_date, &card_use_before_date_s, 1992);
-
- DateTime card_start_trip_minutes_s = {0};
- from_minutes_to_datetime(card_start_trip_minutes, &card_start_trip_minutes_s, 2016);
- furi_string_printf(
- result,
- "Number: %010lu\nValid for: %02d.%02d.%04d\nBalance: %ld rub\nTrip from: %02d.%02d.%04d %02d:%02d\nValidator: %05d",
- card_number,
- card_use_before_date_s.day,
- card_use_before_date_s.month,
- card_use_before_date_s.year,
- card_remaining_funds,
- card_start_trip_minutes_s.day,
- card_start_trip_minutes_s.month,
- card_start_trip_minutes_s.year,
- card_start_trip_minutes_s.hour,
- card_start_trip_minutes_s.minute,
- card_validator);
- break;
- }
- case 0x1C4: {
- card_view = bit_lib_get_bits_16(block->data, 0, 10); //101
- card_type = bit_lib_get_bits_16(block->data, 10, 10); //102
- card_number = bit_lib_get_bits_32(block->data, 20, 32); //201
- card_layout = bit_lib_get_bits(block->data, 52, 4); //111
- card_layout2 = bit_lib_get_bits(block->data, 56, 5); //112
- uint16_t card_type_of_extended = bit_lib_get_bits_16(block->data, 61, 10); //122
- card_use_before_date = bit_lib_get_bits_16(block->data, 71, 13); //202.
- card_blank_type = bit_lib_get_bits_16(block->data, 84, 10); //121.
- uint16_t card_valid_to_date = bit_lib_get_bits_16(block->data, 94, 13); //311
- uint16_t card_activate_during = bit_lib_get_bits_16(block->data, 107, 9); //302
- uint16_t card_extension_counter = bit_lib_get_bits_16(block->data, 116, 10); //304
- uint32_t card_valid_for_minutes = bit_lib_get_bits_32(block->data, 128, 20); //314
- card_minutes_pass = bit_lib_get_bits(block->data, 158, 7); //412.
- uint8_t card_transport_type_flag = bit_lib_get_bits(block->data, 178, 2); //421.0
- uint8_t card_transport_type1 = bit_lib_get_bits(block->data, 180, 2); //421.1
- uint8_t card_transport_type2 = bit_lib_get_bits(block->data, 182, 2); //421.2
- uint8_t card_transport_type3 = bit_lib_get_bits(block->data, 184, 2); //421.3
- uint8_t card_transport_type4 = bit_lib_get_bits(block->data, 186, 2); //421.4
- uint16_t card_remaining_trips = bit_lib_get_bits_16(block->data, 169, 10); //321
- card_validator = bit_lib_get_bits_16(block->data, 179, 16); //422
- uint32_t card_start_trip_neg_minutes = bit_lib_get_bits_32(block->data, 195, 20); //404
- uint8_t card_requires_activation = bit_lib_get_bits(block->data, 215, 1); //301
- card_blocked = bit_lib_get_bits(block->data, 216, 1); //303
- uint8_t card_extended = bit_lib_get_bits(block->data, 217, 1); //123
- card_hash = bit_lib_get_bits_32(block->data, 224, 32); //502
-
- FURI_LOG_D(
- TAG,
- "%x %x %lx %x %x %x %x %x %x %x %x %lx %x %x %x %x %x %x %x %x %lx %x %x %x %lx",
- card_view,
- card_type,
- card_number,
- card_layout,
- card_layout2,
- card_type_of_extended,
- card_use_before_date,
- card_blank_type,
- card_valid_to_date,
- card_activate_during,
- card_extension_counter,
- card_valid_for_minutes,
- card_minutes_pass,
- card_transport_type_flag,
- card_transport_type1,
- card_transport_type2,
- card_transport_type3,
- card_transport_type4,
- card_remaining_trips,
- card_validator,
- card_start_trip_neg_minutes,
- card_requires_activation,
- card_blocked,
- card_extended,
- card_hash);
- DateTime card_use_before_date_s = {0};
- from_days_to_datetime(card_use_before_date, &card_use_before_date_s, 2016);
-
- DateTime card_start_trip_minutes_s = {0};
- from_minutes_to_datetime(
- (card_use_before_date + 1) * 24 * 60 + card_valid_for_minutes -
- card_start_trip_neg_minutes,
- &card_start_trip_minutes_s,
- 2011); //-time
- furi_string_printf(
- result,
- "Number: %010lu\nValid for: %02d.%02d.%04d\nTrip from: %02d.%02d.%04d %02d:%02d\nValidator: %05d",
- card_number,
- card_use_before_date_s.day,
- card_use_before_date_s.month,
- card_use_before_date_s.year,
- card_start_trip_minutes_s.day,
- card_start_trip_minutes_s.month,
- card_start_trip_minutes_s.year,
- card_start_trip_minutes_s.hour,
- card_start_trip_minutes_s.minute,
- card_validator);
- break;
- }
- case 0x1C5: {
- card_view = bit_lib_get_bits_16(block->data, 0, 10); //101
- card_type = bit_lib_get_bits_16(block->data, 10, 10); //102
- card_number = bit_lib_get_bits_32(block->data, 20, 32); //201
- card_layout = bit_lib_get_bits(block->data, 52, 4); //111
- card_layout2 = bit_lib_get_bits(block->data, 56, 5); //112
- card_use_before_date = bit_lib_get_bits_16(block->data, 61, 13); //202.
- card_blank_type = bit_lib_get_bits_16(block->data, 74, 10); //121.
- uint32_t card_valid_to_time = bit_lib_get_bits_32(block->data, 84, 23); //317
- uint16_t card_extension_counter = bit_lib_get_bits_16(block->data, 107, 10); //304
- card_start_trip_minutes = bit_lib_get_bits_32(block->data, 128, 23); //405
- uint8_t card_metro_ride_with = bit_lib_get_bits(block->data, 151, 7); //414
- card_minutes_pass = bit_lib_get_bits(block->data, 158, 7); //412.
- card_remaining_funds = bit_lib_get_bits_32(block->data, 167, 19) / 100; //322
- card_validator = bit_lib_get_bits_16(block->data, 186, 16); //422
- card_blocked = bit_lib_get_bits(block->data, 202, 1); //303
- uint16_t card_route = bit_lib_get_bits_16(block->data, 204, 12); //424
- uint8_t card_passages_ground_transport = bit_lib_get_bits(block->data, 216, 7); //433
- card_hash = bit_lib_get_bits_32(block->data, 224, 32); //502
-
- FURI_LOG_D(
- TAG,
- "%x %x %lx %x %x %x %x %lx %x %lx %x %x %lx %x %x %x %x %lx",
- card_view,
- card_type,
- card_number,
- card_layout,
- card_layout2,
- card_use_before_date,
- card_blank_type,
- card_valid_to_time,
- card_extension_counter,
- card_start_trip_minutes,
- card_metro_ride_with,
- card_minutes_pass,
- card_remaining_funds,
- card_validator,
- card_blocked,
- card_route,
- card_passages_ground_transport,
- card_hash);
- DateTime card_use_before_date_s = {0};
-
- from_days_to_datetime(card_use_before_date, &card_use_before_date_s, 2019);
-
- DateTime card_start_trip_minutes_s = {0};
- from_minutes_to_datetime(card_start_trip_minutes, &card_start_trip_minutes_s, 2019);
- furi_string_printf(
- result,
- "Number: %010lu\nValid for: %02d.%02d.%04d\nBalance: %ld rub\nTrip from: %02d.%02d.%04d %02d:%02d\nValidator: %05d",
- card_number,
- card_use_before_date_s.day,
- card_use_before_date_s.month,
- card_use_before_date_s.year,
- card_remaining_funds,
- card_start_trip_minutes_s.day,
- card_start_trip_minutes_s.month,
- card_start_trip_minutes_s.year,
- card_start_trip_minutes_s.hour,
- card_start_trip_minutes_s.minute,
- card_validator);
- break;
- }
- case 0x1C6: {
- card_view = bit_lib_get_bits_16(block->data, 0, 10); //101
- card_type = bit_lib_get_bits_16(block->data, 10, 10); //102
- card_number = bit_lib_get_bits_32(block->data, 20, 32); //201
- card_layout = bit_lib_get_bits(block->data, 52, 4); //111
- card_layout2 = bit_lib_get_bits(block->data, 56, 5); //112
- uint16_t card_type_of_extended = bit_lib_get_bits_16(block->data, 61, 10); //122
- card_use_before_date = bit_lib_get_bits_16(block->data, 71, 13); //202.
- card_blank_type = bit_lib_get_bits_16(block->data, 84, 10); //121.
- uint32_t card_valid_from_date = bit_lib_get_bits_32(block->data, 94, 23); //311
- uint16_t card_extension_counter = bit_lib_get_bits_16(block->data, 117, 10); //304
- uint32_t card_valid_for_minutes = bit_lib_get_bits_32(block->data, 128, 20); //314
- uint32_t card_start_trip_neg_minutes = bit_lib_get_bits_32(block->data, 148, 20); //404
- uint8_t card_metro_ride_with = bit_lib_get_bits(block->data, 168, 7); //414
- card_minutes_pass = bit_lib_get_bits(block->data, 175, 7); //412.
- uint16_t card_remaining_trips = bit_lib_get_bits_16(block->data, 182, 7); //321
- card_validator = bit_lib_get_bits_16(block->data, 189, 16); //422
- card_blocked = bit_lib_get_bits(block->data, 205, 1); //303
- uint8_t card_extended = bit_lib_get_bits(block->data, 206, 1); //123
- uint16_t card_route = bit_lib_get_bits_16(block->data, 212, 12); //424
- card_hash = bit_lib_get_bits_32(block->data, 224, 32); //502
-
- FURI_LOG_D(
- TAG,
- "%x %x %lx %x %x %x %x %x %lx %x %lx %lx %x %x %x %x %x %x %x %lx",
- card_view,
- card_type,
- card_number,
- card_layout,
- card_layout2,
- card_type_of_extended,
- card_use_before_date,
- card_blank_type,
- card_valid_from_date,
- card_extension_counter,
- card_valid_for_minutes,
- card_start_trip_neg_minutes,
- card_metro_ride_with,
- card_minutes_pass,
- card_remaining_trips,
- card_validator,
- card_blocked,
- card_extended,
- card_route,
- card_hash);
- DateTime card_use_before_date_s = {0};
- from_days_to_datetime(card_use_before_date, &card_use_before_date_s, 2019);
-
- DateTime card_start_trip_minutes_s = {0};
- from_minutes_to_datetime(
- card_valid_from_date + card_valid_for_minutes - card_start_trip_neg_minutes,
- &card_start_trip_minutes_s,
- 2019); //-time
- furi_string_printf(
- result,
- "Number: %010lu\nValid for: %02d.%02d.%04d\nTrip from: %02d.%02d.%04d %02d:%02d\nValidator: %05d",
- card_number,
- card_use_before_date_s.day,
- card_use_before_date_s.month,
- card_use_before_date_s.year,
- card_start_trip_minutes_s.day,
- card_start_trip_minutes_s.month,
- card_start_trip_minutes_s.year,
- card_start_trip_minutes_s.hour,
- card_start_trip_minutes_s.minute,
- card_validator);
- break;
- }
- case 0x3CCB: {
- card_view = bit_lib_get_bits_16(block->data, 0, 10); //101
- card_type = bit_lib_get_bits_16(block->data, 10, 10); //102
- card_number = bit_lib_get_bits_32(block->data, 20, 32); //201
- card_layout = bit_lib_get_bits(block->data, 52, 4); //111
- uint16_t card_tech_code = bit_lib_get_bits_32(block->data, 56, 10); //tech_code
- uint16_t card_valid_to_minutes = bit_lib_get_bits_16(block->data, 66, 16); //311
- uint16_t card_valid_by_date = bit_lib_get_bits_16(block->data, 82, 16); //312
- uint8_t card_interval = bit_lib_get_bits(block->data, 98, 4); //interval
- uint16_t card_app_code1 = bit_lib_get_bits_16(block->data, 102, 16); //app_code1
- uint16_t card_hash1 = bit_lib_get_bits_16(block->data, 112, 16); //502.1
- uint16_t card_type1 = bit_lib_get_bits_16(block->data, 128, 10); //type1
- uint16_t card_app_code2 = bit_lib_get_bits_16(block->data, 138, 10); //app_code2
- uint16_t card_type2 = bit_lib_get_bits_16(block->data, 148, 10); //type2
- uint16_t card_app_code3 = bit_lib_get_bits_16(block->data, 158, 10); //app_code3
- uint16_t card_type3 = bit_lib_get_bits_16(block->data, 148, 10); //type3
- uint16_t card_app_code4 = bit_lib_get_bits_16(block->data, 168, 10); //app_code4
- uint16_t card_type4 = bit_lib_get_bits_16(block->data, 178, 10); //type4
- card_hash = bit_lib_get_bits_32(block->data, 224, 32); //502.2
-
- FURI_LOG_D(
- TAG,
- "%x %x %lx %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %lx",
- card_view,
- card_type,
- card_number,
- card_layout,
- card_tech_code,
- card_use_before_date,
- card_blank_type,
- card_valid_to_minutes,
- card_valid_by_date,
- card_interval,
- card_app_code1,
- card_hash1,
- card_type1,
- card_app_code2,
- card_type2,
- card_app_code3,
- card_type3,
- card_app_code4,
- card_type4,
- card_hash);
- DateTime card_use_before_date_s = {0};
- from_days_to_datetime(card_valid_by_date, &card_use_before_date_s, 1992);
-
- furi_string_printf(
- result,
- "Number: %010lu\nValid for: %02d.%02d.%04d\nValidator: %05d",
- card_number,
- card_use_before_date_s.day,
- card_use_before_date_s.month,
- card_use_before_date_s.year,
- card_validator);
- break;
- }
- case 0x3C0B: {
- card_view = bit_lib_get_bits_16(block->data, 0, 10); //101
- card_type = bit_lib_get_bits_16(block->data, 10, 10); //102
- card_number = bit_lib_get_bits_32(block->data, 20, 32); //201
- card_layout = bit_lib_get_bits(block->data, 52, 4); //111
- uint16_t card_tech_code = bit_lib_get_bits_32(block->data, 56, 10); //tech_code
- uint16_t card_valid_to_minutes = bit_lib_get_bits_16(block->data, 66, 16); //311
- uint16_t card_valid_by_date = bit_lib_get_bits_16(block->data, 82, 16); //312
- uint16_t card_hash = bit_lib_get_bits_16(block->data, 112, 16); //502.1
-
- FURI_LOG_D(
- TAG,
- "%x %x %lx %x %x %x %x %x %x %x",
- card_view,
- card_type,
- card_number,
- card_layout,
- card_tech_code,
- card_use_before_date,
- card_blank_type,
- card_valid_to_minutes,
- card_valid_by_date,
- card_hash);
- DateTime card_use_before_date_s = {0};
- from_days_to_datetime(card_valid_by_date, &card_use_before_date_s, 1992);
-
- furi_string_printf(
- result,
- "Number: %010lu\nValid for: %02d.%02d.%04d\nValidator: %05d",
- card_number,
- card_use_before_date_s.day,
- card_use_before_date_s.month,
- card_use_before_date_s.year,
- card_validator);
- break;
- }
- default:
- return false;
- }
-
- return true;
-}
-
static bool troika_get_card_config(TroikaCardConfig* config, MfClassicType type) {
bool success = true;
@@ -1150,24 +196,30 @@ static bool troika_parse(const NfcDevice* device, FuriString* parsed_data) {
FuriString* metro_result = furi_string_alloc();
FuriString* ground_result = furi_string_alloc();
FuriString* tat_result = furi_string_alloc();
- bool result1 = parse_transport_block(&data->block[32], metro_result);
- bool result2 = parse_transport_block(&data->block[28], ground_result);
- bool result3 = parse_transport_block(&data->block[16], tat_result);
+
+ bool result1 = mosgortrans_parse_transport_block(&data->block[32], metro_result);
+ bool result2 = mosgortrans_parse_transport_block(&data->block[28], ground_result);
+ bool result3 = mosgortrans_parse_transport_block(&data->block[16], tat_result);
+
furi_string_cat_printf(parsed_data, "\e#Troyka card\n");
if(result1) {
furi_string_cat_printf(
parsed_data, "\e#Metro\n%s\n", furi_string_get_cstr(metro_result));
}
+
if(result2) {
furi_string_cat_printf(
parsed_data, "\e#Ediniy\n%s\n", furi_string_get_cstr(ground_result));
}
+
if(result3) {
furi_string_cat_printf(parsed_data, "\e#TAT\n%s\n", furi_string_get_cstr(tat_result));
}
+
furi_string_free(tat_result);
furi_string_free(ground_result);
furi_string_free(metro_result);
+
parsed = result1 || result2 || result3;
} while(false);
diff --git a/applications/main/nfc/plugins/supported_cards/washcity.c b/applications/main/nfc/plugins/supported_cards/washcity.c
index e4610fb94e..7b72c5fc9d 100644
--- a/applications/main/nfc/plugins/supported_cards/washcity.c
+++ b/applications/main/nfc/plugins/supported_cards/washcity.c
@@ -55,20 +55,20 @@ static bool washcity_verify(Nfc* nfc) {
bool verified = false;
do {
- const uint8_t ticket_sector_number = 0;
- const uint8_t ticket_block_number =
- mf_classic_get_first_block_num_of_sector(ticket_sector_number) + 1;
- FURI_LOG_D(TAG, "Verifying sector %u", ticket_sector_number);
+ const uint8_t verify_sector_number = 1;
+ const uint8_t verify_block_number =
+ mf_classic_get_first_block_num_of_sector(verify_sector_number);
+ FURI_LOG_D(TAG, "Verifying sector %u", verify_sector_number);
MfClassicKey key = {0};
bit_lib_num_to_bytes_be(
- washcity_1k_keys[ticket_sector_number].a, COUNT_OF(key.data), key.data);
+ washcity_1k_keys[verify_sector_number].a, COUNT_OF(key.data), key.data);
MfClassicAuthContext auth_context;
MfClassicError error = mf_classic_poller_sync_auth(
- nfc, ticket_block_number, &key, MfClassicKeyTypeA, &auth_context);
+ nfc, verify_block_number, &key, MfClassicKeyTypeA, &auth_context);
if(error != MfClassicErrorNone) {
- FURI_LOG_D(TAG, "Failed to read block %u: %d", ticket_block_number, error);
+ FURI_LOG_D(TAG, "Failed to read block %u: %d", verify_block_number, error);
break;
}
diff --git a/applications/main/nfc/resources/nfc/assets/mf_classic_dict.nfc b/applications/main/nfc/resources/nfc/assets/mf_classic_dict.nfc
index fe49f180c1..8763d7461c 100644
--- a/applications/main/nfc/resources/nfc/assets/mf_classic_dict.nfc
+++ b/applications/main/nfc/resources/nfc/assets/mf_classic_dict.nfc
@@ -4389,4 +4389,4 @@ EB9D9C1B03F6
5A4920FD6F87
544954CBB2C4
4752533E1965
-17C06D19E92F
\ No newline at end of file
+17C06D19E92F
diff --git a/applications/services/dolphin/helpers/dolphin_state.c b/applications/services/dolphin/helpers/dolphin_state.c
index 7dba831296..5010263b79 100644
--- a/applications/services/dolphin/helpers/dolphin_state.c
+++ b/applications/services/dolphin/helpers/dolphin_state.c
@@ -15,7 +15,7 @@
const uint32_t DOLPHIN_LEVELS[] = {100, 200, 300, 450, 600, 750, 950, 1150, 1350, 1600,
1850, 2100, 2400, 2700, 3000, 3350, 3700, 4050, 4450, 4850,
- 5250, 5700, 6150, 6600, 7100, 7600, 8100, 8650, 9200};
+ 5250, 5700, 6150, 6600, 7100, 7600, 8100, 8650, 9999};
const size_t DOLPHIN_LEVEL_COUNT = COUNT_OF(DOLPHIN_LEVELS);
DolphinState* dolphin_state_alloc() {
diff --git a/applications/settings/dolphin_passport/passport.c b/applications/settings/dolphin_passport/passport.c
index abcc12fa5c..09a884d62f 100644
--- a/applications/settings/dolphin_passport/passport.c
+++ b/applications/settings/dolphin_passport/passport.c
@@ -33,7 +33,7 @@ static void render_callback(Canvas* canvas, void* _ctx) {
PassportContext* ctx = _ctx;
DolphinStats* stats = ctx->stats;
- char level_str[20];
+ char level_str[12];
char xp_str[12];
const char* mood_str = NULL;
const Icon* portrait = NULL;
@@ -48,54 +48,52 @@ static void render_callback(Canvas* canvas, void* _ctx) {
portrait = &I_passport_bad_46x49;
mood_str = "Mood: Angry";
}
+
uint32_t xp_progress = 0;
- uint32_t xp_need = dolphin_state_xp_to_levelup(stats->icounter);
+ uint32_t xp_to_levelup = dolphin_state_xp_to_levelup(stats->icounter);
uint32_t xp_above_last_levelup = dolphin_state_xp_above_last_levelup(stats->icounter);
- uint32_t xp_levelup = 0;
+
+ uint32_t xp_have = 0;
+ uint32_t xp_target = 0;
if(ctx->progress_total) {
- xp_levelup = xp_need + stats->icounter;
+ xp_have = stats->icounter;
+ xp_target = DOLPHIN_LEVELS[DOLPHIN_LEVEL_COUNT - 1];
} else {
- xp_levelup = xp_need + xp_above_last_levelup;
+ xp_have = xp_above_last_levelup;
+ xp_target = xp_to_levelup + xp_above_last_levelup;
}
- uint32_t xp_have = xp_levelup - xp_need;
if(stats->level == DOLPHIN_LEVEL_COUNT + 1) {
xp_progress = 0;
} else {
- xp_progress = xp_need * 64 / xp_levelup;
+ xp_progress = (xp_target - xp_have) * 64 / xp_target;
}
// multipass
- canvas_draw_icon(canvas, 0, 0, &I_passport_DB);
+ canvas_draw_icon(canvas, 0, 0, &I_passport_128x64);
// portrait
furi_assert((stats->level > 0) && (stats->level <= DOLPHIN_LEVEL_COUNT + 1));
canvas_draw_icon(canvas, 11, 2, portrait);
const char* my_name = furi_hal_version_get_name_ptr();
- snprintf(level_str, 12, "Level: %hu", stats->level);
+ snprintf(level_str, sizeof(level_str), "Level: %hu", stats->level);
+ canvas_draw_str(canvas, 59, 10, my_name ? my_name : "Unknown");
+ canvas_draw_str(canvas, 59, 22, mood_str);
+ canvas_draw_str(canvas, 59, 34, level_str);
+
if(stats->level == DOLPHIN_LEVEL_COUNT + 1) {
- snprintf(xp_str, 12, "Max Level!");
+ snprintf(xp_str, sizeof(xp_str), "Max Level!");
} else {
- snprintf(xp_str, 12, "%lu/%lu", xp_have, xp_levelup);
+ snprintf(xp_str, sizeof(xp_str), "%lu/%lu", xp_have, xp_target);
}
- canvas_set_font(canvas, FontSecondary);
- canvas_draw_str(canvas, 58, 10, my_name ? my_name : "Unknown");
- canvas_draw_str(canvas, 58, 22, mood_str);
- canvas_set_color(canvas, ColorBlack);
-
- canvas_draw_str(canvas, 58, 34, level_str);
canvas_set_font(canvas, FontBatteryPercent);
- canvas_draw_str(canvas, 58, 42, xp_str);
+ canvas_draw_str(canvas, 59, 42, xp_str);
canvas_set_font(canvas, FontSecondary);
canvas_set_color(canvas, ColorWhite);
canvas_draw_box(canvas, 123 - xp_progress, 45, xp_progress + (xp_progress > 0), 5);
canvas_set_color(canvas, ColorBlack);
-
- canvas_draw_icon(canvas, 52, 51, &I_Ok_btn_9x9);
- canvas_draw_str(
- canvas, ctx->progress_total ? 37 : 36, 59, ctx->progress_total ? "Lvl" : "Tot");
}
int32_t passport_app(void* p) {
diff --git a/applications/settings/power_settings_app/views/battery_info.c b/applications/settings/power_settings_app/views/battery_info.c
index 209a6ddc4b..ea7f932507 100644
--- a/applications/settings/power_settings_app/views/battery_info.c
+++ b/applications/settings/power_settings_app/views/battery_info.c
@@ -83,16 +83,16 @@ static void draw_battery(Canvas* canvas, BatteryInfoModel* data, int x, int y) {
}
if(data->alt) {
- if(!strcmp(value, "")) {
+ if(!value[0]) {
canvas_draw_str_aligned(canvas, x + 92, y + 14, AlignCenter, AlignCenter, header);
- } else if(!strcmp(header, "")) {
+ } else if(!header[0]) {
canvas_draw_str_aligned(canvas, x + 92, y + 14, AlignCenter, AlignCenter, value);
} else {
canvas_draw_str_aligned(canvas, x + 92, y + 9, AlignCenter, AlignCenter, header);
canvas_draw_str_aligned(canvas, x + 92, y + 19, AlignCenter, AlignCenter, value);
}
} else {
- if(!strcmp(emote, "")) {
+ if(!emote[0] && header[0] && value[0]) {
canvas_draw_str_aligned(canvas, x + 92, y + 9, AlignCenter, AlignCenter, header);
canvas_draw_str_aligned(canvas, x + 92, y + 21, AlignCenter, AlignCenter, value);
} else {
diff --git a/applications/system/mass_storage/scenes/mass_storage_scene_create_image.c b/applications/system/mass_storage/scenes/mass_storage_scene_create_image.c
index 4a732374b0..c1b6ca46af 100644
--- a/applications/system/mass_storage/scenes/mass_storage_scene_create_image.c
+++ b/applications/system/mass_storage/scenes/mass_storage_scene_create_image.c
@@ -147,6 +147,7 @@ bool mass_storage_scene_create_image_on_event(void* context, SceneManagerEvent e
}
if(storage_virtual_format(app->fs_api) == FSE_OK) {
success = true;
+ error = NULL;
}
storage_virtual_quit(app->fs_api);
} while(false);
diff --git a/assets/icons/Passport/passport_128x64.png b/assets/icons/Passport/passport_128x64.png
new file mode 100644
index 0000000000..10287aa68e
Binary files /dev/null and b/assets/icons/Passport/passport_128x64.png differ
diff --git a/assets/icons/Passport/passport_DB.png b/assets/icons/Passport/passport_DB.png
deleted file mode 100644
index 69b2ac9ad4..0000000000
Binary files a/assets/icons/Passport/passport_DB.png and /dev/null differ
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_0.png b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_0.png
new file mode 100644
index 0000000000..055618575b
Binary files /dev/null and b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_0.png differ
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_1.png b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_1.png
new file mode 100644
index 0000000000..8a9791cae1
Binary files /dev/null and b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_1.png differ
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_10.png b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_10.png
new file mode 100644
index 0000000000..b266dc7632
Binary files /dev/null and b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_10.png differ
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_11.png b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_11.png
new file mode 100644
index 0000000000..00f572b44a
Binary files /dev/null and b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_11.png differ
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_12.png b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_12.png
new file mode 100644
index 0000000000..6f14b771ee
Binary files /dev/null and b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_12.png differ
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_13.png b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_13.png
new file mode 100644
index 0000000000..11773d46e8
Binary files /dev/null and b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_13.png differ
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_14.png b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_14.png
new file mode 100644
index 0000000000..6673479401
Binary files /dev/null and b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_14.png differ
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_15.png b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_15.png
new file mode 100644
index 0000000000..27e85a5f28
Binary files /dev/null and b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_15.png differ
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_16.png b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_16.png
new file mode 100644
index 0000000000..3513072a05
Binary files /dev/null and b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_16.png differ
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_17.png b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_17.png
new file mode 100644
index 0000000000..ecb5d775ae
Binary files /dev/null and b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_17.png differ
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_18.png b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_18.png
new file mode 100644
index 0000000000..f6f4d1ec88
Binary files /dev/null and b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_18.png differ
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_19.png b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_19.png
new file mode 100644
index 0000000000..b409797300
Binary files /dev/null and b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_19.png differ
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_2.png b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_2.png
new file mode 100644
index 0000000000..befa38a399
Binary files /dev/null and b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_2.png differ
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_20.png b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_20.png
new file mode 100644
index 0000000000..c90df5a9cb
Binary files /dev/null and b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_20.png differ
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_21.png b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_21.png
new file mode 100644
index 0000000000..b933c04c37
Binary files /dev/null and b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_21.png differ
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_22.png b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_22.png
new file mode 100644
index 0000000000..5668abc9af
Binary files /dev/null and b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_22.png differ
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_23.png b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_23.png
new file mode 100644
index 0000000000..bee87ec12f
Binary files /dev/null and b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_23.png differ
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_24.png b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_24.png
new file mode 100644
index 0000000000..c3dc6ef02a
Binary files /dev/null and b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_24.png differ
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_25.png b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_25.png
new file mode 100644
index 0000000000..9eaa9277aa
Binary files /dev/null and b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_25.png differ
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_26.png b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_26.png
new file mode 100644
index 0000000000..508170e76b
Binary files /dev/null and b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_26.png differ
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_27.png b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_27.png
new file mode 100644
index 0000000000..0c8eff3133
Binary files /dev/null and b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_27.png differ
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_28.png b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_28.png
new file mode 100644
index 0000000000..0528366e00
Binary files /dev/null and b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_28.png differ
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_29.png b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_29.png
new file mode 100644
index 0000000000..df704a449c
Binary files /dev/null and b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_29.png differ
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_3.png b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_3.png
new file mode 100644
index 0000000000..cf3162f5f1
Binary files /dev/null and b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_3.png differ
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_30.png b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_30.png
new file mode 100644
index 0000000000..d46c63645f
Binary files /dev/null and b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_30.png differ
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_31.png b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_31.png
new file mode 100644
index 0000000000..37b0281d40
Binary files /dev/null and b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_31.png differ
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_32.png b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_32.png
new file mode 100644
index 0000000000..d31ad12bca
Binary files /dev/null and b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_32.png differ
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_33.png b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_33.png
new file mode 100644
index 0000000000..10b57138cd
Binary files /dev/null and b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_33.png differ
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_34.png b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_34.png
new file mode 100644
index 0000000000..7cb42dd654
Binary files /dev/null and b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_34.png differ
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_35.png b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_35.png
new file mode 100644
index 0000000000..38cb63465b
Binary files /dev/null and b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_35.png differ
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_36.png b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_36.png
new file mode 100644
index 0000000000..0a000d7402
Binary files /dev/null and b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_36.png differ
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_37.png b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_37.png
new file mode 100644
index 0000000000..c3589d31fa
Binary files /dev/null and b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_37.png differ
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_38.png b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_38.png
new file mode 100644
index 0000000000..964e25aa5c
Binary files /dev/null and b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_38.png differ
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_39.png b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_39.png
new file mode 100644
index 0000000000..4decc6b976
Binary files /dev/null and b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_39.png differ
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_4.png b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_4.png
new file mode 100644
index 0000000000..27804f2506
Binary files /dev/null and b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_4.png differ
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_40.png b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_40.png
new file mode 100644
index 0000000000..1a8d8b1d96
Binary files /dev/null and b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_40.png differ
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_41.png b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_41.png
new file mode 100644
index 0000000000..29a35c1b9c
Binary files /dev/null and b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_41.png differ
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_42.png b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_42.png
new file mode 100644
index 0000000000..0893653615
Binary files /dev/null and b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_42.png differ
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_5.png b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_5.png
new file mode 100644
index 0000000000..7799b20aa2
Binary files /dev/null and b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_5.png differ
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_6.png b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_6.png
new file mode 100644
index 0000000000..7cc9292363
Binary files /dev/null and b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_6.png differ
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_7.png b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_7.png
new file mode 100644
index 0000000000..29f8df8f90
Binary files /dev/null and b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_7.png differ
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_8.png b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_8.png
new file mode 100644
index 0000000000..926ff09115
Binary files /dev/null and b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_8.png differ
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_9.png b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_9.png
new file mode 100644
index 0000000000..4f992440f2
Binary files /dev/null and b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/frame_9.png differ
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/meta.txt b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/meta.txt
new file mode 100644
index 0000000000..5b0d0a4664
--- /dev/null
+++ b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum1_128x64/meta.txt
@@ -0,0 +1,14 @@
+Filetype: Flipper Animation
+Version: 1
+
+Width: 128
+Height: 64
+Passive frames: 16
+Active frames: 83
+Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 0 1 2 3 4 5 6 7 8 8 8 8 8 8 16 17 18 19 20 21 22 23 24 25 25 26 27 28 29 20 30 31 32 33 8 8 16 17 18 19 20 21 22 23 24 25 25 26 27 28 29 20 30 31 32 33 8 8 16 17 18 19 20 21 22 23 24 25 25 25 25 25 25 34 35 36 37 38 39 40 41 42 42
+Active cycles: 1
+Frame rate: 6
+Duration: 3600
+Active cooldown: 3
+
+Bubble slots: 0
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_0.png b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_0.png
new file mode 100644
index 0000000000..055618575b
Binary files /dev/null and b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_0.png differ
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_1.png b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_1.png
new file mode 100644
index 0000000000..8a9791cae1
Binary files /dev/null and b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_1.png differ
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_10.png b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_10.png
new file mode 100644
index 0000000000..b266dc7632
Binary files /dev/null and b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_10.png differ
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_11.png b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_11.png
new file mode 100644
index 0000000000..00f572b44a
Binary files /dev/null and b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_11.png differ
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_12.png b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_12.png
new file mode 100644
index 0000000000..6f14b771ee
Binary files /dev/null and b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_12.png differ
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_13.png b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_13.png
new file mode 100644
index 0000000000..11773d46e8
Binary files /dev/null and b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_13.png differ
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_14.png b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_14.png
new file mode 100644
index 0000000000..6673479401
Binary files /dev/null and b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_14.png differ
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_15.png b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_15.png
new file mode 100644
index 0000000000..27e85a5f28
Binary files /dev/null and b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_15.png differ
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_16.png b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_16.png
new file mode 100644
index 0000000000..609da5c6e8
Binary files /dev/null and b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_16.png differ
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_17.png b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_17.png
new file mode 100644
index 0000000000..389e94baa4
Binary files /dev/null and b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_17.png differ
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_18.png b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_18.png
new file mode 100644
index 0000000000..1c67537cf5
Binary files /dev/null and b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_18.png differ
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_19.png b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_19.png
new file mode 100644
index 0000000000..f2f4d2aa75
Binary files /dev/null and b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_19.png differ
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_2.png b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_2.png
new file mode 100644
index 0000000000..befa38a399
Binary files /dev/null and b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_2.png differ
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_20.png b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_20.png
new file mode 100644
index 0000000000..9ef9790f7e
Binary files /dev/null and b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_20.png differ
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_21.png b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_21.png
new file mode 100644
index 0000000000..abc1312431
Binary files /dev/null and b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_21.png differ
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_22.png b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_22.png
new file mode 100644
index 0000000000..baac41dc79
Binary files /dev/null and b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_22.png differ
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_23.png b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_23.png
new file mode 100644
index 0000000000..524c78534d
Binary files /dev/null and b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_23.png differ
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_24.png b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_24.png
new file mode 100644
index 0000000000..cd2ce13b08
Binary files /dev/null and b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_24.png differ
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_25.png b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_25.png
new file mode 100644
index 0000000000..c91f67b9b7
Binary files /dev/null and b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_25.png differ
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_26.png b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_26.png
new file mode 100644
index 0000000000..e952f09fea
Binary files /dev/null and b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_26.png differ
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_27.png b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_27.png
new file mode 100644
index 0000000000..ac093214b7
Binary files /dev/null and b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_27.png differ
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_28.png b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_28.png
new file mode 100644
index 0000000000..d3cd31e51c
Binary files /dev/null and b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_28.png differ
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_29.png b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_29.png
new file mode 100644
index 0000000000..f2e2bc453b
Binary files /dev/null and b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_29.png differ
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_3.png b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_3.png
new file mode 100644
index 0000000000..cf3162f5f1
Binary files /dev/null and b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_3.png differ
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_30.png b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_30.png
new file mode 100644
index 0000000000..aa4e51bbcc
Binary files /dev/null and b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_30.png differ
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_31.png b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_31.png
new file mode 100644
index 0000000000..75229f9c48
Binary files /dev/null and b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_31.png differ
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_32.png b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_32.png
new file mode 100644
index 0000000000..c7b3348b71
Binary files /dev/null and b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_32.png differ
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_33.png b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_33.png
new file mode 100644
index 0000000000..7d8bc6cf85
Binary files /dev/null and b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_33.png differ
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_34.png b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_34.png
new file mode 100644
index 0000000000..2dd3352d85
Binary files /dev/null and b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_34.png differ
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_4.png b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_4.png
new file mode 100644
index 0000000000..27804f2506
Binary files /dev/null and b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_4.png differ
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_5.png b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_5.png
new file mode 100644
index 0000000000..7799b20aa2
Binary files /dev/null and b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_5.png differ
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_6.png b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_6.png
new file mode 100644
index 0000000000..7cc9292363
Binary files /dev/null and b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_6.png differ
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_7.png b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_7.png
new file mode 100644
index 0000000000..29f8df8f90
Binary files /dev/null and b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_7.png differ
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_8.png b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_8.png
new file mode 100644
index 0000000000..926ff09115
Binary files /dev/null and b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_8.png differ
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_9.png b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_9.png
new file mode 100644
index 0000000000..4f992440f2
Binary files /dev/null and b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/frame_9.png differ
diff --git a/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/meta.txt b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/meta.txt
new file mode 100644
index 0000000000..165bf758ed
--- /dev/null
+++ b/assets/packs/Momentum/Anims/Kuronons_CFW_Momentum2_128x64/meta.txt
@@ -0,0 +1,15 @@
+Filetype: Flipper Animation
+Version: 1
+
+Width: 128
+Height: 64
+Passive frames: 32
+Active frames: 132
+Frames order: 0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 10 10 11 11 12 12 13 13 14 14 15 15 0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 8 8 8 8 16 16 16 17 17 17 18 18 18 19 19 19 20 20 21 21 22 22 8 8 16 17 18 19 20 21 22 8 16 17 18 19 20 21 22 16 18 20 22 8 17 19 21 16 18 20 22 8 17 19 21 24 27 30 25 28 23 26 29 24 27 30 25 28 23 26 29 24 27 30 25 28 23 27 24 28 25 29 26 30 27 23 28 24 29 25 30 26 23 31 32 32 33 34 34 34 34 34 34 34 34 34 34 34 34 34 34 34 34 34
+Active cycles: 1
+Frame rate: 12
+Duration: 3600
+Active cooldown: 3
+
+Bubble slots: 0
+
diff --git a/assets/packs/Momentum/Anims/manifest.txt b/assets/packs/Momentum/Anims/manifest.txt
new file mode 100644
index 0000000000..70b59e0157
--- /dev/null
+++ b/assets/packs/Momentum/Anims/manifest.txt
@@ -0,0 +1,16 @@
+Filetype: Flipper Animation Manifest
+Version: 1
+
+Name: Kuronons_CFW_Momentum1_128x64
+Min butthurt: 0
+Max butthurt: 18
+Min level: 1
+Max level: 30
+Weight: 3
+
+Name: Kuronons_CFW_Momentum2_128x64
+Min butthurt: 0
+Max butthurt: 18
+Min level: 1
+Max level: 30
+Weight: 3
\ No newline at end of file
diff --git a/assets/packs/ReadMe.md b/assets/packs/ReadMe.md
index 7dd7f80fb8..cb4ca1ec43 100644
--- a/assets/packs/ReadMe.md
+++ b/assets/packs/ReadMe.md
@@ -1,6 +1,13 @@
# Pre-included Asset Packs
-Includes a WatchDogs asset pack by default. Credits:
-- [WrenchAtHome](https://github.com/wrenchathome) for some [desktop anims](https://github.com/wrenchathome/flip0anims)
-- [u/Cheroon](https://www.reddit.com/user/Cheroon/) for the [passport portait](https://www.reddit.com/r/watch_dogs/comments/50n046/pixel_art_wrench_mask_gif/)
-- [WillyJL](https://github.com/Willy-JL) for other assets, icons and animations
+- **`Momentum`** - Credits:
+ - [Kuronons](https://github.com/Kuronons) for the [desktop animations](https://github.com/Kuronons/FZ_graphics/tree/main/Animations/Custom_Firmwares)
+ - [Kuronons](https://github.com/Kuronons) for the [passport background](https://github.com/Kuronons/FZ_graphics/tree/main/Passport%20background) (not in asset pack, it's in base firmware)
+
+- **`WatchDogs`** - Credits:
+ - [WrenchAtHome](https://github.com/wrenchathome) for some [desktop animations](https://github.com/wrenchathome/flip0anims)
+ - [u/Cheroon](https://www.reddit.com/user/Cheroon/) for the [passport portait](https://www.reddit.com/r/watch_dogs/comments/50n046/pixel_art_wrench_mask_gif/)
+ - [David Libeau](https://davidlibeau.fr/) for the [primary font (hacked)](http://bit.ly/WatchDogsFont)
+ - [Gissio](https://github.com/Gissio) for the [secondary font (tiny5)](https://github.com/Gissio/font_tiny5)
+ - Ife Mena for the [keyboard font (3x5im)](https://www.pentacom.jp/pentacom/bitfontmaker2/gallery/?id=7785)
+ - [WillyJL](https://github.com/Willy-JL) for other assets, icons and animations (mostly converted from game assets)
diff --git a/documentation/fbt.md b/documentation/fbt.md
index 02de2949fa..0220178a2d 100644
--- a/documentation/fbt.md
+++ b/documentation/fbt.md
@@ -71,13 +71,13 @@ To use language servers other than the default VS Code C/C++ language server, us
- `debug` - build and flash firmware, then attach with gdb with firmware's .elf loaded.
- `debug_other`, `debug_other_blackmagic` - attach GDB without loading any `.elf`. It will allow you to manually add external `.elf` files with `add-symbol-file` in GDB.
- `updater_debug` - attach GDB with the updater's `.elf` loaded.
-- `devboard_flash` - update WiFi dev board with the latest firmware.
+- `devboard_flash` - Update WiFi dev board. Supports `ARGS="..."` to pass extra arguments to the update script, e.g. `ARGS="-c dev"`.
- `blackmagic` - debug firmware with Blackmagic probe (WiFi dev board).
-- `openocd` - just start OpenOCD.
+- `openocd` - just start OpenOCD. You can pass extra arguments with `ARGS="..."`.
- `get_blackmagic` - output the blackmagic address in the GDB remote format. Useful for IDE integration.
- `get_stlink` - output serial numbers for attached STLink probes. Used for specifying an adapter with `SWD_TRANSPORT_SERIAL=...`.
-- `lint`, `format` - run clang-format on the C source code to check and reformat it according to the `.clang-format` specs.
-- `lint_py`, `format_py` - run [black](https://black.readthedocs.io/en/stable/index.html) on the Python source code, build system files & application manifests.
+- `lint`, `format` - run clang-format on the C source code to check and reformat it according to the `.clang-format` specs. Supports `ARGS="..."` to pass extra arguments to clang-format.
+- `lint_py`, `format_py` - run [black](https://black.readthedocs.io/en/stable/index.html) on the Python source code, build system files & application manifests. Supports `ARGS="..."` to pass extra arguments to black.
- `firmware_pvs` - generate a PVS Studio report for the firmware. Requires PVS Studio to be available on your system's `PATH`.
- `cli` - start a Flipper CLI session over USB.
diff --git a/documentation/file_formats/AssetPacks.md b/documentation/file_formats/AssetPacks.md
index 602d9c50ff..1a319c7a6f 100644
--- a/documentation/file_formats/AssetPacks.md
+++ b/documentation/file_formats/AssetPacks.md
@@ -1,6 +1,6 @@
## Intro
-Asset Packs are an exclusive feature of Xtreme Firmware that allows you to load custom Animation and Icon sets without recompiling the firmware or messing with manifest.txt files (as a user). Here you can find info on how to install Asset Packs and also how to make your own.
+Asset Packs are an exclusive feature of Momentum Firmware that allows you to load custom Animation and Icon sets without recompiling the firmware or messing with manifest.txt files (as a user). Here you can find info on how to install Asset Packs and also how to make your own.
@@ -14,7 +14,7 @@ Installing Asset Packs is quite easy and straightforward. First, make sure you'r
If you did this correctly, you should see `SD/asset_packs/PackName/Anims` and/or `SD/asset_packs/PackName/Icons`.
-- Now simply open the Xtreme Settings app (from the home screen press `Arrow UP` and then `Xtreme Settings`) and select the asset pack you want. When you back out, Flipper will restart and your animations and icons will use the ones from the selected pack!
+- Now simply open the Momentum Settings app (from the home screen press `Arrow UP` and then `Momentum Settings`) and select the asset pack you want. When you back out, Flipper will restart and your animations and icons will use the ones from the selected pack!
@@ -66,7 +66,7 @@ With icons there are quite a few differences and issues we had to solve. In part
#### Static icons
-The `.bm` format does not include image width and height, with animations that is stored in `meta.txt`, so for static icons we made a special format: `.bmx`, which is `[ int32 width ] + [ int32 height ] + [ standard .bm pixel data ]`, but this is handled by the packer (see below) so don't worry abou it.
+The `.bm` format does not include image width and height, with animations that is stored in `meta.txt`, so for static icons we made a special format: `.bmx`, which is `[ int32 width ] + [ int32 height ] + [ standard .bm pixel data ]`, but this is handled by the packer (see below) so don't worry about it.
#### Animated icons
@@ -93,7 +93,7 @@ SD/
|...
|-Passport/
|-passport_happy_46x49.bmx
- |-passport_DB.bmx
+ |-passport_128x64.bmx
|...
|-RFID/
|-RFIDDolphinReceive_97x61.bmx
@@ -107,7 +107,6 @@ Which is the same you can find in the firmware source code, in `assets/icons`. T
- The pixel numbers in the filename are ignored, they are there purely because of the original Flipper icon names and for a hint as to how you should size your icons, but they are not enforced.
- We kept the original naming scheme and file structure for compatibility, but the original setup is quite bad, so bear with us. Some icons in subfolders (like `SubGhz/Scanning_123x52`) are used in other unrelated apps/places.
- Some icons in the official firmware have different versions with different numbers to indicate the flipper level they target. Since our system has so many levels, we decided to keep it simple and remove the level progression from icons. For example `Passport/passport_happy1_46x49` becomes `Passport/passport_happy_46x49` and `Animations/Levelup1_128x64` becomes `Animations/Levelup_128x64`.
-- The `Passport/passport_DB` is the background for the passport page, it doesn't mention a pixel size because it should be the same as the Flipper screen size (128x64).
This system supports **all** internal assets!
@@ -150,7 +149,7 @@ All the .bm and .bmx struggles are dealt with by the packer system, which is in
- Now upload the packed packs from that folder onto your flipper in `SD/asset_packs`.
-- Done! Just select it from the Xtreme Settings app now. And if you're generous share your (packed) asset pack in #asset-packs on discord.
+- Done! Just select it from the Momentum Settings app now. And if you're generous share your (packed) asset pack in #asset-packs on discord.
#### Building with Firmware
diff --git a/lib/drivers/rgb_backlight.c b/lib/drivers/rgb_backlight.c
index 097fa30de3..959c759228 100644
--- a/lib/drivers/rgb_backlight.c
+++ b/lib/drivers/rgb_backlight.c
@@ -203,9 +203,51 @@ uint8_t rgb_backlight_get_rainbow_saturation() {
return rainbow_saturation;
}
-static void rainbow_timer(void* ctx) {
+void rainbow_timer(void* ctx) {
+ if(!rgb_state.settings_loaded) return;
+ furi_check(furi_mutex_acquire(rgb_state.mutex, FuriWaitForever) == FuriStatusOk);
+
+ if(!rgb_state.enabled || rgb_settings.rainbow_mode == RGBBacklightRainbowModeOff ||
+ !rgb_state.last_brightness) {
+ furi_check(furi_mutex_release(rgb_state.mutex) == FuriStatusOk);
+ return;
+ }
+
+ rgb_state.rainbow_hsv.h += rgb_settings.rainbow_speed;
+
+ RgbColor rgb;
+ hsv2rgb(&rgb_state.rainbow_hsv, &rgb);
+
+ switch(rgb_settings.rainbow_mode) {
+ case RGBBacklightRainbowModeWave: {
+ HsvColor hsv = rgb_state.rainbow_hsv;
+ for(uint8_t i = 0; i < SK6805_get_led_count(); i++) {
+ if(i) {
+ hsv.h += (50 * i);
+ hsv2rgb(&hsv, &rgb);
+ }
+ SK6805_set_led_color(i, rgb.r, rgb.g, rgb.b);
+ }
+ break;
+ }
+
+ case RGBBacklightRainbowModeSolid:
+ for(uint8_t i = 0; i < SK6805_get_led_count(); i++) {
+ SK6805_set_led_color(i, rgb.r, rgb.g, rgb.b);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ SK6805_update();
+
+ if(rgb_state.enabled && rgb_settings.rainbow_mode == RGBBacklightRainbowModeOff)
+ SK6805_update();
+
+ furi_check(furi_mutex_release(rgb_state.mutex) == FuriStatusOk);
UNUSED(ctx);
- rgb_backlight_update(rgb_state.last_brightness, true);
}
void rgb_backlight_reconfigure(bool enabled) {
@@ -226,19 +268,16 @@ void rgb_backlight_reconfigure(bool enabled) {
rgb_state.rainbow_timer = NULL;
}
rgb_state.rainbow_hsv.s = rgb_settings.rainbow_saturation;
- rgb_backlight_update(rgb_state.last_brightness, false);
-
- if(rgb_state.enabled && rgb_settings.rainbow_mode == RGBBacklightRainbowModeOff)
- SK6805_update();
+ rgb_backlight_update(rgb_state.last_brightness, true);
furi_check(furi_mutex_release(rgb_state.mutex) == FuriStatusOk);
}
-void rgb_backlight_update(uint8_t brightness, bool tick) {
+void rgb_backlight_update(uint8_t brightness, bool forced) {
if(!rgb_state.settings_loaded) return;
furi_check(furi_mutex_acquire(rgb_state.mutex, FuriWaitForever) == FuriStatusOk);
- if(!rgb_state.enabled) {
+ if(!rgb_state.enabled || (brightness == rgb_state.last_brightness && !forced)) {
furi_check(furi_mutex_release(rgb_state.mutex) == FuriStatusOk);
return;
}
@@ -258,29 +297,14 @@ void rgb_backlight_update(uint8_t brightness, bool tick) {
case RGBBacklightRainbowModeWave:
case RGBBacklightRainbowModeSolid: {
- if(tick && brightness) {
- rgb_state.rainbow_hsv.h += rgb_settings.rainbow_speed;
- } else {
- rgb_state.rainbow_hsv.v = brightness;
- }
-
- RgbColor rgb;
- hsv2rgb(&rgb_state.rainbow_hsv, &rgb);
-
- if(rgb_settings.rainbow_mode == RGBBacklightRainbowModeWave) {
- HsvColor hsv = rgb_state.rainbow_hsv;
- for(uint8_t i = 0; i < SK6805_get_led_count(); i++) {
- if(i) {
- hsv.h += (50 * i);
- hsv2rgb(&hsv, &rgb);
- }
- SK6805_set_led_color(i, rgb.r, rgb.g, rgb.b);
- }
- } else {
+ if(!brightness) {
+ furi_timer_stop(rgb_state.rainbow_timer);
for(uint8_t i = 0; i < SK6805_get_led_count(); i++) {
- SK6805_set_led_color(i, rgb.r, rgb.g, rgb.b);
+ SK6805_set_led_color(i, 0, 0, 0);
}
- }
+ } else if(!rgb_state.last_brightness)
+ furi_timer_start(rgb_state.rainbow_timer, rgb_settings.rainbow_interval);
+ rgb_state.rainbow_hsv.v = brightness;
break;
}
diff --git a/lib/drivers/rgb_backlight.h b/lib/drivers/rgb_backlight.h
index a30b5e2688..e82c25c466 100644
--- a/lib/drivers/rgb_backlight.h
+++ b/lib/drivers/rgb_backlight.h
@@ -101,9 +101,9 @@ void rgb_backlight_reconfigure(bool enabled);
* @brief Apply current RGB lighting settings
*
* @param brightness Backlight intensity (0-255)
- * @param tick Whether this update was a tick (for rainbow)
+ * @param forced force a update even brightness doesnt changed
*/
-void rgb_backlight_update(uint8_t brightness, bool tick);
+void rgb_backlight_update(uint8_t brightness, bool forced);
#ifdef __cplusplus
}
diff --git a/lib/momentum/momentum.h b/lib/momentum/momentum.h
index de5c21ae0a..1bbbde2d40 100644
--- a/lib/momentum/momentum.h
+++ b/lib/momentum/momentum.h
@@ -83,7 +83,6 @@ typedef struct {
SpiHandle spi_nrf24_handle;
FuriHalSerialId uart_esp_channel;
FuriHalSerialId uart_nmea_channel;
- FuriHalSerialId uart_general_channel;
bool file_naming_prefix_after;
} MomentumSettings;
diff --git a/lib/momentum/settings.c b/lib/momentum/settings.c
index 375bafc5df..f5b1d3de63 100644
--- a/lib/momentum/settings.c
+++ b/lib/momentum/settings.c
@@ -40,7 +40,6 @@ MomentumSettings momentum_settings = {
.spi_nrf24_handle = SpiDefault, // &furi_hal_spi_bus_handle_external
.uart_esp_channel = FuriHalSerialIdUsart, // pin 13,14
.uart_nmea_channel = FuriHalSerialIdUsart, // pin 13,14
- .uart_general_channel = FuriHalSerialIdUsart, // pin 13,14
.file_naming_prefix_after = false, // Before
};
@@ -110,7 +109,6 @@ static const struct {
{setting_enum(spi_nrf24_handle, SpiCount)},
{setting_enum(uart_esp_channel, FuriHalSerialIdMax)},
{setting_enum(uart_nmea_channel, FuriHalSerialIdMax)},
- {setting_enum(uart_general_channel, FuriHalSerialIdMax)},
{setting_bool(file_naming_prefix_after)},
};
diff --git a/scripts/fbt_tools/fbt_dist.py b/scripts/fbt_tools/fbt_dist.py
index 160f5fa035..1f00bc93a8 100644
--- a/scripts/fbt_tools/fbt_dist.py
+++ b/scripts/fbt_tools/fbt_dist.py
@@ -150,6 +150,7 @@ def generate(env):
"--interface=${SWD_TRANSPORT}",
"--serial=${SWD_TRANSPORT_SERIAL}",
"${SOURCE}",
+ "${ARGS}",
],
Touch("${TARGET}"),
]
@@ -162,6 +163,7 @@ def generate(env):
"-p",
"${FLIP_PORT}",
"${UPDATE_BUNDLE_DIR}/update.fuf",
+ "${ARGS}",
],
Touch("${TARGET}"),
]
@@ -180,6 +182,7 @@ def generate(env):
"--stack_type=${COPRO_STACK_TYPE}",
"--stack_file=${COPRO_STACK_BIN}",
"--stack_addr=${COPRO_STACK_ADDR}",
+ "${ARGS}",
]
],
"${COPROCOMSTR}",
diff --git a/scripts/ufbt/SConstruct b/scripts/ufbt/SConstruct
index 45432a3971..7280c657ef 100644
--- a/scripts/ufbt/SConstruct
+++ b/scripts/ufbt/SConstruct
@@ -316,26 +316,56 @@ else:
appenv.PhonyTarget(
"cli",
- [["${PYTHON3}", "${FBT_SCRIPT_DIR}/serial_cli.py", "-p", "${FLIP_PORT}"]],
+ [
+ [
+ "${PYTHON3}",
+ "${FBT_SCRIPT_DIR}/serial_cli.py",
+ "-p",
+ "${FLIP_PORT}",
+ "${ARGS}",
+ ]
+ ],
)
# Update WiFi devboard firmware
dist_env.PhonyTarget(
- "devboard_flash", [["${PYTHON3}", "${FBT_SCRIPT_DIR}/wifi_board.py"]]
+ "devboard_flash",
+ [
+ [
+ "${PYTHON3}",
+ "${FBT_SCRIPT_DIR}/wifi_board.py",
+ "${ARGS}",
+ ]
+ ],
)
# Linter
-
dist_env.PhonyTarget(
"lint",
- [["${PYTHON3}", "${FBT_SCRIPT_DIR}/lint.py", "check", "${LINT_SOURCES}"]],
+ [
+ [
+ "${PYTHON3}",
+ "${FBT_SCRIPT_DIR}/lint.py",
+ "check",
+ "${LINT_SOURCES}",
+ "${ARGS}",
+ ]
+ ],
source=original_app_dir.File(".clang-format"),
LINT_SOURCES=[original_app_dir],
)
dist_env.PhonyTarget(
"format",
- [["${PYTHON3}", "${FBT_SCRIPT_DIR}/lint.py", "format", "${LINT_SOURCES}"]],
+ [
+ [
+ "${PYTHON3}",
+ "${FBT_SCRIPT_DIR}/lint.py",
+ "format",
+ "${LINT_SOURCES}",
+ "${ARGS}",
+ ]
+ ],
source=original_app_dir.File(".clang-format"),
LINT_SOURCES=[original_app_dir],
)
@@ -457,6 +487,7 @@ if dolphin_src_dir.exists():
"send",
"${SOURCE}",
"/ext/dolphin",
+ "${ARGS}",
]
],
source=ufbt_build_dir.Dir("dolphin"),
diff --git a/scripts/ufbt/site_tools/ufbt_help.py b/scripts/ufbt/site_tools/ufbt_help.py
index 4873b385c6..632fd98b2d 100644
--- a/scripts/ufbt/site_tools/ufbt_help.py
+++ b/scripts/ufbt/site_tools/ufbt_help.py
@@ -29,7 +29,8 @@
debug, debug_other, blackmagic:
Start GDB
devboard_flash:
- Update WiFi dev board with the latest firmware
+ Update WiFi dev board.
+ Supports ARGS="..." to pass extra arguments to the update script, e.g. ARGS="-c dev"
Other:
cli:
diff --git a/scripts/wifi_board.py b/scripts/wifi_board.py
index 3f89ebdc65..b01b6225dd 100755
--- a/scripts/wifi_board.py
+++ b/scripts/wifi_board.py
@@ -1,16 +1,16 @@
#!/usr/bin/env python3
-from flipper.app import App
-from serial.tools.list_ports_common import ListPortInfo
-
+import json
import logging
import os
-import tempfile
import subprocess
-import serial.tools.list_ports as list_ports
-import json
-import requests
import tarfile
+import tempfile
+
+import requests
+import serial.tools.list_ports as list_ports
+from flipper.app import App
+from serial.tools.list_ports_common import ListPortInfo
class UpdateDownloader:
@@ -29,15 +29,15 @@ class UpdateDownloader:
def __init__(self):
self.logger = logging.getLogger()
- def download(self, channel_id: str, dir: str) -> bool:
+ def download(self, channel_id: str, target_dir: str) -> bool:
# Aliases
if channel_id in self.CHANNEL_ID_ALIAS:
channel_id = self.CHANNEL_ID_ALIAS[channel_id]
# Make directory
- if not os.path.exists(dir):
- self.logger.info(f"Creating directory {dir}")
- os.makedirs(dir)
+ if not os.path.exists(target_dir):
+ self.logger.info(f"Creating directory {target_dir}")
+ os.makedirs(target_dir)
# Download json index
self.logger.info(f"Downloading {self.UPDATE_INDEX}")
@@ -79,19 +79,14 @@ def download(self, channel_id: str, dir: str) -> bool:
self.logger.info(f"Using version '{version['version']}'")
# Get changelog
- changelog = None
- try:
- changelog = version["changelog"]
- except Exception as e:
- self.logger.error(f"Failed to get changelog: {e}")
-
- # print changelog
- if changelog is not None:
+ if changelog := version.get("changelog"):
self.logger.info(f"Changelog:")
for line in changelog.split("\n"):
if line.strip() == "":
continue
self.logger.info(f" {line}")
+ else:
+ self.logger.warning(f"Changelog not found")
# Find file
file_url = None
@@ -106,7 +101,7 @@ def download(self, channel_id: str, dir: str) -> bool:
# Make file path
file_name = file_url.split("/")[-1]
- file_path = os.path.join(dir, file_name)
+ file_path = os.path.join(target_dir, file_name)
# Download file
self.logger.info(f"Downloading {file_url} to {file_path}")
@@ -117,7 +112,7 @@ def download(self, channel_id: str, dir: str) -> bool:
# Unzip tgz
self.logger.info(f"Unzipping {file_path}")
with tarfile.open(file_path, "r") as tar:
- tar.extractall(dir)
+ tar.extractall(target_dir)
return True
@@ -133,16 +128,24 @@ def init(self):
# logging
self.logger = logging.getLogger()
- def find_wifi_board(self) -> bool:
+ @staticmethod
+ def _grep_ports(regexp: str) -> list[ListPortInfo]:
# idk why, but python thinks that list_ports.grep returns tuple[str, str, str]
- blackmagics: list[ListPortInfo] = list(list_ports.grep("blackmagic")) # type: ignore
- daps: list[ListPortInfo] = list(list_ports.grep("CMSIS-DAP")) # type: ignore
+ return list(list_ports.grep(regexp)) # type: ignore
+
+ def is_wifi_board_connected(self) -> bool:
+ return (
+ len(self._grep_ports("ESP32-S2")) > 0
+ or len(self._grep_ports("CMSIS-DAP")) > 0
+ )
- return len(blackmagics) > 0 or len(daps) > 0
+ @staticmethod
+ def is_windows() -> bool:
+ return os.name == "nt"
- def find_wifi_board_bootloader(self):
- # idk why, but python thinks that list_ports.grep returns tuple[str, str, str]
- ports: list[ListPortInfo] = list(list_ports.grep("ESP32-S2")) # type: ignore
+ @classmethod
+ def find_port(cls, regexp: str) -> str:
+ ports: list[ListPortInfo] = cls._grep_ports(regexp)
if len(ports) == 0:
# Blackmagic probe serial port not found, will be handled later
@@ -151,27 +154,28 @@ def find_wifi_board_bootloader(self):
raise Exception("More than one WiFi board found")
else:
port = ports[0]
- if os.name == "nt":
- port.device = f"\\\\.\\{port.device}"
- return port.device
+ return f"\\\\.\\{port.device}" if cls.is_windows() else port.device
+
+ def find_wifi_board_bootloader_port(self):
+ return self.find_port("ESP32-S2")
+
+ def find_wifi_board_bootloader_port_damn_windows(self):
+ self.logger.info("Trying to find WiFi board using VID:PID")
+ return self.find_port("VID:PID=303A:0002")
def update(self):
try:
- port = self.find_wifi_board_bootloader()
+ port = self.find_wifi_board_bootloader_port()
+
+ # Damn windows fix
+ if port is None and self.is_windows():
+ port = self.find_wifi_board_bootloader_port_damn_windows()
except Exception as e:
self.logger.error(f"{e}")
return 1
- if self.args.port != "auto":
- port = self.args.port
-
- available_ports = [p[0] for p in list(list_ports.comports())]
- if port not in available_ports:
- self.logger.error(f"Port {port} not found")
- return 1
-
if port is None:
- if self.find_wifi_board():
+ if self.is_wifi_board_connected():
self.logger.error("WiFi board found, but not in bootloader mode.")
self.logger.info("Please hold down BOOT button and press RESET button")
else:
@@ -179,6 +183,13 @@ def update(self):
self.logger.info(
"Please connect WiFi board to your computer, hold down BOOT button and press RESET button"
)
+ if not self.is_windows():
+ self.logger.info(
+ "If you are using Linux, you may need to add udev rules to access the device"
+ )
+ self.logger.info(
+ "Check out 41-flipper.rules & README in scripts/debug folder"
+ )
return 1
# get temporary dir
@@ -197,24 +208,29 @@ def update(self):
with open(os.path.join(temp_dir, "flash.command"), "r") as f:
flash_command = f.read()
- flash_command = flash_command.replace("\n", "").replace("\r", "")
- flash_command = flash_command.replace("(PORT)", port)
-
- # We can't reset the board after flashing via usb
- flash_command = flash_command.replace(
- "--after hard_reset", "--after no_reset_stub"
+ replacements = (
+ ("\n", ""),
+ ("\r", ""),
+ ("(PORT)", port),
+ # We can't reset the board after flashing via usb
+ ("--after hard_reset", "--after no_reset_stub"),
)
- args = flash_command.split(" ")[0:]
- args = list(filter(None, args))
+ # hellish toolchain fix
+ if self.is_windows():
+ replacements += (("esptool.py", "python -m esptool"),)
+ else:
+ replacements += (("esptool.py", "python3 -m esptool"),)
+
+ for old, new in replacements:
+ flash_command = flash_command.replace(old, new)
- esptool_params = []
- esptool_params.extend(args)
+ args = list(filter(None, flash_command.split()))
self.logger.info(f'Running command: "{" ".join(args)}" in "{temp_dir}"')
process = subprocess.Popen(
- esptool_params,
+ args,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
cwd=temp_dir,
diff --git a/site_scons/commandline.scons b/site_scons/commandline.scons
index 93c43405ad..4b1c2bc238 100644
--- a/site_scons/commandline.scons
+++ b/site_scons/commandline.scons
@@ -275,6 +275,11 @@ vars.AddVariables(
help="Enable strict import check for .faps",
default=True,
),
+ (
+ "ARGS",
+ "Extra arguments to pass to certain scripts supporting it",
+ "",
+ ),
)
Return("vars")
diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv
index a42949f175..d8f7a99e14 100644
--- a/targets/f7/api_symbols.csv
+++ b/targets/f7/api_symbols.csv
@@ -3908,7 +3908,7 @@ Variable,+,I_next_text_19x6,Icon,
Variable,+,I_off_19x20,Icon,
Variable,+,I_off_hover_19x20,Icon,
Variable,+,I_off_text_12x5,Icon,
-Variable,+,I_passport_DB,Icon,
+Variable,+,I_passport_128x64,Icon,
Variable,+,I_passport_bad_46x49,Icon,
Variable,+,I_passport_happy_46x49,Icon,
Variable,+,I_passport_okay_46x49,Icon,