Stable: Many hours have been spent rewriting core parts of the Flippers firmware as well as some of its apps to ensure stability. A task that was long needed on all Firmware, so we tackled it right away.
-
Customizable: Dont like the Animations, want to turn on/off the Home screen icons (battery, SD card etc), change the flippers name or anything like that? You absolutely can. No need to mess with code or deal with weird manifest files. Its all done with an App.
+
+Note, the below mentioned changes are only a few things we did. For a full list click [here](https://github.com/ClaraCrazy/Flipper-Xtreme/wiki/Customization)
-----
@@ -157,20 +159,37 @@ Note: This repo is always updated with OFW & Unleashed. No need to mention all t
----
Install:
+
-**This is the recommended install procedure. Please follow these steps EXACTLY and CAREFULLY to ensure you install correctly.**
-**This process will NOT delete any saved files and simply ensures the install goes smoothly.**
-
+There are 3 methods to install Xtreme, we recommend you use the **Web Updater**, but choose whichever one you prefer:
-- Download the latest release (.zip) from [The releases tab](https://github.com/ClaraCrazy/Flipper-Xtreme/releases/latest)
-- Extract the archive. This is now your new Firmware folder
-- Open [qFlipper](https://flipperzero.one/update), head to `SD/Update` and simply move the firmware folder there
-- On the Flipper, hit the `Arrow Down` button, this will get you to the file menu. In there simply search for your updates folder
-- Inside that folder, select the Firmware you just moved onto it, and run the file thats simply called `Update`
+
-- Enjoy!
+> Web Updater (Chrome)
Click Connect and select your Flipper from the list
+>
Click Install and wait for the update to complete
+>
-**If you have issues or crashes with that process, you can try to use `Settings > Storage > Factory Reset` then retry the install.**
+> qFlipper Package (.tgz)
Extract the archive. This is now your new Firmware folder
+>
Open qFlipper, head to SD/Update and simply move the firmware folder there
+>
On the Flipper, hit the Arrow Down button, this will get you to the file menu. In there simply search for your updates folder
+>
Inside that folder, select the Firmware you just moved onto it, and run the file thats simply called Update
+>
+
+
+
+**If you have issues or crashes with the install process, you can try to use `Settings > Storage > Factory Reset` then retry the install.**
**Doing that will NOT remove your saved files, it will only forget some settings and paired devices.**
----
@@ -203,7 +222,7 @@ $ ./fbt resources icons dolphin_ext
----
Contributors
-[](https://github.com/ClaraCrazy/Flipper-Xtreme/graphs/contributors)
+[](https://github.com/ClaraCrazy/Flipper-Xtreme/graphs/contributors)
----
diff --git a/SConstruct b/SConstruct
index 81ff67790..609b36af4 100644
--- a/SConstruct
+++ b/SConstruct
@@ -139,34 +139,33 @@ if GetOption("fullenv") or any(
basic_dist = distenv.DistCommand("fw_dist", distenv["DIST_DEPENDS"])
distenv.Default(basic_dist)
-dist_dir = distenv.GetProjectDirName()
+dist_dir_name = distenv.GetProjectDirName()
+dist_dir = distenv.Dir(f"#/dist/{dist_dir_name}")
+external_apps_artifacts = firmware_env["FW_EXTAPPS"]
+external_app_list = external_apps_artifacts.application_map.values()
+
fap_dist = [
distenv.Install(
- distenv.Dir(f"#/dist/{dist_dir}/apps/debug_elf"),
- list(
- app_artifact.debug
- for app_artifact in firmware_env["FW_EXTAPPS"].applications.values()
- ),
+ dist_dir.Dir("debug_elf"),
+ list(app_artifact.debug for app_artifact in external_app_list),
),
*(
distenv.Install(
- f"#/dist/{dist_dir}/apps/{app_artifact.app.fap_category}",
- app_artifact.compact[0],
+ dist_dir.File(dist_entry[1]).dir,
+ app_artifact.compact,
)
- for app_artifact in firmware_env["FW_EXTAPPS"].applications.values()
+ for app_artifact in external_app_list
+ for dist_entry in app_artifact.dist_entries
),
]
Depends(
fap_dist,
- list(
- app_artifact.validator
- for app_artifact in firmware_env["FW_EXTAPPS"].applications.values()
- ),
+ list(app_artifact.validator for app_artifact in external_app_list),
)
Alias("fap_dist", fap_dist)
# distenv.Default(fap_dist)
-distenv.Depends(firmware_env["FW_RESOURCES"], firmware_env["FW_EXTAPPS"].resources_dist)
+distenv.Depends(firmware_env["FW_RESOURCES"], external_apps_artifacts.resources_dist)
# Copy all faps to device
diff --git a/applications/ReadMe.md b/applications/ReadMe.md
index ddbe5d65b..598c8b2ad 100644
--- a/applications/ReadMe.md
+++ b/applications/ReadMe.md
@@ -36,15 +36,20 @@ Applications for main Flipper menu.
- `u2f` - U2F Application
-## plugins
+## External
-Extra apps for Plugins & App Loader menus.
+External applications deployed to SD Card
-- `bt_hid_app` - BT Remote controller
+- `clock` - Clock application
+- `dap_link` - DAP Link OnChip debugger
+- `hid_app` - USB/BT Remote controller
- `music_player` - Music player app (demo)
-- `picopass` - Picopass tool
+- `nfc_magic` - NFC MFC Magic card application
+- `picopass` - Picopass reader / writer
+- `signal_generator` - Signal generator app: PWM and clock generator
- `snake_game` - Snake game application
-
+- `spi_mem_manager` - SPI Memory reader / flasher
+- `weather_station` - SubGHz weather station
## services
diff --git a/applications/debug/uart_echo/application.fam b/applications/debug/uart_echo/application.fam
index b9766254c..c4079c6c1 100644
--- a/applications/debug/uart_echo/application.fam
+++ b/applications/debug/uart_echo/application.fam
@@ -1,7 +1,7 @@
App(
appid="UART_Echo",
name="[GPIO] UART Echo",
- apptype=FlipperAppType.PLUGIN,
+ apptype=FlipperAppType.EXTERNAL,
entry_point="uart_echo_app",
cdefines=["APP_UART_ECHO"],
requires=["gui"],
diff --git a/applications/debug/unit_tests/storage/storage_test.c b/applications/debug/unit_tests/storage/storage_test.c
index 582be7902..f0b45c598 100644
--- a/applications/debug/unit_tests/storage/storage_test.c
+++ b/applications/debug/unit_tests/storage/storage_test.c
@@ -362,8 +362,8 @@ static size_t storage_test_apps_count = COUNT_OF(storage_test_apps);
static int32_t storage_test_app(void* arg) {
UNUSED(arg);
Storage* storage = furi_record_open(RECORD_STORAGE);
- storage_common_remove(storage, "/app/test");
- int32_t ret = storage_file_create(storage, "/app/test", "test");
+ storage_common_remove(storage, "/data/test");
+ int32_t ret = storage_file_create(storage, "/data/test", "test");
furi_record_close(RECORD_STORAGE);
return ret;
}
@@ -401,7 +401,7 @@ MU_TEST(test_storage_data_path) {
Storage* storage = furi_record_open(RECORD_STORAGE);
File* file = storage_file_alloc(storage);
- mu_check(storage_dir_open(file, "/app"));
+ mu_check(storage_dir_open(file, "/data"));
mu_check(storage_dir_close(file));
storage_file_free(file);
diff --git a/applications/debug/usb_mouse/application.fam b/applications/debug/usb_mouse/application.fam
index bf555c671..38ba55425 100644
--- a/applications/debug/usb_mouse/application.fam
+++ b/applications/debug/usb_mouse/application.fam
@@ -7,6 +7,6 @@ App(
requires=["gui"],
stack_size=1 * 1024,
order=60,
- fap_icon="../../plugins/mousejacker/mouse_10px.png",
+ fap_icon="../../external/mousejacker/mouse_10px.png",
fap_category="Debug",
)
diff --git a/applications/examples/application.fam b/applications/examples/application.fam
index 8556714c9..347411fac 100644
--- a/applications/examples/application.fam
+++ b/applications/examples/application.fam
@@ -1,3 +1,4 @@
+# Placeholder
App(
appid="example_apps",
name="Example apps bundle",
diff --git a/applications/examples/example_apps_assets/README.md b/applications/examples/example_apps_assets/README.md
new file mode 100644
index 000000000..a24183e88
--- /dev/null
+++ b/applications/examples/example_apps_assets/README.md
@@ -0,0 +1,58 @@
+# Apps Assets folder Example
+
+This example shows how to use the Apps Assets folder to store data that is not part of the application itself, but is required for its operation, and that data is provided with the application.
+
+## What is the Apps Assets Folder?
+
+The **Apps Assets** folder is a folder where external applications unpack their assets.
+
+The path to the current application folder is related to the `appid` of the app. The `appid` is used to identify the app in the app store and is stored in the `application.fam` file.
+The Apps Assets folder is located only on the external storage, the SD card.
+
+For example, if the `appid` of the app is `snake_game`, the path to the Apps Assets folder will be `/ext/apps_assets/snake_game`. But using raw paths is not recommended, because the path to the Apps Assets folder can change in the future. Use the `/assets` alias instead.
+
+## How to get the path to the Apps Assets folder?
+
+You can use `/assets` alias to get the path to the current application data folder. For example, if you want to open a file `database.txt` in the Apps Assets folder, you can use the next path: `/data/database.txt`. But this way is not recommended, because even the `/assets` alias can change in the future.
+
+We recommend to use the `APP_ASSETS_PATH` macro to get the path to the Apps Assets folder. For example, if you want to open a file `database.txt` in the Apps Assets folder, you can use the next path: `APP_ASSETS_PATH("database.txt")`.
+
+## What is the difference between the Apps Assets folder and the Apps Data folder?
+
+The Apps Assets folder is used to store the data provided with the application. For example, if you want to create a game, you can store game levels (contant data) in the Apps Assets folder.
+
+The Apps Data folder is used to store data generated by the application. For example, if you want to create a game, you can save the progress of the game (user-generated data) in the Apps Data folder.
+
+## How to provide the data with the app?
+
+To provide data with an application, you need to create a folder inside your application folder (eg "files") and place the data in it. After that, you need to add `fap_file_assets="files"` to your application.fam file.
+
+For example, if you want to provide game levels with the application, you need to create a "levels" folder inside the "files" folder and put the game levels in it. After that, you need to add `fap_file_assets="files"` to your application.fam file. The final application folder structure will look like this:
+
+```
+snake_game
+βββ application.fam
+βββ snake_game.c
+βββ files
+ βββ levels
+ βββ level1.txt
+ βββ level2.txt
+ βββ level3.txt
+```
+
+When app is launched, the `files` folder will be unpacked to the Apps Assets folder. The final structure of the Apps Assets folder will look like this:
+
+```
+/assets
+βββ .assets.signature
+βββ levels
+ βββ level1.txt
+ βββ level2.txt
+ βββ level3.txt
+```
+
+## When will the data be unpacked?
+
+The data is unpacked when the application starts, if the application is launched for the first time, or if the data within the application is updated.
+
+When an application is compiled, the contents of the "files" folder are hashed and stored within the application itself. When the application starts, this hash is compared to the hash stored in the `.assets.signature` file. If the hashes differ or the `.assets.signature` file does not exist, the application folder is deleted and the new data is unpacked.
\ No newline at end of file
diff --git a/applications/examples/example_apps_assets/application.fam b/applications/examples/example_apps_assets/application.fam
new file mode 100644
index 000000000..4f324277d
--- /dev/null
+++ b/applications/examples/example_apps_assets/application.fam
@@ -0,0 +1,10 @@
+App(
+ appid="example_apps_assets",
+ name="Example: Apps Assets",
+ apptype=FlipperAppType.EXTERNAL,
+ entry_point="example_apps_assets_main",
+ requires=["gui"],
+ stack_size=4 * 1024,
+ fap_category="Examples",
+ fap_file_assets="files",
+)
diff --git a/applications/examples/example_apps_assets/example_apps_assets.c b/applications/examples/example_apps_assets/example_apps_assets.c
new file mode 100644
index 000000000..f2d0272f0
--- /dev/null
+++ b/applications/examples/example_apps_assets/example_apps_assets.c
@@ -0,0 +1,48 @@
+#include
+#include
+#include
+#include
+
+// Define log tag
+#define TAG "example_apps_assets"
+
+static void example_apps_data_print_file_content(Storage* storage, const char* path) {
+ Stream* stream = file_stream_alloc(storage);
+ FuriString* line = furi_string_alloc();
+
+ FURI_LOG_I(TAG, "----------------------------------------");
+ FURI_LOG_I(TAG, "File \"%s\" content:", path);
+ if(file_stream_open(stream, path, FSAM_READ, FSOM_OPEN_EXISTING)) {
+ while(stream_read_line(stream, line)) {
+ furi_string_replace_all(line, "\r", "");
+ furi_string_replace_all(line, "\n", "");
+ FURI_LOG_I(TAG, "%s", furi_string_get_cstr(line));
+ }
+ } else {
+ FURI_LOG_E(TAG, "Failed to open file");
+ }
+ FURI_LOG_I(TAG, "----------------------------------------");
+
+ furi_string_free(line);
+ file_stream_close(stream);
+ stream_free(stream);
+}
+
+// Application entry point
+int32_t example_apps_assets_main(void* p) {
+ // Mark argument as unused
+ UNUSED(p);
+
+ // Open storage
+ Storage* storage = furi_record_open(RECORD_STORAGE);
+
+ example_apps_data_print_file_content(storage, APP_ASSETS_PATH("test_asset.txt"));
+ example_apps_data_print_file_content(storage, APP_ASSETS_PATH("poems/a jelly-fish.txt"));
+ example_apps_data_print_file_content(storage, APP_ASSETS_PATH("poems/theme in yellow.txt"));
+ example_apps_data_print_file_content(storage, APP_ASSETS_PATH("poems/my shadow.txt"));
+
+ // Close storage
+ furi_record_close(RECORD_STORAGE);
+
+ return 0;
+}
diff --git a/applications/examples/example_apps_assets/files/poems/a jelly-fish.txt b/applications/examples/example_apps_assets/files/poems/a jelly-fish.txt
new file mode 100644
index 000000000..46a5a4dff
--- /dev/null
+++ b/applications/examples/example_apps_assets/files/poems/a jelly-fish.txt
@@ -0,0 +1,24 @@
+A Jelly-Fish by Marianne Moore
+
+Visible, invisible,
+A fluctuating charm,
+An amber-colored amethyst
+Inhabits it; your arm
+Approaches, and
+It opens and
+It closes;
+You have meant
+To catch it,
+And it shrivels;
+You abandon
+Your intentβ
+It opens, and it
+Closes and you
+Reach for itβ
+The blue
+Surrounding it
+Grows cloudy, and
+It floats away
+From you.
+
+source: "https://poets.org/anthology/poems-your-poetry-project-public-domain"
\ No newline at end of file
diff --git a/applications/examples/example_apps_assets/files/poems/my shadow.txt b/applications/examples/example_apps_assets/files/poems/my shadow.txt
new file mode 100644
index 000000000..e113e7df5
--- /dev/null
+++ b/applications/examples/example_apps_assets/files/poems/my shadow.txt
@@ -0,0 +1,23 @@
+My Shadow by Robert Louis Stevenson
+
+I have a little shadow that goes in and out with me,
+And what can be the use of him is more than I can see.
+He is very, very like me from the heels up to the head;
+And I see him jump before me, when I jump into my bed.
+
+The funniest thing about him is the way he likes to growβ
+Not at all like proper children, which is always very slow;
+For he sometimes shoots up taller like an India-rubber ball,
+And he sometimes gets so little that thereβs none of him at all.
+
+He hasnβt got a notion of how children ought to play,
+And can only make a fool of me in every sort of way.
+He stays so close beside me, heβs a coward you can see;
+Iβd think shame to stick to nursie as that shadow sticks to me!
+
+One morning, very early, before the sun was up,
+I rose and found the shining dew on every buttercup;
+But my lazy little shadow, like an arrant sleepy-head,
+Had stayed at home behind me and was fast asleep in bed.
+
+source: "https://poets.org/anthology/poems-your-poetry-project-public-domain"
\ No newline at end of file
diff --git a/applications/examples/example_apps_assets/files/poems/theme in yellow.txt b/applications/examples/example_apps_assets/files/poems/theme in yellow.txt
new file mode 100644
index 000000000..f392287bd
--- /dev/null
+++ b/applications/examples/example_apps_assets/files/poems/theme in yellow.txt
@@ -0,0 +1,19 @@
+Theme in Yellow by Carl Sandburg
+
+I spot the hills
+With yellow balls in autumn.
+I light the prairie cornfields
+Orange and tawny gold clusters
+And I am called pumpkins.
+On the last of October
+When dusk is fallen
+Children join hands
+And circle round me
+Singing ghost songs
+And love to the harvest moon;
+I am a jack-o'-lantern
+With terrible teeth
+And the children know
+I am fooling.
+
+source: "https://poets.org/anthology/poems-your-poetry-project-public-domain"
\ No newline at end of file
diff --git a/applications/examples/example_apps_assets/files/test_asset.txt b/applications/examples/example_apps_assets/files/test_asset.txt
new file mode 100644
index 000000000..1adcb55ee
--- /dev/null
+++ b/applications/examples/example_apps_assets/files/test_asset.txt
@@ -0,0 +1 @@
+## This is test file content
\ No newline at end of file
diff --git a/applications/examples/example_apps_data/README.md b/applications/examples/example_apps_data/README.md
index fd8666077..c70ac055a 100644
--- a/applications/examples/example_apps_data/README.md
+++ b/applications/examples/example_apps_data/README.md
@@ -9,10 +9,16 @@ The **Apps Data** folder is a folder used to store data for external apps that a
The path to the current application folder is related to the `appid` of the app. The `appid` is used to identify the app in the app store and is stored in the `application.fam` file.
The Apps Data folder is located only on the external storage, the SD card.
-For example, if the `appid` of the app is `snake_game`, the path to the Apps Data folder will be `/ext/apps_data/snake_game`. But using raw paths is not recommended, because the path to the Apps Data folder can change in the future. Use the `/app` alias instead.
+For example, if the `appid` of the app is `snake_game`, the path to the Apps Data folder will be `/ext/apps_data/snake_game`. But using raw paths is not recommended, because the path to the Apps Data folder can change in the future. Use the `/data` alias instead.
## How to get the path to the Apps Data folder?
-You can use `/app` alias to get the path to the current application data folder. For example, if you want to open a file `config.txt` in the Apps Data folder, you can use the next path: `/app/config.txt`. But this way is not recommended, because even the `/app` alias can change in the future.
+You can use `/data` alias to get the path to the current application data folder. For example, if you want to open a file `config.txt` in the Apps Data folder, you can use the next path: `/data/config.txt`. But this way is not recommended, because even the `/data` alias can change in the future.
-We recommend to use the `APP_DATA_PATH` macro to get the path to the Apps Data folder. For example, if you want to open a file `config.txt` in the Apps Data folder, you can use the next path: `APP_DATA_PATH("config.txt")`.
\ No newline at end of file
+We recommend to use the `APP_DATA_PATH` macro to get the path to the Apps Data folder. For example, if you want to open a file `config.txt` in the Apps Data folder, you can use the next path: `APP_DATA_PATH("config.txt")`.
+
+## What is the difference between the Apps Assets folder and the Apps Data folder?
+
+The Apps Assets folder is used to store the data provided with the application. For example, if you want to create a game, you can store game levels (contant data) in the Apps Assets folder.
+
+The Apps Data folder is used to store data generated by the application. For example, if you want to create a game, you can save the progress of the game (user-generated data) in the Apps Data folder.
\ No newline at end of file
diff --git a/applications/examples/example_plugins/application.fam b/applications/examples/example_plugins/application.fam
new file mode 100644
index 000000000..a6e3c2078
--- /dev/null
+++ b/applications/examples/example_plugins/application.fam
@@ -0,0 +1,31 @@
+App(
+ appid="example_plugins",
+ name="Example: App w/plugin",
+ apptype=FlipperAppType.EXTERNAL,
+ entry_point="example_plugins_app",
+ stack_size=2 * 1024,
+ fap_category="Examples",
+)
+
+App(
+ appid="example_plugins_multi",
+ name="Example: App w/plugins",
+ apptype=FlipperAppType.EXTERNAL,
+ entry_point="example_plugins_multi_app",
+ stack_size=2 * 1024,
+ fap_category="Examples",
+)
+
+App(
+ appid="example_plugin1",
+ apptype=FlipperAppType.PLUGIN,
+ entry_point="example_plugin1_ep",
+ requires=["example_plugins", "example_plugins_multi"],
+)
+
+App(
+ appid="example_plugin2",
+ apptype=FlipperAppType.PLUGIN,
+ entry_point="example_plugin2_ep",
+ requires=["example_plugins_multi"],
+)
diff --git a/applications/examples/example_plugins/example_plugins.c b/applications/examples/example_plugins/example_plugins.c
new file mode 100644
index 000000000..acc5903ad
--- /dev/null
+++ b/applications/examples/example_plugins/example_plugins.c
@@ -0,0 +1,70 @@
+/*
+ * An example of a plugin host application.
+ * Loads a single plugin and calls its methods.
+ */
+
+#include "plugin_interface.h"
+
+#include
+
+#include
+#include
+#include
+
+#define TAG "example_plugins"
+
+int32_t example_plugins_app(void* p) {
+ UNUSED(p);
+
+ FURI_LOG_I(TAG, "Starting");
+
+ Storage* storage = furi_record_open(RECORD_STORAGE);
+
+ FlipperApplication* app = flipper_application_alloc(storage, firmware_api_interface);
+
+ do {
+ FlipperApplicationPreloadStatus preload_res =
+ flipper_application_preload(app, APP_DATA_PATH("plugins/example_plugin1.fal"));
+
+ if(preload_res != FlipperApplicationPreloadStatusSuccess) {
+ FURI_LOG_E(TAG, "Failed to preload plugin");
+ break;
+ }
+
+ if(!flipper_application_is_plugin(app)) {
+ FURI_LOG_E(TAG, "Plugin file is not a library");
+ break;
+ }
+
+ FlipperApplicationLoadStatus load_status = flipper_application_map_to_memory(app);
+ if(load_status != FlipperApplicationLoadStatusSuccess) {
+ FURI_LOG_E(TAG, "Failed to load plugin file");
+ break;
+ }
+
+ const FlipperAppPluginDescriptor* app_descriptor =
+ flipper_application_plugin_get_descriptor(app);
+
+ FURI_LOG_I(
+ TAG,
+ "Loaded plugin for appid '%s', API %lu",
+ app_descriptor->appid,
+ app_descriptor->ep_api_version);
+
+ furi_check(app_descriptor->ep_api_version == PLUGIN_API_VERSION);
+ furi_check(strcmp(app_descriptor->appid, PLUGIN_APP_ID) == 0);
+
+ const ExamplePlugin* plugin = app_descriptor->entry_point;
+
+ FURI_LOG_I(TAG, "Plugin name: %s", plugin->name);
+ FURI_LOG_I(TAG, "Plugin method1: %d", plugin->method1());
+ FURI_LOG_I(TAG, "Plugin method2(7,8): %d", plugin->method2(7, 8));
+ FURI_LOG_I(TAG, "Plugin method2(1337,228): %d", plugin->method2(1337, 228));
+ } while(false);
+ flipper_application_free(app);
+
+ furi_record_close(RECORD_STORAGE);
+ FURI_LOG_I(TAG, "Goodbye!");
+
+ return 0;
+}
diff --git a/applications/examples/example_plugins/example_plugins_multi.c b/applications/examples/example_plugins/example_plugins_multi.c
new file mode 100644
index 000000000..12eba01c1
--- /dev/null
+++ b/applications/examples/example_plugins/example_plugins_multi.c
@@ -0,0 +1,43 @@
+/*
+ * An example of an advanced plugin host application.
+ * It uses PluginManager to load all plugins from a directory
+ */
+
+#include "plugin_interface.h"
+
+#include
+#include
+#include
+
+#include
+
+#define TAG "example_plugins"
+
+int32_t example_plugins_multi_app(void* p) {
+ UNUSED(p);
+
+ FURI_LOG_I(TAG, "Starting");
+
+ PluginManager* manager =
+ plugin_manager_alloc(PLUGIN_APP_ID, PLUGIN_API_VERSION, firmware_api_interface);
+
+ if(plugin_manager_load_all(manager, APP_DATA_PATH("plugins")) != PluginManagerErrorNone) {
+ FURI_LOG_E(TAG, "Failed to load all libs");
+ return 0;
+ }
+
+ uint32_t plugin_count = plugin_manager_get_count(manager);
+ FURI_LOG_I(TAG, "Loaded %lu plugin(s)", plugin_count);
+
+ for(uint32_t i = 0; i < plugin_count; i++) {
+ const ExamplePlugin* plugin = plugin_manager_get_ep(manager, i);
+ FURI_LOG_I(TAG, "plugin name: %s", plugin->name);
+ FURI_LOG_I(TAG, "plugin method1: %d", plugin->method1());
+ FURI_LOG_I(TAG, "plugin method2(7,8): %d", plugin->method2(7, 8));
+ }
+
+ plugin_manager_free(manager);
+ FURI_LOG_I(TAG, "Goodbye!");
+
+ return 0;
+}
diff --git a/applications/examples/example_plugins/plugin1.c b/applications/examples/example_plugins/plugin1.c
new file mode 100644
index 000000000..156219353
--- /dev/null
+++ b/applications/examples/example_plugins/plugin1.c
@@ -0,0 +1,32 @@
+/* A simple plugin implementing example_plugins application's plugin interface */
+
+#include "plugin_interface.h"
+
+#include
+
+static int example_plugin1_method1() {
+ return 42;
+}
+
+static int example_plugin1_method2(int arg1, int arg2) {
+ return arg1 + arg2;
+}
+
+/* Actual implementation of app<>plugin interface */
+static const ExamplePlugin example_plugin1 = {
+ .name = "Demo App Plugin 1",
+ .method1 = &example_plugin1_method1,
+ .method2 = &example_plugin1_method2,
+};
+
+/* Plugin descriptor to comply with basic plugin specification */
+static const FlipperAppPluginDescriptor example_plugin1_descriptor = {
+ .appid = PLUGIN_APP_ID,
+ .ep_api_version = PLUGIN_API_VERSION,
+ .entry_point = &example_plugin1,
+};
+
+/* Plugin entry point - must return a pointer to const descriptor */
+const FlipperAppPluginDescriptor* example_plugin1_ep() {
+ return &example_plugin1_descriptor;
+}
diff --git a/applications/examples/example_plugins/plugin2.c b/applications/examples/example_plugins/plugin2.c
new file mode 100644
index 000000000..0b774dad2
--- /dev/null
+++ b/applications/examples/example_plugins/plugin2.c
@@ -0,0 +1,32 @@
+/* Second plugin implementing example_plugins application's plugin interface */
+
+#include "plugin_interface.h"
+
+#include
+
+static int example_plugin2_method1() {
+ return 1337;
+}
+
+static int example_plugin2_method2(int arg1, int arg2) {
+ return arg1 - arg2;
+}
+
+/* Actual implementation of app<>plugin interface */
+static const ExamplePlugin example_plugin2 = {
+ .name = "Demo App Plugin 2",
+ .method1 = &example_plugin2_method1,
+ .method2 = &example_plugin2_method2,
+};
+
+/* Plugin descriptor to comply with basic plugin specification */
+static const FlipperAppPluginDescriptor example_plugin2_descriptor = {
+ .appid = PLUGIN_APP_ID,
+ .ep_api_version = PLUGIN_API_VERSION,
+ .entry_point = &example_plugin2,
+};
+
+/* Plugin entry point - must return a pointer to const descriptor */
+const FlipperAppPluginDescriptor* example_plugin2_ep() {
+ return &example_plugin2_descriptor;
+}
diff --git a/applications/examples/example_plugins/plugin_interface.h b/applications/examples/example_plugins/plugin_interface.h
new file mode 100644
index 000000000..e24bc47bf
--- /dev/null
+++ b/applications/examples/example_plugins/plugin_interface.h
@@ -0,0 +1,12 @@
+#pragma once
+
+/* Common interface between a plugin and host applicaion */
+
+#define PLUGIN_APP_ID "example_plugins"
+#define PLUGIN_API_VERSION 1
+
+typedef struct {
+ const char* name;
+ int (*method1)();
+ int (*method2)(int, int);
+} ExamplePlugin;
diff --git a/applications/examples/example_plugins_advanced/app_api.c b/applications/examples/example_plugins_advanced/app_api.c
new file mode 100644
index 000000000..42b3a1860
--- /dev/null
+++ b/applications/examples/example_plugins_advanced/app_api.c
@@ -0,0 +1,25 @@
+#include "app_api.h"
+
+/* Actual implementation of app's API and its private state */
+
+static uint32_t accumulator = 0;
+
+void app_api_accumulator_set(uint32_t value) {
+ accumulator = value;
+}
+
+uint32_t app_api_accumulator_get() {
+ return accumulator;
+}
+
+void app_api_accumulator_add(uint32_t value) {
+ accumulator += value;
+}
+
+void app_api_accumulator_sub(uint32_t value) {
+ accumulator -= value;
+}
+
+void app_api_accumulator_mul(uint32_t value) {
+ accumulator *= value;
+}
diff --git a/applications/examples/example_plugins_advanced/app_api.h b/applications/examples/example_plugins_advanced/app_api.h
new file mode 100644
index 000000000..7035b79f5
--- /dev/null
+++ b/applications/examples/example_plugins_advanced/app_api.h
@@ -0,0 +1,25 @@
+#pragma once
+
+/*
+ * This file contains an API that is internally implemented by the application
+ * It is also exposed to plugins to allow them to use the application's API.
+ */
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void app_api_accumulator_set(uint32_t value);
+
+uint32_t app_api_accumulator_get();
+
+void app_api_accumulator_add(uint32_t value);
+
+void app_api_accumulator_sub(uint32_t value);
+
+void app_api_accumulator_mul(uint32_t value);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/applications/examples/example_plugins_advanced/app_api_interface.h b/applications/examples/example_plugins_advanced/app_api_interface.h
new file mode 100644
index 000000000..d0db44c4a
--- /dev/null
+++ b/applications/examples/example_plugins_advanced/app_api_interface.h
@@ -0,0 +1,9 @@
+#pragma once
+
+#include
+
+/*
+ * Resolver interface with private application's symbols.
+ * Implementation is contained in app_api_table.c
+ */
+extern const ElfApiInterface* const application_api_interface;
\ No newline at end of file
diff --git a/applications/examples/example_plugins_advanced/app_api_table.cpp b/applications/examples/example_plugins_advanced/app_api_table.cpp
new file mode 100644
index 000000000..aacfb8c18
--- /dev/null
+++ b/applications/examples/example_plugins_advanced/app_api_table.cpp
@@ -0,0 +1,27 @@
+#include
+#include
+
+/*
+ * This file contains an implementation of a symbol table
+ * with private app's symbols. It is used by composite API resolver
+ * to load plugins that use internal application's APIs.
+ */
+#include "app_api_table_i.h"
+
+static_assert(!has_hash_collisions(app_api_table), "Detected API method hash collision!");
+
+constexpr HashtableApiInterface applicaton_hashtable_api_interface{
+ {
+ .api_version_major = 0,
+ .api_version_minor = 0,
+ /* generic resolver using pre-sorted array */
+ .resolver_callback = &elf_resolve_from_hashtable,
+ },
+ /* pointers to application's API table boundaries */
+ .table_cbegin = app_api_table.cbegin(),
+ .table_cend = app_api_table.cend(),
+};
+
+/* Casting to generic resolver to use in Composite API resolver */
+extern "C" const ElfApiInterface* const application_api_interface =
+ &applicaton_hashtable_api_interface;
diff --git a/applications/examples/example_plugins_advanced/app_api_table_i.h b/applications/examples/example_plugins_advanced/app_api_table_i.h
new file mode 100644
index 000000000..17cc8be5f
--- /dev/null
+++ b/applications/examples/example_plugins_advanced/app_api_table_i.h
@@ -0,0 +1,13 @@
+#include "app_api.h"
+
+/*
+ * A list of app's private functions and objects to expose for plugins.
+ * It is used to generate a table of symbols for import resolver to use.
+ * TBD: automatically generate this table from app's header files
+ */
+static constexpr auto app_api_table = sort(create_array_t(
+ API_METHOD(app_api_accumulator_set, void, (uint32_t)),
+ API_METHOD(app_api_accumulator_get, uint32_t, ()),
+ API_METHOD(app_api_accumulator_add, void, (uint32_t)),
+ API_METHOD(app_api_accumulator_sub, void, (uint32_t)),
+ API_METHOD(app_api_accumulator_mul, void, (uint32_t))));
\ No newline at end of file
diff --git a/applications/examples/example_plugins_advanced/application.fam b/applications/examples/example_plugins_advanced/application.fam
new file mode 100644
index 000000000..d40c0dde2
--- /dev/null
+++ b/applications/examples/example_plugins_advanced/application.fam
@@ -0,0 +1,24 @@
+App(
+ appid="example_advanced_plugins",
+ name="Example: advanced plugins",
+ apptype=FlipperAppType.EXTERNAL,
+ entry_point="example_advanced_plugins_app",
+ stack_size=2 * 1024,
+ fap_category="Examples",
+)
+
+App(
+ appid="advanced_plugin1",
+ apptype=FlipperAppType.PLUGIN,
+ entry_point="advanced_plugin1_ep",
+ requires=["example_advanced_plugins"],
+ sources=["plugin1.c"],
+)
+
+App(
+ appid="advanced_plugin2",
+ apptype=FlipperAppType.PLUGIN,
+ entry_point="advanced_plugin2_ep",
+ requires=["example_advanced_plugins"],
+ sources=["plugin2.c"],
+)
diff --git a/applications/examples/example_plugins_advanced/example_advanced_plugins.c b/applications/examples/example_plugins_advanced/example_advanced_plugins.c
new file mode 100644
index 000000000..f27b0a084
--- /dev/null
+++ b/applications/examples/example_plugins_advanced/example_advanced_plugins.c
@@ -0,0 +1,48 @@
+#include "app_api.h"
+#include "plugin_interface.h"
+#include "app_api_interface.h"
+
+#include
+#include
+#include
+
+#include
+
+#define TAG "example_advanced_plugins"
+
+int32_t example_advanced_plugins_app(void* p) {
+ UNUSED(p);
+
+ FURI_LOG_I(TAG, "Starting");
+
+ CompositeApiResolver* resolver = composite_api_resolver_alloc();
+ composite_api_resolver_add(resolver, firmware_api_interface);
+ composite_api_resolver_add(resolver, application_api_interface);
+
+ PluginManager* manager = plugin_manager_alloc(
+ PLUGIN_APP_ID, PLUGIN_API_VERSION, composite_api_resolver_get(resolver));
+
+ do {
+ if(plugin_manager_load_all(manager, APP_DATA_PATH("plugins")) != PluginManagerErrorNone) {
+ FURI_LOG_E(TAG, "Failed to load all libs");
+ break;
+ }
+
+ uint32_t plugin_count = plugin_manager_get_count(manager);
+ FURI_LOG_I(TAG, "Loaded libs: %lu", plugin_count);
+
+ for(uint32_t i = 0; i < plugin_count; i++) {
+ const AdvancedPlugin* plugin = plugin_manager_get_ep(manager, i);
+ FURI_LOG_I(TAG, "plugin name: %s. Calling methods", plugin->name);
+ plugin->method1(228);
+ plugin->method2();
+ FURI_LOG_I(TAG, "Accumulator: %lu", app_api_accumulator_get());
+ }
+ } while(0);
+
+ plugin_manager_free(manager);
+ composite_api_resolver_free(resolver);
+ FURI_LOG_I(TAG, "Goodbye!");
+
+ return 0;
+}
diff --git a/applications/examples/example_plugins_advanced/plugin1.c b/applications/examples/example_plugins_advanced/plugin1.c
new file mode 100644
index 000000000..bf0ab50b4
--- /dev/null
+++ b/applications/examples/example_plugins_advanced/plugin1.c
@@ -0,0 +1,40 @@
+/*
+ * This plugin uses both firmware's API interface and private application headers.
+ * It can be loaded by a plugin manager that uses CompoundApiInterface,
+ * which combines both interfaces.
+ */
+
+#include "app_api.h"
+#include "plugin_interface.h"
+
+#include
+#include
+
+static void advanced_plugin1_method1(int arg1) {
+ /* This function is implemented inside host application */
+ app_api_accumulator_add(arg1);
+}
+
+static void advanced_plugin1_method2() {
+ /* Accumulator value is stored inside host application */
+ FURI_LOG_I("TEST", "Plugin 1, accumulator: %lu", app_api_accumulator_get());
+}
+
+/* Actual implementation of app<>plugin interface */
+static const AdvancedPlugin advanced_plugin1 = {
+ .name = "Advanced Plugin 1",
+ .method1 = &advanced_plugin1_method1,
+ .method2 = &advanced_plugin1_method2,
+};
+
+/* Plugin descriptor to comply with basic plugin specification */
+static const FlipperAppPluginDescriptor advanced_plugin1_descriptor = {
+ .appid = PLUGIN_APP_ID,
+ .ep_api_version = PLUGIN_API_VERSION,
+ .entry_point = &advanced_plugin1,
+};
+
+/* Plugin entry point - must return a pointer to const descriptor */
+const FlipperAppPluginDescriptor* advanced_plugin1_ep() {
+ return &advanced_plugin1_descriptor;
+}
diff --git a/applications/examples/example_plugins_advanced/plugin2.c b/applications/examples/example_plugins_advanced/plugin2.c
new file mode 100644
index 000000000..f0b2f726d
--- /dev/null
+++ b/applications/examples/example_plugins_advanced/plugin2.c
@@ -0,0 +1,40 @@
+/*
+ * This plugin uses both firmware's API interface and private application headers.
+ * It can be loaded by a plugin manager that uses CompoundApiInterface,
+ * which combines both interfaces.
+ */
+
+#include "app_api.h"
+#include "plugin_interface.h"
+
+#include
+#include
+
+static void advanced_plugin2_method1(int arg1) {
+ /* This function is implemented inside host application */
+ app_api_accumulator_mul(arg1);
+}
+
+static void advanced_plugin2_method2() {
+ /* Accumulator value is stored inside host application */
+ FURI_LOG_I("TEST", "Plugin 2, accumulator: %lu", app_api_accumulator_get());
+}
+
+/* Actual implementation of app<>plugin interface */
+static const AdvancedPlugin advanced_plugin2 = {
+ .name = "Advanced Plugin 2",
+ .method1 = &advanced_plugin2_method1,
+ .method2 = &advanced_plugin2_method2,
+};
+
+/* Plugin descriptor to comply with basic plugin specification */
+static const FlipperAppPluginDescriptor advanced_plugin2_descriptor = {
+ .appid = PLUGIN_APP_ID,
+ .ep_api_version = PLUGIN_API_VERSION,
+ .entry_point = &advanced_plugin2,
+};
+
+/* Plugin entry point - must return a pointer to const descriptor */
+const FlipperAppPluginDescriptor* advanced_plugin2_ep() {
+ return &advanced_plugin2_descriptor;
+}
diff --git a/applications/examples/example_plugins_advanced/plugin_interface.h b/applications/examples/example_plugins_advanced/plugin_interface.h
new file mode 100644
index 000000000..e8b5a22d6
--- /dev/null
+++ b/applications/examples/example_plugins_advanced/plugin_interface.h
@@ -0,0 +1,12 @@
+#pragma once
+
+/* Common interface between a plugin and host applicaion */
+
+#define PLUGIN_APP_ID "example_plugins_advanced"
+#define PLUGIN_API_VERSION 1
+
+typedef struct {
+ const char* name;
+ void (*method1)(int);
+ void (*method2)();
+} AdvancedPlugin;
diff --git a/applications/examples/example_thermo/example_thermo.c b/applications/examples/example_thermo/example_thermo.c
index b3bc7cd99..4241cb59d 100644
--- a/applications/examples/example_thermo/example_thermo.c
+++ b/applications/examples/example_thermo/example_thermo.c
@@ -19,9 +19,12 @@
#include
#include
+#include
+
#define UPDATE_PERIOD_MS 1000UL
#define TEXT_STORE_SIZE 64U
+#define DS18B20_CMD_SKIP_ROM 0xccU
#define DS18B20_CMD_CONVERT 0x44U
#define DS18B20_CMD_READ_SCRATCHPAD 0xbeU
@@ -92,7 +95,7 @@ static void example_thermo_request_temperature(ExampleThermoContext* context) {
/* After the reset, a ROM operation must follow.
If there is only one device connected, the "Skip ROM" command is most appropriate
(it can also be used to address all of the connected devices in some cases).*/
- onewire_host_skip(onewire);
+ onewire_host_write(onewire, DS18B20_CMD_SKIP_ROM);
/* After the ROM operation, a device-specific command is issued.
In this case, it's a request to start measuring the temperature. */
onewire_host_write(onewire, DS18B20_CMD_CONVERT);
@@ -133,7 +136,7 @@ static void example_thermo_read_temperature(ExampleThermoContext* context) {
/* After the reset, a ROM operation must follow.
If there is only one device connected, the "Skip ROM" command is most appropriate
(it can also be used to address all of the connected devices in some cases).*/
- onewire_host_skip(onewire);
+ onewire_host_write(onewire, DS18B20_CMD_SKIP_ROM);
/* After the ROM operation, a device-specific command is issued.
This time, it will be the "Read Scratchpad" command which will
@@ -267,6 +270,9 @@ static void example_thermo_input_callback(InputEvent* event, void* ctx) {
/* Starts the reader thread and handles the input */
static void example_thermo_run(ExampleThermoContext* context) {
+ /* Enable power on external pins */
+ furi_hal_power_enable_otg();
+
/* Configure the hardware in host mode */
onewire_host_start(context->onewire);
@@ -299,6 +305,9 @@ static void example_thermo_run(ExampleThermoContext* context) {
/* Reset the hardware */
onewire_host_stop(context->onewire);
+
+ /* Disable power on external pins */
+ furi_hal_power_disable_otg();
}
/******************** Initialisation & startup *****************************/
diff --git a/applications/plugins/airmouse/LICENSE b/applications/external/airmouse/LICENSE
similarity index 100%
rename from applications/plugins/airmouse/LICENSE
rename to applications/external/airmouse/LICENSE
diff --git a/applications/plugins/airmouse/air_mouse.c b/applications/external/airmouse/air_mouse.c
similarity index 98%
rename from applications/plugins/airmouse/air_mouse.c
rename to applications/external/airmouse/air_mouse.c
index 7a90e49f1..3bb7253b5 100644
--- a/applications/plugins/airmouse/air_mouse.c
+++ b/applications/external/airmouse/air_mouse.c
@@ -1,7 +1,6 @@
#include "air_mouse.h"
#include
-#include
#include "tracking/imu/imu.h"
@@ -146,7 +145,6 @@ int32_t air_mouse_app(void* p) {
return -1;
}
- DOLPHIN_DEED(DolphinDeedPluginStart);
view_dispatcher_run(app->view_dispatcher);
imu_end();
diff --git a/applications/plugins/airmouse/air_mouse.h b/applications/external/airmouse/air_mouse.h
similarity index 100%
rename from applications/plugins/airmouse/air_mouse.h
rename to applications/external/airmouse/air_mouse.h
diff --git a/applications/plugins/airmouse/application.fam b/applications/external/airmouse/application.fam
similarity index 100%
rename from applications/plugins/airmouse/application.fam
rename to applications/external/airmouse/application.fam
index abc3f55bb..7bdba948a 100644
--- a/applications/plugins/airmouse/application.fam
+++ b/applications/external/airmouse/application.fam
@@ -4,6 +4,6 @@ App(
apptype=FlipperAppType.EXTERNAL,
entry_point="air_mouse_app",
stack_size=10 * 1024,
- fap_icon="mouse_10px.png",
fap_category="GPIO",
+ fap_icon="mouse_10px.png",
)
diff --git a/applications/plugins/airmouse/mouse_10px.png b/applications/external/airmouse/mouse_10px.png
similarity index 100%
rename from applications/plugins/airmouse/mouse_10px.png
rename to applications/external/airmouse/mouse_10px.png
diff --git a/applications/plugins/airmouse/tracking/calibration_data.cc b/applications/external/airmouse/tracking/calibration_data.cc
similarity index 100%
rename from applications/plugins/airmouse/tracking/calibration_data.cc
rename to applications/external/airmouse/tracking/calibration_data.cc
diff --git a/applications/plugins/airmouse/tracking/calibration_data.h b/applications/external/airmouse/tracking/calibration_data.h
similarity index 97%
rename from applications/plugins/airmouse/tracking/calibration_data.h
rename to applications/external/airmouse/tracking/calibration_data.h
index d47dab08d..7d240775a 100644
--- a/applications/plugins/airmouse/tracking/calibration_data.h
+++ b/applications/external/airmouse/tracking/calibration_data.h
@@ -8,7 +8,7 @@
#define CALIBRATION_DATA_VER (1)
#define CALIBRATION_DATA_FILE_NAME ".calibration.data"
-#define CALIBRATION_DATA_PATH INT_PATH(CALIBRATION_DATA_FILE_NAME)
+#define CALIBRATION_DATA_PATH EXT_PATH(CALIBRATION_DATA_FILE_NAME)
#define CALIBRATION_DATA_MAGIC (0x23)
#define CALIBRATION_DATA_SAVE(x) \
diff --git a/applications/plugins/airmouse/tracking/imu/bmi160.c b/applications/external/airmouse/tracking/imu/bmi160.c
similarity index 100%
rename from applications/plugins/airmouse/tracking/imu/bmi160.c
rename to applications/external/airmouse/tracking/imu/bmi160.c
diff --git a/applications/plugins/airmouse/tracking/imu/bmi160.h b/applications/external/airmouse/tracking/imu/bmi160.h
similarity index 100%
rename from applications/plugins/airmouse/tracking/imu/bmi160.h
rename to applications/external/airmouse/tracking/imu/bmi160.h
diff --git a/applications/plugins/airmouse/tracking/imu/bmi160_defs.h b/applications/external/airmouse/tracking/imu/bmi160_defs.h
similarity index 100%
rename from applications/plugins/airmouse/tracking/imu/bmi160_defs.h
rename to applications/external/airmouse/tracking/imu/bmi160_defs.h
diff --git a/applications/plugins/airmouse/tracking/imu/imu.c b/applications/external/airmouse/tracking/imu/imu.c
similarity index 100%
rename from applications/plugins/airmouse/tracking/imu/imu.c
rename to applications/external/airmouse/tracking/imu/imu.c
diff --git a/applications/plugins/airmouse/tracking/imu/imu.h b/applications/external/airmouse/tracking/imu/imu.h
similarity index 100%
rename from applications/plugins/airmouse/tracking/imu/imu.h
rename to applications/external/airmouse/tracking/imu/imu.h
diff --git a/applications/plugins/airmouse/tracking/imu/imu_bmi160.c b/applications/external/airmouse/tracking/imu/imu_bmi160.c
similarity index 100%
rename from applications/plugins/airmouse/tracking/imu/imu_bmi160.c
rename to applications/external/airmouse/tracking/imu/imu_bmi160.c
diff --git a/applications/plugins/airmouse/tracking/imu/imu_lsm6ds3trc.c b/applications/external/airmouse/tracking/imu/imu_lsm6ds3trc.c
similarity index 100%
rename from applications/plugins/airmouse/tracking/imu/imu_lsm6ds3trc.c
rename to applications/external/airmouse/tracking/imu/imu_lsm6ds3trc.c
diff --git a/applications/plugins/airmouse/tracking/imu/lsm6ds3tr_c_reg.c b/applications/external/airmouse/tracking/imu/lsm6ds3tr_c_reg.c
similarity index 100%
rename from applications/plugins/airmouse/tracking/imu/lsm6ds3tr_c_reg.c
rename to applications/external/airmouse/tracking/imu/lsm6ds3tr_c_reg.c
diff --git a/applications/plugins/airmouse/tracking/imu/lsm6ds3tr_c_reg.h b/applications/external/airmouse/tracking/imu/lsm6ds3tr_c_reg.h
similarity index 100%
rename from applications/plugins/airmouse/tracking/imu/lsm6ds3tr_c_reg.h
rename to applications/external/airmouse/tracking/imu/lsm6ds3tr_c_reg.h
diff --git a/applications/plugins/airmouse/tracking/main_loop.cc b/applications/external/airmouse/tracking/main_loop.cc
similarity index 100%
rename from applications/plugins/airmouse/tracking/main_loop.cc
rename to applications/external/airmouse/tracking/main_loop.cc
diff --git a/applications/plugins/airmouse/tracking/main_loop.h b/applications/external/airmouse/tracking/main_loop.h
similarity index 100%
rename from applications/plugins/airmouse/tracking/main_loop.h
rename to applications/external/airmouse/tracking/main_loop.h
diff --git a/applications/plugins/airmouse/tracking/orientation_tracker.cc b/applications/external/airmouse/tracking/orientation_tracker.cc
similarity index 100%
rename from applications/plugins/airmouse/tracking/orientation_tracker.cc
rename to applications/external/airmouse/tracking/orientation_tracker.cc
diff --git a/applications/plugins/airmouse/tracking/orientation_tracker.h b/applications/external/airmouse/tracking/orientation_tracker.h
similarity index 100%
rename from applications/plugins/airmouse/tracking/orientation_tracker.h
rename to applications/external/airmouse/tracking/orientation_tracker.h
diff --git a/applications/plugins/airmouse/tracking/sensors/accelerometer_data.h b/applications/external/airmouse/tracking/sensors/accelerometer_data.h
similarity index 100%
rename from applications/plugins/airmouse/tracking/sensors/accelerometer_data.h
rename to applications/external/airmouse/tracking/sensors/accelerometer_data.h
diff --git a/applications/plugins/airmouse/tracking/sensors/gyroscope_bias_estimator.cc b/applications/external/airmouse/tracking/sensors/gyroscope_bias_estimator.cc
similarity index 100%
rename from applications/plugins/airmouse/tracking/sensors/gyroscope_bias_estimator.cc
rename to applications/external/airmouse/tracking/sensors/gyroscope_bias_estimator.cc
diff --git a/applications/plugins/airmouse/tracking/sensors/gyroscope_bias_estimator.h b/applications/external/airmouse/tracking/sensors/gyroscope_bias_estimator.h
similarity index 100%
rename from applications/plugins/airmouse/tracking/sensors/gyroscope_bias_estimator.h
rename to applications/external/airmouse/tracking/sensors/gyroscope_bias_estimator.h
diff --git a/applications/plugins/airmouse/tracking/sensors/gyroscope_data.h b/applications/external/airmouse/tracking/sensors/gyroscope_data.h
similarity index 100%
rename from applications/plugins/airmouse/tracking/sensors/gyroscope_data.h
rename to applications/external/airmouse/tracking/sensors/gyroscope_data.h
diff --git a/applications/plugins/airmouse/tracking/sensors/lowpass_filter.cc b/applications/external/airmouse/tracking/sensors/lowpass_filter.cc
similarity index 100%
rename from applications/plugins/airmouse/tracking/sensors/lowpass_filter.cc
rename to applications/external/airmouse/tracking/sensors/lowpass_filter.cc
diff --git a/applications/plugins/airmouse/tracking/sensors/lowpass_filter.h b/applications/external/airmouse/tracking/sensors/lowpass_filter.h
similarity index 100%
rename from applications/plugins/airmouse/tracking/sensors/lowpass_filter.h
rename to applications/external/airmouse/tracking/sensors/lowpass_filter.h
diff --git a/applications/plugins/airmouse/tracking/sensors/mean_filter.cc b/applications/external/airmouse/tracking/sensors/mean_filter.cc
similarity index 100%
rename from applications/plugins/airmouse/tracking/sensors/mean_filter.cc
rename to applications/external/airmouse/tracking/sensors/mean_filter.cc
diff --git a/applications/plugins/airmouse/tracking/sensors/mean_filter.h b/applications/external/airmouse/tracking/sensors/mean_filter.h
similarity index 100%
rename from applications/plugins/airmouse/tracking/sensors/mean_filter.h
rename to applications/external/airmouse/tracking/sensors/mean_filter.h
diff --git a/applications/plugins/airmouse/tracking/sensors/median_filter.cc b/applications/external/airmouse/tracking/sensors/median_filter.cc
similarity index 100%
rename from applications/plugins/airmouse/tracking/sensors/median_filter.cc
rename to applications/external/airmouse/tracking/sensors/median_filter.cc
diff --git a/applications/plugins/airmouse/tracking/sensors/median_filter.h b/applications/external/airmouse/tracking/sensors/median_filter.h
similarity index 100%
rename from applications/plugins/airmouse/tracking/sensors/median_filter.h
rename to applications/external/airmouse/tracking/sensors/median_filter.h
diff --git a/applications/plugins/airmouse/tracking/sensors/pose_prediction.cc b/applications/external/airmouse/tracking/sensors/pose_prediction.cc
similarity index 100%
rename from applications/plugins/airmouse/tracking/sensors/pose_prediction.cc
rename to applications/external/airmouse/tracking/sensors/pose_prediction.cc
diff --git a/applications/plugins/airmouse/tracking/sensors/pose_prediction.h b/applications/external/airmouse/tracking/sensors/pose_prediction.h
similarity index 100%
rename from applications/plugins/airmouse/tracking/sensors/pose_prediction.h
rename to applications/external/airmouse/tracking/sensors/pose_prediction.h
diff --git a/applications/plugins/airmouse/tracking/sensors/pose_state.h b/applications/external/airmouse/tracking/sensors/pose_state.h
similarity index 100%
rename from applications/plugins/airmouse/tracking/sensors/pose_state.h
rename to applications/external/airmouse/tracking/sensors/pose_state.h
diff --git a/applications/plugins/airmouse/tracking/sensors/sensor_fusion_ekf.cc b/applications/external/airmouse/tracking/sensors/sensor_fusion_ekf.cc
similarity index 100%
rename from applications/plugins/airmouse/tracking/sensors/sensor_fusion_ekf.cc
rename to applications/external/airmouse/tracking/sensors/sensor_fusion_ekf.cc
diff --git a/applications/plugins/airmouse/tracking/sensors/sensor_fusion_ekf.h b/applications/external/airmouse/tracking/sensors/sensor_fusion_ekf.h
similarity index 100%
rename from applications/plugins/airmouse/tracking/sensors/sensor_fusion_ekf.h
rename to applications/external/airmouse/tracking/sensors/sensor_fusion_ekf.h
diff --git a/applications/plugins/airmouse/tracking/util/logging.h b/applications/external/airmouse/tracking/util/logging.h
similarity index 100%
rename from applications/plugins/airmouse/tracking/util/logging.h
rename to applications/external/airmouse/tracking/util/logging.h
diff --git a/applications/plugins/airmouse/tracking/util/matrix_3x3.cc b/applications/external/airmouse/tracking/util/matrix_3x3.cc
similarity index 100%
rename from applications/plugins/airmouse/tracking/util/matrix_3x3.cc
rename to applications/external/airmouse/tracking/util/matrix_3x3.cc
diff --git a/applications/plugins/airmouse/tracking/util/matrix_3x3.h b/applications/external/airmouse/tracking/util/matrix_3x3.h
similarity index 100%
rename from applications/plugins/airmouse/tracking/util/matrix_3x3.h
rename to applications/external/airmouse/tracking/util/matrix_3x3.h
diff --git a/applications/plugins/airmouse/tracking/util/matrix_4x4.cc b/applications/external/airmouse/tracking/util/matrix_4x4.cc
similarity index 100%
rename from applications/plugins/airmouse/tracking/util/matrix_4x4.cc
rename to applications/external/airmouse/tracking/util/matrix_4x4.cc
diff --git a/applications/plugins/airmouse/tracking/util/matrix_4x4.h b/applications/external/airmouse/tracking/util/matrix_4x4.h
similarity index 100%
rename from applications/plugins/airmouse/tracking/util/matrix_4x4.h
rename to applications/external/airmouse/tracking/util/matrix_4x4.h
diff --git a/applications/plugins/airmouse/tracking/util/matrixutils.cc b/applications/external/airmouse/tracking/util/matrixutils.cc
similarity index 100%
rename from applications/plugins/airmouse/tracking/util/matrixutils.cc
rename to applications/external/airmouse/tracking/util/matrixutils.cc
diff --git a/applications/plugins/airmouse/tracking/util/matrixutils.h b/applications/external/airmouse/tracking/util/matrixutils.h
similarity index 100%
rename from applications/plugins/airmouse/tracking/util/matrixutils.h
rename to applications/external/airmouse/tracking/util/matrixutils.h
diff --git a/applications/plugins/airmouse/tracking/util/rotation.cc b/applications/external/airmouse/tracking/util/rotation.cc
similarity index 100%
rename from applications/plugins/airmouse/tracking/util/rotation.cc
rename to applications/external/airmouse/tracking/util/rotation.cc
diff --git a/applications/plugins/airmouse/tracking/util/rotation.h b/applications/external/airmouse/tracking/util/rotation.h
similarity index 100%
rename from applications/plugins/airmouse/tracking/util/rotation.h
rename to applications/external/airmouse/tracking/util/rotation.h
diff --git a/applications/plugins/airmouse/tracking/util/vector.h b/applications/external/airmouse/tracking/util/vector.h
similarity index 100%
rename from applications/plugins/airmouse/tracking/util/vector.h
rename to applications/external/airmouse/tracking/util/vector.h
diff --git a/applications/plugins/airmouse/tracking/util/vectorutils.cc b/applications/external/airmouse/tracking/util/vectorutils.cc
similarity index 100%
rename from applications/plugins/airmouse/tracking/util/vectorutils.cc
rename to applications/external/airmouse/tracking/util/vectorutils.cc
diff --git a/applications/plugins/airmouse/tracking/util/vectorutils.h b/applications/external/airmouse/tracking/util/vectorutils.h
similarity index 100%
rename from applications/plugins/airmouse/tracking/util/vectorutils.h
rename to applications/external/airmouse/tracking/util/vectorutils.h
diff --git a/applications/plugins/airmouse/views/bt_mouse.c b/applications/external/airmouse/views/bt_mouse.c
similarity index 100%
rename from applications/plugins/airmouse/views/bt_mouse.c
rename to applications/external/airmouse/views/bt_mouse.c
diff --git a/applications/plugins/airmouse/views/bt_mouse.h b/applications/external/airmouse/views/bt_mouse.h
similarity index 100%
rename from applications/plugins/airmouse/views/bt_mouse.h
rename to applications/external/airmouse/views/bt_mouse.h
diff --git a/applications/plugins/airmouse/views/calibration.c b/applications/external/airmouse/views/calibration.c
similarity index 100%
rename from applications/plugins/airmouse/views/calibration.c
rename to applications/external/airmouse/views/calibration.c
diff --git a/applications/plugins/airmouse/views/calibration.h b/applications/external/airmouse/views/calibration.h
similarity index 100%
rename from applications/plugins/airmouse/views/calibration.h
rename to applications/external/airmouse/views/calibration.h
diff --git a/applications/plugins/airmouse/views/usb_mouse.c b/applications/external/airmouse/views/usb_mouse.c
similarity index 100%
rename from applications/plugins/airmouse/views/usb_mouse.c
rename to applications/external/airmouse/views/usb_mouse.c
diff --git a/applications/plugins/airmouse/views/usb_mouse.h b/applications/external/airmouse/views/usb_mouse.h
similarity index 100%
rename from applications/plugins/airmouse/views/usb_mouse.h
rename to applications/external/airmouse/views/usb_mouse.h
diff --git a/applications/external/application.fam b/applications/external/application.fam
new file mode 100644
index 000000000..12dc1cc1a
--- /dev/null
+++ b/applications/external/application.fam
@@ -0,0 +1,6 @@
+# Placeholder
+App(
+ appid="external_apps",
+ name="External apps bundle",
+ apptype=FlipperAppType.METAPACKAGE,
+)
diff --git a/applications/plugins/arkanoid/application.fam b/applications/external/arkanoid/application.fam
similarity index 87%
rename from applications/plugins/arkanoid/application.fam
rename to applications/external/arkanoid/application.fam
index bc202ff00..8440f3a89 100644
--- a/applications/plugins/arkanoid/application.fam
+++ b/applications/external/arkanoid/application.fam
@@ -3,7 +3,6 @@ App(
name="Arkanoid",
apptype=FlipperAppType.EXTERNAL,
entry_point="arkanoid_game_app",
- cdefines=["APP_ARKANOID_GAME"],
requires=["gui"],
stack_size=1 * 1024,
order=20,
diff --git a/applications/plugins/arkanoid/arkanoid_10px.png b/applications/external/arkanoid/arkanoid_10px.png
similarity index 100%
rename from applications/plugins/arkanoid/arkanoid_10px.png
rename to applications/external/arkanoid/arkanoid_10px.png
diff --git a/applications/plugins/arkanoid/arkanoid_game.c b/applications/external/arkanoid/arkanoid_game.c
similarity index 100%
rename from applications/plugins/arkanoid/arkanoid_game.c
rename to applications/external/arkanoid/arkanoid_game.c
diff --git a/applications/plugins/asteroids/LICENSE b/applications/external/asteroids/LICENSE
similarity index 100%
rename from applications/plugins/asteroids/LICENSE
rename to applications/external/asteroids/LICENSE
diff --git a/applications/plugins/asteroids/app.c b/applications/external/asteroids/app.c
similarity index 100%
rename from applications/plugins/asteroids/app.c
rename to applications/external/asteroids/app.c
diff --git a/applications/plugins/asteroids/appicon.png b/applications/external/asteroids/appicon.png
similarity index 100%
rename from applications/plugins/asteroids/appicon.png
rename to applications/external/asteroids/appicon.png
diff --git a/applications/plugins/asteroids/application.fam b/applications/external/asteroids/application.fam
similarity index 100%
rename from applications/plugins/asteroids/application.fam
rename to applications/external/asteroids/application.fam
index 5eb43a6e5..5f70a0e1c 100644
--- a/applications/plugins/asteroids/application.fam
+++ b/applications/external/asteroids/application.fam
@@ -8,8 +8,8 @@ App(
stack_size=8 * 1024,
order=50,
fap_icon="appicon.png",
- fap_category="Games",
fap_icon_assets="assets", # Image assets to compile for this application
+ fap_category="Games",
fap_description="An implementation of the classic arcade game Asteroids",
fap_author="antirez, SimplyMinimal",
fap_weburl="https://github.com/SimplyMinimal/FlipperZero-Asteroids",
diff --git a/applications/plugins/asteroids/assets/ammo_10x10.png b/applications/external/asteroids/assets/ammo_10x10.png
similarity index 100%
rename from applications/plugins/asteroids/assets/ammo_10x10.png
rename to applications/external/asteroids/assets/ammo_10x10.png
diff --git a/applications/plugins/asteroids/assets/ammo_11x11.png b/applications/external/asteroids/assets/ammo_11x11.png
similarity index 100%
rename from applications/plugins/asteroids/assets/ammo_11x11.png
rename to applications/external/asteroids/assets/ammo_11x11.png
diff --git a/applications/plugins/asteroids/assets/firepower_12x12.png b/applications/external/asteroids/assets/firepower_12x12.png
similarity index 100%
rename from applications/plugins/asteroids/assets/firepower_12x12.png
rename to applications/external/asteroids/assets/firepower_12x12.png
diff --git a/applications/plugins/asteroids/assets/firepower_9x10.png b/applications/external/asteroids/assets/firepower_9x10.png
similarity index 100%
rename from applications/plugins/asteroids/assets/firepower_9x10.png
rename to applications/external/asteroids/assets/firepower_9x10.png
diff --git a/applications/plugins/asteroids/assets/firepower_shifted_9x10.png b/applications/external/asteroids/assets/firepower_shifted_9x10.png
similarity index 100%
rename from applications/plugins/asteroids/assets/firepower_shifted_9x10.png
rename to applications/external/asteroids/assets/firepower_shifted_9x10.png
diff --git a/applications/plugins/asteroids/assets/heart_10x10.png b/applications/external/asteroids/assets/heart_10x10.png
similarity index 100%
rename from applications/plugins/asteroids/assets/heart_10x10.png
rename to applications/external/asteroids/assets/heart_10x10.png
diff --git a/applications/plugins/asteroids/assets/heart_12x12.png b/applications/external/asteroids/assets/heart_12x12.png
similarity index 100%
rename from applications/plugins/asteroids/assets/heart_12x12.png
rename to applications/external/asteroids/assets/heart_12x12.png
diff --git a/applications/plugins/asteroids/assets/nuke_10x10.png b/applications/external/asteroids/assets/nuke_10x10.png
similarity index 100%
rename from applications/plugins/asteroids/assets/nuke_10x10.png
rename to applications/external/asteroids/assets/nuke_10x10.png
diff --git a/applications/plugins/asteroids/assets/shield-frame.png b/applications/external/asteroids/assets/shield-frame.png
similarity index 100%
rename from applications/plugins/asteroids/assets/shield-frame.png
rename to applications/external/asteroids/assets/shield-frame.png
diff --git a/applications/plugins/asteroids/assets/shield_clean.png b/applications/external/asteroids/assets/shield_clean.png
similarity index 100%
rename from applications/plugins/asteroids/assets/shield_clean.png
rename to applications/external/asteroids/assets/shield_clean.png
diff --git a/applications/plugins/asteroids/assets/split_shield_10x10.png b/applications/external/asteroids/assets/split_shield_10x10.png
similarity index 100%
rename from applications/plugins/asteroids/assets/split_shield_10x10.png
rename to applications/external/asteroids/assets/split_shield_10x10.png
diff --git a/applications/external/barcode_gen/LICENSE b/applications/external/barcode_gen/LICENSE
new file mode 100644
index 000000000..4c02d8221
--- /dev/null
+++ b/applications/external/barcode_gen/LICENSE
@@ -0,0 +1,22 @@
+
+MIT License
+
+Copyright (c) 2023 Alan Tsui
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
\ No newline at end of file
diff --git a/applications/external/barcode_gen/application.fam b/applications/external/barcode_gen/application.fam
new file mode 100644
index 000000000..72c8aa114
--- /dev/null
+++ b/applications/external/barcode_gen/application.fam
@@ -0,0 +1,11 @@
+App(
+ appid="barcode_app",
+ name="Barcode",
+ apptype=FlipperAppType.EXTERNAL,
+ entry_point="barcode_main",
+ requires=["gui", "storage"],
+ stack_size=2 * 1024,
+ fap_category="Misc",
+ fap_icon="images/barcode_10.png",
+ fap_icon_assets="images",
+)
diff --git a/applications/external/barcode_gen/barcode_app.c b/applications/external/barcode_gen/barcode_app.c
new file mode 100644
index 000000000..581c92fda
--- /dev/null
+++ b/applications/external/barcode_gen/barcode_app.c
@@ -0,0 +1,342 @@
+#include "barcode_app.h"
+
+#include "barcode_app_icons.h"
+
+/**
+ * Opens a file browser dialog and returns the filepath of the selected file
+ *
+ * @param folder the folder to view when the browser opens
+ * @param file_path a string pointer for the file_path when a file is selected,
+ * file_path will be the folder path is nothing is selected
+ * @returns true if a file is selected
+*/
+static bool select_file(const char* folder, FuriString* file_path) {
+ DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS);
+ DialogsFileBrowserOptions browser_options;
+ dialog_file_browser_set_basic_options(&browser_options, BARCODE_EXTENSION, &I_barcode_10);
+ browser_options.base_path = DEFAULT_USER_BARCODES;
+ furi_string_set(file_path, folder);
+
+ bool res = dialog_file_browser_show(dialogs, file_path, file_path, &browser_options);
+
+ furi_record_close(RECORD_DIALOGS);
+
+ return res;
+}
+
+/**
+ * Reads the data from a file and stores them in the FuriStrings raw_type and raw_data
+*/
+ErrorCode read_raw_data(FuriString* file_path, FuriString* raw_type, FuriString* raw_data) {
+ //Open Storage
+ Storage* storage = furi_record_open(RECORD_STORAGE);
+ FlipperFormat* ff = flipper_format_file_alloc(storage);
+
+ ErrorCode reason = OKCode;
+
+ if(!flipper_format_file_open_existing(ff, furi_string_get_cstr(file_path))) {
+ FURI_LOG_E(TAG, "Could not open file %s", furi_string_get_cstr(file_path));
+ reason = FileOpening;
+ } else {
+ if(!flipper_format_read_string(ff, "Type", raw_type)) {
+ FURI_LOG_E(TAG, "Could not read \"Type\" string");
+ reason = InvalidFileData;
+ }
+ if(!flipper_format_read_string(ff, "Data", raw_data)) {
+ FURI_LOG_E(TAG, "Could not read \"Data\" string");
+ reason = InvalidFileData;
+ }
+ }
+
+ //Close Storage
+ flipper_format_free(ff);
+ furi_record_close(RECORD_STORAGE);
+
+ return reason;
+}
+
+/**
+ * Gets the file name from a file path
+ * @param file_path the file path
+ * @param file_name the FuriString to store the file name
+ * @param remove_extension true if the extension should be removed, otherwise false
+*/
+bool get_file_name_from_path(FuriString* file_path, FuriString* file_name, bool remove_extension) {
+ if(file_path == NULL || file_name == NULL) {
+ return false;
+ }
+ uint slash_index = furi_string_search_rchar(file_path, '/', 0);
+ if(slash_index == FURI_STRING_FAILURE || slash_index >= (furi_string_size(file_path) - 1)) {
+ return false;
+ }
+
+ furi_string_set(file_name, file_path);
+ furi_string_right(file_name, slash_index + 1);
+ if(remove_extension) {
+ uint ext_index = furi_string_search_rchar(file_name, '.', 0);
+ if(ext_index != FURI_STRING_FAILURE && ext_index < (furi_string_size(file_path))) {
+ furi_string_left(file_name, ext_index);
+ }
+ }
+
+ return true;
+}
+
+/**
+ * Creates the barcode folder
+*/
+void init_folder() {
+ Storage* storage = furi_record_open(RECORD_STORAGE);
+ FURI_LOG_I(TAG, "Creating barcodes folder");
+ if(storage_simply_mkdir(storage, DEFAULT_USER_BARCODES)) {
+ FURI_LOG_I(TAG, "Barcodes folder successfully created!");
+ } else {
+ FURI_LOG_I(TAG, "Barcodes folder already exists.");
+ }
+ furi_record_close(RECORD_STORAGE);
+}
+
+void select_barcode_item(BarcodeApp* app) {
+ FuriString* file_path = furi_string_alloc();
+ FuriString* raw_type = furi_string_alloc();
+ FuriString* raw_data = furi_string_alloc();
+
+ //this determines if the data was read correctly or if the
+ bool loaded_success = true;
+ ErrorCode reason = OKCode;
+
+ bool file_selected = select_file(DEFAULT_USER_BARCODES, file_path);
+ if(file_selected) {
+ FURI_LOG_I(TAG, "The file selected is %s", furi_string_get_cstr(file_path));
+ Barcode* barcode = app->barcode_view;
+
+ reason = read_raw_data(file_path, raw_type, raw_data);
+ if(reason != OKCode) {
+ loaded_success = false;
+ FURI_LOG_E(TAG, "Could not read data correctly");
+ }
+
+ //Free the data from the previous barcode
+ barcode_free_model(barcode);
+
+ with_view_model(
+ barcode->view,
+ BarcodeModel * model,
+ {
+ model->file_path = furi_string_alloc_set(file_path);
+
+ model->data = malloc(sizeof(BarcodeData));
+ model->data->valid = loaded_success;
+
+ if(loaded_success) {
+ model->data->raw_data = furi_string_alloc_set(raw_data);
+ model->data->correct_data = furi_string_alloc();
+
+ model->data->type_obj = get_type(raw_type);
+
+ barcode_loader(model->data);
+ } else {
+ model->data->reason = reason;
+ }
+ },
+ true);
+
+ view_dispatcher_switch_to_view(app->view_dispatcher, BarcodeView);
+ }
+
+ furi_string_free(raw_type);
+ furi_string_free(raw_data);
+ furi_string_free(file_path);
+}
+
+void edit_barcode_item(BarcodeApp* app) {
+ FuriString* file_path = furi_string_alloc();
+ FuriString* file_name = furi_string_alloc();
+ FuriString* raw_type = furi_string_alloc();
+ FuriString* raw_data = furi_string_alloc();
+
+ //this determines if the data was read correctly or if the
+ ErrorCode reason = OKCode;
+
+ bool file_selected = select_file(DEFAULT_USER_BARCODES, file_path);
+ if(file_selected) {
+ FURI_LOG_I(TAG, "The file selected is %s", furi_string_get_cstr(file_path));
+ CreateView* create_view_object = app->create_view;
+
+ reason = read_raw_data(file_path, raw_type, raw_data);
+ if(reason != OKCode) {
+ FURI_LOG_E(TAG, "Could not read data correctly");
+ with_view_model(
+ app->message_view->view,
+ MessageViewModel * model,
+ { model->message = get_error_code_message(reason); },
+ true);
+
+ view_dispatcher_switch_to_view(
+ create_view_object->barcode_app->view_dispatcher, MessageErrorView);
+
+ } else {
+ BarcodeTypeObj* type_obj = get_type(raw_type);
+ if(type_obj->type == UNKNOWN) {
+ type_obj = barcode_type_objs[0];
+ }
+ get_file_name_from_path(file_path, file_name, true);
+
+ create_view_free_model(create_view_object);
+ with_view_model(
+ create_view_object->view,
+ CreateViewModel * model,
+ {
+ model->selected_menu_item = 0;
+ model->barcode_type = type_obj;
+ model->file_path = furi_string_alloc_set(file_path);
+ model->file_name = furi_string_alloc_set(file_name);
+ model->barcode_data = furi_string_alloc_set(raw_data);
+ model->mode = EditMode;
+ },
+ true);
+ view_dispatcher_switch_to_view(app->view_dispatcher, CreateBarcodeView);
+ }
+ }
+
+ furi_string_free(raw_type);
+ furi_string_free(raw_data);
+ furi_string_free(file_name);
+ furi_string_free(file_path);
+}
+
+void create_barcode_item(BarcodeApp* app) {
+ CreateView* create_view_object = app->create_view;
+
+ create_view_free_model(create_view_object);
+
+ with_view_model(
+ create_view_object->view,
+ CreateViewModel * model,
+ {
+ model->selected_menu_item = 0;
+ model->barcode_type = barcode_type_objs[0];
+ model->file_path = furi_string_alloc();
+ model->file_name = furi_string_alloc();
+ model->barcode_data = furi_string_alloc();
+ model->mode = NewMode;
+ },
+ true);
+ view_dispatcher_switch_to_view(app->view_dispatcher, CreateBarcodeView);
+}
+
+void submenu_callback(void* context, uint32_t index) {
+ furi_assert(context);
+
+ BarcodeApp* app = context;
+
+ if(index == SelectBarcodeItem) {
+ select_barcode_item(app);
+ } else if(index == EditBarcodeItem) {
+ edit_barcode_item(app);
+ } else if(index == CreateBarcodeItem) {
+ create_barcode_item(app);
+ }
+}
+
+uint32_t main_menu_callback(void* context) {
+ UNUSED(context);
+ return MainMenuView;
+}
+
+uint32_t exit_callback(void* context) {
+ UNUSED(context);
+ return VIEW_NONE;
+}
+
+void free_app(BarcodeApp* app) {
+ FURI_LOG_I(TAG, "Freeing Data");
+
+ init_folder();
+ free_types();
+
+ view_dispatcher_remove_view(app->view_dispatcher, TextInputView);
+ text_input_free(app->text_input);
+
+ view_dispatcher_remove_view(app->view_dispatcher, MessageErrorView);
+ message_view_free(app->message_view);
+
+ view_dispatcher_remove_view(app->view_dispatcher, MainMenuView);
+ submenu_free(app->main_menu);
+
+ view_dispatcher_remove_view(app->view_dispatcher, CreateBarcodeView);
+ create_view_free(app->create_view);
+
+ view_dispatcher_remove_view(app->view_dispatcher, BarcodeView);
+ barcode_free(app->barcode_view);
+
+ //free the dispatcher
+ view_dispatcher_free(app->view_dispatcher);
+
+ furi_message_queue_free(app->event_queue);
+
+ furi_record_close(RECORD_GUI);
+ app->gui = NULL;
+
+ free(app);
+}
+
+int32_t barcode_main(void* p) {
+ UNUSED(p);
+ BarcodeApp* app = malloc(sizeof(BarcodeApp));
+ init_types();
+ app->event_queue = furi_message_queue_alloc(8, sizeof(InputEvent));
+
+ // Register view port in GUI
+ app->gui = furi_record_open(RECORD_GUI);
+
+ app->view_dispatcher = view_dispatcher_alloc();
+ view_dispatcher_enable_queue(app->view_dispatcher);
+ view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
+
+ app->main_menu = submenu_alloc();
+ submenu_add_item(app->main_menu, "Load Barcode", SelectBarcodeItem, submenu_callback, app);
+ view_set_previous_callback(submenu_get_view(app->main_menu), exit_callback);
+ view_dispatcher_add_view(app->view_dispatcher, MainMenuView, submenu_get_view(app->main_menu));
+
+ submenu_add_item(app->main_menu, "Edit Barcode", EditBarcodeItem, submenu_callback, app);
+
+ /*****************************
+ * Creating Text Input View
+ ******************************/
+ app->text_input = text_input_alloc();
+ view_dispatcher_add_view(
+ app->view_dispatcher, TextInputView, text_input_get_view(app->text_input));
+
+ /*****************************
+ * Creating Message View
+ ******************************/
+ app->message_view = message_view_allocate(app);
+ view_dispatcher_add_view(
+ app->view_dispatcher, MessageErrorView, message_get_view(app->message_view));
+
+ /*****************************
+ * Creating Create View
+ ******************************/
+ app->create_view = create_view_allocate(app);
+ submenu_add_item(app->main_menu, "Create Barcode", CreateBarcodeItem, submenu_callback, app);
+ view_set_previous_callback(create_get_view(app->create_view), main_menu_callback);
+ view_dispatcher_add_view(
+ app->view_dispatcher, CreateBarcodeView, create_get_view(app->create_view));
+
+ /*****************************
+ * Creating Barcode View
+ ******************************/
+ app->barcode_view = barcode_view_allocate(app);
+ view_set_previous_callback(barcode_get_view(app->barcode_view), main_menu_callback);
+ view_dispatcher_add_view(
+ app->view_dispatcher, BarcodeView, barcode_get_view(app->barcode_view));
+
+ //switch view to submenu and run dispatcher
+ view_dispatcher_switch_to_view(app->view_dispatcher, MainMenuView);
+ view_dispatcher_run(app->view_dispatcher);
+
+ free_app(app);
+
+ return 0;
+}
diff --git a/applications/external/barcode_gen/barcode_app.h b/applications/external/barcode_gen/barcode_app.h
new file mode 100644
index 000000000..31c805a69
--- /dev/null
+++ b/applications/external/barcode_gen/barcode_app.h
@@ -0,0 +1,87 @@
+#pragma once
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+#include "barcode_utils.h"
+
+#define TAG "BARCODE"
+#define VERSION "1.0"
+#define FILE_VERSION "1"
+
+#define TEXT_BUFFER_SIZE 128
+
+#define BARCODE_HEIGHT 50
+#define BARCODE_Y_START 3
+
+#define APPS_DATA EXT_PATH("apps_data")
+
+//the folder where the encodings are located
+#define BARCODE_DATA_FILE_DIR_PATH APPS_DATA "/barcode_data"
+
+//the folder where the code 39 encoding table is located
+#define CODE39_DICT_FILE_PATH BARCODE_DATA_FILE_DIR_PATH "/code39_encodings.txt"
+
+//the folder where the code 128 encoding table is located
+#define CODE128_DICT_FILE_PATH BARCODE_DATA_FILE_DIR_PATH "/code128_encodings.txt"
+
+//the folder where the user stores their barcodes
+#define DEFAULT_USER_BARCODES EXT_PATH("barcodes")
+
+//The extension barcode files use
+#define BARCODE_EXTENSION ".barcode"
+#define BARCODE_EXTENSION_LENGTH 8
+
+#include "views/barcode_view.h"
+#include "views/create_view.h"
+#include "views/message_view.h"
+#include "barcode_validator.h"
+
+typedef struct BarcodeApp BarcodeApp;
+
+struct BarcodeApp {
+ Submenu* main_menu;
+ ViewDispatcher* view_dispatcher;
+ Gui* gui;
+
+ FuriMessageQueue* event_queue;
+
+ CreateView* create_view;
+ Barcode* barcode_view;
+
+ MessageView* message_view;
+ TextInput* text_input;
+};
+
+enum SubmenuItems {
+ SelectBarcodeItem,
+ EditBarcodeItem,
+
+ CreateBarcodeItem
+};
+
+enum Views {
+ TextInputView,
+ MessageErrorView,
+ MainMenuView,
+ CreateBarcodeView,
+
+ BarcodeView
+};
+
+void submenu_callback(void* context, uint32_t index);
+
+uint32_t main_menu_callback(void* context);
+
+uint32_t exit_callback(void* context);
+
+int32_t barcode_main(void* p);
\ No newline at end of file
diff --git a/applications/external/barcode_gen/barcode_utils.c b/applications/external/barcode_gen/barcode_utils.c
new file mode 100644
index 000000000..0a4770045
--- /dev/null
+++ b/applications/external/barcode_gen/barcode_utils.c
@@ -0,0 +1,125 @@
+#include "barcode_utils.h"
+
+BarcodeTypeObj* barcode_type_objs[NUMBER_OF_BARCODE_TYPES] = {NULL};
+
+void init_types() {
+ BarcodeTypeObj* upc_a = malloc(sizeof(BarcodeTypeObj));
+ upc_a->name = "UPC-A";
+ upc_a->type = UPCA;
+ upc_a->min_digits = 11;
+ upc_a->max_digits = 12;
+ upc_a->start_pos = 16;
+ barcode_type_objs[UPCA] = upc_a;
+
+ BarcodeTypeObj* ean_8 = malloc(sizeof(BarcodeTypeObj));
+ ean_8->name = "EAN-8";
+ ean_8->type = EAN8;
+ ean_8->min_digits = 7;
+ ean_8->max_digits = 8;
+ ean_8->start_pos = 32;
+ barcode_type_objs[EAN8] = ean_8;
+
+ BarcodeTypeObj* ean_13 = malloc(sizeof(BarcodeTypeObj));
+ ean_13->name = "EAN-13";
+ ean_13->type = EAN13;
+ ean_13->min_digits = 12;
+ ean_13->max_digits = 13;
+ ean_13->start_pos = 16;
+ barcode_type_objs[EAN13] = ean_13;
+
+ BarcodeTypeObj* code_39 = malloc(sizeof(BarcodeTypeObj));
+ code_39->name = "CODE-39";
+ code_39->type = CODE39;
+ code_39->min_digits = 1;
+ code_39->max_digits = -1;
+ code_39->start_pos = 0;
+ barcode_type_objs[CODE39] = code_39;
+
+ BarcodeTypeObj* code_128 = malloc(sizeof(BarcodeTypeObj));
+ code_128->name = "CODE-128";
+ code_128->type = CODE128;
+ code_128->min_digits = 1;
+ code_128->max_digits = -1;
+ code_128->start_pos = 0;
+ barcode_type_objs[CODE128] = code_128;
+
+ BarcodeTypeObj* unknown = malloc(sizeof(BarcodeTypeObj));
+ unknown->name = "Unknown";
+ unknown->type = UNKNOWN;
+ unknown->min_digits = 0;
+ unknown->max_digits = 0;
+ unknown->start_pos = 0;
+ barcode_type_objs[UNKNOWN] = unknown;
+}
+
+void free_types() {
+ for(int i = 0; i < NUMBER_OF_BARCODE_TYPES; i++) {
+ free(barcode_type_objs[i]);
+ }
+}
+
+BarcodeTypeObj* get_type(FuriString* type_string) {
+ if(furi_string_cmp_str(type_string, "UPC-A") == 0) {
+ return barcode_type_objs[UPCA];
+ }
+ if(furi_string_cmp_str(type_string, "EAN-8") == 0) {
+ return barcode_type_objs[EAN8];
+ }
+ if(furi_string_cmp_str(type_string, "EAN-13") == 0) {
+ return barcode_type_objs[EAN13];
+ }
+ if(furi_string_cmp_str(type_string, "CODE-39") == 0) {
+ return barcode_type_objs[CODE39];
+ }
+ if(furi_string_cmp_str(type_string, "CODE-128") == 0) {
+ return barcode_type_objs[CODE128];
+ }
+
+ return barcode_type_objs[UNKNOWN];
+}
+
+const char* get_error_code_name(ErrorCode error_code) {
+ switch(error_code) {
+ case WrongNumberOfDigits:
+ return "Wrong Number Of Digits";
+ case InvalidCharacters:
+ return "Invalid Characters";
+ case UnsupportedType:
+ return "Unsupported Type";
+ case FileOpening:
+ return "File Opening Error";
+ case InvalidFileData:
+ return "Invalid File Data";
+ case MissingEncodingTable:
+ return "Missing Encoding Table";
+ case EncodingTableError:
+ return "Encoding Table Error";
+ case OKCode:
+ return "OK";
+ default:
+ return "Unknown Code";
+ };
+}
+
+const char* get_error_code_message(ErrorCode error_code) {
+ switch(error_code) {
+ case WrongNumberOfDigits:
+ return "Wrong # of characters";
+ case InvalidCharacters:
+ return "Invalid characters";
+ case UnsupportedType:
+ return "Unsupported barcode type";
+ case FileOpening:
+ return "Could not open file";
+ case InvalidFileData:
+ return "Invalid file data";
+ case MissingEncodingTable:
+ return "Missing encoding table";
+ case EncodingTableError:
+ return "Encoding table error";
+ case OKCode:
+ return "OK";
+ default:
+ return "Could not read barcode data";
+ };
+}
\ No newline at end of file
diff --git a/applications/external/barcode_gen/barcode_utils.h b/applications/external/barcode_gen/barcode_utils.h
new file mode 100644
index 000000000..212923a89
--- /dev/null
+++ b/applications/external/barcode_gen/barcode_utils.h
@@ -0,0 +1,53 @@
+
+#pragma once
+#include
+#include
+
+#define NUMBER_OF_BARCODE_TYPES 6
+
+typedef enum {
+ WrongNumberOfDigits, //There is too many or too few digits in the barcode
+ InvalidCharacters, //The barcode contains invalid characters
+ UnsupportedType, //the barcode type is not supported
+ FileOpening, //A problem occurred when opening the barcode data file
+ InvalidFileData, //One of the key in the file doesn't exist or there is a typo
+ MissingEncodingTable, //The encoding table txt for the barcode type is missing
+ EncodingTableError, //Something is wrong with the encoding table, probably missing data or typo
+ OKCode
+} ErrorCode;
+
+typedef enum {
+ UPCA,
+ EAN8,
+ EAN13,
+ CODE39,
+ CODE128,
+
+ UNKNOWN
+} BarcodeType;
+
+typedef struct {
+ char* name; //The name of the barcode type
+ BarcodeType type; //The barcode type enum
+ int min_digits; //the minimum number of digits
+ int max_digits; //the maximum number of digits
+ int start_pos; //where to start drawing the barcode, set to -1 to dynamically draw barcode
+} BarcodeTypeObj;
+
+typedef struct {
+ BarcodeTypeObj* type_obj;
+ int check_digit; //A place to store the check digit
+ FuriString* raw_data; //the data directly from the file
+ FuriString* correct_data; //the corrected/processed data
+ bool valid; //true if the raw data is correctly formatted, such as correct num of digits, valid characters, etc.
+ ErrorCode reason; //the reason why this barcode is invalid
+} BarcodeData;
+
+//All available barcode types
+extern BarcodeTypeObj* barcode_type_objs[NUMBER_OF_BARCODE_TYPES];
+
+void init_types();
+void free_types();
+BarcodeTypeObj* get_type(FuriString* type_string);
+const char* get_error_code_name(ErrorCode error_code);
+const char* get_error_code_message(ErrorCode error_code);
\ No newline at end of file
diff --git a/applications/external/barcode_gen/barcode_validator.c b/applications/external/barcode_gen/barcode_validator.c
new file mode 100644
index 000000000..6cb3eec4c
--- /dev/null
+++ b/applications/external/barcode_gen/barcode_validator.c
@@ -0,0 +1,344 @@
+#include "barcode_validator.h"
+
+void barcode_loader(BarcodeData* barcode_data) {
+ switch(barcode_data->type_obj->type) {
+ case UPCA:
+ case EAN8:
+ case EAN13:
+ ean_upc_loader(barcode_data);
+ break;
+ case CODE39:
+ code_39_loader(barcode_data);
+ break;
+ case CODE128:
+ code_128_loader(barcode_data);
+ break;
+ case UNKNOWN:
+ barcode_data->reason = UnsupportedType;
+ barcode_data->valid = false;
+ default:
+ break;
+ }
+}
+
+/**
+ * Calculates the check digit of a barcode if they have one
+ * @param barcode_data the barcode data
+ * @returns a check digit or -1 for either an invalid
+*/
+int calculate_check_digit(BarcodeData* barcode_data) {
+ int check_digit = -1;
+ switch(barcode_data->type_obj->type) {
+ case UPCA:
+ case EAN8:
+ case EAN13:
+ check_digit = calculate_ean_upc_check_digit(barcode_data);
+ break;
+ case CODE39:
+ case CODE128:
+ case UNKNOWN:
+ default:
+ break;
+ }
+
+ return check_digit;
+}
+
+/**
+ * Calculates the check digit of barcode types UPC-A, EAN-8, & EAN-13
+*/
+int calculate_ean_upc_check_digit(BarcodeData* barcode_data) {
+ int check_digit = 0;
+ int odd = 0;
+ int even = 0;
+
+ int length = barcode_data->type_obj->min_digits;
+
+ //Get sum of odd digits
+ for(int i = 0; i < length; i += 2) {
+ odd += furi_string_get_char(barcode_data->raw_data, i) - '0';
+ }
+
+ //Get sum of even digits
+ for(int i = 1; i < length; i += 2) {
+ even += furi_string_get_char(barcode_data->raw_data, i) - '0';
+ }
+
+ if(barcode_data->type_obj->type == EAN13) {
+ check_digit = even * 3 + odd;
+ } else {
+ check_digit = odd * 3 + even;
+ }
+
+ check_digit = check_digit % 10;
+
+ return (10 - check_digit) % 10;
+}
+
+/**
+ * Loads and validates Barcode Types EAN-8, EAN-13, and UPC-A
+ * barcode_data and its strings should already be allocated;
+*/
+void ean_upc_loader(BarcodeData* barcode_data) {
+ int barcode_length = furi_string_size(barcode_data->raw_data);
+
+ int min_digits = barcode_data->type_obj->min_digits;
+ int max_digit = barcode_data->type_obj->max_digits;
+
+ //check the length of the barcode
+ if(barcode_length < min_digits || barcode_length > max_digit) {
+ barcode_data->reason = WrongNumberOfDigits;
+ barcode_data->valid = false;
+ return;
+ }
+
+ //checks if the barcode contains any characters that aren't a number
+ for(int i = 0; i < barcode_length; i++) {
+ char character = furi_string_get_char(barcode_data->raw_data, i);
+ int digit = character - '0'; //convert the number into an int (also the index)
+ if(digit < 0 || digit > 9) {
+ barcode_data->reason = InvalidCharacters;
+ barcode_data->valid = false;
+ return;
+ }
+ }
+
+ int check_digit = calculate_check_digit(barcode_data);
+ char check_digit_char = check_digit + '0';
+
+ barcode_data->check_digit = check_digit;
+
+ //if the barcode length is at max length then we will verify if the check digit is correct
+ if(barcode_length == max_digit) {
+ //append the raw_data to the correct data string
+ furi_string_cat(barcode_data->correct_data, barcode_data->raw_data);
+
+ //append the check digit to the correct data string
+ furi_string_set_char(barcode_data->correct_data, min_digits, check_digit_char);
+ }
+ //if the barcode length is at min length, we will calculate the check digit
+ if(barcode_length == min_digits) {
+ //append the raw_data to the correct data string
+ furi_string_cat(barcode_data->correct_data, barcode_data->raw_data);
+
+ //append the check digit to the correct data string
+ furi_string_push_back(barcode_data->correct_data, check_digit_char);
+ }
+}
+
+void code_39_loader(BarcodeData* barcode_data) {
+ int barcode_length = furi_string_size(barcode_data->raw_data);
+
+ int min_digits = barcode_data->type_obj->min_digits;
+ // int max_digit = barcode_data->type_obj->max_digits;
+
+ //check the length of the barcode, must contain atleast a character,
+ //this can have as many characters as it wants, it might not fit on the screen
+ if(barcode_length < min_digits) {
+ barcode_data->reason = WrongNumberOfDigits;
+ barcode_data->valid = false;
+ return;
+ }
+
+ FuriString* barcode_bits = furi_string_alloc();
+ FuriString* temp_string = furi_string_alloc();
+
+ //add starting and ending *
+ if(!furi_string_start_with(barcode_data->raw_data, "*")) {
+ furi_string_push_back(temp_string, '*');
+ furi_string_cat(temp_string, barcode_data->raw_data);
+ furi_string_set(barcode_data->raw_data, temp_string);
+ }
+
+ if(!furi_string_end_with(barcode_data->raw_data, "*")) {
+ furi_string_push_back(barcode_data->raw_data, '*');
+ }
+
+ furi_string_free(temp_string);
+ barcode_length = furi_string_size(barcode_data->raw_data);
+
+ //Open Storage
+ Storage* storage = furi_record_open(RECORD_STORAGE);
+ FlipperFormat* ff = flipper_format_file_alloc(storage);
+
+ if(!flipper_format_file_open_existing(ff, CODE39_DICT_FILE_PATH)) {
+ FURI_LOG_E(TAG, "Could not open file %s", CODE39_DICT_FILE_PATH);
+ barcode_data->reason = MissingEncodingTable;
+ barcode_data->valid = false;
+ } else {
+ FuriString* char_bits = furi_string_alloc();
+ for(int i = 0; i < barcode_length; i++) {
+ char barcode_char = toupper(furi_string_get_char(barcode_data->raw_data, i));
+
+ //convert a char into a string so it used in flipper_format_read_string
+ char current_character[2];
+ snprintf(current_character, 2, "%c", barcode_char);
+
+ if(!flipper_format_read_string(ff, current_character, char_bits)) {
+ FURI_LOG_E(TAG, "Could not read \"%c\" string", barcode_char);
+ barcode_data->reason = InvalidCharacters;
+ barcode_data->valid = false;
+ break;
+ } else {
+ FURI_LOG_I(
+ TAG, "\"%c\" string: %s", barcode_char, furi_string_get_cstr(char_bits));
+ furi_string_cat(barcode_bits, char_bits);
+ }
+ flipper_format_rewind(ff);
+ }
+ furi_string_free(char_bits);
+ }
+
+ //Close Storage
+ flipper_format_free(ff);
+ furi_record_close(RECORD_STORAGE);
+
+ furi_string_cat(barcode_data->correct_data, barcode_bits);
+ furi_string_free(barcode_bits);
+}
+
+/**
+ * Loads a code 128 barcode
+ *
+ * Only supports character set B
+*/
+void code_128_loader(BarcodeData* barcode_data) {
+ int barcode_length = furi_string_size(barcode_data->raw_data);
+
+ //the start code for character set B
+ int start_code_value = 104;
+
+ //The bits for the start code
+ const char* start_code_bits = "11010010000";
+
+ //The bits for the stop code
+ const char* stop_code_bits = "1100011101011";
+
+ int min_digits = barcode_data->type_obj->min_digits;
+ // int max_digit = barcode_data->type_obj->max_digits;
+
+ /**
+ * A sum of all of the characters values
+ * Ex:
+ * Barcode Data : ABC
+ * A has a value of 33
+ * B has a value of 34
+ * C has a value of 35
+ *
+ * the checksum_adder would be (33 * 1) + (34 * 2) + (35 * 3) + 104 = 310
+ *
+ * Add 104 since we are using set B
+ */
+ int checksum_adder = start_code_value;
+ /**
+ * Checksum digits is the number of characters it has read so far
+ * In the above example the checksum_digits would be 3
+ */
+ int checksum_digits = 0;
+
+ //the calculated check digit
+ int final_check_digit = 0;
+
+ //check the length of the barcode, must contain atleast a character,
+ //this can have as many characters as it wants, it might not fit on the screen
+ if(barcode_length < min_digits) {
+ barcode_data->reason = WrongNumberOfDigits;
+ barcode_data->valid = false;
+ return;
+ }
+
+ //Open Storage
+ Storage* storage = furi_record_open(RECORD_STORAGE);
+ FlipperFormat* ff = flipper_format_file_alloc(storage);
+
+ FuriString* barcode_bits = furi_string_alloc();
+
+ //add the start code
+ furi_string_cat(barcode_bits, start_code_bits);
+
+ if(!flipper_format_file_open_existing(ff, CODE128_DICT_FILE_PATH)) {
+ FURI_LOG_E(TAG, "Could not open file %s", CODE128_DICT_FILE_PATH);
+ barcode_data->reason = MissingEncodingTable;
+ barcode_data->valid = false;
+ } else {
+ FuriString* value = furi_string_alloc();
+ FuriString* char_bits = furi_string_alloc();
+ for(int i = 0; i < barcode_length; i++) {
+ char barcode_char = furi_string_get_char(barcode_data->raw_data, i);
+
+ //convert a char into a string so it used in flipper_format_read_string
+ char current_character[2];
+ snprintf(current_character, 2, "%c", barcode_char);
+
+ //get the value of the character
+ if(!flipper_format_read_string(ff, current_character, value)) {
+ FURI_LOG_E(TAG, "Could not read \"%c\" string", barcode_char);
+ barcode_data->reason = InvalidCharacters;
+ barcode_data->valid = false;
+ break;
+ }
+ //using the value of the character, get the characters bits
+ if(!flipper_format_read_string(ff, furi_string_get_cstr(value), char_bits)) {
+ FURI_LOG_E(TAG, "Could not read \"%c\" string", barcode_char);
+ barcode_data->reason = EncodingTableError;
+ barcode_data->valid = false;
+ break;
+ } else {
+ //add the bits to the full barcode
+ furi_string_cat(barcode_bits, char_bits);
+
+ //calculate the checksum
+ checksum_digits += 1;
+ checksum_adder += (atoi(furi_string_get_cstr(value)) * checksum_digits);
+
+ FURI_LOG_D(
+ TAG,
+ "\"%c\" string: %s : %s : %d : %d : %d",
+ barcode_char,
+ furi_string_get_cstr(char_bits),
+ furi_string_get_cstr(value),
+ checksum_digits,
+ (atoi(furi_string_get_cstr(value)) * checksum_digits),
+ checksum_adder);
+ }
+ //bring the file pointer back to the beginning
+ flipper_format_rewind(ff);
+ }
+
+ //calculate the check digit and convert it into a c string for lookup in the encoding table
+ final_check_digit = checksum_adder % 103;
+ int length = snprintf(NULL, 0, "%d", final_check_digit);
+ char* final_check_digit_string = malloc(length + 1);
+ snprintf(final_check_digit_string, length + 1, "%d", final_check_digit);
+
+ //after the checksum has been calculated, add the bits to the full barcode
+ if(!flipper_format_read_string(ff, final_check_digit_string, char_bits)) {
+ FURI_LOG_E(TAG, "Could not read \"%s\" string", final_check_digit_string);
+ barcode_data->reason = EncodingTableError;
+ barcode_data->valid = false;
+ } else {
+ //add the check digit bits to the full barcode
+ furi_string_cat(barcode_bits, char_bits);
+
+ FURI_LOG_D(
+ TAG,
+ "\"%s\" string: %s",
+ final_check_digit_string,
+ furi_string_get_cstr(char_bits));
+ }
+
+ free(final_check_digit_string);
+ furi_string_free(value);
+ furi_string_free(char_bits);
+ }
+
+ //add the stop code
+ furi_string_cat(barcode_bits, stop_code_bits);
+
+ //Close Storage
+ flipper_format_free(ff);
+ furi_record_close(RECORD_STORAGE);
+
+ furi_string_cat(barcode_data->correct_data, barcode_bits);
+ furi_string_free(barcode_bits);
+}
diff --git a/applications/external/barcode_gen/barcode_validator.h b/applications/external/barcode_gen/barcode_validator.h
new file mode 100644
index 000000000..962d14729
--- /dev/null
+++ b/applications/external/barcode_gen/barcode_validator.h
@@ -0,0 +1,13 @@
+#pragma once
+
+#include "barcode_app.h"
+
+int calculate_check_digit(BarcodeData* barcode_data);
+int calculate_ean_upc_check_digit(BarcodeData* barcode_data);
+void ean_upc_loader(BarcodeData* barcode_data);
+void upc_a_loader(BarcodeData* barcode_data);
+void ean_8_loader(BarcodeData* barcode_data);
+void ean_13_loader(BarcodeData* barcode_data);
+void code_39_loader(BarcodeData* barcode_data);
+void code_128_loader(BarcodeData* barcode_data);
+void barcode_loader(BarcodeData* barcode_data);
\ No newline at end of file
diff --git a/applications/external/barcode_gen/encodings.c b/applications/external/barcode_gen/encodings.c
new file mode 100644
index 000000000..764fde796
--- /dev/null
+++ b/applications/external/barcode_gen/encodings.c
@@ -0,0 +1,52 @@
+#include "encodings.h"
+
+const char EAN_13_STRUCTURE_CODES[10][6] = {
+ "LLLLLL",
+ "LLGLGG",
+ "LLGGLG",
+ "LLGGGL",
+ "LGLLGG",
+ "LGGLLG",
+ "LGGGLL",
+ "LGLGLG",
+ "LGLGGL",
+ "LGGLGL"};
+
+const char UPC_EAN_L_CODES[10][8] = {
+ "0001101", // 0
+ "0011001", // 1
+ "0010011", // 2
+ "0111101", // 3
+ "0100011", // 4
+ "0110001", // 5
+ "0101111", // 6
+ "0111011", // 7
+ "0110111", // 8
+ "0001011" // 9
+};
+
+const char EAN_G_CODES[10][8] = {
+ "0100111", // 0
+ "0110011", // 1
+ "0011011", // 2
+ "0100001", // 3
+ "0011101", // 4
+ "0111001", // 5
+ "0000101", // 6
+ "0010001", // 7
+ "0001001", // 8
+ "0010111" // 9
+};
+
+const char UPC_EAN_R_CODES[10][8] = {
+ "1110010", // 0
+ "1100110", // 1
+ "1101100", // 2
+ "1000010", // 3
+ "1011100", // 4
+ "1001110", // 5
+ "1010000", // 6
+ "1000100", // 7
+ "1001000", // 8
+ "1110100" // 9
+};
\ No newline at end of file
diff --git a/applications/external/barcode_gen/encodings.h b/applications/external/barcode_gen/encodings.h
new file mode 100644
index 000000000..c5b8d61ff
--- /dev/null
+++ b/applications/external/barcode_gen/encodings.h
@@ -0,0 +1,6 @@
+#pragma once
+
+extern const char EAN_13_STRUCTURE_CODES[10][6];
+extern const char UPC_EAN_L_CODES[10][8];
+extern const char EAN_G_CODES[10][8];
+extern const char UPC_EAN_R_CODES[10][8];
\ No newline at end of file
diff --git a/applications/external/barcode_gen/images/barcode_10.png b/applications/external/barcode_gen/images/barcode_10.png
new file mode 100644
index 000000000..32d4971ad
Binary files /dev/null and b/applications/external/barcode_gen/images/barcode_10.png differ
diff --git a/applications/external/barcode_gen/views/barcode_view.c b/applications/external/barcode_gen/views/barcode_view.c
new file mode 100644
index 000000000..afd727b63
--- /dev/null
+++ b/applications/external/barcode_gen/views/barcode_view.c
@@ -0,0 +1,444 @@
+#include "../barcode_app.h"
+#include "barcode_view.h"
+#include "../encodings.h"
+
+/**
+ * @brief Draws a single bit from a barcode at a specified location
+ * @param canvas
+ * @param bit a 1 or a 0 to signify a bit of data
+ * @param x the top left x coordinate
+ * @param y the top left y coordinate
+ * @param width the width of the bit
+ * @param height the height of the bit
+ */
+static void draw_bit(Canvas* canvas, int bit, int x, int y, int width, int height) {
+ if(bit == 1) {
+ canvas_set_color(canvas, ColorBlack);
+ } else {
+ canvas_set_color(canvas, ColorWhite);
+ }
+ canvas_draw_box(canvas, x, y, width, height);
+}
+
+/**
+ *
+*/
+static void draw_error_str(Canvas* canvas, const char* error) {
+ canvas_clear(canvas);
+ canvas_draw_str_aligned(canvas, 62, 30, AlignCenter, AlignCenter, error);
+}
+
+/**
+ * @param bits a string of 1's and 0's
+ * @returns the x coordinate after the bits have been drawn, useful for drawing the next section of bits
+*/
+static int draw_bits(Canvas* canvas, const char* bits, int x, int y, int width, int height) {
+ int bits_length = strlen(bits);
+ for(int i = 0; i < bits_length; i++) {
+ char c = bits[i];
+ int num = c - '0';
+
+ draw_bit(canvas, num, x, y, width, height);
+
+ x += width;
+ }
+ return x;
+}
+
+/**
+ * Draws an EAN-8 type barcode, does not check if the barcode is valid
+ * @param canvas the canvas
+ * @param barcode_digits the digits in the barcode, must be 8 characters long
+*/
+static void draw_ean_8(Canvas* canvas, BarcodeData* barcode_data) {
+ FuriString* barcode_digits = barcode_data->correct_data;
+ BarcodeTypeObj* type_obj = barcode_data->type_obj;
+
+ int barcode_length = furi_string_size(barcode_digits);
+
+ int x = type_obj->start_pos;
+ int y = BARCODE_Y_START;
+ int width = 1;
+ int height = BARCODE_HEIGHT;
+
+ //the guard patterns for the beginning, center, ending
+ const char* end_bits = "101";
+ const char* center_bits = "01010";
+
+ //draw the starting guard pattern
+ x = draw_bits(canvas, end_bits, x, y, width, height + 5);
+
+ FuriString* code_part = furi_string_alloc();
+
+ //loop through each digit, find the encoding, and draw it
+ for(int i = 0; i < barcode_length; i++) {
+ char current_digit = furi_string_get_char(barcode_digits, i);
+
+ //the actual number and the index of the bits
+ int index = current_digit - '0';
+ //use the L-codes for the first 4 digits and the R-Codes for the last 4 digits
+ if(i <= 3) {
+ furi_string_set_str(code_part, UPC_EAN_L_CODES[index]);
+ } else {
+ furi_string_set_str(code_part, UPC_EAN_R_CODES[index]);
+ }
+
+ //convert the current_digit char into a string so it can be printed
+ char current_digit_string[2];
+ snprintf(current_digit_string, 2, "%c", current_digit);
+
+ //set the canvas color to black to print the digit
+ canvas_set_color(canvas, ColorBlack);
+ canvas_draw_str(canvas, x + 1, y + height + 8, current_digit_string);
+
+ //draw the bits of the barcode
+ x = draw_bits(canvas, furi_string_get_cstr(code_part), x, y, width, height);
+
+ //if the index has reached 3, that means 4 digits have been drawn and now draw the center guard pattern
+ if(i == 3) {
+ x = draw_bits(canvas, center_bits, x, y, width, height + 5);
+ }
+ }
+ furi_string_free(code_part);
+
+ //draw the ending guard pattern
+ x = draw_bits(canvas, end_bits, x, y, width, height + 5);
+}
+
+static void draw_ean_13(Canvas* canvas, BarcodeData* barcode_data) {
+ FuriString* barcode_digits = barcode_data->correct_data;
+ BarcodeTypeObj* type_obj = barcode_data->type_obj;
+
+ int barcode_length = furi_string_size(barcode_digits);
+
+ int x = type_obj->start_pos;
+ int y = BARCODE_Y_START;
+ int width = 1;
+ int height = BARCODE_HEIGHT;
+
+ //the guard patterns for the beginning, center, ending
+ const char* end_bits = "101";
+ const char* center_bits = "01010";
+
+ //draw the starting guard pattern
+ x = draw_bits(canvas, end_bits, x, y, width, height + 5);
+
+ FuriString* left_structure = furi_string_alloc();
+ FuriString* code_part = furi_string_alloc();
+
+ //loop through each digit, find the encoding, and draw it
+ for(int i = 0; i < barcode_length; i++) {
+ char current_digit = furi_string_get_char(barcode_digits, i);
+ int index = current_digit - '0';
+
+ if(i == 0) {
+ furi_string_set_str(left_structure, EAN_13_STRUCTURE_CODES[index]);
+
+ //convert the current_digit char into a string so it can be printed
+ char current_digit_string[2];
+ snprintf(current_digit_string, 2, "%c", current_digit);
+
+ //set the canvas color to black to print the digit
+ canvas_set_color(canvas, ColorBlack);
+ canvas_draw_str(canvas, x - 10, y + height + 8, current_digit_string);
+
+ continue;
+ } else {
+ //use the L-codes for the first 6 digits and the R-Codes for the last 6 digits
+ if(i <= 6) {
+ //get the encoding type at the current barcode bit position
+ char encoding_type = furi_string_get_char(left_structure, i - 1);
+ if(encoding_type == 'L') {
+ furi_string_set_str(code_part, UPC_EAN_L_CODES[index]);
+ } else {
+ furi_string_set_str(code_part, EAN_G_CODES[index]);
+ }
+ } else {
+ furi_string_set_str(code_part, UPC_EAN_R_CODES[index]);
+ }
+
+ //convert the current_digit char into a string so it can be printed
+ char current_digit_string[2];
+ snprintf(current_digit_string, 2, "%c", current_digit);
+
+ //set the canvas color to black to print the digit
+ canvas_set_color(canvas, ColorBlack);
+ canvas_draw_str(canvas, x + 1, y + height + 8, current_digit_string);
+
+ //draw the bits of the barcode
+ x = draw_bits(canvas, furi_string_get_cstr(code_part), x, y, width, height);
+
+ //if the index has reached 6, that means 6 digits have been drawn and we now draw the center guard pattern
+ if(i == 6) {
+ x = draw_bits(canvas, center_bits, x, y, width, height + 5);
+ }
+ }
+ }
+
+ furi_string_free(left_structure);
+ furi_string_free(code_part);
+
+ //draw the ending guard pattern
+ x = draw_bits(canvas, end_bits, x, y, width, height + 5);
+}
+
+/**
+ * Draw a UPC-A barcode
+*/
+static void draw_upc_a(Canvas* canvas, BarcodeData* barcode_data) {
+ FuriString* barcode_digits = barcode_data->correct_data;
+ BarcodeTypeObj* type_obj = barcode_data->type_obj;
+
+ int barcode_length = furi_string_size(barcode_digits);
+
+ int x = type_obj->start_pos;
+ int y = BARCODE_Y_START;
+ int width = 1;
+ int height = BARCODE_HEIGHT;
+
+ //the guard patterns for the beginning, center, ending
+ char* end_bits = "101";
+ char* center_bits = "01010";
+
+ //draw the starting guard pattern
+ x = draw_bits(canvas, end_bits, x, y, width, height + 5);
+
+ FuriString* code_part = furi_string_alloc();
+
+ //loop through each digit, find the encoding, and draw it
+ for(int i = 0; i < barcode_length; i++) {
+ char current_digit = furi_string_get_char(barcode_digits, i);
+ int index = current_digit - '0'; //convert the number into an int (also the index)
+
+ //use the L-codes for the first 6 digits and the R-Codes for the last 6 digits
+ if(i <= 5) {
+ furi_string_set_str(code_part, UPC_EAN_L_CODES[index]);
+ } else {
+ furi_string_set_str(code_part, UPC_EAN_R_CODES[index]);
+ }
+
+ //convert the current_digit char into a string so it can be printed
+ char current_digit_string[2];
+ snprintf(current_digit_string, 2, "%c", current_digit);
+
+ //set the canvas color to black to print the digit
+ canvas_set_color(canvas, ColorBlack);
+ canvas_draw_str(canvas, x + 1, y + height + 8, current_digit_string);
+
+ //draw the bits of the barcode
+ x = draw_bits(canvas, furi_string_get_cstr(code_part), x, y, width, height);
+
+ //if the index has reached 6, that means 6 digits have been drawn and we now draw the center guard pattern
+ if(i == 5) {
+ x = draw_bits(canvas, center_bits, x, y, width, height + 5);
+ }
+ }
+
+ furi_string_free(code_part);
+
+ //draw the ending guard pattern
+ x = draw_bits(canvas, end_bits, x, y, width, height + 5);
+}
+
+static void draw_code_39(Canvas* canvas, BarcodeData* barcode_data) {
+ FuriString* raw_data = barcode_data->raw_data;
+ FuriString* barcode_digits = barcode_data->correct_data;
+ //BarcodeTypeObj* type_obj = barcode_data->type_obj;
+
+ int barcode_length = furi_string_size(barcode_digits);
+ int total_pixels = 0;
+
+ for(int i = 0; i < barcode_length; i++) {
+ //1 for wide, 0 for narrow
+ char wide_or_narrow = furi_string_get_char(barcode_digits, i);
+ int wn_digit = wide_or_narrow - '0'; //wide(1) or narrow(0) digit
+
+ if(wn_digit == 1) {
+ total_pixels += 3;
+ } else {
+ total_pixels += 1;
+ }
+ if((i + 1) % 9 == 0) {
+ total_pixels += 1;
+ }
+ }
+
+ int x = (128 - total_pixels) / 2;
+ int y = BARCODE_Y_START;
+ int width = 1;
+ int height = BARCODE_HEIGHT;
+ bool filled_in = true;
+
+ //set the canvas color to black to print the digit
+ canvas_set_color(canvas, ColorBlack);
+ // canvas_draw_str_aligned(canvas, 62, 30, AlignCenter, AlignCenter, error);
+ canvas_draw_str_aligned(
+ canvas, 62, y + height + 8, AlignCenter, AlignBottom, furi_string_get_cstr(raw_data));
+
+ for(int i = 0; i < barcode_length; i++) {
+ //1 for wide, 0 for narrow
+ char wide_or_narrow = furi_string_get_char(barcode_digits, i);
+ int wn_digit = wide_or_narrow - '0'; //wide(1) or narrow(0) digit
+
+ if(filled_in) {
+ if(wn_digit == 1) {
+ x = draw_bits(canvas, "111", x, y, width, height);
+ } else {
+ x = draw_bits(canvas, "1", x, y, width, height);
+ }
+ filled_in = false;
+ } else {
+ if(wn_digit == 1) {
+ x = draw_bits(canvas, "000", x, y, width, height);
+ } else {
+ x = draw_bits(canvas, "0", x, y, width, height);
+ }
+ filled_in = true;
+ }
+ if((i + 1) % 9 == 0) {
+ x = draw_bits(canvas, "0", x, y, width, height);
+ filled_in = true;
+ }
+ }
+}
+
+static void draw_code_128(Canvas* canvas, BarcodeData* barcode_data) {
+ FuriString* raw_data = barcode_data->raw_data;
+ FuriString* barcode_digits = barcode_data->correct_data;
+
+ int barcode_length = furi_string_size(barcode_digits);
+
+ int x = (128 - barcode_length) / 2;
+ int y = BARCODE_Y_START;
+ int width = 1;
+ int height = BARCODE_HEIGHT;
+
+ x = draw_bits(canvas, furi_string_get_cstr(barcode_digits), x, y, width, height);
+
+ //set the canvas color to black to print the digit
+ canvas_set_color(canvas, ColorBlack);
+ // canvas_draw_str_aligned(canvas, 62, 30, AlignCenter, AlignCenter, error);
+ canvas_draw_str_aligned(
+ canvas, 62, y + height + 8, AlignCenter, AlignBottom, furi_string_get_cstr(raw_data));
+}
+
+static void barcode_draw_callback(Canvas* canvas, void* ctx) {
+ furi_assert(ctx);
+ BarcodeModel* barcode_model = ctx;
+ BarcodeData* data = barcode_model->data;
+ // const char* barcode_digits =;
+
+ canvas_clear(canvas);
+ if(data->valid) {
+ switch(data->type_obj->type) {
+ case UPCA:
+ draw_upc_a(canvas, data);
+ break;
+ case EAN8:
+ draw_ean_8(canvas, data);
+ break;
+ case EAN13:
+ draw_ean_13(canvas, data);
+ break;
+ case CODE39:
+ draw_code_39(canvas, data);
+ break;
+ case CODE128:
+ draw_code_128(canvas, data);
+ break;
+ case UNKNOWN:
+ default:
+ break;
+ }
+ } else {
+ switch(data->reason) {
+ case WrongNumberOfDigits:
+ draw_error_str(canvas, "Wrong # of characters");
+ break;
+ case InvalidCharacters:
+ draw_error_str(canvas, "Invalid characters");
+ break;
+ case UnsupportedType:
+ draw_error_str(canvas, "Unsupported barcode type");
+ break;
+ case FileOpening:
+ draw_error_str(canvas, "Could not open file");
+ break;
+ case InvalidFileData:
+ draw_error_str(canvas, "Invalid file data");
+ break;
+ case MissingEncodingTable:
+ draw_error_str(canvas, "Missing encoding table");
+ break;
+ case EncodingTableError:
+ draw_error_str(canvas, "Encoding table error");
+ break;
+ default:
+ draw_error_str(canvas, "Could not read barcode data");
+ break;
+ }
+ }
+}
+
+bool barcode_input_callback(InputEvent* input_event, void* ctx) {
+ UNUSED(ctx);
+ //furi_assert(ctx);
+
+ //Barcode* test_view_object = ctx;
+
+ if(input_event->key == InputKeyBack) {
+ return false;
+ } else {
+ return true;
+ }
+}
+
+Barcode* barcode_view_allocate(BarcodeApp* barcode_app) {
+ furi_assert(barcode_app);
+
+ Barcode* barcode = malloc(sizeof(Barcode));
+
+ barcode->view = view_alloc();
+ barcode->barcode_app = barcode_app;
+
+ view_set_context(barcode->view, barcode);
+ view_allocate_model(barcode->view, ViewModelTypeLocking, sizeof(BarcodeModel));
+ view_set_draw_callback(barcode->view, barcode_draw_callback);
+ view_set_input_callback(barcode->view, barcode_input_callback);
+
+ return barcode;
+}
+
+void barcode_free_model(Barcode* barcode) {
+ with_view_model(
+ barcode->view,
+ BarcodeModel * model,
+ {
+ if(model->file_path != NULL) {
+ furi_string_free(model->file_path);
+ }
+ if(model->data != NULL) {
+ if(model->data->raw_data != NULL) {
+ furi_string_free(model->data->raw_data);
+ }
+ if(model->data->correct_data != NULL) {
+ furi_string_free(model->data->correct_data);
+ }
+ free(model->data);
+ }
+ },
+ false);
+}
+
+void barcode_free(Barcode* barcode) {
+ furi_assert(barcode);
+
+ barcode_free_model(barcode);
+ view_free(barcode->view);
+ free(barcode);
+}
+
+View* barcode_get_view(Barcode* barcode) {
+ furi_assert(barcode);
+ return barcode->view;
+}
\ No newline at end of file
diff --git a/applications/external/barcode_gen/views/barcode_view.h b/applications/external/barcode_gen/views/barcode_view.h
new file mode 100644
index 000000000..828428c08
--- /dev/null
+++ b/applications/external/barcode_gen/views/barcode_view.h
@@ -0,0 +1,23 @@
+#pragma once
+
+#include
+
+typedef struct BarcodeApp BarcodeApp;
+
+typedef struct {
+ View* view;
+ BarcodeApp* barcode_app;
+} Barcode;
+
+typedef struct {
+ FuriString* file_path;
+ BarcodeData* data;
+} BarcodeModel;
+
+Barcode* barcode_view_allocate(BarcodeApp* barcode_app);
+
+void barcode_free_model(Barcode* barcode);
+
+void barcode_free(Barcode* barcode);
+
+View* barcode_get_view(Barcode* barcode);
diff --git a/applications/external/barcode_gen/views/create_view.c b/applications/external/barcode_gen/views/create_view.c
new file mode 100644
index 000000000..23a5fa409
--- /dev/null
+++ b/applications/external/barcode_gen/views/create_view.c
@@ -0,0 +1,493 @@
+#include "../barcode_app.h"
+#include "create_view.h"
+#include
+
+#define LINE_HEIGHT 16
+#define TEXT_PADDING 4
+#define TOTAL_MENU_ITEMS 5
+
+typedef enum {
+ TypeMenuItem,
+ FileNameMenuItem,
+ BarcodeDataMenuItem,
+ SaveMenuButton,
+ DeleteMenuButton
+} MenuItems;
+
+/**
+ * Took this function from blackjack
+ * @author @teeebor
+*/
+void draw_menu_item(
+ Canvas* const canvas,
+ const char* text,
+ const char* value,
+ int y,
+ bool left_caret,
+ bool right_caret,
+ bool selected) {
+ UNUSED(selected);
+ if(y < 0 || y >= 64) {
+ return;
+ }
+
+ if(selected) {
+ canvas_set_color(canvas, ColorBlack);
+ canvas_draw_box(canvas, 0, y, 123, LINE_HEIGHT);
+ canvas_set_color(canvas, ColorWhite);
+ }
+
+ canvas_draw_str_aligned(canvas, 4, y + TEXT_PADDING, AlignLeft, AlignTop, text);
+ if(left_caret) {
+ canvas_draw_str_aligned(canvas, 60, y + TEXT_PADDING, AlignLeft, AlignTop, "<");
+ }
+
+ canvas_draw_str_aligned(canvas, 90, y + TEXT_PADDING, AlignCenter, AlignTop, value);
+ if(right_caret) {
+ canvas_draw_str_aligned(canvas, 120, y + TEXT_PADDING, AlignRight, AlignTop, ">");
+ }
+
+ canvas_set_color(canvas, ColorBlack);
+}
+
+void draw_button(Canvas* const canvas, const char* text, int y, bool selected) {
+ if(selected) {
+ canvas_set_color(canvas, ColorBlack);
+ canvas_draw_box(canvas, 0, y, 123, LINE_HEIGHT);
+ canvas_set_color(canvas, ColorWhite);
+ }
+
+ canvas_draw_str_aligned(canvas, 64, y + TEXT_PADDING, AlignCenter, AlignTop, text);
+
+ canvas_set_color(canvas, ColorBlack);
+}
+
+static void app_draw_callback(Canvas* canvas, void* ctx) {
+ furi_assert(ctx);
+
+ CreateViewModel* create_view_model = ctx;
+
+ BarcodeTypeObj* type_obj = create_view_model->barcode_type;
+ if(create_view_model->barcode_type == NULL) {
+ return;
+ }
+ BarcodeType selected_type = type_obj->type;
+
+ int selected_menu_item = create_view_model->selected_menu_item;
+
+ int total_menu_items = create_view_model->mode == EditMode ? TOTAL_MENU_ITEMS :
+ TOTAL_MENU_ITEMS - 1;
+
+ int startY = 0;
+
+ //the menu items index that is/would be in view
+ //int current_last_menu_item = selected_menu_item + 3;
+ if(selected_menu_item > 1) {
+ int offset = 2;
+ if(selected_menu_item + offset > total_menu_items) {
+ offset = 3;
+ }
+ startY -= (LINE_HEIGHT * (selected_menu_item - offset));
+ }
+
+ //ensure that the scroll height is atleast 1
+ int scrollHeight = ceil(64.0 / total_menu_items);
+ int scrollPos = scrollHeight * selected_menu_item;
+
+ canvas_set_color(canvas, ColorBlack);
+ //draw the scroll bar box
+ canvas_draw_box(canvas, 125, scrollPos, 3, scrollHeight);
+ //draw the scroll bar track
+ canvas_draw_box(canvas, 126, 0, 1, 64);
+
+ draw_menu_item(
+ canvas,
+ "Type",
+ type_obj->name,
+ TypeMenuItem * LINE_HEIGHT + startY,
+ selected_type > 0,
+ selected_type < NUMBER_OF_BARCODE_TYPES - 2,
+ selected_menu_item == TypeMenuItem);
+
+ draw_menu_item(
+ canvas,
+ "Name",
+ furi_string_empty(create_view_model->file_name) ?
+ "--" :
+ furi_string_get_cstr(create_view_model->file_name),
+ FileNameMenuItem * LINE_HEIGHT + startY,
+ false,
+ false,
+ selected_menu_item == FileNameMenuItem);
+
+ draw_menu_item(
+ canvas,
+ "Data",
+ furi_string_empty(create_view_model->barcode_data) ?
+ "--" :
+ furi_string_get_cstr(create_view_model->barcode_data),
+ BarcodeDataMenuItem * LINE_HEIGHT + startY,
+ false,
+ false,
+ selected_menu_item == BarcodeDataMenuItem);
+
+ draw_button(
+ canvas,
+ "Save",
+ SaveMenuButton * LINE_HEIGHT + startY,
+ selected_menu_item == SaveMenuButton);
+
+ if(create_view_model->mode == EditMode) {
+ draw_button(
+ canvas,
+ "Delete",
+ DeleteMenuButton * LINE_HEIGHT + startY,
+ selected_menu_item == DeleteMenuButton);
+ }
+}
+
+void text_input_callback(void* ctx) {
+ CreateView* create_view_object = ctx;
+
+ with_view_model(
+ create_view_object->view,
+ CreateViewModel * model,
+ {
+ if(create_view_object->setter == FileNameSetter) {
+ furi_string_set_str(model->file_name, create_view_object->input);
+ }
+ if(create_view_object->setter == BarcodeDataSetter) {
+ furi_string_set_str(model->barcode_data, create_view_object->input);
+ }
+ },
+ true);
+
+ view_dispatcher_switch_to_view(
+ create_view_object->barcode_app->view_dispatcher, CreateBarcodeView);
+}
+
+static bool app_input_callback(InputEvent* input_event, void* ctx) {
+ furi_assert(ctx);
+
+ if(input_event->key == InputKeyBack) {
+ return false;
+ }
+
+ CreateView* create_view_object = ctx;
+
+ //get the currently selected menu item from the model
+ int selected_menu_item = 0;
+ BarcodeTypeObj* barcode_type = NULL;
+ FuriString* file_name;
+ FuriString* barcode_data;
+ CreateMode mode;
+
+ with_view_model(
+ create_view_object->view,
+ CreateViewModel * model,
+ {
+ selected_menu_item = model->selected_menu_item;
+ barcode_type = model->barcode_type;
+ file_name = model->file_name;
+ barcode_data = model->barcode_data;
+ mode = model->mode;
+ },
+ true);
+
+ int total_menu_items = mode == EditMode ? TOTAL_MENU_ITEMS : TOTAL_MENU_ITEMS - 1;
+
+ if(input_event->type == InputTypePress) {
+ if(input_event->key == InputKeyUp && selected_menu_item > 0) {
+ selected_menu_item--;
+ } else if(input_event->key == InputKeyDown && selected_menu_item < total_menu_items - 1) {
+ selected_menu_item++;
+ } else if(input_event->key == InputKeyLeft) {
+ if(selected_menu_item == TypeMenuItem && barcode_type != NULL) { //Select Barcode Type
+ if(barcode_type->type > 0) {
+ barcode_type = barcode_type_objs[barcode_type->type - 1];
+ }
+ }
+ } else if(input_event->key == InputKeyRight) {
+ if(selected_menu_item == TypeMenuItem && barcode_type != NULL) { //Select Barcode Type
+ if(barcode_type->type < NUMBER_OF_BARCODE_TYPES - 2) {
+ barcode_type = barcode_type_objs[barcode_type->type + 1];
+ }
+ }
+ } else if(input_event->key == InputKeyOk) {
+ if(selected_menu_item == FileNameMenuItem && barcode_type != NULL) {
+ create_view_object->setter = FileNameSetter;
+
+ snprintf(
+ create_view_object->input,
+ sizeof(create_view_object->input),
+ "%s",
+ furi_string_get_cstr(file_name));
+
+ text_input_set_result_callback(
+ create_view_object->barcode_app->text_input,
+ text_input_callback,
+ create_view_object,
+ create_view_object->input,
+ TEXT_BUFFER_SIZE - BARCODE_EXTENSION_LENGTH, //remove the barcode length
+ //clear default text
+ false);
+ text_input_set_header_text(
+ create_view_object->barcode_app->text_input, "File Name");
+
+ view_dispatcher_switch_to_view(
+ create_view_object->barcode_app->view_dispatcher, TextInputView);
+ }
+ if(selected_menu_item == BarcodeDataMenuItem && barcode_type != NULL) {
+ create_view_object->setter = BarcodeDataSetter;
+
+ snprintf(
+ create_view_object->input,
+ sizeof(create_view_object->input),
+ "%s",
+ furi_string_get_cstr(barcode_data));
+
+ text_input_set_result_callback(
+ create_view_object->barcode_app->text_input,
+ text_input_callback,
+ create_view_object,
+ create_view_object->input,
+ TEXT_BUFFER_SIZE,
+ //clear default text
+ false);
+ text_input_set_header_text(
+ create_view_object->barcode_app->text_input, "Barcode Data");
+
+ view_dispatcher_switch_to_view(
+ create_view_object->barcode_app->view_dispatcher, TextInputView);
+ }
+ if(selected_menu_item == SaveMenuButton && barcode_type != NULL) {
+ save_barcode(create_view_object);
+ }
+ if(selected_menu_item == DeleteMenuButton && barcode_type != NULL) {
+ if(mode == EditMode) {
+ remove_barcode(create_view_object);
+ } else if(mode == NewMode) {
+ view_dispatcher_switch_to_view(
+ create_view_object->barcode_app->view_dispatcher, MainMenuView);
+ }
+ }
+ }
+ }
+
+ //change the currently selected menu item
+ with_view_model(
+ create_view_object->view,
+ CreateViewModel * model,
+ {
+ model->selected_menu_item = selected_menu_item;
+ model->barcode_type = barcode_type;
+ },
+ true);
+
+ return true;
+}
+
+CreateView* create_view_allocate(BarcodeApp* barcode_app) {
+ furi_assert(barcode_app);
+
+ CreateView* create_view_object = malloc(sizeof(CreateView));
+
+ create_view_object->view = view_alloc();
+ create_view_object->barcode_app = barcode_app;
+
+ view_set_context(create_view_object->view, create_view_object);
+ view_allocate_model(create_view_object->view, ViewModelTypeLocking, sizeof(CreateViewModel));
+ view_set_draw_callback(create_view_object->view, app_draw_callback);
+ view_set_input_callback(create_view_object->view, app_input_callback);
+
+ return create_view_object;
+}
+
+void create_view_free_model(CreateView* create_view_object) {
+ with_view_model(
+ create_view_object->view,
+ CreateViewModel * model,
+ {
+ if(model->file_path != NULL) {
+ furi_string_free(model->file_path);
+ }
+ if(model->file_name != NULL) {
+ furi_string_free(model->file_name);
+ }
+ if(model->barcode_data != NULL) {
+ furi_string_free(model->barcode_data);
+ }
+ },
+ true);
+}
+
+void remove_barcode(CreateView* create_view_object) {
+ Storage* storage = furi_record_open(RECORD_STORAGE);
+
+ bool success = false;
+
+ with_view_model(
+ create_view_object->view,
+ CreateViewModel * model,
+ {
+ FURI_LOG_I(TAG, "Attempting to remove file");
+ if(model->file_path != NULL) {
+ FURI_LOG_I(TAG, "Removing File: %s", furi_string_get_cstr(model->file_path));
+ if(storage_simply_remove(storage, furi_string_get_cstr(model->file_path))) {
+ FURI_LOG_I(
+ TAG,
+ "File: \"%s\" was successfully removed",
+ furi_string_get_cstr(model->file_path));
+ success = true;
+ } else {
+ FURI_LOG_E(TAG, "Unable to remove file!");
+ success = false;
+ }
+ } else {
+ FURI_LOG_E(TAG, "Could not remove barcode file");
+ success = false;
+ }
+ },
+ true);
+ furi_record_close(RECORD_STORAGE);
+
+ with_view_model(
+ create_view_object->barcode_app->message_view->view,
+ MessageViewModel * model,
+ {
+ if(success) {
+ model->message = "File Deleted";
+ } else {
+ model->message = "Could not delete file";
+ }
+ },
+ true);
+
+ view_dispatcher_switch_to_view(
+ create_view_object->barcode_app->view_dispatcher, MessageErrorView);
+}
+
+void save_barcode(CreateView* create_view_object) {
+ BarcodeTypeObj* barcode_type = NULL;
+ FuriString* file_path; //this may be empty
+ FuriString* file_name;
+ FuriString* barcode_data;
+ CreateMode mode;
+
+ with_view_model(
+ create_view_object->view,
+ CreateViewModel * model,
+ {
+ file_path = model->file_path;
+ file_name = model->file_name;
+ barcode_data = model->barcode_data;
+ barcode_type = model->barcode_type;
+ mode = model->mode;
+ },
+ true);
+
+ if(file_name == NULL || furi_string_empty(file_name)) {
+ FURI_LOG_E(TAG, "File Name cannot be empty");
+ return;
+ }
+ if(barcode_data == NULL || furi_string_empty(barcode_data)) {
+ FURI_LOG_E(TAG, "Barcode Data cannot be empty");
+ return;
+ }
+ if(barcode_type == NULL) {
+ FURI_LOG_E(TAG, "Type not defined");
+ return;
+ }
+
+ bool success = false;
+
+ FuriString* full_file_path = furi_string_alloc_set(DEFAULT_USER_BARCODES);
+ furi_string_push_back(full_file_path, '/');
+ furi_string_cat(full_file_path, file_name);
+ furi_string_cat_str(full_file_path, BARCODE_EXTENSION);
+
+ Storage* storage = furi_record_open(RECORD_STORAGE);
+
+ if(mode == EditMode) {
+ if(!furi_string_empty(file_path)) {
+ if(!furi_string_equal(file_path, full_file_path)) {
+ FS_Error error = storage_common_rename(
+ storage,
+ furi_string_get_cstr(file_path),
+ furi_string_get_cstr(full_file_path));
+ if(error != FSE_OK) {
+ FURI_LOG_E(TAG, "Rename error: %s", storage_error_get_desc(error));
+ } else {
+ FURI_LOG_I(TAG, "Rename Success");
+ }
+ }
+ }
+ }
+
+ FlipperFormat* ff = flipper_format_file_alloc(storage);
+
+ FURI_LOG_I(TAG, "Saving Barcode to: %s", furi_string_get_cstr(full_file_path));
+
+ bool file_opened_status = false;
+ if(mode == NewMode) {
+ file_opened_status =
+ flipper_format_file_open_new(ff, furi_string_get_cstr(full_file_path));
+ } else if(mode == EditMode) {
+ file_opened_status =
+ flipper_format_file_open_always(ff, furi_string_get_cstr(full_file_path));
+ }
+
+ if(file_opened_status) {
+ // Filetype: Barcode
+ // Version: 1
+
+ // # Types - UPC-A, EAN-8, EAN-13, CODE-39
+ // Type: CODE-39
+ // Data: AB
+ flipper_format_write_string_cstr(ff, "Filetype", "Barcode");
+
+ flipper_format_write_string_cstr(ff, "Version", FILE_VERSION);
+
+ flipper_format_write_comment_cstr(ff, "Types - UPC-A, EAN-8, EAN-13, CODE-39, CODE-128");
+
+ flipper_format_write_string_cstr(ff, "Type", barcode_type->name);
+
+ flipper_format_write_string_cstr(ff, "Data", furi_string_get_cstr(barcode_data));
+
+ success = true;
+ } else {
+ FURI_LOG_E(TAG, "Save error");
+ success = false;
+ }
+ furi_string_free(full_file_path);
+ flipper_format_free(ff);
+ furi_record_close(RECORD_STORAGE);
+
+ with_view_model(
+ create_view_object->barcode_app->message_view->view,
+ MessageViewModel * model,
+ {
+ if(success) {
+ model->message = "File Saved!";
+ } else {
+ model->message = "A saving error has occurred";
+ }
+ },
+ true);
+
+ view_dispatcher_switch_to_view(
+ create_view_object->barcode_app->view_dispatcher, MessageErrorView);
+}
+
+void create_view_free(CreateView* create_view_object) {
+ furi_assert(create_view_object);
+
+ create_view_free_model(create_view_object);
+ view_free(create_view_object->view);
+ free(create_view_object);
+}
+
+View* create_get_view(CreateView* create_view_object) {
+ furi_assert(create_view_object);
+ return create_view_object->view;
+}
\ No newline at end of file
diff --git a/applications/external/barcode_gen/views/create_view.h b/applications/external/barcode_gen/views/create_view.h
new file mode 100644
index 000000000..6063786d9
--- /dev/null
+++ b/applications/external/barcode_gen/views/create_view.h
@@ -0,0 +1,46 @@
+#pragma once
+
+#include
+
+typedef struct BarcodeApp BarcodeApp;
+
+typedef enum {
+ FileNameSetter,
+ BarcodeDataSetter
+} InputSetter; //what value to set for the text input view
+
+typedef enum {
+ EditMode,
+
+ NewMode
+} CreateMode;
+
+typedef struct {
+ View* view;
+ BarcodeApp* barcode_app;
+
+ InputSetter setter;
+ char input[TEXT_BUFFER_SIZE];
+} CreateView;
+
+typedef struct {
+ int selected_menu_item;
+
+ CreateMode mode;
+ BarcodeTypeObj* barcode_type;
+ FuriString* file_path; //the current file that is opened
+ FuriString* file_name;
+ FuriString* barcode_data;
+} CreateViewModel;
+
+CreateView* create_view_allocate(BarcodeApp* barcode_app);
+
+void remove_barcode(CreateView* create_view_object);
+
+void save_barcode(CreateView* create_view_object);
+
+void create_view_free_model(CreateView* create_view_object);
+
+void create_view_free(CreateView* create_view_object);
+
+View* create_get_view(CreateView* create_view_object);
diff --git a/applications/external/barcode_gen/views/message_view.c b/applications/external/barcode_gen/views/message_view.c
new file mode 100644
index 000000000..13e238381
--- /dev/null
+++ b/applications/external/barcode_gen/views/message_view.c
@@ -0,0 +1,72 @@
+#include "../barcode_app.h"
+#include "message_view.h"
+
+static void app_draw_callback(Canvas* canvas, void* ctx) {
+ furi_assert(ctx);
+
+ MessageViewModel* message_view_model = ctx;
+
+ canvas_clear(canvas);
+ if(message_view_model->message != NULL) {
+ canvas_draw_str_aligned(
+ canvas, 62, 30, AlignCenter, AlignCenter, message_view_model->message);
+ }
+
+ canvas_set_color(canvas, ColorBlack);
+ canvas_draw_box(canvas, 100, 52, 28, 12);
+ canvas_set_color(canvas, ColorWhite);
+ canvas_draw_str_aligned(canvas, 114, 58, AlignCenter, AlignCenter, "OK");
+}
+
+static bool app_input_callback(InputEvent* input_event, void* ctx) {
+ furi_assert(ctx);
+
+ MessageView* message_view_object = ctx;
+
+ if(input_event->key == InputKeyBack) {
+ view_dispatcher_switch_to_view(
+ message_view_object->barcode_app->view_dispatcher, MainMenuView);
+ }
+ if(input_event->type == InputTypeShort) {
+ if(input_event->key == InputKeyOk) {
+ view_dispatcher_switch_to_view(
+ message_view_object->barcode_app->view_dispatcher, MainMenuView);
+ }
+ }
+
+ return true;
+}
+
+MessageView* message_view_allocate(BarcodeApp* barcode_app) {
+ furi_assert(barcode_app);
+
+ MessageView* message_view_object = malloc(sizeof(MessageView));
+
+ message_view_object->view = view_alloc();
+ message_view_object->barcode_app = barcode_app;
+
+ view_set_context(message_view_object->view, message_view_object);
+ view_allocate_model(message_view_object->view, ViewModelTypeLocking, sizeof(MessageViewModel));
+ view_set_draw_callback(message_view_object->view, app_draw_callback);
+ view_set_input_callback(message_view_object->view, app_input_callback);
+
+ return message_view_object;
+}
+
+void message_view_free_model(MessageView* message_view_object) {
+ with_view_model(
+ message_view_object->view, MessageViewModel * model, { UNUSED(model); }, true);
+}
+
+void message_view_free(MessageView* message_view_object) {
+ furi_assert(message_view_object);
+
+ message_view_free_model(message_view_object);
+ view_free(message_view_object->view);
+ free(message_view_object);
+}
+
+View* message_get_view(MessageView* message_view_object) {
+ furi_assert(message_view_object);
+ return message_view_object->view;
+}
diff --git a/applications/external/barcode_gen/views/message_view.h b/applications/external/barcode_gen/views/message_view.h
new file mode 100644
index 000000000..33acc3d0c
--- /dev/null
+++ b/applications/external/barcode_gen/views/message_view.h
@@ -0,0 +1,22 @@
+#pragma once
+
+#include
+
+typedef struct BarcodeApp BarcodeApp;
+
+typedef struct {
+ View* view;
+ BarcodeApp* barcode_app;
+} MessageView;
+
+typedef struct {
+ const char* message;
+} MessageViewModel;
+
+MessageView* message_view_allocate(BarcodeApp* barcode_app);
+
+void message_view_free_model(MessageView* message_view_object);
+
+void message_view_free(MessageView* message_view_object);
+
+View* message_get_view(MessageView* message_view_object);
diff --git a/applications/plugins/blackjack/application.fam b/applications/external/blackjack/application.fam
similarity index 90%
rename from applications/plugins/blackjack/application.fam
rename to applications/external/blackjack/application.fam
index 6ca8add37..04b214e3c 100644
--- a/applications/plugins/blackjack/application.fam
+++ b/applications/external/blackjack/application.fam
@@ -3,7 +3,6 @@ App(
name="BlackJack",
apptype=FlipperAppType.EXTERNAL,
entry_point="blackjack_app",
- cdefines=["APP_BLACKJACK"],
requires=["gui", "storage", "canvas"],
stack_size=2 * 1024,
order=30,
diff --git a/applications/plugins/blackjack/assets/blackjack.png b/applications/external/blackjack/assets/blackjack.png
similarity index 100%
rename from applications/plugins/blackjack/assets/blackjack.png
rename to applications/external/blackjack/assets/blackjack.png
diff --git a/applications/plugins/blackjack/assets/card_graphics.png b/applications/external/blackjack/assets/card_graphics.png
similarity index 100%
rename from applications/plugins/blackjack/assets/card_graphics.png
rename to applications/external/blackjack/assets/card_graphics.png
diff --git a/applications/plugins/blackjack/assets/endscreen.png b/applications/external/blackjack/assets/endscreen.png
similarity index 100%
rename from applications/plugins/blackjack/assets/endscreen.png
rename to applications/external/blackjack/assets/endscreen.png
diff --git a/applications/plugins/blackjack/blackjack.c b/applications/external/blackjack/blackjack.c
similarity index 100%
rename from applications/plugins/blackjack/blackjack.c
rename to applications/external/blackjack/blackjack.c
diff --git a/applications/plugins/blackjack/blackjack_10px.png b/applications/external/blackjack/blackjack_10px.png
similarity index 100%
rename from applications/plugins/blackjack/blackjack_10px.png
rename to applications/external/blackjack/blackjack_10px.png
diff --git a/applications/plugins/blackjack/common/card.c b/applications/external/blackjack/common/card.c
similarity index 100%
rename from applications/plugins/blackjack/common/card.c
rename to applications/external/blackjack/common/card.c
diff --git a/applications/plugins/blackjack/common/card.h b/applications/external/blackjack/common/card.h
similarity index 100%
rename from applications/plugins/blackjack/common/card.h
rename to applications/external/blackjack/common/card.h
diff --git a/applications/plugins/blackjack/common/dml.c b/applications/external/blackjack/common/dml.c
similarity index 100%
rename from applications/plugins/blackjack/common/dml.c
rename to applications/external/blackjack/common/dml.c
diff --git a/applications/plugins/blackjack/common/dml.h b/applications/external/blackjack/common/dml.h
similarity index 100%
rename from applications/plugins/blackjack/common/dml.h
rename to applications/external/blackjack/common/dml.h
diff --git a/applications/plugins/blackjack/common/menu.c b/applications/external/blackjack/common/menu.c
similarity index 100%
rename from applications/plugins/blackjack/common/menu.c
rename to applications/external/blackjack/common/menu.c
diff --git a/applications/plugins/blackjack/common/menu.h b/applications/external/blackjack/common/menu.h
similarity index 100%
rename from applications/plugins/blackjack/common/menu.h
rename to applications/external/blackjack/common/menu.h
diff --git a/applications/plugins/blackjack/common/queue.c b/applications/external/blackjack/common/queue.c
similarity index 100%
rename from applications/plugins/blackjack/common/queue.c
rename to applications/external/blackjack/common/queue.c
diff --git a/applications/plugins/blackjack/common/queue.h b/applications/external/blackjack/common/queue.h
similarity index 100%
rename from applications/plugins/blackjack/common/queue.h
rename to applications/external/blackjack/common/queue.h
diff --git a/applications/plugins/blackjack/common/ui.c b/applications/external/blackjack/common/ui.c
similarity index 100%
rename from applications/plugins/blackjack/common/ui.c
rename to applications/external/blackjack/common/ui.c
diff --git a/applications/plugins/blackjack/common/ui.h b/applications/external/blackjack/common/ui.h
similarity index 100%
rename from applications/plugins/blackjack/common/ui.h
rename to applications/external/blackjack/common/ui.h
diff --git a/applications/plugins/blackjack/defines.h b/applications/external/blackjack/defines.h
similarity index 100%
rename from applications/plugins/blackjack/defines.h
rename to applications/external/blackjack/defines.h
diff --git a/applications/plugins/blackjack/ui.c b/applications/external/blackjack/ui.c
similarity index 100%
rename from applications/plugins/blackjack/ui.c
rename to applications/external/blackjack/ui.c
diff --git a/applications/plugins/blackjack/ui.h b/applications/external/blackjack/ui.h
similarity index 100%
rename from applications/plugins/blackjack/ui.h
rename to applications/external/blackjack/ui.h
diff --git a/applications/plugins/blackjack/util.c b/applications/external/blackjack/util.c
similarity index 100%
rename from applications/plugins/blackjack/util.c
rename to applications/external/blackjack/util.c
diff --git a/applications/plugins/blackjack/util.h b/applications/external/blackjack/util.h
similarity index 100%
rename from applications/plugins/blackjack/util.h
rename to applications/external/blackjack/util.h
diff --git a/applications/plugins/bpmtapper/LICENSE b/applications/external/bpmtapper/LICENSE
similarity index 100%
rename from applications/plugins/bpmtapper/LICENSE
rename to applications/external/bpmtapper/LICENSE
diff --git a/applications/plugins/bpmtapper/application.fam b/applications/external/bpmtapper/application.fam
similarity index 95%
rename from applications/plugins/bpmtapper/application.fam
rename to applications/external/bpmtapper/application.fam
index b6ad315b6..93c4179c5 100644
--- a/applications/plugins/bpmtapper/application.fam
+++ b/applications/external/bpmtapper/application.fam
@@ -7,7 +7,7 @@ App(
requires=["gui"],
stack_size=2 * 1024,
fap_icon="bpm_10px.png",
- fap_icon_assets="icons",
fap_category="Music",
- order=35,
+ fap_icon_assets="icons",
+ order=15,
)
diff --git a/applications/plugins/bpmtapper/bpm.c b/applications/external/bpmtapper/bpm.c
similarity index 100%
rename from applications/plugins/bpmtapper/bpm.c
rename to applications/external/bpmtapper/bpm.c
diff --git a/applications/plugins/bpmtapper/bpm_10px.png b/applications/external/bpmtapper/bpm_10px.png
similarity index 100%
rename from applications/plugins/bpmtapper/bpm_10px.png
rename to applications/external/bpmtapper/bpm_10px.png
diff --git a/applications/plugins/bpmtapper/icons/DolphinCommon_56x48.png b/applications/external/bpmtapper/icons/DolphinCommon_56x48.png
similarity index 100%
rename from applications/plugins/bpmtapper/icons/DolphinCommon_56x48.png
rename to applications/external/bpmtapper/icons/DolphinCommon_56x48.png
diff --git a/applications/plugins/bpmtapper/img/screenshot.png b/applications/external/bpmtapper/img/screenshot.png
similarity index 100%
rename from applications/plugins/bpmtapper/img/screenshot.png
rename to applications/external/bpmtapper/img/screenshot.png
diff --git a/applications/plugins/brainfuck/application.fam b/applications/external/brainfuck/application.fam
similarity index 100%
rename from applications/plugins/brainfuck/application.fam
rename to applications/external/brainfuck/application.fam
diff --git a/applications/plugins/brainfuck/bfico.png b/applications/external/brainfuck/bfico.png
similarity index 100%
rename from applications/plugins/brainfuck/bfico.png
rename to applications/external/brainfuck/bfico.png
diff --git a/applications/plugins/brainfuck/brainfuck.c b/applications/external/brainfuck/brainfuck.c
similarity index 100%
rename from applications/plugins/brainfuck/brainfuck.c
rename to applications/external/brainfuck/brainfuck.c
diff --git a/applications/plugins/brainfuck/brainfuck.h b/applications/external/brainfuck/brainfuck.h
similarity index 100%
rename from applications/plugins/brainfuck/brainfuck.h
rename to applications/external/brainfuck/brainfuck.h
diff --git a/applications/plugins/brainfuck/brainfuck_i.h b/applications/external/brainfuck/brainfuck_i.h
similarity index 100%
rename from applications/plugins/brainfuck/brainfuck_i.h
rename to applications/external/brainfuck/brainfuck_i.h
diff --git a/applications/plugins/brainfuck/icons/ButtonRightSmall_3x5.png b/applications/external/brainfuck/icons/ButtonRightSmall_3x5.png
similarity index 100%
rename from applications/plugins/brainfuck/icons/ButtonRightSmall_3x5.png
rename to applications/external/brainfuck/icons/ButtonRightSmall_3x5.png
diff --git a/applications/plugins/brainfuck/icons/KeyBackspaceSelected_24x11.png b/applications/external/brainfuck/icons/KeyBackspaceSelected_24x11.png
similarity index 100%
rename from applications/plugins/brainfuck/icons/KeyBackspaceSelected_24x11.png
rename to applications/external/brainfuck/icons/KeyBackspaceSelected_24x11.png
diff --git a/applications/plugins/brainfuck/icons/KeyBackspace_24x11.png b/applications/external/brainfuck/icons/KeyBackspace_24x11.png
similarity index 100%
rename from applications/plugins/brainfuck/icons/KeyBackspace_24x11.png
rename to applications/external/brainfuck/icons/KeyBackspace_24x11.png
diff --git a/applications/plugins/brainfuck/icons/KeyInputSelected_30x11.png b/applications/external/brainfuck/icons/KeyInputSelected_30x11.png
similarity index 100%
rename from applications/plugins/brainfuck/icons/KeyInputSelected_30x11.png
rename to applications/external/brainfuck/icons/KeyInputSelected_30x11.png
diff --git a/applications/plugins/brainfuck/icons/KeyInput_30x11.png b/applications/external/brainfuck/icons/KeyInput_30x11.png
similarity index 100%
rename from applications/plugins/brainfuck/icons/KeyInput_30x11.png
rename to applications/external/brainfuck/icons/KeyInput_30x11.png
diff --git a/applications/plugins/brainfuck/icons/KeyRunSelected_24x11.png b/applications/external/brainfuck/icons/KeyRunSelected_24x11.png
similarity index 100%
rename from applications/plugins/brainfuck/icons/KeyRunSelected_24x11.png
rename to applications/external/brainfuck/icons/KeyRunSelected_24x11.png
diff --git a/applications/plugins/brainfuck/icons/KeyRun_24x11.png b/applications/external/brainfuck/icons/KeyRun_24x11.png
similarity index 100%
rename from applications/plugins/brainfuck/icons/KeyRun_24x11.png
rename to applications/external/brainfuck/icons/KeyRun_24x11.png
diff --git a/applications/plugins/brainfuck/icons/KeySaveSelected_24x11.png b/applications/external/brainfuck/icons/KeySaveSelected_24x11.png
similarity index 100%
rename from applications/plugins/brainfuck/icons/KeySaveSelected_24x11.png
rename to applications/external/brainfuck/icons/KeySaveSelected_24x11.png
diff --git a/applications/plugins/brainfuck/icons/KeySave_24x11.png b/applications/external/brainfuck/icons/KeySave_24x11.png
similarity index 100%
rename from applications/plugins/brainfuck/icons/KeySave_24x11.png
rename to applications/external/brainfuck/icons/KeySave_24x11.png
diff --git a/applications/plugins/brainfuck/icons/bfico.png b/applications/external/brainfuck/icons/bfico.png
similarity index 100%
rename from applications/plugins/brainfuck/icons/bfico.png
rename to applications/external/brainfuck/icons/bfico.png
diff --git a/applications/plugins/brainfuck/scenes/brainfuck_scene.c b/applications/external/brainfuck/scenes/brainfuck_scene.c
similarity index 100%
rename from applications/plugins/brainfuck/scenes/brainfuck_scene.c
rename to applications/external/brainfuck/scenes/brainfuck_scene.c
diff --git a/applications/plugins/brainfuck/scenes/brainfuck_scene.h b/applications/external/brainfuck/scenes/brainfuck_scene.h
similarity index 100%
rename from applications/plugins/brainfuck/scenes/brainfuck_scene.h
rename to applications/external/brainfuck/scenes/brainfuck_scene.h
diff --git a/applications/plugins/brainfuck/scenes/brainfuck_scene_config.h b/applications/external/brainfuck/scenes/brainfuck_scene_config.h
similarity index 100%
rename from applications/plugins/brainfuck/scenes/brainfuck_scene_config.h
rename to applications/external/brainfuck/scenes/brainfuck_scene_config.h
diff --git a/applications/plugins/brainfuck/scenes/brainfuck_scene_dev.c b/applications/external/brainfuck/scenes/brainfuck_scene_dev.c
similarity index 100%
rename from applications/plugins/brainfuck/scenes/brainfuck_scene_dev.c
rename to applications/external/brainfuck/scenes/brainfuck_scene_dev.c
diff --git a/applications/plugins/brainfuck/scenes/brainfuck_scene_exec.c b/applications/external/brainfuck/scenes/brainfuck_scene_exec.c
similarity index 100%
rename from applications/plugins/brainfuck/scenes/brainfuck_scene_exec.c
rename to applications/external/brainfuck/scenes/brainfuck_scene_exec.c
diff --git a/applications/plugins/brainfuck/scenes/brainfuck_scene_file_create.c b/applications/external/brainfuck/scenes/brainfuck_scene_file_create.c
similarity index 100%
rename from applications/plugins/brainfuck/scenes/brainfuck_scene_file_create.c
rename to applications/external/brainfuck/scenes/brainfuck_scene_file_create.c
diff --git a/applications/plugins/brainfuck/scenes/brainfuck_scene_file_select.c b/applications/external/brainfuck/scenes/brainfuck_scene_file_select.c
similarity index 100%
rename from applications/plugins/brainfuck/scenes/brainfuck_scene_file_select.c
rename to applications/external/brainfuck/scenes/brainfuck_scene_file_select.c
diff --git a/applications/plugins/brainfuck/scenes/brainfuck_scene_set_input.c b/applications/external/brainfuck/scenes/brainfuck_scene_set_input.c
similarity index 100%
rename from applications/plugins/brainfuck/scenes/brainfuck_scene_set_input.c
rename to applications/external/brainfuck/scenes/brainfuck_scene_set_input.c
diff --git a/applications/plugins/brainfuck/scenes/brainfuck_scene_start.c b/applications/external/brainfuck/scenes/brainfuck_scene_start.c
similarity index 100%
rename from applications/plugins/brainfuck/scenes/brainfuck_scene_start.c
rename to applications/external/brainfuck/scenes/brainfuck_scene_start.c
diff --git a/applications/plugins/brainfuck/views/bf_dev_env.c b/applications/external/brainfuck/views/bf_dev_env.c
similarity index 100%
rename from applications/plugins/brainfuck/views/bf_dev_env.c
rename to applications/external/brainfuck/views/bf_dev_env.c
diff --git a/applications/plugins/brainfuck/views/bf_dev_env.h b/applications/external/brainfuck/views/bf_dev_env.h
similarity index 100%
rename from applications/plugins/brainfuck/views/bf_dev_env.h
rename to applications/external/brainfuck/views/bf_dev_env.h
diff --git a/applications/plugins/brainfuck/worker.c b/applications/external/brainfuck/worker.c
similarity index 100%
rename from applications/plugins/brainfuck/worker.c
rename to applications/external/brainfuck/worker.c
diff --git a/applications/plugins/brainfuck/worker.h b/applications/external/brainfuck/worker.h
similarity index 100%
rename from applications/plugins/brainfuck/worker.h
rename to applications/external/brainfuck/worker.h
diff --git a/applications/plugins/caesarcipher/LICENSE b/applications/external/caesarcipher/LICENSE
similarity index 100%
rename from applications/plugins/caesarcipher/LICENSE
rename to applications/external/caesarcipher/LICENSE
diff --git a/applications/plugins/caesarcipher/application.fam b/applications/external/caesarcipher/application.fam
similarity index 88%
rename from applications/plugins/caesarcipher/application.fam
rename to applications/external/caesarcipher/application.fam
index 652585de2..4f438d2b3 100644
--- a/applications/plugins/caesarcipher/application.fam
+++ b/applications/external/caesarcipher/application.fam
@@ -1,7 +1,7 @@
App(
appid="Caesar_Cipher",
name="Caesar Cipher",
- apptype=FlipperAppType.PLUGIN,
+ apptype=FlipperAppType.EXTERNAL,
entry_point="caesar_cipher_app",
cdefines=["APP_CAESAR_CIPHER"],
requires=[
diff --git a/applications/plugins/caesarcipher/caesar_cipher.c b/applications/external/caesarcipher/caesar_cipher.c
similarity index 100%
rename from applications/plugins/caesarcipher/caesar_cipher.c
rename to applications/external/caesarcipher/caesar_cipher.c
diff --git a/applications/plugins/caesarcipher/caesar_cipher_icon.png b/applications/external/caesarcipher/caesar_cipher_icon.png
similarity index 100%
rename from applications/plugins/caesarcipher/caesar_cipher_icon.png
rename to applications/external/caesarcipher/caesar_cipher_icon.png
diff --git a/applications/plugins/calculator/application.fam b/applications/external/calculator/application.fam
similarity index 100%
rename from applications/plugins/calculator/application.fam
rename to applications/external/calculator/application.fam
diff --git a/applications/plugins/calculator/calcIcon.png b/applications/external/calculator/calcIcon.png
similarity index 100%
rename from applications/plugins/calculator/calcIcon.png
rename to applications/external/calculator/calcIcon.png
diff --git a/applications/plugins/calculator/calculator.c b/applications/external/calculator/calculator.c
similarity index 100%
rename from applications/plugins/calculator/calculator.c
rename to applications/external/calculator/calculator.c
diff --git a/applications/plugins/calculator/tinyexpr.c b/applications/external/calculator/tinyexpr.c
similarity index 100%
rename from applications/plugins/calculator/tinyexpr.c
rename to applications/external/calculator/tinyexpr.c
diff --git a/applications/plugins/calculator/tinyexpr.h b/applications/external/calculator/tinyexpr.h
similarity index 100%
rename from applications/plugins/calculator/tinyexpr.h
rename to applications/external/calculator/tinyexpr.h
diff --git a/applications/plugins/cli_bridge/LICENSE b/applications/external/cli_bridge/LICENSE
similarity index 100%
rename from applications/plugins/cli_bridge/LICENSE
rename to applications/external/cli_bridge/LICENSE
diff --git a/applications/plugins/cli_bridge/application.fam b/applications/external/cli_bridge/application.fam
similarity index 100%
rename from applications/plugins/cli_bridge/application.fam
rename to applications/external/cli_bridge/application.fam
diff --git a/applications/plugins/cli_bridge/cli_control.c b/applications/external/cli_bridge/cli_control.c
similarity index 100%
rename from applications/plugins/cli_bridge/cli_control.c
rename to applications/external/cli_bridge/cli_control.c
diff --git a/applications/plugins/cli_bridge/cli_control.h b/applications/external/cli_bridge/cli_control.h
similarity index 100%
rename from applications/plugins/cli_bridge/cli_control.h
rename to applications/external/cli_bridge/cli_control.h
diff --git a/applications/plugins/cli_bridge/cligui.png b/applications/external/cli_bridge/cligui.png
similarity index 100%
rename from applications/plugins/cli_bridge/cligui.png
rename to applications/external/cli_bridge/cligui.png
diff --git a/applications/plugins/cli_bridge/cligui_main.c b/applications/external/cli_bridge/cligui_main.c
similarity index 100%
rename from applications/plugins/cli_bridge/cligui_main.c
rename to applications/external/cli_bridge/cligui_main.c
diff --git a/applications/plugins/cli_bridge/cligui_main_i.h b/applications/external/cli_bridge/cligui_main_i.h
similarity index 100%
rename from applications/plugins/cli_bridge/cligui_main_i.h
rename to applications/external/cli_bridge/cligui_main_i.h
diff --git a/applications/plugins/cli_bridge/console_output.c b/applications/external/cli_bridge/console_output.c
similarity index 100%
rename from applications/plugins/cli_bridge/console_output.c
rename to applications/external/cli_bridge/console_output.c
diff --git a/applications/plugins/cli_bridge/console_output.h b/applications/external/cli_bridge/console_output.h
similarity index 100%
rename from applications/plugins/cli_bridge/console_output.h
rename to applications/external/cli_bridge/console_output.h
diff --git a/applications/plugins/cli_bridge/internal_defs.h b/applications/external/cli_bridge/internal_defs.h
similarity index 100%
rename from applications/plugins/cli_bridge/internal_defs.h
rename to applications/external/cli_bridge/internal_defs.h
diff --git a/applications/plugins/cli_bridge/text_input.c b/applications/external/cli_bridge/text_input.c
similarity index 100%
rename from applications/plugins/cli_bridge/text_input.c
rename to applications/external/cli_bridge/text_input.c
diff --git a/applications/plugins/cli_bridge/text_input.h b/applications/external/cli_bridge/text_input.h
similarity index 100%
rename from applications/plugins/cli_bridge/text_input.h
rename to applications/external/cli_bridge/text_input.h
diff --git a/applications/plugins/cntdown_timer/app.c b/applications/external/cntdown_timer/app.c
similarity index 100%
rename from applications/plugins/cntdown_timer/app.c
rename to applications/external/cntdown_timer/app.c
diff --git a/applications/plugins/cntdown_timer/app.h b/applications/external/cntdown_timer/app.h
similarity index 100%
rename from applications/plugins/cntdown_timer/app.h
rename to applications/external/cntdown_timer/app.h
diff --git a/applications/plugins/cntdown_timer/application.fam b/applications/external/cntdown_timer/application.fam
similarity index 100%
rename from applications/plugins/cntdown_timer/application.fam
rename to applications/external/cntdown_timer/application.fam
diff --git a/applications/plugins/cntdown_timer/cntdown_timer.png b/applications/external/cntdown_timer/cntdown_timer.png
similarity index 100%
rename from applications/plugins/cntdown_timer/cntdown_timer.png
rename to applications/external/cntdown_timer/cntdown_timer.png
diff --git a/applications/plugins/cntdown_timer/utils/utils.c b/applications/external/cntdown_timer/utils/utils.c
similarity index 100%
rename from applications/plugins/cntdown_timer/utils/utils.c
rename to applications/external/cntdown_timer/utils/utils.c
diff --git a/applications/plugins/cntdown_timer/utils/utils.h b/applications/external/cntdown_timer/utils/utils.h
similarity index 100%
rename from applications/plugins/cntdown_timer/utils/utils.h
rename to applications/external/cntdown_timer/utils/utils.h
diff --git a/applications/plugins/cntdown_timer/views/countdown_view.c b/applications/external/cntdown_timer/views/countdown_view.c
similarity index 100%
rename from applications/plugins/cntdown_timer/views/countdown_view.c
rename to applications/external/cntdown_timer/views/countdown_view.c
diff --git a/applications/plugins/cntdown_timer/views/countdown_view.h b/applications/external/cntdown_timer/views/countdown_view.h
similarity index 100%
rename from applications/plugins/cntdown_timer/views/countdown_view.h
rename to applications/external/cntdown_timer/views/countdown_view.h
diff --git a/applications/plugins/counter/application.fam b/applications/external/counter/application.fam
similarity index 84%
rename from applications/plugins/counter/application.fam
rename to applications/external/counter/application.fam
index 8f0147b09..b287965bf 100644
--- a/applications/plugins/counter/application.fam
+++ b/applications/external/counter/application.fam
@@ -1,7 +1,7 @@
App(
appid="counter",
name="Counter",
- apptype=FlipperAppType.PLUGIN,
+ apptype=FlipperAppType.EXTERNAL,
entry_point="counterapp",
requires=[
"gui",
diff --git a/applications/plugins/counter/counter.c b/applications/external/counter/counter.c
similarity index 100%
rename from applications/plugins/counter/counter.c
rename to applications/external/counter/counter.c
diff --git a/applications/plugins/counter/icons/counter_icon.png b/applications/external/counter/icons/counter_icon.png
similarity index 100%
rename from applications/plugins/counter/icons/counter_icon.png
rename to applications/external/counter/icons/counter_icon.png
diff --git a/applications/plugins/dap_link/application.fam b/applications/external/dap_link/application.fam
similarity index 92%
rename from applications/plugins/dap_link/application.fam
rename to applications/external/dap_link/application.fam
index 4dd2e531e..321b9e102 100644
--- a/applications/plugins/dap_link/application.fam
+++ b/applications/external/dap_link/application.fam
@@ -1,7 +1,7 @@
App(
appid="dap_link",
name="[GPIO] DAP Link",
- apptype=FlipperAppType.PLUGIN,
+ apptype=FlipperAppType.EXTERNAL,
entry_point="dap_link_app",
requires=[
"gui",
diff --git a/applications/plugins/dap_link/dap_config.h b/applications/external/dap_link/dap_config.h
similarity index 100%
rename from applications/plugins/dap_link/dap_config.h
rename to applications/external/dap_link/dap_config.h
diff --git a/applications/plugins/dap_link/dap_link.c b/applications/external/dap_link/dap_link.c
similarity index 100%
rename from applications/plugins/dap_link/dap_link.c
rename to applications/external/dap_link/dap_link.c
diff --git a/applications/plugins/dap_link/dap_link.h b/applications/external/dap_link/dap_link.h
similarity index 100%
rename from applications/plugins/dap_link/dap_link.h
rename to applications/external/dap_link/dap_link.h
diff --git a/applications/plugins/dap_link/dap_link.png b/applications/external/dap_link/dap_link.png
similarity index 100%
rename from applications/plugins/dap_link/dap_link.png
rename to applications/external/dap_link/dap_link.png
diff --git a/applications/plugins/dap_link/gui/dap_gui.c b/applications/external/dap_link/gui/dap_gui.c
similarity index 100%
rename from applications/plugins/dap_link/gui/dap_gui.c
rename to applications/external/dap_link/gui/dap_gui.c
diff --git a/applications/plugins/dap_link/gui/dap_gui.h b/applications/external/dap_link/gui/dap_gui.h
similarity index 100%
rename from applications/plugins/dap_link/gui/dap_gui.h
rename to applications/external/dap_link/gui/dap_gui.h
diff --git a/applications/plugins/dap_link/gui/dap_gui_custom_event.h b/applications/external/dap_link/gui/dap_gui_custom_event.h
similarity index 100%
rename from applications/plugins/dap_link/gui/dap_gui_custom_event.h
rename to applications/external/dap_link/gui/dap_gui_custom_event.h
diff --git a/applications/plugins/dap_link/gui/dap_gui_i.h b/applications/external/dap_link/gui/dap_gui_i.h
similarity index 100%
rename from applications/plugins/dap_link/gui/dap_gui_i.h
rename to applications/external/dap_link/gui/dap_gui_i.h
diff --git a/applications/plugins/dap_link/gui/scenes/config/dap_scene.c b/applications/external/dap_link/gui/scenes/config/dap_scene.c
similarity index 100%
rename from applications/plugins/dap_link/gui/scenes/config/dap_scene.c
rename to applications/external/dap_link/gui/scenes/config/dap_scene.c
diff --git a/applications/plugins/dap_link/gui/scenes/config/dap_scene.h b/applications/external/dap_link/gui/scenes/config/dap_scene.h
similarity index 100%
rename from applications/plugins/dap_link/gui/scenes/config/dap_scene.h
rename to applications/external/dap_link/gui/scenes/config/dap_scene.h
diff --git a/applications/plugins/dap_link/gui/scenes/config/dap_scene_config.h b/applications/external/dap_link/gui/scenes/config/dap_scene_config.h
similarity index 100%
rename from applications/plugins/dap_link/gui/scenes/config/dap_scene_config.h
rename to applications/external/dap_link/gui/scenes/config/dap_scene_config.h
diff --git a/applications/plugins/dap_link/gui/scenes/dap_scene_about.c b/applications/external/dap_link/gui/scenes/dap_scene_about.c
similarity index 100%
rename from applications/plugins/dap_link/gui/scenes/dap_scene_about.c
rename to applications/external/dap_link/gui/scenes/dap_scene_about.c
diff --git a/applications/plugins/dap_link/gui/scenes/dap_scene_config.c b/applications/external/dap_link/gui/scenes/dap_scene_config.c
similarity index 100%
rename from applications/plugins/dap_link/gui/scenes/dap_scene_config.c
rename to applications/external/dap_link/gui/scenes/dap_scene_config.c
diff --git a/applications/plugins/dap_link/gui/scenes/dap_scene_help.c b/applications/external/dap_link/gui/scenes/dap_scene_help.c
similarity index 100%
rename from applications/plugins/dap_link/gui/scenes/dap_scene_help.c
rename to applications/external/dap_link/gui/scenes/dap_scene_help.c
diff --git a/applications/plugins/dap_link/gui/scenes/dap_scene_main.c b/applications/external/dap_link/gui/scenes/dap_scene_main.c
similarity index 100%
rename from applications/plugins/dap_link/gui/scenes/dap_scene_main.c
rename to applications/external/dap_link/gui/scenes/dap_scene_main.c
diff --git a/applications/plugins/dap_link/gui/views/dap_main_view.c b/applications/external/dap_link/gui/views/dap_main_view.c
similarity index 100%
rename from applications/plugins/dap_link/gui/views/dap_main_view.c
rename to applications/external/dap_link/gui/views/dap_main_view.c
diff --git a/applications/plugins/dap_link/gui/views/dap_main_view.h b/applications/external/dap_link/gui/views/dap_main_view.h
similarity index 100%
rename from applications/plugins/dap_link/gui/views/dap_main_view.h
rename to applications/external/dap_link/gui/views/dap_main_view.h
diff --git a/applications/plugins/dap_link/icons/ActiveConnection_50x64.png b/applications/external/dap_link/icons/ActiveConnection_50x64.png
similarity index 100%
rename from applications/plugins/dap_link/icons/ActiveConnection_50x64.png
rename to applications/external/dap_link/icons/ActiveConnection_50x64.png
diff --git a/applications/plugins/dap_link/icons/ArrowDownEmpty_12x18.png b/applications/external/dap_link/icons/ArrowDownEmpty_12x18.png
similarity index 100%
rename from applications/plugins/dap_link/icons/ArrowDownEmpty_12x18.png
rename to applications/external/dap_link/icons/ArrowDownEmpty_12x18.png
diff --git a/applications/plugins/dap_link/icons/ArrowDownFilled_12x18.png b/applications/external/dap_link/icons/ArrowDownFilled_12x18.png
similarity index 100%
rename from applications/plugins/dap_link/icons/ArrowDownFilled_12x18.png
rename to applications/external/dap_link/icons/ArrowDownFilled_12x18.png
diff --git a/applications/plugins/dap_link/icons/ArrowUpEmpty_12x18.png b/applications/external/dap_link/icons/ArrowUpEmpty_12x18.png
similarity index 100%
rename from applications/plugins/dap_link/icons/ArrowUpEmpty_12x18.png
rename to applications/external/dap_link/icons/ArrowUpEmpty_12x18.png
diff --git a/applications/plugins/dap_link/icons/ArrowUpFilled_12x18.png b/applications/external/dap_link/icons/ArrowUpFilled_12x18.png
similarity index 100%
rename from applications/plugins/dap_link/icons/ArrowUpFilled_12x18.png
rename to applications/external/dap_link/icons/ArrowUpFilled_12x18.png
diff --git a/applications/plugins/dap_link/lib/free-dap b/applications/external/dap_link/lib/free-dap
similarity index 100%
rename from applications/plugins/dap_link/lib/free-dap
rename to applications/external/dap_link/lib/free-dap
diff --git a/applications/plugins/dap_link/usb/dap_v2_usb.c b/applications/external/dap_link/usb/dap_v2_usb.c
similarity index 100%
rename from applications/plugins/dap_link/usb/dap_v2_usb.c
rename to applications/external/dap_link/usb/dap_v2_usb.c
diff --git a/applications/plugins/dap_link/usb/dap_v2_usb.h b/applications/external/dap_link/usb/dap_v2_usb.h
similarity index 100%
rename from applications/plugins/dap_link/usb/dap_v2_usb.h
rename to applications/external/dap_link/usb/dap_v2_usb.h
diff --git a/applications/plugins/dap_link/usb/usb_winusb.h b/applications/external/dap_link/usb/usb_winusb.h
similarity index 100%
rename from applications/plugins/dap_link/usb/usb_winusb.h
rename to applications/external/dap_link/usb/usb_winusb.h
diff --git a/applications/plugins/doom/application.fam b/applications/external/doom/application.fam
similarity index 89%
rename from applications/plugins/doom/application.fam
rename to applications/external/doom/application.fam
index d9ae4d67d..4ff7b12f3 100644
--- a/applications/plugins/doom/application.fam
+++ b/applications/external/doom/application.fam
@@ -3,7 +3,6 @@ App(
name="DOOM",
apptype=FlipperAppType.EXTERNAL,
entry_point="doom_app",
- cdefines=["APP_DOOM_GAME"],
requires=[
"gui",
"music_player",
diff --git a/applications/plugins/doom/assets/door2.png b/applications/external/doom/assets/door2.png
similarity index 100%
rename from applications/plugins/doom/assets/door2.png
rename to applications/external/doom/assets/door2.png
diff --git a/applications/plugins/doom/assets/door_inv.png b/applications/external/doom/assets/door_inv.png
similarity index 100%
rename from applications/plugins/doom/assets/door_inv.png
rename to applications/external/doom/assets/door_inv.png
diff --git a/applications/plugins/doom/assets/fire_inv.png b/applications/external/doom/assets/fire_inv.png
similarity index 100%
rename from applications/plugins/doom/assets/fire_inv.png
rename to applications/external/doom/assets/fire_inv.png
diff --git a/applications/plugins/doom/assets/fireball_inv.png b/applications/external/doom/assets/fireball_inv.png
similarity index 100%
rename from applications/plugins/doom/assets/fireball_inv.png
rename to applications/external/doom/assets/fireball_inv.png
diff --git a/applications/plugins/doom/assets/fireball_mask_inv.png b/applications/external/doom/assets/fireball_mask_inv.png
similarity index 100%
rename from applications/plugins/doom/assets/fireball_mask_inv.png
rename to applications/external/doom/assets/fireball_mask_inv.png
diff --git a/applications/plugins/doom/assets/gradient_inv.png b/applications/external/doom/assets/gradient_inv.png
similarity index 100%
rename from applications/plugins/doom/assets/gradient_inv.png
rename to applications/external/doom/assets/gradient_inv.png
diff --git a/applications/plugins/doom/assets/gun_inv.png b/applications/external/doom/assets/gun_inv.png
similarity index 100%
rename from applications/plugins/doom/assets/gun_inv.png
rename to applications/external/doom/assets/gun_inv.png
diff --git a/applications/plugins/doom/assets/gun_mask_inv.png b/applications/external/doom/assets/gun_mask_inv.png
similarity index 100%
rename from applications/plugins/doom/assets/gun_mask_inv.png
rename to applications/external/doom/assets/gun_mask_inv.png
diff --git a/applications/plugins/doom/assets/imp_inv.png b/applications/external/doom/assets/imp_inv.png
similarity index 100%
rename from applications/plugins/doom/assets/imp_inv.png
rename to applications/external/doom/assets/imp_inv.png
diff --git a/applications/plugins/doom/assets/imp_mask_inv.png b/applications/external/doom/assets/imp_mask_inv.png
similarity index 100%
rename from applications/plugins/doom/assets/imp_mask_inv.png
rename to applications/external/doom/assets/imp_mask_inv.png
diff --git a/applications/plugins/doom/assets/item_inv.png b/applications/external/doom/assets/item_inv.png
similarity index 100%
rename from applications/plugins/doom/assets/item_inv.png
rename to applications/external/doom/assets/item_inv.png
diff --git a/applications/plugins/doom/assets/item_mask_inv.png b/applications/external/doom/assets/item_mask_inv.png
similarity index 100%
rename from applications/plugins/doom/assets/item_mask_inv.png
rename to applications/external/doom/assets/item_mask_inv.png
diff --git a/applications/plugins/doom/assets/logo_inv.png b/applications/external/doom/assets/logo_inv.png
similarity index 100%
rename from applications/plugins/doom/assets/logo_inv.png
rename to applications/external/doom/assets/logo_inv.png
diff --git a/applications/plugins/doom/assets/screenshot-imp2.jpg b/applications/external/doom/assets/screenshot-imp2.jpg
similarity index 100%
rename from applications/plugins/doom/assets/screenshot-imp2.jpg
rename to applications/external/doom/assets/screenshot-imp2.jpg
diff --git a/applications/plugins/doom/assets/screenshot-intro2.jpg b/applications/external/doom/assets/screenshot-intro2.jpg
similarity index 100%
rename from applications/plugins/doom/assets/screenshot-intro2.jpg
rename to applications/external/doom/assets/screenshot-intro2.jpg
diff --git a/applications/plugins/doom/assets/screenshot-medkit2.jpg b/applications/external/doom/assets/screenshot-medkit2.jpg
similarity index 100%
rename from applications/plugins/doom/assets/screenshot-medkit2.jpg
rename to applications/external/doom/assets/screenshot-medkit2.jpg
diff --git a/applications/plugins/doom/assets/screenshot-start2.jpg b/applications/external/doom/assets/screenshot-start2.jpg
similarity index 100%
rename from applications/plugins/doom/assets/screenshot-start2.jpg
rename to applications/external/doom/assets/screenshot-start2.jpg
diff --git a/applications/plugins/doom/assets/screenshot1.png b/applications/external/doom/assets/screenshot1.png
similarity index 100%
rename from applications/plugins/doom/assets/screenshot1.png
rename to applications/external/doom/assets/screenshot1.png
diff --git a/applications/plugins/doom/assets/screenshot2.png b/applications/external/doom/assets/screenshot2.png
similarity index 100%
rename from applications/plugins/doom/assets/screenshot2.png
rename to applications/external/doom/assets/screenshot2.png
diff --git a/applications/plugins/doom/assets/screenshot3.png b/applications/external/doom/assets/screenshot3.png
similarity index 100%
rename from applications/plugins/doom/assets/screenshot3.png
rename to applications/external/doom/assets/screenshot3.png
diff --git a/applications/plugins/doom/compiled/assets_icons.c b/applications/external/doom/compiled/assets_icons.c
similarity index 100%
rename from applications/plugins/doom/compiled/assets_icons.c
rename to applications/external/doom/compiled/assets_icons.c
diff --git a/applications/plugins/doom/compiled/assets_icons.h b/applications/external/doom/compiled/assets_icons.h
similarity index 100%
rename from applications/plugins/doom/compiled/assets_icons.h
rename to applications/external/doom/compiled/assets_icons.h
diff --git a/applications/plugins/doom/constants.h b/applications/external/doom/constants.h
similarity index 100%
rename from applications/plugins/doom/constants.h
rename to applications/external/doom/constants.h
diff --git a/applications/plugins/doom/display.h b/applications/external/doom/display.h
similarity index 100%
rename from applications/plugins/doom/display.h
rename to applications/external/doom/display.h
diff --git a/applications/plugins/doom/doom.c b/applications/external/doom/doom.c
similarity index 100%
rename from applications/plugins/doom/doom.c
rename to applications/external/doom/doom.c
diff --git a/applications/plugins/doom/doom_10px.png b/applications/external/doom/doom_10px.png
similarity index 100%
rename from applications/plugins/doom/doom_10px.png
rename to applications/external/doom/doom_10px.png
diff --git a/applications/plugins/doom/doom_music_player_worker.c b/applications/external/doom/doom_music_player_worker.c
similarity index 100%
rename from applications/plugins/doom/doom_music_player_worker.c
rename to applications/external/doom/doom_music_player_worker.c
diff --git a/applications/plugins/doom/doom_music_player_worker.h b/applications/external/doom/doom_music_player_worker.h
similarity index 100%
rename from applications/plugins/doom/doom_music_player_worker.h
rename to applications/external/doom/doom_music_player_worker.h
diff --git a/applications/plugins/doom/entities.c b/applications/external/doom/entities.c
similarity index 100%
rename from applications/plugins/doom/entities.c
rename to applications/external/doom/entities.c
diff --git a/applications/plugins/doom/entities.h b/applications/external/doom/entities.h
similarity index 100%
rename from applications/plugins/doom/entities.h
rename to applications/external/doom/entities.h
diff --git a/applications/plugins/doom/level.h b/applications/external/doom/level.h
similarity index 100%
rename from applications/plugins/doom/level.h
rename to applications/external/doom/level.h
diff --git a/applications/plugins/doom/sound.h b/applications/external/doom/sound.h
similarity index 100%
rename from applications/plugins/doom/sound.h
rename to applications/external/doom/sound.h
diff --git a/applications/plugins/doom/types.c b/applications/external/doom/types.c
similarity index 100%
rename from applications/plugins/doom/types.c
rename to applications/external/doom/types.c
diff --git a/applications/plugins/doom/types.h b/applications/external/doom/types.h
similarity index 100%
rename from applications/plugins/doom/types.h
rename to applications/external/doom/types.h
diff --git a/applications/plugins/dtmf_dolphin/LICENSE b/applications/external/dtmf_dolphin/LICENSE
similarity index 100%
rename from applications/plugins/dtmf_dolphin/LICENSE
rename to applications/external/dtmf_dolphin/LICENSE
diff --git a/applications/plugins/dtmf_dolphin/application.fam b/applications/external/dtmf_dolphin/application.fam
similarity index 90%
rename from applications/plugins/dtmf_dolphin/application.fam
rename to applications/external/dtmf_dolphin/application.fam
index 0727f5f52..460f6ded6 100644
--- a/applications/plugins/dtmf_dolphin/application.fam
+++ b/applications/external/dtmf_dolphin/application.fam
@@ -3,7 +3,6 @@ App(
name="DTMF Dolphin",
apptype=FlipperAppType.EXTERNAL,
entry_point="dtmf_dolphin_app",
- cdefines=["DTMF_DOLPHIN"],
requires=[
"storage",
"gui",
diff --git a/applications/plugins/dtmf_dolphin/dtmf_dolphin.c b/applications/external/dtmf_dolphin/dtmf_dolphin.c
similarity index 97%
rename from applications/plugins/dtmf_dolphin/dtmf_dolphin.c
rename to applications/external/dtmf_dolphin/dtmf_dolphin.c
index c1b10defa..732b22ef8 100644
--- a/applications/plugins/dtmf_dolphin/dtmf_dolphin.c
+++ b/applications/external/dtmf_dolphin/dtmf_dolphin.c
@@ -2,6 +2,7 @@
#include
#include
+#include
static bool dtmf_dolphin_app_custom_event_callback(void* context, uint32_t event) {
furi_assert(context);
@@ -82,8 +83,9 @@ int32_t dtmf_dolphin_app(void* p) {
UNUSED(p);
DTMFDolphinApp* app = app_alloc();
+ DOLPHIN_DEED(DolphinDeedPluginStart);
view_dispatcher_run(app->view_dispatcher);
app_free(app);
return 0;
-}
\ No newline at end of file
+}
diff --git a/applications/plugins/dtmf_dolphin/dtmf_dolphin_audio.c b/applications/external/dtmf_dolphin/dtmf_dolphin_audio.c
similarity index 100%
rename from applications/plugins/dtmf_dolphin/dtmf_dolphin_audio.c
rename to applications/external/dtmf_dolphin/dtmf_dolphin_audio.c
diff --git a/applications/plugins/dtmf_dolphin/dtmf_dolphin_audio.h b/applications/external/dtmf_dolphin/dtmf_dolphin_audio.h
similarity index 100%
rename from applications/plugins/dtmf_dolphin/dtmf_dolphin_audio.h
rename to applications/external/dtmf_dolphin/dtmf_dolphin_audio.h
diff --git a/applications/plugins/dtmf_dolphin/dtmf_dolphin_data.c b/applications/external/dtmf_dolphin/dtmf_dolphin_data.c
similarity index 100%
rename from applications/plugins/dtmf_dolphin/dtmf_dolphin_data.c
rename to applications/external/dtmf_dolphin/dtmf_dolphin_data.c
diff --git a/applications/plugins/dtmf_dolphin/dtmf_dolphin_data.h b/applications/external/dtmf_dolphin/dtmf_dolphin_data.h
similarity index 100%
rename from applications/plugins/dtmf_dolphin/dtmf_dolphin_data.h
rename to applications/external/dtmf_dolphin/dtmf_dolphin_data.h
diff --git a/applications/plugins/dtmf_dolphin/dtmf_dolphin_event.h b/applications/external/dtmf_dolphin/dtmf_dolphin_event.h
similarity index 100%
rename from applications/plugins/dtmf_dolphin/dtmf_dolphin_event.h
rename to applications/external/dtmf_dolphin/dtmf_dolphin_event.h
diff --git a/applications/plugins/dtmf_dolphin/dtmf_dolphin_hal.c b/applications/external/dtmf_dolphin/dtmf_dolphin_hal.c
similarity index 100%
rename from applications/plugins/dtmf_dolphin/dtmf_dolphin_hal.c
rename to applications/external/dtmf_dolphin/dtmf_dolphin_hal.c
diff --git a/applications/plugins/dtmf_dolphin/dtmf_dolphin_hal.h b/applications/external/dtmf_dolphin/dtmf_dolphin_hal.h
similarity index 100%
rename from applications/plugins/dtmf_dolphin/dtmf_dolphin_hal.h
rename to applications/external/dtmf_dolphin/dtmf_dolphin_hal.h
diff --git a/applications/plugins/dtmf_dolphin/dtmf_dolphin_i.h b/applications/external/dtmf_dolphin/dtmf_dolphin_i.h
similarity index 100%
rename from applications/plugins/dtmf_dolphin/dtmf_dolphin_i.h
rename to applications/external/dtmf_dolphin/dtmf_dolphin_i.h
diff --git a/applications/plugins/dtmf_dolphin/phone.png b/applications/external/dtmf_dolphin/phone.png
similarity index 100%
rename from applications/plugins/dtmf_dolphin/phone.png
rename to applications/external/dtmf_dolphin/phone.png
diff --git a/applications/plugins/dtmf_dolphin/scenes/dtmf_dolphin_scene.c b/applications/external/dtmf_dolphin/scenes/dtmf_dolphin_scene.c
similarity index 100%
rename from applications/plugins/dtmf_dolphin/scenes/dtmf_dolphin_scene.c
rename to applications/external/dtmf_dolphin/scenes/dtmf_dolphin_scene.c
diff --git a/applications/plugins/dtmf_dolphin/scenes/dtmf_dolphin_scene.h b/applications/external/dtmf_dolphin/scenes/dtmf_dolphin_scene.h
similarity index 100%
rename from applications/plugins/dtmf_dolphin/scenes/dtmf_dolphin_scene.h
rename to applications/external/dtmf_dolphin/scenes/dtmf_dolphin_scene.h
diff --git a/applications/plugins/dtmf_dolphin/scenes/dtmf_dolphin_scene_config.h b/applications/external/dtmf_dolphin/scenes/dtmf_dolphin_scene_config.h
similarity index 100%
rename from applications/plugins/dtmf_dolphin/scenes/dtmf_dolphin_scene_config.h
rename to applications/external/dtmf_dolphin/scenes/dtmf_dolphin_scene_config.h
diff --git a/applications/plugins/dtmf_dolphin/scenes/dtmf_dolphin_scene_dialer.c b/applications/external/dtmf_dolphin/scenes/dtmf_dolphin_scene_dialer.c
similarity index 100%
rename from applications/plugins/dtmf_dolphin/scenes/dtmf_dolphin_scene_dialer.c
rename to applications/external/dtmf_dolphin/scenes/dtmf_dolphin_scene_dialer.c
diff --git a/applications/plugins/dtmf_dolphin/scenes/dtmf_dolphin_scene_start.c b/applications/external/dtmf_dolphin/scenes/dtmf_dolphin_scene_start.c
similarity index 100%
rename from applications/plugins/dtmf_dolphin/scenes/dtmf_dolphin_scene_start.c
rename to applications/external/dtmf_dolphin/scenes/dtmf_dolphin_scene_start.c
diff --git a/applications/plugins/dtmf_dolphin/views/dtmf_dolphin_common.h b/applications/external/dtmf_dolphin/views/dtmf_dolphin_common.h
similarity index 100%
rename from applications/plugins/dtmf_dolphin/views/dtmf_dolphin_common.h
rename to applications/external/dtmf_dolphin/views/dtmf_dolphin_common.h
diff --git a/applications/plugins/dtmf_dolphin/views/dtmf_dolphin_dialer.c b/applications/external/dtmf_dolphin/views/dtmf_dolphin_dialer.c
similarity index 100%
rename from applications/plugins/dtmf_dolphin/views/dtmf_dolphin_dialer.c
rename to applications/external/dtmf_dolphin/views/dtmf_dolphin_dialer.c
diff --git a/applications/plugins/dtmf_dolphin/views/dtmf_dolphin_dialer.h b/applications/external/dtmf_dolphin/views/dtmf_dolphin_dialer.h
similarity index 100%
rename from applications/plugins/dtmf_dolphin/views/dtmf_dolphin_dialer.h
rename to applications/external/dtmf_dolphin/views/dtmf_dolphin_dialer.h
diff --git a/applications/plugins/esp8266_deauth/FlipperZeroWiFiDeauthModuleDefines.h b/applications/external/esp8266_deauth/FlipperZeroWiFiDeauthModuleDefines.h
similarity index 100%
rename from applications/plugins/esp8266_deauth/FlipperZeroWiFiDeauthModuleDefines.h
rename to applications/external/esp8266_deauth/FlipperZeroWiFiDeauthModuleDefines.h
diff --git a/applications/plugins/esp8266_deauth/application.fam b/applications/external/esp8266_deauth/application.fam
similarity index 87%
rename from applications/plugins/esp8266_deauth/application.fam
rename to applications/external/esp8266_deauth/application.fam
index b289bc88e..7718e1458 100644
--- a/applications/plugins/esp8266_deauth/application.fam
+++ b/applications/external/esp8266_deauth/application.fam
@@ -3,7 +3,6 @@ App(
name="[ESP8266] Deauther",
apptype=FlipperAppType.EXTERNAL,
entry_point="esp8266_deauth_app",
- cdefines=["APP_ESP8266_deauth"],
requires=["gui"],
stack_size=2 * 1024,
order=20,
diff --git a/applications/plugins/esp8266_deauth/esp8266_deauth.c b/applications/external/esp8266_deauth/esp8266_deauth.c
similarity index 97%
rename from applications/plugins/esp8266_deauth/esp8266_deauth.c
rename to applications/external/esp8266_deauth/esp8266_deauth.c
index d32ca4c18..3bf11928f 100644
--- a/applications/plugins/esp8266_deauth/esp8266_deauth.c
+++ b/applications/external/esp8266_deauth/esp8266_deauth.c
@@ -6,6 +6,7 @@
#include
#include
#include
+#include
//#include
//#include
//#include
@@ -328,6 +329,7 @@ int32_t esp8266_deauth_app(void* p) {
SWiFiDeauthApp* app = malloc(sizeof(SWiFiDeauthApp));
+ DOLPHIN_DEED(DolphinDeedPluginStart);
esp8266_deauth_app_init(app);
furi_hal_gpio_init_simple(app->m_GpioButtons.pinButtonUp, GpioModeOutputPushPull);
@@ -352,7 +354,12 @@ int32_t esp8266_deauth_app(void* p) {
#else
#if ENABLE_MODULE_POWER
app->m_context = Initializing;
- furi_hal_power_enable_otg();
+ uint8_t attempts = 0;
+ while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) {
+ furi_hal_power_enable_otg();
+ furi_delay_ms(10);
+ }
+ furi_delay_ms(200);
#else
app->m_context = ModuleActive;
#endif
@@ -409,7 +416,11 @@ int32_t esp8266_deauth_app(void* p) {
app->m_wifiDeauthModuleAttached = true;
#if ENABLE_MODULE_POWER
app->m_context = Initializing;
- furi_hal_power_enable_otg();
+ uint8_t attempts2 = 0;
+ while(!furi_hal_power_is_otg_enabled() && attempts2++ < 3) {
+ furi_hal_power_enable_otg();
+ furi_delay_ms(10);
+ }
#else
app->m_context = ModuleActive;
#endif
@@ -533,7 +544,9 @@ int32_t esp8266_deauth_app(void* p) {
DEAUTH_APP_LOG_I("App freed");
#if ENABLE_MODULE_POWER
- furi_hal_power_disable_otg();
+ if(furi_hal_power_is_otg_enabled()) {
+ furi_hal_power_disable_otg();
+ }
#endif
return 0;
diff --git a/applications/plugins/esp8266_deauth/wifi_10px.png b/applications/external/esp8266_deauth/wifi_10px.png
similarity index 100%
rename from applications/plugins/esp8266_deauth/wifi_10px.png
rename to applications/external/esp8266_deauth/wifi_10px.png
diff --git a/applications/plugins/flappy_bird/application.fam b/applications/external/flappy_bird/application.fam
similarity index 89%
rename from applications/plugins/flappy_bird/application.fam
rename to applications/external/flappy_bird/application.fam
index 0912178ca..f9fe85fa2 100644
--- a/applications/plugins/flappy_bird/application.fam
+++ b/applications/external/flappy_bird/application.fam
@@ -3,7 +3,6 @@ App(
name="Flappy Bird",
apptype=FlipperAppType.EXTERNAL,
entry_point="flappy_game_app",
- cdefines=["APP_FLAPPY_GAME"],
requires=["gui"],
stack_size=4 * 1024,
order=100,
diff --git a/applications/plugins/flappy_bird/assets/bird/frame_01.png b/applications/external/flappy_bird/assets/bird/frame_01.png
similarity index 100%
rename from applications/plugins/flappy_bird/assets/bird/frame_01.png
rename to applications/external/flappy_bird/assets/bird/frame_01.png
diff --git a/applications/plugins/flappy_bird/assets/bird/frame_02.png b/applications/external/flappy_bird/assets/bird/frame_02.png
similarity index 100%
rename from applications/plugins/flappy_bird/assets/bird/frame_02.png
rename to applications/external/flappy_bird/assets/bird/frame_02.png
diff --git a/applications/plugins/flappy_bird/assets/bird/frame_03.png b/applications/external/flappy_bird/assets/bird/frame_03.png
similarity index 100%
rename from applications/plugins/flappy_bird/assets/bird/frame_03.png
rename to applications/external/flappy_bird/assets/bird/frame_03.png
diff --git a/applications/plugins/flappy_bird/assets/bird/frame_rate b/applications/external/flappy_bird/assets/bird/frame_rate
similarity index 100%
rename from applications/plugins/flappy_bird/assets/bird/frame_rate
rename to applications/external/flappy_bird/assets/bird/frame_rate
diff --git a/applications/plugins/flappy_bird/flappy_10px.png b/applications/external/flappy_bird/flappy_10px.png
similarity index 100%
rename from applications/plugins/flappy_bird/flappy_10px.png
rename to applications/external/flappy_bird/flappy_10px.png
diff --git a/applications/plugins/flappy_bird/flappy_bird.c b/applications/external/flappy_bird/flappy_bird.c
similarity index 100%
rename from applications/plugins/flappy_bird/flappy_bird.c
rename to applications/external/flappy_bird/flappy_bird.c
diff --git a/applications/plugins/flashlight/LICENSE b/applications/external/flashlight/LICENSE
similarity index 100%
rename from applications/plugins/flashlight/LICENSE
rename to applications/external/flashlight/LICENSE
diff --git a/applications/plugins/flashlight/application.fam b/applications/external/flashlight/application.fam
similarity index 100%
rename from applications/plugins/flashlight/application.fam
rename to applications/external/flashlight/application.fam
diff --git a/applications/plugins/flashlight/flash10px.png b/applications/external/flashlight/flash10px.png
similarity index 100%
rename from applications/plugins/flashlight/flash10px.png
rename to applications/external/flashlight/flash10px.png
diff --git a/applications/plugins/flashlight/flashlight.c b/applications/external/flashlight/flashlight.c
similarity index 100%
rename from applications/plugins/flashlight/flashlight.c
rename to applications/external/flashlight/flashlight.c
diff --git a/applications/plugins/flipfrid/LICENSE.md b/applications/external/flipfrid/LICENSE.md
similarity index 100%
rename from applications/plugins/flipfrid/LICENSE.md
rename to applications/external/flipfrid/LICENSE.md
diff --git a/applications/plugins/flipfrid/application.fam b/applications/external/flipfrid/application.fam
similarity index 90%
rename from applications/plugins/flipfrid/application.fam
rename to applications/external/flipfrid/application.fam
index fc089de48..e2482e0d1 100644
--- a/applications/plugins/flipfrid/application.fam
+++ b/applications/external/flipfrid/application.fam
@@ -3,7 +3,6 @@ App(
name="RFID Fuzzer",
apptype=FlipperAppType.EXTERNAL,
entry_point="flipfrid_start",
- cdefines=["APP_FLIP_FRID"],
requires=["gui", "storage", "dialogs", "input", "notification"],
stack_size=1 * 1024,
order=180,
diff --git a/applications/plugins/flipfrid/flipfrid.c b/applications/external/flipfrid/flipfrid.c
similarity index 99%
rename from applications/plugins/flipfrid/flipfrid.c
rename to applications/external/flipfrid/flipfrid.c
index 4f28be7b1..ff52ab160 100644
--- a/applications/plugins/flipfrid/flipfrid.c
+++ b/applications/external/flipfrid/flipfrid.c
@@ -5,6 +5,7 @@
#include "scene/flipfrid_scene_select_field.h"
#include "scene/flipfrid_scene_run_attack.h"
#include "scene/flipfrid_scene_load_custom_uids.h"
+#include
#define RFIDFUZZER_APP_FOLDER "/ext/lrfid/rfidfuzzer"
@@ -117,6 +118,7 @@ int32_t flipfrid_start(void* p) {
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(FlipFridEvent));
FlipFridState* flipfrid_state = flipfrid_alloc();
+ DOLPHIN_DEED(DolphinDeedPluginStart);
flipfrid_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
if(!flipfrid_state->mutex) {
FURI_LOG_E(TAG, "cannot create mutex\r\n");
@@ -268,4 +270,4 @@ int32_t flipfrid_start(void* p) {
flipfrid_free(flipfrid_state);
return 0;
-}
\ No newline at end of file
+}
diff --git a/applications/plugins/flipfrid/flipfrid.h b/applications/external/flipfrid/flipfrid.h
similarity index 100%
rename from applications/plugins/flipfrid/flipfrid.h
rename to applications/external/flipfrid/flipfrid.h
diff --git a/applications/plugins/flipfrid/images/125_10px.png b/applications/external/flipfrid/images/125_10px.png
similarity index 100%
rename from applications/plugins/flipfrid/images/125_10px.png
rename to applications/external/flipfrid/images/125_10px.png
diff --git a/applications/plugins/flipfrid/rfid_10px.png b/applications/external/flipfrid/rfid_10px.png
similarity index 100%
rename from applications/plugins/flipfrid/rfid_10px.png
rename to applications/external/flipfrid/rfid_10px.png
diff --git a/applications/plugins/flipfrid/scene/flipfrid_scene_entrypoint.c b/applications/external/flipfrid/scene/flipfrid_scene_entrypoint.c
similarity index 100%
rename from applications/plugins/flipfrid/scene/flipfrid_scene_entrypoint.c
rename to applications/external/flipfrid/scene/flipfrid_scene_entrypoint.c
diff --git a/applications/plugins/flipfrid/scene/flipfrid_scene_entrypoint.h b/applications/external/flipfrid/scene/flipfrid_scene_entrypoint.h
similarity index 100%
rename from applications/plugins/flipfrid/scene/flipfrid_scene_entrypoint.h
rename to applications/external/flipfrid/scene/flipfrid_scene_entrypoint.h
diff --git a/applications/plugins/flipfrid/scene/flipfrid_scene_load_custom_uids.c b/applications/external/flipfrid/scene/flipfrid_scene_load_custom_uids.c
similarity index 100%
rename from applications/plugins/flipfrid/scene/flipfrid_scene_load_custom_uids.c
rename to applications/external/flipfrid/scene/flipfrid_scene_load_custom_uids.c
diff --git a/applications/plugins/flipfrid/scene/flipfrid_scene_load_custom_uids.h b/applications/external/flipfrid/scene/flipfrid_scene_load_custom_uids.h
similarity index 100%
rename from applications/plugins/flipfrid/scene/flipfrid_scene_load_custom_uids.h
rename to applications/external/flipfrid/scene/flipfrid_scene_load_custom_uids.h
diff --git a/applications/plugins/flipfrid/scene/flipfrid_scene_load_file.c b/applications/external/flipfrid/scene/flipfrid_scene_load_file.c
similarity index 100%
rename from applications/plugins/flipfrid/scene/flipfrid_scene_load_file.c
rename to applications/external/flipfrid/scene/flipfrid_scene_load_file.c
diff --git a/applications/plugins/flipfrid/scene/flipfrid_scene_load_file.h b/applications/external/flipfrid/scene/flipfrid_scene_load_file.h
similarity index 100%
rename from applications/plugins/flipfrid/scene/flipfrid_scene_load_file.h
rename to applications/external/flipfrid/scene/flipfrid_scene_load_file.h
diff --git a/applications/plugins/flipfrid/scene/flipfrid_scene_run_attack.c b/applications/external/flipfrid/scene/flipfrid_scene_run_attack.c
similarity index 100%
rename from applications/plugins/flipfrid/scene/flipfrid_scene_run_attack.c
rename to applications/external/flipfrid/scene/flipfrid_scene_run_attack.c
diff --git a/applications/plugins/flipfrid/scene/flipfrid_scene_run_attack.h b/applications/external/flipfrid/scene/flipfrid_scene_run_attack.h
similarity index 100%
rename from applications/plugins/flipfrid/scene/flipfrid_scene_run_attack.h
rename to applications/external/flipfrid/scene/flipfrid_scene_run_attack.h
diff --git a/applications/plugins/flipfrid/scene/flipfrid_scene_select_field.c b/applications/external/flipfrid/scene/flipfrid_scene_select_field.c
similarity index 100%
rename from applications/plugins/flipfrid/scene/flipfrid_scene_select_field.c
rename to applications/external/flipfrid/scene/flipfrid_scene_select_field.c
diff --git a/applications/plugins/flipfrid/scene/flipfrid_scene_select_field.h b/applications/external/flipfrid/scene/flipfrid_scene_select_field.h
similarity index 100%
rename from applications/plugins/flipfrid/scene/flipfrid_scene_select_field.h
rename to applications/external/flipfrid/scene/flipfrid_scene_select_field.h
diff --git a/applications/plugins/flipper_i2ctools/application.fam b/applications/external/flipper_i2ctools/application.fam
similarity index 89%
rename from applications/plugins/flipper_i2ctools/application.fam
rename to applications/external/flipper_i2ctools/application.fam
index 44138691d..3090244b5 100644
--- a/applications/plugins/flipper_i2ctools/application.fam
+++ b/applications/external/flipper_i2ctools/application.fam
@@ -3,7 +3,6 @@ App(
name="[GPIO] i2c Tools",
apptype=FlipperAppType.EXTERNAL,
entry_point="i2ctools_app",
- cdefines=["APP_I2CTOOLS"],
requires=["gui"],
stack_size=2 * 1024,
order=175,
diff --git a/applications/plugins/flipper_i2ctools/i2cscanner.c b/applications/external/flipper_i2ctools/i2cscanner.c
similarity index 100%
rename from applications/plugins/flipper_i2ctools/i2cscanner.c
rename to applications/external/flipper_i2ctools/i2cscanner.c
diff --git a/applications/plugins/flipper_i2ctools/i2cscanner.h b/applications/external/flipper_i2ctools/i2cscanner.h
similarity index 100%
rename from applications/plugins/flipper_i2ctools/i2cscanner.h
rename to applications/external/flipper_i2ctools/i2cscanner.h
diff --git a/applications/plugins/flipper_i2ctools/i2csender.c b/applications/external/flipper_i2ctools/i2csender.c
similarity index 100%
rename from applications/plugins/flipper_i2ctools/i2csender.c
rename to applications/external/flipper_i2ctools/i2csender.c
diff --git a/applications/plugins/flipper_i2ctools/i2csender.h b/applications/external/flipper_i2ctools/i2csender.h
similarity index 100%
rename from applications/plugins/flipper_i2ctools/i2csender.h
rename to applications/external/flipper_i2ctools/i2csender.h
diff --git a/applications/plugins/flipper_i2ctools/i2csniffer.c b/applications/external/flipper_i2ctools/i2csniffer.c
similarity index 100%
rename from applications/plugins/flipper_i2ctools/i2csniffer.c
rename to applications/external/flipper_i2ctools/i2csniffer.c
diff --git a/applications/plugins/flipper_i2ctools/i2csniffer.h b/applications/external/flipper_i2ctools/i2csniffer.h
similarity index 100%
rename from applications/plugins/flipper_i2ctools/i2csniffer.h
rename to applications/external/flipper_i2ctools/i2csniffer.h
diff --git a/applications/plugins/flipper_i2ctools/i2ctools.c b/applications/external/flipper_i2ctools/i2ctools.c
similarity index 100%
rename from applications/plugins/flipper_i2ctools/i2ctools.c
rename to applications/external/flipper_i2ctools/i2ctools.c
diff --git a/applications/plugins/flipper_i2ctools/i2ctools.png b/applications/external/flipper_i2ctools/i2ctools.png
similarity index 100%
rename from applications/plugins/flipper_i2ctools/i2ctools.png
rename to applications/external/flipper_i2ctools/i2ctools.png
diff --git a/applications/plugins/flipper_i2ctools/i2ctools_i.h b/applications/external/flipper_i2ctools/i2ctools_i.h
similarity index 100%
rename from applications/plugins/flipper_i2ctools/i2ctools_i.h
rename to applications/external/flipper_i2ctools/i2ctools_i.h
diff --git a/applications/plugins/flipper_i2ctools/images/ButtonDown_7x4.png b/applications/external/flipper_i2ctools/images/ButtonDown_7x4.png
similarity index 100%
rename from applications/plugins/flipper_i2ctools/images/ButtonDown_7x4.png
rename to applications/external/flipper_i2ctools/images/ButtonDown_7x4.png
diff --git a/applications/plugins/flipper_i2ctools/images/ButtonLeft_4x7.png b/applications/external/flipper_i2ctools/images/ButtonLeft_4x7.png
similarity index 100%
rename from applications/plugins/flipper_i2ctools/images/ButtonLeft_4x7.png
rename to applications/external/flipper_i2ctools/images/ButtonLeft_4x7.png
diff --git a/applications/plugins/flipper_i2ctools/images/ButtonRight_4x7.png b/applications/external/flipper_i2ctools/images/ButtonRight_4x7.png
similarity index 100%
rename from applications/plugins/flipper_i2ctools/images/ButtonRight_4x7.png
rename to applications/external/flipper_i2ctools/images/ButtonRight_4x7.png
diff --git a/applications/plugins/flipper_i2ctools/images/ButtonUp_7x4.png b/applications/external/flipper_i2ctools/images/ButtonUp_7x4.png
similarity index 100%
rename from applications/plugins/flipper_i2ctools/images/ButtonUp_7x4.png
rename to applications/external/flipper_i2ctools/images/ButtonUp_7x4.png
diff --git a/applications/plugins/flipper_i2ctools/images/Ok_btn_9x9.png b/applications/external/flipper_i2ctools/images/Ok_btn_9x9.png
similarity index 100%
rename from applications/plugins/flipper_i2ctools/images/Ok_btn_9x9.png
rename to applications/external/flipper_i2ctools/images/Ok_btn_9x9.png
diff --git a/applications/plugins/flipper_i2ctools/images/i2ctools_main_76x59.png b/applications/external/flipper_i2ctools/images/i2ctools_main_76x59.png
similarity index 100%
rename from applications/plugins/flipper_i2ctools/images/i2ctools_main_76x59.png
rename to applications/external/flipper_i2ctools/images/i2ctools_main_76x59.png
diff --git a/applications/plugins/flipper_i2ctools/views/main_view.c b/applications/external/flipper_i2ctools/views/main_view.c
similarity index 100%
rename from applications/plugins/flipper_i2ctools/views/main_view.c
rename to applications/external/flipper_i2ctools/views/main_view.c
diff --git a/applications/plugins/flipper_i2ctools/views/main_view.h b/applications/external/flipper_i2ctools/views/main_view.h
similarity index 100%
rename from applications/plugins/flipper_i2ctools/views/main_view.h
rename to applications/external/flipper_i2ctools/views/main_view.h
diff --git a/applications/plugins/flipper_i2ctools/views/scanner_view.c b/applications/external/flipper_i2ctools/views/scanner_view.c
similarity index 100%
rename from applications/plugins/flipper_i2ctools/views/scanner_view.c
rename to applications/external/flipper_i2ctools/views/scanner_view.c
diff --git a/applications/plugins/flipper_i2ctools/views/scanner_view.h b/applications/external/flipper_i2ctools/views/scanner_view.h
similarity index 100%
rename from applications/plugins/flipper_i2ctools/views/scanner_view.h
rename to applications/external/flipper_i2ctools/views/scanner_view.h
diff --git a/applications/plugins/flipper_i2ctools/views/sender_view.c b/applications/external/flipper_i2ctools/views/sender_view.c
similarity index 100%
rename from applications/plugins/flipper_i2ctools/views/sender_view.c
rename to applications/external/flipper_i2ctools/views/sender_view.c
diff --git a/applications/plugins/flipper_i2ctools/views/sender_view.h b/applications/external/flipper_i2ctools/views/sender_view.h
similarity index 100%
rename from applications/plugins/flipper_i2ctools/views/sender_view.h
rename to applications/external/flipper_i2ctools/views/sender_view.h
diff --git a/applications/plugins/flipper_i2ctools/views/sniffer_view.c b/applications/external/flipper_i2ctools/views/sniffer_view.c
similarity index 100%
rename from applications/plugins/flipper_i2ctools/views/sniffer_view.c
rename to applications/external/flipper_i2ctools/views/sniffer_view.c
diff --git a/applications/plugins/flipper_i2ctools/views/sniffer_view.h b/applications/external/flipper_i2ctools/views/sniffer_view.h
similarity index 100%
rename from applications/plugins/flipper_i2ctools/views/sniffer_view.h
rename to applications/external/flipper_i2ctools/views/sniffer_view.h
diff --git a/applications/plugins/game15/application.fam b/applications/external/game15/application.fam
similarity index 88%
rename from applications/plugins/game15/application.fam
rename to applications/external/game15/application.fam
index ab00316c1..969cb536a 100644
--- a/applications/plugins/game15/application.fam
+++ b/applications/external/game15/application.fam
@@ -3,7 +3,6 @@ App(
name="Game 15",
apptype=FlipperAppType.EXTERNAL,
entry_point="game15_app",
- cdefines=["APP_GAME15"],
requires=["gui"],
stack_size=1 * 1024,
fap_icon="game15_10px.png",
diff --git a/applications/plugins/game15/game15.c b/applications/external/game15/game15.c
similarity index 100%
rename from applications/plugins/game15/game15.c
rename to applications/external/game15/game15.c
diff --git a/applications/plugins/game15/game15_10px.png b/applications/external/game15/game15_10px.png
similarity index 100%
rename from applications/plugins/game15/game15_10px.png
rename to applications/external/game15/game15_10px.png
diff --git a/applications/plugins/game15/images/Game15.png b/applications/external/game15/images/Game15.png
similarity index 100%
rename from applications/plugins/game15/images/Game15.png
rename to applications/external/game15/images/Game15.png
diff --git a/applications/plugins/game15/images/Game15Popup.png b/applications/external/game15/images/Game15Popup.png
similarity index 100%
rename from applications/plugins/game15/images/Game15Popup.png
rename to applications/external/game15/images/Game15Popup.png
diff --git a/applications/plugins/game15/images/Game15Restore.png b/applications/external/game15/images/Game15Restore.png
similarity index 100%
rename from applications/plugins/game15/images/Game15Restore.png
rename to applications/external/game15/images/Game15Restore.png
diff --git a/applications/plugins/game15/sandbox.c b/applications/external/game15/sandbox.c
similarity index 100%
rename from applications/plugins/game15/sandbox.c
rename to applications/external/game15/sandbox.c
diff --git a/applications/plugins/game15/sandbox.h b/applications/external/game15/sandbox.h
similarity index 100%
rename from applications/plugins/game15/sandbox.h
rename to applications/external/game15/sandbox.h
diff --git a/applications/plugins/game_2048/LICENSE b/applications/external/game_2048/LICENSE
similarity index 100%
rename from applications/plugins/game_2048/LICENSE
rename to applications/external/game_2048/LICENSE
diff --git a/applications/plugins/game_2048/application.fam b/applications/external/game_2048/application.fam
similarity index 89%
rename from applications/plugins/game_2048/application.fam
rename to applications/external/game_2048/application.fam
index 0440d0864..89210ca23 100644
--- a/applications/plugins/game_2048/application.fam
+++ b/applications/external/game_2048/application.fam
@@ -4,7 +4,6 @@ App(
name="2048",
apptype=FlipperAppType.EXTERNAL,
entry_point="game_2048_app",
- cdefines=["APP_GAME_2048"],
requires=[
"gui",
],
diff --git a/applications/plugins/game_2048/array_utils.c b/applications/external/game_2048/array_utils.c
similarity index 100%
rename from applications/plugins/game_2048/array_utils.c
rename to applications/external/game_2048/array_utils.c
diff --git a/applications/plugins/game_2048/array_utils.h b/applications/external/game_2048/array_utils.h
similarity index 100%
rename from applications/plugins/game_2048/array_utils.h
rename to applications/external/game_2048/array_utils.h
diff --git a/applications/plugins/game_2048/digits.h b/applications/external/game_2048/digits.h
similarity index 100%
rename from applications/plugins/game_2048/digits.h
rename to applications/external/game_2048/digits.h
diff --git a/applications/plugins/game_2048/game_2048.c b/applications/external/game_2048/game_2048.c
similarity index 100%
rename from applications/plugins/game_2048/game_2048.c
rename to applications/external/game_2048/game_2048.c
diff --git a/applications/plugins/game_2048/game_2048.png b/applications/external/game_2048/game_2048.png
similarity index 100%
rename from applications/plugins/game_2048/game_2048.png
rename to applications/external/game_2048/game_2048.png
diff --git a/applications/plugins/game_of_life/application.fam b/applications/external/game_of_life/application.fam
similarity index 100%
rename from applications/plugins/game_of_life/application.fam
rename to applications/external/game_of_life/application.fam
diff --git a/applications/plugins/game_of_life/game_of_life.c b/applications/external/game_of_life/game_of_life.c
similarity index 100%
rename from applications/plugins/game_of_life/game_of_life.c
rename to applications/external/game_of_life/game_of_life.c
diff --git a/applications/plugins/game_of_life/golIcon.png b/applications/external/game_of_life/golIcon.png
similarity index 100%
rename from applications/plugins/game_of_life/golIcon.png
rename to applications/external/game_of_life/golIcon.png
diff --git a/applications/plugins/geigercounter/application.fam b/applications/external/geiger/application.fam
similarity index 100%
rename from applications/plugins/geigercounter/application.fam
rename to applications/external/geiger/application.fam
diff --git a/applications/plugins/geigercounter/flipper_geiger.c b/applications/external/geiger/flipper_geiger.c
similarity index 100%
rename from applications/plugins/geigercounter/flipper_geiger.c
rename to applications/external/geiger/flipper_geiger.c
diff --git a/applications/plugins/geigercounter/geiger.png b/applications/external/geiger/geiger.png
similarity index 100%
rename from applications/plugins/geigercounter/geiger.png
rename to applications/external/geiger/geiger.png
diff --git a/applications/plugins/gpio_reader_a/GPIO_reader.c b/applications/external/gpio_reader_a/GPIO_reader.c
similarity index 100%
rename from applications/plugins/gpio_reader_a/GPIO_reader.c
rename to applications/external/gpio_reader_a/GPIO_reader.c
diff --git a/applications/plugins/gpio_reader_a/GPIO_reader_item.c b/applications/external/gpio_reader_a/GPIO_reader_item.c
similarity index 100%
rename from applications/plugins/gpio_reader_a/GPIO_reader_item.c
rename to applications/external/gpio_reader_a/GPIO_reader_item.c
diff --git a/applications/plugins/gpio_reader_a/GPIO_reader_item.h b/applications/external/gpio_reader_a/GPIO_reader_item.h
similarity index 100%
rename from applications/plugins/gpio_reader_a/GPIO_reader_item.h
rename to applications/external/gpio_reader_a/GPIO_reader_item.h
diff --git a/applications/plugins/gpio_reader_a/application.fam b/applications/external/gpio_reader_a/application.fam
similarity index 100%
rename from applications/plugins/gpio_reader_a/application.fam
rename to applications/external/gpio_reader_a/application.fam
diff --git a/applications/plugins/gpio_reader_a/icon.png b/applications/external/gpio_reader_a/icon.png
similarity index 100%
rename from applications/plugins/gpio_reader_a/icon.png
rename to applications/external/gpio_reader_a/icon.png
diff --git a/applications/plugins/gpio_reader_b/LICENSE b/applications/external/gpio_reader_b/LICENSE
similarity index 100%
rename from applications/plugins/gpio_reader_b/LICENSE
rename to applications/external/gpio_reader_b/LICENSE
diff --git a/applications/plugins/gpio_reader_b/application.fam b/applications/external/gpio_reader_b/application.fam
similarity index 100%
rename from applications/plugins/gpio_reader_b/application.fam
rename to applications/external/gpio_reader_b/application.fam
diff --git a/applications/plugins/gpio_reader_b/gpio_app.c b/applications/external/gpio_reader_b/gpio_app.c
similarity index 100%
rename from applications/plugins/gpio_reader_b/gpio_app.c
rename to applications/external/gpio_reader_b/gpio_app.c
diff --git a/applications/plugins/gpio_reader_b/gpio_app.h b/applications/external/gpio_reader_b/gpio_app.h
similarity index 100%
rename from applications/plugins/gpio_reader_b/gpio_app.h
rename to applications/external/gpio_reader_b/gpio_app.h
diff --git a/applications/plugins/gpio_reader_b/gpio_app_i.h b/applications/external/gpio_reader_b/gpio_app_i.h
similarity index 100%
rename from applications/plugins/gpio_reader_b/gpio_app_i.h
rename to applications/external/gpio_reader_b/gpio_app_i.h
diff --git a/applications/plugins/gpio_reader_b/gpio_custom_event.h b/applications/external/gpio_reader_b/gpio_custom_event.h
similarity index 100%
rename from applications/plugins/gpio_reader_b/gpio_custom_event.h
rename to applications/external/gpio_reader_b/gpio_custom_event.h
diff --git a/applications/plugins/gpio_reader_b/gpio_item.c b/applications/external/gpio_reader_b/gpio_item.c
similarity index 100%
rename from applications/plugins/gpio_reader_b/gpio_item.c
rename to applications/external/gpio_reader_b/gpio_item.c
diff --git a/applications/plugins/gpio_reader_b/gpio_item.h b/applications/external/gpio_reader_b/gpio_item.h
similarity index 100%
rename from applications/plugins/gpio_reader_b/gpio_item.h
rename to applications/external/gpio_reader_b/gpio_item.h
diff --git a/applications/plugins/gpio_reader_b/icon.png b/applications/external/gpio_reader_b/icon.png
similarity index 100%
rename from applications/plugins/gpio_reader_b/icon.png
rename to applications/external/gpio_reader_b/icon.png
diff --git a/applications/plugins/gpio_reader_b/scenes/gpio_scene.c b/applications/external/gpio_reader_b/scenes/gpio_scene.c
similarity index 100%
rename from applications/plugins/gpio_reader_b/scenes/gpio_scene.c
rename to applications/external/gpio_reader_b/scenes/gpio_scene.c
diff --git a/applications/plugins/gpio_reader_b/scenes/gpio_scene.h b/applications/external/gpio_reader_b/scenes/gpio_scene.h
similarity index 100%
rename from applications/plugins/gpio_reader_b/scenes/gpio_scene.h
rename to applications/external/gpio_reader_b/scenes/gpio_scene.h
diff --git a/applications/plugins/gpio_reader_b/scenes/gpio_scene_config.h b/applications/external/gpio_reader_b/scenes/gpio_scene_config.h
similarity index 100%
rename from applications/plugins/gpio_reader_b/scenes/gpio_scene_config.h
rename to applications/external/gpio_reader_b/scenes/gpio_scene_config.h
diff --git a/applications/plugins/gpio_reader_b/scenes/gpio_scene_reader.c b/applications/external/gpio_reader_b/scenes/gpio_scene_reader.c
similarity index 100%
rename from applications/plugins/gpio_reader_b/scenes/gpio_scene_reader.c
rename to applications/external/gpio_reader_b/scenes/gpio_scene_reader.c
diff --git a/applications/plugins/gpio_reader_b/scenes/gpio_scene_start.c b/applications/external/gpio_reader_b/scenes/gpio_scene_start.c
similarity index 100%
rename from applications/plugins/gpio_reader_b/scenes/gpio_scene_start.c
rename to applications/external/gpio_reader_b/scenes/gpio_scene_start.c
diff --git a/applications/plugins/gpio_reader_b/scenes/gpio_scene_test.c b/applications/external/gpio_reader_b/scenes/gpio_scene_test.c
similarity index 100%
rename from applications/plugins/gpio_reader_b/scenes/gpio_scene_test.c
rename to applications/external/gpio_reader_b/scenes/gpio_scene_test.c
diff --git a/applications/plugins/gpio_reader_b/scenes/gpio_scene_usb_uart.c b/applications/external/gpio_reader_b/scenes/gpio_scene_usb_uart.c
similarity index 100%
rename from applications/plugins/gpio_reader_b/scenes/gpio_scene_usb_uart.c
rename to applications/external/gpio_reader_b/scenes/gpio_scene_usb_uart.c
diff --git a/applications/plugins/gpio_reader_b/scenes/gpio_scene_usb_uart_close_rpc.c b/applications/external/gpio_reader_b/scenes/gpio_scene_usb_uart_close_rpc.c
similarity index 100%
rename from applications/plugins/gpio_reader_b/scenes/gpio_scene_usb_uart_close_rpc.c
rename to applications/external/gpio_reader_b/scenes/gpio_scene_usb_uart_close_rpc.c
diff --git a/applications/plugins/gpio_reader_b/scenes/gpio_scene_usb_uart_config.c b/applications/external/gpio_reader_b/scenes/gpio_scene_usb_uart_config.c
similarity index 100%
rename from applications/plugins/gpio_reader_b/scenes/gpio_scene_usb_uart_config.c
rename to applications/external/gpio_reader_b/scenes/gpio_scene_usb_uart_config.c
diff --git a/applications/plugins/gpio_reader_b/usb_uart_bridge.c b/applications/external/gpio_reader_b/usb_uart_bridge.c
similarity index 100%
rename from applications/plugins/gpio_reader_b/usb_uart_bridge.c
rename to applications/external/gpio_reader_b/usb_uart_bridge.c
diff --git a/applications/plugins/gpio_reader_b/usb_uart_bridge.h b/applications/external/gpio_reader_b/usb_uart_bridge.h
similarity index 100%
rename from applications/plugins/gpio_reader_b/usb_uart_bridge.h
rename to applications/external/gpio_reader_b/usb_uart_bridge.h
diff --git a/applications/plugins/gpio_reader_b/views/gpio_reader.c b/applications/external/gpio_reader_b/views/gpio_reader.c
similarity index 100%
rename from applications/plugins/gpio_reader_b/views/gpio_reader.c
rename to applications/external/gpio_reader_b/views/gpio_reader.c
diff --git a/applications/plugins/gpio_reader_b/views/gpio_reader.h b/applications/external/gpio_reader_b/views/gpio_reader.h
similarity index 100%
rename from applications/plugins/gpio_reader_b/views/gpio_reader.h
rename to applications/external/gpio_reader_b/views/gpio_reader.h
diff --git a/applications/plugins/gpio_reader_b/views/gpio_test.c b/applications/external/gpio_reader_b/views/gpio_test.c
similarity index 100%
rename from applications/plugins/gpio_reader_b/views/gpio_test.c
rename to applications/external/gpio_reader_b/views/gpio_test.c
diff --git a/applications/plugins/gpio_reader_b/views/gpio_test.h b/applications/external/gpio_reader_b/views/gpio_test.h
similarity index 100%
rename from applications/plugins/gpio_reader_b/views/gpio_test.h
rename to applications/external/gpio_reader_b/views/gpio_test.h
diff --git a/applications/plugins/gpio_reader_b/views/gpio_usb_uart.c b/applications/external/gpio_reader_b/views/gpio_usb_uart.c
similarity index 100%
rename from applications/plugins/gpio_reader_b/views/gpio_usb_uart.c
rename to applications/external/gpio_reader_b/views/gpio_usb_uart.c
diff --git a/applications/plugins/gpio_reader_b/views/gpio_usb_uart.h b/applications/external/gpio_reader_b/views/gpio_usb_uart.h
similarity index 100%
rename from applications/plugins/gpio_reader_b/views/gpio_usb_uart.h
rename to applications/external/gpio_reader_b/views/gpio_usb_uart.h
diff --git a/applications/plugins/gps_nmea_uart/LICENSE b/applications/external/gps_nmea_uart/LICENSE
similarity index 100%
rename from applications/plugins/gps_nmea_uart/LICENSE
rename to applications/external/gps_nmea_uart/LICENSE
diff --git a/applications/plugins/gps_nmea_uart/application.fam b/applications/external/gps_nmea_uart/application.fam
similarity index 89%
rename from applications/plugins/gps_nmea_uart/application.fam
rename to applications/external/gps_nmea_uart/application.fam
index a5fdb4360..e02785744 100644
--- a/applications/plugins/gps_nmea_uart/application.fam
+++ b/applications/external/gps_nmea_uart/application.fam
@@ -3,7 +3,6 @@ App(
name="[NMEA] GPS",
apptype=FlipperAppType.EXTERNAL,
entry_point="gps_app",
- cdefines=["APP_GPS"],
requires=["gui"],
stack_size=1 * 1024,
order=35,
diff --git a/applications/plugins/gps_nmea_uart/gps.c b/applications/external/gps_nmea_uart/gps.c
similarity index 100%
rename from applications/plugins/gps_nmea_uart/gps.c
rename to applications/external/gps_nmea_uart/gps.c
diff --git a/applications/plugins/gps_nmea_uart/gps_10px.png b/applications/external/gps_nmea_uart/gps_10px.png
similarity index 100%
rename from applications/plugins/gps_nmea_uart/gps_10px.png
rename to applications/external/gps_nmea_uart/gps_10px.png
diff --git a/applications/plugins/gps_nmea_uart/gps_uart.c b/applications/external/gps_nmea_uart/gps_uart.c
similarity index 100%
rename from applications/plugins/gps_nmea_uart/gps_uart.c
rename to applications/external/gps_nmea_uart/gps_uart.c
diff --git a/applications/plugins/gps_nmea_uart/gps_uart.h b/applications/external/gps_nmea_uart/gps_uart.h
similarity index 100%
rename from applications/plugins/gps_nmea_uart/gps_uart.h
rename to applications/external/gps_nmea_uart/gps_uart.h
diff --git a/applications/plugins/gps_nmea_uart/minmea.c b/applications/external/gps_nmea_uart/minmea.c
similarity index 100%
rename from applications/plugins/gps_nmea_uart/minmea.c
rename to applications/external/gps_nmea_uart/minmea.c
diff --git a/applications/plugins/gps_nmea_uart/minmea.h b/applications/external/gps_nmea_uart/minmea.h
similarity index 100%
rename from applications/plugins/gps_nmea_uart/minmea.h
rename to applications/external/gps_nmea_uart/minmea.h
diff --git a/applications/plugins/hc_sr04/application.fam b/applications/external/hc_sr04/application.fam
similarity index 89%
rename from applications/plugins/hc_sr04/application.fam
rename to applications/external/hc_sr04/application.fam
index 85b6da2df..351f4e03d 100644
--- a/applications/plugins/hc_sr04/application.fam
+++ b/applications/external/hc_sr04/application.fam
@@ -3,7 +3,6 @@ App(
name="[HC-SR] Dist. Sensor",
apptype=FlipperAppType.EXTERNAL,
entry_point="hc_sr04_app",
- cdefines=["APP_HC_SR04"],
requires=[
"gui",
],
diff --git a/applications/plugins/hc_sr04/dist_sensor10px.png b/applications/external/hc_sr04/dist_sensor10px.png
similarity index 100%
rename from applications/plugins/hc_sr04/dist_sensor10px.png
rename to applications/external/hc_sr04/dist_sensor10px.png
diff --git a/applications/plugins/hc_sr04/hc_sr04.c b/applications/external/hc_sr04/hc_sr04.c
similarity index 100%
rename from applications/plugins/hc_sr04/hc_sr04.c
rename to applications/external/hc_sr04/hc_sr04.c
diff --git a/applications/plugins/heap_defence_game/application.fam b/applications/external/heap_defence_game/application.fam
similarity index 100%
rename from applications/plugins/heap_defence_game/application.fam
rename to applications/external/heap_defence_game/application.fam
diff --git a/applications/plugins/heap_defence_game/assets_images/Background_128x64.png b/applications/external/heap_defence_game/assets_images/Background_128x64.png
similarity index 100%
rename from applications/plugins/heap_defence_game/assets_images/Background_128x64.png
rename to applications/external/heap_defence_game/assets_images/Background_128x64.png
diff --git a/applications/plugins/heap_defence_game/assets_images/Box1_10x10.png b/applications/external/heap_defence_game/assets_images/Box1_10x10.png
similarity index 100%
rename from applications/plugins/heap_defence_game/assets_images/Box1_10x10.png
rename to applications/external/heap_defence_game/assets_images/Box1_10x10.png
diff --git a/applications/plugins/heap_defence_game/assets_images/Box2_10x10.png b/applications/external/heap_defence_game/assets_images/Box2_10x10.png
similarity index 100%
rename from applications/plugins/heap_defence_game/assets_images/Box2_10x10.png
rename to applications/external/heap_defence_game/assets_images/Box2_10x10.png
diff --git a/applications/plugins/heap_defence_game/assets_images/Box3_10x10.png b/applications/external/heap_defence_game/assets_images/Box3_10x10.png
similarity index 100%
rename from applications/plugins/heap_defence_game/assets_images/Box3_10x10.png
rename to applications/external/heap_defence_game/assets_images/Box3_10x10.png
diff --git a/applications/plugins/heap_defence_game/assets_images/Box4_10x10.png b/applications/external/heap_defence_game/assets_images/Box4_10x10.png
similarity index 100%
rename from applications/plugins/heap_defence_game/assets_images/Box4_10x10.png
rename to applications/external/heap_defence_game/assets_images/Box4_10x10.png
diff --git a/applications/plugins/heap_defence_game/assets_images/Box5_10x10.png b/applications/external/heap_defence_game/assets_images/Box5_10x10.png
similarity index 100%
rename from applications/plugins/heap_defence_game/assets_images/Box5_10x10.png
rename to applications/external/heap_defence_game/assets_images/Box5_10x10.png
diff --git a/applications/plugins/heap_defence_game/assets_images/Box6p_10x10.png b/applications/external/heap_defence_game/assets_images/Box6p_10x10.png
similarity index 100%
rename from applications/plugins/heap_defence_game/assets_images/Box6p_10x10.png
rename to applications/external/heap_defence_game/assets_images/Box6p_10x10.png
diff --git a/applications/plugins/heap_defence_game/assets_images/Box7p_10x10.png b/applications/external/heap_defence_game/assets_images/Box7p_10x10.png
similarity index 100%
rename from applications/plugins/heap_defence_game/assets_images/Box7p_10x10.png
rename to applications/external/heap_defence_game/assets_images/Box7p_10x10.png
diff --git a/applications/plugins/heap_defence_game/assets_images/Box8p_10x10.png b/applications/external/heap_defence_game/assets_images/Box8p_10x10.png
similarity index 100%
rename from applications/plugins/heap_defence_game/assets_images/Box8p_10x10.png
rename to applications/external/heap_defence_game/assets_images/Box8p_10x10.png
diff --git a/applications/plugins/heap_defence_game/assets_images/Game_over_128x64.png b/applications/external/heap_defence_game/assets_images/Game_over_128x64.png
similarity index 100%
rename from applications/plugins/heap_defence_game/assets_images/Game_over_128x64.png
rename to applications/external/heap_defence_game/assets_images/Game_over_128x64.png
diff --git a/applications/plugins/heap_defence_game/assets_images/HD_game_over_128x64/frame_01.png b/applications/external/heap_defence_game/assets_images/HD_game_over_128x64/frame_01.png
similarity index 100%
rename from applications/plugins/heap_defence_game/assets_images/HD_game_over_128x64/frame_01.png
rename to applications/external/heap_defence_game/assets_images/HD_game_over_128x64/frame_01.png
diff --git a/applications/plugins/heap_defence_game/assets_images/HD_game_over_128x64/frame_02.png b/applications/external/heap_defence_game/assets_images/HD_game_over_128x64/frame_02.png
similarity index 100%
rename from applications/plugins/heap_defence_game/assets_images/HD_game_over_128x64/frame_02.png
rename to applications/external/heap_defence_game/assets_images/HD_game_over_128x64/frame_02.png
diff --git a/applications/plugins/heap_defence_game/assets_images/HD_game_over_128x64/frame_03.png b/applications/external/heap_defence_game/assets_images/HD_game_over_128x64/frame_03.png
similarity index 100%
rename from applications/plugins/heap_defence_game/assets_images/HD_game_over_128x64/frame_03.png
rename to applications/external/heap_defence_game/assets_images/HD_game_over_128x64/frame_03.png
diff --git a/applications/plugins/heap_defence_game/assets_images/HD_game_over_128x64/frame_04.png b/applications/external/heap_defence_game/assets_images/HD_game_over_128x64/frame_04.png
similarity index 100%
rename from applications/plugins/heap_defence_game/assets_images/HD_game_over_128x64/frame_04.png
rename to applications/external/heap_defence_game/assets_images/HD_game_over_128x64/frame_04.png
diff --git a/applications/plugins/heap_defence_game/assets_images/HD_game_over_128x64/frame_05.png b/applications/external/heap_defence_game/assets_images/HD_game_over_128x64/frame_05.png
similarity index 100%
rename from applications/plugins/heap_defence_game/assets_images/HD_game_over_128x64/frame_05.png
rename to applications/external/heap_defence_game/assets_images/HD_game_over_128x64/frame_05.png
diff --git a/applications/plugins/heap_defence_game/assets_images/HD_game_over_128x64/frame_06.png b/applications/external/heap_defence_game/assets_images/HD_game_over_128x64/frame_06.png
similarity index 100%
rename from applications/plugins/heap_defence_game/assets_images/HD_game_over_128x64/frame_06.png
rename to applications/external/heap_defence_game/assets_images/HD_game_over_128x64/frame_06.png
diff --git a/applications/plugins/heap_defence_game/assets_images/HD_game_over_128x64/frame_07.png b/applications/external/heap_defence_game/assets_images/HD_game_over_128x64/frame_07.png
similarity index 100%
rename from applications/plugins/heap_defence_game/assets_images/HD_game_over_128x64/frame_07.png
rename to applications/external/heap_defence_game/assets_images/HD_game_over_128x64/frame_07.png
diff --git a/applications/plugins/heap_defence_game/assets_images/HD_game_over_128x64/frame_rate b/applications/external/heap_defence_game/assets_images/HD_game_over_128x64/frame_rate
similarity index 100%
rename from applications/plugins/heap_defence_game/assets_images/HD_game_over_128x64/frame_rate
rename to applications/external/heap_defence_game/assets_images/HD_game_over_128x64/frame_rate
diff --git a/applications/plugins/heap_defence_game/assets_images/HD_person_block_left_10x20/frame_01.png b/applications/external/heap_defence_game/assets_images/HD_person_block_left_10x20/frame_01.png
similarity index 100%
rename from applications/plugins/heap_defence_game/assets_images/HD_person_block_left_10x20/frame_01.png
rename to applications/external/heap_defence_game/assets_images/HD_person_block_left_10x20/frame_01.png
diff --git a/applications/plugins/heap_defence_game/assets_images/HD_person_block_left_10x20/frame_02.png b/applications/external/heap_defence_game/assets_images/HD_person_block_left_10x20/frame_02.png
similarity index 100%
rename from applications/plugins/heap_defence_game/assets_images/HD_person_block_left_10x20/frame_02.png
rename to applications/external/heap_defence_game/assets_images/HD_person_block_left_10x20/frame_02.png
diff --git a/applications/plugins/heap_defence_game/assets_images/HD_person_block_left_10x20/frame_rate b/applications/external/heap_defence_game/assets_images/HD_person_block_left_10x20/frame_rate
similarity index 100%
rename from applications/plugins/heap_defence_game/assets_images/HD_person_block_left_10x20/frame_rate
rename to applications/external/heap_defence_game/assets_images/HD_person_block_left_10x20/frame_rate
diff --git a/applications/plugins/heap_defence_game/assets_images/HD_person_block_right_10x20/frame_01.png b/applications/external/heap_defence_game/assets_images/HD_person_block_right_10x20/frame_01.png
similarity index 100%
rename from applications/plugins/heap_defence_game/assets_images/HD_person_block_right_10x20/frame_01.png
rename to applications/external/heap_defence_game/assets_images/HD_person_block_right_10x20/frame_01.png
diff --git a/applications/plugins/heap_defence_game/assets_images/HD_person_block_right_10x20/frame_02.png b/applications/external/heap_defence_game/assets_images/HD_person_block_right_10x20/frame_02.png
similarity index 100%
rename from applications/plugins/heap_defence_game/assets_images/HD_person_block_right_10x20/frame_02.png
rename to applications/external/heap_defence_game/assets_images/HD_person_block_right_10x20/frame_02.png
diff --git a/applications/plugins/heap_defence_game/assets_images/HD_person_block_right_10x20/frame_rate b/applications/external/heap_defence_game/assets_images/HD_person_block_right_10x20/frame_rate
similarity index 100%
rename from applications/plugins/heap_defence_game/assets_images/HD_person_block_right_10x20/frame_rate
rename to applications/external/heap_defence_game/assets_images/HD_person_block_right_10x20/frame_rate
diff --git a/applications/plugins/heap_defence_game/assets_images/HD_person_left_10x20/frame_01.png b/applications/external/heap_defence_game/assets_images/HD_person_left_10x20/frame_01.png
similarity index 100%
rename from applications/plugins/heap_defence_game/assets_images/HD_person_left_10x20/frame_01.png
rename to applications/external/heap_defence_game/assets_images/HD_person_left_10x20/frame_01.png
diff --git a/applications/plugins/heap_defence_game/assets_images/HD_person_left_10x20/frame_02.png b/applications/external/heap_defence_game/assets_images/HD_person_left_10x20/frame_02.png
similarity index 100%
rename from applications/plugins/heap_defence_game/assets_images/HD_person_left_10x20/frame_02.png
rename to applications/external/heap_defence_game/assets_images/HD_person_left_10x20/frame_02.png
diff --git a/applications/plugins/heap_defence_game/assets_images/HD_person_left_10x20/frame_03.png b/applications/external/heap_defence_game/assets_images/HD_person_left_10x20/frame_03.png
similarity index 100%
rename from applications/plugins/heap_defence_game/assets_images/HD_person_left_10x20/frame_03.png
rename to applications/external/heap_defence_game/assets_images/HD_person_left_10x20/frame_03.png
diff --git a/applications/plugins/heap_defence_game/assets_images/HD_person_left_10x20/frame_04.png b/applications/external/heap_defence_game/assets_images/HD_person_left_10x20/frame_04.png
similarity index 100%
rename from applications/plugins/heap_defence_game/assets_images/HD_person_left_10x20/frame_04.png
rename to applications/external/heap_defence_game/assets_images/HD_person_left_10x20/frame_04.png
diff --git a/applications/plugins/heap_defence_game/assets_images/HD_person_left_10x20/frame_rate b/applications/external/heap_defence_game/assets_images/HD_person_left_10x20/frame_rate
similarity index 100%
rename from applications/plugins/heap_defence_game/assets_images/HD_person_left_10x20/frame_rate
rename to applications/external/heap_defence_game/assets_images/HD_person_left_10x20/frame_rate
diff --git a/applications/plugins/heap_defence_game/assets_images/HD_person_right_10x20/frame_01.png b/applications/external/heap_defence_game/assets_images/HD_person_right_10x20/frame_01.png
similarity index 100%
rename from applications/plugins/heap_defence_game/assets_images/HD_person_right_10x20/frame_01.png
rename to applications/external/heap_defence_game/assets_images/HD_person_right_10x20/frame_01.png
diff --git a/applications/plugins/heap_defence_game/assets_images/HD_person_right_10x20/frame_02.png b/applications/external/heap_defence_game/assets_images/HD_person_right_10x20/frame_02.png
similarity index 100%
rename from applications/plugins/heap_defence_game/assets_images/HD_person_right_10x20/frame_02.png
rename to applications/external/heap_defence_game/assets_images/HD_person_right_10x20/frame_02.png
diff --git a/applications/plugins/heap_defence_game/assets_images/HD_person_right_10x20/frame_03.png b/applications/external/heap_defence_game/assets_images/HD_person_right_10x20/frame_03.png
similarity index 100%
rename from applications/plugins/heap_defence_game/assets_images/HD_person_right_10x20/frame_03.png
rename to applications/external/heap_defence_game/assets_images/HD_person_right_10x20/frame_03.png
diff --git a/applications/plugins/heap_defence_game/assets_images/HD_person_right_10x20/frame_04.png b/applications/external/heap_defence_game/assets_images/HD_person_right_10x20/frame_04.png
similarity index 100%
rename from applications/plugins/heap_defence_game/assets_images/HD_person_right_10x20/frame_04.png
rename to applications/external/heap_defence_game/assets_images/HD_person_right_10x20/frame_04.png
diff --git a/applications/plugins/heap_defence_game/assets_images/HD_person_right_10x20/frame_rate b/applications/external/heap_defence_game/assets_images/HD_person_right_10x20/frame_rate
similarity index 100%
rename from applications/plugins/heap_defence_game/assets_images/HD_person_right_10x20/frame_rate
rename to applications/external/heap_defence_game/assets_images/HD_person_right_10x20/frame_rate
diff --git a/applications/plugins/heap_defence_game/assets_images/HD_start_128x64/frame_01.png b/applications/external/heap_defence_game/assets_images/HD_start_128x64/frame_01.png
similarity index 100%
rename from applications/plugins/heap_defence_game/assets_images/HD_start_128x64/frame_01.png
rename to applications/external/heap_defence_game/assets_images/HD_start_128x64/frame_01.png
diff --git a/applications/plugins/heap_defence_game/assets_images/HD_start_128x64/frame_02.png b/applications/external/heap_defence_game/assets_images/HD_start_128x64/frame_02.png
similarity index 100%
rename from applications/plugins/heap_defence_game/assets_images/HD_start_128x64/frame_02.png
rename to applications/external/heap_defence_game/assets_images/HD_start_128x64/frame_02.png
diff --git a/applications/plugins/heap_defence_game/assets_images/HD_start_128x64/frame_03.png b/applications/external/heap_defence_game/assets_images/HD_start_128x64/frame_03.png
similarity index 100%
rename from applications/plugins/heap_defence_game/assets_images/HD_start_128x64/frame_03.png
rename to applications/external/heap_defence_game/assets_images/HD_start_128x64/frame_03.png
diff --git a/applications/plugins/heap_defence_game/assets_images/HD_start_128x64/frame_04.png b/applications/external/heap_defence_game/assets_images/HD_start_128x64/frame_04.png
similarity index 100%
rename from applications/plugins/heap_defence_game/assets_images/HD_start_128x64/frame_04.png
rename to applications/external/heap_defence_game/assets_images/HD_start_128x64/frame_04.png
diff --git a/applications/plugins/heap_defence_game/assets_images/HD_start_128x64/frame_rate b/applications/external/heap_defence_game/assets_images/HD_start_128x64/frame_rate
similarity index 100%
rename from applications/plugins/heap_defence_game/assets_images/HD_start_128x64/frame_rate
rename to applications/external/heap_defence_game/assets_images/HD_start_128x64/frame_rate
diff --git a/applications/plugins/heap_defence_game/assets_images/Person4_1_10x20.png b/applications/external/heap_defence_game/assets_images/Person4_1_10x20.png
similarity index 100%
rename from applications/plugins/heap_defence_game/assets_images/Person4_1_10x20.png
rename to applications/external/heap_defence_game/assets_images/Person4_1_10x20.png
diff --git a/applications/plugins/heap_defence_game/assets_images/Person4_2_10x20.png b/applications/external/heap_defence_game/assets_images/Person4_2_10x20.png
similarity index 100%
rename from applications/plugins/heap_defence_game/assets_images/Person4_2_10x20.png
rename to applications/external/heap_defence_game/assets_images/Person4_2_10x20.png
diff --git a/applications/plugins/heap_defence_game/assets_images/Person5_1_10x20.png b/applications/external/heap_defence_game/assets_images/Person5_1_10x20.png
similarity index 100%
rename from applications/plugins/heap_defence_game/assets_images/Person5_1_10x20.png
rename to applications/external/heap_defence_game/assets_images/Person5_1_10x20.png
diff --git a/applications/plugins/heap_defence_game/assets_images/Person5_2_10x20.png b/applications/external/heap_defence_game/assets_images/Person5_2_10x20.png
similarity index 100%
rename from applications/plugins/heap_defence_game/assets_images/Person5_2_10x20.png
rename to applications/external/heap_defence_game/assets_images/Person5_2_10x20.png
diff --git a/applications/plugins/heap_defence_game/assets_images/Start_128x64.png b/applications/external/heap_defence_game/assets_images/Start_128x64.png
similarity index 100%
rename from applications/plugins/heap_defence_game/assets_images/Start_128x64.png
rename to applications/external/heap_defence_game/assets_images/Start_128x64.png
diff --git a/applications/plugins/heap_defence_game/box.png b/applications/external/heap_defence_game/box.png
similarity index 100%
rename from applications/plugins/heap_defence_game/box.png
rename to applications/external/heap_defence_game/box.png
diff --git a/applications/plugins/heap_defence_game/heap_defence.c b/applications/external/heap_defence_game/heap_defence.c
similarity index 99%
rename from applications/plugins/heap_defence_game/heap_defence.c
rename to applications/external/heap_defence_game/heap_defence.c
index f7d6e4ef7..ae9ae76cc 100644
--- a/applications/plugins/heap_defence_game/heap_defence.c
+++ b/applications/external/heap_defence_game/heap_defence.c
@@ -495,7 +495,9 @@ static void heap_defense_input_callback(InputEvent* input_event, FuriMessageQueu
static void heap_defense_timer_callback(FuriMessageQueue* event_queue) {
furi_assert(event_queue);
- GameEvent event = {.type = EventGameTick, .input = {0}};
+ GameEvent event;
+ event.type = EventGameTick;
+ event.input = (InputEvent){0};
furi_message_queue_put(event_queue, &event, 0);
}
diff --git a/applications/plugins/heap_defence_game/hede_assets.c b/applications/external/heap_defence_game/hede_assets.c
similarity index 100%
rename from applications/plugins/heap_defence_game/hede_assets.c
rename to applications/external/heap_defence_game/hede_assets.c
diff --git a/applications/plugins/heap_defence_game/hede_assets.h b/applications/external/heap_defence_game/hede_assets.h
similarity index 100%
rename from applications/plugins/heap_defence_game/hede_assets.h
rename to applications/external/heap_defence_game/hede_assets.h
diff --git a/applications/plugins/hex_viewer/LICENSE b/applications/external/hex_viewer/LICENSE
similarity index 100%
rename from applications/plugins/hex_viewer/LICENSE
rename to applications/external/hex_viewer/LICENSE
diff --git a/applications/plugins/hex_viewer/application.fam b/applications/external/hex_viewer/application.fam
similarity index 90%
rename from applications/plugins/hex_viewer/application.fam
rename to applications/external/hex_viewer/application.fam
index 30c31ba76..96bf1a387 100644
--- a/applications/plugins/hex_viewer/application.fam
+++ b/applications/external/hex_viewer/application.fam
@@ -3,7 +3,6 @@ App(
name="Hex Viewer",
apptype=FlipperAppType.EXTERNAL,
entry_point="hex_viewer_app",
- cdefines=["APP_HEX_VIEWER"],
requires=[
"gui",
"dialogs",
diff --git a/applications/plugins/hex_viewer/hex_viewer.c b/applications/external/hex_viewer/hex_viewer.c
similarity index 100%
rename from applications/plugins/hex_viewer/hex_viewer.c
rename to applications/external/hex_viewer/hex_viewer.c
diff --git a/applications/plugins/hex_viewer/icons/hex_10px.png b/applications/external/hex_viewer/icons/hex_10px.png
similarity index 100%
rename from applications/plugins/hex_viewer/icons/hex_10px.png
rename to applications/external/hex_viewer/icons/hex_10px.png
diff --git a/applications/plugins/hid_app/application.fam b/applications/external/hid_app/application.fam
similarity index 86%
rename from applications/plugins/hid_app/application.fam
rename to applications/external/hid_app/application.fam
index b8c13e353..f0df85800 100644
--- a/applications/plugins/hid_app/application.fam
+++ b/applications/external/hid_app/application.fam
@@ -1,7 +1,7 @@
App(
appid="hid_usb",
name="USB Remote",
- apptype=FlipperAppType.PLUGIN,
+ apptype=FlipperAppType.EXTERNAL,
entry_point="hid_usb_app",
stack_size=1 * 1024,
fap_category="Tools",
@@ -14,7 +14,7 @@ App(
App(
appid="hid_ble",
name="Bluetooth Remote",
- apptype=FlipperAppType.PLUGIN,
+ apptype=FlipperAppType.EXTERNAL,
entry_point="hid_ble_app",
stack_size=1 * 1024,
fap_category="Tools",
diff --git a/applications/plugins/hid_app/assets/Arr_dwn_7x9.png b/applications/external/hid_app/assets/Arr_dwn_7x9.png
similarity index 100%
rename from applications/plugins/hid_app/assets/Arr_dwn_7x9.png
rename to applications/external/hid_app/assets/Arr_dwn_7x9.png
diff --git a/applications/plugins/hid_app/assets/Arr_up_7x9.png b/applications/external/hid_app/assets/Arr_up_7x9.png
similarity index 100%
rename from applications/plugins/hid_app/assets/Arr_up_7x9.png
rename to applications/external/hid_app/assets/Arr_up_7x9.png
diff --git a/applications/plugins/hid_app/assets/Ble_connected_15x15.png b/applications/external/hid_app/assets/Ble_connected_15x15.png
similarity index 100%
rename from applications/plugins/hid_app/assets/Ble_connected_15x15.png
rename to applications/external/hid_app/assets/Ble_connected_15x15.png
diff --git a/applications/plugins/hid_app/assets/Ble_disconnected_15x15.png b/applications/external/hid_app/assets/Ble_disconnected_15x15.png
similarity index 100%
rename from applications/plugins/hid_app/assets/Ble_disconnected_15x15.png
rename to applications/external/hid_app/assets/Ble_disconnected_15x15.png
diff --git a/applications/plugins/hid_app/assets/ButtonDown_7x4.png b/applications/external/hid_app/assets/ButtonDown_7x4.png
similarity index 100%
rename from applications/plugins/hid_app/assets/ButtonDown_7x4.png
rename to applications/external/hid_app/assets/ButtonDown_7x4.png
diff --git a/applications/plugins/hid_app/assets/ButtonF10_5x8.png b/applications/external/hid_app/assets/ButtonF10_5x8.png
similarity index 100%
rename from applications/plugins/hid_app/assets/ButtonF10_5x8.png
rename to applications/external/hid_app/assets/ButtonF10_5x8.png
diff --git a/applications/plugins/hid_app/assets/ButtonF11_5x8.png b/applications/external/hid_app/assets/ButtonF11_5x8.png
similarity index 100%
rename from applications/plugins/hid_app/assets/ButtonF11_5x8.png
rename to applications/external/hid_app/assets/ButtonF11_5x8.png
diff --git a/applications/plugins/hid_app/assets/ButtonF12_5x8.png b/applications/external/hid_app/assets/ButtonF12_5x8.png
similarity index 100%
rename from applications/plugins/hid_app/assets/ButtonF12_5x8.png
rename to applications/external/hid_app/assets/ButtonF12_5x8.png
diff --git a/applications/plugins/hid_app/assets/ButtonF1_5x8.png b/applications/external/hid_app/assets/ButtonF1_5x8.png
similarity index 100%
rename from applications/plugins/hid_app/assets/ButtonF1_5x8.png
rename to applications/external/hid_app/assets/ButtonF1_5x8.png
diff --git a/applications/plugins/hid_app/assets/ButtonF2_5x8.png b/applications/external/hid_app/assets/ButtonF2_5x8.png
similarity index 100%
rename from applications/plugins/hid_app/assets/ButtonF2_5x8.png
rename to applications/external/hid_app/assets/ButtonF2_5x8.png
diff --git a/applications/plugins/hid_app/assets/ButtonF3_5x8.png b/applications/external/hid_app/assets/ButtonF3_5x8.png
similarity index 100%
rename from applications/plugins/hid_app/assets/ButtonF3_5x8.png
rename to applications/external/hid_app/assets/ButtonF3_5x8.png
diff --git a/applications/plugins/hid_app/assets/ButtonF4_5x8.png b/applications/external/hid_app/assets/ButtonF4_5x8.png
similarity index 100%
rename from applications/plugins/hid_app/assets/ButtonF4_5x8.png
rename to applications/external/hid_app/assets/ButtonF4_5x8.png
diff --git a/applications/plugins/hid_app/assets/ButtonF5_5x8.png b/applications/external/hid_app/assets/ButtonF5_5x8.png
similarity index 100%
rename from applications/plugins/hid_app/assets/ButtonF5_5x8.png
rename to applications/external/hid_app/assets/ButtonF5_5x8.png
diff --git a/applications/plugins/hid_app/assets/ButtonF6_5x8.png b/applications/external/hid_app/assets/ButtonF6_5x8.png
similarity index 100%
rename from applications/plugins/hid_app/assets/ButtonF6_5x8.png
rename to applications/external/hid_app/assets/ButtonF6_5x8.png
diff --git a/applications/plugins/hid_app/assets/ButtonF7_5x8.png b/applications/external/hid_app/assets/ButtonF7_5x8.png
similarity index 100%
rename from applications/plugins/hid_app/assets/ButtonF7_5x8.png
rename to applications/external/hid_app/assets/ButtonF7_5x8.png
diff --git a/applications/plugins/hid_app/assets/ButtonF8_5x8.png b/applications/external/hid_app/assets/ButtonF8_5x8.png
similarity index 100%
rename from applications/plugins/hid_app/assets/ButtonF8_5x8.png
rename to applications/external/hid_app/assets/ButtonF8_5x8.png
diff --git a/applications/plugins/hid_app/assets/ButtonF9_5x8.png b/applications/external/hid_app/assets/ButtonF9_5x8.png
similarity index 100%
rename from applications/plugins/hid_app/assets/ButtonF9_5x8.png
rename to applications/external/hid_app/assets/ButtonF9_5x8.png
diff --git a/applications/plugins/hid_app/assets/ButtonLeft_4x7.png b/applications/external/hid_app/assets/ButtonLeft_4x7.png
similarity index 100%
rename from applications/plugins/hid_app/assets/ButtonLeft_4x7.png
rename to applications/external/hid_app/assets/ButtonLeft_4x7.png
diff --git a/applications/plugins/hid_app/assets/ButtonRight_4x7.png b/applications/external/hid_app/assets/ButtonRight_4x7.png
similarity index 100%
rename from applications/plugins/hid_app/assets/ButtonRight_4x7.png
rename to applications/external/hid_app/assets/ButtonRight_4x7.png
diff --git a/applications/plugins/hid_app/assets/ButtonUp_7x4.png b/applications/external/hid_app/assets/ButtonUp_7x4.png
similarity index 100%
rename from applications/plugins/hid_app/assets/ButtonUp_7x4.png
rename to applications/external/hid_app/assets/ButtonUp_7x4.png
diff --git a/applications/plugins/hid_app/assets/Button_18x18.png b/applications/external/hid_app/assets/Button_18x18.png
similarity index 100%
rename from applications/plugins/hid_app/assets/Button_18x18.png
rename to applications/external/hid_app/assets/Button_18x18.png
diff --git a/applications/plugins/hid_app/assets/Circles_47x47.png b/applications/external/hid_app/assets/Circles_47x47.png
similarity index 100%
rename from applications/plugins/hid_app/assets/Circles_47x47.png
rename to applications/external/hid_app/assets/Circles_47x47.png
diff --git a/applications/plugins/hid_app/assets/Left_mouse_icon_9x9.png b/applications/external/hid_app/assets/Left_mouse_icon_9x9.png
similarity index 100%
rename from applications/plugins/hid_app/assets/Left_mouse_icon_9x9.png
rename to applications/external/hid_app/assets/Left_mouse_icon_9x9.png
diff --git a/applications/plugins/hid_app/assets/Like_def_11x9.png b/applications/external/hid_app/assets/Like_def_11x9.png
similarity index 100%
rename from applications/plugins/hid_app/assets/Like_def_11x9.png
rename to applications/external/hid_app/assets/Like_def_11x9.png
diff --git a/applications/plugins/hid_app/assets/Like_pressed_17x17.png b/applications/external/hid_app/assets/Like_pressed_17x17.png
similarity index 100%
rename from applications/plugins/hid_app/assets/Like_pressed_17x17.png
rename to applications/external/hid_app/assets/Like_pressed_17x17.png
diff --git a/applications/plugins/hid_app/assets/Ok_btn_9x9.png b/applications/external/hid_app/assets/Ok_btn_9x9.png
similarity index 100%
rename from applications/plugins/hid_app/assets/Ok_btn_9x9.png
rename to applications/external/hid_app/assets/Ok_btn_9x9.png
diff --git a/applications/plugins/hid_app/assets/Ok_btn_pressed_13x13.png b/applications/external/hid_app/assets/Ok_btn_pressed_13x13.png
similarity index 100%
rename from applications/plugins/hid_app/assets/Ok_btn_pressed_13x13.png
rename to applications/external/hid_app/assets/Ok_btn_pressed_13x13.png
diff --git a/applications/plugins/hid_app/assets/Pin_arrow_down_7x9.png b/applications/external/hid_app/assets/Pin_arrow_down_7x9.png
similarity index 100%
rename from applications/plugins/hid_app/assets/Pin_arrow_down_7x9.png
rename to applications/external/hid_app/assets/Pin_arrow_down_7x9.png
diff --git a/applications/plugins/hid_app/assets/Pin_arrow_left_9x7.png b/applications/external/hid_app/assets/Pin_arrow_left_9x7.png
similarity index 100%
rename from applications/plugins/hid_app/assets/Pin_arrow_left_9x7.png
rename to applications/external/hid_app/assets/Pin_arrow_left_9x7.png
diff --git a/applications/plugins/hid_app/assets/Pin_arrow_right_9x7.png b/applications/external/hid_app/assets/Pin_arrow_right_9x7.png
similarity index 100%
rename from applications/plugins/hid_app/assets/Pin_arrow_right_9x7.png
rename to applications/external/hid_app/assets/Pin_arrow_right_9x7.png
diff --git a/applications/plugins/hid_app/assets/Pin_arrow_up_7x9.png b/applications/external/hid_app/assets/Pin_arrow_up_7x9.png
similarity index 100%
rename from applications/plugins/hid_app/assets/Pin_arrow_up_7x9.png
rename to applications/external/hid_app/assets/Pin_arrow_up_7x9.png
diff --git a/applications/plugins/hid_app/assets/Pin_back_arrow_10x8.png b/applications/external/hid_app/assets/Pin_back_arrow_10x8.png
similarity index 100%
rename from applications/plugins/hid_app/assets/Pin_back_arrow_10x8.png
rename to applications/external/hid_app/assets/Pin_back_arrow_10x8.png
diff --git a/applications/plugins/hid_app/assets/Pressed_Button_13x13.png b/applications/external/hid_app/assets/Pressed_Button_13x13.png
similarity index 100%
rename from applications/plugins/hid_app/assets/Pressed_Button_13x13.png
rename to applications/external/hid_app/assets/Pressed_Button_13x13.png
diff --git a/applications/plugins/hid_app/assets/Right_mouse_icon_9x9.png b/applications/external/hid_app/assets/Right_mouse_icon_9x9.png
similarity index 100%
rename from applications/plugins/hid_app/assets/Right_mouse_icon_9x9.png
rename to applications/external/hid_app/assets/Right_mouse_icon_9x9.png
diff --git a/applications/plugins/hid_app/assets/Space_65x18.png b/applications/external/hid_app/assets/Space_65x18.png
similarity index 100%
rename from applications/plugins/hid_app/assets/Space_65x18.png
rename to applications/external/hid_app/assets/Space_65x18.png
diff --git a/applications/plugins/hid_app/assets/Voldwn_6x6.png b/applications/external/hid_app/assets/Voldwn_6x6.png
similarity index 100%
rename from applications/plugins/hid_app/assets/Voldwn_6x6.png
rename to applications/external/hid_app/assets/Voldwn_6x6.png
diff --git a/applications/plugins/hid_app/assets/Volup_8x6.png b/applications/external/hid_app/assets/Volup_8x6.png
similarity index 100%
rename from applications/plugins/hid_app/assets/Volup_8x6.png
rename to applications/external/hid_app/assets/Volup_8x6.png
diff --git a/applications/plugins/hid_app/hid.c b/applications/external/hid_app/hid.c
similarity index 100%
rename from applications/plugins/hid_app/hid.c
rename to applications/external/hid_app/hid.c
diff --git a/applications/plugins/hid_app/hid.h b/applications/external/hid_app/hid.h
similarity index 100%
rename from applications/plugins/hid_app/hid.h
rename to applications/external/hid_app/hid.h
diff --git a/applications/plugins/hid_app/hid_ble_10px.png b/applications/external/hid_app/hid_ble_10px.png
similarity index 100%
rename from applications/plugins/hid_app/hid_ble_10px.png
rename to applications/external/hid_app/hid_ble_10px.png
diff --git a/applications/plugins/hid_app/hid_usb_10px.png b/applications/external/hid_app/hid_usb_10px.png
similarity index 100%
rename from applications/plugins/hid_app/hid_usb_10px.png
rename to applications/external/hid_app/hid_usb_10px.png
diff --git a/applications/plugins/hid_app/views.h b/applications/external/hid_app/views.h
similarity index 100%
rename from applications/plugins/hid_app/views.h
rename to applications/external/hid_app/views.h
diff --git a/applications/plugins/hid_app/views/hid_keyboard.c b/applications/external/hid_app/views/hid_keyboard.c
similarity index 100%
rename from applications/plugins/hid_app/views/hid_keyboard.c
rename to applications/external/hid_app/views/hid_keyboard.c
diff --git a/applications/plugins/hid_app/views/hid_keyboard.h b/applications/external/hid_app/views/hid_keyboard.h
similarity index 100%
rename from applications/plugins/hid_app/views/hid_keyboard.h
rename to applications/external/hid_app/views/hid_keyboard.h
diff --git a/applications/plugins/hid_app/views/hid_keynote.c b/applications/external/hid_app/views/hid_keynote.c
similarity index 100%
rename from applications/plugins/hid_app/views/hid_keynote.c
rename to applications/external/hid_app/views/hid_keynote.c
diff --git a/applications/plugins/hid_app/views/hid_keynote.h b/applications/external/hid_app/views/hid_keynote.h
similarity index 100%
rename from applications/plugins/hid_app/views/hid_keynote.h
rename to applications/external/hid_app/views/hid_keynote.h
diff --git a/applications/plugins/hid_app/views/hid_media.c b/applications/external/hid_app/views/hid_media.c
similarity index 100%
rename from applications/plugins/hid_app/views/hid_media.c
rename to applications/external/hid_app/views/hid_media.c
diff --git a/applications/plugins/hid_app/views/hid_media.h b/applications/external/hid_app/views/hid_media.h
similarity index 100%
rename from applications/plugins/hid_app/views/hid_media.h
rename to applications/external/hid_app/views/hid_media.h
diff --git a/applications/plugins/hid_app/views/hid_mouse.c b/applications/external/hid_app/views/hid_mouse.c
similarity index 100%
rename from applications/plugins/hid_app/views/hid_mouse.c
rename to applications/external/hid_app/views/hid_mouse.c
diff --git a/applications/plugins/hid_app/views/hid_mouse.h b/applications/external/hid_app/views/hid_mouse.h
similarity index 100%
rename from applications/plugins/hid_app/views/hid_mouse.h
rename to applications/external/hid_app/views/hid_mouse.h
diff --git a/applications/plugins/hid_app/views/hid_mouse_jiggler.c b/applications/external/hid_app/views/hid_mouse_jiggler.c
similarity index 100%
rename from applications/plugins/hid_app/views/hid_mouse_jiggler.c
rename to applications/external/hid_app/views/hid_mouse_jiggler.c
diff --git a/applications/plugins/hid_app/views/hid_mouse_jiggler.h b/applications/external/hid_app/views/hid_mouse_jiggler.h
similarity index 100%
rename from applications/plugins/hid_app/views/hid_mouse_jiggler.h
rename to applications/external/hid_app/views/hid_mouse_jiggler.h
diff --git a/applications/plugins/hid_app/views/hid_tiktok.c b/applications/external/hid_app/views/hid_tiktok.c
similarity index 100%
rename from applications/plugins/hid_app/views/hid_tiktok.c
rename to applications/external/hid_app/views/hid_tiktok.c
diff --git a/applications/plugins/hid_app/views/hid_tiktok.h b/applications/external/hid_app/views/hid_tiktok.h
similarity index 100%
rename from applications/plugins/hid_app/views/hid_tiktok.h
rename to applications/external/hid_app/views/hid_tiktok.h
diff --git a/applications/plugins/ibtn_fuzzer/LICENSE.md b/applications/external/ibtn_fuzzer/LICENSE.md
similarity index 100%
rename from applications/plugins/ibtn_fuzzer/LICENSE.md
rename to applications/external/ibtn_fuzzer/LICENSE.md
diff --git a/applications/plugins/ibtn_fuzzer/application.fam b/applications/external/ibtn_fuzzer/application.fam
similarity index 91%
rename from applications/plugins/ibtn_fuzzer/application.fam
rename to applications/external/ibtn_fuzzer/application.fam
index b27f47ba9..00c244c41 100644
--- a/applications/plugins/ibtn_fuzzer/application.fam
+++ b/applications/external/ibtn_fuzzer/application.fam
@@ -3,7 +3,6 @@ App(
name="iButton Fuzzer",
apptype=FlipperAppType.EXTERNAL,
entry_point="ibtnfuzzer_start",
- cdefines=["APP_IBTN_FUZZ"],
requires=["gui", "storage", "dialogs", "input", "notification"],
stack_size=1 * 1024,
order=15,
diff --git a/applications/plugins/ibtn_fuzzer/ibtnfuzzer.c b/applications/external/ibtn_fuzzer/ibtnfuzzer.c
similarity index 99%
rename from applications/plugins/ibtn_fuzzer/ibtnfuzzer.c
rename to applications/external/ibtn_fuzzer/ibtnfuzzer.c
index c5f2a5f7c..04d18886f 100644
--- a/applications/plugins/ibtn_fuzzer/ibtnfuzzer.c
+++ b/applications/external/ibtn_fuzzer/ibtnfuzzer.c
@@ -5,6 +5,7 @@
#include "scene/ibtnfuzzer_scene_select_field.h"
#include "scene/ibtnfuzzer_scene_run_attack.h"
#include "scene/ibtnfuzzer_scene_load_custom_uids.h"
+#include
#define IBTNFUZZER_APP_FOLDER "/ext/ibtnfuzzer"
@@ -119,6 +120,7 @@ int32_t ibtnfuzzer_start(void* p) {
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(iBtnFuzzerEvent));
iBtnFuzzerState* ibtnfuzzer_state = ibtnfuzzer_alloc();
+ DOLPHIN_DEED(DolphinDeedPluginStart);
ibtnfuzzer_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
if(!ibtnfuzzer_state->mutex) {
FURI_LOG_E(TAG, "cannot create mutex\r\n");
@@ -264,4 +266,4 @@ int32_t ibtnfuzzer_start(void* p) {
ibtnfuzzer_free(ibtnfuzzer_state);
return 0;
-}
\ No newline at end of file
+}
diff --git a/applications/plugins/ibtn_fuzzer/ibtnfuzzer.h b/applications/external/ibtn_fuzzer/ibtnfuzzer.h
similarity index 95%
rename from applications/plugins/ibtn_fuzzer/ibtnfuzzer.h
rename to applications/external/ibtn_fuzzer/ibtnfuzzer.h
index 91a9c6b0c..ed42cc541 100644
--- a/applications/plugins/ibtn_fuzzer/ibtnfuzzer.h
+++ b/applications/external/ibtn_fuzzer/ibtnfuzzer.h
@@ -17,8 +17,8 @@
#include
-#include
-#include
+#include
+#include
#define TAG "iBtnFuzzer"
diff --git a/applications/plugins/ibtn_fuzzer/ibutt_10px.png b/applications/external/ibtn_fuzzer/ibutt_10px.png
similarity index 100%
rename from applications/plugins/ibtn_fuzzer/ibutt_10px.png
rename to applications/external/ibtn_fuzzer/ibutt_10px.png
diff --git a/applications/plugins/ibtn_fuzzer/images/ibutt_10px.png b/applications/external/ibtn_fuzzer/images/ibutt_10px.png
similarity index 100%
rename from applications/plugins/ibtn_fuzzer/images/ibutt_10px.png
rename to applications/external/ibtn_fuzzer/images/ibutt_10px.png
diff --git a/applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_entrypoint.c b/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_entrypoint.c
similarity index 100%
rename from applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_entrypoint.c
rename to applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_entrypoint.c
diff --git a/applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_entrypoint.h b/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_entrypoint.h
similarity index 100%
rename from applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_entrypoint.h
rename to applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_entrypoint.h
diff --git a/applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_custom_uids.c b/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_custom_uids.c
similarity index 100%
rename from applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_custom_uids.c
rename to applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_custom_uids.c
diff --git a/applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_custom_uids.h b/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_custom_uids.h
similarity index 100%
rename from applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_custom_uids.h
rename to applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_custom_uids.h
diff --git a/applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_file.c b/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_file.c
similarity index 100%
rename from applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_file.c
rename to applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_file.c
diff --git a/applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_file.h b/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_file.h
similarity index 100%
rename from applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_file.h
rename to applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_file.h
diff --git a/applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.c b/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.c
similarity index 100%
rename from applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.c
rename to applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.c
diff --git a/applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.h b/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.h
similarity index 100%
rename from applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.h
rename to applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.h
diff --git a/applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_select_field.c b/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_select_field.c
similarity index 100%
rename from applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_select_field.c
rename to applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_select_field.c
diff --git a/applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_select_field.h b/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_select_field.h
similarity index 100%
rename from applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_select_field.h
rename to applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_select_field.h
diff --git a/applications/plugins/ifttt/application.fam b/applications/external/ifttt/application.fam
similarity index 100%
rename from applications/plugins/ifttt/application.fam
rename to applications/external/ifttt/application.fam
diff --git a/applications/plugins/ifttt/icon.png b/applications/external/ifttt/icon.png
similarity index 100%
rename from applications/plugins/ifttt/icon.png
rename to applications/external/ifttt/icon.png
diff --git a/applications/plugins/ifttt/ifttt_virtual_button.c b/applications/external/ifttt/ifttt_virtual_button.c
similarity index 100%
rename from applications/plugins/ifttt/ifttt_virtual_button.c
rename to applications/external/ifttt/ifttt_virtual_button.c
diff --git a/applications/plugins/ifttt/ifttt_virtual_button.h b/applications/external/ifttt/ifttt_virtual_button.h
similarity index 100%
rename from applications/plugins/ifttt/ifttt_virtual_button.h
rename to applications/external/ifttt/ifttt_virtual_button.h
diff --git a/applications/plugins/ifttt/scenes/virtual_button_scene.c b/applications/external/ifttt/scenes/virtual_button_scene.c
similarity index 100%
rename from applications/plugins/ifttt/scenes/virtual_button_scene.c
rename to applications/external/ifttt/scenes/virtual_button_scene.c
diff --git a/applications/plugins/ifttt/scenes/virtual_button_scene.h b/applications/external/ifttt/scenes/virtual_button_scene.h
similarity index 100%
rename from applications/plugins/ifttt/scenes/virtual_button_scene.h
rename to applications/external/ifttt/scenes/virtual_button_scene.h
diff --git a/applications/plugins/ifttt/scenes/virtual_button_scene_about.c b/applications/external/ifttt/scenes/virtual_button_scene_about.c
similarity index 100%
rename from applications/plugins/ifttt/scenes/virtual_button_scene_about.c
rename to applications/external/ifttt/scenes/virtual_button_scene_about.c
diff --git a/applications/plugins/ifttt/scenes/virtual_button_scene_config.h b/applications/external/ifttt/scenes/virtual_button_scene_config.h
similarity index 100%
rename from applications/plugins/ifttt/scenes/virtual_button_scene_config.h
rename to applications/external/ifttt/scenes/virtual_button_scene_config.h
diff --git a/applications/plugins/ifttt/scenes/virtual_button_scene_send.c b/applications/external/ifttt/scenes/virtual_button_scene_send.c
similarity index 100%
rename from applications/plugins/ifttt/scenes/virtual_button_scene_send.c
rename to applications/external/ifttt/scenes/virtual_button_scene_send.c
diff --git a/applications/plugins/ifttt/scenes/virtual_button_scene_start.c b/applications/external/ifttt/scenes/virtual_button_scene_start.c
similarity index 100%
rename from applications/plugins/ifttt/scenes/virtual_button_scene_start.c
rename to applications/external/ifttt/scenes/virtual_button_scene_start.c
diff --git a/applications/plugins/ifttt/views/about_view.c b/applications/external/ifttt/views/about_view.c
similarity index 100%
rename from applications/plugins/ifttt/views/about_view.c
rename to applications/external/ifttt/views/about_view.c
diff --git a/applications/plugins/ifttt/views/about_view.h b/applications/external/ifttt/views/about_view.h
similarity index 100%
rename from applications/plugins/ifttt/views/about_view.h
rename to applications/external/ifttt/views/about_view.h
diff --git a/applications/plugins/ifttt/views/send_view.c b/applications/external/ifttt/views/send_view.c
similarity index 100%
rename from applications/plugins/ifttt/views/send_view.c
rename to applications/external/ifttt/views/send_view.c
diff --git a/applications/plugins/ifttt/views/send_view.h b/applications/external/ifttt/views/send_view.h
similarity index 100%
rename from applications/plugins/ifttt/views/send_view.h
rename to applications/external/ifttt/views/send_view.h
diff --git a/applications/external/ir_remote/application.fam b/applications/external/ir_remote/application.fam
new file mode 100644
index 000000000..1107ae45d
--- /dev/null
+++ b/applications/external/ir_remote/application.fam
@@ -0,0 +1,14 @@
+App(
+ appid="ir_remote",
+ name="IR Remote",
+ apptype=FlipperAppType.EXTERNAL,
+ entry_point="infrared_remote_app",
+ stack_size=3 * 1024,
+ requires=[
+ "gui",
+ "dialogs",
+ ],
+ fap_category="Tools",
+ fap_icon="ir_10px.png",
+ fap_icon_assets="images",
+)
diff --git a/applications/plugins/swd_probe/icons/ButtonDown_7x4.png b/applications/external/ir_remote/images/ButtonDown_7x4.png
similarity index 100%
rename from applications/plugins/swd_probe/icons/ButtonDown_7x4.png
rename to applications/external/ir_remote/images/ButtonDown_7x4.png
diff --git a/applications/plugins/timelapse/icons/ButtonLeft_4x7.png b/applications/external/ir_remote/images/ButtonLeft_4x7.png
similarity index 100%
rename from applications/plugins/timelapse/icons/ButtonLeft_4x7.png
rename to applications/external/ir_remote/images/ButtonLeft_4x7.png
diff --git a/applications/plugins/playlist/images/ButtonRight_4x7.png b/applications/external/ir_remote/images/ButtonRight_4x7.png
similarity index 100%
rename from applications/plugins/playlist/images/ButtonRight_4x7.png
rename to applications/external/ir_remote/images/ButtonRight_4x7.png
diff --git a/applications/plugins/metronome/images/ButtonUp_7x4.png b/applications/external/ir_remote/images/ButtonUp_7x4.png
similarity index 100%
rename from applications/plugins/metronome/images/ButtonUp_7x4.png
rename to applications/external/ir_remote/images/ButtonUp_7x4.png
diff --git a/applications/plugins/passgen/icons/Ok_btn_9x9.png b/applications/external/ir_remote/images/Ok_btn_9x9.png
similarity index 100%
rename from applications/plugins/passgen/icons/Ok_btn_9x9.png
rename to applications/external/ir_remote/images/Ok_btn_9x9.png
diff --git a/applications/external/ir_remote/images/back_10px.png b/applications/external/ir_remote/images/back_10px.png
new file mode 100644
index 000000000..f9c615a99
Binary files /dev/null and b/applications/external/ir_remote/images/back_10px.png differ
diff --git a/applications/plugins/mousejacker/images/sub1_10px.png b/applications/external/ir_remote/images/sub1_10px.png
similarity index 100%
rename from applications/plugins/mousejacker/images/sub1_10px.png
rename to applications/external/ir_remote/images/sub1_10px.png
diff --git a/applications/external/ir_remote/infrared_remote.c b/applications/external/ir_remote/infrared_remote.c
new file mode 100644
index 000000000..3a528a656
--- /dev/null
+++ b/applications/external/ir_remote/infrared_remote.c
@@ -0,0 +1,188 @@
+#include "infrared_remote.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define TAG "InfraredRemote"
+
+ARRAY_DEF(InfraredButtonArray, InfraredRemoteButton*, M_PTR_OPLIST);
+
+struct InfraredRemote {
+ InfraredButtonArray_t buttons;
+ FuriString* name;
+ FuriString* path;
+};
+
+static void infrared_remote_clear_buttons(InfraredRemote* remote) {
+ InfraredButtonArray_it_t it;
+ for(InfraredButtonArray_it(it, remote->buttons); !InfraredButtonArray_end_p(it);
+ InfraredButtonArray_next(it)) {
+ infrared_remote_button_free(*InfraredButtonArray_cref(it));
+ }
+ InfraredButtonArray_reset(remote->buttons);
+}
+
+InfraredRemote* infrared_remote_alloc() {
+ InfraredRemote* remote = malloc(sizeof(InfraredRemote));
+ InfraredButtonArray_init(remote->buttons);
+ remote->name = furi_string_alloc();
+ remote->path = furi_string_alloc();
+ return remote;
+}
+
+void infrared_remote_free(InfraredRemote* remote) {
+ infrared_remote_clear_buttons(remote);
+ InfraredButtonArray_clear(remote->buttons);
+ furi_string_free(remote->path);
+ furi_string_free(remote->name);
+ free(remote);
+}
+
+void infrared_remote_reset(InfraredRemote* remote) {
+ infrared_remote_clear_buttons(remote);
+ furi_string_reset(remote->name);
+ furi_string_reset(remote->path);
+}
+
+void infrared_remote_set_name(InfraredRemote* remote, const char* name) {
+ furi_string_set(remote->name, name);
+}
+
+const char* infrared_remote_get_name(InfraredRemote* remote) {
+ return furi_string_get_cstr(remote->name);
+}
+
+void infrared_remote_set_path(InfraredRemote* remote, const char* path) {
+ furi_string_set(remote->path, path);
+}
+
+const char* infrared_remote_get_path(InfraredRemote* remote) {
+ return furi_string_get_cstr(remote->path);
+}
+
+size_t infrared_remote_get_button_count(InfraredRemote* remote) {
+ return InfraredButtonArray_size(remote->buttons);
+}
+
+InfraredRemoteButton* infrared_remote_get_button(InfraredRemote* remote, size_t index) {
+ furi_assert(index < InfraredButtonArray_size(remote->buttons));
+ return *InfraredButtonArray_get(remote->buttons, index);
+}
+
+bool infrared_remote_find_button_by_name(InfraredRemote* remote, const char* name, size_t* index) {
+ for(size_t i = 0; i < InfraredButtonArray_size(remote->buttons); i++) {
+ InfraredRemoteButton* button = *InfraredButtonArray_get(remote->buttons, i);
+ if(!strcmp(infrared_remote_button_get_name(button), name)) {
+ *index = i;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool infrared_remote_add_button(InfraredRemote* remote, const char* name, InfraredSignal* signal) {
+ InfraredRemoteButton* button = infrared_remote_button_alloc();
+ infrared_remote_button_set_name(button, name);
+ infrared_remote_button_set_signal(button, signal);
+ InfraredButtonArray_push_back(remote->buttons, button);
+ return infrared_remote_store(remote);
+}
+
+bool infrared_remote_rename_button(InfraredRemote* remote, const char* new_name, size_t index) {
+ furi_assert(index < InfraredButtonArray_size(remote->buttons));
+ InfraredRemoteButton* button = *InfraredButtonArray_get(remote->buttons, index);
+ infrared_remote_button_set_name(button, new_name);
+ return infrared_remote_store(remote);
+}
+
+bool infrared_remote_delete_button(InfraredRemote* remote, size_t index) {
+ furi_assert(index < InfraredButtonArray_size(remote->buttons));
+ InfraredRemoteButton* button;
+ InfraredButtonArray_pop_at(&button, remote->buttons, index);
+ infrared_remote_button_free(button);
+ return infrared_remote_store(remote);
+}
+
+bool infrared_remote_store(InfraredRemote* remote) {
+ Storage* storage = furi_record_open(RECORD_STORAGE);
+ FlipperFormat* ff = flipper_format_file_alloc(storage);
+ const char* path = furi_string_get_cstr(remote->path);
+
+ FURI_LOG_I(TAG, "store file: \'%s\'", path);
+
+ bool success = flipper_format_file_open_always(ff, path) &&
+ flipper_format_write_header_cstr(ff, "IR signals file", 1);
+ if(success) {
+ InfraredButtonArray_it_t it;
+ for(InfraredButtonArray_it(it, remote->buttons); !InfraredButtonArray_end_p(it);
+ InfraredButtonArray_next(it)) {
+ InfraredRemoteButton* button = *InfraredButtonArray_cref(it);
+ success = infrared_signal_save(
+ infrared_remote_button_get_signal(button),
+ ff,
+ infrared_remote_button_get_name(button));
+ if(!success) {
+ break;
+ }
+ }
+ }
+
+ flipper_format_free(ff);
+ furi_record_close(RECORD_STORAGE);
+ return success;
+}
+
+bool infrared_remote_load(InfraredRemote* remote, FuriString* path) {
+ Storage* storage = furi_record_open(RECORD_STORAGE);
+ FlipperFormat* ff = flipper_format_buffered_file_alloc(storage);
+
+ FuriString* buf;
+ buf = furi_string_alloc();
+
+ FURI_LOG_I(TAG, "load file: \'%s\'", furi_string_get_cstr(path));
+ bool success = flipper_format_buffered_file_open_existing(ff, furi_string_get_cstr(path));
+
+ if(success) {
+ uint32_t version;
+ success = flipper_format_read_header(ff, buf, &version) &&
+ !furi_string_cmp(buf, "IR signals file") && (version == 1);
+ }
+
+ if(success) {
+ path_extract_filename(path, buf, true);
+ infrared_remote_clear_buttons(remote);
+ infrared_remote_set_name(remote, furi_string_get_cstr(buf));
+ infrared_remote_set_path(remote, furi_string_get_cstr(path));
+
+ for(bool can_read = true; can_read;) {
+ InfraredRemoteButton* button = infrared_remote_button_alloc();
+ can_read = infrared_signal_read(infrared_remote_button_get_signal(button), ff, buf);
+ if(can_read) {
+ infrared_remote_button_set_name(button, furi_string_get_cstr(buf));
+ InfraredButtonArray_push_back(remote->buttons, button);
+ } else {
+ infrared_remote_button_free(button);
+ }
+ }
+ }
+
+ furi_string_free(buf);
+ flipper_format_free(ff);
+ furi_record_close(RECORD_STORAGE);
+ return success;
+}
+
+bool infrared_remote_remove(InfraredRemote* remote) {
+ Storage* storage = furi_record_open(RECORD_STORAGE);
+
+ FS_Error status = storage_common_remove(storage, furi_string_get_cstr(remote->path));
+ infrared_remote_reset(remote);
+
+ furi_record_close(RECORD_STORAGE);
+ return (status == FSE_OK || status == FSE_NOT_EXIST);
+}
diff --git a/applications/external/ir_remote/infrared_remote.h b/applications/external/ir_remote/infrared_remote.h
new file mode 100644
index 000000000..6eac193d3
--- /dev/null
+++ b/applications/external/ir_remote/infrared_remote.h
@@ -0,0 +1,29 @@
+#pragma once
+
+#include
+
+#include "infrared_remote_button.h"
+
+typedef struct InfraredRemote InfraredRemote;
+
+InfraredRemote* infrared_remote_alloc();
+void infrared_remote_free(InfraredRemote* remote);
+void infrared_remote_reset(InfraredRemote* remote);
+
+void infrared_remote_set_name(InfraredRemote* remote, const char* name);
+const char* infrared_remote_get_name(InfraredRemote* remote);
+
+void infrared_remote_set_path(InfraredRemote* remote, const char* path);
+const char* infrared_remote_get_path(InfraredRemote* remote);
+
+size_t infrared_remote_get_button_count(InfraredRemote* remote);
+InfraredRemoteButton* infrared_remote_get_button(InfraredRemote* remote, size_t index);
+bool infrared_remote_find_button_by_name(InfraredRemote* remote, const char* name, size_t* index);
+
+bool infrared_remote_add_button(InfraredRemote* remote, const char* name, InfraredSignal* signal);
+bool infrared_remote_rename_button(InfraredRemote* remote, const char* new_name, size_t index);
+bool infrared_remote_delete_button(InfraredRemote* remote, size_t index);
+
+bool infrared_remote_store(InfraredRemote* remote);
+bool infrared_remote_load(InfraredRemote* remote, FuriString* path);
+bool infrared_remote_remove(InfraredRemote* remote);
diff --git a/applications/external/ir_remote/infrared_remote_app.c b/applications/external/ir_remote/infrared_remote_app.c
new file mode 100644
index 000000000..7056fae89
--- /dev/null
+++ b/applications/external/ir_remote/infrared_remote_app.c
@@ -0,0 +1,600 @@
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+#include "infrared_signal.h"
+#include "infrared_remote.h"
+#include "infrared_remote_button.h"
+#define TAG "IR_Remote"
+#define MENU_BTN_TXT_X 36
+
+#include
+
+typedef struct {
+ int status;
+ ViewPort* view_port;
+ FuriString* up_button;
+ FuriString* down_button;
+ FuriString* left_button;
+ FuriString* right_button;
+ FuriString* ok_button;
+ FuriString* back_button;
+ FuriString* up_hold_button;
+ FuriString* down_hold_button;
+ FuriString* left_hold_button;
+ FuriString* right_hold_button;
+ FuriString* ok_hold_button;
+} IRApp;
+
+// Screen is 128x64 px
+static void app_draw_callback(Canvas* canvas, void* ctx) {
+ // Show config is incorrect when cannot read the remote file
+ // Showing button string in the screen, upper part is short press, lower part is long press
+ IRApp* app = ctx;
+ if(app->status) {
+ canvas_clear(canvas);
+ view_port_set_orientation(app->view_port, ViewPortOrientationHorizontal);
+ canvas_set_font(canvas, FontPrimary);
+ canvas_draw_str_aligned(canvas, 62, 5, AlignCenter, AlignTop, "Config is incorrect.");
+ canvas_set_font(canvas, FontSecondary);
+ canvas_draw_str_aligned(canvas, 62, 30, AlignCenter, AlignTop, "Please configure map.");
+ canvas_draw_str_aligned(canvas, 62, 60, AlignCenter, AlignBottom, "Press Back to Exit.");
+ } else {
+ canvas_clear(canvas);
+ view_port_set_orientation(app->view_port, ViewPortOrientationVertical);
+ canvas_draw_icon(canvas, 1, 5, &I_ButtonUp_7x4);
+ canvas_draw_icon(canvas, 1, 15, &I_ButtonDown_7x4);
+ canvas_draw_icon(canvas, 2, 23, &I_ButtonLeft_4x7);
+ canvas_draw_icon(canvas, 2, 33, &I_ButtonRight_4x7);
+ canvas_draw_icon(canvas, 0, 42, &I_Ok_btn_9x9);
+ canvas_draw_icon(canvas, 0, 53, &I_back_10px);
+
+ //Labels
+ canvas_set_font(canvas, FontSecondary);
+
+ canvas_draw_str_aligned(
+ canvas,
+ MENU_BTN_TXT_X,
+ 8,
+ AlignCenter,
+ AlignCenter,
+ furi_string_get_cstr(app->up_button));
+ canvas_draw_str_aligned(
+ canvas,
+ MENU_BTN_TXT_X,
+ 18,
+ AlignCenter,
+ AlignCenter,
+ furi_string_get_cstr(app->down_button));
+ canvas_draw_str_aligned(
+ canvas,
+ MENU_BTN_TXT_X,
+ 28,
+ AlignCenter,
+ AlignCenter,
+ furi_string_get_cstr(app->left_button));
+ canvas_draw_str_aligned(
+ canvas,
+ MENU_BTN_TXT_X,
+ 38,
+ AlignCenter,
+ AlignCenter,
+ furi_string_get_cstr(app->right_button));
+ canvas_draw_str_aligned(
+ canvas,
+ MENU_BTN_TXT_X,
+ 48,
+ AlignCenter,
+ AlignCenter,
+ furi_string_get_cstr(app->ok_button));
+ canvas_draw_str_aligned(
+ canvas,
+ MENU_BTN_TXT_X,
+ 58,
+ AlignCenter,
+ AlignCenter,
+ furi_string_get_cstr(app->back_button));
+
+ canvas_draw_line(canvas, 0, 65, 64, 65);
+
+ canvas_draw_icon(canvas, 1, 70, &I_ButtonUp_7x4);
+ canvas_draw_icon(canvas, 1, 80, &I_ButtonDown_7x4);
+ canvas_draw_icon(canvas, 2, 88, &I_ButtonLeft_4x7);
+ canvas_draw_icon(canvas, 2, 98, &I_ButtonRight_4x7);
+ canvas_draw_icon(canvas, 0, 107, &I_Ok_btn_9x9);
+ canvas_draw_icon(canvas, 0, 118, &I_back_10px);
+
+ canvas_draw_str_aligned(
+ canvas,
+ MENU_BTN_TXT_X,
+ 73,
+ AlignCenter,
+ AlignCenter,
+ furi_string_get_cstr(app->up_hold_button));
+ canvas_draw_str_aligned(
+ canvas,
+ MENU_BTN_TXT_X,
+ 83,
+ AlignCenter,
+ AlignCenter,
+ furi_string_get_cstr(app->down_hold_button));
+ canvas_draw_str_aligned(
+ canvas,
+ MENU_BTN_TXT_X,
+ 93,
+ AlignCenter,
+ AlignCenter,
+ furi_string_get_cstr(app->left_hold_button));
+ canvas_draw_str_aligned(
+ canvas,
+ MENU_BTN_TXT_X,
+ 103,
+ AlignCenter,
+ AlignCenter,
+ furi_string_get_cstr(app->right_hold_button));
+ canvas_draw_str_aligned(
+ canvas,
+ MENU_BTN_TXT_X,
+ 113,
+ AlignCenter,
+ AlignCenter,
+ furi_string_get_cstr(app->ok_hold_button));
+ canvas_draw_str_aligned(canvas, MENU_BTN_TXT_X, 123, AlignCenter, AlignCenter, "Exit App");
+ }
+}
+
+static void app_input_callback(InputEvent* input_event, void* ctx) {
+ furi_assert(ctx);
+
+ FuriMessageQueue* event_queue = ctx;
+ furi_message_queue_put(event_queue, input_event, FuriWaitForever);
+}
+
+int32_t infrared_remote_app(void* p) {
+ UNUSED(p);
+ FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent));
+ DOLPHIN_DEED(DolphinDeedPluginStart);
+
+ // App button string
+ IRApp* app = malloc(sizeof(IRApp));
+ app->up_button = furi_string_alloc();
+ app->down_button = furi_string_alloc();
+ app->left_button = furi_string_alloc();
+ app->right_button = furi_string_alloc();
+ app->ok_button = furi_string_alloc();
+ app->back_button = furi_string_alloc();
+ app->up_hold_button = furi_string_alloc();
+ app->down_hold_button = furi_string_alloc();
+ app->left_hold_button = furi_string_alloc();
+ app->right_hold_button = furi_string_alloc();
+ app->ok_hold_button = furi_string_alloc();
+ app->view_port = view_port_alloc();
+
+ // Configure view port
+ view_port_draw_callback_set(app->view_port, app_draw_callback, app);
+ view_port_input_callback_set(app->view_port, app_input_callback, event_queue);
+
+ // Register view port in GUI
+ Gui* gui = furi_record_open(RECORD_GUI);
+ gui_add_view_port(gui, app->view_port, GuiLayerFullscreen);
+
+ InputEvent event;
+
+ Storage* storage = furi_record_open(RECORD_STORAGE);
+ FlipperFormat* ff = flipper_format_file_alloc(storage);
+
+ DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS);
+ DialogsFileBrowserOptions browser_options;
+ dialog_file_browser_set_basic_options(&browser_options, ".txt", &I_sub1_10px);
+ FuriString* map_file = furi_string_alloc();
+ furi_string_set(map_file, "/ext/infrared/remote");
+ if(!storage_file_exists(storage, ANY_PATH("infrared/remote"))) {
+ storage_common_mkdir(storage, ANY_PATH("infrared/remote")); //Make Folder If dir not exist
+ }
+
+ bool res = dialog_file_browser_show(dialogs, map_file, map_file, &browser_options);
+
+ furi_record_close(RECORD_DIALOGS);
+
+ // if user didn't choose anything, free everything and exit
+ if(!res) {
+ FURI_LOG_I(TAG, "exit");
+ flipper_format_free(ff);
+ furi_record_close(RECORD_STORAGE);
+
+ furi_string_free(app->up_button);
+ furi_string_free(app->down_button);
+ furi_string_free(app->left_button);
+ furi_string_free(app->right_button);
+ furi_string_free(app->ok_button);
+ furi_string_free(app->back_button);
+ furi_string_free(app->up_hold_button);
+ furi_string_free(app->down_hold_button);
+ furi_string_free(app->left_hold_button);
+ furi_string_free(app->right_hold_button);
+ furi_string_free(app->ok_hold_button);
+
+ view_port_enabled_set(app->view_port, false);
+ gui_remove_view_port(gui, app->view_port);
+ view_port_free(app->view_port);
+ free(app);
+ furi_message_queue_free(event_queue);
+
+ furi_record_close(RECORD_GUI);
+ return 255;
+ }
+
+ InfraredRemote* remote = infrared_remote_alloc();
+ FuriString* remote_path = furi_string_alloc();
+
+ InfraredSignal* up_signal = infrared_signal_alloc();
+ InfraredSignal* down_signal = infrared_signal_alloc();
+ InfraredSignal* left_signal = infrared_signal_alloc();
+ InfraredSignal* right_signal = infrared_signal_alloc();
+ InfraredSignal* ok_signal = infrared_signal_alloc();
+ InfraredSignal* back_signal = infrared_signal_alloc();
+ InfraredSignal* up_hold_signal = infrared_signal_alloc();
+ InfraredSignal* down_hold_signal = infrared_signal_alloc();
+ InfraredSignal* left_hold_signal = infrared_signal_alloc();
+ InfraredSignal* right_hold_signal = infrared_signal_alloc();
+ InfraredSignal* ok_hold_signal = infrared_signal_alloc();
+
+ bool up_enabled = false;
+ bool down_enabled = false;
+ bool left_enabled = false;
+ bool right_enabled = false;
+ bool ok_enabled = false;
+ bool back_enabled = false;
+ bool up_hold_enabled = false;
+ bool down_hold_enabled = false;
+ bool left_hold_enabled = false;
+ bool right_hold_enabled = false;
+ bool ok_hold_enabled = false;
+
+ if(!flipper_format_file_open_existing(ff, furi_string_get_cstr(map_file))) {
+ FURI_LOG_E(TAG, "Could not open MAP file %s", furi_string_get_cstr(map_file));
+ app->status = 1;
+ } else {
+ //Filename Assignment/Check Start
+
+ if(!flipper_format_read_string(ff, "REMOTE", remote_path)) {
+ FURI_LOG_E(TAG, "Could not read REMOTE string");
+ app->status = 1;
+ } else {
+ if(!infrared_remote_load(remote, remote_path)) {
+ FURI_LOG_E(TAG, "Could not load ir file: %s", furi_string_get_cstr(remote_path));
+ app->status = 1;
+ } else {
+ FURI_LOG_I(TAG, "Loaded REMOTE file: %s", furi_string_get_cstr(remote_path));
+ }
+ }
+
+ //assign variables to values within map file
+ //set missing filenames to N/A
+ //assign button signals
+ size_t index = 0;
+
+ flipper_format_rewind(ff);
+ if(!flipper_format_read_string(ff, "UP", app->up_button)) {
+ FURI_LOG_W(TAG, "Could not read UP string");
+ furi_string_set(app->up_button, "N/A");
+ } else {
+ if(!infrared_remote_find_button_by_name(
+ remote, furi_string_get_cstr(app->up_button), &index)) {
+ FURI_LOG_W(TAG, "Error");
+ } else {
+ up_signal =
+ infrared_remote_button_get_signal(infrared_remote_get_button(remote, index));
+ up_enabled = true;
+ }
+ }
+
+ flipper_format_rewind(ff);
+ if(!flipper_format_read_string(ff, "DOWN", app->down_button)) {
+ FURI_LOG_W(TAG, "Could not read DOWN string");
+ furi_string_set(app->down_button, "N/A");
+ } else {
+ if(!infrared_remote_find_button_by_name(
+ remote, furi_string_get_cstr(app->down_button), &index)) {
+ FURI_LOG_W(TAG, "Error");
+ } else {
+ down_signal =
+ infrared_remote_button_get_signal(infrared_remote_get_button(remote, index));
+ down_enabled = true;
+ }
+ }
+
+ flipper_format_rewind(ff);
+ if(!flipper_format_read_string(ff, "LEFT", app->left_button)) {
+ FURI_LOG_W(TAG, "Could not read LEFT string");
+ furi_string_set(app->left_button, "N/A");
+ } else {
+ if(!infrared_remote_find_button_by_name(
+ remote, furi_string_get_cstr(app->left_button), &index)) {
+ FURI_LOG_W(TAG, "Error");
+ } else {
+ left_signal =
+ infrared_remote_button_get_signal(infrared_remote_get_button(remote, index));
+ left_enabled = true;
+ }
+ }
+
+ flipper_format_rewind(ff);
+ if(!flipper_format_read_string(ff, "RIGHT", app->right_button)) {
+ FURI_LOG_W(TAG, "Could not read RIGHT string");
+ furi_string_set(app->right_button, "N/A");
+ } else {
+ if(!infrared_remote_find_button_by_name(
+ remote, furi_string_get_cstr(app->right_button), &index)) {
+ FURI_LOG_W(TAG, "Error");
+ } else {
+ right_signal =
+ infrared_remote_button_get_signal(infrared_remote_get_button(remote, index));
+ right_enabled = true;
+ }
+ }
+
+ flipper_format_rewind(ff);
+ if(!flipper_format_read_string(ff, "OK", app->ok_button)) {
+ FURI_LOG_W(TAG, "Could not read OK string");
+ furi_string_set(app->ok_button, "N/A");
+ } else {
+ if(!infrared_remote_find_button_by_name(
+ remote, furi_string_get_cstr(app->ok_button), &index)) {
+ FURI_LOG_W(TAG, "Error");
+ } else {
+ ok_signal =
+ infrared_remote_button_get_signal(infrared_remote_get_button(remote, index));
+ ok_enabled = true;
+ }
+ }
+
+ flipper_format_rewind(ff);
+ if(!flipper_format_read_string(ff, "BACK", app->back_button)) {
+ FURI_LOG_W(TAG, "Could not read BACK string");
+ furi_string_set(app->back_button, "N/A");
+ } else {
+ if(!infrared_remote_find_button_by_name(
+ remote, furi_string_get_cstr(app->back_button), &index)) {
+ FURI_LOG_W(TAG, "Error");
+ } else {
+ back_signal =
+ infrared_remote_button_get_signal(infrared_remote_get_button(remote, index));
+ back_enabled = true;
+ }
+ }
+
+ flipper_format_rewind(ff);
+ if(!flipper_format_read_string(ff, "UPHOLD", app->up_hold_button)) {
+ FURI_LOG_W(TAG, "Could not read UPHOLD string");
+ furi_string_set(app->up_hold_button, "N/A");
+ } else {
+ if(!infrared_remote_find_button_by_name(
+ remote, furi_string_get_cstr(app->up_hold_button), &index)) {
+ FURI_LOG_W(TAG, "Error");
+ } else {
+ up_hold_signal =
+ infrared_remote_button_get_signal(infrared_remote_get_button(remote, index));
+ up_hold_enabled = true;
+ }
+ }
+
+ flipper_format_rewind(ff);
+ if(!flipper_format_read_string(ff, "DOWNHOLD", app->down_hold_button)) {
+ FURI_LOG_W(TAG, "Could not read DOWNHOLD string");
+ furi_string_set(app->down_hold_button, "N/A");
+ } else {
+ if(!infrared_remote_find_button_by_name(
+ remote, furi_string_get_cstr(app->down_hold_button), &index)) {
+ FURI_LOG_W(TAG, "Error");
+ } else {
+ down_hold_signal =
+ infrared_remote_button_get_signal(infrared_remote_get_button(remote, index));
+ down_hold_enabled = true;
+ }
+ }
+
+ flipper_format_rewind(ff);
+ if(!flipper_format_read_string(ff, "LEFTHOLD", app->left_hold_button)) {
+ FURI_LOG_W(TAG, "Could not read LEFTHOLD string");
+ furi_string_set(app->left_hold_button, "N/A");
+ } else {
+ if(!infrared_remote_find_button_by_name(
+ remote, furi_string_get_cstr(app->left_hold_button), &index)) {
+ FURI_LOG_W(TAG, "Error");
+ } else {
+ left_hold_signal =
+ infrared_remote_button_get_signal(infrared_remote_get_button(remote, index));
+ left_hold_enabled = true;
+ }
+ }
+
+ flipper_format_rewind(ff);
+ if(!flipper_format_read_string(ff, "RIGHTHOLD", app->right_hold_button)) {
+ FURI_LOG_W(TAG, "Could not read RIGHTHOLD string");
+ furi_string_set(app->right_hold_button, "N/A");
+ } else {
+ if(!infrared_remote_find_button_by_name(
+ remote, furi_string_get_cstr(app->right_hold_button), &index)) {
+ FURI_LOG_W(TAG, "Error");
+ } else {
+ right_hold_signal =
+ infrared_remote_button_get_signal(infrared_remote_get_button(remote, index));
+ right_hold_enabled = true;
+ }
+ }
+
+ flipper_format_rewind(ff);
+ if(!flipper_format_read_string(ff, "OKHOLD", app->ok_hold_button)) {
+ FURI_LOG_W(TAG, "Could not read OKHOLD string");
+ furi_string_set(app->ok_hold_button, "N/A");
+ } else {
+ if(!infrared_remote_find_button_by_name(
+ remote, furi_string_get_cstr(app->ok_hold_button), &index)) {
+ FURI_LOG_W(TAG, "Error");
+ } else {
+ ok_hold_signal =
+ infrared_remote_button_get_signal(infrared_remote_get_button(remote, index));
+ ok_hold_enabled = true;
+ }
+ }
+ }
+
+ furi_string_free(remote_path);
+
+ flipper_format_free(ff);
+ furi_record_close(RECORD_STORAGE);
+
+ bool running = true;
+ NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
+
+ if(app->status) {
+ view_port_update(app->view_port);
+ while(running) {
+ if(furi_message_queue_get(event_queue, &event, 100) == FuriStatusOk) {
+ if(event.type == InputTypeShort) {
+ switch(event.key) {
+ case InputKeyBack:
+ running = false;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+ } else {
+ view_port_update(app->view_port);
+ while(running) {
+ if(furi_message_queue_get(event_queue, &event, 100) == FuriStatusOk) {
+ // short press signal
+ if(event.type == InputTypeShort) {
+ switch(event.key) {
+ case InputKeyUp:
+ if(up_enabled) {
+ infrared_signal_transmit(up_signal);
+ notification_message(notification, &sequence_blink_start_magenta);
+ FURI_LOG_I(TAG, "up");
+ }
+ break;
+ case InputKeyDown:
+ if(down_enabled) {
+ infrared_signal_transmit(down_signal);
+ notification_message(notification, &sequence_blink_start_magenta);
+ FURI_LOG_I(TAG, "down");
+ }
+ break;
+ case InputKeyRight:
+ if(right_enabled) {
+ infrared_signal_transmit(right_signal);
+ notification_message(notification, &sequence_blink_start_magenta);
+ FURI_LOG_I(TAG, "right");
+ }
+ break;
+ case InputKeyLeft:
+ if(left_enabled) {
+ infrared_signal_transmit(left_signal);
+ notification_message(notification, &sequence_blink_start_magenta);
+ FURI_LOG_I(TAG, "left");
+ }
+ break;
+ case InputKeyOk:
+ if(ok_enabled) {
+ infrared_signal_transmit(ok_signal);
+ notification_message(notification, &sequence_blink_start_magenta);
+ FURI_LOG_I(TAG, "ok");
+ }
+ break;
+ case InputKeyBack:
+ if(back_enabled) {
+ infrared_signal_transmit(back_signal);
+ notification_message(notification, &sequence_blink_start_magenta);
+ FURI_LOG_I(TAG, "back");
+ }
+ break;
+ default:
+ running = false;
+ break;
+ }
+ // long press signal
+ } else if(event.type == InputTypeLong) {
+ switch(event.key) {
+ case InputKeyUp:
+ if(up_hold_enabled) {
+ infrared_signal_transmit(up_hold_signal);
+ notification_message(notification, &sequence_blink_start_magenta);
+ FURI_LOG_I(TAG, "up!");
+ }
+ break;
+ case InputKeyDown:
+ if(down_hold_enabled) {
+ infrared_signal_transmit(down_hold_signal);
+ notification_message(notification, &sequence_blink_start_magenta);
+ FURI_LOG_I(TAG, "down!");
+ }
+ break;
+ case InputKeyRight:
+ if(right_hold_enabled) {
+ infrared_signal_transmit(right_hold_signal);
+ notification_message(notification, &sequence_blink_start_magenta);
+ FURI_LOG_I(TAG, "right!");
+ }
+ break;
+ case InputKeyLeft:
+ if(left_hold_enabled) {
+ infrared_signal_transmit(left_hold_signal);
+ notification_message(notification, &sequence_blink_start_magenta);
+ FURI_LOG_I(TAG, "left!");
+ }
+ break;
+ case InputKeyOk:
+ if(ok_hold_enabled) {
+ infrared_signal_transmit(ok_hold_signal);
+ notification_message(notification, &sequence_blink_start_magenta);
+ FURI_LOG_I(TAG, "ok!");
+ }
+ break;
+ default:
+ running = false;
+ break;
+ }
+ } else if(event.type == InputTypeRelease) {
+ notification_message(notification, &sequence_blink_stop);
+ }
+ }
+ }
+ }
+
+ // Free all things
+ furi_string_free(app->up_button);
+ furi_string_free(app->down_button);
+ furi_string_free(app->left_button);
+ furi_string_free(app->right_button);
+ furi_string_free(app->ok_button);
+ furi_string_free(app->back_button);
+ furi_string_free(app->up_hold_button);
+ furi_string_free(app->down_hold_button);
+ furi_string_free(app->left_hold_button);
+ furi_string_free(app->right_hold_button);
+ furi_string_free(app->ok_hold_button);
+
+ infrared_remote_free(remote);
+ view_port_enabled_set(app->view_port, false);
+ gui_remove_view_port(gui, app->view_port);
+ view_port_free(app->view_port);
+ free(app);
+ furi_message_queue_free(event_queue);
+
+ furi_record_close(RECORD_NOTIFICATION);
+ furi_record_close(RECORD_GUI);
+
+ return 0;
+}
diff --git a/applications/external/ir_remote/infrared_remote_button.c b/applications/external/ir_remote/infrared_remote_button.c
new file mode 100644
index 000000000..1f6315ec5
--- /dev/null
+++ b/applications/external/ir_remote/infrared_remote_button.c
@@ -0,0 +1,37 @@
+#include "infrared_remote_button.h"
+
+#include
+
+struct InfraredRemoteButton {
+ FuriString* name;
+ InfraredSignal* signal;
+};
+
+InfraredRemoteButton* infrared_remote_button_alloc() {
+ InfraredRemoteButton* button = malloc(sizeof(InfraredRemoteButton));
+ button->name = furi_string_alloc();
+ button->signal = infrared_signal_alloc();
+ return button;
+}
+
+void infrared_remote_button_free(InfraredRemoteButton* button) {
+ furi_string_free(button->name);
+ infrared_signal_free(button->signal);
+ free(button);
+}
+
+void infrared_remote_button_set_name(InfraredRemoteButton* button, const char* name) {
+ furi_string_set(button->name, name);
+}
+
+const char* infrared_remote_button_get_name(InfraredRemoteButton* button) {
+ return furi_string_get_cstr(button->name);
+}
+
+void infrared_remote_button_set_signal(InfraredRemoteButton* button, InfraredSignal* signal) {
+ infrared_signal_set_signal(button->signal, signal);
+}
+
+InfraredSignal* infrared_remote_button_get_signal(InfraredRemoteButton* button) {
+ return button->signal;
+}
diff --git a/applications/external/ir_remote/infrared_remote_button.h b/applications/external/ir_remote/infrared_remote_button.h
new file mode 100644
index 000000000..f25b759b5
--- /dev/null
+++ b/applications/external/ir_remote/infrared_remote_button.h
@@ -0,0 +1,14 @@
+#pragma once
+
+#include "infrared_signal.h"
+
+typedef struct InfraredRemoteButton InfraredRemoteButton;
+
+InfraredRemoteButton* infrared_remote_button_alloc();
+void infrared_remote_button_free(InfraredRemoteButton* button);
+
+void infrared_remote_button_set_name(InfraredRemoteButton* button, const char* name);
+const char* infrared_remote_button_get_name(InfraredRemoteButton* button);
+
+void infrared_remote_button_set_signal(InfraredRemoteButton* button, InfraredSignal* signal);
+InfraredSignal* infrared_remote_button_get_signal(InfraredRemoteButton* button);
diff --git a/applications/external/ir_remote/infrared_signal.c b/applications/external/ir_remote/infrared_signal.c
new file mode 100644
index 000000000..0c7e3d3bf
--- /dev/null
+++ b/applications/external/ir_remote/infrared_signal.c
@@ -0,0 +1,300 @@
+#include "infrared_signal.h"
+
+#include
+#include
+#include
+#include
+#include
+
+#define TAG "InfraredSignal"
+
+struct InfraredSignal {
+ bool is_raw;
+ union {
+ InfraredMessage message;
+ InfraredRawSignal raw;
+ } payload;
+};
+
+static void infrared_signal_clear_timings(InfraredSignal* signal) {
+ if(signal->is_raw) {
+ free(signal->payload.raw.timings);
+ signal->payload.raw.timings_size = 0;
+ signal->payload.raw.timings = NULL;
+ }
+}
+
+static bool infrared_signal_is_message_valid(InfraredMessage* message) {
+ if(!infrared_is_protocol_valid(message->protocol)) {
+ FURI_LOG_E(TAG, "Unknown protocol");
+ return false;
+ }
+
+ uint32_t address_length = infrared_get_protocol_address_length(message->protocol);
+ uint32_t address_mask = (1UL << address_length) - 1;
+
+ if(message->address != (message->address & address_mask)) {
+ FURI_LOG_E(
+ TAG,
+ "Address is out of range (mask 0x%08lX): 0x%lX\r\n",
+ address_mask,
+ message->address);
+ return false;
+ }
+
+ uint32_t command_length = infrared_get_protocol_command_length(message->protocol);
+ uint32_t command_mask = (1UL << command_length) - 1;
+
+ if(message->command != (message->command & command_mask)) {
+ FURI_LOG_E(
+ TAG,
+ "Command is out of range (mask 0x%08lX): 0x%lX\r\n",
+ command_mask,
+ message->command);
+ return false;
+ }
+
+ return true;
+}
+
+static bool infrared_signal_is_raw_valid(InfraredRawSignal* raw) {
+ if((raw->frequency > INFRARED_MAX_FREQUENCY) || (raw->frequency < INFRARED_MIN_FREQUENCY)) {
+ FURI_LOG_E(
+ TAG,
+ "Frequency is out of range (%X - %X): %lX",
+ INFRARED_MIN_FREQUENCY,
+ INFRARED_MAX_FREQUENCY,
+ raw->frequency);
+ return false;
+
+ } else if((raw->duty_cycle <= 0) || (raw->duty_cycle > 1)) {
+ FURI_LOG_E(TAG, "Duty cycle is out of range (0 - 1): %f", (double)raw->duty_cycle);
+ return false;
+
+ } else if((raw->timings_size <= 0) || (raw->timings_size > MAX_TIMINGS_AMOUNT)) {
+ FURI_LOG_E(
+ TAG,
+ "Timings amount is out of range (0 - %X): %X",
+ MAX_TIMINGS_AMOUNT,
+ raw->timings_size);
+ return false;
+ }
+
+ return true;
+}
+
+static inline bool infrared_signal_save_message(InfraredMessage* message, FlipperFormat* ff) {
+ const char* protocol_name = infrared_get_protocol_name(message->protocol);
+ return flipper_format_write_string_cstr(ff, "type", "parsed") &&
+ flipper_format_write_string_cstr(ff, "protocol", protocol_name) &&
+ flipper_format_write_hex(ff, "address", (uint8_t*)&message->address, 4) &&
+ flipper_format_write_hex(ff, "command", (uint8_t*)&message->command, 4);
+}
+
+static inline bool infrared_signal_save_raw(InfraredRawSignal* raw, FlipperFormat* ff) {
+ furi_assert(raw->timings_size <= MAX_TIMINGS_AMOUNT);
+ return flipper_format_write_string_cstr(ff, "type", "raw") &&
+ flipper_format_write_uint32(ff, "frequency", &raw->frequency, 1) &&
+ flipper_format_write_float(ff, "duty_cycle", &raw->duty_cycle, 1) &&
+ flipper_format_write_uint32(ff, "data", raw->timings, raw->timings_size);
+}
+
+static inline bool infrared_signal_read_message(InfraredSignal* signal, FlipperFormat* ff) {
+ FuriString* buf;
+ buf = furi_string_alloc();
+ bool success = false;
+
+ do {
+ if(!flipper_format_read_string(ff, "protocol", buf)) break;
+
+ InfraredMessage message;
+ message.protocol = infrared_get_protocol_by_name(furi_string_get_cstr(buf));
+
+ success = flipper_format_read_hex(ff, "address", (uint8_t*)&message.address, 4) &&
+ flipper_format_read_hex(ff, "command", (uint8_t*)&message.command, 4) &&
+ infrared_signal_is_message_valid(&message);
+
+ if(!success) break;
+
+ infrared_signal_set_message(signal, &message);
+ } while(0);
+
+ furi_string_free(buf);
+ return success;
+}
+
+static inline bool infrared_signal_read_raw(InfraredSignal* signal, FlipperFormat* ff) {
+ uint32_t timings_size, frequency;
+ float duty_cycle;
+
+ bool success = flipper_format_read_uint32(ff, "frequency", &frequency, 1) &&
+ flipper_format_read_float(ff, "duty_cycle", &duty_cycle, 1) &&
+ flipper_format_get_value_count(ff, "data", &timings_size);
+
+ if(!success || timings_size > MAX_TIMINGS_AMOUNT) {
+ return false;
+ }
+
+ uint32_t* timings = malloc(sizeof(uint32_t) * timings_size);
+ success = flipper_format_read_uint32(ff, "data", timings, timings_size);
+
+ if(success) {
+ infrared_signal_set_raw_signal(signal, timings, timings_size, frequency, duty_cycle);
+ }
+
+ free(timings);
+ return success;
+}
+
+static bool infrared_signal_read_body(InfraredSignal* signal, FlipperFormat* ff) {
+ FuriString* tmp = furi_string_alloc();
+
+ bool success = false;
+
+ do {
+ if(!flipper_format_read_string(ff, "type", tmp)) break;
+ if(furi_string_equal(tmp, "raw")) {
+ success = infrared_signal_read_raw(signal, ff);
+ } else if(furi_string_equal(tmp, "parsed")) {
+ success = infrared_signal_read_message(signal, ff);
+ } else {
+ FURI_LOG_E(TAG, "Unknown signal type");
+ }
+ } while(false);
+
+ furi_string_free(tmp);
+ return success;
+}
+
+InfraredSignal* infrared_signal_alloc() {
+ InfraredSignal* signal = malloc(sizeof(InfraredSignal));
+
+ signal->is_raw = false;
+ signal->payload.message.protocol = InfraredProtocolUnknown;
+
+ return signal;
+}
+
+void infrared_signal_free(InfraredSignal* signal) {
+ infrared_signal_clear_timings(signal);
+ free(signal);
+}
+
+bool infrared_signal_is_raw(InfraredSignal* signal) {
+ return signal->is_raw;
+}
+
+bool infrared_signal_is_valid(InfraredSignal* signal) {
+ return signal->is_raw ? infrared_signal_is_raw_valid(&signal->payload.raw) :
+ infrared_signal_is_message_valid(&signal->payload.message);
+}
+
+void infrared_signal_set_signal(InfraredSignal* signal, const InfraredSignal* other) {
+ if(other->is_raw) {
+ const InfraredRawSignal* raw = &other->payload.raw;
+ infrared_signal_set_raw_signal(
+ signal, raw->timings, raw->timings_size, raw->frequency, raw->duty_cycle);
+ } else {
+ const InfraredMessage* message = &other->payload.message;
+ infrared_signal_set_message(signal, message);
+ }
+}
+
+void infrared_signal_set_raw_signal(
+ InfraredSignal* signal,
+ const uint32_t* timings,
+ size_t timings_size,
+ uint32_t frequency,
+ float duty_cycle) {
+ infrared_signal_clear_timings(signal);
+
+ signal->is_raw = true;
+
+ signal->payload.raw.timings_size = timings_size;
+ signal->payload.raw.frequency = frequency;
+ signal->payload.raw.duty_cycle = duty_cycle;
+
+ signal->payload.raw.timings = malloc(timings_size * sizeof(uint32_t));
+ memcpy(signal->payload.raw.timings, timings, timings_size * sizeof(uint32_t));
+}
+
+InfraredRawSignal* infrared_signal_get_raw_signal(InfraredSignal* signal) {
+ furi_assert(signal->is_raw);
+ return &signal->payload.raw;
+}
+
+void infrared_signal_set_message(InfraredSignal* signal, const InfraredMessage* message) {
+ infrared_signal_clear_timings(signal);
+
+ signal->is_raw = false;
+ signal->payload.message = *message;
+}
+
+InfraredMessage* infrared_signal_get_message(InfraredSignal* signal) {
+ furi_assert(!signal->is_raw);
+ return &signal->payload.message;
+}
+
+bool infrared_signal_save(InfraredSignal* signal, FlipperFormat* ff, const char* name) {
+ if(!flipper_format_write_comment_cstr(ff, "") ||
+ !flipper_format_write_string_cstr(ff, "name", name)) {
+ return false;
+ } else if(signal->is_raw) {
+ return infrared_signal_save_raw(&signal->payload.raw, ff);
+ } else {
+ return infrared_signal_save_message(&signal->payload.message, ff);
+ }
+}
+
+bool infrared_signal_read(InfraredSignal* signal, FlipperFormat* ff, FuriString* name) {
+ FuriString* tmp = furi_string_alloc();
+
+ bool success = false;
+
+ do {
+ if(!flipper_format_read_string(ff, "name", tmp)) break;
+ furi_string_set(name, tmp);
+ if(!infrared_signal_read_body(signal, ff)) break;
+ success = true;
+ } while(0);
+
+ furi_string_free(tmp);
+ return success;
+}
+
+bool infrared_signal_search_and_read(
+ InfraredSignal* signal,
+ FlipperFormat* ff,
+ const FuriString* name) {
+ bool success = false;
+ FuriString* tmp = furi_string_alloc();
+
+ do {
+ bool is_name_found = false;
+ while(flipper_format_read_string(ff, "name", tmp)) {
+ is_name_found = furi_string_equal(name, tmp);
+ if(is_name_found) break;
+ }
+ if(!is_name_found) break;
+ if(!infrared_signal_read_body(signal, ff)) break;
+ success = true;
+ } while(false);
+
+ furi_string_free(tmp);
+ return success;
+}
+
+void infrared_signal_transmit(InfraredSignal* signal) {
+ if(signal->is_raw) {
+ InfraredRawSignal* raw_signal = &signal->payload.raw;
+ infrared_send_raw_ext(
+ raw_signal->timings,
+ raw_signal->timings_size,
+ true,
+ raw_signal->frequency,
+ raw_signal->duty_cycle);
+ } else {
+ InfraredMessage* message = &signal->payload.message;
+ infrared_send(message, 2);
+ }
+}
diff --git a/applications/external/ir_remote/infrared_signal.h b/applications/external/ir_remote/infrared_signal.h
new file mode 100644
index 000000000..637d859b0
--- /dev/null
+++ b/applications/external/ir_remote/infrared_signal.h
@@ -0,0 +1,45 @@
+#pragma once
+
+#include
+#include
+#include
+
+#include
+#include
+
+typedef struct InfraredSignal InfraredSignal;
+
+typedef struct {
+ size_t timings_size;
+ uint32_t* timings;
+ uint32_t frequency;
+ float duty_cycle;
+} InfraredRawSignal;
+
+InfraredSignal* infrared_signal_alloc();
+void infrared_signal_free(InfraredSignal* signal);
+
+bool infrared_signal_is_raw(InfraredSignal* signal);
+bool infrared_signal_is_valid(InfraredSignal* signal);
+
+void infrared_signal_set_signal(InfraredSignal* signal, const InfraredSignal* other);
+
+void infrared_signal_set_raw_signal(
+ InfraredSignal* signal,
+ const uint32_t* timings,
+ size_t timings_size,
+ uint32_t frequency,
+ float duty_cycle);
+InfraredRawSignal* infrared_signal_get_raw_signal(InfraredSignal* signal);
+
+void infrared_signal_set_message(InfraredSignal* signal, const InfraredMessage* message);
+InfraredMessage* infrared_signal_get_message(InfraredSignal* signal);
+
+bool infrared_signal_save(InfraredSignal* signal, FlipperFormat* ff, const char* name);
+bool infrared_signal_read(InfraredSignal* signal, FlipperFormat* ff, FuriString* name);
+bool infrared_signal_search_and_read(
+ InfraredSignal* signal,
+ FlipperFormat* ff,
+ const FuriString* name);
+
+void infrared_signal_transmit(InfraredSignal* signal);
diff --git a/applications/external/ir_remote/ir_10px.png b/applications/external/ir_remote/ir_10px.png
new file mode 100644
index 000000000..22c986180
Binary files /dev/null and b/applications/external/ir_remote/ir_10px.png differ
diff --git a/applications/plugins/lightmeter/LICENSE b/applications/external/lightmeter/LICENSE
similarity index 100%
rename from applications/plugins/lightmeter/LICENSE
rename to applications/external/lightmeter/LICENSE
diff --git a/applications/plugins/lightmeter/application.fam b/applications/external/lightmeter/application.fam
similarity index 93%
rename from applications/plugins/lightmeter/application.fam
rename to applications/external/lightmeter/application.fam
index 8cd90ee26..7df664517 100644
--- a/applications/plugins/lightmeter/application.fam
+++ b/applications/external/lightmeter/application.fam
@@ -3,7 +3,6 @@ App(
name="[BH1750] Lightmeter",
apptype=FlipperAppType.EXTERNAL,
entry_point="lightmeter_app",
- cdefines=["APP_LIGHTMETER"],
requires=[
"gui",
],
diff --git a/applications/plugins/lightmeter/gui/scenes/config/lightmeter_scene.c b/applications/external/lightmeter/gui/scenes/config/lightmeter_scene.c
similarity index 100%
rename from applications/plugins/lightmeter/gui/scenes/config/lightmeter_scene.c
rename to applications/external/lightmeter/gui/scenes/config/lightmeter_scene.c
diff --git a/applications/plugins/lightmeter/gui/scenes/config/lightmeter_scene.h b/applications/external/lightmeter/gui/scenes/config/lightmeter_scene.h
similarity index 100%
rename from applications/plugins/lightmeter/gui/scenes/config/lightmeter_scene.h
rename to applications/external/lightmeter/gui/scenes/config/lightmeter_scene.h
diff --git a/applications/plugins/lightmeter/gui/scenes/config/lightmeter_scene_config.h b/applications/external/lightmeter/gui/scenes/config/lightmeter_scene_config.h
similarity index 100%
rename from applications/plugins/lightmeter/gui/scenes/config/lightmeter_scene_config.h
rename to applications/external/lightmeter/gui/scenes/config/lightmeter_scene_config.h
diff --git a/applications/plugins/lightmeter/gui/scenes/lightmeter_scene_about.c b/applications/external/lightmeter/gui/scenes/lightmeter_scene_about.c
similarity index 100%
rename from applications/plugins/lightmeter/gui/scenes/lightmeter_scene_about.c
rename to applications/external/lightmeter/gui/scenes/lightmeter_scene_about.c
diff --git a/applications/plugins/lightmeter/gui/scenes/lightmeter_scene_config.c b/applications/external/lightmeter/gui/scenes/lightmeter_scene_config.c
similarity index 100%
rename from applications/plugins/lightmeter/gui/scenes/lightmeter_scene_config.c
rename to applications/external/lightmeter/gui/scenes/lightmeter_scene_config.c
diff --git a/applications/plugins/lightmeter/gui/scenes/lightmeter_scene_help.c b/applications/external/lightmeter/gui/scenes/lightmeter_scene_help.c
similarity index 100%
rename from applications/plugins/lightmeter/gui/scenes/lightmeter_scene_help.c
rename to applications/external/lightmeter/gui/scenes/lightmeter_scene_help.c
diff --git a/applications/plugins/lightmeter/gui/scenes/lightmeter_scene_main.c b/applications/external/lightmeter/gui/scenes/lightmeter_scene_main.c
similarity index 100%
rename from applications/plugins/lightmeter/gui/scenes/lightmeter_scene_main.c
rename to applications/external/lightmeter/gui/scenes/lightmeter_scene_main.c
diff --git a/applications/plugins/lightmeter/gui/views/main_view.c b/applications/external/lightmeter/gui/views/main_view.c
similarity index 100%
rename from applications/plugins/lightmeter/gui/views/main_view.c
rename to applications/external/lightmeter/gui/views/main_view.c
diff --git a/applications/plugins/lightmeter/gui/views/main_view.h b/applications/external/lightmeter/gui/views/main_view.h
similarity index 100%
rename from applications/plugins/lightmeter/gui/views/main_view.h
rename to applications/external/lightmeter/gui/views/main_view.h
diff --git a/applications/plugins/lightmeter/icons/T_10x14.png b/applications/external/lightmeter/icons/T_10x14.png
similarity index 100%
rename from applications/plugins/lightmeter/icons/T_10x14.png
rename to applications/external/lightmeter/icons/T_10x14.png
diff --git a/applications/plugins/lightmeter/icons/f_10x14.png b/applications/external/lightmeter/icons/f_10x14.png
similarity index 100%
rename from applications/plugins/lightmeter/icons/f_10x14.png
rename to applications/external/lightmeter/icons/f_10x14.png
diff --git a/applications/plugins/lightmeter/lib/BH1750/BH1750.c b/applications/external/lightmeter/lib/BH1750/BH1750.c
similarity index 100%
rename from applications/plugins/lightmeter/lib/BH1750/BH1750.c
rename to applications/external/lightmeter/lib/BH1750/BH1750.c
diff --git a/applications/plugins/lightmeter/lib/BH1750/BH1750.h b/applications/external/lightmeter/lib/BH1750/BH1750.h
similarity index 100%
rename from applications/plugins/lightmeter/lib/BH1750/BH1750.h
rename to applications/external/lightmeter/lib/BH1750/BH1750.h
diff --git a/applications/plugins/lightmeter/lib/BH1750/LICENSE b/applications/external/lightmeter/lib/BH1750/LICENSE
similarity index 100%
rename from applications/plugins/lightmeter/lib/BH1750/LICENSE
rename to applications/external/lightmeter/lib/BH1750/LICENSE
diff --git a/applications/plugins/lightmeter/lib/BH1750/docs/BH1750.pdf b/applications/external/lightmeter/lib/BH1750/docs/BH1750.pdf
similarity index 100%
rename from applications/plugins/lightmeter/lib/BH1750/docs/BH1750.pdf
rename to applications/external/lightmeter/lib/BH1750/docs/BH1750.pdf
diff --git a/applications/plugins/lightmeter/lightmeter.c b/applications/external/lightmeter/lightmeter.c
similarity index 100%
rename from applications/plugins/lightmeter/lightmeter.c
rename to applications/external/lightmeter/lightmeter.c
diff --git a/applications/plugins/lightmeter/lightmeter.h b/applications/external/lightmeter/lightmeter.h
similarity index 100%
rename from applications/plugins/lightmeter/lightmeter.h
rename to applications/external/lightmeter/lightmeter.h
diff --git a/applications/plugins/lightmeter/lightmeter.png b/applications/external/lightmeter/lightmeter.png
similarity index 100%
rename from applications/plugins/lightmeter/lightmeter.png
rename to applications/external/lightmeter/lightmeter.png
diff --git a/applications/plugins/lightmeter/lightmeter_config.h b/applications/external/lightmeter/lightmeter_config.h
similarity index 100%
rename from applications/plugins/lightmeter/lightmeter_config.h
rename to applications/external/lightmeter/lightmeter_config.h
diff --git a/applications/plugins/lightmeter/lightmeter_helper.c b/applications/external/lightmeter/lightmeter_helper.c
similarity index 100%
rename from applications/plugins/lightmeter/lightmeter_helper.c
rename to applications/external/lightmeter/lightmeter_helper.c
diff --git a/applications/plugins/lightmeter/lightmeter_helper.h b/applications/external/lightmeter/lightmeter_helper.h
similarity index 100%
rename from applications/plugins/lightmeter/lightmeter_helper.h
rename to applications/external/lightmeter/lightmeter_helper.h
diff --git a/applications/plugins/mandelbrot/Mandelbrot.png b/applications/external/mandelbrot/Mandelbrot.png
similarity index 100%
rename from applications/plugins/mandelbrot/Mandelbrot.png
rename to applications/external/mandelbrot/Mandelbrot.png
diff --git a/applications/plugins/mandelbrot/application.fam b/applications/external/mandelbrot/application.fam
similarity index 100%
rename from applications/plugins/mandelbrot/application.fam
rename to applications/external/mandelbrot/application.fam
diff --git a/applications/plugins/mandelbrot/mandelbrot.c b/applications/external/mandelbrot/mandelbrot.c
similarity index 100%
rename from applications/plugins/mandelbrot/mandelbrot.c
rename to applications/external/mandelbrot/mandelbrot.c
diff --git a/applications/plugins/metronome/application.fam b/applications/external/metronome/application.fam
similarity index 90%
rename from applications/plugins/metronome/application.fam
rename to applications/external/metronome/application.fam
index 32588d06e..1a99825a8 100644
--- a/applications/plugins/metronome/application.fam
+++ b/applications/external/metronome/application.fam
@@ -3,7 +3,6 @@ App(
name="Metronome",
apptype=FlipperAppType.EXTERNAL,
entry_point="metronome_app",
- cdefines=["APP_METRONOME"],
requires=[
"gui",
],
diff --git a/applications/plugins/metronome/gui_extensions.c b/applications/external/metronome/gui_extensions.c
similarity index 100%
rename from applications/plugins/metronome/gui_extensions.c
rename to applications/external/metronome/gui_extensions.c
diff --git a/applications/plugins/metronome/gui_extensions.h b/applications/external/metronome/gui_extensions.h
similarity index 100%
rename from applications/plugins/metronome/gui_extensions.h
rename to applications/external/metronome/gui_extensions.h
diff --git a/applications/plugins/swd_probe/icons/ButtonUp_7x4.png b/applications/external/metronome/images/ButtonUp_7x4.png
similarity index 100%
rename from applications/plugins/swd_probe/icons/ButtonUp_7x4.png
rename to applications/external/metronome/images/ButtonUp_7x4.png
diff --git a/applications/plugins/metronome/img/screenshot.png b/applications/external/metronome/img/screenshot.png
similarity index 100%
rename from applications/plugins/metronome/img/screenshot.png
rename to applications/external/metronome/img/screenshot.png
diff --git a/applications/plugins/metronome/img/wave_left_4x14.png b/applications/external/metronome/img/wave_left_4x14.png
similarity index 100%
rename from applications/plugins/metronome/img/wave_left_4x14.png
rename to applications/external/metronome/img/wave_left_4x14.png
diff --git a/applications/plugins/metronome/img/wave_right_4x14.png b/applications/external/metronome/img/wave_right_4x14.png
similarity index 100%
rename from applications/plugins/metronome/img/wave_right_4x14.png
rename to applications/external/metronome/img/wave_right_4x14.png
diff --git a/applications/plugins/metronome/metronome.c b/applications/external/metronome/metronome.c
similarity index 100%
rename from applications/plugins/metronome/metronome.c
rename to applications/external/metronome/metronome.c
diff --git a/applications/plugins/metronome/metronome_icon.png b/applications/external/metronome/metronome_icon.png
similarity index 100%
rename from applications/plugins/metronome/metronome_icon.png
rename to applications/external/metronome/metronome_icon.png
diff --git a/applications/plugins/minesweeper/LICENSE b/applications/external/mfkey32/LICENSE
similarity index 100%
rename from applications/plugins/minesweeper/LICENSE
rename to applications/external/mfkey32/LICENSE
diff --git a/applications/external/mfkey32/application.fam b/applications/external/mfkey32/application.fam
new file mode 100644
index 000000000..47ae9c084
--- /dev/null
+++ b/applications/external/mfkey32/application.fam
@@ -0,0 +1,16 @@
+App(
+ appid="mfkey32",
+ name="Mfkey32",
+ apptype=FlipperAppType.EXTERNAL,
+ entry_point="mfkey32_main",
+ requires=[
+ "gui",
+ "storage",
+ ],
+ stack_size=1 * 1024,
+ fap_icon="mfkey.png",
+ fap_category="Tools",
+ fap_author="noproto",
+ fap_icon_assets="images",
+ fap_weburl="https://github.com/noproto/FlipperMfkey",
+)
diff --git a/applications/external/mfkey32/images/mfkey.png b/applications/external/mfkey32/images/mfkey.png
new file mode 100644
index 000000000..52ab29efb
Binary files /dev/null and b/applications/external/mfkey32/images/mfkey.png differ
diff --git a/applications/external/mfkey32/mfkey.png b/applications/external/mfkey32/mfkey.png
new file mode 100644
index 000000000..52ab29efb
Binary files /dev/null and b/applications/external/mfkey32/mfkey.png differ
diff --git a/applications/external/mfkey32/mfkey32.c b/applications/external/mfkey32/mfkey32.c
new file mode 100644
index 000000000..48d6aa64f
--- /dev/null
+++ b/applications/external/mfkey32/mfkey32.c
@@ -0,0 +1,1287 @@
+#pragma GCC optimize("O3")
+#pragma GCC optimize("-funroll-all-loops")
+
+// TODO: Handle back button correctly
+// TODO: Add keys to top of the user dictionary, not the bottom
+
+#include
+#include
+#include "time.h"
+#include
+#include
+#include
+#include
+#include "mfkey32_icons.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define MF_CLASSIC_DICT_FLIPPER_PATH EXT_PATH("nfc/assets/mf_classic_dict.nfc")
+#define MF_CLASSIC_DICT_USER_PATH EXT_PATH("nfc/assets/mf_classic_dict_user.nfc")
+#define MF_CLASSIC_NONCE_PATH EXT_PATH("nfc/.mfkey32.log")
+#define TAG "Mfkey32"
+#define NFC_MF_CLASSIC_KEY_LEN (13)
+
+// MSB_LIMIT: Chunk size (out of 256)
+#define MSB_LIMIT 16
+#define MIN_RAM 114500
+#define LF_POLY_ODD (0x29CE5C)
+#define LF_POLY_EVEN (0x870804)
+#define CONST_M1_1 (LF_POLY_EVEN << 1 | 1)
+#define CONST_M2_1 (LF_POLY_ODD << 1)
+#define CONST_M1_2 (LF_POLY_ODD)
+#define CONST_M2_2 (LF_POLY_EVEN << 1 | 1)
+#define BIT(x, n) ((x) >> (n)&1)
+#define BEBIT(x, n) BIT(x, (n) ^ 24)
+#define SWAPENDIAN(x) (x = (x >> 8 & 0xff00ff) | (x & 0xff00ff) << 8, x = x >> 16 | x << 16)
+//#define SIZEOF(arr) sizeof(arr) / sizeof(*arr)
+
+struct Crypto1State {
+ uint32_t odd, even;
+};
+struct Crypto1Params {
+ uint64_t key;
+ uint32_t nr0_enc, uid_xor_nt0, uid_xor_nt1, nr1_enc, p64b, ar1_enc;
+};
+struct Msb {
+ int tail;
+ uint32_t states[768];
+};
+
+typedef enum {
+ EventTypeTick,
+ EventTypeKey,
+} EventType;
+
+typedef struct {
+ EventType type;
+ InputEvent input;
+} PluginEvent;
+
+typedef enum {
+ MissingNonces,
+ ZeroNonces,
+ OutOfMemory,
+} MfkeyError;
+
+typedef enum {
+ Ready,
+ Initializing,
+ DictionaryAttack,
+ MfkeyAttack,
+ Complete,
+ Error,
+ Help,
+} MfkeyState;
+
+typedef struct {
+ FuriMutex* mutex;
+ MfkeyError err;
+ MfkeyState mfkey_state;
+ int cracked;
+ int unique_cracked;
+ int total;
+ int dict_count;
+ int search;
+ bool is_thread_running;
+ bool close_thread_please;
+ FuriThread* mfkeythread;
+} ProgramState;
+
+// TODO: Merge this with Crypto1Params?
+typedef struct {
+ uint32_t uid; // serial number
+ uint32_t nt0; // tag challenge first
+ uint32_t nt1; // tag challenge second
+ uint32_t nr0_enc; // first encrypted reader challenge
+ uint32_t ar0_enc; // first encrypted reader response
+ uint32_t nr1_enc; // second encrypted reader challenge
+ uint32_t ar1_enc; // second encrypted reader response
+} MfClassicNonce;
+
+typedef struct {
+ Stream* stream;
+ uint32_t total_nonces;
+ MfClassicNonce* remaining_nonce_array;
+ size_t remaining_nonces;
+} MfClassicNonceArray;
+
+struct MfClassicDict {
+ Stream* stream;
+ uint32_t total_keys;
+};
+
+static const uint8_t table[256] = {
+ 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3,
+ 4, 4, 5, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4,
+ 4, 5, 4, 5, 5, 6, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4,
+ 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5,
+ 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2,
+ 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5,
+ 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4,
+ 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6,
+ 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8};
+static const uint8_t lookup1[256] = {
+ 0, 0, 16, 16, 0, 16, 0, 0, 0, 16, 0, 0, 16, 16, 16, 16, 0, 0, 16, 16, 0, 16, 0, 0,
+ 0, 16, 0, 0, 16, 16, 16, 16, 0, 0, 16, 16, 0, 16, 0, 0, 0, 16, 0, 0, 16, 16, 16, 16,
+ 8, 8, 24, 24, 8, 24, 8, 8, 8, 24, 8, 8, 24, 24, 24, 24, 8, 8, 24, 24, 8, 24, 8, 8,
+ 8, 24, 8, 8, 24, 24, 24, 24, 8, 8, 24, 24, 8, 24, 8, 8, 8, 24, 8, 8, 24, 24, 24, 24,
+ 0, 0, 16, 16, 0, 16, 0, 0, 0, 16, 0, 0, 16, 16, 16, 16, 0, 0, 16, 16, 0, 16, 0, 0,
+ 0, 16, 0, 0, 16, 16, 16, 16, 8, 8, 24, 24, 8, 24, 8, 8, 8, 24, 8, 8, 24, 24, 24, 24,
+ 0, 0, 16, 16, 0, 16, 0, 0, 0, 16, 0, 0, 16, 16, 16, 16, 0, 0, 16, 16, 0, 16, 0, 0,
+ 0, 16, 0, 0, 16, 16, 16, 16, 8, 8, 24, 24, 8, 24, 8, 8, 8, 24, 8, 8, 24, 24, 24, 24,
+ 8, 8, 24, 24, 8, 24, 8, 8, 8, 24, 8, 8, 24, 24, 24, 24, 0, 0, 16, 16, 0, 16, 0, 0,
+ 0, 16, 0, 0, 16, 16, 16, 16, 8, 8, 24, 24, 8, 24, 8, 8, 8, 24, 8, 8, 24, 24, 24, 24,
+ 8, 8, 24, 24, 8, 24, 8, 8, 8, 24, 8, 8, 24, 24, 24, 24};
+static const uint8_t lookup2[256] = {
+ 0, 0, 4, 4, 0, 4, 0, 0, 0, 4, 0, 0, 4, 4, 4, 4, 0, 0, 4, 4, 0, 4, 0, 0, 0, 4, 0, 0, 4,
+ 4, 4, 4, 2, 2, 6, 6, 2, 6, 2, 2, 2, 6, 2, 2, 6, 6, 6, 6, 2, 2, 6, 6, 2, 6, 2, 2, 2, 6,
+ 2, 2, 6, 6, 6, 6, 0, 0, 4, 4, 0, 4, 0, 0, 0, 4, 0, 0, 4, 4, 4, 4, 2, 2, 6, 6, 2, 6, 2,
+ 2, 2, 6, 2, 2, 6, 6, 6, 6, 0, 0, 4, 4, 0, 4, 0, 0, 0, 4, 0, 0, 4, 4, 4, 4, 0, 0, 4, 4,
+ 0, 4, 0, 0, 0, 4, 0, 0, 4, 4, 4, 4, 0, 0, 4, 4, 0, 4, 0, 0, 0, 4, 0, 0, 4, 4, 4, 4, 2,
+ 2, 6, 6, 2, 6, 2, 2, 2, 6, 2, 2, 6, 6, 6, 6, 0, 0, 4, 4, 0, 4, 0, 0, 0, 4, 0, 0, 4, 4,
+ 4, 4, 0, 0, 4, 4, 0, 4, 0, 0, 0, 4, 0, 0, 4, 4, 4, 4, 2, 2, 6, 6, 2, 6, 2, 2, 2, 6, 2,
+ 2, 6, 6, 6, 6, 2, 2, 6, 6, 2, 6, 2, 2, 2, 6, 2, 2, 6, 6, 6, 6, 2, 2, 6, 6, 2, 6, 2, 2,
+ 2, 6, 2, 2, 6, 6, 6, 6, 2, 2, 6, 6, 2, 6, 2, 2, 2, 6, 2, 2, 6, 6, 6, 6};
+
+uint32_t prng_successor(uint32_t x, uint32_t n) {
+ SWAPENDIAN(x);
+ while(n--) x = x >> 1 | (x >> 16 ^ x >> 18 ^ x >> 19 ^ x >> 21) << 31;
+ return SWAPENDIAN(x);
+}
+
+static inline int filter(uint32_t const x) {
+ uint32_t f;
+ f = lookup1[x & 0xff] | lookup2[(x >> 8) & 0xff];
+ f |= 0x0d938 >> (x >> 16 & 0xf) & 1;
+ return BIT(0xEC57E80A, f);
+}
+
+static inline uint8_t evenparity32(uint32_t x) {
+ if((table[x & 0xff] + table[(x >> 8) & 0xff] + table[(x >> 16) & 0xff] + table[x >> 24]) % 2 ==
+ 0) {
+ return 0;
+ } else {
+ return 1;
+ }
+ //return ((table[x & 0xff] + table[(x >> 8) & 0xff] + table[(x >> 16) & 0xff] + table[x >> 24]) % 2) & 0xFF;
+}
+
+static inline void update_contribution(unsigned int data[], int item, int mask1, int mask2) {
+ int p = data[item] >> 25;
+ p = p << 1 | evenparity32(data[item] & mask1);
+ p = p << 1 | evenparity32(data[item] & mask2);
+ data[item] = p << 24 | (data[item] & 0xffffff);
+}
+
+void crypto1_get_lfsr(struct Crypto1State* state, uint64_t* lfsr) {
+ int i;
+ for(*lfsr = 0, i = 23; i >= 0; --i) {
+ *lfsr = *lfsr << 1 | BIT(state->odd, i ^ 3);
+ *lfsr = *lfsr << 1 | BIT(state->even, i ^ 3);
+ }
+}
+
+static inline uint32_t crypt_word(struct Crypto1State* s) {
+ // "in" and "x" are always 0 (last iteration)
+ uint32_t res_ret = 0;
+ uint32_t feedin, t;
+ for(int i = 0; i <= 31; i++) {
+ res_ret |= (filter(s->odd) << (24 ^ i));
+ feedin = LF_POLY_EVEN & s->even;
+ feedin ^= LF_POLY_ODD & s->odd;
+ s->even = s->even << 1 | (evenparity32(feedin));
+ t = s->odd, s->odd = s->even, s->even = t;
+ }
+ return res_ret;
+}
+
+static inline void crypt_word_noret(struct Crypto1State* s, uint32_t in, int x) {
+ uint8_t ret;
+ uint32_t feedin, t, next_in;
+ for(int i = 0; i <= 31; i++) {
+ next_in = BEBIT(in, i);
+ ret = filter(s->odd);
+ feedin = ret & (!!x);
+ feedin ^= LF_POLY_EVEN & s->even;
+ feedin ^= LF_POLY_ODD & s->odd;
+ feedin ^= !!next_in;
+ s->even = s->even << 1 | (evenparity32(feedin));
+ t = s->odd, s->odd = s->even, s->even = t;
+ }
+ return;
+}
+
+static inline void rollback_word_noret(struct Crypto1State* s, uint32_t in, int x) {
+ uint8_t ret;
+ uint32_t feedin, t, next_in;
+ for(int i = 31; i >= 0; i--) {
+ next_in = BEBIT(in, i);
+ s->odd &= 0xffffff;
+ t = s->odd, s->odd = s->even, s->even = t;
+ ret = filter(s->odd);
+ feedin = ret & (!!x);
+ feedin ^= s->even & 1;
+ feedin ^= LF_POLY_EVEN & (s->even >>= 1);
+ feedin ^= LF_POLY_ODD & s->odd;
+ feedin ^= !!next_in;
+ s->even |= (evenparity32(feedin)) << 23;
+ }
+ return;
+}
+
+int key_already_found_for_nonce(
+ uint64_t* keyarray,
+ int keyarray_size,
+ uint32_t uid_xor_nt1,
+ uint32_t nr1_enc,
+ uint32_t p64b,
+ uint32_t ar1_enc) {
+ for(int k = 0; k < keyarray_size; k++) {
+ struct Crypto1State temp = {0, 0};
+
+ for(int i = 0; i < 24; i++) {
+ (&temp)->odd |= (BIT(keyarray[k], 2 * i + 1) << (i ^ 3));
+ (&temp)->even |= (BIT(keyarray[k], 2 * i) << (i ^ 3));
+ }
+
+ crypt_word_noret(&temp, uid_xor_nt1, 0);
+ crypt_word_noret(&temp, nr1_enc, 1);
+
+ if(ar1_enc == (crypt_word(&temp) ^ p64b)) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+int check_state(struct Crypto1State* t, struct Crypto1Params* p) {
+ if(!(t->odd | t->even)) return 0;
+ rollback_word_noret(t, 0, 0);
+ rollback_word_noret(t, p->nr0_enc, 1);
+ rollback_word_noret(t, p->uid_xor_nt0, 0);
+ struct Crypto1State temp = {t->odd, t->even};
+ crypt_word_noret(t, p->uid_xor_nt1, 0);
+ crypt_word_noret(t, p->nr1_enc, 1);
+ if(p->ar1_enc == (crypt_word(t) ^ p->p64b)) {
+ crypto1_get_lfsr(&temp, &(p->key));
+ return 1;
+ }
+ return 0;
+}
+
+static inline int state_loop(unsigned int* states_buffer, int xks, int m1, int m2) {
+ int states_tail = 0;
+ int round = 0, s = 0, xks_bit = 0;
+
+ for(round = 1; round <= 12; round++) {
+ xks_bit = BIT(xks, round);
+
+ for(s = 0; s <= states_tail; s++) {
+ states_buffer[s] <<= 1;
+
+ if((filter(states_buffer[s]) ^ filter(states_buffer[s] | 1)) != 0) {
+ states_buffer[s] |= filter(states_buffer[s]) ^ xks_bit;
+ if(round > 4) {
+ update_contribution(states_buffer, s, m1, m2);
+ }
+ } else if(filter(states_buffer[s]) == xks_bit) {
+ // TODO: Refactor
+ if(round > 4) {
+ states_buffer[++states_tail] = states_buffer[s + 1];
+ states_buffer[s + 1] = states_buffer[s] | 1;
+ update_contribution(states_buffer, s, m1, m2);
+ s++;
+ update_contribution(states_buffer, s, m1, m2);
+ } else {
+ states_buffer[++states_tail] = states_buffer[++s];
+ states_buffer[s] = states_buffer[s - 1] | 1;
+ }
+ } else {
+ states_buffer[s--] = states_buffer[states_tail--];
+ }
+ }
+ }
+
+ return states_tail;
+}
+
+int binsearch(unsigned int data[], int start, int stop) {
+ int mid, val = data[stop] & 0xff000000;
+ while(start != stop) {
+ mid = (stop - start) >> 1;
+ if((data[start + mid] ^ 0x80000000) > (val ^ 0x80000000))
+ stop = start + mid;
+ else
+ start += mid + 1;
+ }
+ return start;
+}
+void quicksort(unsigned int array[], int low, int high) {
+ //if (SIZEOF(array) == 0)
+ // return;
+ if(low >= high) return;
+ int middle = low + (high - low) / 2;
+ unsigned int pivot = array[middle];
+ int i = low, j = high;
+ while(i <= j) {
+ while(array[i] < pivot) {
+ i++;
+ }
+ while(array[j] > pivot) {
+ j--;
+ }
+ if(i <= j) { // swap
+ int temp = array[i];
+ array[i] = array[j];
+ array[j] = temp;
+ i++;
+ j--;
+ }
+ }
+ if(low < j) {
+ quicksort(array, low, j);
+ }
+ if(high > i) {
+ quicksort(array, i, high);
+ }
+}
+int extend_table(unsigned int data[], int tbl, int end, int bit, int m1, int m2) {
+ for(data[tbl] <<= 1; tbl <= end; data[++tbl] <<= 1) {
+ if((filter(data[tbl]) ^ filter(data[tbl] | 1)) != 0) {
+ data[tbl] |= filter(data[tbl]) ^ bit;
+ update_contribution(data, tbl, m1, m2);
+ } else if(filter(data[tbl]) == bit) {
+ data[++end] = data[tbl + 1];
+ data[tbl + 1] = data[tbl] | 1;
+ update_contribution(data, tbl, m1, m2);
+ tbl++;
+ update_contribution(data, tbl, m1, m2);
+ } else {
+ data[tbl--] = data[end--];
+ }
+ }
+ return end;
+}
+
+int old_recover(
+ unsigned int odd[],
+ int o_head,
+ int o_tail,
+ int oks,
+ unsigned int even[],
+ int e_head,
+ int e_tail,
+ int eks,
+ int rem,
+ int s,
+ struct Crypto1Params* p,
+ int first_run) {
+ int o, e, i;
+ if(rem == -1) {
+ for(e = e_head; e <= e_tail; ++e) {
+ even[e] = (even[e] << 1) ^ evenparity32(even[e] & LF_POLY_EVEN);
+ for(o = o_head; o <= o_tail; ++o, ++s) {
+ struct Crypto1State temp = {0, 0};
+ temp.even = odd[o];
+ temp.odd = even[e] ^ evenparity32(odd[o] & LF_POLY_ODD);
+ if(check_state(&temp, p)) {
+ return -1;
+ }
+ }
+ }
+ return s;
+ }
+ if(first_run == 0) {
+ for(i = 0; (i < 4) && (rem-- != 0); i++) {
+ oks >>= 1;
+ eks >>= 1;
+ o_tail = extend_table(
+ odd, o_head, o_tail, oks & 1, LF_POLY_EVEN << 1 | 1, LF_POLY_ODD << 1);
+ if(o_head > o_tail) return s;
+ e_tail =
+ extend_table(even, e_head, e_tail, eks & 1, LF_POLY_ODD, LF_POLY_EVEN << 1 | 1);
+ if(e_head > e_tail) return s;
+ }
+ }
+ first_run = 0;
+ quicksort(odd, o_head, o_tail);
+ quicksort(even, e_head, e_tail);
+ while(o_tail >= o_head && e_tail >= e_head) {
+ if(((odd[o_tail] ^ even[e_tail]) >> 24) == 0) {
+ o_tail = binsearch(odd, o_head, o = o_tail);
+ e_tail = binsearch(even, e_head, e = e_tail);
+ s = old_recover(odd, o_tail--, o, oks, even, e_tail--, e, eks, rem, s, p, first_run);
+ if(s == -1) {
+ break;
+ }
+ } else if((odd[o_tail] ^ 0x80000000) > (even[e_tail] ^ 0x80000000)) {
+ o_tail = binsearch(odd, o_head, o_tail) - 1;
+ } else {
+ e_tail = binsearch(even, e_head, e_tail) - 1;
+ }
+ }
+ return s;
+}
+
+int calculate_msb_tables(
+ int oks,
+ int eks,
+ int msb_round,
+ struct Crypto1Params* p,
+ unsigned int* states_buffer,
+ struct Msb* odd_msbs,
+ struct Msb* even_msbs,
+ unsigned int* temp_states_odd,
+ unsigned int* temp_states_even) {
+ //FURI_LOG_I(TAG, "MSB GO %i", msb_iter); // DEBUG
+ unsigned int msb_head = (MSB_LIMIT * msb_round); // msb_iter ranges from 0 to (256/MSB_LIMIT)-1
+ unsigned int msb_tail = (MSB_LIMIT * (msb_round + 1));
+ int states_tail = 0, tail = 0;
+ int i = 0, j = 0, semi_state = 0, found = 0;
+ unsigned int msb = 0;
+ // TODO: Why is this necessary?
+ memset(odd_msbs, 0, MSB_LIMIT * sizeof(struct Msb));
+ memset(even_msbs, 0, MSB_LIMIT * sizeof(struct Msb));
+
+ for(semi_state = 1 << 20; semi_state >= 0; semi_state--) {
+ //if (main_iter % 2048 == 0) {
+ // FURI_LOG_I(TAG, "On main_iter %i", main_iter); // DEBUG
+ //}
+ if(filter(semi_state) == (oks & 1)) {
+ states_buffer[0] = semi_state;
+ states_tail = state_loop(states_buffer, oks, CONST_M1_1, CONST_M2_1);
+
+ for(i = states_tail; i >= 0; i--) {
+ msb = states_buffer[i] >> 24;
+ if((msb >= msb_head) && (msb < msb_tail)) {
+ found = 0;
+ for(j = 0; j < odd_msbs[msb - msb_head].tail - 1; j++) {
+ if(odd_msbs[msb - msb_head].states[j] == states_buffer[i]) {
+ found = 1;
+ break;
+ }
+ }
+
+ if(!found) {
+ tail = odd_msbs[msb - msb_head].tail++;
+ odd_msbs[msb - msb_head].states[tail] = states_buffer[i];
+ }
+ }
+ }
+ }
+
+ if(filter(semi_state) == (eks & 1)) {
+ states_buffer[0] = semi_state;
+ states_tail = state_loop(states_buffer, eks, CONST_M1_2, CONST_M2_2);
+
+ for(i = 0; i <= states_tail; i++) {
+ msb = states_buffer[i] >> 24;
+ if((msb >= msb_head) && (msb < msb_tail)) {
+ found = 0;
+
+ for(j = 0; j < even_msbs[msb - msb_head].tail; j++) {
+ if(even_msbs[msb - msb_head].states[j] == states_buffer[i]) {
+ found = 1;
+ break;
+ }
+ }
+
+ if(!found) {
+ tail = even_msbs[msb - msb_head].tail++;
+ even_msbs[msb - msb_head].states[tail] = states_buffer[i];
+ }
+ }
+ }
+ }
+ }
+
+ oks >>= 12;
+ eks >>= 12;
+
+ for(i = 0; i < MSB_LIMIT; i++) {
+ memcpy(temp_states_odd, odd_msbs[i].states, odd_msbs[i].tail * sizeof(unsigned int));
+ memcpy(temp_states_even, even_msbs[i].states, even_msbs[i].tail * sizeof(unsigned int));
+ int res = old_recover(
+ temp_states_odd,
+ 0,
+ odd_msbs[i].tail,
+ oks,
+ temp_states_even,
+ 0,
+ even_msbs[i].tail,
+ eks,
+ 3,
+ 0,
+ p,
+ 1);
+ if(res == -1) {
+ return 1;
+ }
+ //odd_msbs[i].tail = 0;
+ //even_msbs[i].tail = 0;
+ }
+
+ return 0;
+}
+
+int recover(struct Crypto1Params* p, int ks2, ProgramState* const program_state) {
+ unsigned int* states_buffer = malloc(sizeof(unsigned int) * (2 << 9));
+ struct Msb* odd_msbs = (struct Msb*)malloc(MSB_LIMIT * sizeof(struct Msb));
+ struct Msb* even_msbs = (struct Msb*)malloc(MSB_LIMIT * sizeof(struct Msb));
+ unsigned int* temp_states_odd = malloc(sizeof(unsigned int) * (1280));
+ unsigned int* temp_states_even = malloc(sizeof(unsigned int) * (1280));
+ int oks = 0, eks = 0;
+ int i = 0, msb = 0;
+ for(i = 31; i >= 0; i -= 2) {
+ oks = oks << 1 | BEBIT(ks2, i);
+ }
+ for(i = 30; i >= 0; i -= 2) {
+ eks = eks << 1 | BEBIT(ks2, i);
+ }
+ int bench_start = furi_hal_rtc_get_timestamp();
+ for(msb = 0; msb <= ((256 / MSB_LIMIT) - 1); msb++) {
+ //printf("MSB: %i\n", msb);
+ program_state->search = msb;
+ if(calculate_msb_tables(
+ oks,
+ eks,
+ msb,
+ p,
+ states_buffer,
+ odd_msbs,
+ even_msbs,
+ temp_states_odd,
+ temp_states_even)) {
+ int bench_stop = furi_hal_rtc_get_timestamp();
+ FURI_LOG_I(TAG, "Cracked in %i seconds", bench_stop - bench_start);
+ free(states_buffer);
+ free(odd_msbs);
+ free(even_msbs);
+ free(temp_states_odd);
+ free(temp_states_even);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+bool napi_mf_classic_dict_check_presence(MfClassicDictType dict_type) {
+ Storage* storage = furi_record_open(RECORD_STORAGE);
+
+ bool dict_present = false;
+ if(dict_type == MfClassicDictTypeSystem) {
+ dict_present = storage_common_stat(storage, MF_CLASSIC_DICT_FLIPPER_PATH, NULL) == FSE_OK;
+ } else if(dict_type == MfClassicDictTypeUser) {
+ dict_present = storage_common_stat(storage, MF_CLASSIC_DICT_USER_PATH, NULL) == FSE_OK;
+ }
+
+ furi_record_close(RECORD_STORAGE);
+
+ return dict_present;
+}
+
+MfClassicDict* napi_mf_classic_dict_alloc(MfClassicDictType dict_type) {
+ MfClassicDict* dict = malloc(sizeof(MfClassicDict));
+ Storage* storage = furi_record_open(RECORD_STORAGE);
+ dict->stream = buffered_file_stream_alloc(storage);
+ furi_record_close(RECORD_STORAGE);
+
+ bool dict_loaded = false;
+ do {
+ if(dict_type == MfClassicDictTypeSystem) {
+ if(!buffered_file_stream_open(
+ dict->stream,
+ MF_CLASSIC_DICT_FLIPPER_PATH,
+ FSAM_READ_WRITE,
+ FSOM_OPEN_EXISTING)) {
+ buffered_file_stream_close(dict->stream);
+ break;
+ }
+ } else if(dict_type == MfClassicDictTypeUser) {
+ if(!buffered_file_stream_open(
+ dict->stream, MF_CLASSIC_DICT_USER_PATH, FSAM_READ_WRITE, FSOM_OPEN_ALWAYS)) {
+ buffered_file_stream_close(dict->stream);
+ break;
+ }
+ }
+
+ // Check for newline ending
+ if(!stream_eof(dict->stream)) {
+ if(!stream_seek(dict->stream, -1, StreamOffsetFromEnd)) break;
+ uint8_t last_char = 0;
+ if(stream_read(dict->stream, &last_char, 1) != 1) break;
+ if(last_char != '\n') {
+ FURI_LOG_D(TAG, "Adding new line ending");
+ if(stream_write_char(dict->stream, '\n') != 1) break;
+ }
+ if(!stream_rewind(dict->stream)) break;
+ }
+
+ // Read total amount of keys
+ FuriString* next_line;
+ next_line = furi_string_alloc();
+ while(true) {
+ if(!stream_read_line(dict->stream, next_line)) {
+ FURI_LOG_T(TAG, "No keys left in dict");
+ break;
+ }
+ FURI_LOG_T(
+ TAG,
+ "Read line: %s, len: %zu",
+ furi_string_get_cstr(next_line),
+ furi_string_size(next_line));
+ if(furi_string_get_char(next_line, 0) == '#') continue;
+ if(furi_string_size(next_line) != NFC_MF_CLASSIC_KEY_LEN) continue;
+ dict->total_keys++;
+ }
+ furi_string_free(next_line);
+ stream_rewind(dict->stream);
+
+ dict_loaded = true;
+ FURI_LOG_I(TAG, "Loaded dictionary with %lu keys", dict->total_keys);
+ } while(false);
+
+ if(!dict_loaded) {
+ buffered_file_stream_close(dict->stream);
+ free(dict);
+ dict = NULL;
+ }
+
+ return dict;
+}
+
+bool napi_mf_classic_dict_add_key_str(MfClassicDict* dict, FuriString* key) {
+ furi_assert(dict);
+ furi_assert(dict->stream);
+ FURI_LOG_I(TAG, "Saving key: %s", furi_string_get_cstr(key));
+
+ furi_string_cat_printf(key, "\n");
+
+ bool key_added = false;
+ do {
+ if(!stream_seek(dict->stream, 0, StreamOffsetFromEnd)) break;
+ if(!stream_insert_string(dict->stream, key)) break;
+ dict->total_keys++;
+ key_added = true;
+ } while(false);
+
+ furi_string_left(key, 12);
+ return key_added;
+}
+
+void napi_mf_classic_dict_free(MfClassicDict* dict) {
+ furi_assert(dict);
+ furi_assert(dict->stream);
+
+ buffered_file_stream_close(dict->stream);
+ stream_free(dict->stream);
+ free(dict);
+}
+
+static void napi_mf_classic_dict_int_to_str(uint8_t* key_int, FuriString* key_str) {
+ furi_string_reset(key_str);
+ for(size_t i = 0; i < 6; i++) {
+ furi_string_cat_printf(key_str, "%02X", key_int[i]);
+ }
+}
+
+static void napi_mf_classic_dict_str_to_int(FuriString* key_str, uint64_t* key_int) {
+ uint8_t key_byte_tmp;
+
+ *key_int = 0ULL;
+ for(uint8_t i = 0; i < 12; i += 2) {
+ args_char_to_hex(
+ furi_string_get_char(key_str, i), furi_string_get_char(key_str, i + 1), &key_byte_tmp);
+ *key_int |= (uint64_t)key_byte_tmp << (8 * (5 - i / 2));
+ }
+}
+
+uint32_t napi_mf_classic_dict_get_total_keys(MfClassicDict* dict) {
+ furi_assert(dict);
+
+ return dict->total_keys;
+}
+
+bool napi_mf_classic_dict_rewind(MfClassicDict* dict) {
+ furi_assert(dict);
+ furi_assert(dict->stream);
+
+ return stream_rewind(dict->stream);
+}
+
+bool napi_mf_classic_dict_get_next_key_str(MfClassicDict* dict, FuriString* key) {
+ furi_assert(dict);
+ furi_assert(dict->stream);
+
+ bool key_read = false;
+ furi_string_reset(key);
+ while(!key_read) {
+ if(!stream_read_line(dict->stream, key)) break;
+ if(furi_string_get_char(key, 0) == '#') continue;
+ if(furi_string_size(key) != NFC_MF_CLASSIC_KEY_LEN) continue;
+ furi_string_left(key, 12);
+ key_read = true;
+ }
+
+ return key_read;
+}
+
+bool napi_mf_classic_dict_get_next_key(MfClassicDict* dict, uint64_t* key) {
+ furi_assert(dict);
+ furi_assert(dict->stream);
+
+ FuriString* temp_key;
+ temp_key = furi_string_alloc();
+ bool key_read = napi_mf_classic_dict_get_next_key_str(dict, temp_key);
+ if(key_read) {
+ napi_mf_classic_dict_str_to_int(temp_key, key);
+ }
+ furi_string_free(temp_key);
+ return key_read;
+}
+
+bool napi_mf_classic_dict_is_key_present_str(MfClassicDict* dict, FuriString* key) {
+ furi_assert(dict);
+ furi_assert(dict->stream);
+
+ FuriString* next_line;
+ next_line = furi_string_alloc();
+
+ bool key_found = false;
+ stream_rewind(dict->stream);
+ while(!key_found) { //-V654
+ if(!stream_read_line(dict->stream, next_line)) break;
+ if(furi_string_get_char(next_line, 0) == '#') continue;
+ if(furi_string_size(next_line) != NFC_MF_CLASSIC_KEY_LEN) continue;
+ furi_string_left(next_line, 12);
+ if(!furi_string_equal(key, next_line)) continue;
+ key_found = true;
+ }
+
+ furi_string_free(next_line);
+ return key_found;
+}
+
+bool napi_mf_classic_dict_is_key_present(MfClassicDict* dict, uint8_t* key) {
+ FuriString* temp_key;
+
+ temp_key = furi_string_alloc();
+ napi_mf_classic_dict_int_to_str(key, temp_key);
+ bool key_found = napi_mf_classic_dict_is_key_present_str(dict, temp_key);
+ furi_string_free(temp_key);
+ return key_found;
+}
+
+bool napi_key_already_found_for_nonce(
+ MfClassicDict* dict,
+ uint32_t uid_xor_nt1,
+ uint32_t nr1_enc,
+ uint32_t p64b,
+ uint32_t ar1_enc) {
+ bool found = false;
+ uint64_t k = 0;
+ napi_mf_classic_dict_rewind(dict);
+ while(napi_mf_classic_dict_get_next_key(dict, &k)) {
+ struct Crypto1State temp = {0, 0};
+ int i;
+ for(i = 0; i < 24; i++) {
+ (&temp)->odd |= (BIT(k, 2 * i + 1) << (i ^ 3));
+ (&temp)->even |= (BIT(k, 2 * i) << (i ^ 3));
+ }
+ crypt_word_noret(&temp, uid_xor_nt1, 0);
+ crypt_word_noret(&temp, nr1_enc, 1);
+ if(ar1_enc == (crypt_word(&temp) ^ p64b)) {
+ found = true;
+ break;
+ }
+ }
+ return found;
+}
+
+bool napi_mf_classic_nonces_check_presence() {
+ Storage* storage = furi_record_open(RECORD_STORAGE);
+
+ bool nonces_present = storage_common_stat(storage, MF_CLASSIC_NONCE_PATH, NULL) == FSE_OK;
+
+ furi_record_close(RECORD_STORAGE);
+
+ return nonces_present;
+}
+
+MfClassicNonceArray* napi_mf_classic_nonce_array_alloc(
+ MfClassicDict* system_dict,
+ bool system_dict_exists,
+ MfClassicDict* user_dict,
+ bool user_dict_exists,
+ ProgramState* const program_state) {
+ MfClassicNonceArray* nonce_array = malloc(sizeof(MfClassicNonceArray));
+ MfClassicNonce* remaining_nonce_array_init = malloc(sizeof(MfClassicNonce) * 1);
+ nonce_array->remaining_nonce_array = remaining_nonce_array_init;
+ Storage* storage = furi_record_open(RECORD_STORAGE);
+ nonce_array->stream = buffered_file_stream_alloc(storage);
+ furi_record_close(RECORD_STORAGE);
+
+ bool array_loaded = false;
+ do {
+ // https://github.com/flipperdevices/flipperzero-firmware/blob/5134f44c09d39344a8747655c0d59864bb574b96/applications/services/storage/filesystem_api_defines.h#L8-L22
+ if(!buffered_file_stream_open(
+ nonce_array->stream, MF_CLASSIC_NONCE_PATH, FSAM_READ_WRITE, FSOM_OPEN_EXISTING)) {
+ buffered_file_stream_close(nonce_array->stream);
+ break;
+ }
+
+ // Check for newline ending
+ if(!stream_eof(nonce_array->stream)) {
+ if(!stream_seek(nonce_array->stream, -1, StreamOffsetFromEnd)) break;
+ uint8_t last_char = 0;
+ if(stream_read(nonce_array->stream, &last_char, 1) != 1) break;
+ if(last_char != '\n') {
+ FURI_LOG_D(TAG, "Adding new line ending");
+ if(stream_write_char(nonce_array->stream, '\n') != 1) break;
+ }
+ if(!stream_rewind(nonce_array->stream)) break;
+ }
+
+ // Read total amount of nonces
+ FuriString* next_line;
+ next_line = furi_string_alloc();
+ while(true) {
+ if(!stream_read_line(nonce_array->stream, next_line)) {
+ FURI_LOG_T(TAG, "No nonces left");
+ break;
+ }
+ FURI_LOG_T(
+ TAG,
+ "Read line: %s, len: %zu",
+ furi_string_get_cstr(next_line),
+ furi_string_size(next_line));
+ if(!furi_string_start_with_str(next_line, "Sec")) continue;
+ const char* next_line_cstr = furi_string_get_cstr(next_line);
+ MfClassicNonce res = {0};
+ char token[20];
+ int i = 0;
+ const char* ptr = next_line_cstr;
+ while(sscanf(ptr, "%s", token) == 1) {
+ switch(i) {
+ case 5:
+ sscanf(token, "%lx", &res.uid);
+ break;
+ case 7:
+ sscanf(token, "%lx", &res.nt0);
+ break;
+ case 9:
+ sscanf(token, "%lx", &res.nr0_enc);
+ break;
+ case 11:
+ sscanf(token, "%lx", &res.ar0_enc);
+ break;
+ case 13:
+ sscanf(token, "%lx", &res.nt1);
+ break;
+ case 15:
+ sscanf(token, "%lx", &res.nr1_enc);
+ break;
+ case 17:
+ sscanf(token, "%lx", &res.ar1_enc);
+ break;
+ default:
+ break; // Do nothing
+ }
+ i++;
+ ptr = strchr(ptr, ' ');
+ if(!ptr) {
+ break;
+ }
+ ptr++;
+ }
+ (program_state->total)++;
+ uint32_t p64b = prng_successor(res.nt1, 64);
+ if((system_dict_exists &&
+ napi_key_already_found_for_nonce(
+ system_dict, res.uid ^ res.nt1, res.nr1_enc, p64b, res.ar1_enc)) ||
+ (user_dict_exists &&
+ napi_key_already_found_for_nonce(
+ user_dict, res.uid ^ res.nt1, res.nr1_enc, p64b, res.ar1_enc))) {
+ (program_state->cracked)++;
+ continue;
+ }
+ FURI_LOG_I(TAG, "No key found for %lx %lx", res.uid, res.ar1_enc);
+ // TODO: Refactor
+ nonce_array->remaining_nonce_array = realloc(
+ nonce_array->remaining_nonce_array,
+ sizeof(MfClassicNonce) * ((nonce_array->remaining_nonces) + 1));
+ nonce_array->remaining_nonces++;
+ nonce_array->remaining_nonce_array[(nonce_array->remaining_nonces) - 1] = res;
+ nonce_array->total_nonces++;
+ }
+ furi_string_free(next_line);
+ stream_rewind(nonce_array->stream);
+
+ array_loaded = true;
+ FURI_LOG_I(TAG, "Loaded %lu nonces", nonce_array->total_nonces);
+ } while(false);
+
+ if(!array_loaded) {
+ buffered_file_stream_close(nonce_array->stream);
+ free(nonce_array);
+ nonce_array = NULL;
+ }
+
+ return nonce_array;
+}
+
+void napi_mf_classic_nonce_array_free(MfClassicNonceArray* nonce_array) {
+ furi_assert(nonce_array);
+ furi_assert(nonce_array->stream);
+
+ buffered_file_stream_close(nonce_array->stream);
+ stream_free(nonce_array->stream);
+ free(nonce_array);
+}
+
+static void finished_beep() {
+ // Beep to indicate completion if the speaker is available
+ if(furi_hal_speaker_acquire(1000)) { // Wait up to a second for the speaker
+ float freq = 3000;
+ float volume = 1.0f; // 100% volume
+ furi_hal_speaker_start(freq, volume);
+ furi_delay_ms(75);
+ furi_hal_speaker_stop();
+ furi_hal_speaker_release();
+ }
+}
+
+void mfkey32(ProgramState* const program_state) {
+ uint64_t found_key; // recovered key
+ size_t keyarray_size = 0;
+ uint64_t* keyarray = malloc(sizeof(uint64_t) * 1);
+ uint32_t i = 0;
+ // Check for nonces
+ if(!napi_mf_classic_nonces_check_presence()) {
+ program_state->err = MissingNonces;
+ program_state->mfkey_state = Error;
+ return;
+ }
+ // Read dictionaries (optional)
+ MfClassicDict* system_dict = {0};
+ bool system_dict_exists = napi_mf_classic_dict_check_presence(MfClassicDictTypeSystem);
+ MfClassicDict* user_dict = {0};
+ bool user_dict_exists = napi_mf_classic_dict_check_presence(MfClassicDictTypeUser);
+ uint32_t total_dict_keys = 0;
+ if(system_dict_exists) {
+ system_dict = napi_mf_classic_dict_alloc(MfClassicDictTypeSystem);
+ total_dict_keys += napi_mf_classic_dict_get_total_keys(system_dict);
+ }
+ user_dict = napi_mf_classic_dict_alloc(MfClassicDictTypeUser);
+ if(user_dict_exists) {
+ total_dict_keys += napi_mf_classic_dict_get_total_keys(user_dict);
+ }
+ user_dict_exists = true;
+ program_state->dict_count = total_dict_keys;
+ program_state->mfkey_state = DictionaryAttack;
+ // Read nonces
+ MfClassicNonceArray* nonce_arr;
+ nonce_arr = napi_mf_classic_nonce_array_alloc(
+ system_dict, system_dict_exists, user_dict, user_dict_exists, program_state);
+ if(system_dict_exists) {
+ napi_mf_classic_dict_free(system_dict);
+ }
+ if(nonce_arr->total_nonces == 0) {
+ // Nothing to crack
+ program_state->err = ZeroNonces;
+ program_state->mfkey_state = Error;
+ napi_mf_classic_nonce_array_free(nonce_arr);
+ napi_mf_classic_dict_free(user_dict);
+ free(keyarray);
+ return;
+ }
+ if(memmgr_get_free_heap() < MIN_RAM) {
+ // Insufficient RAM
+ program_state->err = OutOfMemory;
+ program_state->mfkey_state = Error;
+ napi_mf_classic_nonce_array_free(nonce_arr);
+ napi_mf_classic_dict_free(user_dict);
+ free(keyarray);
+ return;
+ }
+ program_state->mfkey_state = MfkeyAttack;
+ for(i = 0; i < nonce_arr->total_nonces; i++) {
+ MfClassicNonce next_nonce = nonce_arr->remaining_nonce_array[i];
+ uint32_t p64 = prng_successor(next_nonce.nt0, 64);
+ uint32_t p64b = prng_successor(next_nonce.nt1, 64);
+ if(key_already_found_for_nonce(
+ keyarray,
+ keyarray_size,
+ next_nonce.uid ^ next_nonce.nt1,
+ next_nonce.nr1_enc,
+ p64b,
+ next_nonce.ar1_enc)) {
+ nonce_arr->remaining_nonces--;
+ (program_state->cracked)++;
+ continue;
+ }
+ FURI_LOG_I(TAG, "Cracking %lx %lx", next_nonce.uid, next_nonce.ar1_enc);
+ struct Crypto1Params p = {
+ 0,
+ next_nonce.nr0_enc,
+ next_nonce.uid ^ next_nonce.nt0,
+ next_nonce.uid ^ next_nonce.nt1,
+ next_nonce.nr1_enc,
+ p64b,
+ next_nonce.ar1_enc};
+ if(recover(&p, next_nonce.ar0_enc ^ p64, program_state) == 0) {
+ // No key found in recover()
+ continue;
+ }
+ found_key = p.key;
+ bool already_found = false;
+ for(i = 0; i < keyarray_size; i++) {
+ if(keyarray[i] == found_key) {
+ already_found = true;
+ break;
+ }
+ }
+ if(already_found == false) {
+ // New key
+ keyarray = realloc(keyarray, sizeof(uint64_t) * (keyarray_size + 1));
+ keyarray_size += 1;
+ keyarray[keyarray_size - 1] = found_key;
+ (program_state->cracked)++;
+ (program_state->unique_cracked)++;
+ }
+ }
+ // TODO: Update display to show all keys were found
+ // TODO: Prepend found key(s) to user dictionary file
+ //FURI_LOG_I(TAG, "Unique keys found:");
+ for(i = 0; i < keyarray_size; i++) {
+ //FURI_LOG_I(TAG, "%012" PRIx64, keyarray[i]);
+ FuriString* temp_key = furi_string_alloc();
+ furi_string_cat_printf(temp_key, "%012" PRIX64, keyarray[i]);
+ napi_mf_classic_dict_add_key_str(user_dict, temp_key);
+ furi_string_free(temp_key);
+ }
+ napi_mf_classic_nonce_array_free(nonce_arr);
+ if(user_dict_exists) {
+ napi_mf_classic_dict_free(user_dict);
+ }
+ free(keyarray);
+ //FURI_LOG_I(TAG, "mfkey32 function completed normally"); // DEBUG
+ program_state->mfkey_state = Complete;
+ finished_beep();
+ return;
+}
+
+// Screen is 128x64 px
+static void render_callback(Canvas* const canvas, void* ctx) {
+ furi_assert(ctx);
+ const ProgramState* program_state = ctx;
+ furi_mutex_acquire(program_state->mutex, FuriWaitForever);
+ char draw_str[32] = {};
+ canvas_clear(canvas);
+ canvas_draw_frame(canvas, 0, 0, 128, 64);
+ canvas_draw_frame(canvas, 0, 15, 128, 64);
+ canvas_set_font(canvas, FontPrimary);
+ canvas_draw_str_aligned(canvas, 5, 4, AlignLeft, AlignTop, "Mfkey32");
+ canvas_draw_icon(canvas, 114, 4, &I_mfkey);
+ if(program_state->is_thread_running && program_state->mfkey_state == MfkeyAttack) {
+ float progress = (float)program_state->cracked / (float)program_state->total;
+ elements_progress_bar_with_text(canvas, 5, 18, 118, progress, draw_str);
+ canvas_set_font(canvas, FontSecondary);
+ snprintf(
+ draw_str,
+ sizeof(draw_str),
+ "Keys found: %d/%d (in prog.)",
+ program_state->cracked,
+ program_state->total);
+ canvas_draw_str_aligned(canvas, 5, 31, AlignLeft, AlignTop, draw_str);
+ snprintf(
+ draw_str, sizeof(draw_str), "Search: %d/%d", program_state->search, 256 / MSB_LIMIT);
+ canvas_draw_str_aligned(canvas, 26, 41, AlignLeft, AlignTop, draw_str);
+ } else if(program_state->is_thread_running && program_state->mfkey_state == DictionaryAttack) {
+ elements_progress_bar_with_text(canvas, 5, 18, 118, 0, draw_str);
+ canvas_set_font(canvas, FontSecondary);
+ snprintf(
+ draw_str, sizeof(draw_str), "Dict solves: %d (in progress)", program_state->cracked);
+ canvas_draw_str_aligned(canvas, 10, 31, AlignLeft, AlignTop, draw_str);
+ snprintf(draw_str, sizeof(draw_str), "Keys in dict: %d", program_state->dict_count);
+ canvas_draw_str_aligned(canvas, 26, 41, AlignLeft, AlignTop, draw_str);
+ } else if(program_state->mfkey_state == Complete) {
+ // TODO: Scrollable list view to see cracked keys if user presses down
+ elements_progress_bar_with_text(canvas, 5, 18, 118, 1, draw_str);
+ canvas_set_font(canvas, FontSecondary);
+ snprintf(draw_str, sizeof(draw_str), "Complete");
+ canvas_draw_str_aligned(canvas, 40, 31, AlignLeft, AlignTop, draw_str);
+ snprintf(
+ draw_str,
+ sizeof(draw_str),
+ "Keys added to user dict: %d",
+ program_state->unique_cracked);
+ canvas_draw_str_aligned(canvas, 10, 41, AlignLeft, AlignTop, draw_str);
+ } else if(program_state->mfkey_state == Ready) {
+ canvas_set_font(canvas, FontSecondary);
+ canvas_draw_str_aligned(canvas, 50, 30, AlignLeft, AlignTop, "Ready");
+ elements_button_center(canvas, "Start");
+ elements_button_right(canvas, "Help");
+ } else if(program_state->mfkey_state == Help) {
+ canvas_set_font(canvas, FontSecondary);
+ canvas_draw_str_aligned(canvas, 7, 20, AlignLeft, AlignTop, "Collect nonces using");
+ canvas_draw_str_aligned(canvas, 7, 30, AlignLeft, AlignTop, "Detect Reader.");
+ canvas_draw_str_aligned(canvas, 7, 40, AlignLeft, AlignTop, "Developers: noproto, AG");
+ canvas_draw_str_aligned(canvas, 7, 50, AlignLeft, AlignTop, "Thanks: bettse");
+ } else if(program_state->mfkey_state == Error) {
+ canvas_draw_str_aligned(canvas, 50, 25, AlignLeft, AlignTop, "Error");
+ canvas_set_font(canvas, FontSecondary);
+ if(program_state->err == MissingNonces) {
+ canvas_draw_str_aligned(canvas, 25, 36, AlignLeft, AlignTop, "No nonces found");
+ } else if(program_state->err == ZeroNonces) {
+ canvas_draw_str_aligned(canvas, 25, 36, AlignLeft, AlignTop, "No nonces to crack");
+ } else if(program_state->err == OutOfMemory) {
+ canvas_draw_str_aligned(canvas, 25, 36, AlignLeft, AlignTop, "Insufficient memory");
+ } else {
+ // Unhandled error
+ }
+ } else {
+ // Unhandled program state
+ }
+ furi_mutex_release(program_state->mutex);
+}
+
+static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) {
+ furi_assert(event_queue);
+
+ PluginEvent event = {.type = EventTypeKey, .input = *input_event};
+ furi_message_queue_put(event_queue, &event, FuriWaitForever);
+}
+
+static void mfkey32_state_init(ProgramState* const program_state) {
+ program_state->is_thread_running = false;
+ program_state->mfkey_state = Ready;
+ program_state->cracked = 0;
+ program_state->unique_cracked = 0;
+ program_state->total = 0;
+ program_state->dict_count = 0;
+}
+
+// Entrypoint for worker thread
+static int32_t mfkey32_worker_thread(void* ctx) {
+ ProgramState* program_state = ctx;
+ program_state->is_thread_running = true;
+ program_state->mfkey_state = Initializing;
+ //FURI_LOG_I(TAG, "Hello from the mfkey32 worker thread"); // DEBUG
+ mfkey32(program_state);
+ program_state->is_thread_running = false;
+ return 0;
+}
+
+void start_mfkey32_thread(ProgramState* program_state) {
+ if(!program_state->is_thread_running) {
+ furi_thread_start(program_state->mfkeythread);
+ }
+}
+
+int32_t mfkey32_main() {
+ FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent));
+
+ DOLPHIN_DEED(DolphinDeedPluginStart);
+ ProgramState* program_state = malloc(sizeof(ProgramState));
+
+ mfkey32_state_init(program_state);
+
+ program_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
+ if(!program_state->mutex) {
+ FURI_LOG_E(TAG, "cannot create mutex\r\n");
+ free(program_state);
+ return 255;
+ }
+
+ // Set system callbacks
+ ViewPort* view_port = view_port_alloc();
+ view_port_draw_callback_set(view_port, render_callback, program_state);
+ view_port_input_callback_set(view_port, input_callback, event_queue);
+
+ // Open GUI and register view_port
+ Gui* gui = furi_record_open(RECORD_GUI);
+ gui_add_view_port(gui, view_port, GuiLayerFullscreen);
+
+ program_state->mfkeythread = furi_thread_alloc();
+ furi_thread_set_name(program_state->mfkeythread, "Mfkey32 Worker");
+ furi_thread_set_stack_size(program_state->mfkeythread, 2048);
+ furi_thread_set_context(program_state->mfkeythread, program_state);
+ furi_thread_set_callback(program_state->mfkeythread, mfkey32_worker_thread);
+
+ PluginEvent event;
+ for(bool main_loop = true; main_loop;) {
+ FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
+
+ furi_mutex_acquire(program_state->mutex, FuriWaitForever);
+
+ if(event_status == FuriStatusOk) {
+ // press events
+ if(event.type == EventTypeKey) {
+ if(event.input.type == InputTypePress) {
+ switch(event.input.key) {
+ case InputKeyUp:
+ break;
+ case InputKeyDown:
+ break;
+ case InputKeyRight:
+ if(!program_state->is_thread_running &&
+ program_state->mfkey_state == Ready) {
+ program_state->mfkey_state = Help;
+ view_port_update(view_port);
+ }
+ break;
+ case InputKeyLeft:
+ break;
+ case InputKeyOk:
+ if(!program_state->is_thread_running &&
+ program_state->mfkey_state == Ready) {
+ start_mfkey32_thread(program_state);
+ view_port_update(view_port);
+ }
+ break;
+ case InputKeyBack:
+ if(!program_state->is_thread_running &&
+ program_state->mfkey_state == Help) {
+ program_state->mfkey_state = Ready;
+ view_port_update(view_port);
+ } else {
+ program_state->close_thread_please = true;
+ if(program_state->is_thread_running && program_state->mfkeythread) {
+ // Wait until thread is finished
+ furi_thread_join(program_state->mfkeythread);
+ }
+ program_state->close_thread_please = false;
+ main_loop = false;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+
+ view_port_update(view_port);
+ furi_mutex_release(program_state->mutex);
+ }
+
+ furi_thread_free(program_state->mfkeythread);
+ view_port_enabled_set(view_port, false);
+ gui_remove_view_port(gui, view_port);
+ furi_record_close("gui");
+ view_port_free(view_port);
+ furi_message_queue_free(event_queue);
+ furi_mutex_free(program_state->mutex);
+ free(program_state);
+
+ return 0;
+}
diff --git a/applications/plugins/nrf24scan/LICENSE b/applications/external/minesweeper/LICENSE
similarity index 100%
rename from applications/plugins/nrf24scan/LICENSE
rename to applications/external/minesweeper/LICENSE
diff --git a/applications/plugins/minesweeper/application.fam b/applications/external/minesweeper/application.fam
similarity index 88%
rename from applications/plugins/minesweeper/application.fam
rename to applications/external/minesweeper/application.fam
index 405cb0d1c..c0211282c 100644
--- a/applications/plugins/minesweeper/application.fam
+++ b/applications/external/minesweeper/application.fam
@@ -3,7 +3,6 @@ App(
name="Minesweeper",
apptype=FlipperAppType.EXTERNAL,
entry_point="minesweeper_app",
- cdefines=["APP_MINESWEEPER"],
requires=["gui"],
stack_size=8 * 1024,
fap_category="Games",
diff --git a/applications/plugins/minesweeper/assets.h b/applications/external/minesweeper/assets.h
similarity index 100%
rename from applications/plugins/minesweeper/assets.h
rename to applications/external/minesweeper/assets.h
diff --git a/applications/plugins/minesweeper/assets/asset b/applications/external/minesweeper/assets/asset
similarity index 100%
rename from applications/plugins/minesweeper/assets/asset
rename to applications/external/minesweeper/assets/asset
diff --git a/applications/plugins/minesweeper/assets/mockup.png b/applications/external/minesweeper/assets/mockup.png
similarity index 100%
rename from applications/plugins/minesweeper/assets/mockup.png
rename to applications/external/minesweeper/assets/mockup.png
diff --git a/applications/plugins/minesweeper/assets/tile_0.png b/applications/external/minesweeper/assets/tile_0.png
similarity index 100%
rename from applications/plugins/minesweeper/assets/tile_0.png
rename to applications/external/minesweeper/assets/tile_0.png
diff --git a/applications/plugins/minesweeper/assets/tile_0.xbm b/applications/external/minesweeper/assets/tile_0.xbm
similarity index 100%
rename from applications/plugins/minesweeper/assets/tile_0.xbm
rename to applications/external/minesweeper/assets/tile_0.xbm
diff --git a/applications/plugins/minesweeper/assets/tile_1.png b/applications/external/minesweeper/assets/tile_1.png
similarity index 100%
rename from applications/plugins/minesweeper/assets/tile_1.png
rename to applications/external/minesweeper/assets/tile_1.png
diff --git a/applications/plugins/minesweeper/assets/tile_1.xbm b/applications/external/minesweeper/assets/tile_1.xbm
similarity index 100%
rename from applications/plugins/minesweeper/assets/tile_1.xbm
rename to applications/external/minesweeper/assets/tile_1.xbm
diff --git a/applications/plugins/minesweeper/assets/tile_2.png b/applications/external/minesweeper/assets/tile_2.png
similarity index 100%
rename from applications/plugins/minesweeper/assets/tile_2.png
rename to applications/external/minesweeper/assets/tile_2.png
diff --git a/applications/plugins/minesweeper/assets/tile_2.xbm b/applications/external/minesweeper/assets/tile_2.xbm
similarity index 100%
rename from applications/plugins/minesweeper/assets/tile_2.xbm
rename to applications/external/minesweeper/assets/tile_2.xbm
diff --git a/applications/plugins/minesweeper/assets/tile_3.png b/applications/external/minesweeper/assets/tile_3.png
similarity index 100%
rename from applications/plugins/minesweeper/assets/tile_3.png
rename to applications/external/minesweeper/assets/tile_3.png
diff --git a/applications/plugins/minesweeper/assets/tile_3.xbm b/applications/external/minesweeper/assets/tile_3.xbm
similarity index 100%
rename from applications/plugins/minesweeper/assets/tile_3.xbm
rename to applications/external/minesweeper/assets/tile_3.xbm
diff --git a/applications/plugins/minesweeper/assets/tile_4.png b/applications/external/minesweeper/assets/tile_4.png
similarity index 100%
rename from applications/plugins/minesweeper/assets/tile_4.png
rename to applications/external/minesweeper/assets/tile_4.png
diff --git a/applications/plugins/minesweeper/assets/tile_4.xbm b/applications/external/minesweeper/assets/tile_4.xbm
similarity index 100%
rename from applications/plugins/minesweeper/assets/tile_4.xbm
rename to applications/external/minesweeper/assets/tile_4.xbm
diff --git a/applications/plugins/minesweeper/assets/tile_5.png b/applications/external/minesweeper/assets/tile_5.png
similarity index 100%
rename from applications/plugins/minesweeper/assets/tile_5.png
rename to applications/external/minesweeper/assets/tile_5.png
diff --git a/applications/plugins/minesweeper/assets/tile_5.xbm b/applications/external/minesweeper/assets/tile_5.xbm
similarity index 100%
rename from applications/plugins/minesweeper/assets/tile_5.xbm
rename to applications/external/minesweeper/assets/tile_5.xbm
diff --git a/applications/plugins/minesweeper/assets/tile_6.png b/applications/external/minesweeper/assets/tile_6.png
similarity index 100%
rename from applications/plugins/minesweeper/assets/tile_6.png
rename to applications/external/minesweeper/assets/tile_6.png
diff --git a/applications/plugins/minesweeper/assets/tile_6.xbm b/applications/external/minesweeper/assets/tile_6.xbm
similarity index 100%
rename from applications/plugins/minesweeper/assets/tile_6.xbm
rename to applications/external/minesweeper/assets/tile_6.xbm
diff --git a/applications/plugins/minesweeper/assets/tile_7.png b/applications/external/minesweeper/assets/tile_7.png
similarity index 100%
rename from applications/plugins/minesweeper/assets/tile_7.png
rename to applications/external/minesweeper/assets/tile_7.png
diff --git a/applications/plugins/minesweeper/assets/tile_7.xbm b/applications/external/minesweeper/assets/tile_7.xbm
similarity index 100%
rename from applications/plugins/minesweeper/assets/tile_7.xbm
rename to applications/external/minesweeper/assets/tile_7.xbm
diff --git a/applications/plugins/minesweeper/assets/tile_8.png b/applications/external/minesweeper/assets/tile_8.png
similarity index 100%
rename from applications/plugins/minesweeper/assets/tile_8.png
rename to applications/external/minesweeper/assets/tile_8.png
diff --git a/applications/plugins/minesweeper/assets/tile_8.xbm b/applications/external/minesweeper/assets/tile_8.xbm
similarity index 100%
rename from applications/plugins/minesweeper/assets/tile_8.xbm
rename to applications/external/minesweeper/assets/tile_8.xbm
diff --git a/applications/plugins/minesweeper/assets/tile_empty.png b/applications/external/minesweeper/assets/tile_empty.png
similarity index 100%
rename from applications/plugins/minesweeper/assets/tile_empty.png
rename to applications/external/minesweeper/assets/tile_empty.png
diff --git a/applications/plugins/minesweeper/assets/tile_flag.png b/applications/external/minesweeper/assets/tile_flag.png
similarity index 100%
rename from applications/plugins/minesweeper/assets/tile_flag.png
rename to applications/external/minesweeper/assets/tile_flag.png
diff --git a/applications/plugins/minesweeper/assets/tile_flag.xbm b/applications/external/minesweeper/assets/tile_flag.xbm
similarity index 100%
rename from applications/plugins/minesweeper/assets/tile_flag.xbm
rename to applications/external/minesweeper/assets/tile_flag.xbm
diff --git a/applications/plugins/minesweeper/assets/tile_mine.png b/applications/external/minesweeper/assets/tile_mine.png
similarity index 100%
rename from applications/plugins/minesweeper/assets/tile_mine.png
rename to applications/external/minesweeper/assets/tile_mine.png
diff --git a/applications/plugins/minesweeper/assets/tile_mine.xbm b/applications/external/minesweeper/assets/tile_mine.xbm
similarity index 100%
rename from applications/plugins/minesweeper/assets/tile_mine.xbm
rename to applications/external/minesweeper/assets/tile_mine.xbm
diff --git a/applications/plugins/minesweeper/assets/tile_uncleared.png b/applications/external/minesweeper/assets/tile_uncleared.png
similarity index 100%
rename from applications/plugins/minesweeper/assets/tile_uncleared.png
rename to applications/external/minesweeper/assets/tile_uncleared.png
diff --git a/applications/plugins/minesweeper/assets/tile_uncleared.xbm b/applications/external/minesweeper/assets/tile_uncleared.xbm
similarity index 100%
rename from applications/plugins/minesweeper/assets/tile_uncleared.xbm
rename to applications/external/minesweeper/assets/tile_uncleared.xbm
diff --git a/applications/plugins/minesweeper/img/screenshot.png b/applications/external/minesweeper/img/screenshot.png
similarity index 100%
rename from applications/plugins/minesweeper/img/screenshot.png
rename to applications/external/minesweeper/img/screenshot.png
diff --git a/applications/plugins/minesweeper/minesweeper.c b/applications/external/minesweeper/minesweeper.c
similarity index 89%
rename from applications/plugins/minesweeper/minesweeper.c
rename to applications/external/minesweeper/minesweeper.c
index 43e8f027e..af056eabc 100644
--- a/applications/plugins/minesweeper/minesweeper.c
+++ b/applications/external/minesweeper/minesweeper.c
@@ -45,9 +45,11 @@ typedef enum {
typedef enum { FieldEmpty, FieldMine } Field;
typedef struct {
+ FuriMutex* mutex;
+ DialogsApp* dialogs;
+ NotificationApp* notifications;
Field minefield[PLAYFIELD_WIDTH][PLAYFIELD_HEIGHT];
TileType playfield[PLAYFIELD_WIDTH][PLAYFIELD_HEIGHT];
- FuriTimer* timer;
int cursor_x;
int cursor_y;
int mines_left;
@@ -55,16 +57,8 @@ typedef struct {
int flags_set;
bool game_started;
uint32_t game_started_tick;
- FuriMutex* mutex;
} Minesweeper;
-static void timer_callback(void* ctx) {
- UNUSED(ctx);
- NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
- notification_message(notification, &sequence_reset_vibro);
- furi_record_close(RECORD_NOTIFICATION);
-}
-
static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) {
furi_assert(event_queue);
@@ -177,7 +171,7 @@ static void setup_playfield(Minesweeper* minesweeper_state) {
int rand_y = rand() % PLAYFIELD_HEIGHT;
// make sure first guess isn't a mine
if(minesweeper_state->minefield[rand_x][rand_y] == FieldEmpty &&
- (minesweeper_state->cursor_x != rand_x && minesweeper_state->cursor_y != rand_y)) {
+ (minesweeper_state->cursor_x != rand_x || minesweeper_state->cursor_y != rand_y)) {
minesweeper_state->minefield[rand_x][rand_y] = FieldMine;
mines_left--;
}
@@ -206,37 +200,25 @@ static void place_flag(Minesweeper* minesweeper_state) {
static bool game_lost(Minesweeper* minesweeper_state) {
// returns true if the player wants to restart, otherwise false
- DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS);
-
DialogMessage* message = dialog_message_alloc();
- const char* header_text = "Game Over";
- const char* message_text = "You hit a mine!";
- dialog_message_set_header(message, header_text, 64, 3, AlignCenter, AlignTop);
- dialog_message_set_text(message, message_text, 64, 32, AlignCenter, AlignCenter);
+ dialog_message_set_header(message, "Game Over", 64, 3, AlignCenter, AlignTop);
+ dialog_message_set_text(message, "You hit a mine!", 64, 32, AlignCenter, AlignCenter);
dialog_message_set_buttons(message, NULL, "Play again", NULL);
- dialog_message_set_icon(message, NULL, 0, 10);
-
// Set cursor to initial position
minesweeper_state->cursor_x = 0;
minesweeper_state->cursor_y = 0;
- NotificationApp* notifications = furi_record_open(RECORD_NOTIFICATION);
- notification_message(notifications, &sequence_set_vibro_on);
- furi_record_close(RECORD_NOTIFICATION);
- furi_timer_start(minesweeper_state->timer, (uint32_t)furi_kernel_get_tick_frequency() * 0.2);
+ notification_message(minesweeper_state->notifications, &sequence_single_vibro);
- DialogMessageButton choice = dialog_message_show(dialogs, message);
+ DialogMessageButton choice = dialog_message_show(minesweeper_state->dialogs, message);
dialog_message_free(message);
- furi_record_close(RECORD_DIALOGS);
return choice == DialogMessageButtonCenter;
}
static bool game_won(Minesweeper* minesweeper_state) {
- DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS);
-
FuriString* tempStr;
tempStr = furi_string_alloc();
@@ -254,12 +236,10 @@ static bool game_won(Minesweeper* minesweeper_state) {
dialog_message_set_text(
message, furi_string_get_cstr(tempStr), 64, 32, AlignCenter, AlignCenter);
dialog_message_set_buttons(message, NULL, "Play again", NULL);
- dialog_message_set_icon(message, NULL, 72, 17);
- DialogMessageButton choice = dialog_message_show(dialogs, message);
+ DialogMessageButton choice = dialog_message_show(minesweeper_state->dialogs, message);
dialog_message_free(message);
furi_string_free(tempStr);
- furi_record_close(RECORD_DIALOGS);
return choice == DialogMessageButtonCenter;
}
@@ -370,22 +350,6 @@ static void minesweeper_state_init(Minesweeper* const minesweeper_state) {
int32_t minesweeper_app(void* p) {
UNUSED(p);
- DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS);
-
- DialogMessage* message = dialog_message_alloc();
- const char* header_text = "Minesweeper";
- const char* message_text = "Hold OK pressed to toggle flags.\ngithub.com/panki27";
-
- dialog_message_set_header(message, header_text, 64, 3, AlignCenter, AlignTop);
- dialog_message_set_text(message, message_text, 64, 32, AlignCenter, AlignCenter);
- dialog_message_set_buttons(message, NULL, "Play", NULL);
-
- dialog_message_set_icon(message, NULL, 0, 10);
-
- dialog_message_show(dialogs, message);
- dialog_message_free(message);
- furi_record_close(RECORD_DIALOGS);
-
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent));
Minesweeper* minesweeper_state = malloc(sizeof(Minesweeper));
@@ -400,12 +364,28 @@ int32_t minesweeper_app(void* p) {
}
// BEGIN IMPLEMENTATION
+ minesweeper_state->dialogs = furi_record_open(RECORD_DIALOGS);
+ minesweeper_state->notifications = furi_record_open(RECORD_NOTIFICATION);
+
+ DialogMessage* message = dialog_message_alloc();
+
+ dialog_message_set_header(message, "Minesweeper", 64, 3, AlignCenter, AlignTop);
+ dialog_message_set_text(
+ message,
+ "Hold OK pressed to toggle flags.\ngithub.com/panki27",
+ 64,
+ 32,
+ AlignCenter,
+ AlignCenter);
+ dialog_message_set_buttons(message, NULL, "Play", NULL);
+
+ dialog_message_show(minesweeper_state->dialogs, message);
+ dialog_message_free(message);
+
// Set system callbacks
ViewPort* view_port = view_port_alloc();
view_port_draw_callback_set(view_port, render_callback, minesweeper_state);
view_port_input_callback_set(view_port, input_callback, event_queue);
- minesweeper_state->timer =
- furi_timer_alloc(timer_callback, FuriTimerTypeOnce, minesweeper_state);
// Open GUI and register view_port
Gui* gui = furi_record_open(RECORD_GUI);
@@ -414,35 +394,42 @@ int32_t minesweeper_app(void* p) {
PluginEvent event;
for(bool processing = true; processing;) {
FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
- furi_mutex_acquire(minesweeper_state->mutex, FuriWaitForever);
if(event_status == FuriStatusOk) {
// press events
if(event.type == EventTypeKey) {
if(event.input.type == InputTypeShort) {
switch(event.input.key) {
case InputKeyUp:
+ furi_mutex_acquire(minesweeper_state->mutex, FuriWaitForever);
minesweeper_state->cursor_y--;
if(minesweeper_state->cursor_y < 0) {
minesweeper_state->cursor_y = PLAYFIELD_HEIGHT - 1;
}
+ furi_mutex_release(minesweeper_state->mutex);
break;
case InputKeyDown:
+ furi_mutex_acquire(minesweeper_state->mutex, FuriWaitForever);
minesweeper_state->cursor_y++;
if(minesweeper_state->cursor_y >= PLAYFIELD_HEIGHT) {
minesweeper_state->cursor_y = 0;
}
+ furi_mutex_release(minesweeper_state->mutex);
break;
case InputKeyRight:
+ furi_mutex_acquire(minesweeper_state->mutex, FuriWaitForever);
minesweeper_state->cursor_x++;
if(minesweeper_state->cursor_x >= PLAYFIELD_WIDTH) {
minesweeper_state->cursor_x = 0;
}
+ furi_mutex_release(minesweeper_state->mutex);
break;
case InputKeyLeft:
+ furi_mutex_acquire(minesweeper_state->mutex, FuriWaitForever);
minesweeper_state->cursor_x--;
if(minesweeper_state->cursor_x < 0) {
minesweeper_state->cursor_x = PLAYFIELD_WIDTH - 1;
}
+ furi_mutex_release(minesweeper_state->mutex);
break;
case InputKeyOk:
if(!minesweeper_state->game_started) {
@@ -492,7 +479,9 @@ int32_t minesweeper_app(void* p) {
break;
case InputKeyOk:
FURI_LOG_D("Minesweeper", "Toggling flag");
+ furi_mutex_acquire(minesweeper_state->mutex, FuriWaitForever);
place_flag(minesweeper_state);
+ furi_mutex_release(minesweeper_state->mutex);
break;
case InputKeyBack:
processing = false;
@@ -504,15 +493,15 @@ int32_t minesweeper_app(void* p) {
}
}
view_port_update(view_port);
- furi_mutex_release(minesweeper_state->mutex);
}
view_port_enabled_set(view_port, false);
gui_remove_view_port(gui, view_port);
furi_record_close(RECORD_GUI);
+ furi_record_close(RECORD_DIALOGS);
+ furi_record_close(RECORD_NOTIFICATION);
view_port_free(view_port);
furi_message_queue_free(event_queue);
furi_mutex_free(minesweeper_state->mutex);
- furi_timer_free(minesweeper_state->timer);
free(minesweeper_state);
return 0;
diff --git a/applications/plugins/minesweeper/minesweeper_icon.png b/applications/external/minesweeper/minesweeper_icon.png
similarity index 100%
rename from applications/plugins/minesweeper/minesweeper_icon.png
rename to applications/external/minesweeper/minesweeper_icon.png
diff --git a/applications/plugins/morse_code/application.fam b/applications/external/morse_code/application.fam
similarity index 88%
rename from applications/plugins/morse_code/application.fam
rename to applications/external/morse_code/application.fam
index d5a2e6c9a..d6898100d 100644
--- a/applications/plugins/morse_code/application.fam
+++ b/applications/external/morse_code/application.fam
@@ -3,7 +3,6 @@ App(
name="Morse Code",
apptype=FlipperAppType.EXTERNAL,
entry_point="morse_code_app",
- cdefines=["APP_MORSE_CODE"],
requires=[
"gui",
],
diff --git a/applications/plugins/morse_code/morse_code.c b/applications/external/morse_code/morse_code.c
similarity index 100%
rename from applications/plugins/morse_code/morse_code.c
rename to applications/external/morse_code/morse_code.c
diff --git a/applications/plugins/morse_code/morse_code_10px.png b/applications/external/morse_code/morse_code_10px.png
similarity index 100%
rename from applications/plugins/morse_code/morse_code_10px.png
rename to applications/external/morse_code/morse_code_10px.png
diff --git a/applications/plugins/morse_code/morse_code_worker.c b/applications/external/morse_code/morse_code_worker.c
similarity index 100%
rename from applications/plugins/morse_code/morse_code_worker.c
rename to applications/external/morse_code/morse_code_worker.c
diff --git a/applications/plugins/morse_code/morse_code_worker.h b/applications/external/morse_code/morse_code_worker.h
similarity index 100%
rename from applications/plugins/morse_code/morse_code_worker.h
rename to applications/external/morse_code/morse_code_worker.h
diff --git a/applications/plugins/mousejacker/application.fam b/applications/external/mousejacker/application.fam
similarity index 93%
rename from applications/plugins/mousejacker/application.fam
rename to applications/external/mousejacker/application.fam
index 725e81ae8..f65b06cc2 100644
--- a/applications/plugins/mousejacker/application.fam
+++ b/applications/external/mousejacker/application.fam
@@ -3,7 +3,6 @@ App(
name="[NRF24] Mouse Jacker",
apptype=FlipperAppType.EXTERNAL,
entry_point="mousejacker_app",
- cdefines=["APP_MOUSEJACKER"],
requires=[
"gui",
"dialogs",
diff --git a/applications/plugins/mousejacker/images/badusb_10px.png b/applications/external/mousejacker/images/badusb_10px.png
similarity index 100%
rename from applications/plugins/mousejacker/images/badusb_10px.png
rename to applications/external/mousejacker/images/badusb_10px.png
diff --git a/applications/plugins/playlist/images/sub1_10px.png b/applications/external/mousejacker/images/sub1_10px.png
similarity index 100%
rename from applications/plugins/playlist/images/sub1_10px.png
rename to applications/external/mousejacker/images/sub1_10px.png
diff --git a/applications/plugins/mousejacker/lib/nrf24/nrf24.c b/applications/external/mousejacker/lib/nrf24/nrf24.c
similarity index 100%
rename from applications/plugins/mousejacker/lib/nrf24/nrf24.c
rename to applications/external/mousejacker/lib/nrf24/nrf24.c
diff --git a/applications/plugins/mousejacker/lib/nrf24/nrf24.h b/applications/external/mousejacker/lib/nrf24/nrf24.h
similarity index 100%
rename from applications/plugins/mousejacker/lib/nrf24/nrf24.h
rename to applications/external/mousejacker/lib/nrf24/nrf24.h
diff --git a/applications/plugins/mousejacker/mouse_10px.png b/applications/external/mousejacker/mouse_10px.png
similarity index 100%
rename from applications/plugins/mousejacker/mouse_10px.png
rename to applications/external/mousejacker/mouse_10px.png
diff --git a/applications/plugins/mousejacker/mousejacker.c b/applications/external/mousejacker/mousejacker.c
similarity index 99%
rename from applications/plugins/mousejacker/mousejacker.c
rename to applications/external/mousejacker/mousejacker.c
index 9ef23983f..cc34d9485 100644
--- a/applications/plugins/mousejacker/mousejacker.c
+++ b/applications/external/mousejacker/mousejacker.c
@@ -11,6 +11,7 @@
#include
#include "mousejacker_ducky.h"
#include
+#include
#define TAG "mousejacker"
#define LOGITECH_MAX_CHANNEL 85
@@ -286,6 +287,7 @@ void start_mjthread(PluginState* plugin_state) {
int32_t mousejacker_app(void* p) {
UNUSED(p);
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent));
+ DOLPHIN_DEED(DolphinDeedPluginStart);
PluginState* plugin_state = malloc(sizeof(PluginState));
mousejacker_state_init(plugin_state);
diff --git a/applications/plugins/mousejacker/mousejacker_ducky.c b/applications/external/mousejacker/mousejacker_ducky.c
similarity index 100%
rename from applications/plugins/mousejacker/mousejacker_ducky.c
rename to applications/external/mousejacker/mousejacker_ducky.c
diff --git a/applications/plugins/mousejacker/mousejacker_ducky.h b/applications/external/mousejacker/mousejacker_ducky.h
similarity index 100%
rename from applications/plugins/mousejacker/mousejacker_ducky.h
rename to applications/external/mousejacker/mousejacker_ducky.h
diff --git a/applications/plugins/multi_converter/application.fam b/applications/external/multi_converter/application.fam
similarity index 86%
rename from applications/plugins/multi_converter/application.fam
rename to applications/external/multi_converter/application.fam
index e0abe3ed4..106ddb127 100644
--- a/applications/plugins/multi_converter/application.fam
+++ b/applications/external/multi_converter/application.fam
@@ -3,7 +3,6 @@ App(
name="Multi Converter",
apptype=FlipperAppType.EXTERNAL,
entry_point="multi_converter_app",
- cdefines=["APP_DEC_HEX_CONVERTER"],
requires=["gui"],
stack_size=1 * 1024,
order=160,
diff --git a/applications/plugins/multi_converter/converter_10px.png b/applications/external/multi_converter/converter_10px.png
similarity index 100%
rename from applications/plugins/multi_converter/converter_10px.png
rename to applications/external/multi_converter/converter_10px.png
diff --git a/applications/plugins/multi_converter/multi_converter.c b/applications/external/multi_converter/multi_converter.c
similarity index 100%
rename from applications/plugins/multi_converter/multi_converter.c
rename to applications/external/multi_converter/multi_converter.c
diff --git a/applications/plugins/multi_converter/multi_converter_definitions.h b/applications/external/multi_converter/multi_converter_definitions.h
similarity index 100%
rename from applications/plugins/multi_converter/multi_converter_definitions.h
rename to applications/external/multi_converter/multi_converter_definitions.h
diff --git a/applications/plugins/multi_converter/multi_converter_mode_display.c b/applications/external/multi_converter/multi_converter_mode_display.c
similarity index 100%
rename from applications/plugins/multi_converter/multi_converter_mode_display.c
rename to applications/external/multi_converter/multi_converter_mode_display.c
diff --git a/applications/plugins/multi_converter/multi_converter_mode_display.h b/applications/external/multi_converter/multi_converter_mode_display.h
similarity index 100%
rename from applications/plugins/multi_converter/multi_converter_mode_display.h
rename to applications/external/multi_converter/multi_converter_mode_display.h
diff --git a/applications/plugins/multi_converter/multi_converter_mode_select.c b/applications/external/multi_converter/multi_converter_mode_select.c
similarity index 100%
rename from applications/plugins/multi_converter/multi_converter_mode_select.c
rename to applications/external/multi_converter/multi_converter_mode_select.c
diff --git a/applications/plugins/multi_converter/multi_converter_mode_select.h b/applications/external/multi_converter/multi_converter_mode_select.h
similarity index 100%
rename from applications/plugins/multi_converter/multi_converter_mode_select.h
rename to applications/external/multi_converter/multi_converter_mode_select.h
diff --git a/applications/plugins/multi_converter/multi_converter_units.c b/applications/external/multi_converter/multi_converter_units.c
similarity index 100%
rename from applications/plugins/multi_converter/multi_converter_units.c
rename to applications/external/multi_converter/multi_converter_units.c
diff --git a/applications/plugins/multi_converter/multi_converter_units.h b/applications/external/multi_converter/multi_converter_units.h
similarity index 100%
rename from applications/plugins/multi_converter/multi_converter_units.h
rename to applications/external/multi_converter/multi_converter_units.h
diff --git a/applications/plugins/multi_dice/application.fam b/applications/external/multi_dice/application.fam
similarity index 100%
rename from applications/plugins/multi_dice/application.fam
rename to applications/external/multi_dice/application.fam
diff --git a/applications/plugins/multi_dice/dice.c b/applications/external/multi_dice/dice.c
similarity index 100%
rename from applications/plugins/multi_dice/dice.c
rename to applications/external/multi_dice/dice.c
diff --git a/applications/plugins/multi_dice/dice.png b/applications/external/multi_dice/dice.png
similarity index 100%
rename from applications/plugins/multi_dice/dice.png
rename to applications/external/multi_dice/dice.png
diff --git a/applications/plugins/music_beeper/application.fam b/applications/external/music_beeper/application.fam
similarity index 100%
rename from applications/plugins/music_beeper/application.fam
rename to applications/external/music_beeper/application.fam
diff --git a/applications/plugins/music_beeper/icons/music_10px.png b/applications/external/music_beeper/icons/music_10px.png
similarity index 100%
rename from applications/plugins/music_beeper/icons/music_10px.png
rename to applications/external/music_beeper/icons/music_10px.png
diff --git a/applications/plugins/music_beeper/music_10px.png b/applications/external/music_beeper/music_10px.png
similarity index 100%
rename from applications/plugins/music_beeper/music_10px.png
rename to applications/external/music_beeper/music_10px.png
diff --git a/applications/plugins/music_beeper/music_beeper.c b/applications/external/music_beeper/music_beeper.c
similarity index 100%
rename from applications/plugins/music_beeper/music_beeper.c
rename to applications/external/music_beeper/music_beeper.c
diff --git a/applications/plugins/music_beeper/music_beeper_cli.c b/applications/external/music_beeper/music_beeper_cli.c
similarity index 100%
rename from applications/plugins/music_beeper/music_beeper_cli.c
rename to applications/external/music_beeper/music_beeper_cli.c
diff --git a/applications/plugins/music_beeper/music_beeper_worker.c b/applications/external/music_beeper/music_beeper_worker.c
similarity index 100%
rename from applications/plugins/music_beeper/music_beeper_worker.c
rename to applications/external/music_beeper/music_beeper_worker.c
diff --git a/applications/plugins/music_beeper/music_beeper_worker.h b/applications/external/music_beeper/music_beeper_worker.h
similarity index 100%
rename from applications/plugins/music_beeper/music_beeper_worker.h
rename to applications/external/music_beeper/music_beeper_worker.h
diff --git a/applications/plugins/music_player/application.fam b/applications/external/music_player/application.fam
similarity index 87%
rename from applications/plugins/music_player/application.fam
rename to applications/external/music_player/application.fam
index 07f489072..1a8f5505b 100644
--- a/applications/plugins/music_player/application.fam
+++ b/applications/external/music_player/application.fam
@@ -1,9 +1,8 @@
App(
appid="Music_Player",
name="Music Player",
- apptype=FlipperAppType.PLUGIN,
+ apptype=FlipperAppType.EXTERNAL,
entry_point="music_player_app",
- cdefines=["APP_MUSIC_PLAYER"],
requires=[
"gui",
"dialogs",
@@ -12,8 +11,8 @@ App(
stack_size=2 * 1024,
order=45,
fap_icon="icons/music_10px.png",
- fap_icon_assets="icons",
fap_category="Music",
+ fap_icon_assets="icons",
)
App(
diff --git a/applications/plugins/music_player/icons/music_10px.png b/applications/external/music_player/icons/music_10px.png
similarity index 100%
rename from applications/plugins/music_player/icons/music_10px.png
rename to applications/external/music_player/icons/music_10px.png
diff --git a/applications/plugins/music_player/music_player.c b/applications/external/music_player/music_player.c
similarity index 100%
rename from applications/plugins/music_player/music_player.c
rename to applications/external/music_player/music_player.c
diff --git a/applications/plugins/music_player/music_player_cli.c b/applications/external/music_player/music_player_cli.c
similarity index 100%
rename from applications/plugins/music_player/music_player_cli.c
rename to applications/external/music_player/music_player_cli.c
diff --git a/applications/plugins/music_player/music_player_worker.c b/applications/external/music_player/music_player_worker.c
similarity index 100%
rename from applications/plugins/music_player/music_player_worker.c
rename to applications/external/music_player/music_player_worker.c
diff --git a/applications/plugins/music_player/music_player_worker.h b/applications/external/music_player/music_player_worker.h
similarity index 100%
rename from applications/plugins/music_player/music_player_worker.h
rename to applications/external/music_player/music_player_worker.h
diff --git a/applications/plugins/musictracker/application.fam b/applications/external/musictracker/application.fam
similarity index 78%
rename from applications/plugins/musictracker/application.fam
rename to applications/external/musictracker/application.fam
index c0f7edca6..fe4355e86 100644
--- a/applications/plugins/musictracker/application.fam
+++ b/applications/external/musictracker/application.fam
@@ -1,7 +1,7 @@
App(
appid="zero_tracker",
name="Zero Tracker",
- apptype=FlipperAppType.PLUGIN,
+ apptype=FlipperAppType.EXTERNAL,
entry_point="zero_tracker_app",
requires=[
"gui",
@@ -10,5 +10,4 @@ App(
order=20,
fap_icon="zero_tracker.png",
fap_category="Music",
- fap_icon_assets="icons",
)
diff --git a/applications/plugins/musictracker/tracker_engine/speaker_hal.c b/applications/external/musictracker/tracker_engine/speaker_hal.c
similarity index 100%
rename from applications/plugins/musictracker/tracker_engine/speaker_hal.c
rename to applications/external/musictracker/tracker_engine/speaker_hal.c
diff --git a/applications/plugins/musictracker/tracker_engine/speaker_hal.h b/applications/external/musictracker/tracker_engine/speaker_hal.h
similarity index 100%
rename from applications/plugins/musictracker/tracker_engine/speaker_hal.h
rename to applications/external/musictracker/tracker_engine/speaker_hal.h
diff --git a/applications/plugins/musictracker/tracker_engine/tracker.c b/applications/external/musictracker/tracker_engine/tracker.c
similarity index 100%
rename from applications/plugins/musictracker/tracker_engine/tracker.c
rename to applications/external/musictracker/tracker_engine/tracker.c
diff --git a/applications/plugins/musictracker/tracker_engine/tracker.h b/applications/external/musictracker/tracker_engine/tracker.h
similarity index 100%
rename from applications/plugins/musictracker/tracker_engine/tracker.h
rename to applications/external/musictracker/tracker_engine/tracker.h
diff --git a/applications/plugins/musictracker/tracker_engine/tracker_notes.h b/applications/external/musictracker/tracker_engine/tracker_notes.h
similarity index 100%
rename from applications/plugins/musictracker/tracker_engine/tracker_notes.h
rename to applications/external/musictracker/tracker_engine/tracker_notes.h
diff --git a/applications/plugins/musictracker/tracker_engine/tracker_song.h b/applications/external/musictracker/tracker_engine/tracker_song.h
similarity index 100%
rename from applications/plugins/musictracker/tracker_engine/tracker_song.h
rename to applications/external/musictracker/tracker_engine/tracker_song.h
diff --git a/applications/plugins/musictracker/view/tracker_view.c b/applications/external/musictracker/view/tracker_view.c
similarity index 100%
rename from applications/plugins/musictracker/view/tracker_view.c
rename to applications/external/musictracker/view/tracker_view.c
diff --git a/applications/plugins/musictracker/view/tracker_view.h b/applications/external/musictracker/view/tracker_view.h
similarity index 100%
rename from applications/plugins/musictracker/view/tracker_view.h
rename to applications/external/musictracker/view/tracker_view.h
diff --git a/applications/plugins/musictracker/zero_tracker.c b/applications/external/musictracker/zero_tracker.c
similarity index 100%
rename from applications/plugins/musictracker/zero_tracker.c
rename to applications/external/musictracker/zero_tracker.c
diff --git a/applications/plugins/musictracker/zero_tracker.h b/applications/external/musictracker/zero_tracker.h
similarity index 100%
rename from applications/plugins/musictracker/zero_tracker.h
rename to applications/external/musictracker/zero_tracker.h
diff --git a/applications/plugins/musictracker/zero_tracker.png b/applications/external/musictracker/zero_tracker.png
similarity index 100%
rename from applications/plugins/musictracker/zero_tracker.png
rename to applications/external/musictracker/zero_tracker.png
diff --git a/applications/plugins/nfc_magic/application.fam b/applications/external/nfc_magic/application.fam
similarity index 100%
rename from applications/plugins/nfc_magic/application.fam
rename to applications/external/nfc_magic/application.fam
diff --git a/applications/plugins/bpmtapper/images/DolphinCommon_56x48.png b/applications/external/nfc_magic/assets/DolphinCommon_56x48.png
similarity index 100%
rename from applications/plugins/bpmtapper/images/DolphinCommon_56x48.png
rename to applications/external/nfc_magic/assets/DolphinCommon_56x48.png
diff --git a/applications/plugins/nfc_magic/assets/DolphinNice_96x59.png b/applications/external/nfc_magic/assets/DolphinNice_96x59.png
similarity index 100%
rename from applications/plugins/nfc_magic/assets/DolphinNice_96x59.png
rename to applications/external/nfc_magic/assets/DolphinNice_96x59.png
diff --git a/applications/plugins/nfc_magic/assets/Loading_24.png b/applications/external/nfc_magic/assets/Loading_24.png
similarity index 100%
rename from applications/plugins/nfc_magic/assets/Loading_24.png
rename to applications/external/nfc_magic/assets/Loading_24.png
diff --git a/applications/plugins/nfc_magic/assets/NFC_manual_60x50.png b/applications/external/nfc_magic/assets/NFC_manual_60x50.png
similarity index 100%
rename from applications/plugins/nfc_magic/assets/NFC_manual_60x50.png
rename to applications/external/nfc_magic/assets/NFC_manual_60x50.png
diff --git a/applications/plugins/nfc_magic/lib/magic/magic.c b/applications/external/nfc_magic/lib/magic/magic.c
similarity index 98%
rename from applications/plugins/nfc_magic/lib/magic/magic.c
rename to applications/external/nfc_magic/lib/magic/magic.c
index a922bc7a8..9a71daaa0 100644
--- a/applications/plugins/nfc_magic/lib/magic/magic.c
+++ b/applications/external/nfc_magic/lib/magic/magic.c
@@ -6,8 +6,7 @@
#define MAGIC_CMD_WUPA (0x40)
#define MAGIC_CMD_WIPE (0x41)
-#define MAGIC_CMD_READ (0x43)
-#define MAGIC_CMD_WRITE (0x43)
+#define MAGIC_CMD_ACCESS (0x43)
#define MAGIC_MIFARE_READ_CMD (0x30)
#define MAGIC_MIFARE_WRITE_CMD (0xA0)
@@ -70,7 +69,7 @@ bool magic_data_access_cmd() {
FuriHalNfcReturn ret = 0;
do {
- tx_data[0] = MAGIC_CMD_WRITE;
+ tx_data[0] = MAGIC_CMD_ACCESS;
ret = furi_hal_nfc_ll_txrx_bits(
tx_data,
8,
diff --git a/applications/plugins/nfc_magic/lib/magic/magic.h b/applications/external/nfc_magic/lib/magic/magic.h
similarity index 100%
rename from applications/plugins/nfc_magic/lib/magic/magic.h
rename to applications/external/nfc_magic/lib/magic/magic.h
diff --git a/applications/plugins/nfc_magic/nfc_magic.c b/applications/external/nfc_magic/nfc_magic.c
similarity index 98%
rename from applications/plugins/nfc_magic/nfc_magic.c
rename to applications/external/nfc_magic/nfc_magic.c
index 1805f35ed..0a015ec77 100644
--- a/applications/plugins/nfc_magic/nfc_magic.c
+++ b/applications/external/nfc_magic/nfc_magic.c
@@ -1,4 +1,5 @@
#include "nfc_magic_i.h"
+#include
bool nfc_magic_custom_event_callback(void* context, uint32_t event) {
furi_assert(context);
@@ -160,6 +161,7 @@ int32_t nfc_magic_app(void* p) {
UNUSED(p);
NfcMagic* nfc_magic = nfc_magic_alloc();
+ DOLPHIN_DEED(DolphinDeedPluginStart);
scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneStart);
view_dispatcher_run(nfc_magic->view_dispatcher);
diff --git a/applications/plugins/nfc_magic/nfc_magic.h b/applications/external/nfc_magic/nfc_magic.h
similarity index 100%
rename from applications/plugins/nfc_magic/nfc_magic.h
rename to applications/external/nfc_magic/nfc_magic.h
diff --git a/applications/plugins/nfc_magic/nfc_magic_i.h b/applications/external/nfc_magic/nfc_magic_i.h
similarity index 100%
rename from applications/plugins/nfc_magic/nfc_magic_i.h
rename to applications/external/nfc_magic/nfc_magic_i.h
diff --git a/applications/plugins/nfc_magic/nfc_magic_worker.c b/applications/external/nfc_magic/nfc_magic_worker.c
similarity index 95%
rename from applications/plugins/nfc_magic/nfc_magic_worker.c
rename to applications/external/nfc_magic/nfc_magic_worker.c
index 32202f12d..1564c87cd 100644
--- a/applications/plugins/nfc_magic/nfc_magic_worker.c
+++ b/applications/external/nfc_magic/nfc_magic_worker.c
@@ -88,15 +88,17 @@ void nfc_magic_worker_write(NfcMagicWorker* nfc_magic_worker) {
card_found_notified = true;
}
furi_hal_nfc_sleep();
-
if(!magic_wupa()) {
- FURI_LOG_E(TAG, "Not Magic card");
+ FURI_LOG_E(TAG, "No card response to WUPA (not a magic card)");
nfc_magic_worker->callback(
NfcMagicWorkerEventWrongCard, nfc_magic_worker->context);
break;
}
+ furi_hal_nfc_sleep();
+ }
+ if(magic_wupa()) {
if(!magic_data_access_cmd()) {
- FURI_LOG_E(TAG, "Not Magic card");
+ FURI_LOG_E(TAG, "No card response to data access command (not a magic card)");
nfc_magic_worker->callback(
NfcMagicWorkerEventWrongCard, nfc_magic_worker->context);
break;
diff --git a/applications/plugins/nfc_magic/nfc_magic_worker.h b/applications/external/nfc_magic/nfc_magic_worker.h
similarity index 100%
rename from applications/plugins/nfc_magic/nfc_magic_worker.h
rename to applications/external/nfc_magic/nfc_magic_worker.h
diff --git a/applications/plugins/nfc_magic/nfc_magic_worker_i.h b/applications/external/nfc_magic/nfc_magic_worker_i.h
similarity index 100%
rename from applications/plugins/nfc_magic/nfc_magic_worker_i.h
rename to applications/external/nfc_magic/nfc_magic_worker_i.h
diff --git a/applications/plugins/nfc_magic/scenes/nfc_magic_scene.c b/applications/external/nfc_magic/scenes/nfc_magic_scene.c
similarity index 100%
rename from applications/plugins/nfc_magic/scenes/nfc_magic_scene.c
rename to applications/external/nfc_magic/scenes/nfc_magic_scene.c
diff --git a/applications/plugins/nfc_magic/scenes/nfc_magic_scene.h b/applications/external/nfc_magic/scenes/nfc_magic_scene.h
similarity index 100%
rename from applications/plugins/nfc_magic/scenes/nfc_magic_scene.h
rename to applications/external/nfc_magic/scenes/nfc_magic_scene.h
diff --git a/applications/plugins/nfc_magic/scenes/nfc_magic_scene_check.c b/applications/external/nfc_magic/scenes/nfc_magic_scene_check.c
similarity index 100%
rename from applications/plugins/nfc_magic/scenes/nfc_magic_scene_check.c
rename to applications/external/nfc_magic/scenes/nfc_magic_scene_check.c
diff --git a/applications/plugins/nfc_magic/scenes/nfc_magic_scene_config.h b/applications/external/nfc_magic/scenes/nfc_magic_scene_config.h
similarity index 100%
rename from applications/plugins/nfc_magic/scenes/nfc_magic_scene_config.h
rename to applications/external/nfc_magic/scenes/nfc_magic_scene_config.h
diff --git a/applications/plugins/nfc_magic/scenes/nfc_magic_scene_file_select.c b/applications/external/nfc_magic/scenes/nfc_magic_scene_file_select.c
similarity index 100%
rename from applications/plugins/nfc_magic/scenes/nfc_magic_scene_file_select.c
rename to applications/external/nfc_magic/scenes/nfc_magic_scene_file_select.c
diff --git a/applications/plugins/nfc_magic/scenes/nfc_magic_scene_magic_info.c b/applications/external/nfc_magic/scenes/nfc_magic_scene_magic_info.c
similarity index 100%
rename from applications/plugins/nfc_magic/scenes/nfc_magic_scene_magic_info.c
rename to applications/external/nfc_magic/scenes/nfc_magic_scene_magic_info.c
diff --git a/applications/plugins/nfc_magic/scenes/nfc_magic_scene_not_magic.c b/applications/external/nfc_magic/scenes/nfc_magic_scene_not_magic.c
similarity index 100%
rename from applications/plugins/nfc_magic/scenes/nfc_magic_scene_not_magic.c
rename to applications/external/nfc_magic/scenes/nfc_magic_scene_not_magic.c
diff --git a/applications/plugins/nfc_magic/scenes/nfc_magic_scene_start.c b/applications/external/nfc_magic/scenes/nfc_magic_scene_start.c
similarity index 100%
rename from applications/plugins/nfc_magic/scenes/nfc_magic_scene_start.c
rename to applications/external/nfc_magic/scenes/nfc_magic_scene_start.c
diff --git a/applications/plugins/nfc_magic/scenes/nfc_magic_scene_success.c b/applications/external/nfc_magic/scenes/nfc_magic_scene_success.c
similarity index 100%
rename from applications/plugins/nfc_magic/scenes/nfc_magic_scene_success.c
rename to applications/external/nfc_magic/scenes/nfc_magic_scene_success.c
diff --git a/applications/plugins/nfc_magic/scenes/nfc_magic_scene_wipe.c b/applications/external/nfc_magic/scenes/nfc_magic_scene_wipe.c
similarity index 100%
rename from applications/plugins/nfc_magic/scenes/nfc_magic_scene_wipe.c
rename to applications/external/nfc_magic/scenes/nfc_magic_scene_wipe.c
diff --git a/applications/plugins/nfc_magic/scenes/nfc_magic_scene_wipe_fail.c b/applications/external/nfc_magic/scenes/nfc_magic_scene_wipe_fail.c
similarity index 100%
rename from applications/plugins/nfc_magic/scenes/nfc_magic_scene_wipe_fail.c
rename to applications/external/nfc_magic/scenes/nfc_magic_scene_wipe_fail.c
diff --git a/applications/plugins/nfc_magic/scenes/nfc_magic_scene_write.c b/applications/external/nfc_magic/scenes/nfc_magic_scene_write.c
similarity index 100%
rename from applications/plugins/nfc_magic/scenes/nfc_magic_scene_write.c
rename to applications/external/nfc_magic/scenes/nfc_magic_scene_write.c
diff --git a/applications/plugins/nfc_magic/scenes/nfc_magic_scene_write_confirm.c b/applications/external/nfc_magic/scenes/nfc_magic_scene_write_confirm.c
similarity index 100%
rename from applications/plugins/nfc_magic/scenes/nfc_magic_scene_write_confirm.c
rename to applications/external/nfc_magic/scenes/nfc_magic_scene_write_confirm.c
diff --git a/applications/plugins/nfc_magic/scenes/nfc_magic_scene_write_fail.c b/applications/external/nfc_magic/scenes/nfc_magic_scene_write_fail.c
similarity index 100%
rename from applications/plugins/nfc_magic/scenes/nfc_magic_scene_write_fail.c
rename to applications/external/nfc_magic/scenes/nfc_magic_scene_write_fail.c
diff --git a/applications/plugins/nfc_magic/scenes/nfc_magic_scene_wrong_card.c b/applications/external/nfc_magic/scenes/nfc_magic_scene_wrong_card.c
similarity index 100%
rename from applications/plugins/nfc_magic/scenes/nfc_magic_scene_wrong_card.c
rename to applications/external/nfc_magic/scenes/nfc_magic_scene_wrong_card.c
diff --git a/applications/plugins/nightstand/application.fam b/applications/external/nightstand_clock/application.fam
similarity index 91%
rename from applications/plugins/nightstand/application.fam
rename to applications/external/nightstand_clock/application.fam
index ea28c7b77..1fb572054 100644
--- a/applications/plugins/nightstand/application.fam
+++ b/applications/external/nightstand_clock/application.fam
@@ -4,7 +4,6 @@ App(
apptype=FlipperAppType.EXTERNAL,
entry_point="clock_app",
requires=["gui"],
- icon="A_Clock_14",
stack_size=2 * 1024,
fap_icon="clock.png",
fap_category="Misc",
diff --git a/applications/plugins/nightstand/clock.png b/applications/external/nightstand_clock/clock.png
similarity index 100%
rename from applications/plugins/nightstand/clock.png
rename to applications/external/nightstand_clock/clock.png
diff --git a/applications/plugins/nightstand/clock_app.c b/applications/external/nightstand_clock/clock_app.c
similarity index 100%
rename from applications/plugins/nightstand/clock_app.c
rename to applications/external/nightstand_clock/clock_app.c
diff --git a/applications/plugins/nightstand/clock_app.h b/applications/external/nightstand_clock/clock_app.h
similarity index 100%
rename from applications/plugins/nightstand/clock_app.h
rename to applications/external/nightstand_clock/clock_app.h
diff --git a/applications/external/nrf24scan/Distr/nrf24scan/addr-CO2mini.txt b/applications/external/nrf24scan/Distr/nrf24scan/addr-CO2mini.txt
new file mode 100644
index 000000000..d159da337
--- /dev/null
+++ b/applications/external/nrf24scan/Distr/nrf24scan/addr-CO2mini.txt
@@ -0,0 +1,7 @@
+Rate: 1
+Ch: 122
+ESB: 1
+DPL: 0
+CRC: 2
+Payload: 4
+P0: C8C8C1
diff --git a/applications/external/nrf24scan/Distr/nrf24scan/addr-WCO1.txt b/applications/external/nrf24scan/Distr/nrf24scan/addr-WCO1.txt
new file mode 100644
index 000000000..658340d03
--- /dev/null
+++ b/applications/external/nrf24scan/Distr/nrf24scan/addr-WCO1.txt
@@ -0,0 +1,7 @@
+Rate: 1
+Ch: 120
+ESB: 1
+DPL: 1
+CRC: 2
+Payload: 4
+P0: C8C8E5
diff --git a/applications/external/nrf24scan/Distr/nrf24scan/addresses.txt b/applications/external/nrf24scan/Distr/nrf24scan/addresses.txt
new file mode 100644
index 000000000..e5bd3ed32
--- /dev/null
+++ b/applications/external/nrf24scan/Distr/nrf24scan/addresses.txt
@@ -0,0 +1,11 @@
+Rate: 1
+Ch: 2
+ESB: 1
+DPL: 0
+CRC: 2
+Payload: 4
+P0: C8C8C0
+P1: C8C8C1
+P2: C2
+P3: C3
+P4: E5
diff --git a/applications/external/nrf24scan/Distr/nrf24scan/sniff.txt b/applications/external/nrf24scan/Distr/nrf24scan/sniff.txt
new file mode 100644
index 000000000..fa8c5ba9f
--- /dev/null
+++ b/applications/external/nrf24scan/Distr/nrf24scan/sniff.txt
@@ -0,0 +1,5 @@
+SNIFF
+ESB: 1
+CRC: 2
+P0: 00AA
+P1: 0055
diff --git a/applications/plugins/rc2014_coleco/LICENSE b/applications/external/nrf24scan/LICENSE
similarity index 100%
rename from applications/plugins/rc2014_coleco/LICENSE
rename to applications/external/nrf24scan/LICENSE
diff --git a/applications/plugins/nrf24scan/application.fam b/applications/external/nrf24scan/application.fam
similarity index 100%
rename from applications/plugins/nrf24scan/application.fam
rename to applications/external/nrf24scan/application.fam
diff --git a/applications/plugins/nrf24scan/lib/nrf24/nrf24.c b/applications/external/nrf24scan/lib/nrf24/nrf24.c
similarity index 100%
rename from applications/plugins/nrf24scan/lib/nrf24/nrf24.c
rename to applications/external/nrf24scan/lib/nrf24/nrf24.c
diff --git a/applications/plugins/nrf24scan/lib/nrf24/nrf24.h b/applications/external/nrf24scan/lib/nrf24/nrf24.h
similarity index 100%
rename from applications/plugins/nrf24scan/lib/nrf24/nrf24.h
rename to applications/external/nrf24scan/lib/nrf24/nrf24.h
diff --git a/applications/plugins/nrf24scan/nrf24_packet_decoder.py b/applications/external/nrf24scan/nrf24_packet_decoder.py
similarity index 100%
rename from applications/plugins/nrf24scan/nrf24_packet_decoder.py
rename to applications/external/nrf24scan/nrf24_packet_decoder.py
diff --git a/applications/plugins/nrf24scan/nrf24scan.c b/applications/external/nrf24scan/nrf24scan.c
similarity index 98%
rename from applications/plugins/nrf24scan/nrf24scan.c
rename to applications/external/nrf24scan/nrf24scan.c
index d66e7caf6..d7a8b3d31 100644
--- a/applications/plugins/nrf24scan/nrf24scan.c
+++ b/applications/external/nrf24scan/nrf24scan.c
@@ -12,6 +12,7 @@
#include
#include
#include
+#include
#define TAG "nrf24scan"
#define VERSION "2.2"
@@ -531,6 +532,7 @@ static void prepare_nrf24(bool fsend_packet) {
nrf24_HANDLE,
REG_FEATURE,
0); // Enables the W_TX_PAYLOAD_NOACK command, Disable Payload with ACK, set Dynamic Payload
+ // EN_DYN_ACK(0x01) for W_TX_PAYLOAD_NOACK cmd broke AA on some fake nRF24l01+ modules
} else if(setup_from_log) { // Scan
nrf24_write_reg(
nrf24_HANDLE,
@@ -543,8 +545,8 @@ static void prepare_nrf24(bool fsend_packet) {
nrf24_HANDLE,
REG_FEATURE,
*(rec + 2 + addr_size) >> 2 != 0x33 ?
- 4 + 1 :
- 1); // Enables the W_TX_PAYLOAD_NOACK command, Disable Payload with ACK, set Dynamic Payload
+ 4 :
+ 0); // Enables the W_TX_PAYLOAD_NOACK command, Disable Payload with ACK, set Dynamic Payload
if(*(rec + 1) & 0b100) { // ESB
nrf24_write_reg(nrf24_HANDLE, REG_SETUP_RETR, 0x01); // Automatic Retransmission
nrf24_write_reg(nrf24_HANDLE, REG_EN_AA, 0x3F); // Auto acknowledgement
@@ -569,8 +571,8 @@ static void prepare_nrf24(bool fsend_packet) {
nrf24_HANDLE,
REG_FEATURE,
NRF_DPL ?
- 4 + 1 :
- 1); // Enables the W_TX_PAYLOAD_NOACK command, Disable Payload with ACK, set Dynamic Payload
+ 4 :
+ 0); // Enables the W_TX_PAYLOAD_NOACK command, Disable Payload with ACK, set Dynamic Payload
nrf24_write_reg(
nrf24_HANDLE, REG_DYNPD, NRF_DPL ? 0x3F : 0); // Enable dynamic payload reg
nrf24_write_reg(nrf24_HANDLE, REG_RF_CH, NRF_channel);
@@ -908,7 +910,7 @@ bool nrf24_send_packet() {
nrf24_set_maclen(nrf24_HANDLE, 2);
nrf24_set_mac(REG_RX_ADDR_P0, adr, 2);
nrf24_set_mac(REG_TX_ADDR, adr, 2);
- last_packet_send_st = nrf24_txpacket(nrf24_HANDLE, ptr + 2 + 2, 32 - 2, false);
+ last_packet_send_st = nrf24_txpacket(nrf24_HANDLE, ptr + 2 + 2, 32 - 2, true);
} else {
nrf24_write_reg(
nrf24_HANDLE, REG_SETUP_RETR, NRF_ESB ? 0x11 : 0); // Automatic Retransmission
@@ -942,7 +944,7 @@ bool nrf24_send_packet() {
NRF_CRC == 2 ? 0b1100 :
0))); // Mask all interrupts
nrf24_write_reg(nrf24_HANDLE, REG_DYNPD, NRF_DPL ? 0x3F : 0); // Enable dynamic payload reg
- last_packet_send_st = nrf24_txpacket(nrf24_HANDLE, ptr + 2, a, false);
+ last_packet_send_st = nrf24_txpacket(nrf24_HANDLE, ptr + 2, a, true);
}
last_packet_send = view_log_arr_idx;
notification_message(
@@ -953,10 +955,9 @@ bool nrf24_send_packet() {
}
static void render_callback(Canvas* const canvas, void* ctx) {
- furi_assert(ctx);
const PluginState* plugin_state = ctx;
- furi_mutex_acquire(plugin_state->mutex, FuriWaitForever);
-
+ if(plugin_state == NULL) return;
+ if(furi_mutex_acquire(plugin_state->mutex, 25) != FuriStatusOk) return;
//canvas_draw_frame(canvas, 0, 0, 128, 64); // border around the edge of the screen
if(what_doing == 0) {
canvas_set_font(canvas, FontSecondary); // 8x10 font, 6 lines
@@ -1331,13 +1332,14 @@ static void render_callback(Canvas* const canvas, void* ctx) {
int32_t nrf24scan_app(void* p) {
UNUSED(p);
APP = malloc(sizeof(Nrf24Scan));
+ DOLPHIN_DEED(DolphinDeedPluginStart);
APP->event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent));
- APP->plugin_state = malloc(sizeof(PluginState));
- APP->plugin_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
- if(!APP->plugin_state->mutex) {
+ PluginState* plugin_state = malloc(sizeof(PluginState));
+ plugin_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
+ if(!plugin_state->mutex) {
furi_message_queue_free(APP->event_queue);
FURI_LOG_E(TAG, "cannot create mutex");
- free(APP->plugin_state);
+ free(plugin_state);
return 255;
}
APP->log_arr = malloc(LOG_REC_SIZE * MAX_LOG_RECORDS);
@@ -1363,7 +1365,7 @@ int32_t nrf24scan_app(void* p) {
// Set system callbacks
APP->view_port = view_port_alloc();
- view_port_draw_callback_set(APP->view_port, render_callback, APP->plugin_state);
+ view_port_draw_callback_set(APP->view_port, render_callback, plugin_state);
view_port_input_callback_set(APP->view_port, input_callback, APP->event_queue);
// Open GUI and register view_port
@@ -1404,7 +1406,7 @@ int32_t nrf24scan_app(void* p) {
PluginEvent event;
for(bool processing = true; processing;) {
FuriStatus event_status = furi_message_queue_get(APP->event_queue, &event, 100);
- furi_mutex_acquire(APP->plugin_state->mutex, FuriWaitForever);
+ furi_mutex_acquire(plugin_state->mutex, FuriWaitForever);
if(event_status == FuriStatusOk) {
// press events
@@ -1631,7 +1633,7 @@ int32_t nrf24scan_app(void* p) {
}
view_port_update(APP->view_port);
- furi_mutex_release(APP->plugin_state->mutex);
+ furi_mutex_release(plugin_state->mutex);
}
nrf24_set_idle(nrf24_HANDLE);
if(log_arr_idx && (log_to_file == 1 || log_to_file == 2)) {
@@ -1646,11 +1648,11 @@ int32_t nrf24scan_app(void* p) {
furi_record_close(RECORD_NOTIFICATION);
furi_record_close(RECORD_STORAGE);
view_port_free(APP->view_port);
- furi_mutex_free(APP->plugin_state->mutex);
furi_message_queue_free(APP->event_queue);
- free(APP->plugin_state);
if(APP->log_arr) free(APP->log_arr);
if(APP->found) free(APP->found);
+ furi_mutex_free(plugin_state->mutex);
+ free(plugin_state);
free(APP);
return 0;
}
diff --git a/applications/plugins/nrf24scan/nrf24scan.h b/applications/external/nrf24scan/nrf24scan.h
similarity index 95%
rename from applications/plugins/nrf24scan/nrf24scan.h
rename to applications/external/nrf24scan/nrf24scan.h
index cfc4806e7..86e34856d 100644
--- a/applications/plugins/nrf24scan/nrf24scan.h
+++ b/applications/external/nrf24scan/nrf24scan.h
@@ -31,7 +31,6 @@ struct FOUND {
typedef struct {
Gui* gui;
FuriMessageQueue* event_queue;
- PluginState* plugin_state;
ViewPort* view_port;
Storage* storage;
NotificationApp* notification;
diff --git a/applications/plugins/nrf24scan/nrf24scan_10px.png b/applications/external/nrf24scan/nrf24scan_10px.png
similarity index 100%
rename from applications/plugins/nrf24scan/nrf24scan_10px.png
rename to applications/external/nrf24scan/nrf24scan_10px.png
diff --git a/applications/plugins/nrfsniff/application.fam b/applications/external/nrfsniff/application.fam
similarity index 92%
rename from applications/plugins/nrfsniff/application.fam
rename to applications/external/nrfsniff/application.fam
index e55ab3e9c..0330ab11a 100644
--- a/applications/plugins/nrfsniff/application.fam
+++ b/applications/external/nrfsniff/application.fam
@@ -3,7 +3,6 @@ App(
name="[NRF24] Sniffer",
apptype=FlipperAppType.EXTERNAL,
entry_point="nrfsniff_app",
- cdefines=["APP_NRFSNIFF"],
requires=["gui"],
stack_size=2 * 1024,
order=60,
diff --git a/applications/plugins/nrfsniff/lib/nrf24/nrf24.c b/applications/external/nrfsniff/lib/nrf24/nrf24.c
similarity index 100%
rename from applications/plugins/nrfsniff/lib/nrf24/nrf24.c
rename to applications/external/nrfsniff/lib/nrf24/nrf24.c
diff --git a/applications/plugins/nrfsniff/lib/nrf24/nrf24.h b/applications/external/nrfsniff/lib/nrf24/nrf24.h
similarity index 100%
rename from applications/plugins/nrfsniff/lib/nrf24/nrf24.h
rename to applications/external/nrfsniff/lib/nrf24/nrf24.h
diff --git a/applications/plugins/nrfsniff/nrfsniff.c b/applications/external/nrfsniff/nrfsniff.c
similarity index 99%
rename from applications/plugins/nrfsniff/nrfsniff.c
rename to applications/external/nrfsniff/nrfsniff.c
index 56bc33d05..1c2ea543f 100644
--- a/applications/plugins/nrfsniff/nrfsniff.c
+++ b/applications/external/nrfsniff/nrfsniff.c
@@ -4,6 +4,7 @@
#include
#include
#include
+#include
#include
#include
@@ -314,6 +315,7 @@ static void start_sniffing() {
int32_t nrfsniff_app(void* p) {
UNUSED(p);
+ DOLPHIN_DEED(DolphinDeedPluginStart);
uint8_t address[5] = {0};
uint32_t start = 0;
hexlify(address, 5, top_address);
diff --git a/applications/plugins/nrfsniff/nrfsniff_10px.png b/applications/external/nrfsniff/nrfsniff_10px.png
similarity index 100%
rename from applications/plugins/nrfsniff/nrfsniff_10px.png
rename to applications/external/nrfsniff/nrfsniff_10px.png
diff --git a/applications/plugins/ocarina/application.fam b/applications/external/ocarina/application.fam
similarity index 100%
rename from applications/plugins/ocarina/application.fam
rename to applications/external/ocarina/application.fam
diff --git a/applications/plugins/ocarina/icons/music_10px.png b/applications/external/ocarina/icons/music_10px.png
similarity index 100%
rename from applications/plugins/ocarina/icons/music_10px.png
rename to applications/external/ocarina/icons/music_10px.png
diff --git a/applications/plugins/ocarina/ocarina.c b/applications/external/ocarina/ocarina.c
similarity index 100%
rename from applications/plugins/ocarina/ocarina.c
rename to applications/external/ocarina/ocarina.c
diff --git a/applications/plugins/orgasmotron/application.fam b/applications/external/orgasmotron/application.fam
similarity index 100%
rename from applications/plugins/orgasmotron/application.fam
rename to applications/external/orgasmotron/application.fam
diff --git a/applications/plugins/orgasmotron/orgasmotron.c b/applications/external/orgasmotron/orgasmotron.c
similarity index 100%
rename from applications/plugins/orgasmotron/orgasmotron.c
rename to applications/external/orgasmotron/orgasmotron.c
diff --git a/applications/plugins/orgasmotron/orgasmotron_10px.png b/applications/external/orgasmotron/orgasmotron_10px.png
similarity index 100%
rename from applications/plugins/orgasmotron/orgasmotron_10px.png
rename to applications/external/orgasmotron/orgasmotron_10px.png
diff --git a/applications/plugins/paint/application.fam b/applications/external/paint/application.fam
similarity index 100%
rename from applications/plugins/paint/application.fam
rename to applications/external/paint/application.fam
diff --git a/applications/plugins/paint/paint.c b/applications/external/paint/paint.c
similarity index 100%
rename from applications/plugins/paint/paint.c
rename to applications/external/paint/paint.c
diff --git a/applications/plugins/paint/paintIcon.png b/applications/external/paint/paintIcon.png
similarity index 100%
rename from applications/plugins/paint/paintIcon.png
rename to applications/external/paint/paintIcon.png
diff --git a/applications/plugins/passgen/LICENSE b/applications/external/passgen/LICENSE
similarity index 100%
rename from applications/plugins/passgen/LICENSE
rename to applications/external/passgen/LICENSE
diff --git a/applications/plugins/passgen/application.fam b/applications/external/passgen/application.fam
similarity index 85%
rename from applications/plugins/passgen/application.fam
rename to applications/external/passgen/application.fam
index b6c1ae8e0..6a9652dc1 100644
--- a/applications/plugins/passgen/application.fam
+++ b/applications/external/passgen/application.fam
@@ -1,7 +1,7 @@
App(
appid="passgen",
name="Password Generator",
- apptype=FlipperAppType.PLUGIN,
+ apptype=FlipperAppType.EXTERNAL,
entry_point="passgenapp",
requires=[
"gui",
diff --git a/applications/plugins/passgen/icons/Horizontal_arrow_9x7.png b/applications/external/passgen/icons/Horizontal_arrow_9x7.png
similarity index 100%
rename from applications/plugins/passgen/icons/Horizontal_arrow_9x7.png
rename to applications/external/passgen/icons/Horizontal_arrow_9x7.png
diff --git a/assets/icons/MainMenu/Debug_14/frame_01.png b/applications/external/passgen/icons/Ok_btn_9x9.png
similarity index 74%
rename from assets/icons/MainMenu/Debug_14/frame_01.png
rename to applications/external/passgen/icons/Ok_btn_9x9.png
index 59b61fea8..9a1539da2 100644
Binary files a/assets/icons/MainMenu/Debug_14/frame_01.png and b/applications/external/passgen/icons/Ok_btn_9x9.png differ
diff --git a/applications/plugins/passgen/icons/Pin_back_arrow_10x8.png b/applications/external/passgen/icons/Pin_back_arrow_10x8.png
similarity index 100%
rename from applications/plugins/passgen/icons/Pin_back_arrow_10x8.png
rename to applications/external/passgen/icons/Pin_back_arrow_10x8.png
diff --git a/applications/plugins/passgen/icons/Vertical_arrow_7x9.png b/applications/external/passgen/icons/Vertical_arrow_7x9.png
similarity index 100%
rename from applications/plugins/passgen/icons/Vertical_arrow_7x9.png
rename to applications/external/passgen/icons/Vertical_arrow_7x9.png
diff --git a/applications/plugins/passgen/icons/passgen_icon.png b/applications/external/passgen/icons/passgen_icon.png
similarity index 100%
rename from applications/plugins/passgen/icons/passgen_icon.png
rename to applications/external/passgen/icons/passgen_icon.png
diff --git a/applications/plugins/passgen/images/preview.png b/applications/external/passgen/images/preview.png
similarity index 100%
rename from applications/plugins/passgen/images/preview.png
rename to applications/external/passgen/images/preview.png
diff --git a/applications/plugins/passgen/passgen.c b/applications/external/passgen/passgen.c
similarity index 100%
rename from applications/plugins/passgen/passgen.c
rename to applications/external/passgen/passgen.c
diff --git a/applications/plugins/picopass/125_10px.png b/applications/external/picopass/125_10px.png
similarity index 100%
rename from applications/plugins/picopass/125_10px.png
rename to applications/external/picopass/125_10px.png
diff --git a/applications/plugins/picopass/application.fam b/applications/external/picopass/application.fam
similarity index 100%
rename from applications/plugins/picopass/application.fam
rename to applications/external/picopass/application.fam
diff --git a/applications/plugins/picopass/helpers/iclass_elite_dict.c b/applications/external/picopass/helpers/iclass_elite_dict.c
similarity index 87%
rename from applications/plugins/picopass/helpers/iclass_elite_dict.c
rename to applications/external/picopass/helpers/iclass_elite_dict.c
index e8c13dd1d..f92dce0aa 100644
--- a/applications/plugins/picopass/helpers/iclass_elite_dict.c
+++ b/applications/external/picopass/helpers/iclass_elite_dict.c
@@ -5,6 +5,7 @@
#define ICLASS_ELITE_DICT_FLIPPER_NAME APP_DATA_PATH("assets/iclass_elite_dict.txt")
#define ICLASS_ELITE_DICT_USER_NAME APP_DATA_PATH("assets/iclass_elite_dict_user.txt")
+#define ICLASS_STANDARD_DICT_FLIPPER_NAME APP_DATA_PATH("assets/iclass_standard_dict.txt")
#define TAG "IclassEliteDict"
@@ -25,6 +26,9 @@ bool iclass_elite_dict_check_presence(IclassEliteDictType dict_type) {
(storage_common_stat(storage, ICLASS_ELITE_DICT_FLIPPER_NAME, NULL) == FSE_OK);
} else if(dict_type == IclassEliteDictTypeUser) {
dict_present = (storage_common_stat(storage, ICLASS_ELITE_DICT_USER_NAME, NULL) == FSE_OK);
+ } else if(dict_type == IclassStandardDictTypeFlipper) {
+ dict_present =
+ (storage_common_stat(storage, ICLASS_STANDARD_DICT_FLIPPER_NAME, NULL) == FSE_OK);
}
furi_record_close(RECORD_STORAGE);
@@ -52,6 +56,15 @@ IclassEliteDict* iclass_elite_dict_alloc(IclassEliteDictType dict_type) {
buffered_file_stream_close(dict->stream);
break;
}
+ } else if(dict_type == IclassStandardDictTypeFlipper) {
+ if(!buffered_file_stream_open(
+ dict->stream,
+ ICLASS_STANDARD_DICT_FLIPPER_NAME,
+ FSAM_READ,
+ FSOM_OPEN_EXISTING)) {
+ buffered_file_stream_close(dict->stream);
+ break;
+ }
}
// Read total amount of keys
@@ -148,4 +161,4 @@ bool iclass_elite_dict_add_key(IclassEliteDict* dict, uint8_t* key) {
furi_string_free(key_str);
return key_added;
-}
\ No newline at end of file
+}
diff --git a/applications/plugins/picopass/helpers/iclass_elite_dict.h b/applications/external/picopass/helpers/iclass_elite_dict.h
similarity index 95%
rename from applications/plugins/picopass/helpers/iclass_elite_dict.h
rename to applications/external/picopass/helpers/iclass_elite_dict.h
index e5ec8dfcb..150cd1b76 100644
--- a/applications/plugins/picopass/helpers/iclass_elite_dict.h
+++ b/applications/external/picopass/helpers/iclass_elite_dict.h
@@ -9,6 +9,7 @@
typedef enum {
IclassEliteDictTypeUser,
IclassEliteDictTypeFlipper,
+ IclassStandardDictTypeFlipper,
} IclassEliteDictType;
typedef struct IclassEliteDict IclassEliteDict;
@@ -25,4 +26,4 @@ bool iclass_elite_dict_get_next_key(IclassEliteDict* dict, uint8_t* key);
bool iclass_elite_dict_rewind(IclassEliteDict* dict);
-bool iclass_elite_dict_add_key(IclassEliteDict* dict, uint8_t* key);
\ No newline at end of file
+bool iclass_elite_dict_add_key(IclassEliteDict* dict, uint8_t* key);
diff --git a/applications/plugins/picopass/icons/DolphinMafia_115x62.png b/applications/external/picopass/icons/DolphinMafia_115x62.png
similarity index 100%
rename from applications/plugins/picopass/icons/DolphinMafia_115x62.png
rename to applications/external/picopass/icons/DolphinMafia_115x62.png
diff --git a/applications/plugins/picopass/icons/DolphinNice_96x59.png b/applications/external/picopass/icons/DolphinNice_96x59.png
similarity index 100%
rename from applications/plugins/picopass/icons/DolphinNice_96x59.png
rename to applications/external/picopass/icons/DolphinNice_96x59.png
diff --git a/applications/plugins/picopass/icons/Nfc_10px.png b/applications/external/picopass/icons/Nfc_10px.png
similarity index 100%
rename from applications/plugins/picopass/icons/Nfc_10px.png
rename to applications/external/picopass/icons/Nfc_10px.png
diff --git a/applications/plugins/picopass/icons/RFIDDolphinReceive_97x61.png b/applications/external/picopass/icons/RFIDDolphinReceive_97x61.png
similarity index 100%
rename from applications/plugins/picopass/icons/RFIDDolphinReceive_97x61.png
rename to applications/external/picopass/icons/RFIDDolphinReceive_97x61.png
diff --git a/applications/plugins/picopass/icons/RFIDDolphinSend_97x61.png b/applications/external/picopass/icons/RFIDDolphinSend_97x61.png
similarity index 100%
rename from applications/plugins/picopass/icons/RFIDDolphinSend_97x61.png
rename to applications/external/picopass/icons/RFIDDolphinSend_97x61.png
diff --git a/applications/plugins/picopass/lib/loclass/optimized_cipher.c b/applications/external/picopass/lib/loclass/optimized_cipher.c
similarity index 100%
rename from applications/plugins/picopass/lib/loclass/optimized_cipher.c
rename to applications/external/picopass/lib/loclass/optimized_cipher.c
diff --git a/applications/plugins/picopass/lib/loclass/optimized_cipher.h b/applications/external/picopass/lib/loclass/optimized_cipher.h
similarity index 100%
rename from applications/plugins/picopass/lib/loclass/optimized_cipher.h
rename to applications/external/picopass/lib/loclass/optimized_cipher.h
diff --git a/applications/plugins/picopass/lib/loclass/optimized_cipherutils.c b/applications/external/picopass/lib/loclass/optimized_cipherutils.c
similarity index 100%
rename from applications/plugins/picopass/lib/loclass/optimized_cipherutils.c
rename to applications/external/picopass/lib/loclass/optimized_cipherutils.c
diff --git a/applications/plugins/picopass/lib/loclass/optimized_cipherutils.h b/applications/external/picopass/lib/loclass/optimized_cipherutils.h
similarity index 100%
rename from applications/plugins/picopass/lib/loclass/optimized_cipherutils.h
rename to applications/external/picopass/lib/loclass/optimized_cipherutils.h
diff --git a/applications/plugins/picopass/lib/loclass/optimized_elite.c b/applications/external/picopass/lib/loclass/optimized_elite.c
similarity index 100%
rename from applications/plugins/picopass/lib/loclass/optimized_elite.c
rename to applications/external/picopass/lib/loclass/optimized_elite.c
diff --git a/applications/plugins/picopass/lib/loclass/optimized_elite.h b/applications/external/picopass/lib/loclass/optimized_elite.h
similarity index 100%
rename from applications/plugins/picopass/lib/loclass/optimized_elite.h
rename to applications/external/picopass/lib/loclass/optimized_elite.h
diff --git a/applications/plugins/picopass/lib/loclass/optimized_ikeys.c b/applications/external/picopass/lib/loclass/optimized_ikeys.c
similarity index 100%
rename from applications/plugins/picopass/lib/loclass/optimized_ikeys.c
rename to applications/external/picopass/lib/loclass/optimized_ikeys.c
diff --git a/applications/plugins/picopass/lib/loclass/optimized_ikeys.h b/applications/external/picopass/lib/loclass/optimized_ikeys.h
similarity index 100%
rename from applications/plugins/picopass/lib/loclass/optimized_ikeys.h
rename to applications/external/picopass/lib/loclass/optimized_ikeys.h
diff --git a/applications/plugins/picopass/picopass.c b/applications/external/picopass/picopass.c
similarity index 93%
rename from applications/plugins/picopass/picopass.c
rename to applications/external/picopass/picopass.c
index c1428b2fb..e3271ae48 100644
--- a/applications/plugins/picopass/picopass.c
+++ b/applications/external/picopass/picopass.c
@@ -1,4 +1,5 @@
#include "picopass_i.h"
+#include
#define TAG "PicoPass"
@@ -73,6 +74,12 @@ Picopass* picopass_alloc() {
view_dispatcher_add_view(
picopass->view_dispatcher, PicopassViewWidget, widget_get_view(picopass->widget));
+ picopass->dict_attack = dict_attack_alloc();
+ view_dispatcher_add_view(
+ picopass->view_dispatcher,
+ PicopassViewDictAttack,
+ dict_attack_get_view(picopass->dict_attack));
+
return picopass;
}
@@ -103,6 +110,9 @@ void picopass_free(Picopass* picopass) {
view_dispatcher_remove_view(picopass->view_dispatcher, PicopassViewWidget);
widget_free(picopass->widget);
+ view_dispatcher_remove_view(picopass->view_dispatcher, PicopassViewDictAttack);
+ dict_attack_free(picopass->dict_attack);
+
// Worker
picopass_worker_stop(picopass->worker);
picopass_worker_free(picopass->worker);
@@ -191,6 +201,7 @@ int32_t picopass_app(void* p) {
UNUSED(p);
picopass_migrate_from_old_folder();
+ DOLPHIN_DEED(DolphinDeedPluginStart);
Picopass* picopass = picopass_alloc();
scene_manager_next_scene(picopass->scene_manager, PicopassSceneStart);
@@ -200,4 +211,4 @@ int32_t picopass_app(void* p) {
picopass_free(picopass);
return 0;
-}
\ No newline at end of file
+}
diff --git a/applications/plugins/picopass/picopass.h b/applications/external/picopass/picopass.h
similarity index 100%
rename from applications/plugins/picopass/picopass.h
rename to applications/external/picopass/picopass.h
diff --git a/applications/plugins/picopass/picopass_device.c b/applications/external/picopass/picopass_device.c
similarity index 100%
rename from applications/plugins/picopass/picopass_device.c
rename to applications/external/picopass/picopass_device.c
diff --git a/applications/plugins/picopass/picopass_device.h b/applications/external/picopass/picopass_device.h
similarity index 92%
rename from applications/plugins/picopass/picopass_device.h
rename to applications/external/picopass/picopass_device.h
index d7d0977df..7fc35ebda 100644
--- a/applications/plugins/picopass/picopass_device.h
+++ b/applications/external/picopass/picopass_device.h
@@ -27,8 +27,16 @@
#define PICOPASS_APP_EXTENSION ".picopass"
#define PICOPASS_APP_SHADOW_EXTENSION ".pas"
+#define PICOPASS_DICT_KEY_BATCH_SIZE 10
+
typedef void (*PicopassLoadingCallback)(void* context, bool state);
+typedef struct {
+ IclassEliteDict* dict;
+ IclassEliteDictType type;
+ uint8_t current_sector;
+} IclassEliteDictAttackData;
+
typedef enum {
PicopassDeviceEncryptionUnknown = 0,
PicopassDeviceEncryptionNone = 0x14,
@@ -69,6 +77,7 @@ typedef struct {
typedef struct {
PicopassBlock AA1[PICOPASS_MAX_APP_LIMIT];
PicopassPacs pacs;
+ IclassEliteDictAttackData iclass_elite_dict_attack_data;
} PicopassDeviceData;
typedef struct {
diff --git a/applications/plugins/picopass/picopass_i.h b/applications/external/picopass/picopass_i.h
similarity index 94%
rename from applications/plugins/picopass/picopass_i.h
rename to applications/external/picopass/picopass_i.h
index 79c2a1af8..23da3056e 100644
--- a/applications/plugins/picopass/picopass_i.h
+++ b/applications/external/picopass/picopass_i.h
@@ -21,6 +21,7 @@
#include
#include "scenes/picopass_scene.h"
+#include "views/dict_attack.h"
#include
#include
@@ -36,6 +37,7 @@ enum PicopassCustomEvent {
PicopassCustomEventWorkerExit,
PicopassCustomEventByteInputDone,
PicopassCustomEventTextInputDone,
+ PicopassCustomEventDictAttackSkip,
};
typedef enum {
@@ -60,6 +62,7 @@ struct Picopass {
Loading* loading;
TextInput* text_input;
Widget* widget;
+ DictAttack* dict_attack;
};
typedef enum {
@@ -68,6 +71,7 @@ typedef enum {
PicopassViewLoading,
PicopassViewTextInput,
PicopassViewWidget,
+ PicopassViewDictAttack,
} PicopassView;
Picopass* picopass_alloc();
diff --git a/applications/plugins/picopass/picopass_keys.c b/applications/external/picopass/picopass_keys.c
similarity index 100%
rename from applications/plugins/picopass/picopass_keys.c
rename to applications/external/picopass/picopass_keys.c
diff --git a/applications/plugins/picopass/picopass_keys.h b/applications/external/picopass/picopass_keys.h
similarity index 100%
rename from applications/plugins/picopass/picopass_keys.h
rename to applications/external/picopass/picopass_keys.h
diff --git a/applications/plugins/picopass/picopass_worker.c b/applications/external/picopass/picopass_worker.c
similarity index 73%
rename from applications/plugins/picopass/picopass_worker.c
rename to applications/external/picopass/picopass_worker.c
index d141bdf6b..06d361fb5 100644
--- a/applications/plugins/picopass/picopass_worker.c
+++ b/applications/external/picopass/picopass_worker.c
@@ -23,7 +23,7 @@ PicopassWorker* picopass_worker_alloc() {
// Worker thread attributes
picopass_worker->thread =
- furi_thread_alloc_ex("PicopassWorker", 8192, picopass_worker_task, picopass_worker);
+ furi_thread_alloc_ex("PicopassWorker", 8 * 1024, picopass_worker_task, picopass_worker);
picopass_worker->callback = NULL;
picopass_worker->context = NULL;
@@ -66,14 +66,12 @@ void picopass_worker_start(
void picopass_worker_stop(PicopassWorker* picopass_worker) {
furi_assert(picopass_worker);
- if(picopass_worker->state == PicopassWorkerStateBroken ||
- picopass_worker->state == PicopassWorkerStateReady) {
- return;
- }
- picopass_worker_disable_field(ERR_NONE);
+ furi_assert(picopass_worker->thread);
- picopass_worker_change_state(picopass_worker, PicopassWorkerStateStop);
- furi_thread_join(picopass_worker->thread);
+ if(furi_thread_get_state(picopass_worker->thread) != FuriThreadStateStopped) {
+ picopass_worker_change_state(picopass_worker, PicopassWorkerStateStop);
+ furi_thread_join(picopass_worker->thread);
+ }
}
void picopass_worker_change_state(PicopassWorker* picopass_worker, PicopassWorkerState state) {
@@ -172,14 +170,18 @@ ReturnCode picopass_read_preauth(PicopassBlock* AA1) {
return ERR_NONE;
}
-static ReturnCode picopass_auth_dict(
- uint8_t* csn,
- PicopassPacs* pacs,
- uint8_t* div_key,
- IclassEliteDictType dict_type,
- bool elite) {
+static ReturnCode
+ picopass_auth_dict(PicopassWorker* picopass_worker, IclassEliteDictType dict_type) {
rfalPicoPassReadCheckRes rcRes;
rfalPicoPassCheckRes chkRes;
+ bool elite = (dict_type != IclassStandardDictTypeFlipper);
+
+ PicopassDeviceData* dev_data = picopass_worker->dev_data;
+ PicopassBlock* AA1 = dev_data->AA1;
+ PicopassPacs* pacs = &dev_data->pacs;
+
+ uint8_t* csn = AA1[PICOPASS_CSN_BLOCK_INDEX].data;
+ uint8_t* div_key = AA1[PICOPASS_KD_BLOCK_INDEX].data;
ReturnCode err = ERR_PARAM;
@@ -204,7 +206,8 @@ static ReturnCode picopass_auth_dict(
while(iclass_elite_dict_get_next_key(dict, key)) {
FURI_LOG_D(
TAG,
- "Try to auth with key %zu %02x%02x%02x%02x%02x%02x%02x%02x",
+ "Try to %s auth with key %zu %02x%02x%02x%02x%02x%02x%02x%02x",
+ elite ? "elite" : "standard",
index++,
key[0],
key[1],
@@ -230,6 +233,8 @@ static ReturnCode picopass_auth_dict(
memcpy(pacs->key, key, PICOPASS_BLOCK_LEN);
break;
}
+
+ if(picopass_worker->state != PicopassWorkerStateDetect) break;
}
iclass_elite_dict_free(dict);
@@ -237,38 +242,23 @@ static ReturnCode picopass_auth_dict(
return err;
}
-ReturnCode picopass_auth(PicopassBlock* AA1, PicopassPacs* pacs) {
+ReturnCode picopass_auth(PicopassWorker* picopass_worker) {
ReturnCode err;
FURI_LOG_I(TAG, "Starting system dictionary attack [Standard KDF]");
- err = picopass_auth_dict(
- AA1[PICOPASS_CSN_BLOCK_INDEX].data,
- pacs,
- AA1[PICOPASS_KD_BLOCK_INDEX].data,
- IclassEliteDictTypeFlipper,
- false);
+ err = picopass_auth_dict(picopass_worker, IclassStandardDictTypeFlipper);
if(err == ERR_NONE) {
return ERR_NONE;
}
FURI_LOG_I(TAG, "Starting user dictionary attack [Elite KDF]");
- err = picopass_auth_dict(
- AA1[PICOPASS_CSN_BLOCK_INDEX].data,
- pacs,
- AA1[PICOPASS_KD_BLOCK_INDEX].data,
- IclassEliteDictTypeUser,
- true);
+ err = picopass_auth_dict(picopass_worker, IclassEliteDictTypeUser);
if(err == ERR_NONE) {
return ERR_NONE;
}
FURI_LOG_I(TAG, "Starting system dictionary attack [Elite KDF]");
- err = picopass_auth_dict(
- AA1[PICOPASS_CSN_BLOCK_INDEX].data,
- pacs,
- AA1[PICOPASS_KD_BLOCK_INDEX].data,
- IclassEliteDictTypeFlipper,
- true);
+ err = picopass_auth_dict(picopass_worker, IclassEliteDictTypeFlipper);
if(err == ERR_NONE) {
return ERR_NONE;
}
@@ -468,6 +458,132 @@ ReturnCode picopass_write_block(PicopassBlock* AA1, uint8_t blockNo, uint8_t* ne
return ERR_NONE;
}
+void picopass_worker_elite_dict_attack(PicopassWorker* picopass_worker) {
+ furi_assert(picopass_worker);
+ furi_assert(picopass_worker->callback);
+
+ picopass_device_data_clear(picopass_worker->dev_data);
+ PicopassDeviceData* dev_data = picopass_worker->dev_data;
+ PicopassBlock* AA1 = dev_data->AA1;
+ PicopassPacs* pacs = &dev_data->pacs;
+
+ for(size_t i = 0; i < PICOPASS_MAX_APP_LIMIT; i++) {
+ memset(AA1[i].data, 0, sizeof(AA1[i].data));
+ }
+ memset(pacs, 0, sizeof(PicopassPacs));
+
+ IclassEliteDictAttackData* dict_attack_data =
+ &picopass_worker->dev_data->iclass_elite_dict_attack_data;
+ bool elite = (dict_attack_data->type != IclassStandardDictTypeFlipper);
+
+ rfalPicoPassReadCheckRes rcRes;
+ rfalPicoPassCheckRes chkRes;
+
+ ReturnCode err;
+ uint8_t mac[4] = {0};
+ uint8_t ccnr[12] = {0};
+
+ size_t index = 0;
+ uint8_t key[PICOPASS_BLOCK_LEN] = {0};
+
+ // Load dictionary
+ IclassEliteDict* dict = dict_attack_data->dict;
+ if(!dict) {
+ FURI_LOG_E(TAG, "Dictionary not found");
+ picopass_worker->callback(PicopassWorkerEventNoDictFound, picopass_worker->context);
+ return;
+ }
+
+ do {
+ if(picopass_detect_card(1000) == ERR_NONE) {
+ picopass_worker->callback(PicopassWorkerEventCardDetected, picopass_worker->context);
+
+ // Process first found device
+ err = picopass_read_preauth(AA1);
+ if(err != ERR_NONE) {
+ FURI_LOG_E(TAG, "picopass_read_preauth error %d", err);
+ picopass_worker->callback(PicopassWorkerEventAborted, picopass_worker->context);
+ return;
+ }
+
+ // Thank you proxmark!
+ pacs->legacy = picopass_is_memset(AA1[5].data, 0xFF, 8);
+ pacs->se_enabled = (memcmp(AA1[5].data, "\xff\xff\xff\x00\x06\xff\xff\xff", 8) == 0);
+ if(pacs->se_enabled) {
+ FURI_LOG_D(TAG, "SE enabled");
+ picopass_worker->callback(PicopassWorkerEventAborted, picopass_worker->context);
+ return;
+ }
+
+ break;
+ } else {
+ picopass_worker->callback(PicopassWorkerEventNoCardDetected, picopass_worker->context);
+ }
+ if(picopass_worker->state != PicopassWorkerStateEliteDictAttack) break;
+
+ furi_delay_ms(100);
+ } while(true);
+
+ FURI_LOG_D(
+ TAG, "Start Dictionary attack, Key Count %lu", iclass_elite_dict_get_total_keys(dict));
+ while(iclass_elite_dict_get_next_key(dict, key)) {
+ FURI_LOG_T(TAG, "Key %zu", index);
+ if(++index % PICOPASS_DICT_KEY_BATCH_SIZE == 0) {
+ picopass_worker->callback(
+ PicopassWorkerEventNewDictKeyBatch, picopass_worker->context);
+ }
+
+ err = rfalPicoPassPollerReadCheck(&rcRes);
+ if(err != ERR_NONE) {
+ FURI_LOG_E(TAG, "rfalPicoPassPollerReadCheck error %d", err);
+ break;
+ }
+ memcpy(ccnr, rcRes.CCNR, sizeof(rcRes.CCNR)); // last 4 bytes left 0
+
+ uint8_t* csn = AA1[PICOPASS_CSN_BLOCK_INDEX].data;
+ uint8_t* div_key = AA1[PICOPASS_KD_BLOCK_INDEX].data;
+
+ loclass_iclass_calc_div_key(csn, key, div_key, elite);
+ loclass_opt_doReaderMAC(ccnr, div_key, mac);
+
+ err = rfalPicoPassPollerCheck(mac, &chkRes);
+ if(err == ERR_NONE) {
+ FURI_LOG_I(TAG, "Found key");
+ memcpy(pacs->key, key, PICOPASS_BLOCK_LEN);
+ err = picopass_read_card(AA1);
+ if(err != ERR_NONE) {
+ FURI_LOG_E(TAG, "picopass_read_card error %d", err);
+ picopass_worker->callback(PicopassWorkerEventFail, picopass_worker->context);
+ break;
+ }
+
+ err = picopass_device_parse_credential(AA1, pacs);
+ if(err != ERR_NONE) {
+ FURI_LOG_E(TAG, "picopass_device_parse_credential error %d", err);
+ picopass_worker->callback(PicopassWorkerEventFail, picopass_worker->context);
+ break;
+ }
+
+ err = picopass_device_parse_wiegand(pacs->credential, &pacs->record);
+ if(err != ERR_NONE) {
+ FURI_LOG_E(TAG, "picopass_device_parse_wiegand error %d", err);
+ picopass_worker->callback(PicopassWorkerEventFail, picopass_worker->context);
+ break;
+ }
+ picopass_worker->callback(PicopassWorkerEventSuccess, picopass_worker->context);
+ break;
+ }
+
+ if(picopass_worker->state != PicopassWorkerStateEliteDictAttack) break;
+ }
+ FURI_LOG_D(TAG, "Dictionary complete");
+ if(picopass_worker->state == PicopassWorkerStateEliteDictAttack) {
+ picopass_worker->callback(PicopassWorkerEventSuccess, picopass_worker->context);
+ } else {
+ picopass_worker->callback(PicopassWorkerEventAborted, picopass_worker->context);
+ }
+}
+
int32_t picopass_worker_task(void* context) {
PicopassWorker* picopass_worker = context;
@@ -478,9 +594,12 @@ int32_t picopass_worker_task(void* context) {
picopass_worker_write(picopass_worker);
} else if(picopass_worker->state == PicopassWorkerStateWriteKey) {
picopass_worker_write_key(picopass_worker);
+ } else if(picopass_worker->state == PicopassWorkerStateEliteDictAttack) {
+ picopass_worker_elite_dict_attack(picopass_worker);
+ } else {
+ FURI_LOG_W(TAG, "Unknown state %d", picopass_worker->state);
}
picopass_worker_disable_field(ERR_NONE);
-
picopass_worker_change_state(picopass_worker, PicopassWorkerStateReady);
return 0;
@@ -520,7 +639,7 @@ void picopass_worker_detect(PicopassWorker* picopass_worker) {
}
if(nextState == PicopassWorkerEventSuccess) {
- err = picopass_auth(AA1, pacs);
+ err = picopass_auth(picopass_worker);
if(err != ERR_NONE) {
FURI_LOG_E(TAG, "picopass_try_auth error %d", err);
nextState = PicopassWorkerEventFail;
diff --git a/applications/plugins/picopass/picopass_worker.h b/applications/external/picopass/picopass_worker.h
similarity index 87%
rename from applications/plugins/picopass/picopass_worker.h
rename to applications/external/picopass/picopass_worker.h
index f5e9f3039..e9d37481b 100644
--- a/applications/plugins/picopass/picopass_worker.h
+++ b/applications/external/picopass/picopass_worker.h
@@ -14,6 +14,7 @@ typedef enum {
PicopassWorkerStateDetect,
PicopassWorkerStateWrite,
PicopassWorkerStateWriteKey,
+ PicopassWorkerStateEliteDictAttack,
// Transition
PicopassWorkerStateStop,
} PicopassWorkerState;
@@ -27,8 +28,10 @@ typedef enum {
PicopassWorkerEventFail,
PicopassWorkerEventNoCardDetected,
PicopassWorkerEventSeEnabled,
-
- PicopassWorkerEventStartReading,
+ PicopassWorkerEventAborted,
+ PicopassWorkerEventCardDetected,
+ PicopassWorkerEventNewDictKeyBatch,
+ PicopassWorkerEventNoDictFound,
} PicopassWorkerEvent;
typedef void (*PicopassWorkerCallback)(PicopassWorkerEvent event, void* context);
diff --git a/applications/plugins/picopass/picopass_worker_i.h b/applications/external/picopass/picopass_worker_i.h
similarity index 100%
rename from applications/plugins/picopass/picopass_worker_i.h
rename to applications/external/picopass/picopass_worker_i.h
diff --git a/applications/plugins/picopass/rfal_picopass.c b/applications/external/picopass/rfal_picopass.c
similarity index 100%
rename from applications/plugins/picopass/rfal_picopass.c
rename to applications/external/picopass/rfal_picopass.c
diff --git a/applications/plugins/picopass/rfal_picopass.h b/applications/external/picopass/rfal_picopass.h
similarity index 100%
rename from applications/plugins/picopass/rfal_picopass.h
rename to applications/external/picopass/rfal_picopass.h
diff --git a/applications/plugins/picopass/scenes/picopass_scene.c b/applications/external/picopass/scenes/picopass_scene.c
similarity index 100%
rename from applications/plugins/picopass/scenes/picopass_scene.c
rename to applications/external/picopass/scenes/picopass_scene.c
diff --git a/applications/plugins/picopass/scenes/picopass_scene.h b/applications/external/picopass/scenes/picopass_scene.h
similarity index 100%
rename from applications/plugins/picopass/scenes/picopass_scene.h
rename to applications/external/picopass/scenes/picopass_scene.h
diff --git a/applications/plugins/picopass/scenes/picopass_scene_card_menu.c b/applications/external/picopass/scenes/picopass_scene_card_menu.c
similarity index 100%
rename from applications/plugins/picopass/scenes/picopass_scene_card_menu.c
rename to applications/external/picopass/scenes/picopass_scene_card_menu.c
diff --git a/applications/plugins/picopass/scenes/picopass_scene_config.h b/applications/external/picopass/scenes/picopass_scene_config.h
similarity index 92%
rename from applications/plugins/picopass/scenes/picopass_scene_config.h
rename to applications/external/picopass/scenes/picopass_scene_config.h
index f5a90d46e..8ea970498 100644
--- a/applications/plugins/picopass/scenes/picopass_scene_config.h
+++ b/applications/external/picopass/scenes/picopass_scene_config.h
@@ -14,3 +14,4 @@ ADD_SCENE(picopass, write_card_success, WriteCardSuccess)
ADD_SCENE(picopass, read_factory_success, ReadFactorySuccess)
ADD_SCENE(picopass, write_key, WriteKey)
ADD_SCENE(picopass, key_menu, KeyMenu)
+ADD_SCENE(picopass, elite_dict_attack, EliteDictAttack)
diff --git a/applications/plugins/picopass/scenes/picopass_scene_delete.c b/applications/external/picopass/scenes/picopass_scene_delete.c
similarity index 100%
rename from applications/plugins/picopass/scenes/picopass_scene_delete.c
rename to applications/external/picopass/scenes/picopass_scene_delete.c
diff --git a/applications/plugins/picopass/scenes/picopass_scene_delete_success.c b/applications/external/picopass/scenes/picopass_scene_delete_success.c
similarity index 100%
rename from applications/plugins/picopass/scenes/picopass_scene_delete_success.c
rename to applications/external/picopass/scenes/picopass_scene_delete_success.c
diff --git a/applications/plugins/picopass/scenes/picopass_scene_device_info.c b/applications/external/picopass/scenes/picopass_scene_device_info.c
similarity index 100%
rename from applications/plugins/picopass/scenes/picopass_scene_device_info.c
rename to applications/external/picopass/scenes/picopass_scene_device_info.c
diff --git a/applications/external/picopass/scenes/picopass_scene_elite_dict_attack.c b/applications/external/picopass/scenes/picopass_scene_elite_dict_attack.c
new file mode 100644
index 000000000..c76a8ffae
--- /dev/null
+++ b/applications/external/picopass/scenes/picopass_scene_elite_dict_attack.c
@@ -0,0 +1,170 @@
+#include "../picopass_i.h"
+#include
+
+#define TAG "IclassEliteDictAttack"
+
+typedef enum {
+ DictAttackStateIdle,
+ DictAttackStateUserDictInProgress,
+ DictAttackStateFlipperDictInProgress,
+ DictAttackStateStandardDictInProgress,
+} DictAttackState;
+
+void picopass_dict_attack_worker_callback(PicopassWorkerEvent event, void* context) {
+ furi_assert(context);
+ Picopass* picopass = context;
+ view_dispatcher_send_custom_event(picopass->view_dispatcher, event);
+}
+
+void picopass_dict_attack_result_callback(void* context) {
+ furi_assert(context);
+ Picopass* picopass = context;
+ view_dispatcher_send_custom_event(
+ picopass->view_dispatcher, PicopassCustomEventDictAttackSkip);
+}
+
+static void
+ picopass_scene_elite_dict_attack_prepare_view(Picopass* picopass, DictAttackState state) {
+ IclassEliteDictAttackData* dict_attack_data =
+ &picopass->dev->dev_data.iclass_elite_dict_attack_data;
+ PicopassWorkerState worker_state = PicopassWorkerStateReady;
+ IclassEliteDict* dict = NULL;
+
+ // Identify scene state
+ if(state == DictAttackStateIdle) {
+ if(iclass_elite_dict_check_presence(IclassEliteDictTypeUser)) {
+ FURI_LOG_D(TAG, "Starting with user dictionary");
+ state = DictAttackStateUserDictInProgress;
+ } else {
+ FURI_LOG_D(TAG, "Starting with standard dictionary");
+ state = DictAttackStateStandardDictInProgress;
+ }
+ } else if(state == DictAttackStateUserDictInProgress) {
+ FURI_LOG_D(TAG, "Moving from user dictionary to standard dictionary");
+ state = DictAttackStateStandardDictInProgress;
+ } else if(state == DictAttackStateStandardDictInProgress) {
+ FURI_LOG_D(TAG, "Moving from standard dictionary to elite dictionary");
+ state = DictAttackStateFlipperDictInProgress;
+ }
+
+ // Setup view
+ if(state == DictAttackStateUserDictInProgress) {
+ worker_state = PicopassWorkerStateEliteDictAttack;
+ dict_attack_set_header(picopass->dict_attack, "Elite User Dictionary");
+ dict_attack_data->type = IclassEliteDictTypeUser;
+ dict = iclass_elite_dict_alloc(IclassEliteDictTypeUser);
+
+ // If failed to load user dictionary - try the system dictionary
+ if(!dict) {
+ FURI_LOG_E(TAG, "User dictionary not found");
+ state = DictAttackStateStandardDictInProgress;
+ }
+ }
+ if(state == DictAttackStateStandardDictInProgress) {
+ worker_state = PicopassWorkerStateEliteDictAttack;
+ dict_attack_set_header(picopass->dict_attack, "Standard System Dictionary");
+ dict_attack_data->type = IclassStandardDictTypeFlipper;
+ dict = iclass_elite_dict_alloc(IclassStandardDictTypeFlipper);
+
+ if(!dict) {
+ FURI_LOG_E(TAG, "Flipper standard dictionary not found");
+ state = DictAttackStateFlipperDictInProgress;
+ }
+ }
+ if(state == DictAttackStateFlipperDictInProgress) {
+ worker_state = PicopassWorkerStateEliteDictAttack;
+ dict_attack_set_header(picopass->dict_attack, "Elite System Dictionary");
+ dict_attack_data->type = IclassEliteDictTypeFlipper;
+ dict = iclass_elite_dict_alloc(IclassEliteDictTypeFlipper);
+ if(!dict) {
+ FURI_LOG_E(TAG, "Flipper Elite dictionary not found");
+ // Pass through to let the worker handle the failure
+ }
+ }
+ // Free previous dictionary
+ if(dict_attack_data->dict) {
+ iclass_elite_dict_free(dict_attack_data->dict);
+ }
+ dict_attack_data->dict = dict;
+ scene_manager_set_scene_state(picopass->scene_manager, PicopassSceneEliteDictAttack, state);
+ dict_attack_set_callback(
+ picopass->dict_attack, picopass_dict_attack_result_callback, picopass);
+ dict_attack_set_current_sector(picopass->dict_attack, 0);
+ dict_attack_set_card_detected(picopass->dict_attack);
+ dict_attack_set_total_dict_keys(
+ picopass->dict_attack, dict ? iclass_elite_dict_get_total_keys(dict) : 0);
+ picopass_worker_start(
+ picopass->worker,
+ worker_state,
+ &picopass->dev->dev_data,
+ picopass_dict_attack_worker_callback,
+ picopass);
+}
+
+void picopass_scene_elite_dict_attack_on_enter(void* context) {
+ Picopass* picopass = context;
+ picopass_scene_elite_dict_attack_prepare_view(picopass, DictAttackStateIdle);
+ view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewDictAttack);
+ picopass_blink_start(picopass);
+ notification_message(picopass->notifications, &sequence_display_backlight_enforce_on);
+}
+
+bool picopass_scene_elite_dict_attack_on_event(void* context, SceneManagerEvent event) {
+ Picopass* picopass = context;
+ bool consumed = false;
+
+ uint32_t state =
+ scene_manager_get_scene_state(picopass->scene_manager, PicopassSceneEliteDictAttack);
+ if(event.type == SceneManagerEventTypeCustom) {
+ if(event.event == PicopassWorkerEventSuccess ||
+ event.event == PicopassWorkerEventAborted) {
+ if(state == DictAttackStateUserDictInProgress ||
+ state == DictAttackStateStandardDictInProgress) {
+ picopass_worker_stop(picopass->worker);
+ picopass_scene_elite_dict_attack_prepare_view(picopass, state);
+ consumed = true;
+ } else {
+ scene_manager_next_scene(picopass->scene_manager, PicopassSceneReadCardSuccess);
+ consumed = true;
+ }
+ } else if(event.event == PicopassWorkerEventCardDetected) {
+ dict_attack_set_card_detected(picopass->dict_attack);
+ consumed = true;
+ } else if(event.event == PicopassWorkerEventNoCardDetected) {
+ dict_attack_set_card_removed(picopass->dict_attack);
+ consumed = true;
+ } else if(event.event == PicopassWorkerEventNewDictKeyBatch) {
+ dict_attack_inc_current_dict_key(picopass->dict_attack, PICOPASS_DICT_KEY_BATCH_SIZE);
+ consumed = true;
+ } else if(event.event == PicopassCustomEventDictAttackSkip) {
+ if(state == DictAttackStateUserDictInProgress) {
+ picopass_worker_stop(picopass->worker);
+ consumed = true;
+ } else if(state == DictAttackStateFlipperDictInProgress) {
+ picopass_worker_stop(picopass->worker);
+ consumed = true;
+ } else if(state == DictAttackStateStandardDictInProgress) {
+ picopass_worker_stop(picopass->worker);
+ consumed = true;
+ }
+ }
+ } else if(event.type == SceneManagerEventTypeBack) {
+ consumed = scene_manager_previous_scene(picopass->scene_manager);
+ }
+ return consumed;
+}
+
+void picopass_scene_elite_dict_attack_on_exit(void* context) {
+ Picopass* picopass = context;
+ IclassEliteDictAttackData* dict_attack_data =
+ &picopass->dev->dev_data.iclass_elite_dict_attack_data;
+ // Stop worker
+ picopass_worker_stop(picopass->worker);
+ if(dict_attack_data->dict) {
+ iclass_elite_dict_free(dict_attack_data->dict);
+ dict_attack_data->dict = NULL;
+ }
+ dict_attack_reset(picopass->dict_attack);
+ picopass_blink_stop(picopass);
+ notification_message(picopass->notifications, &sequence_display_backlight_enforce_auto);
+}
diff --git a/applications/plugins/picopass/scenes/picopass_scene_file_select.c b/applications/external/picopass/scenes/picopass_scene_file_select.c
similarity index 100%
rename from applications/plugins/picopass/scenes/picopass_scene_file_select.c
rename to applications/external/picopass/scenes/picopass_scene_file_select.c
diff --git a/applications/plugins/picopass/scenes/picopass_scene_key_menu.c b/applications/external/picopass/scenes/picopass_scene_key_menu.c
similarity index 100%
rename from applications/plugins/picopass/scenes/picopass_scene_key_menu.c
rename to applications/external/picopass/scenes/picopass_scene_key_menu.c
diff --git a/applications/plugins/picopass/scenes/picopass_scene_read_card.c b/applications/external/picopass/scenes/picopass_scene_read_card.c
similarity index 100%
rename from applications/plugins/picopass/scenes/picopass_scene_read_card.c
rename to applications/external/picopass/scenes/picopass_scene_read_card.c
diff --git a/applications/plugins/picopass/scenes/picopass_scene_read_card_success.c b/applications/external/picopass/scenes/picopass_scene_read_card_success.c
similarity index 86%
rename from applications/plugins/picopass/scenes/picopass_scene_read_card_success.c
rename to applications/external/picopass/scenes/picopass_scene_read_card_success.c
index f078d460a..198b21d98 100644
--- a/applications/plugins/picopass/scenes/picopass_scene_read_card_success.c
+++ b/applications/external/picopass/scenes/picopass_scene_read_card_success.c
@@ -47,8 +47,21 @@ void picopass_scene_read_card_success_on_enter(void* context) {
if(pacs->se_enabled) {
furi_string_cat_printf(credential_str, "SE enabled");
}
+
+ widget_add_button_element(
+ widget,
+ GuiButtonTypeCenter,
+ "Menu",
+ picopass_scene_read_card_success_widget_callback,
+ picopass);
} else if(empty) {
furi_string_cat_printf(wiegand_str, "Empty");
+ widget_add_button_element(
+ widget,
+ GuiButtonTypeCenter,
+ "Menu",
+ picopass_scene_read_card_success_widget_callback,
+ picopass);
} else if(pacs->record.bitLength == 0 || pacs->record.bitLength == 255) {
// Neither of these are valid. Indicates the block was all 0x00 or all 0xff
furi_string_cat_printf(wiegand_str, "Invalid PACS");
@@ -56,6 +69,12 @@ void picopass_scene_read_card_success_on_enter(void* context) {
if(pacs->se_enabled) {
furi_string_cat_printf(credential_str, "SE enabled");
}
+ widget_add_button_element(
+ widget,
+ GuiButtonTypeCenter,
+ "Menu",
+ picopass_scene_read_card_success_widget_callback,
+ picopass);
} else {
size_t bytesLength = 1 + pacs->record.bitLength / 8;
furi_string_set(credential_str, "");
@@ -137,6 +156,9 @@ bool picopass_scene_read_card_success_on_event(void* context, SceneManagerEvent
picopass_device_set_name(picopass->dev, "");
scene_manager_next_scene(picopass->scene_manager, PicopassSceneCardMenu);
consumed = true;
+ } else if(event.event == GuiButtonTypeCenter) {
+ consumed = scene_manager_search_and_switch_to_another_scene(
+ picopass->scene_manager, PicopassSceneStart);
}
}
return consumed;
diff --git a/applications/plugins/picopass/scenes/picopass_scene_read_factory_success.c b/applications/external/picopass/scenes/picopass_scene_read_factory_success.c
similarity index 100%
rename from applications/plugins/picopass/scenes/picopass_scene_read_factory_success.c
rename to applications/external/picopass/scenes/picopass_scene_read_factory_success.c
diff --git a/applications/plugins/picopass/scenes/picopass_scene_save_name.c b/applications/external/picopass/scenes/picopass_scene_save_name.c
similarity index 100%
rename from applications/plugins/picopass/scenes/picopass_scene_save_name.c
rename to applications/external/picopass/scenes/picopass_scene_save_name.c
diff --git a/applications/plugins/picopass/scenes/picopass_scene_save_success.c b/applications/external/picopass/scenes/picopass_scene_save_success.c
similarity index 100%
rename from applications/plugins/picopass/scenes/picopass_scene_save_success.c
rename to applications/external/picopass/scenes/picopass_scene_save_success.c
diff --git a/applications/plugins/picopass/scenes/picopass_scene_saved_menu.c b/applications/external/picopass/scenes/picopass_scene_saved_menu.c
similarity index 100%
rename from applications/plugins/picopass/scenes/picopass_scene_saved_menu.c
rename to applications/external/picopass/scenes/picopass_scene_saved_menu.c
diff --git a/applications/plugins/picopass/scenes/picopass_scene_start.c b/applications/external/picopass/scenes/picopass_scene_start.c
similarity index 78%
rename from applications/plugins/picopass/scenes/picopass_scene_start.c
rename to applications/external/picopass/scenes/picopass_scene_start.c
index d33a1d264..8f7b627aa 100644
--- a/applications/plugins/picopass/scenes/picopass_scene_start.c
+++ b/applications/external/picopass/scenes/picopass_scene_start.c
@@ -1,10 +1,8 @@
#include "../picopass_i.h"
enum SubmenuIndex {
SubmenuIndexRead,
- SubmenuIndexRunScript,
+ SubmenuIndexEliteDictAttack,
SubmenuIndexSaved,
- SubmenuIndexAddManually,
- SubmenuIndexDebug,
};
void picopass_scene_start_submenu_callback(void* context, uint32_t index) {
@@ -17,6 +15,12 @@ void picopass_scene_start_on_enter(void* context) {
Submenu* submenu = picopass->submenu;
submenu_add_item(
submenu, "Read Card", SubmenuIndexRead, picopass_scene_start_submenu_callback, picopass);
+ submenu_add_item(
+ submenu,
+ "Elite Dict. Attack",
+ SubmenuIndexEliteDictAttack,
+ picopass_scene_start_submenu_callback,
+ picopass);
submenu_add_item(
submenu, "Saved", SubmenuIndexSaved, picopass_scene_start_submenu_callback, picopass);
@@ -43,6 +47,11 @@ bool picopass_scene_start_on_event(void* context, SceneManagerEvent event) {
picopass->scene_manager, PicopassSceneStart, SubmenuIndexSaved);
scene_manager_next_scene(picopass->scene_manager, PicopassSceneFileSelect);
consumed = true;
+ } else if(event.event == SubmenuIndexEliteDictAttack) {
+ scene_manager_set_scene_state(
+ picopass->scene_manager, PicopassSceneStart, SubmenuIndexEliteDictAttack);
+ scene_manager_next_scene(picopass->scene_manager, PicopassSceneEliteDictAttack);
+ consumed = true;
}
}
diff --git a/applications/plugins/picopass/scenes/picopass_scene_write_card.c b/applications/external/picopass/scenes/picopass_scene_write_card.c
similarity index 100%
rename from applications/plugins/picopass/scenes/picopass_scene_write_card.c
rename to applications/external/picopass/scenes/picopass_scene_write_card.c
diff --git a/applications/plugins/picopass/scenes/picopass_scene_write_card_success.c b/applications/external/picopass/scenes/picopass_scene_write_card_success.c
similarity index 100%
rename from applications/plugins/picopass/scenes/picopass_scene_write_card_success.c
rename to applications/external/picopass/scenes/picopass_scene_write_card_success.c
diff --git a/applications/plugins/picopass/scenes/picopass_scene_write_key.c b/applications/external/picopass/scenes/picopass_scene_write_key.c
similarity index 100%
rename from applications/plugins/picopass/scenes/picopass_scene_write_key.c
rename to applications/external/picopass/scenes/picopass_scene_write_key.c
diff --git a/applications/external/picopass/views/dict_attack.c b/applications/external/picopass/views/dict_attack.c
new file mode 100644
index 000000000..fb7335f6c
--- /dev/null
+++ b/applications/external/picopass/views/dict_attack.c
@@ -0,0 +1,281 @@
+#include "dict_attack.h"
+
+#include
+
+typedef enum {
+ DictAttackStateRead,
+ DictAttackStateCardRemoved,
+} DictAttackState;
+
+struct DictAttack {
+ View* view;
+ DictAttackCallback callback;
+ void* context;
+};
+
+typedef struct {
+ DictAttackState state;
+ MfClassicType type;
+ FuriString* header;
+ uint8_t sectors_total;
+ uint8_t sectors_read;
+ uint8_t sector_current;
+ uint8_t keys_total;
+ uint8_t keys_found;
+ uint16_t dict_keys_total;
+ uint16_t dict_keys_current;
+ bool is_key_attack;
+ uint8_t key_attack_current_sector;
+} DictAttackViewModel;
+
+static void dict_attack_draw_callback(Canvas* canvas, void* model) {
+ DictAttackViewModel* m = model;
+ if(m->state == DictAttackStateCardRemoved) {
+ canvas_set_font(canvas, FontPrimary);
+ canvas_draw_str_aligned(canvas, 64, 4, AlignCenter, AlignTop, "Lost the tag!");
+ canvas_set_font(canvas, FontSecondary);
+ elements_multiline_text_aligned(
+ canvas, 64, 23, AlignCenter, AlignTop, "Make sure the tag is\npositioned correctly.");
+ } else if(m->state == DictAttackStateRead) {
+ char draw_str[32] = {};
+ canvas_set_font(canvas, FontSecondary);
+ canvas_draw_str_aligned(
+ canvas, 64, 0, AlignCenter, AlignTop, furi_string_get_cstr(m->header));
+ if(m->is_key_attack) {
+ snprintf(
+ draw_str,
+ sizeof(draw_str),
+ "Reuse key check for sector: %d",
+ m->key_attack_current_sector);
+ } else {
+ snprintf(draw_str, sizeof(draw_str), "Unlocking sector: %d", m->sector_current);
+ }
+ canvas_draw_str_aligned(canvas, 0, 10, AlignLeft, AlignTop, draw_str);
+ float dict_progress = m->dict_keys_total == 0 ?
+ 0 :
+ (float)(m->dict_keys_current) / (float)(m->dict_keys_total);
+ float progress = m->sectors_total == 0 ? 0 :
+ ((float)(m->sector_current) + dict_progress) /
+ (float)(m->sectors_total);
+ if(progress > 1.0) {
+ progress = 1.0;
+ }
+ if(m->dict_keys_current == 0) {
+ // Cause when people see 0 they think it's broken
+ snprintf(draw_str, sizeof(draw_str), "%d/%d", 1, m->dict_keys_total);
+ } else {
+ snprintf(
+ draw_str, sizeof(draw_str), "%d/%d", m->dict_keys_current, m->dict_keys_total);
+ }
+ elements_progress_bar_with_text(canvas, 0, 20, 128, dict_progress, draw_str);
+ canvas_set_font(canvas, FontSecondary);
+ snprintf(draw_str, sizeof(draw_str), "Keys found: %d/%d", m->keys_found, m->keys_total);
+ canvas_draw_str_aligned(canvas, 0, 33, AlignLeft, AlignTop, draw_str);
+ snprintf(
+ draw_str, sizeof(draw_str), "Sectors Read: %d/%d", m->sectors_read, m->sectors_total);
+ canvas_draw_str_aligned(canvas, 0, 43, AlignLeft, AlignTop, draw_str);
+ }
+ elements_button_center(canvas, "Skip");
+}
+
+static bool dict_attack_input_callback(InputEvent* event, void* context) {
+ DictAttack* dict_attack = context;
+ bool consumed = false;
+ if(event->type == InputTypeShort && event->key == InputKeyOk) {
+ if(dict_attack->callback) {
+ dict_attack->callback(dict_attack->context);
+ }
+ consumed = true;
+ }
+ return consumed;
+}
+
+DictAttack* dict_attack_alloc() {
+ DictAttack* dict_attack = malloc(sizeof(DictAttack));
+ dict_attack->view = view_alloc();
+ view_allocate_model(dict_attack->view, ViewModelTypeLocking, sizeof(DictAttackViewModel));
+ view_set_draw_callback(dict_attack->view, dict_attack_draw_callback);
+ view_set_input_callback(dict_attack->view, dict_attack_input_callback);
+ view_set_context(dict_attack->view, dict_attack);
+ with_view_model(
+ dict_attack->view,
+ DictAttackViewModel * model,
+ { model->header = furi_string_alloc(); },
+ false);
+ return dict_attack;
+}
+
+void dict_attack_free(DictAttack* dict_attack) {
+ furi_assert(dict_attack);
+ with_view_model(
+ dict_attack->view,
+ DictAttackViewModel * model,
+ { furi_string_free(model->header); },
+ false);
+ view_free(dict_attack->view);
+ free(dict_attack);
+}
+
+void dict_attack_reset(DictAttack* dict_attack) {
+ furi_assert(dict_attack);
+ with_view_model(
+ dict_attack->view,
+ DictAttackViewModel * model,
+ {
+ model->state = DictAttackStateRead;
+ model->type = MfClassicType1k;
+ model->sectors_total = 1;
+ model->sectors_read = 0;
+ model->sector_current = 0;
+ model->keys_total = 0;
+ model->keys_found = 0;
+ model->dict_keys_total = 0;
+ model->dict_keys_current = 0;
+ model->is_key_attack = false;
+ furi_string_reset(model->header);
+ },
+ false);
+}
+
+View* dict_attack_get_view(DictAttack* dict_attack) {
+ furi_assert(dict_attack);
+ return dict_attack->view;
+}
+
+void dict_attack_set_callback(DictAttack* dict_attack, DictAttackCallback callback, void* context) {
+ furi_assert(dict_attack);
+ furi_assert(callback);
+ dict_attack->callback = callback;
+ dict_attack->context = context;
+}
+
+void dict_attack_set_header(DictAttack* dict_attack, const char* header) {
+ furi_assert(dict_attack);
+ furi_assert(header);
+
+ with_view_model(
+ dict_attack->view,
+ DictAttackViewModel * model,
+ { furi_string_set(model->header, header); },
+ true);
+}
+
+void dict_attack_set_card_detected(DictAttack* dict_attack) {
+ furi_assert(dict_attack);
+ with_view_model(
+ dict_attack->view,
+ DictAttackViewModel * model,
+ {
+ model->state = DictAttackStateRead;
+ model->sectors_total = 1;
+ model->keys_total = model->sectors_total;
+ },
+ true);
+}
+
+void dict_attack_set_card_removed(DictAttack* dict_attack) {
+ furi_assert(dict_attack);
+ with_view_model(
+ dict_attack->view,
+ DictAttackViewModel * model,
+ { model->state = DictAttackStateCardRemoved; },
+ true);
+}
+
+void dict_attack_set_sector_read(DictAttack* dict_attack, uint8_t sec_read) {
+ furi_assert(dict_attack);
+ with_view_model(
+ dict_attack->view, DictAttackViewModel * model, { model->sectors_read = sec_read; }, true);
+}
+
+void dict_attack_set_keys_found(DictAttack* dict_attack, uint8_t keys_found) {
+ furi_assert(dict_attack);
+ with_view_model(
+ dict_attack->view, DictAttackViewModel * model, { model->keys_found = keys_found; }, true);
+}
+
+void dict_attack_set_current_sector(DictAttack* dict_attack, uint8_t curr_sec) {
+ furi_assert(dict_attack);
+ with_view_model(
+ dict_attack->view,
+ DictAttackViewModel * model,
+ {
+ model->sector_current = curr_sec;
+ model->dict_keys_current = 0;
+ },
+ true);
+}
+
+void dict_attack_inc_current_sector(DictAttack* dict_attack) {
+ furi_assert(dict_attack);
+ with_view_model(
+ dict_attack->view,
+ DictAttackViewModel * model,
+ {
+ if(model->sector_current < model->sectors_total) {
+ model->sector_current++;
+ model->dict_keys_current = 0;
+ }
+ },
+ true);
+}
+
+void dict_attack_inc_keys_found(DictAttack* dict_attack) {
+ furi_assert(dict_attack);
+ with_view_model(
+ dict_attack->view,
+ DictAttackViewModel * model,
+ {
+ if(model->keys_found < model->keys_total) {
+ model->keys_found++;
+ }
+ },
+ true);
+}
+
+void dict_attack_set_total_dict_keys(DictAttack* dict_attack, uint16_t dict_keys_total) {
+ furi_assert(dict_attack);
+ with_view_model(
+ dict_attack->view,
+ DictAttackViewModel * model,
+ { model->dict_keys_total = dict_keys_total; },
+ true);
+}
+
+void dict_attack_inc_current_dict_key(DictAttack* dict_attack, uint16_t keys_tried) {
+ furi_assert(dict_attack);
+ with_view_model(
+ dict_attack->view,
+ DictAttackViewModel * model,
+ {
+ if(model->dict_keys_current + keys_tried < model->dict_keys_total) {
+ model->dict_keys_current += keys_tried;
+ }
+ },
+ true);
+}
+
+void dict_attack_set_key_attack(DictAttack* dict_attack, bool is_key_attack, uint8_t sector) {
+ furi_assert(dict_attack);
+ with_view_model(
+ dict_attack->view,
+ DictAttackViewModel * model,
+ {
+ model->is_key_attack = is_key_attack;
+ model->key_attack_current_sector = sector;
+ },
+ true);
+}
+
+void dict_attack_inc_key_attack_current_sector(DictAttack* dict_attack) {
+ furi_assert(dict_attack);
+ with_view_model(
+ dict_attack->view,
+ DictAttackViewModel * model,
+ {
+ if(model->key_attack_current_sector < model->sectors_total) {
+ model->key_attack_current_sector++;
+ }
+ },
+ true);
+}
diff --git a/applications/external/picopass/views/dict_attack.h b/applications/external/picopass/views/dict_attack.h
new file mode 100644
index 000000000..bdfa3e952
--- /dev/null
+++ b/applications/external/picopass/views/dict_attack.h
@@ -0,0 +1,44 @@
+#pragma once
+#include
+#include
+#include
+
+#include
+
+typedef struct DictAttack DictAttack;
+
+typedef void (*DictAttackCallback)(void* context);
+
+DictAttack* dict_attack_alloc();
+
+void dict_attack_free(DictAttack* dict_attack);
+
+void dict_attack_reset(DictAttack* dict_attack);
+
+View* dict_attack_get_view(DictAttack* dict_attack);
+
+void dict_attack_set_callback(DictAttack* dict_attack, DictAttackCallback callback, void* context);
+
+void dict_attack_set_header(DictAttack* dict_attack, const char* header);
+
+void dict_attack_set_card_detected(DictAttack* dict_attack);
+
+void dict_attack_set_card_removed(DictAttack* dict_attack);
+
+void dict_attack_set_sector_read(DictAttack* dict_attack, uint8_t sec_read);
+
+void dict_attack_set_keys_found(DictAttack* dict_attack, uint8_t keys_found);
+
+void dict_attack_set_current_sector(DictAttack* dict_attack, uint8_t curr_sec);
+
+void dict_attack_inc_current_sector(DictAttack* dict_attack);
+
+void dict_attack_inc_keys_found(DictAttack* dict_attack);
+
+void dict_attack_set_total_dict_keys(DictAttack* dict_attack, uint16_t dict_keys_total);
+
+void dict_attack_inc_current_dict_key(DictAttack* dict_attack, uint16_t keys_tried);
+
+void dict_attack_set_key_attack(DictAttack* dict_attack, bool is_key_attack, uint8_t sector);
+
+void dict_attack_inc_key_attack_current_sector(DictAttack* dict_attack);
diff --git a/applications/plugins/pocsag_pager/application.fam b/applications/external/pocsag_pager/application.fam
similarity index 78%
rename from applications/plugins/pocsag_pager/application.fam
rename to applications/external/pocsag_pager/application.fam
index 86f8d528b..3cef05374 100644
--- a/applications/plugins/pocsag_pager/application.fam
+++ b/applications/external/pocsag_pager/application.fam
@@ -1,9 +1,8 @@
App(
appid="pocsag_pager",
name="POCSAG Pager",
- apptype=FlipperAppType.PLUGIN,
+ apptype=FlipperAppType.EXTERNAL,
entry_point="pocsag_pager_app",
- cdefines=["APP_POCSAG_PAGER"],
requires=["gui"],
stack_size=4 * 1024,
order=50,
diff --git a/applications/plugins/pocsag_pager/helpers/pocsag_pager_event.h b/applications/external/pocsag_pager/helpers/pocsag_pager_event.h
similarity index 100%
rename from applications/plugins/pocsag_pager/helpers/pocsag_pager_event.h
rename to applications/external/pocsag_pager/helpers/pocsag_pager_event.h
diff --git a/applications/plugins/pocsag_pager/helpers/pocsag_pager_types.h b/applications/external/pocsag_pager/helpers/pocsag_pager_types.h
similarity index 100%
rename from applications/plugins/pocsag_pager/helpers/pocsag_pager_types.h
rename to applications/external/pocsag_pager/helpers/pocsag_pager_types.h
diff --git a/applications/plugins/pocsag_pager/images/Lock_7x8.png b/applications/external/pocsag_pager/images/Lock_7x8.png
similarity index 100%
rename from applications/plugins/pocsag_pager/images/Lock_7x8.png
rename to applications/external/pocsag_pager/images/Lock_7x8.png
diff --git a/applications/plugins/pocsag_pager/images/Message_8x7.png b/applications/external/pocsag_pager/images/Message_8x7.png
similarity index 100%
rename from applications/plugins/pocsag_pager/images/Message_8x7.png
rename to applications/external/pocsag_pager/images/Message_8x7.png
diff --git a/applications/plugins/pocsag_pager/images/Pin_back_arrow_10x8.png b/applications/external/pocsag_pager/images/Pin_back_arrow_10x8.png
similarity index 100%
rename from applications/plugins/pocsag_pager/images/Pin_back_arrow_10x8.png
rename to applications/external/pocsag_pager/images/Pin_back_arrow_10x8.png
diff --git a/applications/plugins/pocsag_pager/images/Quest_7x8.png b/applications/external/pocsag_pager/images/Quest_7x8.png
similarity index 100%
rename from applications/plugins/pocsag_pager/images/Quest_7x8.png
rename to applications/external/pocsag_pager/images/Quest_7x8.png
diff --git a/applications/plugins/pocsag_pager/images/Scanning_123x52.png b/applications/external/pocsag_pager/images/Scanning_123x52.png
similarity index 100%
rename from applications/plugins/pocsag_pager/images/Scanning_123x52.png
rename to applications/external/pocsag_pager/images/Scanning_123x52.png
diff --git a/applications/plugins/pocsag_pager/images/Unlock_7x8.png b/applications/external/pocsag_pager/images/Unlock_7x8.png
similarity index 100%
rename from applications/plugins/pocsag_pager/images/Unlock_7x8.png
rename to applications/external/pocsag_pager/images/Unlock_7x8.png
diff --git a/applications/plugins/pocsag_pager/images/WarningDolphin_45x42.png b/applications/external/pocsag_pager/images/WarningDolphin_45x42.png
similarity index 100%
rename from applications/plugins/pocsag_pager/images/WarningDolphin_45x42.png
rename to applications/external/pocsag_pager/images/WarningDolphin_45x42.png
diff --git a/applications/plugins/pocsag_pager/pocsag_pager_10px.png b/applications/external/pocsag_pager/pocsag_pager_10px.png
similarity index 100%
rename from applications/plugins/pocsag_pager/pocsag_pager_10px.png
rename to applications/external/pocsag_pager/pocsag_pager_10px.png
diff --git a/applications/plugins/pocsag_pager/pocsag_pager_app.c b/applications/external/pocsag_pager/pocsag_pager_app.c
similarity index 96%
rename from applications/plugins/pocsag_pager/pocsag_pager_app.c
rename to applications/external/pocsag_pager/pocsag_pager_app.c
index 123b3ee9d..d4b12c466 100644
--- a/applications/plugins/pocsag_pager/pocsag_pager_app.c
+++ b/applications/external/pocsag_pager/pocsag_pager_app.c
@@ -4,6 +4,7 @@
#include
#include
#include "protocols/protocol_items.h"
+#include
static bool pocsag_pager_app_custom_event_callback(void* context, uint32_t event) {
furi_assert(context);
@@ -124,6 +125,11 @@ POCSAGPagerApp* pocsag_pager_app_alloc() {
// Enable power for External CC1101 if it is connected
furi_hal_subghz_enable_ext_power();
+ // Auto switch to internal radio if external radio is not available
+ furi_delay_ms(15);
+ if(!furi_hal_subghz_check_radio()) {
+ furi_hal_subghz_set_radio_type(SubGhzRadioInternal);
+ }
furi_hal_power_suppress_charge_enter();
@@ -193,6 +199,7 @@ int32_t pocsag_pager_app(void* p) {
UNUSED(p);
POCSAGPagerApp* pocsag_pager_app = pocsag_pager_app_alloc();
+ DOLPHIN_DEED(DolphinDeedPluginStart);
view_dispatcher_run(pocsag_pager_app->view_dispatcher);
pocsag_pager_app_free(pocsag_pager_app);
diff --git a/applications/plugins/pocsag_pager/pocsag_pager_app_i.c b/applications/external/pocsag_pager/pocsag_pager_app_i.c
similarity index 100%
rename from applications/plugins/pocsag_pager/pocsag_pager_app_i.c
rename to applications/external/pocsag_pager/pocsag_pager_app_i.c
diff --git a/applications/plugins/pocsag_pager/pocsag_pager_app_i.h b/applications/external/pocsag_pager/pocsag_pager_app_i.h
similarity index 100%
rename from applications/plugins/pocsag_pager/pocsag_pager_app_i.h
rename to applications/external/pocsag_pager/pocsag_pager_app_i.h
diff --git a/applications/plugins/pocsag_pager/pocsag_pager_history.c b/applications/external/pocsag_pager/pocsag_pager_history.c
similarity index 100%
rename from applications/plugins/pocsag_pager/pocsag_pager_history.c
rename to applications/external/pocsag_pager/pocsag_pager_history.c
diff --git a/applications/plugins/pocsag_pager/pocsag_pager_history.h b/applications/external/pocsag_pager/pocsag_pager_history.h
similarity index 100%
rename from applications/plugins/pocsag_pager/pocsag_pager_history.h
rename to applications/external/pocsag_pager/pocsag_pager_history.h
diff --git a/applications/plugins/pocsag_pager/protocols/pcsg_generic.c b/applications/external/pocsag_pager/protocols/pcsg_generic.c
similarity index 100%
rename from applications/plugins/pocsag_pager/protocols/pcsg_generic.c
rename to applications/external/pocsag_pager/protocols/pcsg_generic.c
diff --git a/applications/plugins/pocsag_pager/protocols/pcsg_generic.h b/applications/external/pocsag_pager/protocols/pcsg_generic.h
similarity index 100%
rename from applications/plugins/pocsag_pager/protocols/pcsg_generic.h
rename to applications/external/pocsag_pager/protocols/pcsg_generic.h
diff --git a/applications/plugins/pocsag_pager/protocols/pocsag.c b/applications/external/pocsag_pager/protocols/pocsag.c
similarity index 99%
rename from applications/plugins/pocsag_pager/protocols/pocsag.c
rename to applications/external/pocsag_pager/protocols/pocsag.c
index ca210c2a4..0296a70a7 100644
--- a/applications/plugins/pocsag_pager/protocols/pocsag.c
+++ b/applications/external/pocsag_pager/protocols/pocsag.c
@@ -157,8 +157,7 @@ static bool pocsag_decode_message_word(SubGhzProtocolDecoderPocsag* instance, ui
// Function called when current message got decoded, but other messages might follow
static void pocsag_message_done(SubGhzProtocolDecoderPocsag* instance) {
// append the message to the long-term storage string
- furi_string_cat_printf(
- instance->generic.result_ric, "\e#RIC: %" PRIu32 "\e# | ", instance->ric);
+ furi_string_printf(instance->generic.result_ric, "\e#RIC: %" PRIu32 "\e# | ", instance->ric);
furi_string_cat_str(instance->generic.result_ric, func_msg[instance->func]);
if(instance->func != POCSAG_FUNC_ALERT1) {
furi_string_cat(instance->done_msg, instance->msg);
diff --git a/applications/plugins/pocsag_pager/protocols/pocsag.h b/applications/external/pocsag_pager/protocols/pocsag.h
similarity index 100%
rename from applications/plugins/pocsag_pager/protocols/pocsag.h
rename to applications/external/pocsag_pager/protocols/pocsag.h
diff --git a/applications/plugins/pocsag_pager/protocols/protocol_items.c b/applications/external/pocsag_pager/protocols/protocol_items.c
similarity index 100%
rename from applications/plugins/pocsag_pager/protocols/protocol_items.c
rename to applications/external/pocsag_pager/protocols/protocol_items.c
diff --git a/applications/plugins/pocsag_pager/protocols/protocol_items.h b/applications/external/pocsag_pager/protocols/protocol_items.h
similarity index 100%
rename from applications/plugins/pocsag_pager/protocols/protocol_items.h
rename to applications/external/pocsag_pager/protocols/protocol_items.h
diff --git a/applications/plugins/pocsag_pager/scenes/pocsag_pager_receiver.c b/applications/external/pocsag_pager/scenes/pocsag_pager_receiver.c
similarity index 100%
rename from applications/plugins/pocsag_pager/scenes/pocsag_pager_receiver.c
rename to applications/external/pocsag_pager/scenes/pocsag_pager_receiver.c
diff --git a/applications/plugins/pocsag_pager/scenes/pocsag_pager_scene.c b/applications/external/pocsag_pager/scenes/pocsag_pager_scene.c
similarity index 100%
rename from applications/plugins/pocsag_pager/scenes/pocsag_pager_scene.c
rename to applications/external/pocsag_pager/scenes/pocsag_pager_scene.c
diff --git a/applications/plugins/pocsag_pager/scenes/pocsag_pager_scene.h b/applications/external/pocsag_pager/scenes/pocsag_pager_scene.h
similarity index 100%
rename from applications/plugins/pocsag_pager/scenes/pocsag_pager_scene.h
rename to applications/external/pocsag_pager/scenes/pocsag_pager_scene.h
diff --git a/applications/plugins/pocsag_pager/scenes/pocsag_pager_scene_about.c b/applications/external/pocsag_pager/scenes/pocsag_pager_scene_about.c
similarity index 100%
rename from applications/plugins/pocsag_pager/scenes/pocsag_pager_scene_about.c
rename to applications/external/pocsag_pager/scenes/pocsag_pager_scene_about.c
diff --git a/applications/plugins/pocsag_pager/scenes/pocsag_pager_scene_config.h b/applications/external/pocsag_pager/scenes/pocsag_pager_scene_config.h
similarity index 100%
rename from applications/plugins/pocsag_pager/scenes/pocsag_pager_scene_config.h
rename to applications/external/pocsag_pager/scenes/pocsag_pager_scene_config.h
diff --git a/applications/plugins/pocsag_pager/scenes/pocsag_pager_scene_receiver_config.c b/applications/external/pocsag_pager/scenes/pocsag_pager_scene_receiver_config.c
similarity index 100%
rename from applications/plugins/pocsag_pager/scenes/pocsag_pager_scene_receiver_config.c
rename to applications/external/pocsag_pager/scenes/pocsag_pager_scene_receiver_config.c
diff --git a/applications/plugins/pocsag_pager/scenes/pocsag_pager_scene_receiver_info.c b/applications/external/pocsag_pager/scenes/pocsag_pager_scene_receiver_info.c
similarity index 100%
rename from applications/plugins/pocsag_pager/scenes/pocsag_pager_scene_receiver_info.c
rename to applications/external/pocsag_pager/scenes/pocsag_pager_scene_receiver_info.c
diff --git a/applications/plugins/pocsag_pager/scenes/pocsag_pager_scene_start.c b/applications/external/pocsag_pager/scenes/pocsag_pager_scene_start.c
similarity index 100%
rename from applications/plugins/pocsag_pager/scenes/pocsag_pager_scene_start.c
rename to applications/external/pocsag_pager/scenes/pocsag_pager_scene_start.c
diff --git a/applications/plugins/pocsag_pager/views/pocsag_pager_receiver.c b/applications/external/pocsag_pager/views/pocsag_pager_receiver.c
similarity index 100%
rename from applications/plugins/pocsag_pager/views/pocsag_pager_receiver.c
rename to applications/external/pocsag_pager/views/pocsag_pager_receiver.c
diff --git a/applications/plugins/pocsag_pager/views/pocsag_pager_receiver.h b/applications/external/pocsag_pager/views/pocsag_pager_receiver.h
similarity index 100%
rename from applications/plugins/pocsag_pager/views/pocsag_pager_receiver.h
rename to applications/external/pocsag_pager/views/pocsag_pager_receiver.h
diff --git a/applications/plugins/pocsag_pager/views/pocsag_pager_receiver_info.c b/applications/external/pocsag_pager/views/pocsag_pager_receiver_info.c
similarity index 100%
rename from applications/plugins/pocsag_pager/views/pocsag_pager_receiver_info.c
rename to applications/external/pocsag_pager/views/pocsag_pager_receiver_info.c
diff --git a/applications/plugins/pocsag_pager/views/pocsag_pager_receiver_info.h b/applications/external/pocsag_pager/views/pocsag_pager_receiver_info.h
similarity index 100%
rename from applications/plugins/pocsag_pager/views/pocsag_pager_receiver_info.h
rename to applications/external/pocsag_pager/views/pocsag_pager_receiver_info.h
diff --git a/applications/plugins/pomodoro/application.fam b/applications/external/pomodoro/application.fam
similarity index 100%
rename from applications/plugins/pomodoro/application.fam
rename to applications/external/pomodoro/application.fam
diff --git a/applications/plugins/pomodoro/flipp_pomodoro_10.png b/applications/external/pomodoro/flipp_pomodoro_10.png
similarity index 100%
rename from applications/plugins/pomodoro/flipp_pomodoro_10.png
rename to applications/external/pomodoro/flipp_pomodoro_10.png
diff --git a/applications/plugins/pomodoro/flipp_pomodoro_app.c b/applications/external/pomodoro/flipp_pomodoro_app.c
similarity index 100%
rename from applications/plugins/pomodoro/flipp_pomodoro_app.c
rename to applications/external/pomodoro/flipp_pomodoro_app.c
diff --git a/applications/plugins/pomodoro/flipp_pomodoro_app.h b/applications/external/pomodoro/flipp_pomodoro_app.h
similarity index 100%
rename from applications/plugins/pomodoro/flipp_pomodoro_app.h
rename to applications/external/pomodoro/flipp_pomodoro_app.h
diff --git a/applications/plugins/pomodoro/flipp_pomodoro_app_i.h b/applications/external/pomodoro/flipp_pomodoro_app_i.h
similarity index 100%
rename from applications/plugins/pomodoro/flipp_pomodoro_app_i.h
rename to applications/external/pomodoro/flipp_pomodoro_app_i.h
diff --git a/applications/plugins/pomodoro/helpers/debug.h b/applications/external/pomodoro/helpers/debug.h
similarity index 100%
rename from applications/plugins/pomodoro/helpers/debug.h
rename to applications/external/pomodoro/helpers/debug.h
diff --git a/applications/plugins/pomodoro/helpers/notifications.c b/applications/external/pomodoro/helpers/notifications.c
similarity index 100%
rename from applications/plugins/pomodoro/helpers/notifications.c
rename to applications/external/pomodoro/helpers/notifications.c
diff --git a/applications/plugins/pomodoro/helpers/notifications.h b/applications/external/pomodoro/helpers/notifications.h
similarity index 100%
rename from applications/plugins/pomodoro/helpers/notifications.h
rename to applications/external/pomodoro/helpers/notifications.h
diff --git a/applications/plugins/pomodoro/helpers/time.c b/applications/external/pomodoro/helpers/time.c
similarity index 100%
rename from applications/plugins/pomodoro/helpers/time.c
rename to applications/external/pomodoro/helpers/time.c
diff --git a/applications/plugins/pomodoro/helpers/time.h b/applications/external/pomodoro/helpers/time.h
similarity index 100%
rename from applications/plugins/pomodoro/helpers/time.h
rename to applications/external/pomodoro/helpers/time.h
diff --git a/applications/plugins/pomodoro/images/flipp_pomodoro_focus_64/frame_00.png b/applications/external/pomodoro/images/flipp_pomodoro_focus_64/frame_00.png
similarity index 100%
rename from applications/plugins/pomodoro/images/flipp_pomodoro_focus_64/frame_00.png
rename to applications/external/pomodoro/images/flipp_pomodoro_focus_64/frame_00.png
diff --git a/applications/plugins/pomodoro/images/flipp_pomodoro_focus_64/frame_01.png b/applications/external/pomodoro/images/flipp_pomodoro_focus_64/frame_01.png
similarity index 100%
rename from applications/plugins/pomodoro/images/flipp_pomodoro_focus_64/frame_01.png
rename to applications/external/pomodoro/images/flipp_pomodoro_focus_64/frame_01.png
diff --git a/applications/plugins/pomodoro/images/flipp_pomodoro_focus_64/frame_rate b/applications/external/pomodoro/images/flipp_pomodoro_focus_64/frame_rate
similarity index 100%
rename from applications/plugins/pomodoro/images/flipp_pomodoro_focus_64/frame_rate
rename to applications/external/pomodoro/images/flipp_pomodoro_focus_64/frame_rate
diff --git a/applications/plugins/pomodoro/images/flipp_pomodoro_rest_64/frame_00.png b/applications/external/pomodoro/images/flipp_pomodoro_rest_64/frame_00.png
similarity index 100%
rename from applications/plugins/pomodoro/images/flipp_pomodoro_rest_64/frame_00.png
rename to applications/external/pomodoro/images/flipp_pomodoro_rest_64/frame_00.png
diff --git a/applications/plugins/pomodoro/images/flipp_pomodoro_rest_64/frame_01.png b/applications/external/pomodoro/images/flipp_pomodoro_rest_64/frame_01.png
similarity index 100%
rename from applications/plugins/pomodoro/images/flipp_pomodoro_rest_64/frame_01.png
rename to applications/external/pomodoro/images/flipp_pomodoro_rest_64/frame_01.png
diff --git a/applications/plugins/pomodoro/images/flipp_pomodoro_rest_64/frame_rate b/applications/external/pomodoro/images/flipp_pomodoro_rest_64/frame_rate
similarity index 100%
rename from applications/plugins/pomodoro/images/flipp_pomodoro_rest_64/frame_rate
rename to applications/external/pomodoro/images/flipp_pomodoro_rest_64/frame_rate
diff --git a/applications/plugins/pomodoro/modules/flipp_pomodoro.c b/applications/external/pomodoro/modules/flipp_pomodoro.c
similarity index 100%
rename from applications/plugins/pomodoro/modules/flipp_pomodoro.c
rename to applications/external/pomodoro/modules/flipp_pomodoro.c
diff --git a/applications/plugins/pomodoro/modules/flipp_pomodoro.h b/applications/external/pomodoro/modules/flipp_pomodoro.h
similarity index 100%
rename from applications/plugins/pomodoro/modules/flipp_pomodoro.h
rename to applications/external/pomodoro/modules/flipp_pomodoro.h
diff --git a/applications/plugins/pomodoro/scenes/.keep b/applications/external/pomodoro/scenes/.keep
similarity index 100%
rename from applications/plugins/pomodoro/scenes/.keep
rename to applications/external/pomodoro/scenes/.keep
diff --git a/applications/plugins/pomodoro/scenes/config/flipp_pomodoro_scene_config.h b/applications/external/pomodoro/scenes/config/flipp_pomodoro_scene_config.h
similarity index 100%
rename from applications/plugins/pomodoro/scenes/config/flipp_pomodoro_scene_config.h
rename to applications/external/pomodoro/scenes/config/flipp_pomodoro_scene_config.h
diff --git a/applications/plugins/pomodoro/scenes/flipp_pomodoro_scene.c b/applications/external/pomodoro/scenes/flipp_pomodoro_scene.c
similarity index 100%
rename from applications/plugins/pomodoro/scenes/flipp_pomodoro_scene.c
rename to applications/external/pomodoro/scenes/flipp_pomodoro_scene.c
diff --git a/applications/plugins/pomodoro/scenes/flipp_pomodoro_scene.h b/applications/external/pomodoro/scenes/flipp_pomodoro_scene.h
similarity index 100%
rename from applications/plugins/pomodoro/scenes/flipp_pomodoro_scene.h
rename to applications/external/pomodoro/scenes/flipp_pomodoro_scene.h
diff --git a/applications/plugins/pomodoro/scenes/flipp_pomodoro_scene_timer.c b/applications/external/pomodoro/scenes/flipp_pomodoro_scene_timer.c
similarity index 100%
rename from applications/plugins/pomodoro/scenes/flipp_pomodoro_scene_timer.c
rename to applications/external/pomodoro/scenes/flipp_pomodoro_scene_timer.c
diff --git a/applications/plugins/pomodoro/views/.keep b/applications/external/pomodoro/views/.keep
similarity index 100%
rename from applications/plugins/pomodoro/views/.keep
rename to applications/external/pomodoro/views/.keep
diff --git a/applications/plugins/pomodoro/views/flipp_pomodoro_timer_view.c b/applications/external/pomodoro/views/flipp_pomodoro_timer_view.c
similarity index 100%
rename from applications/plugins/pomodoro/views/flipp_pomodoro_timer_view.c
rename to applications/external/pomodoro/views/flipp_pomodoro_timer_view.c
diff --git a/applications/plugins/pomodoro/views/flipp_pomodoro_timer_view.h b/applications/external/pomodoro/views/flipp_pomodoro_timer_view.h
similarity index 100%
rename from applications/plugins/pomodoro/views/flipp_pomodoro_timer_view.h
rename to applications/external/pomodoro/views/flipp_pomodoro_timer_view.h
diff --git a/applications/plugins/pong/application.fam b/applications/external/pong/application.fam
similarity index 100%
rename from applications/plugins/pong/application.fam
rename to applications/external/pong/application.fam
diff --git a/applications/plugins/pong/flipper_pong.c b/applications/external/pong/flipper_pong.c
similarity index 100%
rename from applications/plugins/pong/flipper_pong.c
rename to applications/external/pong/flipper_pong.c
diff --git a/applications/plugins/pong/pong.png b/applications/external/pong/pong.png
similarity index 100%
rename from applications/plugins/pong/pong.png
rename to applications/external/pong/pong.png
diff --git a/applications/plugins/protoview/LICENSE b/applications/external/protoview/LICENSE
similarity index 100%
rename from applications/plugins/protoview/LICENSE
rename to applications/external/protoview/LICENSE
diff --git a/applications/plugins/protoview/app.c b/applications/external/protoview/app.c
similarity index 98%
rename from applications/plugins/protoview/app.c
rename to applications/external/protoview/app.c
index a4dab9f40..2d3acccac 100644
--- a/applications/plugins/protoview/app.c
+++ b/applications/external/protoview/app.c
@@ -2,6 +2,7 @@
* See the LICENSE file for information about the license. */
#include "app.h"
+#include
RawSamplesBuffer *RawSamples, *DetectedSamples;
extern const SubGhzProtocolRegistry protoview_protocol_registry;
@@ -169,6 +170,11 @@ ProtoViewApp* protoview_app_alloc() {
// Enable power for External CC1101 if it is connected
furi_hal_subghz_enable_ext_power();
+ // Auto switch to internal radio if external radio is not available
+ furi_delay_ms(15);
+ if(!furi_hal_subghz_check_radio()) {
+ furi_hal_subghz_set_radio_type(SubGhzRadioInternal);
+ }
furi_hal_power_suppress_charge_enter();
app->running = 1;
@@ -253,6 +259,7 @@ static bool keyboard_view_dispatcher_navigation_callback(void* ctx) {
int32_t protoview_app_entry(void* p) {
UNUSED(p);
ProtoViewApp* app = protoview_app_alloc();
+ DOLPHIN_DEED(DolphinDeedPluginStart);
/* Create a timer. We do data analysis in the callback. */
FuriTimer* timer = furi_timer_alloc(timer_callback, FuriTimerTypePeriodic, app);
diff --git a/applications/plugins/protoview/app.h b/applications/external/protoview/app.h
similarity index 100%
rename from applications/plugins/protoview/app.h
rename to applications/external/protoview/app.h
diff --git a/applications/plugins/protoview/app_subghz.c b/applications/external/protoview/app_subghz.c
similarity index 100%
rename from applications/plugins/protoview/app_subghz.c
rename to applications/external/protoview/app_subghz.c
diff --git a/applications/plugins/protoview/appicon.png b/applications/external/protoview/appicon.png
similarity index 100%
rename from applications/plugins/protoview/appicon.png
rename to applications/external/protoview/appicon.png
diff --git a/applications/plugins/protoview/application.fam b/applications/external/protoview/application.fam
similarity index 88%
rename from applications/plugins/protoview/application.fam
rename to applications/external/protoview/application.fam
index 22ad2d628..329a95c8a 100644
--- a/applications/plugins/protoview/application.fam
+++ b/applications/external/protoview/application.fam
@@ -3,7 +3,6 @@ App(
name="ProtoView",
apptype=FlipperAppType.EXTERNAL,
entry_point="protoview_app_entry",
- cdefines=["APP_PROTOVIEW"],
requires=["gui"],
stack_size=8 * 1024,
order=50,
diff --git a/applications/plugins/protoview/crc.c b/applications/external/protoview/crc.c
similarity index 100%
rename from applications/plugins/protoview/crc.c
rename to applications/external/protoview/crc.c
diff --git a/applications/plugins/protoview/custom_presets.h b/applications/external/protoview/custom_presets.h
similarity index 100%
rename from applications/plugins/protoview/custom_presets.h
rename to applications/external/protoview/custom_presets.h
diff --git a/applications/plugins/protoview/fields.c b/applications/external/protoview/fields.c
similarity index 100%
rename from applications/plugins/protoview/fields.c
rename to applications/external/protoview/fields.c
diff --git a/applications/plugins/protoview/protocols/b4b1.c b/applications/external/protoview/protocols/b4b1.c
similarity index 100%
rename from applications/plugins/protoview/protocols/b4b1.c
rename to applications/external/protoview/protocols/b4b1.c
diff --git a/applications/plugins/protoview/protocols/keeloq.c b/applications/external/protoview/protocols/keeloq.c
similarity index 100%
rename from applications/plugins/protoview/protocols/keeloq.c
rename to applications/external/protoview/protocols/keeloq.c
diff --git a/applications/plugins/protoview/protocols/oregon2.c b/applications/external/protoview/protocols/oregon2.c
similarity index 100%
rename from applications/plugins/protoview/protocols/oregon2.c
rename to applications/external/protoview/protocols/oregon2.c
diff --git a/applications/plugins/protoview/protocols/pvchat.c b/applications/external/protoview/protocols/pvchat.c
similarity index 100%
rename from applications/plugins/protoview/protocols/pvchat.c
rename to applications/external/protoview/protocols/pvchat.c
diff --git a/applications/plugins/protoview/protocols/tpms/citroen.c b/applications/external/protoview/protocols/tpms/citroen.c
similarity index 100%
rename from applications/plugins/protoview/protocols/tpms/citroen.c
rename to applications/external/protoview/protocols/tpms/citroen.c
diff --git a/applications/plugins/protoview/protocols/tpms/ford.c b/applications/external/protoview/protocols/tpms/ford.c
similarity index 100%
rename from applications/plugins/protoview/protocols/tpms/ford.c
rename to applications/external/protoview/protocols/tpms/ford.c
diff --git a/applications/plugins/protoview/protocols/tpms/renault.c b/applications/external/protoview/protocols/tpms/renault.c
similarity index 100%
rename from applications/plugins/protoview/protocols/tpms/renault.c
rename to applications/external/protoview/protocols/tpms/renault.c
diff --git a/applications/plugins/protoview/protocols/tpms/schrader.c b/applications/external/protoview/protocols/tpms/schrader.c
similarity index 100%
rename from applications/plugins/protoview/protocols/tpms/schrader.c
rename to applications/external/protoview/protocols/tpms/schrader.c
diff --git a/applications/plugins/protoview/protocols/tpms/schrader_eg53ma4.c b/applications/external/protoview/protocols/tpms/schrader_eg53ma4.c
similarity index 100%
rename from applications/plugins/protoview/protocols/tpms/schrader_eg53ma4.c
rename to applications/external/protoview/protocols/tpms/schrader_eg53ma4.c
diff --git a/applications/plugins/protoview/protocols/tpms/toyota.c b/applications/external/protoview/protocols/tpms/toyota.c
similarity index 100%
rename from applications/plugins/protoview/protocols/tpms/toyota.c
rename to applications/external/protoview/protocols/tpms/toyota.c
diff --git a/applications/plugins/protoview/protocols/unknown.c b/applications/external/protoview/protocols/unknown.c
similarity index 100%
rename from applications/plugins/protoview/protocols/unknown.c
rename to applications/external/protoview/protocols/unknown.c
diff --git a/applications/plugins/protoview/raw_samples.c b/applications/external/protoview/raw_samples.c
similarity index 100%
rename from applications/plugins/protoview/raw_samples.c
rename to applications/external/protoview/raw_samples.c
diff --git a/applications/plugins/protoview/raw_samples.h b/applications/external/protoview/raw_samples.h
similarity index 100%
rename from applications/plugins/protoview/raw_samples.h
rename to applications/external/protoview/raw_samples.h
diff --git a/applications/plugins/protoview/signal.c b/applications/external/protoview/signal.c
similarity index 100%
rename from applications/plugins/protoview/signal.c
rename to applications/external/protoview/signal.c
diff --git a/applications/plugins/protoview/signal_file.c b/applications/external/protoview/signal_file.c
similarity index 100%
rename from applications/plugins/protoview/signal_file.c
rename to applications/external/protoview/signal_file.c
diff --git a/applications/plugins/protoview/ui.c b/applications/external/protoview/ui.c
similarity index 100%
rename from applications/plugins/protoview/ui.c
rename to applications/external/protoview/ui.c
diff --git a/applications/plugins/protoview/view_build.c b/applications/external/protoview/view_build.c
similarity index 100%
rename from applications/plugins/protoview/view_build.c
rename to applications/external/protoview/view_build.c
diff --git a/applications/plugins/protoview/view_direct_sampling.c b/applications/external/protoview/view_direct_sampling.c
similarity index 100%
rename from applications/plugins/protoview/view_direct_sampling.c
rename to applications/external/protoview/view_direct_sampling.c
diff --git a/applications/plugins/protoview/view_info.c b/applications/external/protoview/view_info.c
similarity index 100%
rename from applications/plugins/protoview/view_info.c
rename to applications/external/protoview/view_info.c
diff --git a/applications/plugins/protoview/view_raw_signal.c b/applications/external/protoview/view_raw_signal.c
similarity index 100%
rename from applications/plugins/protoview/view_raw_signal.c
rename to applications/external/protoview/view_raw_signal.c
diff --git a/applications/plugins/protoview/view_settings.c b/applications/external/protoview/view_settings.c
similarity index 100%
rename from applications/plugins/protoview/view_settings.c
rename to applications/external/protoview/view_settings.c
diff --git a/applications/external/qrcode/LICENSE b/applications/external/qrcode/LICENSE
new file mode 100644
index 000000000..85e7f6b40
--- /dev/null
+++ b/applications/external/qrcode/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2022 Bob Matcuk
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/applications/plugins/qrcode/application.fam b/applications/external/qrcode/application.fam
similarity index 100%
rename from applications/plugins/qrcode/application.fam
rename to applications/external/qrcode/application.fam
diff --git a/applications/plugins/qrcode/icons/qrcode_10px.png b/applications/external/qrcode/icons/qrcode_10px.png
similarity index 100%
rename from applications/plugins/qrcode/icons/qrcode_10px.png
rename to applications/external/qrcode/icons/qrcode_10px.png
diff --git a/applications/plugins/qrcode/qrcode.c b/applications/external/qrcode/qrcode.c
similarity index 100%
rename from applications/plugins/qrcode/qrcode.c
rename to applications/external/qrcode/qrcode.c
diff --git a/applications/plugins/qrcode/qrcode.h b/applications/external/qrcode/qrcode.h
similarity index 100%
rename from applications/plugins/qrcode/qrcode.h
rename to applications/external/qrcode/qrcode.h
diff --git a/applications/plugins/qrcode/qrcode_app.c b/applications/external/qrcode/qrcode_app.c
similarity index 100%
rename from applications/plugins/qrcode/qrcode_app.c
rename to applications/external/qrcode/qrcode_app.c
diff --git a/applications/plugins/tuning_fork/LICENSE b/applications/external/rc2014_coleco/LICENSE
similarity index 100%
rename from applications/plugins/tuning_fork/LICENSE
rename to applications/external/rc2014_coleco/LICENSE
diff --git a/applications/plugins/rc2014_coleco/application.fam b/applications/external/rc2014_coleco/application.fam
similarity index 100%
rename from applications/plugins/rc2014_coleco/application.fam
rename to applications/external/rc2014_coleco/application.fam
diff --git a/applications/plugins/rc2014_coleco/coleco.c b/applications/external/rc2014_coleco/coleco.c
similarity index 100%
rename from applications/plugins/rc2014_coleco/coleco.c
rename to applications/external/rc2014_coleco/coleco.c
diff --git a/applications/plugins/rc2014_coleco/coleco_10px.png b/applications/external/rc2014_coleco/coleco_10px.png
similarity index 100%
rename from applications/plugins/rc2014_coleco/coleco_10px.png
rename to applications/external/rc2014_coleco/coleco_10px.png
diff --git a/applications/plugins/rc2014_coleco/icons/Coleco0_17x17.png b/applications/external/rc2014_coleco/icons/Coleco0_17x17.png
similarity index 100%
rename from applications/plugins/rc2014_coleco/icons/Coleco0_17x17.png
rename to applications/external/rc2014_coleco/icons/Coleco0_17x17.png
diff --git a/applications/plugins/rc2014_coleco/icons/Coleco0_hvr_17x17.png b/applications/external/rc2014_coleco/icons/Coleco0_hvr_17x17.png
similarity index 100%
rename from applications/plugins/rc2014_coleco/icons/Coleco0_hvr_17x17.png
rename to applications/external/rc2014_coleco/icons/Coleco0_hvr_17x17.png
diff --git a/applications/plugins/rc2014_coleco/icons/Coleco1_17x17.png b/applications/external/rc2014_coleco/icons/Coleco1_17x17.png
similarity index 100%
rename from applications/plugins/rc2014_coleco/icons/Coleco1_17x17.png
rename to applications/external/rc2014_coleco/icons/Coleco1_17x17.png
diff --git a/applications/plugins/rc2014_coleco/icons/Coleco1_hvr_17x17.png b/applications/external/rc2014_coleco/icons/Coleco1_hvr_17x17.png
similarity index 100%
rename from applications/plugins/rc2014_coleco/icons/Coleco1_hvr_17x17.png
rename to applications/external/rc2014_coleco/icons/Coleco1_hvr_17x17.png
diff --git a/applications/plugins/rc2014_coleco/icons/Coleco2_17x17.png b/applications/external/rc2014_coleco/icons/Coleco2_17x17.png
similarity index 100%
rename from applications/plugins/rc2014_coleco/icons/Coleco2_17x17.png
rename to applications/external/rc2014_coleco/icons/Coleco2_17x17.png
diff --git a/applications/plugins/rc2014_coleco/icons/Coleco2_hvr_17x17.png b/applications/external/rc2014_coleco/icons/Coleco2_hvr_17x17.png
similarity index 100%
rename from applications/plugins/rc2014_coleco/icons/Coleco2_hvr_17x17.png
rename to applications/external/rc2014_coleco/icons/Coleco2_hvr_17x17.png
diff --git a/applications/plugins/rc2014_coleco/icons/Coleco3_17x17.png b/applications/external/rc2014_coleco/icons/Coleco3_17x17.png
similarity index 100%
rename from applications/plugins/rc2014_coleco/icons/Coleco3_17x17.png
rename to applications/external/rc2014_coleco/icons/Coleco3_17x17.png
diff --git a/applications/plugins/rc2014_coleco/icons/Coleco3_hvr_17x17.png b/applications/external/rc2014_coleco/icons/Coleco3_hvr_17x17.png
similarity index 100%
rename from applications/plugins/rc2014_coleco/icons/Coleco3_hvr_17x17.png
rename to applications/external/rc2014_coleco/icons/Coleco3_hvr_17x17.png
diff --git a/applications/plugins/rc2014_coleco/icons/Coleco4_17x17.png b/applications/external/rc2014_coleco/icons/Coleco4_17x17.png
similarity index 100%
rename from applications/plugins/rc2014_coleco/icons/Coleco4_17x17.png
rename to applications/external/rc2014_coleco/icons/Coleco4_17x17.png
diff --git a/applications/plugins/rc2014_coleco/icons/Coleco4_hvr_17x17.png b/applications/external/rc2014_coleco/icons/Coleco4_hvr_17x17.png
similarity index 100%
rename from applications/plugins/rc2014_coleco/icons/Coleco4_hvr_17x17.png
rename to applications/external/rc2014_coleco/icons/Coleco4_hvr_17x17.png
diff --git a/applications/plugins/rc2014_coleco/icons/Coleco5_17x17.png b/applications/external/rc2014_coleco/icons/Coleco5_17x17.png
similarity index 100%
rename from applications/plugins/rc2014_coleco/icons/Coleco5_17x17.png
rename to applications/external/rc2014_coleco/icons/Coleco5_17x17.png
diff --git a/applications/plugins/rc2014_coleco/icons/Coleco5_hvr_17x17.png b/applications/external/rc2014_coleco/icons/Coleco5_hvr_17x17.png
similarity index 100%
rename from applications/plugins/rc2014_coleco/icons/Coleco5_hvr_17x17.png
rename to applications/external/rc2014_coleco/icons/Coleco5_hvr_17x17.png
diff --git a/applications/plugins/rc2014_coleco/icons/Coleco6_17x17.png b/applications/external/rc2014_coleco/icons/Coleco6_17x17.png
similarity index 100%
rename from applications/plugins/rc2014_coleco/icons/Coleco6_17x17.png
rename to applications/external/rc2014_coleco/icons/Coleco6_17x17.png
diff --git a/applications/plugins/rc2014_coleco/icons/Coleco6_hvr_17x17.png b/applications/external/rc2014_coleco/icons/Coleco6_hvr_17x17.png
similarity index 100%
rename from applications/plugins/rc2014_coleco/icons/Coleco6_hvr_17x17.png
rename to applications/external/rc2014_coleco/icons/Coleco6_hvr_17x17.png
diff --git a/applications/plugins/rc2014_coleco/icons/Coleco7_17x17.png b/applications/external/rc2014_coleco/icons/Coleco7_17x17.png
similarity index 100%
rename from applications/plugins/rc2014_coleco/icons/Coleco7_17x17.png
rename to applications/external/rc2014_coleco/icons/Coleco7_17x17.png
diff --git a/applications/plugins/rc2014_coleco/icons/Coleco7_hvr_17x17.png b/applications/external/rc2014_coleco/icons/Coleco7_hvr_17x17.png
similarity index 100%
rename from applications/plugins/rc2014_coleco/icons/Coleco7_hvr_17x17.png
rename to applications/external/rc2014_coleco/icons/Coleco7_hvr_17x17.png
diff --git a/applications/plugins/rc2014_coleco/icons/Coleco8_17x17.png b/applications/external/rc2014_coleco/icons/Coleco8_17x17.png
similarity index 100%
rename from applications/plugins/rc2014_coleco/icons/Coleco8_17x17.png
rename to applications/external/rc2014_coleco/icons/Coleco8_17x17.png
diff --git a/applications/plugins/rc2014_coleco/icons/Coleco8_hvr_17x17.png b/applications/external/rc2014_coleco/icons/Coleco8_hvr_17x17.png
similarity index 100%
rename from applications/plugins/rc2014_coleco/icons/Coleco8_hvr_17x17.png
rename to applications/external/rc2014_coleco/icons/Coleco8_hvr_17x17.png
diff --git a/applications/plugins/rc2014_coleco/icons/Coleco9_17x17.png b/applications/external/rc2014_coleco/icons/Coleco9_17x17.png
similarity index 100%
rename from applications/plugins/rc2014_coleco/icons/Coleco9_17x17.png
rename to applications/external/rc2014_coleco/icons/Coleco9_17x17.png
diff --git a/applications/plugins/rc2014_coleco/icons/Coleco9_hvr_17x17.png b/applications/external/rc2014_coleco/icons/Coleco9_hvr_17x17.png
similarity index 100%
rename from applications/plugins/rc2014_coleco/icons/Coleco9_hvr_17x17.png
rename to applications/external/rc2014_coleco/icons/Coleco9_hvr_17x17.png
diff --git a/applications/plugins/rc2014_coleco/icons/ColecoAlt_18x9.png b/applications/external/rc2014_coleco/icons/ColecoAlt_18x9.png
similarity index 100%
rename from applications/plugins/rc2014_coleco/icons/ColecoAlt_18x9.png
rename to applications/external/rc2014_coleco/icons/ColecoAlt_18x9.png
diff --git a/applications/plugins/rc2014_coleco/icons/ColecoAlt_hvr_18x9.png b/applications/external/rc2014_coleco/icons/ColecoAlt_hvr_18x9.png
similarity index 100%
rename from applications/plugins/rc2014_coleco/icons/ColecoAlt_hvr_18x9.png
rename to applications/external/rc2014_coleco/icons/ColecoAlt_hvr_18x9.png
diff --git a/applications/plugins/rc2014_coleco/icons/ColecoFire_18x9.png b/applications/external/rc2014_coleco/icons/ColecoFire_18x9.png
similarity index 100%
rename from applications/plugins/rc2014_coleco/icons/ColecoFire_18x9.png
rename to applications/external/rc2014_coleco/icons/ColecoFire_18x9.png
diff --git a/applications/plugins/rc2014_coleco/icons/ColecoFire_hvr_18x9.png b/applications/external/rc2014_coleco/icons/ColecoFire_hvr_18x9.png
similarity index 100%
rename from applications/plugins/rc2014_coleco/icons/ColecoFire_hvr_18x9.png
rename to applications/external/rc2014_coleco/icons/ColecoFire_hvr_18x9.png
diff --git a/applications/plugins/rc2014_coleco/icons/ColecoFire_sel_18x9.png b/applications/external/rc2014_coleco/icons/ColecoFire_sel_18x9.png
similarity index 100%
rename from applications/plugins/rc2014_coleco/icons/ColecoFire_sel_18x9.png
rename to applications/external/rc2014_coleco/icons/ColecoFire_sel_18x9.png
diff --git a/applications/plugins/rc2014_coleco/icons/ColecoJoystick_33x33.png b/applications/external/rc2014_coleco/icons/ColecoJoystick_33x33.png
similarity index 100%
rename from applications/plugins/rc2014_coleco/icons/ColecoJoystick_33x33.png
rename to applications/external/rc2014_coleco/icons/ColecoJoystick_33x33.png
diff --git a/applications/plugins/rc2014_coleco/icons/ColecoJoystick_hvr_33x33.png b/applications/external/rc2014_coleco/icons/ColecoJoystick_hvr_33x33.png
similarity index 100%
rename from applications/plugins/rc2014_coleco/icons/ColecoJoystick_hvr_33x33.png
rename to applications/external/rc2014_coleco/icons/ColecoJoystick_hvr_33x33.png
diff --git a/applications/plugins/rc2014_coleco/icons/ColecoJoystick_sel_33x33.png b/applications/external/rc2014_coleco/icons/ColecoJoystick_sel_33x33.png
similarity index 100%
rename from applications/plugins/rc2014_coleco/icons/ColecoJoystick_sel_33x33.png
rename to applications/external/rc2014_coleco/icons/ColecoJoystick_sel_33x33.png
diff --git a/applications/plugins/rc2014_coleco/icons/ColecoPound_17x17.png b/applications/external/rc2014_coleco/icons/ColecoPound_17x17.png
similarity index 100%
rename from applications/plugins/rc2014_coleco/icons/ColecoPound_17x17.png
rename to applications/external/rc2014_coleco/icons/ColecoPound_17x17.png
diff --git a/applications/plugins/rc2014_coleco/icons/ColecoPound_hvr_17x17.png b/applications/external/rc2014_coleco/icons/ColecoPound_hvr_17x17.png
similarity index 100%
rename from applications/plugins/rc2014_coleco/icons/ColecoPound_hvr_17x17.png
rename to applications/external/rc2014_coleco/icons/ColecoPound_hvr_17x17.png
diff --git a/applications/plugins/rc2014_coleco/icons/ColecoStar_17x17.png b/applications/external/rc2014_coleco/icons/ColecoStar_17x17.png
similarity index 100%
rename from applications/plugins/rc2014_coleco/icons/ColecoStar_17x17.png
rename to applications/external/rc2014_coleco/icons/ColecoStar_17x17.png
diff --git a/applications/plugins/rc2014_coleco/icons/ColecoStar_hvr_17x17.png b/applications/external/rc2014_coleco/icons/ColecoStar_hvr_17x17.png
similarity index 100%
rename from applications/plugins/rc2014_coleco/icons/ColecoStar_hvr_17x17.png
rename to applications/external/rc2014_coleco/icons/ColecoStar_hvr_17x17.png
diff --git a/applications/plugins/rc2014_coleco/interface/flipper-coleco.brd b/applications/external/rc2014_coleco/interface/flipper-coleco.brd
similarity index 100%
rename from applications/plugins/rc2014_coleco/interface/flipper-coleco.brd
rename to applications/external/rc2014_coleco/interface/flipper-coleco.brd
diff --git a/applications/plugins/rc2014_coleco/interface/flipper-coleco.sch b/applications/external/rc2014_coleco/interface/flipper-coleco.sch
similarity index 100%
rename from applications/plugins/rc2014_coleco/interface/flipper-coleco.sch
rename to applications/external/rc2014_coleco/interface/flipper-coleco.sch
diff --git a/applications/plugins/rubiks_cube_scrambler/LICENSE b/applications/external/rubiks_cube_scrambler/LICENSE
similarity index 100%
rename from applications/plugins/rubiks_cube_scrambler/LICENSE
rename to applications/external/rubiks_cube_scrambler/LICENSE
diff --git a/applications/plugins/rubiks_cube_scrambler/application.fam b/applications/external/rubiks_cube_scrambler/application.fam
similarity index 100%
rename from applications/plugins/rubiks_cube_scrambler/application.fam
rename to applications/external/rubiks_cube_scrambler/application.fam
diff --git a/applications/plugins/rubiks_cube_scrambler/cube.png b/applications/external/rubiks_cube_scrambler/cube.png
similarity index 100%
rename from applications/plugins/rubiks_cube_scrambler/cube.png
rename to applications/external/rubiks_cube_scrambler/cube.png
diff --git a/applications/plugins/rubiks_cube_scrambler/rubiks_cube_scrambler.c b/applications/external/rubiks_cube_scrambler/rubiks_cube_scrambler.c
similarity index 100%
rename from applications/plugins/rubiks_cube_scrambler/rubiks_cube_scrambler.c
rename to applications/external/rubiks_cube_scrambler/rubiks_cube_scrambler.c
diff --git a/applications/plugins/rubiks_cube_scrambler/scrambler.c b/applications/external/rubiks_cube_scrambler/scrambler.c
similarity index 100%
rename from applications/plugins/rubiks_cube_scrambler/scrambler.c
rename to applications/external/rubiks_cube_scrambler/scrambler.c
diff --git a/applications/plugins/rubiks_cube_scrambler/scrambler.h b/applications/external/rubiks_cube_scrambler/scrambler.h
similarity index 100%
rename from applications/plugins/rubiks_cube_scrambler/scrambler.h
rename to applications/external/rubiks_cube_scrambler/scrambler.h
diff --git a/applications/plugins/sam/application.fam b/applications/external/sam/application.fam
similarity index 96%
rename from applications/plugins/sam/application.fam
rename to applications/external/sam/application.fam
index 1c49eb236..dc0641b3c 100644
--- a/applications/plugins/sam/application.fam
+++ b/applications/external/sam/application.fam
@@ -1,7 +1,7 @@
App(
appid="SAM",
name="SAM AYBABTU",
- apptype=FlipperAppType.PLUGIN,
+ apptype=FlipperAppType.EXTERNAL,
entry_point="sam_app",
requires=[
"gui",
diff --git a/applications/plugins/sam/icons/music_10px.png b/applications/external/sam/icons/music_10px.png
similarity index 100%
rename from applications/plugins/sam/icons/music_10px.png
rename to applications/external/sam/icons/music_10px.png
diff --git a/applications/plugins/sam/music_10px.png b/applications/external/sam/music_10px.png
similarity index 100%
rename from applications/plugins/sam/music_10px.png
rename to applications/external/sam/music_10px.png
diff --git a/applications/plugins/sam/sam_app.cpp b/applications/external/sam/sam_app.cpp
similarity index 100%
rename from applications/plugins/sam/sam_app.cpp
rename to applications/external/sam/sam_app.cpp
diff --git a/applications/plugins/sam/stm32_sam.cpp b/applications/external/sam/stm32_sam.cpp
similarity index 100%
rename from applications/plugins/sam/stm32_sam.cpp
rename to applications/external/sam/stm32_sam.cpp
diff --git a/applications/plugins/sam/stm32_sam.h b/applications/external/sam/stm32_sam.h
similarity index 100%
rename from applications/plugins/sam/stm32_sam.h
rename to applications/external/sam/stm32_sam.h
diff --git a/applications/plugins/sentry_safe/application.fam b/applications/external/sentry_safe/application.fam
similarity index 88%
rename from applications/plugins/sentry_safe/application.fam
rename to applications/external/sentry_safe/application.fam
index 2eb43f4aa..28d1da04f 100644
--- a/applications/plugins/sentry_safe/application.fam
+++ b/applications/external/sentry_safe/application.fam
@@ -3,7 +3,6 @@ App(
name="[GPIO] Sentry Safe",
apptype=FlipperAppType.EXTERNAL,
entry_point="sentry_safe_app",
- cdefines=["APP_SENTRY_SAFE"],
requires=["gui"],
stack_size=1 * 1024,
order=40,
diff --git a/applications/plugins/sentry_safe/safe_10px.png b/applications/external/sentry_safe/safe_10px.png
similarity index 100%
rename from applications/plugins/sentry_safe/safe_10px.png
rename to applications/external/sentry_safe/safe_10px.png
diff --git a/applications/plugins/sentry_safe/sentry_safe.c b/applications/external/sentry_safe/sentry_safe.c
similarity index 98%
rename from applications/plugins/sentry_safe/sentry_safe.c
rename to applications/external/sentry_safe/sentry_safe.c
index 789b43f2c..a0667b686 100644
--- a/applications/plugins/sentry_safe/sentry_safe.c
+++ b/applications/external/sentry_safe/sentry_safe.c
@@ -2,6 +2,7 @@
#include
#include
#include
+#include
#include
@@ -84,6 +85,7 @@ int32_t sentry_safe_app(void* p) {
UNUSED(p);
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(Event));
+ DOLPHIN_DEED(DolphinDeedPluginStart);
SentryState* sentry_state = malloc(sizeof(SentryState));
@@ -166,4 +168,4 @@ int32_t sentry_safe_app(void* p) {
free(sentry_state);
return 0;
-}
\ No newline at end of file
+}
diff --git a/applications/plugins/signal_generator/application.fam b/applications/external/signal_generator/application.fam
similarity index 79%
rename from applications/plugins/signal_generator/application.fam
rename to applications/external/signal_generator/application.fam
index 8edc9221b..df2dd2559 100644
--- a/applications/plugins/signal_generator/application.fam
+++ b/applications/external/signal_generator/application.fam
@@ -1,9 +1,8 @@
App(
appid="Signal_Generator",
name="[GPIO] Signal Generator",
- apptype=FlipperAppType.PLUGIN,
+ apptype=FlipperAppType.EXTERNAL,
entry_point="signal_gen_app",
- cdefines=["APP_SIGNAL_GEN"],
requires=["gui"],
stack_size=1 * 1024,
order=50,
diff --git a/applications/plugins/signal_generator/icons/SmallArrowDown_3x5.png b/applications/external/signal_generator/icons/SmallArrowDown_3x5.png
similarity index 100%
rename from applications/plugins/signal_generator/icons/SmallArrowDown_3x5.png
rename to applications/external/signal_generator/icons/SmallArrowDown_3x5.png
diff --git a/applications/plugins/signal_generator/icons/SmallArrowUp_3x5.png b/applications/external/signal_generator/icons/SmallArrowUp_3x5.png
similarity index 100%
rename from applications/plugins/signal_generator/icons/SmallArrowUp_3x5.png
rename to applications/external/signal_generator/icons/SmallArrowUp_3x5.png
diff --git a/applications/plugins/signal_generator/scenes/signal_gen_scene.c b/applications/external/signal_generator/scenes/signal_gen_scene.c
similarity index 100%
rename from applications/plugins/signal_generator/scenes/signal_gen_scene.c
rename to applications/external/signal_generator/scenes/signal_gen_scene.c
diff --git a/applications/plugins/signal_generator/scenes/signal_gen_scene.h b/applications/external/signal_generator/scenes/signal_gen_scene.h
similarity index 100%
rename from applications/plugins/signal_generator/scenes/signal_gen_scene.h
rename to applications/external/signal_generator/scenes/signal_gen_scene.h
diff --git a/applications/plugins/signal_generator/scenes/signal_gen_scene_config.h b/applications/external/signal_generator/scenes/signal_gen_scene_config.h
similarity index 100%
rename from applications/plugins/signal_generator/scenes/signal_gen_scene_config.h
rename to applications/external/signal_generator/scenes/signal_gen_scene_config.h
diff --git a/applications/plugins/signal_generator/scenes/signal_gen_scene_mco.c b/applications/external/signal_generator/scenes/signal_gen_scene_mco.c
similarity index 100%
rename from applications/plugins/signal_generator/scenes/signal_gen_scene_mco.c
rename to applications/external/signal_generator/scenes/signal_gen_scene_mco.c
diff --git a/applications/plugins/signal_generator/scenes/signal_gen_scene_pwm.c b/applications/external/signal_generator/scenes/signal_gen_scene_pwm.c
similarity index 100%
rename from applications/plugins/signal_generator/scenes/signal_gen_scene_pwm.c
rename to applications/external/signal_generator/scenes/signal_gen_scene_pwm.c
diff --git a/applications/plugins/signal_generator/scenes/signal_gen_scene_start.c b/applications/external/signal_generator/scenes/signal_gen_scene_start.c
similarity index 100%
rename from applications/plugins/signal_generator/scenes/signal_gen_scene_start.c
rename to applications/external/signal_generator/scenes/signal_gen_scene_start.c
diff --git a/applications/plugins/signal_generator/signal_gen_10px.png b/applications/external/signal_generator/signal_gen_10px.png
similarity index 100%
rename from applications/plugins/signal_generator/signal_gen_10px.png
rename to applications/external/signal_generator/signal_gen_10px.png
diff --git a/applications/plugins/signal_generator/signal_gen_app.c b/applications/external/signal_generator/signal_gen_app.c
similarity index 100%
rename from applications/plugins/signal_generator/signal_gen_app.c
rename to applications/external/signal_generator/signal_gen_app.c
diff --git a/applications/plugins/signal_generator/signal_gen_app_i.h b/applications/external/signal_generator/signal_gen_app_i.h
similarity index 100%
rename from applications/plugins/signal_generator/signal_gen_app_i.h
rename to applications/external/signal_generator/signal_gen_app_i.h
diff --git a/applications/plugins/signal_generator/views/signal_gen_pwm.c b/applications/external/signal_generator/views/signal_gen_pwm.c
similarity index 100%
rename from applications/plugins/signal_generator/views/signal_gen_pwm.c
rename to applications/external/signal_generator/views/signal_gen_pwm.c
diff --git a/applications/plugins/signal_generator/views/signal_gen_pwm.h b/applications/external/signal_generator/views/signal_gen_pwm.h
similarity index 100%
rename from applications/plugins/signal_generator/views/signal_gen_pwm.h
rename to applications/external/signal_generator/views/signal_gen_pwm.h
diff --git a/applications/plugins/snake_game/application.fam b/applications/external/snake_game/application.fam
similarity index 74%
rename from applications/plugins/snake_game/application.fam
rename to applications/external/snake_game/application.fam
index 4e88e1941..021ab4900 100644
--- a/applications/plugins/snake_game/application.fam
+++ b/applications/external/snake_game/application.fam
@@ -1,9 +1,8 @@
App(
appid="Snake",
name="Snake Game",
- apptype=FlipperAppType.PLUGIN,
+ apptype=FlipperAppType.EXTERNAL,
entry_point="snake_game_app",
- cdefines=["APP_SNAKE_GAME"],
requires=["gui"],
stack_size=1 * 1024,
order=210,
diff --git a/applications/plugins/snake_game/snake_10px.png b/applications/external/snake_game/snake_10px.png
similarity index 100%
rename from applications/plugins/snake_game/snake_10px.png
rename to applications/external/snake_game/snake_10px.png
diff --git a/applications/plugins/snake_game/snake_game.c b/applications/external/snake_game/snake_game.c
similarity index 100%
rename from applications/plugins/snake_game/snake_game.c
rename to applications/external/snake_game/snake_game.c
diff --git a/applications/plugins/solitaire/application.fam b/applications/external/solitaire/application.fam
similarity index 90%
rename from applications/plugins/solitaire/application.fam
rename to applications/external/solitaire/application.fam
index 4011c343e..299b4a2dc 100644
--- a/applications/plugins/solitaire/application.fam
+++ b/applications/external/solitaire/application.fam
@@ -3,7 +3,6 @@ App(
name="Solitaire",
apptype=FlipperAppType.EXTERNAL,
entry_point="solitaire_app",
- cdefines=["APP_SOLITAIRE"],
requires=["gui", "storage", "canvas"],
stack_size=2 * 1024,
order=30,
diff --git a/applications/plugins/solitaire/assets/card_graphics.png b/applications/external/solitaire/assets/card_graphics.png
similarity index 100%
rename from applications/plugins/solitaire/assets/card_graphics.png
rename to applications/external/solitaire/assets/card_graphics.png
diff --git a/applications/plugins/solitaire/assets/solitaire_main.png b/applications/external/solitaire/assets/solitaire_main.png
similarity index 100%
rename from applications/plugins/solitaire/assets/solitaire_main.png
rename to applications/external/solitaire/assets/solitaire_main.png
diff --git a/applications/plugins/solitaire/common/card.c b/applications/external/solitaire/common/card.c
similarity index 100%
rename from applications/plugins/solitaire/common/card.c
rename to applications/external/solitaire/common/card.c
diff --git a/applications/plugins/solitaire/common/card.h b/applications/external/solitaire/common/card.h
similarity index 100%
rename from applications/plugins/solitaire/common/card.h
rename to applications/external/solitaire/common/card.h
diff --git a/applications/plugins/solitaire/common/dml.c b/applications/external/solitaire/common/dml.c
similarity index 100%
rename from applications/plugins/solitaire/common/dml.c
rename to applications/external/solitaire/common/dml.c
diff --git a/applications/plugins/solitaire/common/dml.h b/applications/external/solitaire/common/dml.h
similarity index 100%
rename from applications/plugins/solitaire/common/dml.h
rename to applications/external/solitaire/common/dml.h
diff --git a/applications/plugins/solitaire/common/menu.c b/applications/external/solitaire/common/menu.c
similarity index 100%
rename from applications/plugins/solitaire/common/menu.c
rename to applications/external/solitaire/common/menu.c
diff --git a/applications/plugins/solitaire/common/menu.h b/applications/external/solitaire/common/menu.h
similarity index 100%
rename from applications/plugins/solitaire/common/menu.h
rename to applications/external/solitaire/common/menu.h
diff --git a/applications/plugins/solitaire/common/queue.c b/applications/external/solitaire/common/queue.c
similarity index 100%
rename from applications/plugins/solitaire/common/queue.c
rename to applications/external/solitaire/common/queue.c
diff --git a/applications/plugins/solitaire/common/queue.h b/applications/external/solitaire/common/queue.h
similarity index 100%
rename from applications/plugins/solitaire/common/queue.h
rename to applications/external/solitaire/common/queue.h
diff --git a/applications/plugins/solitaire/common/ui.c b/applications/external/solitaire/common/ui.c
similarity index 100%
rename from applications/plugins/solitaire/common/ui.c
rename to applications/external/solitaire/common/ui.c
diff --git a/applications/plugins/solitaire/common/ui.h b/applications/external/solitaire/common/ui.h
similarity index 100%
rename from applications/plugins/solitaire/common/ui.h
rename to applications/external/solitaire/common/ui.h
diff --git a/applications/plugins/solitaire/defines.h b/applications/external/solitaire/defines.h
similarity index 100%
rename from applications/plugins/solitaire/defines.h
rename to applications/external/solitaire/defines.h
diff --git a/applications/plugins/solitaire/solitaire.c b/applications/external/solitaire/solitaire.c
similarity index 100%
rename from applications/plugins/solitaire/solitaire.c
rename to applications/external/solitaire/solitaire.c
diff --git a/applications/plugins/solitaire/solitaire_10px.png b/applications/external/solitaire/solitaire_10px.png
similarity index 100%
rename from applications/plugins/solitaire/solitaire_10px.png
rename to applications/external/solitaire/solitaire_10px.png
diff --git a/applications/plugins/spectrum_analyzer/application.fam b/applications/external/spectrum_analyzer/application.fam
similarity index 87%
rename from applications/plugins/spectrum_analyzer/application.fam
rename to applications/external/spectrum_analyzer/application.fam
index 344c2244f..286aa64ba 100644
--- a/applications/plugins/spectrum_analyzer/application.fam
+++ b/applications/external/spectrum_analyzer/application.fam
@@ -3,7 +3,6 @@ App(
name="Spectrum Analyzer",
apptype=FlipperAppType.EXTERNAL,
entry_point="spectrum_analyzer_app",
- cdefines=["APP_SPECTRUM_ANALYZER"],
requires=["gui"],
stack_size=2 * 1024,
order=12,
diff --git a/applications/plugins/spectrum_analyzer/spectrum_10px.png b/applications/external/spectrum_analyzer/spectrum_10px.png
similarity index 100%
rename from applications/plugins/spectrum_analyzer/spectrum_10px.png
rename to applications/external/spectrum_analyzer/spectrum_10px.png
diff --git a/applications/plugins/spectrum_analyzer/spectrum_analyzer.c b/applications/external/spectrum_analyzer/spectrum_analyzer.c
similarity index 98%
rename from applications/plugins/spectrum_analyzer/spectrum_analyzer.c
rename to applications/external/spectrum_analyzer/spectrum_analyzer.c
index 99c12adf7..7148ad92b 100644
--- a/applications/plugins/spectrum_analyzer/spectrum_analyzer.c
+++ b/applications/external/spectrum_analyzer/spectrum_analyzer.c
@@ -5,6 +5,7 @@
#include
#include
#include "spectrum_analyzer.h"
+#include
#include
#include "spectrum_analyzer_worker.h"
@@ -400,11 +401,17 @@ void spectrum_analyzer_free(SpectrumAnalyzer* instance) {
int32_t spectrum_analyzer_app(void* p) {
UNUSED(p);
+ DOLPHIN_DEED(DolphinDeedPluginStart);
SpectrumAnalyzer* spectrum_analyzer = spectrum_analyzer_alloc();
InputEvent input;
// Enable power for External CC1101 if it is connected
furi_hal_subghz_enable_ext_power();
+ // Auto switch to internal radio if external radio is not available
+ furi_delay_ms(15);
+ if(!furi_hal_subghz_check_radio()) {
+ furi_hal_subghz_set_radio_type(SubGhzRadioInternal);
+ }
furi_hal_power_suppress_charge_enter();
@@ -518,4 +525,4 @@ int32_t spectrum_analyzer_app(void* p) {
spectrum_analyzer_free(spectrum_analyzer);
return 0;
-}
\ No newline at end of file
+}
diff --git a/applications/plugins/spectrum_analyzer/spectrum_analyzer.h b/applications/external/spectrum_analyzer/spectrum_analyzer.h
similarity index 100%
rename from applications/plugins/spectrum_analyzer/spectrum_analyzer.h
rename to applications/external/spectrum_analyzer/spectrum_analyzer.h
diff --git a/applications/plugins/spectrum_analyzer/spectrum_analyzer_worker.c b/applications/external/spectrum_analyzer/spectrum_analyzer_worker.c
similarity index 100%
rename from applications/plugins/spectrum_analyzer/spectrum_analyzer_worker.c
rename to applications/external/spectrum_analyzer/spectrum_analyzer_worker.c
diff --git a/applications/plugins/spectrum_analyzer/spectrum_analyzer_worker.h b/applications/external/spectrum_analyzer/spectrum_analyzer_worker.h
similarity index 100%
rename from applications/plugins/spectrum_analyzer/spectrum_analyzer_worker.h
rename to applications/external/spectrum_analyzer/spectrum_analyzer_worker.h
diff --git a/applications/plugins/spi_mem_manager/application.fam b/applications/external/spi_mem_manager/application.fam
similarity index 100%
rename from applications/plugins/spi_mem_manager/application.fam
rename to applications/external/spi_mem_manager/application.fam
diff --git a/applications/plugins/spi_mem_manager/images/ChipLooking_64x64/frame_01.png b/applications/external/spi_mem_manager/images/ChipLooking_64x64/frame_01.png
similarity index 100%
rename from applications/plugins/spi_mem_manager/images/ChipLooking_64x64/frame_01.png
rename to applications/external/spi_mem_manager/images/ChipLooking_64x64/frame_01.png
diff --git a/applications/plugins/spi_mem_manager/images/ChipLooking_64x64/frame_02.png b/applications/external/spi_mem_manager/images/ChipLooking_64x64/frame_02.png
similarity index 100%
rename from applications/plugins/spi_mem_manager/images/ChipLooking_64x64/frame_02.png
rename to applications/external/spi_mem_manager/images/ChipLooking_64x64/frame_02.png
diff --git a/applications/plugins/spi_mem_manager/images/ChipLooking_64x64/frame_03.png b/applications/external/spi_mem_manager/images/ChipLooking_64x64/frame_03.png
similarity index 100%
rename from applications/plugins/spi_mem_manager/images/ChipLooking_64x64/frame_03.png
rename to applications/external/spi_mem_manager/images/ChipLooking_64x64/frame_03.png
diff --git a/applications/plugins/spi_mem_manager/images/ChipLooking_64x64/frame_rate b/applications/external/spi_mem_manager/images/ChipLooking_64x64/frame_rate
similarity index 100%
rename from applications/plugins/spi_mem_manager/images/ChipLooking_64x64/frame_rate
rename to applications/external/spi_mem_manager/images/ChipLooking_64x64/frame_rate
diff --git a/applications/plugins/spi_mem_manager/images/Dip8_10px.png b/applications/external/spi_mem_manager/images/Dip8_10px.png
similarity index 100%
rename from applications/plugins/spi_mem_manager/images/Dip8_10px.png
rename to applications/external/spi_mem_manager/images/Dip8_10px.png
diff --git a/applications/plugins/spi_mem_manager/images/Dip8_32x36.png b/applications/external/spi_mem_manager/images/Dip8_32x36.png
similarity index 100%
rename from applications/plugins/spi_mem_manager/images/Dip8_32x36.png
rename to applications/external/spi_mem_manager/images/Dip8_32x36.png
diff --git a/applications/plugins/spi_mem_manager/images/DolphinMafia_115x62.png b/applications/external/spi_mem_manager/images/DolphinMafia_115x62.png
similarity index 100%
rename from applications/plugins/spi_mem_manager/images/DolphinMafia_115x62.png
rename to applications/external/spi_mem_manager/images/DolphinMafia_115x62.png
diff --git a/applications/plugins/spi_mem_manager/images/DolphinNice_96x59.png b/applications/external/spi_mem_manager/images/DolphinNice_96x59.png
similarity index 100%
rename from applications/plugins/spi_mem_manager/images/DolphinNice_96x59.png
rename to applications/external/spi_mem_manager/images/DolphinNice_96x59.png
diff --git a/applications/plugins/spi_mem_manager/images/SDQuestion_35x43.png b/applications/external/spi_mem_manager/images/SDQuestion_35x43.png
similarity index 100%
rename from applications/plugins/spi_mem_manager/images/SDQuestion_35x43.png
rename to applications/external/spi_mem_manager/images/SDQuestion_35x43.png
diff --git a/applications/plugins/spi_mem_manager/images/Wiring_SPI_128x64.png b/applications/external/spi_mem_manager/images/Wiring_SPI_128x64.png
similarity index 100%
rename from applications/plugins/spi_mem_manager/images/Wiring_SPI_128x64.png
rename to applications/external/spi_mem_manager/images/Wiring_SPI_128x64.png
diff --git a/applications/plugins/spi_mem_manager/lib/spi/spi_mem_chip.c b/applications/external/spi_mem_manager/lib/spi/spi_mem_chip.c
similarity index 100%
rename from applications/plugins/spi_mem_manager/lib/spi/spi_mem_chip.c
rename to applications/external/spi_mem_manager/lib/spi/spi_mem_chip.c
diff --git a/applications/plugins/spi_mem_manager/lib/spi/spi_mem_chip.h b/applications/external/spi_mem_manager/lib/spi/spi_mem_chip.h
similarity index 100%
rename from applications/plugins/spi_mem_manager/lib/spi/spi_mem_chip.h
rename to applications/external/spi_mem_manager/lib/spi/spi_mem_chip.h
diff --git a/applications/plugins/spi_mem_manager/lib/spi/spi_mem_chip_arr.c b/applications/external/spi_mem_manager/lib/spi/spi_mem_chip_arr.c
similarity index 100%
rename from applications/plugins/spi_mem_manager/lib/spi/spi_mem_chip_arr.c
rename to applications/external/spi_mem_manager/lib/spi/spi_mem_chip_arr.c
diff --git a/applications/plugins/spi_mem_manager/lib/spi/spi_mem_chip_i.h b/applications/external/spi_mem_manager/lib/spi/spi_mem_chip_i.h
similarity index 100%
rename from applications/plugins/spi_mem_manager/lib/spi/spi_mem_chip_i.h
rename to applications/external/spi_mem_manager/lib/spi/spi_mem_chip_i.h
diff --git a/applications/plugins/spi_mem_manager/lib/spi/spi_mem_tools.c b/applications/external/spi_mem_manager/lib/spi/spi_mem_tools.c
similarity index 100%
rename from applications/plugins/spi_mem_manager/lib/spi/spi_mem_tools.c
rename to applications/external/spi_mem_manager/lib/spi/spi_mem_tools.c
diff --git a/applications/plugins/spi_mem_manager/lib/spi/spi_mem_tools.h b/applications/external/spi_mem_manager/lib/spi/spi_mem_tools.h
similarity index 100%
rename from applications/plugins/spi_mem_manager/lib/spi/spi_mem_tools.h
rename to applications/external/spi_mem_manager/lib/spi/spi_mem_tools.h
diff --git a/applications/plugins/spi_mem_manager/lib/spi/spi_mem_worker.c b/applications/external/spi_mem_manager/lib/spi/spi_mem_worker.c
similarity index 100%
rename from applications/plugins/spi_mem_manager/lib/spi/spi_mem_worker.c
rename to applications/external/spi_mem_manager/lib/spi/spi_mem_worker.c
diff --git a/applications/plugins/spi_mem_manager/lib/spi/spi_mem_worker.h b/applications/external/spi_mem_manager/lib/spi/spi_mem_worker.h
similarity index 100%
rename from applications/plugins/spi_mem_manager/lib/spi/spi_mem_worker.h
rename to applications/external/spi_mem_manager/lib/spi/spi_mem_worker.h
diff --git a/applications/plugins/spi_mem_manager/lib/spi/spi_mem_worker_i.h b/applications/external/spi_mem_manager/lib/spi/spi_mem_worker_i.h
similarity index 100%
rename from applications/plugins/spi_mem_manager/lib/spi/spi_mem_worker_i.h
rename to applications/external/spi_mem_manager/lib/spi/spi_mem_worker_i.h
diff --git a/applications/plugins/spi_mem_manager/lib/spi/spi_mem_worker_modes.c b/applications/external/spi_mem_manager/lib/spi/spi_mem_worker_modes.c
similarity index 100%
rename from applications/plugins/spi_mem_manager/lib/spi/spi_mem_worker_modes.c
rename to applications/external/spi_mem_manager/lib/spi/spi_mem_worker_modes.c
diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene.c b/applications/external/spi_mem_manager/scenes/spi_mem_scene.c
similarity index 100%
rename from applications/plugins/spi_mem_manager/scenes/spi_mem_scene.c
rename to applications/external/spi_mem_manager/scenes/spi_mem_scene.c
diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene.h b/applications/external/spi_mem_manager/scenes/spi_mem_scene.h
similarity index 100%
rename from applications/plugins/spi_mem_manager/scenes/spi_mem_scene.h
rename to applications/external/spi_mem_manager/scenes/spi_mem_scene.h
diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_about.c b/applications/external/spi_mem_manager/scenes/spi_mem_scene_about.c
similarity index 100%
rename from applications/plugins/spi_mem_manager/scenes/spi_mem_scene_about.c
rename to applications/external/spi_mem_manager/scenes/spi_mem_scene_about.c
diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_chip_detect.c b/applications/external/spi_mem_manager/scenes/spi_mem_scene_chip_detect.c
similarity index 100%
rename from applications/plugins/spi_mem_manager/scenes/spi_mem_scene_chip_detect.c
rename to applications/external/spi_mem_manager/scenes/spi_mem_scene_chip_detect.c
diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_chip_detect_fail.c b/applications/external/spi_mem_manager/scenes/spi_mem_scene_chip_detect_fail.c
similarity index 100%
rename from applications/plugins/spi_mem_manager/scenes/spi_mem_scene_chip_detect_fail.c
rename to applications/external/spi_mem_manager/scenes/spi_mem_scene_chip_detect_fail.c
diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_chip_detected.c b/applications/external/spi_mem_manager/scenes/spi_mem_scene_chip_detected.c
similarity index 100%
rename from applications/plugins/spi_mem_manager/scenes/spi_mem_scene_chip_detected.c
rename to applications/external/spi_mem_manager/scenes/spi_mem_scene_chip_detected.c
diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_chip_error.c b/applications/external/spi_mem_manager/scenes/spi_mem_scene_chip_error.c
similarity index 100%
rename from applications/plugins/spi_mem_manager/scenes/spi_mem_scene_chip_error.c
rename to applications/external/spi_mem_manager/scenes/spi_mem_scene_chip_error.c
diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_config.h b/applications/external/spi_mem_manager/scenes/spi_mem_scene_config.h
similarity index 100%
rename from applications/plugins/spi_mem_manager/scenes/spi_mem_scene_config.h
rename to applications/external/spi_mem_manager/scenes/spi_mem_scene_config.h
diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_delete_confirm.c b/applications/external/spi_mem_manager/scenes/spi_mem_scene_delete_confirm.c
similarity index 100%
rename from applications/plugins/spi_mem_manager/scenes/spi_mem_scene_delete_confirm.c
rename to applications/external/spi_mem_manager/scenes/spi_mem_scene_delete_confirm.c
diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_erase.c b/applications/external/spi_mem_manager/scenes/spi_mem_scene_erase.c
similarity index 100%
rename from applications/plugins/spi_mem_manager/scenes/spi_mem_scene_erase.c
rename to applications/external/spi_mem_manager/scenes/spi_mem_scene_erase.c
diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_file_info.c b/applications/external/spi_mem_manager/scenes/spi_mem_scene_file_info.c
similarity index 100%
rename from applications/plugins/spi_mem_manager/scenes/spi_mem_scene_file_info.c
rename to applications/external/spi_mem_manager/scenes/spi_mem_scene_file_info.c
diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_read.c b/applications/external/spi_mem_manager/scenes/spi_mem_scene_read.c
similarity index 100%
rename from applications/plugins/spi_mem_manager/scenes/spi_mem_scene_read.c
rename to applications/external/spi_mem_manager/scenes/spi_mem_scene_read.c
diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_read_filename.c b/applications/external/spi_mem_manager/scenes/spi_mem_scene_read_filename.c
similarity index 100%
rename from applications/plugins/spi_mem_manager/scenes/spi_mem_scene_read_filename.c
rename to applications/external/spi_mem_manager/scenes/spi_mem_scene_read_filename.c
diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_saved_file_menu.c b/applications/external/spi_mem_manager/scenes/spi_mem_scene_saved_file_menu.c
similarity index 100%
rename from applications/plugins/spi_mem_manager/scenes/spi_mem_scene_saved_file_menu.c
rename to applications/external/spi_mem_manager/scenes/spi_mem_scene_saved_file_menu.c
diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_select_file.c b/applications/external/spi_mem_manager/scenes/spi_mem_scene_select_file.c
similarity index 100%
rename from applications/plugins/spi_mem_manager/scenes/spi_mem_scene_select_file.c
rename to applications/external/spi_mem_manager/scenes/spi_mem_scene_select_file.c
diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_select_model.c b/applications/external/spi_mem_manager/scenes/spi_mem_scene_select_model.c
similarity index 100%
rename from applications/plugins/spi_mem_manager/scenes/spi_mem_scene_select_model.c
rename to applications/external/spi_mem_manager/scenes/spi_mem_scene_select_model.c
diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_select_vendor.c b/applications/external/spi_mem_manager/scenes/spi_mem_scene_select_vendor.c
similarity index 100%
rename from applications/plugins/spi_mem_manager/scenes/spi_mem_scene_select_vendor.c
rename to applications/external/spi_mem_manager/scenes/spi_mem_scene_select_vendor.c
diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_start.c b/applications/external/spi_mem_manager/scenes/spi_mem_scene_start.c
similarity index 100%
rename from applications/plugins/spi_mem_manager/scenes/spi_mem_scene_start.c
rename to applications/external/spi_mem_manager/scenes/spi_mem_scene_start.c
diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_storage_error.c b/applications/external/spi_mem_manager/scenes/spi_mem_scene_storage_error.c
similarity index 100%
rename from applications/plugins/spi_mem_manager/scenes/spi_mem_scene_storage_error.c
rename to applications/external/spi_mem_manager/scenes/spi_mem_scene_storage_error.c
diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_success.c b/applications/external/spi_mem_manager/scenes/spi_mem_scene_success.c
similarity index 100%
rename from applications/plugins/spi_mem_manager/scenes/spi_mem_scene_success.c
rename to applications/external/spi_mem_manager/scenes/spi_mem_scene_success.c
diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_verify.c b/applications/external/spi_mem_manager/scenes/spi_mem_scene_verify.c
similarity index 100%
rename from applications/plugins/spi_mem_manager/scenes/spi_mem_scene_verify.c
rename to applications/external/spi_mem_manager/scenes/spi_mem_scene_verify.c
diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_verify_error.c b/applications/external/spi_mem_manager/scenes/spi_mem_scene_verify_error.c
similarity index 100%
rename from applications/plugins/spi_mem_manager/scenes/spi_mem_scene_verify_error.c
rename to applications/external/spi_mem_manager/scenes/spi_mem_scene_verify_error.c
diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_wiring.c b/applications/external/spi_mem_manager/scenes/spi_mem_scene_wiring.c
similarity index 100%
rename from applications/plugins/spi_mem_manager/scenes/spi_mem_scene_wiring.c
rename to applications/external/spi_mem_manager/scenes/spi_mem_scene_wiring.c
diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_write.c b/applications/external/spi_mem_manager/scenes/spi_mem_scene_write.c
similarity index 100%
rename from applications/plugins/spi_mem_manager/scenes/spi_mem_scene_write.c
rename to applications/external/spi_mem_manager/scenes/spi_mem_scene_write.c
diff --git a/applications/plugins/spi_mem_manager/spi_mem_app.c b/applications/external/spi_mem_manager/spi_mem_app.c
similarity index 100%
rename from applications/plugins/spi_mem_manager/spi_mem_app.c
rename to applications/external/spi_mem_manager/spi_mem_app.c
diff --git a/applications/plugins/spi_mem_manager/spi_mem_app.h b/applications/external/spi_mem_manager/spi_mem_app.h
similarity index 100%
rename from applications/plugins/spi_mem_manager/spi_mem_app.h
rename to applications/external/spi_mem_manager/spi_mem_app.h
diff --git a/applications/plugins/spi_mem_manager/spi_mem_app_i.h b/applications/external/spi_mem_manager/spi_mem_app_i.h
similarity index 100%
rename from applications/plugins/spi_mem_manager/spi_mem_app_i.h
rename to applications/external/spi_mem_manager/spi_mem_app_i.h
diff --git a/applications/plugins/spi_mem_manager/spi_mem_files.c b/applications/external/spi_mem_manager/spi_mem_files.c
similarity index 100%
rename from applications/plugins/spi_mem_manager/spi_mem_files.c
rename to applications/external/spi_mem_manager/spi_mem_files.c
diff --git a/applications/plugins/spi_mem_manager/spi_mem_files.h b/applications/external/spi_mem_manager/spi_mem_files.h
similarity index 100%
rename from applications/plugins/spi_mem_manager/spi_mem_files.h
rename to applications/external/spi_mem_manager/spi_mem_files.h
diff --git a/applications/plugins/spi_mem_manager/tools/chiplist/LICENSE b/applications/external/spi_mem_manager/tools/chiplist/LICENSE
similarity index 100%
rename from applications/plugins/spi_mem_manager/tools/chiplist/LICENSE
rename to applications/external/spi_mem_manager/tools/chiplist/LICENSE
diff --git a/applications/plugins/spi_mem_manager/tools/chiplist/chiplist.xml b/applications/external/spi_mem_manager/tools/chiplist/chiplist.xml
similarity index 100%
rename from applications/plugins/spi_mem_manager/tools/chiplist/chiplist.xml
rename to applications/external/spi_mem_manager/tools/chiplist/chiplist.xml
diff --git a/applications/plugins/spi_mem_manager/tools/chiplist_convert.py b/applications/external/spi_mem_manager/tools/chiplist_convert.py
similarity index 100%
rename from applications/plugins/spi_mem_manager/tools/chiplist_convert.py
rename to applications/external/spi_mem_manager/tools/chiplist_convert.py
diff --git a/applications/plugins/spi_mem_manager/views/spi_mem_view_detect.c b/applications/external/spi_mem_manager/views/spi_mem_view_detect.c
similarity index 100%
rename from applications/plugins/spi_mem_manager/views/spi_mem_view_detect.c
rename to applications/external/spi_mem_manager/views/spi_mem_view_detect.c
diff --git a/applications/plugins/spi_mem_manager/views/spi_mem_view_detect.h b/applications/external/spi_mem_manager/views/spi_mem_view_detect.h
similarity index 100%
rename from applications/plugins/spi_mem_manager/views/spi_mem_view_detect.h
rename to applications/external/spi_mem_manager/views/spi_mem_view_detect.h
diff --git a/applications/plugins/spi_mem_manager/views/spi_mem_view_progress.c b/applications/external/spi_mem_manager/views/spi_mem_view_progress.c
similarity index 100%
rename from applications/plugins/spi_mem_manager/views/spi_mem_view_progress.c
rename to applications/external/spi_mem_manager/views/spi_mem_view_progress.c
diff --git a/applications/plugins/spi_mem_manager/views/spi_mem_view_progress.h b/applications/external/spi_mem_manager/views/spi_mem_view_progress.h
similarity index 100%
rename from applications/plugins/spi_mem_manager/views/spi_mem_view_progress.h
rename to applications/external/spi_mem_manager/views/spi_mem_view_progress.h
diff --git a/applications/external/subghz_bruteforcer/LICENSE b/applications/external/subghz_bruteforcer/LICENSE
new file mode 100644
index 000000000..06dcf7e87
--- /dev/null
+++ b/applications/external/subghz_bruteforcer/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2022 Der Skythe
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/applications/external/subghz_bruteforcer/application.fam b/applications/external/subghz_bruteforcer/application.fam
new file mode 100644
index 000000000..f756ae51a
--- /dev/null
+++ b/applications/external/subghz_bruteforcer/application.fam
@@ -0,0 +1,12 @@
+App(
+ appid="SubGHz_Bruteforcer",
+ name="Sub-GHz Bruteforcer",
+ apptype=FlipperAppType.EXTERNAL,
+ entry_point="subbrute_app",
+ requires=["gui", "dialogs"],
+ stack_size=2 * 1024,
+ order=11,
+ fap_icon="images/subbrute_10px.png",
+ fap_category="Sub-GHz",
+ fap_icon_assets="images",
+)
diff --git a/applications/external/subghz_bruteforcer/helpers/gui_top_buttons.c b/applications/external/subghz_bruteforcer/helpers/gui_top_buttons.c
new file mode 100644
index 000000000..0415c5ae7
--- /dev/null
+++ b/applications/external/subghz_bruteforcer/helpers/gui_top_buttons.c
@@ -0,0 +1,59 @@
+#include "gui_top_buttons.h"
+
+void elements_button_top_left(Canvas* canvas, const char* str) {
+ const Icon* icon = &I_ButtonUp_7x4;
+
+ const uint8_t button_height = 12;
+ const uint8_t vertical_offset = 3;
+ const uint8_t horizontal_offset = 3;
+ const uint8_t string_width = canvas_string_width(canvas, str);
+ const uint8_t icon_h_offset = 3;
+ const uint8_t icon_width_with_offset = icon_get_width(icon) + icon_h_offset;
+ const uint8_t icon_v_offset = icon_get_height(icon) + vertical_offset;
+ const uint8_t button_width = string_width + horizontal_offset * 2 + icon_width_with_offset;
+
+ const uint8_t x = 0;
+ const uint8_t y = 0 + button_height;
+
+ uint8_t line_x = x + button_width;
+ uint8_t line_y = y - button_height;
+ canvas_draw_box(canvas, x, line_y, button_width, button_height);
+ canvas_draw_line(canvas, line_x + 0, line_y, line_x + 0, y - 1);
+ canvas_draw_line(canvas, line_x + 1, line_y, line_x + 1, y - 2);
+ canvas_draw_line(canvas, line_x + 2, line_y, line_x + 2, y - 3);
+
+ canvas_invert_color(canvas);
+ canvas_draw_icon(canvas, x + horizontal_offset, y - icon_v_offset, icon);
+ canvas_draw_str(
+ canvas, x + horizontal_offset + icon_width_with_offset, y - vertical_offset, str);
+ canvas_invert_color(canvas);
+}
+
+void elements_button_top_right(Canvas* canvas, const char* str) {
+ const Icon* icon = &I_ButtonDown_7x4;
+
+ const uint8_t button_height = 12;
+ const uint8_t vertical_offset = 3;
+ const uint8_t horizontal_offset = 3;
+ const uint8_t string_width = canvas_string_width(canvas, str);
+ const uint8_t icon_h_offset = 3;
+ const uint8_t icon_width_with_offset = icon_get_width(icon) + icon_h_offset;
+ const uint8_t icon_v_offset = icon_get_height(icon) + vertical_offset + 1;
+ const uint8_t button_width = string_width + horizontal_offset * 2 + icon_width_with_offset;
+
+ const uint8_t x = canvas_width(canvas);
+ const uint8_t y = 0 + button_height;
+
+ uint8_t line_x = x - button_width;
+ uint8_t line_y = y - button_height;
+ canvas_draw_box(canvas, line_x, line_y, button_width, button_height);
+ canvas_draw_line(canvas, line_x - 1, line_y, line_x - 1, y - 1);
+ canvas_draw_line(canvas, line_x - 2, line_y, line_x - 2, y - 2);
+ canvas_draw_line(canvas, line_x - 3, line_y, line_x - 3, y - 3);
+
+ canvas_invert_color(canvas);
+ canvas_draw_str(canvas, x - button_width + horizontal_offset, y - vertical_offset, str);
+ canvas_draw_icon(
+ canvas, x - horizontal_offset - icon_get_width(icon), y - icon_v_offset, icon);
+ canvas_invert_color(canvas);
+}
\ No newline at end of file
diff --git a/applications/external/subghz_bruteforcer/helpers/gui_top_buttons.h b/applications/external/subghz_bruteforcer/helpers/gui_top_buttons.h
new file mode 100644
index 000000000..b5ca507b7
--- /dev/null
+++ b/applications/external/subghz_bruteforcer/helpers/gui_top_buttons.h
@@ -0,0 +1,21 @@
+#pragma once
+
+#include
+#include
+#include
+#include
+#include
+
+/**
+ * Thanks to the author of metronome
+ * @param canvas
+ * @param str
+ */
+void elements_button_top_left(Canvas* canvas, const char* str);
+
+/**
+ * Thanks to the author of metronome
+ * @param canvas
+ * @param str
+ */
+void elements_button_top_right(Canvas* canvas, const char* str);
\ No newline at end of file
diff --git a/applications/external/subghz_bruteforcer/helpers/subbrute_worker.c b/applications/external/subghz_bruteforcer/helpers/subbrute_worker.c
new file mode 100644
index 000000000..118e63c65
--- /dev/null
+++ b/applications/external/subghz_bruteforcer/helpers/subbrute_worker.c
@@ -0,0 +1,437 @@
+#include "subbrute_worker_private.h"
+#include
+#include
+#include
+#include
+#include
+
+#define TAG "SubBruteWorker"
+#define SUBBRUTE_TX_TIMEOUT 5
+#define SUBBRUTE_MANUAL_TRANSMIT_INTERVAL 400
+
+SubBruteWorker* subbrute_worker_alloc() {
+ SubBruteWorker* instance = malloc(sizeof(SubBruteWorker));
+
+ instance->state = SubBruteWorkerStateIDLE;
+ instance->step = 0;
+ instance->worker_running = false;
+ instance->initiated = false;
+ instance->last_time_tx_data = 0;
+ instance->load_index = 0;
+
+ instance->thread = furi_thread_alloc();
+ furi_thread_set_name(instance->thread, "SubBruteAttackWorker");
+ furi_thread_set_stack_size(instance->thread, 2048);
+ furi_thread_set_context(instance->thread, instance);
+ furi_thread_set_callback(instance->thread, subbrute_worker_thread);
+
+ instance->context = NULL;
+ instance->callback = NULL;
+
+ instance->decoder_result = NULL;
+ instance->transmitter = NULL;
+ instance->environment = subghz_environment_alloc();
+ subghz_environment_set_protocol_registry(
+ instance->environment, (void*)&subghz_protocol_registry);
+
+ instance->transmit_mode = false;
+
+ return instance;
+}
+
+void subbrute_worker_free(SubBruteWorker* instance) {
+ furi_assert(instance);
+
+ // I don't know how to free this
+ instance->decoder_result = NULL;
+
+ if(instance->transmitter != NULL) {
+ subghz_transmitter_free(instance->transmitter);
+ instance->transmitter = NULL;
+ }
+
+ subghz_environment_free(instance->environment);
+ instance->environment = NULL;
+
+ furi_thread_free(instance->thread);
+
+ free(instance);
+}
+
+uint64_t subbrute_worker_get_step(SubBruteWorker* instance) {
+ return instance->step;
+}
+
+bool subbrute_worker_set_step(SubBruteWorker* instance, uint64_t step) {
+ furi_assert(instance);
+ if(!subbrute_worker_can_manual_transmit(instance)) {
+ FURI_LOG_W(TAG, "Cannot set step during running mode");
+ return false;
+ }
+
+ instance->step = step;
+
+ return true;
+}
+
+bool subbrute_worker_init_default_attack(
+ SubBruteWorker* instance,
+ SubBruteAttacks attack_type,
+ uint64_t step,
+ const SubBruteProtocol* protocol,
+ uint8_t extra_repeats) {
+ furi_assert(instance);
+
+ if(instance->worker_running) {
+ FURI_LOG_W(TAG, "Init Worker when it's running");
+ subbrute_worker_stop(instance);
+ }
+
+ instance->attack = attack_type;
+ instance->frequency = protocol->frequency;
+ instance->preset = protocol->preset;
+ instance->file = protocol->file;
+ instance->step = step;
+ instance->bits = protocol->bits;
+ instance->te = protocol->te;
+ instance->repeat = protocol->repeat + extra_repeats;
+ instance->load_index = 0;
+ instance->file_key = 0;
+ instance->two_bytes = false;
+
+ instance->max_value =
+ subbrute_protocol_calc_max_value(instance->attack, instance->bits, instance->two_bytes);
+
+ instance->initiated = true;
+ instance->state = SubBruteWorkerStateReady;
+ subbrute_worker_send_callback(instance);
+#ifdef FURI_DEBUG
+ FURI_LOG_I(
+ TAG,
+ "subbrute_worker_init_default_attack: %s, bits: %d, preset: %s, file: %s, te: %ld, repeat: %d, max_value: %lld",
+ subbrute_protocol_name(instance->attack),
+ instance->bits,
+ subbrute_protocol_preset(instance->preset),
+ subbrute_protocol_file(instance->file),
+ instance->te,
+ instance->repeat,
+ instance->max_value);
+#endif
+
+ return true;
+}
+
+bool subbrute_worker_init_file_attack(
+ SubBruteWorker* instance,
+ uint64_t step,
+ uint8_t load_index,
+ uint64_t file_key,
+ SubBruteProtocol* protocol,
+ uint8_t extra_repeats,
+ bool two_bytes) {
+ furi_assert(instance);
+
+ if(instance->worker_running) {
+ FURI_LOG_W(TAG, "Init Worker when it's running");
+ subbrute_worker_stop(instance);
+ }
+
+ instance->attack = SubBruteAttackLoadFile;
+ instance->frequency = protocol->frequency;
+ instance->preset = protocol->preset;
+ instance->file = protocol->file;
+ instance->step = step;
+ instance->bits = protocol->bits;
+ instance->te = protocol->te;
+ instance->load_index = load_index;
+ instance->repeat = protocol->repeat + extra_repeats;
+ instance->file_key = file_key;
+ instance->two_bytes = two_bytes;
+
+ instance->max_value =
+ subbrute_protocol_calc_max_value(instance->attack, instance->bits, instance->two_bytes);
+
+ instance->initiated = true;
+ instance->state = SubBruteWorkerStateReady;
+ subbrute_worker_send_callback(instance);
+#ifdef FURI_DEBUG
+ FURI_LOG_I(
+ TAG,
+ "subbrute_worker_init_file_attack: %s, bits: %d, preset: %s, file: %s, te: %ld, repeat: %d, max_value: %lld, key: %llX",
+ subbrute_protocol_name(instance->attack),
+ instance->bits,
+ subbrute_protocol_preset(instance->preset),
+ subbrute_protocol_file(instance->file),
+ instance->te,
+ instance->repeat,
+ instance->max_value,
+ instance->file_key);
+#endif
+
+ return true;
+}
+
+bool subbrute_worker_start(SubBruteWorker* instance) {
+ furi_assert(instance);
+
+ if(!instance->initiated) {
+ FURI_LOG_W(TAG, "Worker not init!");
+ return false;
+ }
+
+ if(instance->worker_running) {
+ FURI_LOG_W(TAG, "Worker is already running!");
+ return false;
+ }
+ if(instance->state != SubBruteWorkerStateReady &&
+ instance->state != SubBruteWorkerStateFinished) {
+ FURI_LOG_W(TAG, "Worker cannot start, invalid device state: %d", instance->state);
+ return false;
+ }
+
+ instance->worker_running = true;
+ furi_thread_start(instance->thread);
+
+ return true;
+}
+
+void subbrute_worker_stop(SubBruteWorker* instance) {
+ furi_assert(instance);
+
+ if(!instance->worker_running) {
+ return;
+ }
+
+ instance->worker_running = false;
+ furi_thread_join(instance->thread);
+
+ furi_hal_subghz_set_path(FuriHalSubGhzPathIsolate);
+ furi_hal_subghz_sleep();
+}
+
+bool subbrute_worker_transmit_current_key(SubBruteWorker* instance, uint64_t step) {
+ furi_assert(instance);
+
+ if(!instance->initiated) {
+ FURI_LOG_W(TAG, "Worker not init!");
+ return false;
+ }
+ if(instance->worker_running) {
+ FURI_LOG_W(TAG, "Worker in running state!");
+ return false;
+ }
+ if(instance->state != SubBruteWorkerStateReady &&
+ instance->state != SubBruteWorkerStateFinished) {
+ FURI_LOG_W(TAG, "Invalid state for running worker! State: %d", instance->state);
+ return false;
+ }
+
+ uint32_t ticks = furi_get_tick();
+ if((ticks - instance->last_time_tx_data) < SUBBRUTE_MANUAL_TRANSMIT_INTERVAL) {
+#if FURI_DEBUG
+ FURI_LOG_D(TAG, "Need to wait, current: %ld", ticks - instance->last_time_tx_data);
+#endif
+ return false;
+ }
+
+ instance->last_time_tx_data = ticks;
+ instance->step = step;
+
+ bool result;
+ instance->protocol_name = subbrute_protocol_file(instance->file);
+ FlipperFormat* flipper_format = flipper_format_string_alloc();
+ Stream* stream = flipper_format_get_raw_stream(flipper_format);
+
+ stream_clean(stream);
+
+ if(instance->attack == SubBruteAttackLoadFile) {
+ subbrute_protocol_file_payload(
+ stream,
+ step,
+ instance->bits,
+ instance->te,
+ instance->repeat,
+ instance->load_index,
+ instance->file_key,
+ instance->two_bytes);
+ } else {
+ subbrute_protocol_default_payload(
+ stream, instance->file, step, instance->bits, instance->te, instance->repeat);
+ }
+
+ // size_t written = stream_write_string(stream, payload);
+ // if(written <= 0) {
+ // FURI_LOG_W(TAG, "Error creating packet! EXIT");
+ // result = false;
+ // } else {
+ subbrute_worker_subghz_transmit(instance, flipper_format);
+
+ result = true;
+#if FURI_DEBUG
+ FURI_LOG_D(TAG, "Manual transmit done");
+#endif
+ // }
+
+ flipper_format_free(flipper_format);
+ // furi_string_free(payload);
+
+ return result;
+}
+
+bool subbrute_worker_is_running(SubBruteWorker* instance) {
+ return instance->worker_running;
+}
+
+bool subbrute_worker_can_manual_transmit(SubBruteWorker* instance) {
+ furi_assert(instance);
+
+ if(!instance->initiated) {
+ FURI_LOG_W(TAG, "Worker not init!");
+ return false;
+ }
+
+ return !instance->worker_running && instance->state != SubBruteWorkerStateIDLE &&
+ instance->state != SubBruteWorkerStateTx &&
+ ((furi_get_tick() - instance->last_time_tx_data) > SUBBRUTE_MANUAL_TRANSMIT_INTERVAL);
+}
+
+void subbrute_worker_set_callback(
+ SubBruteWorker* instance,
+ SubBruteWorkerCallback callback,
+ void* context) {
+ furi_assert(instance);
+
+ instance->callback = callback;
+ instance->context = context;
+}
+
+void subbrute_worker_subghz_transmit(SubBruteWorker* instance, FlipperFormat* flipper_format) {
+ while(instance->transmit_mode) {
+ furi_delay_ms(SUBBRUTE_TX_TIMEOUT);
+ }
+ instance->transmit_mode = true;
+ if(instance->transmitter != NULL) {
+ subghz_transmitter_free(instance->transmitter);
+ instance->transmitter = NULL;
+ }
+ instance->transmitter =
+ subghz_transmitter_alloc_init(instance->environment, instance->protocol_name);
+ subghz_transmitter_deserialize(instance->transmitter, flipper_format);
+ furi_hal_subghz_reset();
+ furi_hal_subghz_load_preset(instance->preset);
+ furi_hal_subghz_set_frequency_and_path(instance->frequency);
+ furi_hal_subghz_start_async_tx(subghz_transmitter_yield, instance->transmitter);
+
+ while(!furi_hal_subghz_is_async_tx_complete()) {
+ furi_delay_ms(SUBBRUTE_TX_TIMEOUT);
+ }
+ furi_hal_subghz_stop_async_tx();
+
+ furi_hal_subghz_set_path(FuriHalSubGhzPathIsolate);
+ furi_hal_subghz_sleep();
+ subghz_transmitter_free(instance->transmitter);
+ instance->transmitter = NULL;
+
+ instance->transmit_mode = false;
+}
+
+void subbrute_worker_send_callback(SubBruteWorker* instance) {
+ if(instance->callback != NULL) {
+ instance->callback(instance->context, instance->state);
+ }
+}
+
+/**
+ * Entrypoint for worker
+ *
+ * @param context SubBruteWorker*
+ * @return 0 if ok
+ */
+int32_t subbrute_worker_thread(void* context) {
+ furi_assert(context);
+ SubBruteWorker* instance = (SubBruteWorker*)context;
+
+ if(!instance->worker_running) {
+ FURI_LOG_W(TAG, "Worker is not set to running state!");
+ return -1;
+ }
+ if(instance->state != SubBruteWorkerStateReady &&
+ instance->state != SubBruteWorkerStateFinished) {
+ FURI_LOG_W(TAG, "Invalid state for running worker! State: %d", instance->state);
+ return -2;
+ }
+#ifdef FURI_DEBUG
+ FURI_LOG_I(TAG, "Worker start");
+#endif
+
+ SubBruteWorkerState local_state = instance->state = SubBruteWorkerStateTx;
+ subbrute_worker_send_callback(instance);
+
+ instance->protocol_name = subbrute_protocol_file(instance->file);
+
+ FlipperFormat* flipper_format = flipper_format_string_alloc();
+ Stream* stream = flipper_format_get_raw_stream(flipper_format);
+
+ while(instance->worker_running) {
+ stream_clean(stream);
+ if(instance->attack == SubBruteAttackLoadFile) {
+ subbrute_protocol_file_payload(
+ stream,
+ instance->step,
+ instance->bits,
+ instance->te,
+ instance->repeat,
+ instance->load_index,
+ instance->file_key,
+ instance->two_bytes);
+ } else {
+ subbrute_protocol_default_payload(
+ stream,
+ instance->file,
+ instance->step,
+ instance->bits,
+ instance->te,
+ instance->repeat);
+ }
+#ifdef FURI_DEBUG
+ //FURI_LOG_I(TAG, "Payload: %s", furi_string_get_cstr(payload));
+ //furi_delay_ms(SUBBRUTE_MANUAL_TRANSMIT_INTERVAL / 4);
+#endif
+
+ // size_t written = stream_write_stream_write_string(stream, payload);
+ // if(written <= 0) {
+ // FURI_LOG_W(TAG, "Error creating packet! BREAK");
+ // instance->worker_running = false;
+ // local_state = SubBruteWorkerStateIDLE;
+ // furi_string_free(payload);
+ // break;
+ // }
+
+ subbrute_worker_subghz_transmit(instance, flipper_format);
+
+ if(instance->step + 1 > instance->max_value) {
+#ifdef FURI_DEBUG
+ FURI_LOG_I(TAG, "Worker finished to end");
+#endif
+ local_state = SubBruteWorkerStateFinished;
+ // furi_string_free(payload);
+ break;
+ }
+ instance->step++;
+
+ // furi_string_free(payload);
+ furi_delay_ms(SUBBRUTE_TX_TIMEOUT);
+ }
+
+ flipper_format_free(flipper_format);
+
+ instance->worker_running = false; // Because we have error states
+ instance->state = local_state == SubBruteWorkerStateTx ? SubBruteWorkerStateReady :
+ local_state;
+ subbrute_worker_send_callback(instance);
+
+#ifdef FURI_DEBUG
+ FURI_LOG_I(TAG, "Worker stop");
+#endif
+ return 0;
+}
diff --git a/applications/external/subghz_bruteforcer/helpers/subbrute_worker.h b/applications/external/subghz_bruteforcer/helpers/subbrute_worker.h
new file mode 100644
index 000000000..4046f997c
--- /dev/null
+++ b/applications/external/subghz_bruteforcer/helpers/subbrute_worker.h
@@ -0,0 +1,42 @@
+#pragma once
+
+#include "../subbrute_protocols.h"
+
+typedef enum {
+ SubBruteWorkerStateIDLE,
+ SubBruteWorkerStateReady,
+ SubBruteWorkerStateTx,
+ SubBruteWorkerStateFinished
+} SubBruteWorkerState;
+
+typedef void (*SubBruteWorkerCallback)(void* context, SubBruteWorkerState state);
+
+typedef struct SubBruteWorker SubBruteWorker;
+
+SubBruteWorker* subbrute_worker_alloc();
+void subbrute_worker_free(SubBruteWorker* instance);
+uint64_t subbrute_worker_get_step(SubBruteWorker* instance);
+bool subbrute_worker_set_step(SubBruteWorker* instance, uint64_t step);
+bool subbrute_worker_is_running(SubBruteWorker* instance);
+bool subbrute_worker_init_default_attack(
+ SubBruteWorker* instance,
+ SubBruteAttacks attack_type,
+ uint64_t step,
+ const SubBruteProtocol* protocol,
+ uint8_t extra_repeats);
+bool subbrute_worker_init_file_attack(
+ SubBruteWorker* instance,
+ uint64_t step,
+ uint8_t load_index,
+ uint64_t file_key,
+ SubBruteProtocol* protocol,
+ uint8_t extra_repeats,
+ bool two_bytes);
+bool subbrute_worker_start(SubBruteWorker* instance);
+void subbrute_worker_stop(SubBruteWorker* instance);
+bool subbrute_worker_transmit_current_key(SubBruteWorker* instance, uint64_t step);
+bool subbrute_worker_can_manual_transmit(SubBruteWorker* instance);
+void subbrute_worker_set_callback(
+ SubBruteWorker* instance,
+ SubBruteWorkerCallback callback,
+ void* context);
\ No newline at end of file
diff --git a/applications/external/subghz_bruteforcer/helpers/subbrute_worker_private.h b/applications/external/subghz_bruteforcer/helpers/subbrute_worker_private.h
new file mode 100644
index 000000000..e38e77dc4
--- /dev/null
+++ b/applications/external/subghz_bruteforcer/helpers/subbrute_worker_private.h
@@ -0,0 +1,48 @@
+#pragma once
+
+#include "subbrute_worker.h"
+#include
+#include
+#include
+#include
+
+struct SubBruteWorker {
+ SubBruteWorkerState state;
+ volatile bool worker_running;
+ volatile bool initiated;
+ volatile bool transmit_mode;
+
+ // Current step
+ uint64_t step;
+
+ // SubGhz
+ FuriThread* thread;
+ SubGhzProtocolDecoderBase* decoder_result;
+ SubGhzEnvironment* environment;
+ SubGhzTransmitter* transmitter;
+ const char* protocol_name;
+
+ // Initiated values
+ SubBruteAttacks attack; // Attack state
+ uint32_t frequency;
+ FuriHalSubGhzPreset preset;
+ SubBruteFileProtocol file;
+ uint8_t bits;
+ uint32_t te;
+ uint8_t repeat;
+ uint8_t load_index; // Index of group to bruteforce in loaded file
+ uint64_t file_key;
+ uint64_t max_value; // Max step
+ bool two_bytes;
+
+ // Manual transmit
+ uint32_t last_time_tx_data;
+
+ // Callback for changed states
+ SubBruteWorkerCallback callback;
+ void* context;
+};
+
+int32_t subbrute_worker_thread(void* context);
+void subbrute_worker_subghz_transmit(SubBruteWorker* instance, FlipperFormat* flipper_format);
+void subbrute_worker_send_callback(SubBruteWorker* instance);
\ No newline at end of file
diff --git a/applications/plugins/timelapse/icons/ButtonDown_7x4.png b/applications/external/subghz_bruteforcer/images/ButtonDown_7x4.png
similarity index 100%
rename from applications/plugins/timelapse/icons/ButtonDown_7x4.png
rename to applications/external/subghz_bruteforcer/images/ButtonDown_7x4.png
diff --git a/applications/plugins/timelapse/icons/ButtonUp_7x4.png b/applications/external/subghz_bruteforcer/images/ButtonUp_7x4.png
similarity index 100%
rename from applications/plugins/timelapse/icons/ButtonUp_7x4.png
rename to applications/external/subghz_bruteforcer/images/ButtonUp_7x4.png
diff --git a/applications/external/subghz_bruteforcer/images/DolphinNice_96x59.png b/applications/external/subghz_bruteforcer/images/DolphinNice_96x59.png
new file mode 100644
index 000000000..a299d3630
Binary files /dev/null and b/applications/external/subghz_bruteforcer/images/DolphinNice_96x59.png differ
diff --git a/applications/external/subghz_bruteforcer/images/Sub1ghz_14/frame_01.png b/applications/external/subghz_bruteforcer/images/Sub1ghz_14/frame_01.png
new file mode 100644
index 000000000..52dc4ad21
Binary files /dev/null and b/applications/external/subghz_bruteforcer/images/Sub1ghz_14/frame_01.png differ
diff --git a/applications/external/subghz_bruteforcer/images/Sub1ghz_14/frame_02.png b/applications/external/subghz_bruteforcer/images/Sub1ghz_14/frame_02.png
new file mode 100644
index 000000000..2dff1c031
Binary files /dev/null and b/applications/external/subghz_bruteforcer/images/Sub1ghz_14/frame_02.png differ
diff --git a/applications/external/subghz_bruteforcer/images/Sub1ghz_14/frame_03.png b/applications/external/subghz_bruteforcer/images/Sub1ghz_14/frame_03.png
new file mode 100644
index 000000000..c1e438b01
Binary files /dev/null and b/applications/external/subghz_bruteforcer/images/Sub1ghz_14/frame_03.png differ
diff --git a/applications/external/subghz_bruteforcer/images/Sub1ghz_14/frame_04.png b/applications/external/subghz_bruteforcer/images/Sub1ghz_14/frame_04.png
new file mode 100644
index 000000000..169fb6147
Binary files /dev/null and b/applications/external/subghz_bruteforcer/images/Sub1ghz_14/frame_04.png differ
diff --git a/applications/external/subghz_bruteforcer/images/Sub1ghz_14/frame_05.png b/applications/external/subghz_bruteforcer/images/Sub1ghz_14/frame_05.png
new file mode 100644
index 000000000..79b2bc972
Binary files /dev/null and b/applications/external/subghz_bruteforcer/images/Sub1ghz_14/frame_05.png differ
diff --git a/applications/external/subghz_bruteforcer/images/Sub1ghz_14/frame_06.png b/applications/external/subghz_bruteforcer/images/Sub1ghz_14/frame_06.png
new file mode 100644
index 000000000..8fce0c44d
Binary files /dev/null and b/applications/external/subghz_bruteforcer/images/Sub1ghz_14/frame_06.png differ
diff --git a/assets/icons/MainMenu/Clock_14/frame_rate b/applications/external/subghz_bruteforcer/images/Sub1ghz_14/frame_rate
similarity index 100%
rename from assets/icons/MainMenu/Clock_14/frame_rate
rename to applications/external/subghz_bruteforcer/images/Sub1ghz_14/frame_rate
diff --git a/applications/external/subghz_bruteforcer/images/sub1_10px.png b/applications/external/subghz_bruteforcer/images/sub1_10px.png
new file mode 100644
index 000000000..5a25fdf4e
Binary files /dev/null and b/applications/external/subghz_bruteforcer/images/sub1_10px.png differ
diff --git a/applications/plugins/playlist/playlist_10px.png b/applications/external/subghz_bruteforcer/images/subbrute_10px.png
similarity index 55%
rename from applications/plugins/playlist/playlist_10px.png
rename to applications/external/subghz_bruteforcer/images/subbrute_10px.png
index fc33471f7..57d6f6a45 100644
Binary files a/applications/plugins/playlist/playlist_10px.png and b/applications/external/subghz_bruteforcer/images/subbrute_10px.png differ
diff --git a/applications/plugins/dolphinrestorer/scenes/drestorer_scene.h b/applications/external/subghz_bruteforcer/scenes/subbrute_scene.h
similarity index 65%
rename from applications/plugins/dolphinrestorer/scenes/drestorer_scene.h
rename to applications/external/subghz_bruteforcer/scenes/subbrute_scene.h
index 9843ff676..c048985e2 100644
--- a/applications/plugins/dolphinrestorer/scenes/drestorer_scene.h
+++ b/applications/external/subghz_bruteforcer/scenes/subbrute_scene.h
@@ -3,27 +3,27 @@
#include
// Generate scene id and total number
-#define ADD_SCENE(prefix, name, id) StorageMoveToSd##id,
+#define ADD_SCENE(prefix, name, id) SubBruteScene##id,
typedef enum {
-#include "drestorer_scene_config.h"
- StorageMoveToSdSceneNum,
-} StorageMoveToSdScene;
+#include "subbrute_scene_config.h"
+ SubBruteSceneNum,
+} SubBruteScene;
#undef ADD_SCENE
-extern const SceneManagerHandlers drestorer_scene_handlers;
+extern const SceneManagerHandlers subbrute_scene_handlers;
// Generate scene on_enter handlers declaration
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);
-#include "drestorer_scene_config.h"
+#include "subbrute_scene_config.h"
#undef ADD_SCENE
// Generate scene on_event handlers declaration
#define ADD_SCENE(prefix, name, id) \
bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event);
-#include "drestorer_scene_config.h"
+#include "subbrute_scene_config.h"
#undef ADD_SCENE
// Generate scene on_exit handlers declaration
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context);
-#include "drestorer_scene_config.h"
+#include "subbrute_scene_config.h"
#undef ADD_SCENE
diff --git a/applications/external/subghz_bruteforcer/scenes/subbrute_scene_config.h b/applications/external/subghz_bruteforcer/scenes/subbrute_scene_config.h
new file mode 100644
index 000000000..3541df9ac
--- /dev/null
+++ b/applications/external/subghz_bruteforcer/scenes/subbrute_scene_config.h
@@ -0,0 +1,7 @@
+ADD_SCENE(subbrute, load_file, LoadFile)
+ADD_SCENE(subbrute, load_select, LoadSelect)
+ADD_SCENE(subbrute, run_attack, RunAttack)
+ADD_SCENE(subbrute, save_name, SaveName)
+ADD_SCENE(subbrute, save_success, SaveSuccess)
+ADD_SCENE(subbrute, setup_attack, SetupAttack)
+ADD_SCENE(subbrute, start, Start)
\ No newline at end of file
diff --git a/applications/external/subghz_bruteforcer/scenes/subbrute_scene_load_file.c b/applications/external/subghz_bruteforcer/scenes/subbrute_scene_load_file.c
new file mode 100644
index 000000000..8aae1bcad
--- /dev/null
+++ b/applications/external/subghz_bruteforcer/scenes/subbrute_scene_load_file.c
@@ -0,0 +1,91 @@
+#include "../subbrute_i.h"
+#include "subbrute_scene.h"
+
+#define TAG "SubBruteSceneLoadFile"
+
+void subbrute_scene_load_file_on_enter(void* context) {
+ furi_assert(context);
+ SubBruteState* instance = (SubBruteState*)context;
+
+ // Input events and views are managed by file_browser
+ FuriString* app_folder;
+ FuriString* load_path;
+ load_path = furi_string_alloc();
+ app_folder = furi_string_alloc_set(SUBBRUTE_PATH);
+
+ DialogsFileBrowserOptions browser_options;
+ dialog_file_browser_set_basic_options(&browser_options, SUBBRUTE_FILE_EXT, &I_sub1_10px);
+
+ SubBruteFileResult load_result = SubBruteFileResultUnknown;
+ // TODO: DELETE IT
+#ifdef SUBBRUTE_FAST_TRACK
+ bool res = true;
+ furi_string_printf(load_path, "%s", "/ext/subghz/princeton.sub");
+#else
+ bool res =
+ dialog_file_browser_show(instance->dialogs, load_path, app_folder, &browser_options);
+#endif
+#ifdef FURI_DEBUG
+ FURI_LOG_D(
+ TAG,
+ "load_path: %s, app_folder: %s",
+ furi_string_get_cstr(load_path),
+ furi_string_get_cstr(app_folder));
+#endif
+ if(res) {
+ load_result =
+ subbrute_device_load_from_file(instance->device, furi_string_get_cstr(load_path));
+ if(load_result == SubBruteFileResultOk) {
+ uint8_t extra_repeats = subbrute_main_view_get_extra_repeats(instance->view_main);
+
+ load_result = subbrute_device_attack_set(
+ instance->device, SubBruteAttackLoadFile, extra_repeats);
+ if(load_result == SubBruteFileResultOk) {
+ if(!subbrute_worker_init_file_attack(
+ instance->worker,
+ instance->device->current_step,
+ instance->device->bit_index,
+ instance->device->key_from_file,
+ instance->device->file_protocol_info,
+ extra_repeats,
+ instance->device->two_bytes)) {
+ furi_crash("Invalid attack set!");
+ }
+ // Ready to run!
+ FURI_LOG_I(TAG, "Ready to run");
+ res = true;
+ }
+ }
+
+ if(load_result == SubBruteFileResultOk) {
+ scene_manager_next_scene(instance->scene_manager, SubBruteSceneLoadSelect);
+ } else {
+ FURI_LOG_E(TAG, "Returned error: %d", load_result);
+
+ FuriString* dialog_msg;
+ dialog_msg = furi_string_alloc();
+ furi_string_cat_printf(
+ dialog_msg, "Cannot parse\nfile: %s", subbrute_device_error_get_desc(load_result));
+ dialog_message_show_storage_error(instance->dialogs, furi_string_get_cstr(dialog_msg));
+ furi_string_free(dialog_msg);
+ scene_manager_search_and_switch_to_previous_scene(
+ instance->scene_manager, SubBruteSceneStart);
+ }
+ } else {
+ scene_manager_search_and_switch_to_previous_scene(
+ instance->scene_manager, SubBruteSceneStart);
+ }
+
+ furi_string_free(app_folder);
+ furi_string_free(load_path);
+}
+
+void subbrute_scene_load_file_on_exit(void* context) {
+ UNUSED(context);
+}
+
+bool subbrute_scene_load_file_on_event(void* context, SceneManagerEvent event) {
+ UNUSED(context);
+ UNUSED(event);
+ return false;
+}
\ No newline at end of file
diff --git a/applications/external/subghz_bruteforcer/scenes/subbrute_scene_load_select.c b/applications/external/subghz_bruteforcer/scenes/subbrute_scene_load_select.c
new file mode 100644
index 000000000..d018e8b4d
--- /dev/null
+++ b/applications/external/subghz_bruteforcer/scenes/subbrute_scene_load_select.c
@@ -0,0 +1,82 @@
+#include "../subbrute_i.h"
+#include "subbrute_scene.h"
+
+#define TAG "SubBruteSceneStart"
+
+void subbrute_scene_load_select_callback(SubBruteCustomEvent event, void* context) {
+ furi_assert(context);
+
+ SubBruteState* instance = (SubBruteState*)context;
+ view_dispatcher_send_custom_event(instance->view_dispatcher, event);
+}
+
+void subbrute_scene_load_select_on_enter(void* context) {
+ furi_assert(context);
+#ifdef FURI_DEBUG
+ FURI_LOG_I(TAG, "subbrute_scene_load_select_on_enter");
+#endif
+ SubBruteState* instance = (SubBruteState*)context;
+ SubBruteMainView* view = instance->view_main;
+
+ instance->current_view = SubBruteViewMain;
+ subbrute_main_view_set_callback(view, subbrute_scene_load_select_callback, instance);
+ subbrute_main_view_set_index(
+ view, 7, true, instance->device->two_bytes, instance->device->key_from_file);
+
+ view_dispatcher_switch_to_view(instance->view_dispatcher, instance->current_view);
+}
+
+void subbrute_scene_load_select_on_exit(void* context) {
+ UNUSED(context);
+#ifdef FURI_DEBUG
+ FURI_LOG_I(TAG, "subbrute_scene_load_select_on_exit");
+#endif
+}
+
+bool subbrute_scene_load_select_on_event(void* context, SceneManagerEvent event) {
+ SubBruteState* instance = (SubBruteState*)context;
+ bool consumed = false;
+
+ if(event.type == SceneManagerEventTypeCustom) {
+ if(event.event == SubBruteCustomEventTypeIndexSelected) {
+ /*#ifdef FURI_DEBUG && !SUBBRUTE_FAST_TRACK
+ view_dispatcher_stop(instance->view_dispatcher);
+ consumed = true;
+#else*/
+ instance->device->current_step = 0;
+ instance->device->bit_index = subbrute_main_view_get_index(instance->view_main);
+ instance->device->two_bytes = subbrute_main_view_get_two_bytes(instance->view_main);
+ uint8_t extra_repeats = subbrute_main_view_get_extra_repeats(instance->view_main);
+ instance->device->max_value = subbrute_protocol_calc_max_value(
+ instance->device->attack,
+ instance->device->bit_index,
+ instance->device->two_bytes);
+
+ if(!subbrute_worker_init_file_attack(
+ instance->worker,
+ instance->device->current_step,
+ instance->device->bit_index,
+ instance->device->key_from_file,
+ instance->device->file_protocol_info,
+ extra_repeats,
+ instance->device->two_bytes)) {
+ furi_crash("Invalid attack set!");
+ }
+ scene_manager_next_scene(instance->scene_manager, SubBruteSceneSetupAttack);
+ /*#endif*/
+ consumed = true;
+ } /* else if(event.event == SubBruteCustomEventTypeChangeStepUp) {
+ instance->device->two_bytes = true;
+ } else if(event.event == SubBruteCustomEventTypeChangeStepDown) {
+ instance->device->two_bytes = false;
+ }*/
+ } else if(event.type == SceneManagerEventTypeBack) {
+ if(!scene_manager_search_and_switch_to_previous_scene(
+ instance->scene_manager, SubBruteSceneStart)) {
+ scene_manager_next_scene(instance->scene_manager, SubBruteSceneStart);
+ }
+ consumed = true;
+ }
+
+ return consumed;
+}
\ No newline at end of file
diff --git a/applications/external/subghz_bruteforcer/scenes/subbrute_scene_run_attack.c b/applications/external/subghz_bruteforcer/scenes/subbrute_scene_run_attack.c
new file mode 100644
index 000000000..2f22c25d4
--- /dev/null
+++ b/applications/external/subghz_bruteforcer/scenes/subbrute_scene_run_attack.c
@@ -0,0 +1,104 @@
+#include "../subbrute_i.h"
+#include "subbrute_scene.h"
+
+#define TAG "SubBruteSceneRunAttack"
+
+static void subbrute_scene_run_attack_callback(SubBruteCustomEvent event, void* context) {
+ furi_assert(context);
+
+ SubBruteState* instance = (SubBruteState*)context;
+ view_dispatcher_send_custom_event(instance->view_dispatcher, event);
+}
+
+static void
+ subbrute_scene_run_attack_device_state_changed(void* context, SubBruteWorkerState state) {
+ furi_assert(context);
+
+ SubBruteState* instance = (SubBruteState*)context;
+
+ if(state == SubBruteWorkerStateIDLE) {
+ // Can't be IDLE on this step!
+ view_dispatcher_send_custom_event(instance->view_dispatcher, SubBruteCustomEventTypeError);
+ } else if(state == SubBruteWorkerStateFinished) {
+ view_dispatcher_send_custom_event(
+ instance->view_dispatcher, SubBruteCustomEventTypeTransmitFinished);
+ }
+}
+void subbrute_scene_run_attack_on_exit(void* context) {
+ furi_assert(context);
+ SubBruteState* instance = (SubBruteState*)context;
+
+ notification_message(instance->notifications, &sequence_blink_stop);
+ subbrute_worker_stop(instance->worker);
+}
+
+void subbrute_scene_run_attack_on_enter(void* context) {
+ furi_assert(context);
+ SubBruteState* instance = (SubBruteState*)context;
+ SubBruteAttackView* view = instance->view_attack;
+
+ instance->current_view = SubBruteViewAttack;
+ subbrute_attack_view_set_callback(view, subbrute_scene_run_attack_callback, instance);
+ view_dispatcher_switch_to_view(instance->view_dispatcher, instance->current_view);
+
+ subbrute_worker_set_callback(
+ instance->worker, subbrute_scene_run_attack_device_state_changed, instance);
+
+ if(!subbrute_worker_is_running(instance->worker)) {
+ subbrute_worker_set_step(instance->worker, instance->device->current_step);
+ if(!subbrute_worker_start(instance->worker)) {
+ view_dispatcher_send_custom_event(
+ instance->view_dispatcher, SubBruteCustomEventTypeError);
+ } else {
+ notification_message(instance->notifications, &sequence_single_vibro);
+ notification_message(instance->notifications, &sequence_blink_start_yellow);
+ }
+ }
+}
+
+bool subbrute_scene_run_attack_on_event(void* context, SceneManagerEvent event) {
+ SubBruteState* instance = (SubBruteState*)context;
+ SubBruteAttackView* view = instance->view_attack;
+
+ bool consumed = false;
+
+ if(event.type == SceneManagerEventTypeCustom) {
+ uint64_t step = subbrute_worker_get_step(instance->worker);
+ instance->device->current_step = step;
+ subbrute_attack_view_set_current_step(view, step);
+
+ if(event.event == SubBruteCustomEventTypeTransmitFinished) {
+ notification_message(instance->notifications, &sequence_display_backlight_on);
+ notification_message(instance->notifications, &sequence_double_vibro);
+
+ scene_manager_next_scene(instance->scene_manager, SubBruteSceneSetupAttack);
+ } else if(
+ event.event == SubBruteCustomEventTypeTransmitNotStarted ||
+ event.event == SubBruteCustomEventTypeBackPressed) {
+ if(subbrute_worker_is_running(instance->worker)) {
+ // Notify
+ notification_message(instance->notifications, &sequence_single_vibro);
+ }
+ // Stop transmit
+ scene_manager_search_and_switch_to_previous_scene(
+ instance->scene_manager, SubBruteSceneSetupAttack);
+ } else if(event.event == SubBruteCustomEventTypeError) {
+ notification_message(instance->notifications, &sequence_error);
+
+ // Stop transmit
+ scene_manager_search_and_switch_to_previous_scene(
+ instance->scene_manager, SubBruteSceneSetupAttack);
+ } else if(event.event == SubBruteCustomEventTypeUpdateView) {
+ //subbrute_attack_view_set_current_step(view, instance->device->current_step);
+ }
+ consumed = true;
+ } else if(event.type == SceneManagerEventTypeTick) {
+ uint64_t step = subbrute_worker_get_step(instance->worker);
+ instance->device->current_step = step;
+ subbrute_attack_view_set_current_step(view, step);
+
+ consumed = true;
+ }
+
+ return consumed;
+}
diff --git a/applications/external/subghz_bruteforcer/scenes/subbrute_scene_save_name.c b/applications/external/subghz_bruteforcer/scenes/subbrute_scene_save_name.c
new file mode 100644
index 000000000..bb129e948
--- /dev/null
+++ b/applications/external/subghz_bruteforcer/scenes/subbrute_scene_save_name.c
@@ -0,0 +1,84 @@
+#include "../subbrute_i.h"
+#include "subbrute_scene.h"
+#include
+
+#define TAG "SubBruteSceneSaveFile"
+
+void subbrute_scene_save_name_on_enter(void* context) {
+ SubBruteState* instance = (SubBruteState*)context;
+
+ // Setup view
+ TextInput* text_input = instance->text_input;
+ set_random_name(instance->text_store, sizeof(instance->text_store));
+
+ text_input_set_header_text(text_input, "Name of file");
+ text_input_set_result_callback(
+ text_input,
+ subbrute_text_input_callback,
+ instance,
+ instance->text_store,
+ SUBBRUTE_MAX_LEN_NAME,
+ true);
+
+ furi_string_reset(instance->file_path);
+ furi_string_set_str(instance->file_path, SUBBRUTE_PATH);
+
+ ValidatorIsFile* validator_is_file = validator_is_file_alloc_init(
+ furi_string_get_cstr(instance->file_path), SUBBRUTE_FILE_EXT, "");
+ text_input_set_validator(text_input, validator_is_file_callback, validator_is_file);
+
+ view_dispatcher_switch_to_view(instance->view_dispatcher, SubBruteViewTextInput);
+}
+
+bool subbrute_scene_save_name_on_event(void* context, SceneManagerEvent event) {
+ SubBruteState* instance = (SubBruteState*)context;
+ bool consumed = false;
+
+ if(event.type == SceneManagerEventTypeBack) {
+ scene_manager_previous_scene(instance->scene_manager);
+ return true;
+ } else if(
+ event.type == SceneManagerEventTypeCustom &&
+ event.event == SubBruteCustomEventTypeTextEditDone) {
+#ifdef FURI_DEBUG
+ FURI_LOG_D(TAG, "Saving: %s", instance->text_store);
+#endif
+ bool success = false;
+ if(strcmp(instance->text_store, "")) {
+ furi_string_reset(instance->file_path);
+ furi_string_cat_printf(
+ instance->file_path,
+ "%s/%s%s",
+ SUBBRUTE_PATH,
+ instance->text_store,
+ SUBBRUTE_FILE_EXT);
+
+ if(subbrute_device_save_file(
+ instance->device, furi_string_get_cstr(instance->file_path))) {
+ scene_manager_next_scene(instance->scene_manager, SubBruteSceneSaveSuccess);
+ success = true;
+ consumed = true;
+ }
+ }
+
+ if(!success) {
+ dialog_message_show_storage_error(instance->dialogs, "Error during saving!");
+ consumed = scene_manager_search_and_switch_to_previous_scene(
+ instance->scene_manager, SubBruteSceneSetupAttack);
+ }
+ }
+ return consumed;
+}
+
+void subbrute_scene_save_name_on_exit(void* context) {
+ SubBruteState* instance = (SubBruteState*)context;
+
+ // Clear view
+ void* validator_context = text_input_get_validator_callback_context(instance->text_input);
+ text_input_set_validator(instance->text_input, NULL, NULL);
+ validator_is_file_free(validator_context);
+
+ text_input_reset(instance->text_input);
+
+ furi_string_reset(instance->file_path);
+}
diff --git a/applications/external/subghz_bruteforcer/scenes/subbrute_scene_save_success.c b/applications/external/subghz_bruteforcer/scenes/subbrute_scene_save_success.c
new file mode 100644
index 000000000..20b1a0de4
--- /dev/null
+++ b/applications/external/subghz_bruteforcer/scenes/subbrute_scene_save_success.c
@@ -0,0 +1,51 @@
+#include "../subbrute_i.h"
+#include "subbrute_scene.h"
+
+void subbrute_scene_save_success_on_enter(void* context) {
+ furi_assert(context);
+ SubBruteState* instance = context;
+
+ // Setup view
+ Popup* popup = instance->popup;
+ popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59);
+ popup_set_header(popup, "Saved!", 13, 22, AlignLeft, AlignBottom);
+ popup_set_timeout(popup, 1500);
+ popup_set_context(popup, instance);
+ popup_set_callback(popup, subbrute_popup_closed_callback);
+ popup_enable_timeout(popup);
+ view_dispatcher_switch_to_view(instance->view_dispatcher, SubBruteViewPopup);
+}
+
+bool subbrute_scene_save_success_on_event(void* context, SceneManagerEvent event) {
+ furi_assert(context);
+
+ SubBruteState* instance = (SubBruteState*)context;
+ //SubBruteMainView* view = instance->view_main;
+
+ if(event.type == SceneManagerEventTypeCustom) {
+ if(event.event == SubBruteCustomEventTypePopupClosed) {
+ if(!scene_manager_search_and_switch_to_previous_scene(
+ instance->scene_manager, SubBruteSceneSetupAttack)) {
+ scene_manager_next_scene(instance->scene_manager, SubBruteSceneStart);
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+void subbrute_scene_save_success_on_exit(void* context) {
+ furi_assert(context);
+
+ SubBruteState* instance = (SubBruteState*)context;
+
+ // Clear view
+ Popup* popup = instance->popup;
+ popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom);
+ popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop);
+ popup_set_icon(popup, 0, 0, NULL);
+ popup_set_callback(popup, NULL);
+ popup_set_context(popup, NULL);
+ popup_set_timeout(popup, 0);
+ popup_disable_timeout(popup);
+}
diff --git a/applications/external/subghz_bruteforcer/scenes/subbrute_scene_setup_attack.c b/applications/external/subghz_bruteforcer/scenes/subbrute_scene_setup_attack.c
new file mode 100644
index 000000000..c2877c7cb
--- /dev/null
+++ b/applications/external/subghz_bruteforcer/scenes/subbrute_scene_setup_attack.c
@@ -0,0 +1,138 @@
+#include "../subbrute_i.h"
+#include "subbrute_scene.h"
+
+#define TAG "SubBruteSceneSetupAttack"
+
+static void subbrute_scene_setup_attack_callback(SubBruteCustomEvent event, void* context) {
+ furi_assert(context);
+
+ SubBruteState* instance = (SubBruteState*)context;
+ view_dispatcher_send_custom_event(instance->view_dispatcher, event);
+}
+
+static void
+ subbrute_scene_setup_attack_device_state_changed(void* context, SubBruteWorkerState state) {
+ furi_assert(context);
+
+ SubBruteState* instance = (SubBruteState*)context;
+
+ if(state == SubBruteWorkerStateIDLE) {
+ // Can't be IDLE on this step!
+ view_dispatcher_send_custom_event(instance->view_dispatcher, SubBruteCustomEventTypeError);
+ }
+}
+
+void subbrute_scene_setup_attack_on_enter(void* context) {
+ furi_assert(context);
+ SubBruteState* instance = (SubBruteState*)context;
+ SubBruteAttackView* view = instance->view_attack;
+
+ notification_message(instance->notifications, &sequence_reset_vibro);
+
+#ifdef FURI_DEBUG
+ FURI_LOG_D(TAG, "Enter Attack: %s", subbrute_protocol_name(instance->device->attack));
+#endif
+
+ subbrute_worker_set_callback(
+ instance->worker, subbrute_scene_setup_attack_device_state_changed, context);
+ if(subbrute_worker_is_running(instance->worker)) {
+ subbrute_worker_stop(instance->worker);
+ instance->device->current_step = subbrute_worker_get_step(instance->worker);
+ }
+
+ subbrute_attack_view_init_values(
+ view,
+ instance->device->attack,
+ instance->device->max_value,
+ instance->device->current_step,
+ false,
+ instance->device->extra_repeats);
+
+ instance->current_view = SubBruteViewAttack;
+ subbrute_attack_view_set_callback(view, subbrute_scene_setup_attack_callback, instance);
+ view_dispatcher_switch_to_view(instance->view_dispatcher, instance->current_view);
+}
+
+void subbrute_scene_setup_attack_on_exit(void* context) {
+ furi_assert(context);
+#ifdef FURI_DEBUG
+ FURI_LOG_D(TAG, "subbrute_scene_setup_attack_on_exit");
+#endif
+ SubBruteState* instance = (SubBruteState*)context;
+ subbrute_worker_stop(instance->worker);
+ notification_message(instance->notifications, &sequence_blink_stop);
+ notification_message(instance->notifications, &sequence_reset_vibro);
+}
+
+bool subbrute_scene_setup_attack_on_event(void* context, SceneManagerEvent event) {
+ SubBruteState* instance = (SubBruteState*)context;
+ SubBruteAttackView* view = instance->view_attack;
+ bool consumed = false;
+
+ if(event.type == SceneManagerEventTypeCustom) {
+ if(event.event == SubBruteCustomEventTypeTransmitStarted) {
+ scene_manager_next_scene(instance->scene_manager, SubBruteSceneRunAttack);
+ } else if(event.event == SubBruteCustomEventTypeSaveFile) {
+ subbrute_attack_view_init_values(
+ view,
+ instance->device->attack,
+ instance->device->max_value,
+ instance->device->current_step,
+ false,
+ instance->device->extra_repeats);
+ scene_manager_next_scene(instance->scene_manager, SubBruteSceneSaveName);
+ } else if(event.event == SubBruteCustomEventTypeBackPressed) {
+ subbrute_attack_view_init_values(
+ view,
+ instance->device->attack,
+ instance->device->max_value,
+ instance->device->current_step,
+ false,
+ instance->device->extra_repeats);
+ scene_manager_next_scene(instance->scene_manager, SubBruteSceneStart);
+ } else if(event.event == SubBruteCustomEventTypeError) {
+ notification_message(instance->notifications, &sequence_error);
+ } else if(event.event == SubBruteCustomEventTypeTransmitCustom) {
+ // We can transmit only in not working states
+ if(subbrute_worker_can_manual_transmit(instance->worker)) {
+ // MANUAL Transmit!
+ // Blink
+ notification_message(instance->notifications, &sequence_blink_green_100);
+ subbrute_worker_transmit_current_key(
+ instance->worker, instance->device->current_step);
+ // Stop
+ notification_message(instance->notifications, &sequence_blink_stop);
+ }
+ } else if(event.event == SubBruteCustomEventTypeChangeStepUp) {
+ // +1
+ uint64_t step = subbrute_device_add_step(instance->device, 1);
+ subbrute_worker_set_step(instance->worker, step);
+ subbrute_attack_view_set_current_step(view, step);
+ } else if(event.event == SubBruteCustomEventTypeChangeStepUpMore) {
+ // +50
+ uint64_t step = subbrute_device_add_step(instance->device, 50);
+ subbrute_worker_set_step(instance->worker, step);
+ subbrute_attack_view_set_current_step(view, step);
+ } else if(event.event == SubBruteCustomEventTypeChangeStepDown) {
+ // -1
+ uint64_t step = subbrute_device_add_step(instance->device, -1);
+ subbrute_worker_set_step(instance->worker, step);
+ subbrute_attack_view_set_current_step(view, step);
+ } else if(event.event == SubBruteCustomEventTypeChangeStepDownMore) {
+ // -50
+ uint64_t step = subbrute_device_add_step(instance->device, -50);
+ subbrute_worker_set_step(instance->worker, step);
+ subbrute_attack_view_set_current_step(view, step);
+ }
+
+ consumed = true;
+ } else if(event.type == SceneManagerEventTypeTick) {
+ if(subbrute_worker_is_running(instance->worker)) {
+ instance->device->current_step = subbrute_worker_get_step(instance->worker);
+ }
+ subbrute_attack_view_set_current_step(view, instance->device->current_step);
+ consumed = true;
+ }
+
+ return consumed;
+}
diff --git a/applications/external/subghz_bruteforcer/scenes/subbrute_scene_start.c b/applications/external/subghz_bruteforcer/scenes/subbrute_scene_start.c
new file mode 100644
index 000000000..256762d92
--- /dev/null
+++ b/applications/external/subghz_bruteforcer/scenes/subbrute_scene_start.c
@@ -0,0 +1,89 @@
+#include "../subbrute_i.h"
+#include "subbrute_scene.h"
+
+#define TAG "SubBruteSceneStart"
+
+void subbrute_scene_start_callback(SubBruteCustomEvent event, void* context) {
+ furi_assert(context);
+
+ SubBruteState* instance = (SubBruteState*)context;
+#ifdef FURI_DEBUG
+ FURI_LOG_D(TAG, "subbrute_scene_start_callback");
+#endif
+ view_dispatcher_send_custom_event(instance->view_dispatcher, event);
+}
+
+void subbrute_scene_start_on_enter(void* context) {
+ furi_assert(context);
+#ifdef FURI_DEBUG
+ FURI_LOG_I(TAG, "subbrute_scene_start_on_enter");
+#endif
+ SubBruteState* instance = (SubBruteState*)context;
+ SubBruteMainView* view = instance->view_main;
+
+ instance->current_view = SubBruteViewMain;
+ subbrute_main_view_set_callback(view, subbrute_scene_start_callback, instance);
+ subbrute_main_view_set_index(
+ view, instance->device->attack, false, instance->device->two_bytes, 0);
+
+ view_dispatcher_switch_to_view(instance->view_dispatcher, instance->current_view);
+
+ // TODO: DELETE IT
+#ifdef SUBBRUTE_FAST_TRACK
+ scene_manager_next_scene(instance->scene_manager, SubBruteSceneLoadFile);
+#endif
+}
+
+void subbrute_scene_start_on_exit(void* context) {
+ UNUSED(context);
+#ifdef FURI_DEBUG
+ FURI_LOG_I(TAG, "subbrute_scene_start_on_exit");
+#endif
+}
+
+bool subbrute_scene_start_on_event(void* context, SceneManagerEvent event) {
+ SubBruteState* instance = (SubBruteState*)context;
+ bool consumed = false;
+
+ if(event.type == SceneManagerEventTypeCustom) {
+#ifdef FURI_DEBUG
+ FURI_LOG_D(
+ TAG,
+ "Event: %ld, SubBruteCustomEventTypeMenuSelected: %s, SubBruteCustomEventTypeLoadFile: %s",
+ event.event,
+ event.event == SubBruteCustomEventTypeMenuSelected ? "true" : "false",
+ event.event == SubBruteCustomEventTypeLoadFile ? "true" : "false");
+#endif
+ if(event.event == SubBruteCustomEventTypeMenuSelected) {
+ SubBruteAttacks attack = subbrute_main_view_get_index(instance->view_main);
+ uint8_t extra_repeats = subbrute_main_view_get_extra_repeats(instance->view_main);
+
+ if((subbrute_device_attack_set(instance->device, attack, extra_repeats) !=
+ SubBruteFileResultOk) ||
+ (!subbrute_worker_init_default_attack(
+ instance->worker,
+ attack,
+ instance->device->current_step,
+ instance->device->protocol_info,
+ instance->device->extra_repeats))) {
+ furi_crash("Invalid attack set!");
+ }
+ scene_manager_next_scene(instance->scene_manager, SubBruteSceneSetupAttack);
+
+ consumed = true;
+ } else if(event.event == SubBruteCustomEventTypeLoadFile) {
+ //uint8_t extra_repeats = subbrute_main_view_get_extra_repeats(instance->view_main);
+
+ //instance->device->extra_repeats = extra_repeats;
+ scene_manager_next_scene(instance->scene_manager, SubBruteSceneLoadFile);
+ consumed = true;
+ }
+ } else if(event.type == SceneManagerEventTypeBack) {
+ //exit app
+ scene_manager_stop(instance->scene_manager);
+ view_dispatcher_stop(instance->view_dispatcher);
+ consumed = true;
+ }
+
+ return consumed;
+}
\ No newline at end of file
diff --git a/applications/external/subghz_bruteforcer/scenes/subbute_scene.c b/applications/external/subghz_bruteforcer/scenes/subbute_scene.c
new file mode 100644
index 000000000..6d9ba9799
--- /dev/null
+++ b/applications/external/subghz_bruteforcer/scenes/subbute_scene.c
@@ -0,0 +1,30 @@
+#include "subbrute_scene.h"
+
+// Generate scene on_enter handlers array
+#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,
+void (*const subbrute_on_enter_handlers[])(void*) = {
+#include "subbrute_scene_config.h"
+};
+#undef ADD_SCENE
+
+// Generate scene on_event handlers array
+#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event,
+bool (*const subbrute_on_event_handlers[])(void* context, SceneManagerEvent event) = {
+#include "subbrute_scene_config.h"
+};
+#undef ADD_SCENE
+
+// Generate scene on_exit handlers array
+#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit,
+void (*const subbrute_on_exit_handlers[])(void* context) = {
+#include "subbrute_scene_config.h"
+};
+#undef ADD_SCENE
+
+// Initialize scene handlers configuration structure
+const SceneManagerHandlers subbrute_scene_handlers = {
+ .on_enter_handlers = subbrute_on_enter_handlers,
+ .on_event_handlers = subbrute_on_event_handlers,
+ .on_exit_handlers = subbrute_on_exit_handlers,
+ .scene_num = SubBruteSceneNum,
+};
diff --git a/applications/external/subghz_bruteforcer/subbrute.c b/applications/external/subghz_bruteforcer/subbrute.c
new file mode 100644
index 000000000..59d2c1ccb
--- /dev/null
+++ b/applications/external/subghz_bruteforcer/subbrute.c
@@ -0,0 +1,202 @@
+#include "subbrute_i.h"
+#include "subbrute_custom_event.h"
+#include "scenes/subbrute_scene.h"
+#include
+
+#define TAG "SubBruteApp"
+
+static bool subbrute_custom_event_callback(void* context, uint32_t event) {
+ furi_assert(context);
+ SubBruteState* instance = context;
+ return scene_manager_handle_custom_event(instance->scene_manager, event);
+}
+
+static bool subbrute_back_event_callback(void* context) {
+ furi_assert(context);
+ SubBruteState* instance = context;
+ return scene_manager_handle_back_event(instance->scene_manager);
+}
+
+static void subbrute_tick_event_callback(void* context) {
+ furi_assert(context);
+ SubBruteState* instance = context;
+ scene_manager_handle_tick_event(instance->scene_manager);
+}
+
+SubBruteState* subbrute_alloc() {
+ SubBruteState* instance = malloc(sizeof(SubBruteState));
+
+ memset(instance->text_store, 0, sizeof(instance->text_store));
+ instance->file_path = furi_string_alloc();
+
+ instance->scene_manager = scene_manager_alloc(&subbrute_scene_handlers, instance);
+ instance->view_dispatcher = view_dispatcher_alloc();
+
+ instance->gui = furi_record_open(RECORD_GUI);
+
+ view_dispatcher_enable_queue(instance->view_dispatcher);
+ view_dispatcher_set_event_callback_context(instance->view_dispatcher, instance);
+ view_dispatcher_set_custom_event_callback(
+ instance->view_dispatcher, subbrute_custom_event_callback);
+ view_dispatcher_set_navigation_event_callback(
+ instance->view_dispatcher, subbrute_back_event_callback);
+ view_dispatcher_set_tick_event_callback(
+ instance->view_dispatcher, subbrute_tick_event_callback, 100);
+
+ //Dialog
+ instance->dialogs = furi_record_open(RECORD_DIALOGS);
+
+ // Notifications
+ instance->notifications = furi_record_open(RECORD_NOTIFICATION);
+
+ // Devices
+ instance->device = subbrute_device_alloc();
+
+ // SubBruteWorker
+ instance->worker = subbrute_worker_alloc();
+
+ // TextInput
+ instance->text_input = text_input_alloc();
+ view_dispatcher_add_view(
+ instance->view_dispatcher,
+ SubBruteViewTextInput,
+ text_input_get_view(instance->text_input));
+
+ // Custom Widget
+ instance->widget = widget_alloc();
+ view_dispatcher_add_view(
+ instance->view_dispatcher, SubBruteViewWidget, widget_get_view(instance->widget));
+
+ // Popup
+ instance->popup = popup_alloc();
+ view_dispatcher_add_view(
+ instance->view_dispatcher, SubBruteViewPopup, popup_get_view(instance->popup));
+
+ // ViewStack
+ instance->view_stack = view_stack_alloc();
+ view_dispatcher_add_view(
+ instance->view_dispatcher, SubBruteViewStack, view_stack_get_view(instance->view_stack));
+
+ // SubBruteMainView
+ instance->view_main = subbrute_main_view_alloc();
+ view_dispatcher_add_view(
+ instance->view_dispatcher,
+ SubBruteViewMain,
+ subbrute_main_view_get_view(instance->view_main));
+
+ // SubBruteAttackView
+ instance->view_attack = subbrute_attack_view_alloc();
+ view_dispatcher_add_view(
+ instance->view_dispatcher,
+ SubBruteViewAttack,
+ subbrute_attack_view_get_view(instance->view_attack));
+
+ //instance->flipper_format = flipper_format_string_alloc();
+ //instance->environment = subghz_environment_alloc();
+
+ return instance;
+}
+
+void subbrute_free(SubBruteState* instance) {
+ furi_assert(instance);
+
+ // SubBruteWorker
+ subbrute_worker_stop(instance->worker);
+ subbrute_worker_free(instance->worker);
+
+ // SubBruteDevice
+ subbrute_device_free(instance->device);
+
+ // Notifications
+ notification_message(instance->notifications, &sequence_blink_stop);
+ furi_record_close(RECORD_NOTIFICATION);
+ instance->notifications = NULL;
+
+ // View Main
+ view_dispatcher_remove_view(instance->view_dispatcher, SubBruteViewMain);
+ subbrute_main_view_free(instance->view_main);
+
+ // View Attack
+ view_dispatcher_remove_view(instance->view_dispatcher, SubBruteViewAttack);
+ subbrute_attack_view_free(instance->view_attack);
+
+ // TextInput
+ view_dispatcher_remove_view(instance->view_dispatcher, SubBruteViewTextInput);
+ text_input_free(instance->text_input);
+
+ // Custom Widget
+ view_dispatcher_remove_view(instance->view_dispatcher, SubBruteViewWidget);
+ widget_free(instance->widget);
+
+ // Popup
+ view_dispatcher_remove_view(instance->view_dispatcher, SubBruteViewPopup);
+ popup_free(instance->popup);
+
+ // ViewStack
+ view_dispatcher_remove_view(instance->view_dispatcher, SubBruteViewStack);
+ view_stack_free(instance->view_stack);
+
+ //Dialog
+ furi_record_close(RECORD_DIALOGS);
+ instance->dialogs = NULL;
+
+ // Scene manager
+ scene_manager_free(instance->scene_manager);
+
+ // View Dispatcher
+ view_dispatcher_free(instance->view_dispatcher);
+
+ // GUI
+ furi_record_close(RECORD_GUI);
+ instance->gui = NULL;
+
+ furi_string_free(instance->file_path);
+
+ // The rest
+ free(instance);
+}
+
+void subbrute_text_input_callback(void* context) {
+ furi_assert(context);
+ SubBruteState* instance = context;
+ view_dispatcher_send_custom_event(
+ instance->view_dispatcher, SubBruteCustomEventTypeTextEditDone);
+}
+
+void subbrute_popup_closed_callback(void* context) {
+ furi_assert(context);
+ SubBruteState* instance = context;
+ view_dispatcher_send_custom_event(
+ instance->view_dispatcher, SubBruteCustomEventTypePopupClosed);
+}
+
+// ENTRYPOINT
+int32_t subbrute_app(void* p) {
+ UNUSED(p);
+
+ DOLPHIN_DEED(DolphinDeedPluginStart);
+ SubBruteState* instance = subbrute_alloc();
+ view_dispatcher_attach_to_gui(
+ instance->view_dispatcher, instance->gui, ViewDispatcherTypeFullscreen);
+ scene_manager_next_scene(instance->scene_manager, SubBruteSceneStart);
+
+ // Enable power for External CC1101 if it is connected
+ furi_hal_subghz_enable_ext_power();
+ // Auto switch to internal radio if external radio is not available
+ furi_delay_ms(15);
+ if(!furi_hal_subghz_check_radio()) {
+ furi_hal_subghz_set_radio_type(SubGhzRadioInternal);
+ }
+
+ furi_hal_power_suppress_charge_enter();
+
+ notification_message(instance->notifications, &sequence_display_backlight_on);
+ view_dispatcher_run(instance->view_dispatcher);
+ furi_hal_power_suppress_charge_exit();
+ // Disable power for External CC1101 if it was enabled and module is connected
+ furi_hal_subghz_disable_ext_power();
+
+ subbrute_free(instance);
+
+ return 0;
+}
diff --git a/applications/external/subghz_bruteforcer/subbrute.h b/applications/external/subghz_bruteforcer/subbrute.h
new file mode 100644
index 000000000..5fedb9158
--- /dev/null
+++ b/applications/external/subghz_bruteforcer/subbrute.h
@@ -0,0 +1,3 @@
+#pragma once
+
+typedef struct SubBruteState SubBruteState;
\ No newline at end of file
diff --git a/applications/external/subghz_bruteforcer/subbrute_custom_event.h b/applications/external/subghz_bruteforcer/subbrute_custom_event.h
new file mode 100644
index 000000000..2864f8934
--- /dev/null
+++ b/applications/external/subghz_bruteforcer/subbrute_custom_event.h
@@ -0,0 +1,26 @@
+#pragma once
+
+typedef enum {
+ // Reserve first 100 events for button types and indexes, starting from 0
+ SubBruteCustomEventTypeReserved = 100,
+
+ SubBruteCustomEventTypeBackPressed,
+ SubBruteCustomEventTypeIndexSelected,
+ SubBruteCustomEventTypeTransmitStarted,
+ SubBruteCustomEventTypeError,
+ SubBruteCustomEventTypeTransmitFinished,
+ SubBruteCustomEventTypeTransmitNotStarted,
+ SubBruteCustomEventTypeTransmitCustom,
+ SubBruteCustomEventTypeSaveFile,
+ SubBruteCustomEventTypeUpdateView,
+ SubBruteCustomEventTypeChangeStepUp,
+ SubBruteCustomEventTypeChangeStepDown,
+ SubBruteCustomEventTypeChangeStepUpMore,
+ SubBruteCustomEventTypeChangeStepDownMore,
+
+ SubBruteCustomEventTypeMenuSelected,
+ SubBruteCustomEventTypeTextEditDone,
+ SubBruteCustomEventTypePopupClosed,
+
+ SubBruteCustomEventTypeLoadFile,
+} SubBruteCustomEvent;
\ No newline at end of file
diff --git a/applications/external/subghz_bruteforcer/subbrute_device.c b/applications/external/subghz_bruteforcer/subbrute_device.c
new file mode 100644
index 000000000..0971c380e
--- /dev/null
+++ b/applications/external/subghz_bruteforcer/subbrute_device.c
@@ -0,0 +1,460 @@
+#include "subbrute_device.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define TAG "SubBruteDevice"
+
+SubBruteDevice* subbrute_device_alloc() {
+ SubBruteDevice* instance = malloc(sizeof(SubBruteDevice));
+
+ instance->current_step = 0;
+
+ instance->protocol_info = NULL;
+ instance->file_protocol_info = NULL;
+ instance->decoder_result = NULL;
+ instance->receiver = NULL;
+ instance->environment = subghz_environment_alloc();
+ subghz_environment_set_protocol_registry(
+ instance->environment, (void*)&subghz_protocol_registry);
+
+#ifdef FURI_DEBUG
+ subbrute_device_attack_set_default_values(instance, SubBruteAttackLoadFile);
+#else
+ subbrute_device_attack_set_default_values(instance, SubBruteAttackCAME12bit433);
+#endif
+ return instance;
+}
+
+void subbrute_device_free(SubBruteDevice* instance) {
+ furi_assert(instance);
+
+ // I don't know how to free this
+ instance->decoder_result = NULL;
+
+ if(instance->receiver != NULL) {
+ subghz_receiver_free(instance->receiver);
+ instance->receiver = NULL;
+ }
+
+ subghz_environment_free(instance->environment);
+ instance->environment = NULL;
+
+ subbrute_device_free_protocol_info(instance);
+
+ free(instance);
+}
+
+uint64_t subbrute_device_add_step(SubBruteDevice* instance, int8_t step) {
+ if(step > 0) {
+ if((instance->current_step + step) - instance->max_value == 1) {
+ instance->current_step = 0x00;
+ } else {
+ uint64_t value = instance->current_step + step;
+ if(value == instance->max_value) {
+ instance->current_step = value;
+ } else {
+ instance->current_step = value % instance->max_value;
+ }
+ }
+ } else {
+ if(instance->current_step + step == 0) {
+ instance->current_step = 0x00;
+ } else if(instance->current_step == 0) {
+ instance->current_step = instance->max_value;
+ } else {
+ uint64_t value = ((instance->current_step + step) + instance->max_value);
+ if(value == instance->max_value) {
+ instance->current_step = value;
+ } else {
+ instance->current_step = value % instance->max_value;
+ }
+ }
+ }
+
+ return instance->current_step;
+}
+
+bool subbrute_device_save_file(SubBruteDevice* instance, const char* dev_file_name) {
+ furi_assert(instance);
+
+#ifdef FURI_DEBUG
+ FURI_LOG_D(TAG, "subbrute_device_save_file: %s", dev_file_name);
+#endif
+
+ Storage* storage = furi_record_open(RECORD_STORAGE);
+ FlipperFormat* file = flipper_format_file_alloc(storage);
+ bool result = false;
+ do {
+ if(!flipper_format_file_open_always(file, dev_file_name)) {
+ FURI_LOG_E(TAG, "Failed to open file: %s", dev_file_name);
+ break;
+ }
+ Stream* stream = flipper_format_get_raw_stream(file);
+ if(instance->attack == SubBruteAttackLoadFile) {
+ subbrute_protocol_file_generate_file(
+ stream,
+ instance->file_protocol_info->frequency,
+ instance->file_protocol_info->preset,
+ instance->file_protocol_info->file,
+ instance->current_step,
+ instance->file_protocol_info->bits,
+ instance->file_protocol_info->te,
+ instance->file_protocol_info->repeat,
+ instance->bit_index,
+ instance->key_from_file,
+ instance->two_bytes);
+ } else {
+ subbrute_protocol_default_generate_file(
+ stream,
+ instance->protocol_info->frequency,
+ instance->protocol_info->preset,
+ instance->protocol_info->file,
+ instance->current_step,
+ instance->protocol_info->bits,
+ instance->protocol_info->te,
+ instance->protocol_info->repeat);
+ }
+
+ result = true;
+ } while(false);
+
+ if(!result) {
+ FURI_LOG_E(TAG, "subbrute_device_save_file failed!");
+ }
+
+ flipper_format_file_close(file);
+ flipper_format_free(file);
+ furi_record_close(RECORD_STORAGE);
+
+ return result;
+}
+
+SubBruteFileResult subbrute_device_attack_set(
+ SubBruteDevice* instance,
+ SubBruteAttacks type,
+ uint8_t extra_repeats) {
+ furi_assert(instance);
+#ifdef FURI_DEBUG
+ FURI_LOG_D(TAG, "subbrute_device_attack_set: %d, extra_repeats: %d", type, extra_repeats);
+#endif
+ subbrute_device_attack_set_default_values(instance, type);
+
+ if(type != SubBruteAttackLoadFile) {
+ subbrute_device_free_protocol_info(instance);
+ instance->protocol_info = subbrute_protocol(type);
+ }
+
+ instance->extra_repeats = extra_repeats;
+
+ // For non-file types we didn't set SubGhzProtocolDecoderBase
+ instance->receiver = subghz_receiver_alloc_init(instance->environment);
+ subghz_receiver_set_filter(instance->receiver, SubGhzProtocolFlag_Decodable);
+ furi_hal_subghz_reset();
+
+ uint8_t protocol_check_result = SubBruteFileResultProtocolNotFound;
+#ifdef FURI_DEBUG
+ uint8_t bits;
+ uint32_t te;
+ uint8_t repeat;
+ FuriHalSubGhzPreset preset;
+ SubBruteFileProtocol file;
+#endif
+ if(type != SubBruteAttackLoadFile) {
+ instance->decoder_result = subghz_receiver_search_decoder_base_by_name(
+ instance->receiver, subbrute_protocol_file(instance->protocol_info->file));
+
+ if(!instance->decoder_result ||
+ instance->decoder_result->protocol->type == SubGhzProtocolTypeDynamic) {
+ FURI_LOG_E(TAG, "Can't load SubGhzProtocolDecoderBase in phase non-file decoder set");
+ } else {
+ protocol_check_result = SubBruteFileResultOk;
+
+ // Calc max value
+ instance->max_value = subbrute_protocol_calc_max_value(
+ instance->attack, instance->protocol_info->bits, instance->two_bytes);
+ }
+#ifdef FURI_DEBUG
+ bits = instance->protocol_info->bits;
+ te = instance->protocol_info->te;
+ repeat = instance->protocol_info->repeat + instance->extra_repeats;
+ preset = instance->protocol_info->preset;
+ file = instance->protocol_info->file;
+#endif
+ } else {
+ // And here we need to set preset enum
+ protocol_check_result = SubBruteFileResultOk;
+
+ // Calc max value
+ instance->max_value = subbrute_protocol_calc_max_value(
+ instance->attack, instance->file_protocol_info->bits, instance->two_bytes);
+#ifdef FURI_DEBUG
+ bits = instance->file_protocol_info->bits;
+ te = instance->file_protocol_info->te;
+ repeat = instance->file_protocol_info->repeat + instance->extra_repeats;
+ preset = instance->file_protocol_info->preset;
+ file = instance->file_protocol_info->file;
+#endif
+ }
+
+ subghz_receiver_free(instance->receiver);
+ instance->receiver = NULL;
+
+ if(protocol_check_result != SubBruteFileResultOk) {
+ return SubBruteFileResultProtocolNotFound;
+ }
+
+#ifdef FURI_DEBUG
+ FURI_LOG_I(
+ TAG,
+ "subbrute_device_attack_set: %s, bits: %d, preset: %s, file: %s, te: %ld, repeat: %d, max_value: %lld",
+ subbrute_protocol_name(instance->attack),
+ bits,
+ subbrute_protocol_preset(preset),
+ subbrute_protocol_file(file),
+ te,
+ repeat,
+ instance->max_value);
+#endif
+
+ return SubBruteFileResultOk;
+}
+
+uint8_t subbrute_device_load_from_file(SubBruteDevice* instance, const char* file_path) {
+ furi_assert(instance);
+#ifdef FURI_DEBUG
+ FURI_LOG_D(TAG, "subbrute_device_load_from_file: %s", file_path);
+#endif
+ SubBruteFileResult result = SubBruteFileResultUnknown;
+
+ Storage* storage = furi_record_open(RECORD_STORAGE);
+ FlipperFormat* fff_data_file = flipper_format_file_alloc(storage);
+
+ subbrute_device_free_protocol_info(instance);
+ instance->file_protocol_info = malloc(sizeof(SubBruteProtocol));
+
+ FuriString* temp_str;
+ temp_str = furi_string_alloc();
+ uint32_t temp_data32;
+
+ instance->receiver = subghz_receiver_alloc_init(instance->environment);
+ subghz_receiver_set_filter(instance->receiver, SubGhzProtocolFlag_Decodable);
+ furi_hal_subghz_reset();
+
+ do {
+ if(!flipper_format_file_open_existing(fff_data_file, file_path)) {
+ FURI_LOG_E(TAG, "Error open file %s", file_path);
+ result = SubBruteFileResultErrorOpenFile;
+ break;
+ }
+ if(!flipper_format_read_header(fff_data_file, temp_str, &temp_data32)) {
+ FURI_LOG_E(TAG, "Missing or incorrect header");
+ result = SubBruteFileResultMissingOrIncorrectHeader;
+ break;
+ }
+
+ // Frequency
+ if(!flipper_format_read_uint32(fff_data_file, "Frequency", &temp_data32, 1)) {
+ FURI_LOG_E(TAG, "Missing or incorrect Frequency");
+ result = SubBruteFileResultMissingOrIncorrectFrequency;
+ break;
+ }
+ instance->file_protocol_info->frequency = temp_data32;
+ if(!furi_hal_subghz_is_tx_allowed(instance->file_protocol_info->frequency)) {
+ result = SubBruteFileResultFrequencyNotAllowed;
+ break;
+ }
+
+ // Preset
+ if(!flipper_format_read_string(fff_data_file, "Preset", temp_str)) {
+ FURI_LOG_E(TAG, "Preset FAIL");
+ result = SubBruteFileResultPresetInvalid;
+ break;
+ }
+ instance->file_protocol_info->preset = subbrute_protocol_convert_preset(temp_str);
+
+ const char* protocol_file = NULL;
+ // Protocol
+ if(!flipper_format_read_string(fff_data_file, "Protocol", temp_str)) {
+ FURI_LOG_E(TAG, "Missing Protocol");
+ result = SubBruteFileResultMissingProtocol;
+ break;
+ }
+ instance->file_protocol_info->file = subbrute_protocol_file_protocol_name(temp_str);
+ protocol_file = subbrute_protocol_file(instance->file_protocol_info->file);
+#ifdef FURI_DEBUG
+ FURI_LOG_D(TAG, "Protocol: %s", protocol_file);
+#endif
+
+ instance->decoder_result = subghz_receiver_search_decoder_base_by_name(
+ instance->receiver, furi_string_get_cstr(temp_str));
+
+ if((!instance->decoder_result) || (strcmp(protocol_file, "RAW") == 0) ||
+ (strcmp(protocol_file, "Unknown") == 0)) {
+ FURI_LOG_E(TAG, "Protocol unsupported");
+ result = SubBruteFileResultProtocolNotSupported;
+ break;
+ }
+
+ if(instance->decoder_result->protocol->type == SubGhzProtocolTypeDynamic) {
+ FURI_LOG_E(TAG, "Protocol is dynamic - not supported");
+ result = SubBruteFileResultDynamicProtocolNotValid;
+ break;
+ }
+#ifdef FURI_DEBUG
+ FURI_LOG_D(TAG, "Decoder: %s", instance->decoder_result->protocol->name);
+#endif
+
+ // Bit
+ if(!flipper_format_read_uint32(fff_data_file, "Bit", &temp_data32, 1)) {
+ FURI_LOG_E(TAG, "Missing or incorrect Bit");
+ result = SubBruteFileResultMissingOrIncorrectBit;
+ break;
+ }
+ instance->file_protocol_info->bits = temp_data32;
+#ifdef FURI_DEBUG
+ FURI_LOG_D(TAG, "Bit: %d", instance->file_protocol_info->bits);
+#endif
+
+ uint8_t key_data[sizeof(uint64_t)] = {0};
+ if(!flipper_format_read_hex(fff_data_file, "Key", key_data, sizeof(uint64_t))) {
+ FURI_LOG_E(TAG, "Missing Key");
+ result = SubBruteFileResultMissingOrIncorrectKey;
+ break;
+ }
+ uint64_t data = 0;
+ for(uint8_t i = 0; i < sizeof(uint64_t); i++) {
+ data = (data << 8) | key_data[i];
+ }
+#if FURI_DEBUG
+ FURI_LOG_D(TAG, "Key: %.16llX", data);
+#endif
+ instance->key_from_file = data;
+
+ // TE
+ if(!flipper_format_read_uint32(fff_data_file, "TE", &temp_data32, 1)) {
+ FURI_LOG_E(TAG, "Missing or incorrect TE");
+ //result = SubBruteFileResultMissingOrIncorrectTe;
+ //break;
+ } else {
+ instance->file_protocol_info->te = temp_data32 != 0 ? temp_data32 : 0;
+ }
+
+ // Repeat
+ if(flipper_format_read_uint32(fff_data_file, "Repeat", &temp_data32, 1)) {
+#ifdef FURI_DEBUG
+ FURI_LOG_D(TAG, "Repeat: %ld", temp_data32);
+#endif
+ instance->file_protocol_info->repeat = (uint8_t)temp_data32;
+ } else {
+#ifdef FURI_DEBUG
+ FURI_LOG_D(TAG, "Repeat: 3 (default)");
+#endif
+ instance->file_protocol_info->repeat = 3;
+ }
+
+ result = SubBruteFileResultOk;
+ } while(0);
+
+ furi_string_free(temp_str);
+ flipper_format_file_close(fff_data_file);
+ flipper_format_free(fff_data_file);
+ furi_record_close(RECORD_STORAGE);
+
+ subghz_receiver_free(instance->receiver);
+
+ instance->decoder_result = NULL;
+ instance->receiver = NULL;
+
+ if(result == SubBruteFileResultOk) {
+#ifdef FURI_DEBUG
+ FURI_LOG_D(TAG, "Loaded successfully");
+#endif
+ } else {
+ subbrute_device_free_protocol_info(instance);
+ }
+
+ return result;
+}
+
+void subbrute_device_attack_set_default_values(
+ SubBruteDevice* instance,
+ SubBruteAttacks default_attack) {
+ furi_assert(instance);
+#ifdef FURI_DEBUG
+ FURI_LOG_D(TAG, "subbrute_device_attack_set_default_values");
+#endif
+ instance->attack = default_attack;
+ instance->current_step = 0x00;
+ instance->bit_index = 0x00;
+ instance->extra_repeats = 0;
+ instance->two_bytes = false;
+
+ if(default_attack != SubBruteAttackLoadFile) {
+ instance->max_value = subbrute_protocol_calc_max_value(
+ instance->attack, instance->bit_index, instance->two_bytes);
+ }
+}
+
+const char* subbrute_device_error_get_desc(SubBruteFileResult error_id) {
+ const char* result;
+ switch(error_id) {
+ case(SubBruteFileResultOk):
+ result = "OK";
+ break;
+ case(SubBruteFileResultErrorOpenFile):
+ result = "invalid name/path";
+ break;
+ case(SubBruteFileResultMissingOrIncorrectHeader):
+ result = "Missing or incorrect header";
+ break;
+ case(SubBruteFileResultFrequencyNotAllowed):
+ result = "Invalid frequency!";
+ break;
+ case(SubBruteFileResultMissingOrIncorrectFrequency):
+ result = "Missing or incorrect Frequency";
+ break;
+ case(SubBruteFileResultPresetInvalid):
+ result = "Preset FAIL";
+ break;
+ case(SubBruteFileResultMissingProtocol):
+ result = "Missing Protocol";
+ break;
+ case(SubBruteFileResultProtocolNotSupported):
+ result = "Protocol unsupported";
+ break;
+ case(SubBruteFileResultDynamicProtocolNotValid):
+ result = "Dynamic protocol unsupported";
+ break;
+ case(SubBruteFileResultProtocolNotFound):
+ result = "Protocol not found";
+ break;
+ case(SubBruteFileResultMissingOrIncorrectBit):
+ result = "Missing or incorrect Bit";
+ break;
+ case(SubBruteFileResultMissingOrIncorrectKey):
+ result = "Missing or incorrect Key";
+ break;
+ case(SubBruteFileResultMissingOrIncorrectTe):
+ result = "Missing or incorrect TE";
+ break;
+ case SubBruteFileResultUnknown:
+ default:
+ result = "Unknown error";
+ break;
+ }
+ return result;
+}
+
+void subbrute_device_free_protocol_info(SubBruteDevice* instance) {
+ furi_assert(instance);
+ instance->protocol_info = NULL;
+ if(instance->file_protocol_info) {
+ free(instance->file_protocol_info);
+ }
+ instance->file_protocol_info = NULL;
+}
\ No newline at end of file
diff --git a/applications/external/subghz_bruteforcer/subbrute_device.h b/applications/external/subghz_bruteforcer/subbrute_device.h
new file mode 100644
index 000000000..7ff650e93
--- /dev/null
+++ b/applications/external/subghz_bruteforcer/subbrute_device.h
@@ -0,0 +1,75 @@
+#pragma once
+
+#include "subbrute_protocols.h"
+#include
+#include
+#include
+#include
+
+#define SUBBRUTE_TEXT_STORE_SIZE 256
+
+#define SUBBRUTE_MAX_LEN_NAME 64
+#define SUBBRUTE_PATH EXT_PATH("subghz")
+#define SUBBRUTE_FILE_EXT ".sub"
+
+#define SUBBRUTE_PAYLOAD_SIZE 16
+
+typedef enum {
+ SubBruteFileResultUnknown,
+ SubBruteFileResultOk,
+ SubBruteFileResultErrorOpenFile,
+ SubBruteFileResultMissingOrIncorrectHeader,
+ SubBruteFileResultFrequencyNotAllowed,
+ SubBruteFileResultMissingOrIncorrectFrequency,
+ SubBruteFileResultPresetInvalid,
+ SubBruteFileResultMissingProtocol,
+ SubBruteFileResultProtocolNotSupported,
+ SubBruteFileResultDynamicProtocolNotValid,
+ SubBruteFileResultProtocolNotFound,
+ SubBruteFileResultMissingOrIncorrectBit,
+ SubBruteFileResultMissingOrIncorrectKey,
+ SubBruteFileResultMissingOrIncorrectTe,
+} SubBruteFileResult;
+
+typedef struct {
+ const SubBruteProtocol* protocol_info;
+ SubBruteProtocol* file_protocol_info;
+
+ // Current step
+ uint64_t current_step;
+
+ // SubGhz
+ SubGhzReceiver* receiver;
+ SubGhzProtocolDecoderBase* decoder_result;
+ SubGhzEnvironment* environment;
+
+ // Attack state
+ SubBruteAttacks attack;
+ uint64_t max_value;
+ uint8_t extra_repeats;
+
+ // Loaded info for attack type
+ uint64_t key_from_file;
+ uint64_t current_key_from_file;
+ bool two_bytes;
+ // Index of group to bruteforce in loaded file
+ uint8_t bit_index;
+} SubBruteDevice;
+
+SubBruteDevice* subbrute_device_alloc();
+void subbrute_device_free(SubBruteDevice* instance);
+
+bool subbrute_device_save_file(SubBruteDevice* instance, const char* key_name);
+const char* subbrute_device_error_get_desc(SubBruteFileResult error_id);
+SubBruteFileResult subbrute_device_attack_set(
+ SubBruteDevice* context,
+ SubBruteAttacks type,
+ uint8_t extra_repeats);
+uint8_t subbrute_device_load_from_file(SubBruteDevice* context, const char* file_path);
+
+uint64_t subbrute_device_add_step(SubBruteDevice* instance, int8_t step);
+
+void subbrute_device_free_protocol_info(SubBruteDevice* instance);
+void subbrute_device_attack_set_default_values(
+ SubBruteDevice* context,
+ SubBruteAttacks default_attack);
\ No newline at end of file
diff --git a/applications/external/subghz_bruteforcer/subbrute_i.h b/applications/external/subghz_bruteforcer/subbrute_i.h
new file mode 100644
index 000000000..c50a7ed9b
--- /dev/null
+++ b/applications/external/subghz_bruteforcer/subbrute_i.h
@@ -0,0 +1,80 @@
+#pragma once
+
+#include
+#include
+#include
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "SubGHz_Bruteforcer_icons.h"
+
+#include
+
+#include
+#include
+
+#include "subbrute.h"
+#include "subbrute_device.h"
+#include "helpers/subbrute_worker.h"
+#include "views/subbrute_attack_view.h"
+#include "views/subbrute_main_view.h"
+
+#define SUBBRUTEFORCER_VER "Sub-GHz BruteForcer 3.5"
+
+#ifdef FURI_DEBUG
+//#define SUBBRUTE_FAST_TRACK false
+#endif
+
+typedef enum {
+ SubBruteViewNone,
+ SubBruteViewMain,
+ SubBruteViewAttack,
+ SubBruteViewTextInput,
+ SubBruteViewDialogEx,
+ SubBruteViewPopup,
+ SubBruteViewWidget,
+ SubBruteViewStack,
+} SubBruteView;
+
+struct SubBruteState {
+ // GUI elements
+ NotificationApp* notifications;
+ Gui* gui;
+ ViewDispatcher* view_dispatcher;
+ ViewStack* view_stack;
+ TextInput* text_input;
+ Popup* popup;
+ Widget* widget;
+ DialogsApp* dialogs;
+
+ // Text store
+ char text_store[SUBBRUTE_MAX_LEN_NAME];
+ FuriString* file_path;
+
+ // Views
+ SubBruteMainView* view_main;
+ SubBruteAttackView* view_attack;
+ SubBruteView current_view;
+
+ // Scene
+ SceneManager* scene_manager;
+
+ // SubBruteDevice
+ SubBruteDevice* device;
+ // SubBruteWorker
+ SubBruteWorker* worker;
+};
+
+void subbrute_show_loading_popup(void* context, bool show);
+void subbrute_text_input_callback(void* context);
+void subbrute_popup_closed_callback(void* context);
\ No newline at end of file
diff --git a/applications/external/subghz_bruteforcer/subbrute_protocols.c b/applications/external/subghz_bruteforcer/subbrute_protocols.c
new file mode 100644
index 000000000..6c6781b78
--- /dev/null
+++ b/applications/external/subghz_bruteforcer/subbrute_protocols.c
@@ -0,0 +1,881 @@
+#include "subbrute_protocols.h"
+#include "math.h"
+#include
+
+#define TAG "SubBruteProtocols"
+
+/**
+ * CAME 12bit 303MHz
+ */
+const SubBruteProtocol subbrute_protocol_came_12bit_303 = {
+ .frequency = 303875000,
+ .bits = 12,
+ .te = 0,
+ .repeat = 3,
+ .preset = FuriHalSubGhzPresetOok650Async,
+ .file = CAMEFileProtocol};
+
+/**
+ * CAME 12bit 307MHz
+ */
+const SubBruteProtocol subbrute_protocol_came_12bit_307 = {
+ .frequency = 307800000,
+ .bits = 12,
+ .te = 0,
+ .repeat = 3,
+ .preset = FuriHalSubGhzPresetOok650Async,
+ .file = CAMEFileProtocol};
+
+/**
+ * CAME 12bit 315MHz
+ */
+const SubBruteProtocol subbrute_protocol_came_12bit_315 = {
+ .frequency = 315000000,
+ .bits = 12,
+ .te = 0,
+ .repeat = 3,
+ .preset = FuriHalSubGhzPresetOok650Async,
+ .file = CAMEFileProtocol};
+
+/**
+ * CAME 12bit 433MHz
+ */
+const SubBruteProtocol subbrute_protocol_came_12bit_433 = {
+ .frequency = 433920000,
+ .bits = 12,
+ .te = 0,
+ .repeat = 3,
+ .preset = FuriHalSubGhzPresetOok650Async,
+ .file = CAMEFileProtocol};
+
+/**
+ * CAME 12bit 868MHz
+ */
+const SubBruteProtocol subbrute_protocol_came_12bit_868 = {
+ .frequency = 868350000,
+ .bits = 12,
+ .te = 0,
+ .repeat = 3,
+ .preset = FuriHalSubGhzPresetOok650Async,
+ .file = CAMEFileProtocol};
+
+/**
+ * NICE 12bit 433MHz
+ */
+const SubBruteProtocol subbrute_protocol_nice_12bit_433 = {
+ .frequency = 433920000,
+ .bits = 12,
+ .te = 0,
+ .repeat = 3,
+ .preset = FuriHalSubGhzPresetOok650Async,
+ .file = NICEFileProtocol};
+
+/**
+ * NICE 12bit 868MHz
+ */
+const SubBruteProtocol subbrute_protocol_nice_12bit_868 = {
+ .frequency = 868350000,
+ .bits = 12,
+ .te = 0,
+ .repeat = 3,
+ .preset = FuriHalSubGhzPresetOok650Async,
+ .file = NICEFileProtocol};
+
+/**
+ * Ansonic 12bit 433.075MHz
+ */
+const SubBruteProtocol subbrute_protocol_ansonic_12bit_433075 = {
+ .frequency = 433075000,
+ .bits = 12,
+ .te = 0,
+ .repeat = 3,
+ .preset = FuriHalSubGhzPreset2FSKDev238Async,
+ .file = AnsonicFileProtocol};
+
+/**
+ * Ansonic 12bit 433.92MHz
+ */
+const SubBruteProtocol subbrute_protocol_ansonic_12bit_433 = {
+ .frequency = 433920000,
+ .bits = 12,
+ .te = 0,
+ .repeat = 3,
+ .preset = FuriHalSubGhzPreset2FSKDev238Async,
+ .file = AnsonicFileProtocol};
+
+/**
+ * Ansonic 12bit 434.075MHz
+ */
+const SubBruteProtocol subbrute_protocol_ansonic_12bit_434 = {
+ .frequency = 434075000,
+ .bits = 12,
+ .te = 0,
+ .repeat = 3,
+ .preset = FuriHalSubGhzPreset2FSKDev238Async,
+ .file = AnsonicFileProtocol};
+
+/**
+ * Chamberlain 9bit 300MHz
+ */
+const SubBruteProtocol subbrute_protocol_chamberlain_9bit_300 = {
+ .frequency = 300000000,
+ .bits = 9,
+ .te = 0,
+ .repeat = 3,
+ .preset = FuriHalSubGhzPresetOok650Async,
+ .file = ChamberlainFileProtocol};
+
+/**
+ * Chamberlain 9bit 315MHz
+ */
+const SubBruteProtocol subbrute_protocol_chamberlain_9bit_315 = {
+ .frequency = 315000000,
+ .bits = 9,
+ .te = 0,
+ .repeat = 3,
+ .preset = FuriHalSubGhzPresetOok650Async,
+ .file = ChamberlainFileProtocol};
+
+/**
+ * Chamberlain 9bit 390MHz
+ */
+const SubBruteProtocol subbrute_protocol_chamberlain_9bit_390 = {
+ .frequency = 390000000,
+ .bits = 9,
+ .te = 0,
+ .repeat = 3,
+ .preset = FuriHalSubGhzPresetOok650Async,
+ .file = ChamberlainFileProtocol};
+
+/**
+ * Chamberlain 9bit 433MHz
+ */
+const SubBruteProtocol subbrute_protocol_chamberlain_9bit_433 = {
+ .frequency = 433920000,
+ .bits = 9,
+ .te = 0,
+ .repeat = 3,
+ .preset = FuriHalSubGhzPresetOok650Async,
+ .file = ChamberlainFileProtocol};
+
+/**
+ * Chamberlain 8bit 300MHz
+ */
+const SubBruteProtocol subbrute_protocol_chamberlain_8bit_300 = {
+ .frequency = 300000000,
+ .bits = 8,
+ .te = 0,
+ .repeat = 3,
+ .preset = FuriHalSubGhzPresetOok650Async,
+ .file = ChamberlainFileProtocol};
+
+/**
+ * Chamberlain 8bit 315MHz
+ */
+const SubBruteProtocol subbrute_protocol_chamberlain_8bit_315 = {
+ .frequency = 315000000,
+ .bits = 8,
+ .te = 0,
+ .repeat = 3,
+ .preset = FuriHalSubGhzPresetOok650Async,
+ .file = ChamberlainFileProtocol};
+
+/**
+ * Chamberlain 8bit 390MHz
+ */
+const SubBruteProtocol subbrute_protocol_chamberlain_8bit_390 = {
+ .frequency = 390000000,
+ .bits = 8,
+ .te = 0,
+ .repeat = 3,
+ .preset = FuriHalSubGhzPresetOok650Async,
+ .file = ChamberlainFileProtocol};
+
+/**
+ * Chamberlain 7bit 300MHz
+ */
+const SubBruteProtocol subbrute_protocol_chamberlain_7bit_300 = {
+ .frequency = 300000000,
+ .bits = 7,
+ .te = 0,
+ .repeat = 3,
+ .preset = FuriHalSubGhzPresetOok650Async,
+ .file = ChamberlainFileProtocol};
+
+/**
+ * Chamberlain 7bit 315MHz
+ */
+const SubBruteProtocol subbrute_protocol_chamberlain_7bit_315 = {
+ .frequency = 315000000,
+ .bits = 7,
+ .te = 0,
+ .repeat = 3,
+ .preset = FuriHalSubGhzPresetOok650Async,
+ .file = ChamberlainFileProtocol};
+
+/**
+ * Chamberlain 7bit 390MHz
+ */
+const SubBruteProtocol subbrute_protocol_chamberlain_7bit_390 = {
+ .frequency = 390000000,
+ .bits = 7,
+ .te = 0,
+ .repeat = 3,
+ .preset = FuriHalSubGhzPresetOok650Async,
+ .file = ChamberlainFileProtocol};
+
+/**
+ * Linear 10bit 300MHz
+ */
+const SubBruteProtocol subbrute_protocol_linear_10bit_300 = {
+ .frequency = 300000000,
+ .bits = 10,
+ .te = 0,
+ .repeat = 5,
+ .preset = FuriHalSubGhzPresetOok650Async,
+ .file = LinearFileProtocol};
+
+/**
+ * Linear 10bit 310MHz
+ */
+const SubBruteProtocol subbrute_protocol_linear_10bit_310 = {
+ .frequency = 310000000,
+ .bits = 10,
+ .te = 0,
+ .repeat = 5,
+ .preset = FuriHalSubGhzPresetOok650Async,
+ .file = LinearFileProtocol};
+
+/**
+ * Linear Delta 3 8bit 310MHz
+ */
+const SubBruteProtocol subbrute_protocol_linear_delta_8bit_310 = {
+ .frequency = 310000000,
+ .bits = 8,
+ .te = 0,
+ .repeat = 5,
+ .preset = FuriHalSubGhzPresetOok650Async,
+ .file = LinearDeltaFileProtocol};
+
+/**
+ * UNILARM 24bit 330MHz
+ */
+const SubBruteProtocol subbrute_protocol_unilarm_24bit_330 = {
+ .frequency = 330000000,
+ .bits = 25,
+ .te = 209,
+ .repeat = 4,
+ .preset = FuriHalSubGhzPresetOok650Async,
+ .file = UNILARMFileProtocol};
+
+/**
+ * UNILARM 24bit 433MHz
+ */
+const SubBruteProtocol subbrute_protocol_unilarm_24bit_433 = {
+ .frequency = 433920000,
+ .bits = 25,
+ .te = 209,
+ .repeat = 4,
+ .preset = FuriHalSubGhzPresetOok650Async,
+ .file = UNILARMFileProtocol};
+
+/**
+ * SMC5326 24bit 330MHz
+ */
+const SubBruteProtocol subbrute_protocol_smc5326_24bit_330 = {
+ .frequency = 330000000,
+ .bits = 25,
+ .te = 320,
+ .repeat = 4,
+ .preset = FuriHalSubGhzPresetOok650Async,
+ .file = SMC5326FileProtocol};
+
+/**
+ * SMC5326 24bit 433MHz
+ */
+const SubBruteProtocol subbrute_protocol_smc5326_24bit_433 = {
+ .frequency = 433920000,
+ .bits = 25,
+ .te = 320,
+ .repeat = 4,
+ .preset = FuriHalSubGhzPresetOok650Async,
+ .file = SMC5326FileProtocol};
+
+/**
+ * PT2260 (Princeton) 24bit 315MHz
+ */
+const SubBruteProtocol subbrute_protocol_pt2260_24bit_315 = {
+ .frequency = 315000000,
+ .bits = 24,
+ .te = 286,
+ .repeat = 4,
+ .preset = FuriHalSubGhzPresetOok650Async,
+ .file = PT2260FileProtocol};
+
+/**
+ * PT2260 (Princeton) 24bit 330MHz
+ */
+const SubBruteProtocol subbrute_protocol_pt2260_24bit_330 = {
+ .frequency = 330000000,
+ .bits = 24,
+ .te = 286,
+ .repeat = 4,
+ .preset = FuriHalSubGhzPresetOok650Async,
+ .file = PT2260FileProtocol};
+
+/**
+ * PT2260 (Princeton) 24bit 390MHz
+ */
+const SubBruteProtocol subbrute_protocol_pt2260_24bit_390 = {
+ .frequency = 390000000,
+ .bits = 24,
+ .te = 286,
+ .repeat = 4,
+ .preset = FuriHalSubGhzPresetOok650Async,
+ .file = PT2260FileProtocol};
+
+/**
+ * PT2260 (Princeton) 24bit 433MHz
+ */
+const SubBruteProtocol subbrute_protocol_pt2260_24bit_433 = {
+ .frequency = 433920000,
+ .bits = 24,
+ .te = 286,
+ .repeat = 4,
+ .preset = FuriHalSubGhzPresetOok650Async,
+ .file = PT2260FileProtocol};
+
+/**
+ * Holtek FM 12bit 433MHz
+ */
+const SubBruteProtocol subbrute_protocol_holtek_12bit_433 = {
+ .frequency = 433920000,
+ .bits = 12,
+ .te = 204,
+ .repeat = 4,
+ .preset = FuriHalSubGhzPreset2FSKDev476Async,
+ .file = HoltekFileProtocol};
+
+/**
+ * Holtek AM 12bit 433MHz
+ */
+const SubBruteProtocol subbrute_protocol_holtek_12bit_am_433 = {
+ .frequency = 433920000,
+ .bits = 12,
+ .te = 433,
+ .repeat = 3,
+ .preset = FuriHalSubGhzPresetOok650Async,
+ .file = HoltekFileProtocol};
+
+/**
+ * Holtek AM 12bit 315MHz
+ */
+const SubBruteProtocol subbrute_protocol_holtek_12bit_am_315 = {
+ .frequency = 315000000,
+ .bits = 12,
+ .te = 433,
+ .repeat = 3,
+ .preset = FuriHalSubGhzPresetOok650Async,
+ .file = HoltekFileProtocol};
+
+/**
+ * Holtek AM 12bit 868MHz
+ */
+const SubBruteProtocol subbrute_protocol_holtek_12bit_am_868 = {
+ .frequency = 868350000,
+ .bits = 12,
+ .te = 433,
+ .repeat = 3,
+ .preset = FuriHalSubGhzPresetOok650Async,
+ .file = HoltekFileProtocol};
+
+/**
+ * Holtek AM 12bit 915MHz
+ */
+const SubBruteProtocol subbrute_protocol_holtek_12bit_am_915 = {
+ .frequency = 915000000,
+ .bits = 12,
+ .te = 433,
+ .repeat = 3,
+ .preset = FuriHalSubGhzPresetOok650Async,
+ .file = HoltekFileProtocol};
+
+/**
+ * BF existing dump
+ */
+const SubBruteProtocol subbrute_protocol_load_file =
+ {0, 0, 0, 3, FuriHalSubGhzPresetOok650Async, UnknownFileProtocol};
+
+static const char* subbrute_protocol_names[] = {
+ [SubBruteAttackCAME12bit303] = "CAME 12bit 303MHz",
+ [SubBruteAttackCAME12bit307] = "CAME 12bit 307MHz",
+ [SubBruteAttackCAME12bit315] = "CAME 12bit 315MHz",
+ [SubBruteAttackCAME12bit433] = "CAME 12bit 433MHz",
+ [SubBruteAttackCAME12bit868] = "CAME 12bit 868MHz",
+ [SubBruteAttackNICE12bit433] = "NICE 12bit 433MHz",
+ [SubBruteAttackNICE12bit868] = "NICE 12bit 868MHz",
+ [SubBruteAttackAnsonic12bit433075] = "Ansonic 12bit 433.07MHz",
+ [SubBruteAttackAnsonic12bit433] = "Ansonic 12bit 433.92MHz",
+ [SubBruteAttackAnsonic12bit434] = "Ansonic 12bit 434.07MHz",
+ [SubBruteAttackHoltek12bitFM433] = "Holtek FM 12bit 433MHz",
+ [SubBruteAttackHoltek12bitAM433] = "Holtek AM 12bit 433MHz",
+ [SubBruteAttackHoltek12bitAM315] = "Holtek AM 12bit 315MHz",
+ [SubBruteAttackHoltek12bitAM868] = "Holtek AM 12bit 868MHz",
+ [SubBruteAttackHoltek12bitAM915] = "Holtek AM 12bit 915MHz",
+ [SubBruteAttackChamberlain9bit300] = "Chamberlain 9bit 300MHz",
+ [SubBruteAttackChamberlain9bit315] = "Chamberlain 9bit 315MHz",
+ [SubBruteAttackChamberlain9bit390] = "Chamberlain 9bit 390MHz",
+ [SubBruteAttackChamberlain9bit433] = "Chamberlain 9bit 433MHz",
+ [SubBruteAttackChamberlain8bit300] = "Chamberlain 8bit 300MHz",
+ [SubBruteAttackChamberlain8bit315] = "Chamberlain 8bit 315MHz",
+ [SubBruteAttackChamberlain8bit390] = "Chamberlain 8bit 390MHz",
+ [SubBruteAttackChamberlain7bit300] = "Chamberlain 7bit 300MHz",
+ [SubBruteAttackChamberlain7bit315] = "Chamberlain 7bit 315MHz",
+ [SubBruteAttackChamberlain7bit390] = "Chamberlain 7bit 390MHz",
+ [SubBruteAttackLinear10bit300] = "Linear 10bit 300MHz",
+ [SubBruteAttackLinear10bit310] = "Linear 10bit 310MHz",
+ [SubBruteAttackLinearDelta8bit310] = "LinearDelta3 8bit 310MHz",
+ [SubBruteAttackUNILARM24bit330] = "UNILARM 25bit 330MHz",
+ [SubBruteAttackUNILARM24bit433] = "UNILARM 25bit 433MHz",
+ [SubBruteAttackSMC532624bit330] = "SMC5326 25bit 330MHz",
+ [SubBruteAttackSMC532624bit433] = "SMC5326 25bit 433MHz",
+ [SubBruteAttackPT226024bit315] = "PT2260 24bit 315MHz",
+ [SubBruteAttackPT226024bit330] = "PT2260 24bit 330MHz",
+ [SubBruteAttackPT226024bit390] = "PT2260 24bit 390MHz",
+ [SubBruteAttackPT226024bit433] = "PT2260 24bit 433MHz",
+ [SubBruteAttackLoadFile] = "BF existing dump",
+ [SubBruteAttackTotalCount] = "Total Count",
+};
+
+static const char* subbrute_protocol_presets[] = {
+ [FuriHalSubGhzPresetIDLE] = "FuriHalSubGhzPresetIDLE",
+ [FuriHalSubGhzPresetOok270Async] = "FuriHalSubGhzPresetOok270Async",
+ [FuriHalSubGhzPresetOok650Async] = "FuriHalSubGhzPresetOok650Async",
+ [FuriHalSubGhzPreset2FSKDev238Async] = "FuriHalSubGhzPreset2FSKDev238Async",
+ [FuriHalSubGhzPreset2FSKDev476Async] = "FuriHalSubGhzPreset2FSKDev476Async",
+ [FuriHalSubGhzPresetMSK99_97KbAsync] = "FuriHalSubGhzPresetMSK99_97KbAsync",
+ [FuriHalSubGhzPresetGFSK9_99KbAsync] = "FuriHalSubGhzPresetGFSK9_99KbAsync",
+};
+
+const SubBruteProtocol* subbrute_protocol_registry[] = {
+ [SubBruteAttackCAME12bit303] = &subbrute_protocol_came_12bit_303,
+ [SubBruteAttackCAME12bit307] = &subbrute_protocol_came_12bit_307,
+ [SubBruteAttackCAME12bit315] = &subbrute_protocol_came_12bit_315,
+ [SubBruteAttackCAME12bit433] = &subbrute_protocol_came_12bit_433,
+ [SubBruteAttackCAME12bit868] = &subbrute_protocol_came_12bit_868,
+ [SubBruteAttackNICE12bit433] = &subbrute_protocol_nice_12bit_433,
+ [SubBruteAttackNICE12bit868] = &subbrute_protocol_nice_12bit_868,
+ [SubBruteAttackAnsonic12bit433075] = &subbrute_protocol_ansonic_12bit_433075,
+ [SubBruteAttackAnsonic12bit433] = &subbrute_protocol_ansonic_12bit_433,
+ [SubBruteAttackAnsonic12bit434] = &subbrute_protocol_ansonic_12bit_434,
+ [SubBruteAttackHoltek12bitFM433] = &subbrute_protocol_holtek_12bit_433,
+ [SubBruteAttackHoltek12bitAM433] = &subbrute_protocol_holtek_12bit_am_433,
+ [SubBruteAttackHoltek12bitAM315] = &subbrute_protocol_holtek_12bit_am_315,
+ [SubBruteAttackHoltek12bitAM868] = &subbrute_protocol_holtek_12bit_am_868,
+ [SubBruteAttackHoltek12bitAM915] = &subbrute_protocol_holtek_12bit_am_915,
+ [SubBruteAttackChamberlain9bit300] = &subbrute_protocol_chamberlain_9bit_300,
+ [SubBruteAttackChamberlain9bit315] = &subbrute_protocol_chamberlain_9bit_315,
+ [SubBruteAttackChamberlain9bit390] = &subbrute_protocol_chamberlain_9bit_390,
+ [SubBruteAttackChamberlain9bit433] = &subbrute_protocol_chamberlain_9bit_433,
+ [SubBruteAttackChamberlain8bit300] = &subbrute_protocol_chamberlain_8bit_300,
+ [SubBruteAttackChamberlain8bit315] = &subbrute_protocol_chamberlain_8bit_315,
+ [SubBruteAttackChamberlain8bit390] = &subbrute_protocol_chamberlain_8bit_390,
+ [SubBruteAttackChamberlain7bit300] = &subbrute_protocol_chamberlain_7bit_300,
+ [SubBruteAttackChamberlain7bit315] = &subbrute_protocol_chamberlain_7bit_315,
+ [SubBruteAttackChamberlain7bit390] = &subbrute_protocol_chamberlain_7bit_390,
+ [SubBruteAttackLinear10bit300] = &subbrute_protocol_linear_10bit_300,
+ [SubBruteAttackLinear10bit310] = &subbrute_protocol_linear_10bit_310,
+ [SubBruteAttackLinearDelta8bit310] = &subbrute_protocol_linear_delta_8bit_310,
+ [SubBruteAttackUNILARM24bit330] = &subbrute_protocol_unilarm_24bit_330,
+ [SubBruteAttackUNILARM24bit433] = &subbrute_protocol_unilarm_24bit_433,
+ [SubBruteAttackSMC532624bit330] = &subbrute_protocol_smc5326_24bit_330,
+ [SubBruteAttackSMC532624bit433] = &subbrute_protocol_smc5326_24bit_433,
+ [SubBruteAttackPT226024bit315] = &subbrute_protocol_pt2260_24bit_315,
+ [SubBruteAttackPT226024bit330] = &subbrute_protocol_pt2260_24bit_330,
+ [SubBruteAttackPT226024bit390] = &subbrute_protocol_pt2260_24bit_390,
+ [SubBruteAttackPT226024bit433] = &subbrute_protocol_pt2260_24bit_433,
+ [SubBruteAttackLoadFile] = &subbrute_protocol_load_file};
+
+static const char* subbrute_protocol_file_types[] = {
+ [CAMEFileProtocol] = "CAME",
+ [NICEFileProtocol] = "Nice FLO",
+ [ChamberlainFileProtocol] = "Cham_Code",
+ [LinearFileProtocol] = "Linear",
+ [LinearDeltaFileProtocol] = "LinearDelta3",
+ [PrincetonFileProtocol] = "Princeton",
+ [RAWFileProtocol] = "RAW",
+ [BETTFileProtocol] = "BETT",
+ [ClemsaFileProtocol] = "Clemsa",
+ [DoitrandFileProtocol] = "Doitrand",
+ [GateTXFileProtocol] = "GateTX",
+ [MagellanFileProtocol] = "Magellan",
+ [IntertechnoV3FileProtocol] = "Intertechno_V3",
+ [AnsonicFileProtocol] = "Ansonic",
+ [SMC5326FileProtocol] = "SMC5326",
+ [UNILARMFileProtocol] = "SMC5326",
+ [PT2260FileProtocol] = "Princeton",
+ [HoneywellFileProtocol] = "Honeywell",
+ [HoltekFileProtocol] = "Holtek_HT12X",
+ [UnknownFileProtocol] = "Unknown"};
+
+/**
+ * Values to not use less memory for packet parse operations
+ */
+static const char* subbrute_key_file_start_no_tail =
+ "Filetype: Flipper SubGhz Key File\nVersion: 1\nFrequency: %u\nPreset: %s\nProtocol: %s\nBit: %d\nKey: %s\nRepeat: %d\n";
+static const char* subbrute_key_file_start_with_tail =
+ "Filetype: Flipper SubGhz Key File\nVersion: 1\nFrequency: %u\nPreset: %s\nProtocol: %s\nBit: %d\nKey: %s\nTE: %d\nRepeat: %d\n";
+static const char* subbrute_key_small_no_tail = "Bit: %d\nKey: %s\nRepeat: %d\n";
+//static const char* subbrute_key_small_raw =
+// "Filetype: Flipper SubGhz Key File\nVersion: 1\nFrequency: %u\nPreset: %s\nProtocol: %s\nBit: %d\n";
+static const char* subbrute_key_small_with_tail = "Bit: %d\nKey: %s\nTE: %d\nRepeat: %d\n";
+
+const char* subbrute_protocol_name(SubBruteAttacks index) {
+ return subbrute_protocol_names[index];
+}
+
+const SubBruteProtocol* subbrute_protocol(SubBruteAttacks index) {
+ return subbrute_protocol_registry[index];
+}
+
+uint8_t subbrute_protocol_repeats_count(SubBruteAttacks index) {
+ return subbrute_protocol_registry[index]->repeat;
+}
+
+const char* subbrute_protocol_preset(FuriHalSubGhzPreset preset) {
+ return subbrute_protocol_presets[preset];
+}
+
+const char* subbrute_protocol_file(SubBruteFileProtocol protocol) {
+ return subbrute_protocol_file_types[protocol];
+}
+
+FuriHalSubGhzPreset subbrute_protocol_convert_preset(FuriString* preset_name) {
+ for(size_t i = FuriHalSubGhzPresetIDLE; i < FuriHalSubGhzPresetCustom; i++) {
+ if(furi_string_cmp_str(preset_name, subbrute_protocol_presets[i]) == 0) {
+ return i;
+ }
+ }
+
+ return FuriHalSubGhzPresetIDLE;
+}
+
+SubBruteFileProtocol subbrute_protocol_file_protocol_name(FuriString* name) {
+ for(size_t i = CAMEFileProtocol; i < TotalFileProtocol - 1; i++) {
+ if(furi_string_cmp_str(name, subbrute_protocol_file_types[i]) == 0) {
+ return i;
+ }
+ }
+
+ return UnknownFileProtocol;
+}
+
+void subbrute_protocol_create_candidate_for_existing_file(
+ FuriString* candidate,
+ uint64_t step,
+ uint8_t bit_index,
+ uint64_t file_key,
+ bool two_bytes) {
+ uint8_t p[8];
+ for(int i = 0; i < 8; i++) {
+ p[i] = (uint8_t)(file_key >> 8 * (7 - i)) & 0xFF;
+ }
+ uint8_t low_byte = step & (0xff);
+ uint8_t high_byte = (step >> 8) & 0xff;
+
+ size_t size = sizeof(uint64_t);
+ for(uint8_t i = 0; i < size; i++) {
+ if(i == bit_index - 1 && two_bytes) {
+ furi_string_cat_printf(candidate, "%02X %02X", high_byte, low_byte);
+ i++;
+ } else if(i == bit_index) {
+ furi_string_cat_printf(candidate, "%02X", low_byte);
+ } else if(p[i] != 0) {
+ furi_string_cat_printf(candidate, "%02X", p[i]);
+ } else {
+ furi_string_cat_printf(candidate, "%s", "00");
+ }
+
+ if(i < size - 1) {
+ furi_string_push_back(candidate, ' ');
+ }
+ }
+
+#ifdef FURI_DEBUG
+ FURI_LOG_D(TAG, "file candidate: %s, step: %lld", furi_string_get_cstr(candidate), step);
+#endif
+}
+
+void subbrute_protocol_create_candidate_for_default(
+ FuriString* candidate,
+ SubBruteFileProtocol file,
+ uint64_t step) {
+ uint8_t p[8];
+ if(file == SMC5326FileProtocol) {
+ const uint8_t lut[] = {0x00, 0x02, 0x03}; // 00, 10, 11
+ const uint64_t gate1 = 0x01D5; // 111010101
+ //const uint8_t gate2 = 0x0175; // 101110101
+
+ uint64_t total = 0;
+ for(size_t j = 0; j < 8; j++) {
+ total |= lut[step % 3] << (2 * j);
+ double sub_step = step / 3;
+ step = (uint64_t)floor(sub_step);
+ }
+ total <<= 9;
+ total |= gate1;
+
+ for(int i = 0; i < 8; i++) {
+ p[i] = (uint8_t)(total >> 8 * (7 - i)) & 0xFF;
+ }
+ } else if(file == UNILARMFileProtocol) {
+ const uint8_t lut[] = {0x00, 0x02, 0x03}; // 00, 10, 11
+ const uint64_t gate1 = 3 << 7;
+ //const uint8_t gate2 = 3 << 5;
+
+ uint64_t total = 0;
+ for(size_t j = 0; j < 8; j++) {
+ total |= lut[step % 3] << (2 * j);
+ double sub_step = step / 3;
+ step = (uint64_t)floor(sub_step);
+ }
+ total <<= 9;
+ total |= gate1;
+
+ for(int i = 0; i < 8; i++) {
+ p[i] = (uint8_t)(total >> 8 * (7 - i)) & 0xFF;
+ }
+ } else if(file == PT2260FileProtocol) {
+ const uint8_t lut[] = {0x00, 0x01, 0x03}; // 00, 01, 11
+ const uint64_t button_open = 0x03; // 11
+ //const uint8_t button_lock = 0x0C; // 1100
+ //const uint8_t button_stop = 0x30; // 110000
+ //const uint8_t button_close = 0xC0; // 11000000
+
+ uint64_t total = 0;
+ for(size_t j = 0; j < 8; j++) {
+ total |= lut[step % 3] << (2 * j);
+ double sub_step = step / 3;
+ step = (uint64_t)floor(sub_step);
+ }
+ total <<= 8;
+ total |= button_open;
+
+ for(int i = 0; i < 8; i++) {
+ p[i] = (uint8_t)(total >> 8 * (7 - i)) & 0xFF;
+ }
+ } else {
+ for(int i = 0; i < 8; i++) {
+ p[i] = (uint8_t)(step >> 8 * (7 - i)) & 0xFF;
+ }
+ }
+
+ size_t size = sizeof(uint64_t);
+ for(uint8_t i = 0; i < size; i++) {
+ if(p[i] != 0) {
+ furi_string_cat_printf(candidate, "%02X", p[i]);
+ } else {
+ furi_string_cat_printf(candidate, "%s", "00");
+ }
+
+ if(i < size - 1) {
+ furi_string_push_back(candidate, ' ');
+ }
+ }
+
+#ifdef FURI_DEBUG
+ FURI_LOG_D(TAG, "candidate: %s, step: %lld", furi_string_get_cstr(candidate), step);
+#endif
+}
+
+void subbrute_protocol_default_payload(
+ Stream* stream,
+ SubBruteFileProtocol file,
+ uint64_t step,
+ uint8_t bits,
+ uint32_t te,
+ uint8_t repeat) {
+ FuriString* candidate = furi_string_alloc();
+ subbrute_protocol_create_candidate_for_default(candidate, file, step);
+
+#ifdef FURI_DEBUG
+ FURI_LOG_D(
+ TAG,
+ "candidate: %s, step: %lld, repeat: %d, te: %s",
+ furi_string_get_cstr(candidate),
+ step,
+ repeat,
+ te ? "true" : "false");
+#endif
+ stream_clean(stream);
+ if(te) {
+ stream_write_format(
+ stream,
+ subbrute_key_small_with_tail,
+ bits,
+ furi_string_get_cstr(candidate),
+ te,
+ repeat);
+ } else {
+ stream_write_format(
+ stream, subbrute_key_small_no_tail, bits, furi_string_get_cstr(candidate), repeat);
+ }
+
+ furi_string_free(candidate);
+}
+
+void subbrute_protocol_file_payload(
+ Stream* stream,
+ uint64_t step,
+ uint8_t bits,
+ uint32_t te,
+ uint8_t repeat,
+ uint8_t bit_index,
+ uint64_t file_key,
+ bool two_bytes) {
+ FuriString* candidate = furi_string_alloc();
+ subbrute_protocol_create_candidate_for_existing_file(
+ candidate, step, bit_index, file_key, two_bytes);
+
+#ifdef FURI_DEBUG
+ FURI_LOG_D(
+ TAG,
+ "candidate: %s, step: %lld, repeat: %d, te: %s",
+ furi_string_get_cstr(candidate),
+ step,
+ repeat,
+ te ? "true" : "false");
+#endif
+ stream_clean(stream);
+
+ if(te) {
+ stream_write_format(
+ stream,
+ subbrute_key_small_with_tail,
+ bits,
+ furi_string_get_cstr(candidate),
+ te,
+ repeat);
+ } else {
+ stream_write_format(
+ stream, subbrute_key_small_no_tail, bits, furi_string_get_cstr(candidate), repeat);
+ }
+
+ furi_string_free(candidate);
+}
+
+void subbrute_protocol_default_generate_file(
+ Stream* stream,
+ uint32_t frequency,
+ FuriHalSubGhzPreset preset,
+ SubBruteFileProtocol file,
+ uint64_t step,
+ uint8_t bits,
+ uint32_t te,
+ uint8_t repeat) {
+ FuriString* candidate = furi_string_alloc();
+ subbrute_protocol_create_candidate_for_default(candidate, file, step);
+
+#ifdef FURI_DEBUG
+ FURI_LOG_D(TAG, "candidate: %s, step: %lld", furi_string_get_cstr(candidate), step);
+#endif
+ stream_clean(stream);
+
+ if(te) {
+ stream_write_format(
+ stream,
+ subbrute_key_file_start_with_tail,
+ frequency,
+ subbrute_protocol_preset(preset),
+ subbrute_protocol_file(file),
+ bits,
+ furi_string_get_cstr(candidate),
+ te,
+ repeat);
+ } else {
+ stream_write_format(
+ stream,
+ subbrute_key_file_start_no_tail,
+ frequency,
+ subbrute_protocol_preset(preset),
+ subbrute_protocol_file(file),
+ bits,
+ furi_string_get_cstr(candidate),
+ repeat);
+ }
+
+ furi_string_free(candidate);
+}
+
+void subbrute_protocol_file_generate_file(
+ Stream* stream,
+ uint32_t frequency,
+ FuriHalSubGhzPreset preset,
+ SubBruteFileProtocol file,
+ uint64_t step,
+ uint8_t bits,
+ uint32_t te,
+ uint8_t repeat,
+ uint8_t bit_index,
+ uint64_t file_key,
+ bool two_bytes) {
+ FuriString* candidate = furi_string_alloc();
+ // char subbrute_payload_byte[8];
+ //furi_string_set_str(candidate, file_key);
+ subbrute_protocol_create_candidate_for_existing_file(
+ candidate, step, bit_index, file_key, two_bytes);
+
+ stream_clean(stream);
+ if(te) {
+ stream_write_format(
+ stream,
+ subbrute_key_file_start_with_tail,
+ frequency,
+ subbrute_protocol_preset(preset),
+ subbrute_protocol_file(file),
+ bits,
+ furi_string_get_cstr(candidate),
+ te,
+ repeat);
+ } else {
+ stream_write_format(
+ stream,
+ subbrute_key_file_start_no_tail,
+ frequency,
+ subbrute_protocol_preset(preset),
+ subbrute_protocol_file(file),
+ bits,
+ furi_string_get_cstr(candidate),
+ repeat);
+ }
+
+ furi_string_free(candidate);
+}
+
+uint64_t
+ subbrute_protocol_calc_max_value(SubBruteAttacks attack_type, uint8_t bits, bool two_bytes) {
+ uint64_t max_value;
+ if(attack_type == SubBruteAttackLoadFile) {
+ max_value = two_bytes ? 0xFFFF : 0xFF;
+ } else if(
+ attack_type == SubBruteAttackSMC532624bit330 ||
+ attack_type == SubBruteAttackSMC532624bit433 ||
+ attack_type == SubBruteAttackUNILARM24bit330 ||
+ attack_type == SubBruteAttackUNILARM24bit433 ||
+ attack_type == SubBruteAttackPT226024bit315 ||
+ attack_type == SubBruteAttackPT226024bit330 ||
+ attack_type == SubBruteAttackPT226024bit390 ||
+ attack_type == SubBruteAttackPT226024bit433) {
+ max_value = 6561;
+ } else {
+ FuriString* max_value_s;
+ max_value_s = furi_string_alloc();
+ for(uint8_t i = 0; i < bits; i++) {
+ furi_string_cat_printf(max_value_s, "1");
+ }
+ max_value = (uint64_t)strtol(furi_string_get_cstr(max_value_s), NULL, 2);
+ furi_string_free(max_value_s);
+ }
+
+ return max_value;
+}
\ No newline at end of file
diff --git a/applications/external/subghz_bruteforcer/subbrute_protocols.h b/applications/external/subghz_bruteforcer/subbrute_protocols.h
new file mode 100644
index 000000000..2f41b185b
--- /dev/null
+++ b/applications/external/subghz_bruteforcer/subbrute_protocols.h
@@ -0,0 +1,128 @@
+#pragma once
+
+#include
+#include
+#include
+#include
+
+typedef enum {
+ CAMEFileProtocol,
+ NICEFileProtocol,
+ ChamberlainFileProtocol,
+ LinearFileProtocol,
+ LinearDeltaFileProtocol,
+ PrincetonFileProtocol,
+ RAWFileProtocol,
+ BETTFileProtocol,
+ ClemsaFileProtocol,
+ DoitrandFileProtocol,
+ GateTXFileProtocol,
+ MagellanFileProtocol,
+ IntertechnoV3FileProtocol,
+ AnsonicFileProtocol,
+ SMC5326FileProtocol,
+ UNILARMFileProtocol,
+ PT2260FileProtocol,
+ HoneywellFileProtocol,
+ HoltekFileProtocol,
+ UnknownFileProtocol,
+ TotalFileProtocol,
+} SubBruteFileProtocol;
+
+typedef enum {
+ SubBruteAttackCAME12bit303,
+ SubBruteAttackCAME12bit307,
+ SubBruteAttackCAME12bit315,
+ SubBruteAttackCAME12bit433,
+ SubBruteAttackCAME12bit868,
+ SubBruteAttackNICE12bit433,
+ SubBruteAttackNICE12bit868,
+ SubBruteAttackAnsonic12bit433075,
+ SubBruteAttackAnsonic12bit433,
+ SubBruteAttackAnsonic12bit434,
+ SubBruteAttackHoltek12bitFM433,
+ SubBruteAttackHoltek12bitAM433,
+ SubBruteAttackHoltek12bitAM315,
+ SubBruteAttackHoltek12bitAM868,
+ SubBruteAttackHoltek12bitAM915,
+ SubBruteAttackChamberlain9bit300,
+ SubBruteAttackChamberlain9bit315,
+ SubBruteAttackChamberlain9bit390,
+ SubBruteAttackChamberlain9bit433,
+ SubBruteAttackChamberlain8bit300,
+ SubBruteAttackChamberlain8bit315,
+ SubBruteAttackChamberlain8bit390,
+ SubBruteAttackChamberlain7bit300,
+ SubBruteAttackChamberlain7bit315,
+ SubBruteAttackChamberlain7bit390,
+ SubBruteAttackLinear10bit300,
+ SubBruteAttackLinear10bit310,
+ SubBruteAttackLinearDelta8bit310,
+ SubBruteAttackUNILARM24bit330,
+ SubBruteAttackUNILARM24bit433,
+ SubBruteAttackSMC532624bit330,
+ SubBruteAttackSMC532624bit433,
+ SubBruteAttackPT226024bit315,
+ SubBruteAttackPT226024bit330,
+ SubBruteAttackPT226024bit390,
+ SubBruteAttackPT226024bit433,
+ SubBruteAttackLoadFile,
+ SubBruteAttackTotalCount,
+} SubBruteAttacks;
+
+typedef struct {
+ uint32_t frequency;
+ uint8_t bits;
+ uint32_t te;
+ uint8_t repeat;
+ FuriHalSubGhzPreset preset;
+ SubBruteFileProtocol file;
+} SubBruteProtocol;
+
+const SubBruteProtocol* subbrute_protocol(SubBruteAttacks index);
+const char* subbrute_protocol_preset(FuriHalSubGhzPreset preset);
+const char* subbrute_protocol_file(SubBruteFileProtocol protocol);
+FuriHalSubGhzPreset subbrute_protocol_convert_preset(FuriString* preset_name);
+SubBruteFileProtocol subbrute_protocol_file_protocol_name(FuriString* name);
+uint8_t subbrute_protocol_repeats_count(SubBruteAttacks index);
+const char* subbrute_protocol_name(SubBruteAttacks index);
+
+void subbrute_protocol_default_payload(
+ Stream* stream,
+ SubBruteFileProtocol file,
+ uint64_t step,
+ uint8_t bits,
+ uint32_t te,
+ uint8_t repeat);
+void subbrute_protocol_file_payload(
+ Stream* stream,
+ uint64_t step,
+ uint8_t bits,
+ uint32_t te,
+ uint8_t repeat,
+ uint8_t bit_index,
+ uint64_t file_key,
+ bool two_bytes);
+void subbrute_protocol_default_generate_file(
+ Stream* stream,
+ uint32_t frequency,
+ FuriHalSubGhzPreset preset,
+ SubBruteFileProtocol file,
+ uint64_t step,
+ uint8_t bits,
+ uint32_t te,
+ uint8_t repeat);
+void subbrute_protocol_file_generate_file(
+ Stream* stream,
+ uint32_t frequency,
+ FuriHalSubGhzPreset preset,
+ SubBruteFileProtocol file,
+ uint64_t step,
+ uint8_t bits,
+ uint32_t te,
+ uint8_t repeat,
+ uint8_t bit_index,
+ uint64_t file_key,
+ bool two_bytes);
+uint64_t
+ subbrute_protocol_calc_max_value(SubBruteAttacks attack_type, uint8_t bits, bool two_bytes);
\ No newline at end of file
diff --git a/applications/external/subghz_bruteforcer/views/subbrute_attack_view.c b/applications/external/subghz_bruteforcer/views/subbrute_attack_view.c
new file mode 100644
index 000000000..d7770bb44
--- /dev/null
+++ b/applications/external/subghz_bruteforcer/views/subbrute_attack_view.c
@@ -0,0 +1,341 @@
+#include "subbrute_attack_view.h"
+#include "../subbrute_i.h"
+#include "../subbrute_protocols.h"
+#include "../helpers/gui_top_buttons.h"
+
+#include
+#include
+#include
+#include
+#include
+
+#define TAG "SubBruteAttackView"
+
+struct SubBruteAttackView {
+ View* view;
+ SubBruteAttackViewCallback callback;
+ void* context;
+ SubBruteAttacks attack_type;
+ uint64_t max_value;
+ uint64_t current_step;
+ bool is_attacking;
+ uint8_t extra_repeats;
+};
+
+typedef struct {
+ SubBruteAttacks attack_type;
+ uint64_t max_value;
+ uint64_t current_step;
+ uint8_t extra_repeats;
+ bool is_attacking;
+ IconAnimation* icon;
+} SubBruteAttackViewModel;
+
+void subbrute_attack_view_set_callback(
+ SubBruteAttackView* instance,
+ SubBruteAttackViewCallback callback,
+ void* context) {
+ furi_assert(instance);
+ furi_assert(callback);
+
+ instance->callback = callback;
+ instance->context = context;
+}
+
+bool subbrute_attack_view_input(InputEvent* event, void* context) {
+ furi_assert(event);
+ furi_assert(context);
+ SubBruteAttackView* instance = context;
+#ifdef FURI_DEBUG
+ FURI_LOG_D(TAG, "InputKey: %d", event->key);
+#endif
+
+ if(event->key == InputKeyBack && event->type == InputTypeShort) {
+ instance->is_attacking = false;
+ with_view_model(
+ instance->view,
+ SubBruteAttackViewModel * model,
+ { model->is_attacking = false; },
+ true);
+
+ instance->callback(SubBruteCustomEventTypeBackPressed, instance->context);
+ return true;
+ }
+
+ bool update = false;
+
+ if(!instance->is_attacking) {
+ if(event->type == InputTypeShort && event->key == InputKeyOk) {
+#ifdef FURI_DEBUG
+ FURI_LOG_D(TAG, "InputKey: %d OK", event->key);
+#endif
+ instance->is_attacking = true;
+ instance->callback(SubBruteCustomEventTypeTransmitStarted, instance->context);
+ update = true;
+ } else if(event->key == InputKeyUp) {
+ instance->callback(SubBruteCustomEventTypeSaveFile, instance->context);
+ update = true;
+ } else if(event->key == InputKeyDown) {
+ instance->callback(SubBruteCustomEventTypeTransmitCustom, instance->context);
+ update = true;
+ } else if(event->type == InputTypeShort) {
+ if(event->key == InputKeyLeft) {
+ instance->callback(SubBruteCustomEventTypeChangeStepDown, instance->context);
+ } else if(event->key == InputKeyRight) {
+ instance->callback(SubBruteCustomEventTypeChangeStepUp, instance->context);
+ }
+ update = true;
+ } else if(event->type == InputTypeRepeat) {
+ if(event->key == InputKeyLeft) {
+ instance->callback(SubBruteCustomEventTypeChangeStepDownMore, instance->context);
+ } else if(event->key == InputKeyRight) {
+ instance->callback(SubBruteCustomEventTypeChangeStepUpMore, instance->context);
+ }
+ update = true;
+ }
+ } else {
+ // ATTACK Mode!
+ if((event->type == InputTypeShort || event->type == InputTypeRepeat) &&
+ (event->key == InputKeyOk || event->key == InputKeyBack)) {
+ instance->is_attacking = false;
+ instance->callback(SubBruteCustomEventTypeTransmitNotStarted, instance->context);
+
+ update = true;
+ }
+ }
+
+ if(update) {
+ with_view_model(
+ instance->view,
+ SubBruteAttackViewModel * model,
+ {
+ if(model->is_attacking != instance->is_attacking) {
+ if(instance->is_attacking) {
+ icon_animation_stop(model->icon);
+ icon_animation_start(model->icon);
+ } else {
+ icon_animation_stop(model->icon);
+ }
+ }
+
+ model->attack_type = instance->attack_type;
+ model->max_value = instance->max_value;
+ model->current_step = instance->current_step;
+ model->is_attacking = instance->is_attacking;
+ },
+ true);
+ }
+
+ return true;
+}
+
+SubBruteAttackView* subbrute_attack_view_alloc() {
+ SubBruteAttackView* instance = malloc(sizeof(SubBruteAttackView));
+
+ instance->view = view_alloc();
+ view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(SubBruteAttackViewModel));
+ view_set_context(instance->view, instance);
+
+ with_view_model(
+ instance->view,
+ SubBruteAttackViewModel * model,
+ {
+ model->icon = icon_animation_alloc(&A_Sub1ghz_14);
+ view_tie_icon_animation(instance->view, model->icon);
+ },
+ true);
+
+ view_set_draw_callback(instance->view, (ViewDrawCallback)subbrute_attack_view_draw);
+ view_set_input_callback(instance->view, subbrute_attack_view_input);
+ view_set_enter_callback(instance->view, subbrute_attack_view_enter);
+ view_set_exit_callback(instance->view, subbrute_attack_view_exit);
+
+ instance->attack_type = SubBruteAttackTotalCount;
+ instance->max_value = 0x00;
+ instance->current_step = 0;
+ instance->is_attacking = false;
+
+ return instance;
+}
+
+void subbrute_attack_view_enter(void* context) {
+ furi_assert(context);
+
+#ifdef FURI_DEBUG
+ FURI_LOG_D(TAG, "subbrute_attack_view_enter");
+#endif
+}
+
+void subbrute_attack_view_free(SubBruteAttackView* instance) {
+ furi_assert(instance);
+
+#ifdef FURI_DEBUG
+ FURI_LOG_D(TAG, "subbrute_attack_view_free");
+#endif
+
+ with_view_model(
+ instance->view,
+ SubBruteAttackViewModel * model,
+ { icon_animation_free(model->icon); },
+ false);
+
+ view_free(instance->view);
+ free(instance);
+}
+
+View* subbrute_attack_view_get_view(SubBruteAttackView* instance) {
+ furi_assert(instance);
+ return instance->view;
+}
+
+void subbrute_attack_view_set_current_step(SubBruteAttackView* instance, uint64_t current_step) {
+ furi_assert(instance);
+#ifdef FURI_DEBUG
+ //FURI_LOG_D(TAG, "Set step: %d", current_step);
+#endif
+ instance->current_step = current_step;
+ with_view_model(
+ instance->view,
+ SubBruteAttackViewModel * model,
+ { model->current_step = current_step; },
+ true);
+}
+
+// We need to call init every time, because not every time we calls enter
+// normally, call enter only once
+void subbrute_attack_view_init_values(
+ SubBruteAttackView* instance,
+ uint8_t index,
+ uint64_t max_value,
+ uint64_t current_step,
+ bool is_attacking,
+ uint8_t extra_repeats) {
+#ifdef FURI_DEBUG
+ FURI_LOG_I(
+ TAG,
+ "INIT, attack_type: %d, max_value: %lld, current_step: %lld, extra_repeats: %d",
+ index,
+ max_value,
+ current_step,
+ extra_repeats);
+#endif
+ instance->attack_type = index;
+ instance->max_value = max_value;
+ instance->current_step = current_step;
+ instance->is_attacking = is_attacking;
+ instance->extra_repeats = extra_repeats;
+
+ with_view_model(
+ instance->view,
+ SubBruteAttackViewModel * model,
+ {
+ model->max_value = max_value;
+ model->attack_type = index;
+ model->current_step = current_step;
+ model->is_attacking = is_attacking;
+ model->extra_repeats = extra_repeats;
+ if(is_attacking) {
+ icon_animation_start(model->icon);
+ } else {
+ icon_animation_stop(model->icon);
+ }
+ },
+ true);
+}
+
+void subbrute_attack_view_exit(void* context) {
+ furi_assert(context);
+ SubBruteAttackView* instance = context;
+#ifdef FURI_DEBUG
+ FURI_LOG_D(TAG, "subbrute_attack_view_exit");
+#endif
+ with_view_model(
+ instance->view,
+ SubBruteAttackViewModel * model,
+ { icon_animation_stop(model->icon); },
+ false);
+}
+
+void subbrute_attack_view_draw(Canvas* canvas, void* context) {
+ furi_assert(context);
+ SubBruteAttackViewModel* model = (SubBruteAttackViewModel*)context;
+ char buffer[64];
+
+ const char* attack_name = NULL;
+ attack_name = subbrute_protocol_name(model->attack_type);
+
+ // Title
+ if(model->is_attacking) {
+ canvas_set_color(canvas, ColorBlack);
+ canvas_set_font(canvas, FontSecondary);
+ canvas_draw_str_aligned(canvas, 64, 2, AlignCenter, AlignTop, attack_name);
+ }
+
+ // Current Step / Max value
+ const uint8_t y_frequency = 17;
+ if(model->max_value > 9999) {
+ canvas_set_font(canvas, FontBigNumbers);
+ snprintf(buffer, sizeof(buffer), "%05d/", (int)model->current_step);
+ canvas_draw_str_aligned(canvas, 5, y_frequency, AlignLeft, AlignTop, buffer);
+
+ // Second part with another font, because BigNumber is out of screen bounds
+ canvas_set_font(canvas, FontPrimary);
+ snprintf(buffer, sizeof(buffer), "%05d", (int)model->max_value);
+ canvas_draw_str_aligned(canvas, 107, y_frequency + 13, AlignRight, AlignBottom, buffer);
+ } else if(model->max_value <= 0xFF) {
+ canvas_set_font(canvas, FontBigNumbers);
+ snprintf(
+ buffer, sizeof(buffer), "%03d/%03d", (int)model->current_step, (int)model->max_value);
+ canvas_draw_str_aligned(canvas, 64, y_frequency, AlignCenter, AlignTop, buffer);
+ } else {
+ canvas_set_font(canvas, FontBigNumbers);
+ snprintf(
+ buffer, sizeof(buffer), "%04d/%04d", (int)model->current_step, (int)model->max_value);
+ canvas_draw_str_aligned(canvas, 64, y_frequency, AlignCenter, AlignTop, buffer);
+ }
+ canvas_set_font(canvas, FontSecondary);
+
+ memset(buffer, 0, sizeof(buffer));
+ if(!model->is_attacking) {
+ canvas_set_color(canvas, ColorBlack);
+ canvas_set_font(canvas, FontSecondary);
+ canvas_draw_str_aligned(canvas, 64, 44, AlignCenter, AlignBottom, attack_name);
+
+ snprintf(
+ buffer,
+ sizeof(buffer),
+ "x%d",
+ model->extra_repeats + subbrute_protocol_repeats_count(model->attack_type));
+ canvas_draw_str_aligned(canvas, 60, 6, AlignCenter, AlignCenter, buffer);
+
+ elements_button_left(canvas, "-1");
+ elements_button_right(canvas, "+1");
+ elements_button_center(canvas, "Start");
+ elements_button_top_left(canvas, "Save");
+ elements_button_top_right(canvas, "Resend");
+ } else {
+ // canvas_draw_icon_animation
+ const uint8_t icon_h_offset = 0;
+ const uint8_t icon_width_with_offset =
+ icon_animation_get_width(model->icon) + icon_h_offset;
+ const uint8_t icon_v_offset = icon_animation_get_height(model->icon); // + vertical_offset;
+ const uint8_t x = canvas_width(canvas);
+ const uint8_t y = canvas_height(canvas);
+ canvas_draw_icon_animation(
+ canvas, x - icon_width_with_offset, y - icon_v_offset, model->icon);
+ // Progress bar
+ // Resolution: 128x64 px
+ float progress_value = (float)model->current_step / model->max_value;
+ elements_progress_bar(canvas, 8, 37, 110, progress_value > 1 ? 1 : progress_value);
+
+ snprintf(
+ buffer,
+ sizeof(buffer),
+ "x%d",
+ model->extra_repeats + subbrute_protocol_repeats_count(model->attack_type));
+ canvas_draw_str(canvas, 4, y - 8, buffer);
+ canvas_draw_str(canvas, 4, y - 1, "repeats");
+
+ elements_button_center(canvas, "Stop");
+ }
+}
diff --git a/applications/external/subghz_bruteforcer/views/subbrute_attack_view.h b/applications/external/subghz_bruteforcer/views/subbrute_attack_view.h
new file mode 100644
index 000000000..55e3a8222
--- /dev/null
+++ b/applications/external/subghz_bruteforcer/views/subbrute_attack_view.h
@@ -0,0 +1,25 @@
+#pragma once
+
+#include "../subbrute_custom_event.h"
+#include
+#include
+#include
+
+typedef void (*SubBruteAttackViewCallback)(SubBruteCustomEvent event, void* context);
+typedef struct SubBruteAttackView SubBruteAttackView;
+
+void subbrute_attack_view_set_callback(
+ SubBruteAttackView* instance,
+ SubBruteAttackViewCallback callback,
+ void* context);
+SubBruteAttackView* subbrute_attack_view_alloc();
+void subbrute_attack_view_free(SubBruteAttackView* instance);
+View* subbrute_attack_view_get_view(SubBruteAttackView* instance);
+void subbrute_attack_view_set_current_step(SubBruteAttackView* instance, uint64_t current_step);
+void subbrute_attack_view_init_values(
+ SubBruteAttackView* instance,
+ uint8_t index,
+ uint64_t max_value,
+ uint64_t current_step,
+ bool is_attacking,
+ uint8_t extra_repeats);
\ No newline at end of file
diff --git a/applications/external/subghz_bruteforcer/views/subbrute_main_view.c b/applications/external/subghz_bruteforcer/views/subbrute_main_view.c
new file mode 100644
index 000000000..c21f2ea33
--- /dev/null
+++ b/applications/external/subghz_bruteforcer/views/subbrute_main_view.c
@@ -0,0 +1,463 @@
+#include "subbrute_main_view.h"
+#include "../subbrute_i.h"
+#include "../subbrute_protocols.h"
+#include "../helpers/gui_top_buttons.h"
+
+#include
+#include
+#include
+
+#define STATUS_BAR_Y_SHIFT 14
+#define TAG "SubBruteMainView"
+
+#define ITEMS_ON_SCREEN 3
+#define ITEMS_INTERVAL 1
+#define ITEM_WIDTH 14
+#define ITEM_Y 27
+#define ITEM_HEIGHT 13
+#define TEXT_X 6
+#define TEXT_Y 37
+#define TEXT_INTERVAL 3
+#define TEXT_WIDTH 12
+#define ITEM_FRAME_RADIUS 2
+
+struct SubBruteMainView {
+ View* view;
+ SubBruteMainViewCallback callback;
+ void* context;
+ uint8_t index;
+ bool is_select_byte;
+ bool two_bytes;
+ uint64_t key_from_file;
+ uint8_t extra_repeats;
+ uint8_t window_position;
+};
+
+typedef struct {
+ uint8_t index;
+ uint8_t extra_repeats;
+ uint8_t window_position;
+ bool is_select_byte;
+ bool two_bytes;
+ uint64_t key_from_file;
+} SubBruteMainViewModel;
+
+void subbrute_main_view_set_callback(
+ SubBruteMainView* instance,
+ SubBruteMainViewCallback callback,
+ void* context) {
+ furi_assert(instance);
+ furi_assert(callback);
+
+ instance->callback = callback;
+ instance->context = context;
+}
+
+void subbrute_main_view_center_displayed_key(
+ Canvas* canvas,
+ uint64_t key,
+ uint8_t index,
+ bool two_bytes) {
+ uint8_t text_x = TEXT_X;
+ uint8_t item_x = TEXT_X - ITEMS_INTERVAL;
+ canvas_set_font(canvas, FontSecondary);
+
+ for(int i = 0; i < 8; i++) {
+ char current_value[3] = {0};
+ uint8_t byte_value = (uint8_t)(key >> 8 * (7 - i)) & 0xFF;
+ snprintf(current_value, sizeof(current_value), "%02X", byte_value);
+
+ // For two bytes we need to select prev location
+ if(!two_bytes && i == index) {
+ canvas_set_color(canvas, ColorBlack);
+ canvas_draw_rbox(
+ canvas, item_x - 1, ITEM_Y, ITEM_WIDTH + 1, ITEM_HEIGHT, ITEM_FRAME_RADIUS);
+ canvas_set_color(canvas, ColorWhite);
+ canvas_draw_str(canvas, text_x, TEXT_Y, current_value);
+ } else if(two_bytes && (i == index || i == index - 1)) {
+ if(i == index) {
+ canvas_set_color(canvas, ColorBlack);
+ canvas_draw_rbox(
+ canvas,
+ item_x - ITEMS_INTERVAL - ITEM_WIDTH - 1,
+ ITEM_Y,
+ ITEM_WIDTH * 2 + ITEMS_INTERVAL * 2 + 1,
+ ITEM_HEIGHT,
+ ITEM_FRAME_RADIUS);
+
+ canvas_set_color(canvas, ColorWhite);
+ canvas_draw_str(canvas, text_x, TEXT_Y, current_value);
+
+ // Redraw prev element with white
+ memset(current_value, 0, sizeof(current_value));
+ byte_value = (uint8_t)(key >> 8 * (7 - i + 1)) & 0xFF;
+ snprintf(current_value, sizeof(current_value), "%02X", byte_value);
+ canvas_draw_str(
+ canvas, text_x - (TEXT_WIDTH + TEXT_INTERVAL), TEXT_Y, current_value);
+ } else {
+ canvas_set_color(canvas, ColorWhite);
+ canvas_draw_str(canvas, text_x, TEXT_Y, current_value);
+ }
+ } else {
+ canvas_set_color(canvas, ColorBlack);
+ canvas_draw_str(canvas, text_x, TEXT_Y, current_value);
+ }
+ text_x = text_x + TEXT_WIDTH + TEXT_INTERVAL;
+ item_x = item_x + ITEM_WIDTH + ITEMS_INTERVAL;
+ }
+
+ // Return normal color
+ canvas_set_color(canvas, ColorBlack);
+}
+
+void subbrute_main_view_draw(Canvas* canvas, SubBruteMainViewModel* model) {
+ uint16_t screen_width = canvas_width(canvas);
+ uint16_t screen_height = canvas_height(canvas);
+
+ if(model->is_select_byte) {
+#ifdef FURI_DEBUG
+ //FURI_LOG_D(TAG, "key_from_file: %s", model->key_from_file);
+#endif
+ //char msg_index[18];
+ //snprintf(msg_index, sizeof(msg_index), "Field index: %d", model->index);
+ canvas_set_font(canvas, FontSecondary);
+ canvas_draw_str_aligned(
+ canvas, 64, 17, AlignCenter, AlignTop, "Please select values to calc:");
+
+ subbrute_main_view_center_displayed_key(
+ canvas, model->key_from_file, model->index, model->two_bytes);
+ //const char* line = furi_string_get_cstr(menu_items);
+ //canvas_set_font(canvas, FontSecondary);
+ //canvas_draw_str_aligned(
+ // canvas, 64, 37, AlignCenter, AlignTop, furi_string_get_cstr(menu_items));
+
+ elements_button_center(canvas, "Select");
+ if(model->index > 0) {
+ elements_button_left(canvas, " ");
+ }
+ if(model->index < 7) {
+ elements_button_right(canvas, " ");
+ }
+ // Switch to another mode
+ if(model->two_bytes) {
+ elements_button_top_left(canvas, "One byte");
+ } else {
+ elements_button_top_left(canvas, "Two bytes");
+ }
+ } else {
+ // Title
+ canvas_set_font(canvas, FontPrimary);
+ canvas_draw_box(canvas, 0, 0, canvas_width(canvas), STATUS_BAR_Y_SHIFT);
+ canvas_invert_color(canvas);
+ canvas_draw_str_aligned(canvas, 64, 3, AlignCenter, AlignTop, SUBBRUTEFORCER_VER);
+ canvas_invert_color(canvas);
+
+ // Menu
+ canvas_set_color(canvas, ColorBlack);
+ canvas_set_font(canvas, FontSecondary);
+ const uint8_t item_height = 16;
+
+#ifdef FURI_DEBUG
+ //FURI_LOG_D(TAG, "window_position: %d, index: %d", model->window_position, model->index);
+#endif
+ for(uint8_t position = 0; position < SubBruteAttackTotalCount; ++position) {
+ uint8_t item_position = position - model->window_position;
+
+ if(item_position < ITEMS_ON_SCREEN) {
+ if(model->index == position) {
+ canvas_draw_str_aligned(
+ canvas,
+ 4,
+ 9 + (item_position * item_height) + STATUS_BAR_Y_SHIFT,
+ AlignLeft,
+ AlignCenter,
+ subbrute_protocol_name(position));
+
+ if(model->extra_repeats > 0) {
+ canvas_set_font(canvas, FontBatteryPercent);
+ char buffer[10];
+ snprintf(
+ buffer,
+ sizeof(buffer),
+ "x%d",
+ model->extra_repeats + subbrute_protocol_repeats_count(model->index));
+ canvas_draw_str_aligned(
+ canvas,
+ screen_width - 15,
+ 9 + (item_position * item_height) + STATUS_BAR_Y_SHIFT,
+ AlignLeft,
+ AlignCenter,
+ buffer);
+ canvas_set_font(canvas, FontSecondary);
+ }
+
+ elements_frame(
+ canvas, 1, 1 + (item_position * item_height) + STATUS_BAR_Y_SHIFT, 124, 15);
+ } else {
+ canvas_draw_str_aligned(
+ canvas,
+ 4,
+ 9 + (item_position * item_height) + STATUS_BAR_Y_SHIFT,
+ AlignLeft,
+ AlignCenter,
+ subbrute_protocol_name(position));
+ }
+ }
+ }
+
+ elements_scrollbar_pos(
+ canvas,
+ screen_width,
+ STATUS_BAR_Y_SHIFT + 2,
+ screen_height - STATUS_BAR_Y_SHIFT,
+ model->index,
+ SubBruteAttackTotalCount);
+ }
+}
+
+bool subbrute_main_view_input(InputEvent* event, void* context) {
+ furi_assert(event);
+ furi_assert(context);
+
+ if(event->key == InputKeyBack && event->type == InputTypeShort) {
+#ifdef FURI_DEBUG
+ FURI_LOG_I(TAG, "InputKey: BACK");
+#endif
+ return false;
+ }
+
+ SubBruteMainView* instance = context;
+#ifdef FURI_DEBUG
+ FURI_LOG_D(TAG, "InputKey: %d, extra_repeats: %d", event->key, instance->extra_repeats);
+#endif
+ const uint8_t min_value = 0;
+ const uint8_t correct_total = SubBruteAttackTotalCount - 1;
+ uint8_t max_repeats = 9 - subbrute_protocol_repeats_count(instance->index);
+
+ bool updated = false;
+ bool consumed = false;
+ bool is_short = (event->type == InputTypeShort) || (event->type == InputTypeRepeat);
+
+ if(!instance->is_select_byte) {
+ if(event->key == InputKeyUp && is_short) {
+ if(instance->index == min_value) {
+ instance->index = correct_total;
+ } else {
+ instance->index = CLAMP(instance->index - 1, correct_total, min_value);
+ }
+ instance->extra_repeats = 0;
+ updated = true;
+ consumed = true;
+ } else if(event->key == InputKeyDown && is_short) {
+ if(instance->index == correct_total) {
+ instance->index = min_value;
+ } else {
+ instance->index = CLAMP(instance->index + 1, correct_total, min_value);
+ }
+ instance->extra_repeats = 0;
+ updated = true;
+ consumed = true;
+ } else if(event->key == InputKeyLeft && is_short) {
+ instance->extra_repeats = CLAMP(instance->extra_repeats - 1, max_repeats, 0);
+
+ updated = true;
+ consumed = true;
+ } else if(event->key == InputKeyRight && is_short) {
+ instance->extra_repeats = CLAMP(instance->extra_repeats + 1, max_repeats, 0);
+
+ updated = true;
+ consumed = true;
+ } else if(event->key == InputKeyOk && is_short) {
+ if(instance->index == SubBruteAttackLoadFile) {
+ instance->callback(SubBruteCustomEventTypeLoadFile, instance->context);
+ } else {
+ instance->callback(SubBruteCustomEventTypeMenuSelected, instance->context);
+ }
+ consumed = true;
+ updated = true;
+ }
+ if(updated) {
+ instance->window_position = instance->index;
+ if(instance->window_position > 0) {
+ instance->window_position -= 1;
+ }
+
+ if(SubBruteAttackTotalCount <= ITEMS_ON_SCREEN) {
+ instance->window_position = 0;
+ } else {
+ if(instance->window_position >= (SubBruteAttackTotalCount - ITEMS_ON_SCREEN)) {
+ instance->window_position = (SubBruteAttackTotalCount - ITEMS_ON_SCREEN);
+ }
+ }
+ }
+ } else if(is_short) {
+ if(event->key == InputKeyLeft) {
+ if((instance->index > 0 && !instance->two_bytes) ||
+ (instance->two_bytes && instance->index > 1)) {
+ instance->index--;
+ }
+ updated = true;
+ consumed = true;
+ } else if(event->key == InputKeyRight) {
+ if(instance->index < 7) {
+ instance->index++;
+ }
+ updated = true;
+ consumed = true;
+ } else if(event->key == InputKeyUp) {
+ instance->two_bytes = !instance->two_bytes;
+ // Because index is changing
+ if(instance->two_bytes && instance->index < 7) {
+ instance->index++;
+ }
+ // instance->callback(
+ // instance->two_bytes ? SubBruteCustomEventTypeChangeStepUp :
+ // SubBruteCustomEventTypeChangeStepDown,
+ // instance->context);
+
+ updated = true;
+ consumed = true;
+ } else if(event->key == InputKeyOk) {
+ instance->callback(SubBruteCustomEventTypeIndexSelected, instance->context);
+ consumed = true;
+ updated = true;
+ }
+ }
+
+ if(updated) {
+ with_view_model(
+ instance->view,
+ SubBruteMainViewModel * model,
+ {
+ model->index = instance->index;
+ model->window_position = instance->window_position;
+ model->key_from_file = instance->key_from_file;
+ model->is_select_byte = instance->is_select_byte;
+ model->two_bytes = instance->two_bytes;
+ model->extra_repeats = instance->extra_repeats;
+ },
+ true);
+ }
+
+ return consumed;
+}
+
+void subbrute_main_view_enter(void* context) {
+ furi_assert(context);
+
+#ifdef FURI_DEBUG
+ FURI_LOG_D(TAG, "subbrute_main_view_enter");
+#endif
+}
+
+void subbrute_main_view_exit(void* context) {
+ furi_assert(context);
+
+#ifdef FURI_DEBUG
+ FURI_LOG_D(TAG, "subbrute_main_view_exit");
+#endif
+}
+
+SubBruteMainView* subbrute_main_view_alloc() {
+ SubBruteMainView* instance = malloc(sizeof(SubBruteMainView));
+ instance->view = view_alloc();
+ view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(SubBruteMainViewModel));
+ view_set_context(instance->view, instance);
+ view_set_draw_callback(instance->view, (ViewDrawCallback)subbrute_main_view_draw);
+ view_set_input_callback(instance->view, subbrute_main_view_input);
+ view_set_enter_callback(instance->view, subbrute_main_view_enter);
+ view_set_exit_callback(instance->view, subbrute_main_view_exit);
+
+ instance->index = 0;
+ instance->window_position = 0;
+ instance->key_from_file = 0;
+ instance->is_select_byte = false;
+ instance->two_bytes = false;
+ instance->extra_repeats = 0;
+ with_view_model(
+ instance->view,
+ SubBruteMainViewModel * model,
+ {
+ model->index = instance->index;
+ model->window_position = instance->window_position;
+ model->key_from_file = instance->key_from_file;
+ model->is_select_byte = instance->is_select_byte;
+ model->two_bytes = instance->two_bytes;
+ model->extra_repeats = instance->extra_repeats;
+ },
+ true);
+
+ return instance;
+}
+
+void subbrute_main_view_free(SubBruteMainView* instance) {
+ furi_assert(instance);
+
+ view_free(instance->view);
+ free(instance);
+}
+
+View* subbrute_main_view_get_view(SubBruteMainView* instance) {
+ furi_assert(instance);
+ return instance->view;
+}
+
+void subbrute_main_view_set_index(
+ SubBruteMainView* instance,
+ uint8_t idx,
+ bool is_select_byte,
+ bool two_bytes,
+ uint64_t key_from_file) {
+ furi_assert(instance);
+ furi_assert(idx < SubBruteAttackTotalCount);
+#ifdef FURI_DEBUG
+ FURI_LOG_I(TAG, "Set index: %d, is_select_byte: %d", idx, is_select_byte);
+#endif
+ instance->is_select_byte = is_select_byte;
+ instance->two_bytes = two_bytes;
+ instance->key_from_file = key_from_file;
+ instance->index = idx;
+ instance->window_position = idx;
+
+ if(!is_select_byte) {
+ if(instance->window_position > 0) {
+ instance->window_position -= 1;
+ }
+
+ if(SubBruteAttackTotalCount <= ITEMS_ON_SCREEN) {
+ instance->window_position = 0;
+ } else {
+ if(instance->window_position >= (SubBruteAttackTotalCount - ITEMS_ON_SCREEN)) {
+ instance->window_position = (SubBruteAttackTotalCount - ITEMS_ON_SCREEN);
+ }
+ }
+ }
+
+ with_view_model(
+ instance->view,
+ SubBruteMainViewModel * model,
+ {
+ model->index = instance->index;
+ model->window_position = instance->window_position;
+ model->key_from_file = instance->key_from_file;
+ model->is_select_byte = instance->is_select_byte;
+ model->two_bytes = instance->two_bytes;
+ model->extra_repeats = instance->extra_repeats;
+ },
+ true);
+}
+
+SubBruteAttacks subbrute_main_view_get_index(SubBruteMainView* instance) {
+ furi_assert(instance);
+ return instance->index;
+}
+
+uint8_t subbrute_main_view_get_extra_repeats(SubBruteMainView* instance) {
+ furi_assert(instance);
+ return instance->extra_repeats;
+}
+
+bool subbrute_main_view_get_two_bytes(SubBruteMainView* instance) {
+ furi_assert(instance);
+ return instance->two_bytes;
+}
\ No newline at end of file
diff --git a/applications/external/subghz_bruteforcer/views/subbrute_main_view.h b/applications/external/subghz_bruteforcer/views/subbrute_main_view.h
new file mode 100644
index 000000000..003cd9817
--- /dev/null
+++ b/applications/external/subghz_bruteforcer/views/subbrute_main_view.h
@@ -0,0 +1,32 @@
+#pragma once
+
+#include "../subbrute_custom_event.h"
+#include "../subbrute_protocols.h"
+#include
+#include
+#include
+
+typedef void (*SubBruteMainViewCallback)(SubBruteCustomEvent event, void* context);
+typedef struct SubBruteMainView SubBruteMainView;
+
+void subbrute_main_view_set_callback(
+ SubBruteMainView* instance,
+ SubBruteMainViewCallback callback,
+ void* context);
+
+SubBruteMainView* subbrute_main_view_alloc();
+void subbrute_main_view_free(SubBruteMainView* instance);
+View* subbrute_main_view_get_view(SubBruteMainView* instance);
+void subbrute_main_view_set_index(
+ SubBruteMainView* instance,
+ uint8_t idx,
+ bool is_select_byte,
+ bool two_bytes,
+ uint64_t file_key);
+SubBruteAttacks subbrute_main_view_get_index(SubBruteMainView* instance);
+uint8_t subbrute_main_view_get_extra_repeats(SubBruteMainView* instance);
+bool subbrute_main_view_get_two_bytes(SubBruteMainView* instance);
+void subbrute_attack_view_enter(void* context);
+void subbrute_attack_view_exit(void* context);
+bool subbrute_attack_view_input(InputEvent* event, void* context);
+void subbrute_attack_view_draw(Canvas* canvas, void* context);
\ No newline at end of file
diff --git a/applications/plugins/playlist/application.fam b/applications/external/subghz_playlist/application.fam
similarity index 91%
rename from applications/plugins/playlist/application.fam
rename to applications/external/subghz_playlist/application.fam
index 06357e24b..e137cdb39 100644
--- a/applications/plugins/playlist/application.fam
+++ b/applications/external/subghz_playlist/application.fam
@@ -3,7 +3,6 @@ App(
name="Sub-GHz Playlist",
apptype=FlipperAppType.EXTERNAL,
entry_point="playlist_app",
- cdefines=["APP_PLAYLIST"],
requires=["storage", "gui", "dialogs", "subghz"],
stack_size=2 * 1024,
order=14,
diff --git a/applications/plugins/playlist/canvas_helper.c b/applications/external/subghz_playlist/canvas_helper.c
similarity index 100%
rename from applications/plugins/playlist/canvas_helper.c
rename to applications/external/subghz_playlist/canvas_helper.c
diff --git a/applications/plugins/playlist/canvas_helper.h b/applications/external/subghz_playlist/canvas_helper.h
similarity index 100%
rename from applications/plugins/playlist/canvas_helper.h
rename to applications/external/subghz_playlist/canvas_helper.h
diff --git a/applications/plugins/timelapse/icons/ButtonRight_4x7.png b/applications/external/subghz_playlist/images/ButtonRight_4x7.png
similarity index 100%
rename from applications/plugins/timelapse/icons/ButtonRight_4x7.png
rename to applications/external/subghz_playlist/images/ButtonRight_4x7.png
diff --git a/applications/external/subghz_playlist/images/sub1_10px.png b/applications/external/subghz_playlist/images/sub1_10px.png
new file mode 100644
index 000000000..5a25fdf4e
Binary files /dev/null and b/applications/external/subghz_playlist/images/sub1_10px.png differ
diff --git a/applications/plugins/playlist/playlist.c b/applications/external/subghz_playlist/playlist.c
similarity index 98%
rename from applications/plugins/playlist/playlist.c
rename to applications/external/subghz_playlist/playlist.c
index 1b16b1749..345b19927 100644
--- a/applications/plugins/playlist/playlist.c
+++ b/applications/external/subghz_playlist/playlist.c
@@ -17,6 +17,7 @@
#include
#include
+#include
#include "playlist_file.h"
#include "canvas_helper.h"
@@ -696,6 +697,7 @@ void playlist_free(Playlist* app) {
int32_t playlist_app(void* p) {
UNUSED(p);
+ DOLPHIN_DEED(DolphinDeedPluginStart);
// create playlist folder
{
@@ -713,6 +715,11 @@ int32_t playlist_app(void* p) {
// Enable power for External CC1101 if it is connected
furi_hal_subghz_enable_ext_power();
+ // Auto switch to internal radio if external radio is not available
+ furi_delay_ms(15);
+ if(!furi_hal_subghz_check_radio()) {
+ furi_hal_subghz_set_radio_type(SubGhzRadioInternal);
+ }
furi_hal_power_suppress_charge_enter();
diff --git a/applications/external/subghz_playlist/playlist_10px.png b/applications/external/subghz_playlist/playlist_10px.png
new file mode 100644
index 000000000..3d3f1d27f
Binary files /dev/null and b/applications/external/subghz_playlist/playlist_10px.png differ
diff --git a/applications/plugins/playlist/playlist_file.c b/applications/external/subghz_playlist/playlist_file.c
similarity index 100%
rename from applications/plugins/playlist/playlist_file.c
rename to applications/external/subghz_playlist/playlist_file.c
diff --git a/applications/plugins/playlist/playlist_file.h b/applications/external/subghz_playlist/playlist_file.h
similarity index 100%
rename from applications/plugins/playlist/playlist_file.h
rename to applications/external/subghz_playlist/playlist_file.h
diff --git a/applications/main/subghz_remote/application.fam b/applications/external/subghz_remote/application.fam
similarity index 55%
rename from applications/main/subghz_remote/application.fam
rename to applications/external/subghz_remote/application.fam
index e09f8500f..e89f55b82 100644
--- a/applications/main/subghz_remote/application.fam
+++ b/applications/external/subghz_remote/application.fam
@@ -1,14 +1,16 @@
App(
- appid="subghz_remote",
+ appid="SubGHz_Remote",
name="Sub-GHz Remote",
- apptype=FlipperAppType.APP,
+ apptype=FlipperAppType.EXTERNAL,
entry_point="subghz_remote_app",
cdefines=["APP_SUBGHZREMOTE"],
requires=[
"gui",
"dialogs",
],
- icon="A_SubGHzRemote_14",
stack_size=4 * 1024,
order=11,
+ fap_icon="subghz_remote_10px.png",
+ fap_category="Sub-GHz",
+ fap_icon_assets="images",
)
diff --git a/applications/external/subghz_remote/images/ButtonDown_7x4.png b/applications/external/subghz_remote/images/ButtonDown_7x4.png
new file mode 100644
index 000000000..2954bb6a6
Binary files /dev/null and b/applications/external/subghz_remote/images/ButtonDown_7x4.png differ
diff --git a/applications/external/subghz_remote/images/ButtonLeft_4x7.png b/applications/external/subghz_remote/images/ButtonLeft_4x7.png
new file mode 100644
index 000000000..0b4655d43
Binary files /dev/null and b/applications/external/subghz_remote/images/ButtonLeft_4x7.png differ
diff --git a/applications/external/subghz_remote/images/ButtonRight_4x7.png b/applications/external/subghz_remote/images/ButtonRight_4x7.png
new file mode 100644
index 000000000..8e1c74c1c
Binary files /dev/null and b/applications/external/subghz_remote/images/ButtonRight_4x7.png differ
diff --git a/applications/external/subghz_remote/images/ButtonUp_7x4.png b/applications/external/subghz_remote/images/ButtonUp_7x4.png
new file mode 100644
index 000000000..1be79328b
Binary files /dev/null and b/applications/external/subghz_remote/images/ButtonUp_7x4.png differ
diff --git a/assets/icons/MainMenu/Debug_14/frame_02.png b/applications/external/subghz_remote/images/Ok_btn_9x9.png
similarity index 73%
rename from assets/icons/MainMenu/Debug_14/frame_02.png
rename to applications/external/subghz_remote/images/Ok_btn_9x9.png
index 93b4f950e..9a1539da2 100644
Binary files a/assets/icons/MainMenu/Debug_14/frame_02.png and b/applications/external/subghz_remote/images/Ok_btn_9x9.png differ
diff --git a/assets/icons/MainMenu/Debug_14/frame_03.png b/applications/external/subghz_remote/images/Pin_arrow_down_7x9.png
similarity index 73%
rename from assets/icons/MainMenu/Debug_14/frame_03.png
rename to applications/external/subghz_remote/images/Pin_arrow_down_7x9.png
index cf55953c8..9687397af 100644
Binary files a/assets/icons/MainMenu/Debug_14/frame_03.png and b/applications/external/subghz_remote/images/Pin_arrow_down_7x9.png differ
diff --git a/assets/icons/MainMenu/Debug_14/frame_04.png b/applications/external/subghz_remote/images/Pin_arrow_left_9x7.png
similarity index 73%
rename from assets/icons/MainMenu/Debug_14/frame_04.png
rename to applications/external/subghz_remote/images/Pin_arrow_left_9x7.png
index 5f89c2e1b..fb4ded78f 100644
Binary files a/assets/icons/MainMenu/Debug_14/frame_04.png and b/applications/external/subghz_remote/images/Pin_arrow_left_9x7.png differ
diff --git a/applications/external/subghz_remote/images/Pin_arrow_right_9x7.png b/applications/external/subghz_remote/images/Pin_arrow_right_9x7.png
new file mode 100644
index 000000000..97648d176
Binary files /dev/null and b/applications/external/subghz_remote/images/Pin_arrow_right_9x7.png differ
diff --git a/applications/external/subghz_remote/images/Pin_arrow_up_7x9.png b/applications/external/subghz_remote/images/Pin_arrow_up_7x9.png
new file mode 100644
index 000000000..a91a6fd5e
Binary files /dev/null and b/applications/external/subghz_remote/images/Pin_arrow_up_7x9.png differ
diff --git a/applications/external/subghz_remote/images/Pin_cell_13x13.png b/applications/external/subghz_remote/images/Pin_cell_13x13.png
new file mode 100644
index 000000000..1b1ff0c2f
Binary files /dev/null and b/applications/external/subghz_remote/images/Pin_cell_13x13.png differ
diff --git a/applications/plugins/timelapse/icons/Pin_star_7x7.png b/applications/external/subghz_remote/images/Pin_star_7x7.png
similarity index 100%
rename from applications/plugins/timelapse/icons/Pin_star_7x7.png
rename to applications/external/subghz_remote/images/Pin_star_7x7.png
diff --git a/applications/external/subghz_remote/images/back_10px.png b/applications/external/subghz_remote/images/back_10px.png
new file mode 100644
index 000000000..f9c615a99
Binary files /dev/null and b/applications/external/subghz_remote/images/back_10px.png differ
diff --git a/applications/external/subghz_remote/images/sub1_10px.png b/applications/external/subghz_remote/images/sub1_10px.png
new file mode 100644
index 000000000..5a25fdf4e
Binary files /dev/null and b/applications/external/subghz_remote/images/sub1_10px.png differ
diff --git a/applications/external/subghz_remote/subghz_remote_10px.png b/applications/external/subghz_remote/subghz_remote_10px.png
new file mode 100644
index 000000000..c6b410f4c
Binary files /dev/null and b/applications/external/subghz_remote/subghz_remote_10px.png differ
diff --git a/applications/main/subghz_remote/subghz_remote_app.c b/applications/external/subghz_remote/subghz_remote_app.c
similarity index 98%
rename from applications/main/subghz_remote/subghz_remote_app.c
rename to applications/external/subghz_remote/subghz_remote_app.c
index 77627f5ca..df4b8465a 100644
--- a/applications/main/subghz_remote/subghz_remote_app.c
+++ b/applications/external/subghz_remote/subghz_remote_app.c
@@ -8,7 +8,7 @@
#include
#include
-#include
+#include
#include
#include
@@ -22,6 +22,8 @@
#include
#include
#include
+#include
+#include
#define SUBREMOTEMAP_FOLDER "/ext/subghz/remote"
#define SUBREMOTEMAP_EXTENSION ".txt"
@@ -491,6 +493,7 @@ void subghz_remote_tx_stop(SubGHzRemote* app) {
alutech_reset_original_btn();
nice_flors_reset_original_btn();
somfy_telis_reset_original_btn();
+ secplus2_reset_original_btn();
star_line_reset_mfname();
star_line_reset_kl_type();
}
@@ -738,6 +741,11 @@ SubGHzRemote* subghz_remote_alloc(void) {
// Enable power for External CC1101 if it is connected
furi_hal_subghz_enable_ext_power();
+ // Auto switch to internal radio if external radio is not available
+ furi_delay_ms(15);
+ if(!furi_hal_subghz_check_radio()) {
+ furi_hal_subghz_set_radio_type(SubGhzRadioInternal);
+ }
furi_hal_power_suppress_charge_enter();
@@ -797,6 +805,7 @@ void subghz_remote_free(SubGHzRemote* app, bool with_subghz) {
int32_t subghz_remote_app(void* p) {
UNUSED(p);
+ DOLPHIN_DEED(DolphinDeedPluginStart);
SubGHzRemote* app = subghz_remote_alloc();
app->file_path = furi_string_alloc();
diff --git a/applications/plugins/swd_probe/LICENSE.txt b/applications/external/swd_probe/LICENSE.txt
similarity index 100%
rename from applications/plugins/swd_probe/LICENSE.txt
rename to applications/external/swd_probe/LICENSE.txt
diff --git a/applications/plugins/swd_probe/adi.c b/applications/external/swd_probe/adi.c
similarity index 100%
rename from applications/plugins/swd_probe/adi.c
rename to applications/external/swd_probe/adi.c
diff --git a/applications/plugins/swd_probe/adi.h b/applications/external/swd_probe/adi.h
similarity index 100%
rename from applications/plugins/swd_probe/adi.h
rename to applications/external/swd_probe/adi.h
diff --git a/applications/plugins/swd_probe/application.fam b/applications/external/swd_probe/application.fam
similarity index 80%
rename from applications/plugins/swd_probe/application.fam
rename to applications/external/swd_probe/application.fam
index de2708e41..065ee8769 100644
--- a/applications/plugins/swd_probe/application.fam
+++ b/applications/external/swd_probe/application.fam
@@ -1,9 +1,8 @@
App(
appid="swd_probe",
name="[SWD] Probe",
- apptype=FlipperAppType.PLUGIN,
+ apptype=FlipperAppType.EXTERNAL,
entry_point="swd_probe_app_main",
- cdefines=["APP_SWD_PROBE"],
requires=["notification", "gui", "storage", "dialogs", "cli"],
stack_size=2 * 1024,
order=10,
diff --git a/applications/external/swd_probe/icons/ButtonDown_7x4.png b/applications/external/swd_probe/icons/ButtonDown_7x4.png
new file mode 100644
index 000000000..2954bb6a6
Binary files /dev/null and b/applications/external/swd_probe/icons/ButtonDown_7x4.png differ
diff --git a/applications/external/swd_probe/icons/ButtonUp_7x4.png b/applications/external/swd_probe/icons/ButtonUp_7x4.png
new file mode 100644
index 000000000..1be79328b
Binary files /dev/null and b/applications/external/swd_probe/icons/ButtonUp_7x4.png differ
diff --git a/applications/plugins/swd_probe/icons/app.png b/applications/external/swd_probe/icons/app.png
similarity index 100%
rename from applications/plugins/swd_probe/icons/app.png
rename to applications/external/swd_probe/icons/app.png
diff --git a/applications/plugins/swd_probe/icons/swd.png b/applications/external/swd_probe/icons/swd.png
similarity index 100%
rename from applications/plugins/swd_probe/icons/swd.png
rename to applications/external/swd_probe/icons/swd.png
diff --git a/applications/plugins/swd_probe/jep106.c b/applications/external/swd_probe/jep106.c
similarity index 100%
rename from applications/plugins/swd_probe/jep106.c
rename to applications/external/swd_probe/jep106.c
diff --git a/applications/plugins/swd_probe/jep106.h b/applications/external/swd_probe/jep106.h
similarity index 100%
rename from applications/plugins/swd_probe/jep106.h
rename to applications/external/swd_probe/jep106.h
diff --git a/applications/plugins/swd_probe/jep106.inc b/applications/external/swd_probe/jep106.inc
similarity index 100%
rename from applications/plugins/swd_probe/jep106.inc
rename to applications/external/swd_probe/jep106.inc
diff --git a/applications/plugins/swd_probe/model/chip.ply b/applications/external/swd_probe/model/chip.ply
similarity index 100%
rename from applications/plugins/swd_probe/model/chip.ply
rename to applications/external/swd_probe/model/chip.ply
diff --git a/applications/plugins/swd_probe/model/convert.py b/applications/external/swd_probe/model/convert.py
similarity index 100%
rename from applications/plugins/swd_probe/model/convert.py
rename to applications/external/swd_probe/model/convert.py
diff --git a/applications/plugins/swd_probe/model/model_chip.h b/applications/external/swd_probe/model/model_chip.h
similarity index 100%
rename from applications/plugins/swd_probe/model/model_chip.h
rename to applications/external/swd_probe/model/model_chip.h
diff --git a/applications/plugins/swd_probe/swd_probe_app.c b/applications/external/swd_probe/swd_probe_app.c
similarity index 100%
rename from applications/plugins/swd_probe/swd_probe_app.c
rename to applications/external/swd_probe/swd_probe_app.c
diff --git a/applications/plugins/swd_probe/swd_probe_app.h b/applications/external/swd_probe/swd_probe_app.h
similarity index 100%
rename from applications/plugins/swd_probe/swd_probe_app.h
rename to applications/external/swd_probe/swd_probe_app.h
diff --git a/applications/plugins/swd_probe/usb_uart.c b/applications/external/swd_probe/usb_uart.c
similarity index 100%
rename from applications/plugins/swd_probe/usb_uart.c
rename to applications/external/swd_probe/usb_uart.c
diff --git a/applications/plugins/swd_probe/usb_uart.h b/applications/external/swd_probe/usb_uart.h
similarity index 100%
rename from applications/plugins/swd_probe/usb_uart.h
rename to applications/external/swd_probe/usb_uart.h
diff --git a/applications/plugins/tama_p1/application.fam b/applications/external/tama_p1/application.fam
similarity index 100%
rename from applications/plugins/tama_p1/application.fam
rename to applications/external/tama_p1/application.fam
diff --git a/applications/plugins/tama_p1/compiled/assets_icons.h b/applications/external/tama_p1/compiled/assets_icons.h
similarity index 100%
rename from applications/plugins/tama_p1/compiled/assets_icons.h
rename to applications/external/tama_p1/compiled/assets_icons.h
diff --git a/applications/plugins/tama_p1/hal.c b/applications/external/tama_p1/hal.c
similarity index 100%
rename from applications/plugins/tama_p1/hal.c
rename to applications/external/tama_p1/hal.c
diff --git a/applications/plugins/tama_p1/hal_types.h b/applications/external/tama_p1/hal_types.h
similarity index 100%
rename from applications/plugins/tama_p1/hal_types.h
rename to applications/external/tama_p1/hal_types.h
diff --git a/applications/plugins/tama_p1/icons/icon_0.png b/applications/external/tama_p1/icons/icon_0.png
similarity index 100%
rename from applications/plugins/tama_p1/icons/icon_0.png
rename to applications/external/tama_p1/icons/icon_0.png
diff --git a/applications/plugins/tama_p1/icons/icon_1.png b/applications/external/tama_p1/icons/icon_1.png
similarity index 100%
rename from applications/plugins/tama_p1/icons/icon_1.png
rename to applications/external/tama_p1/icons/icon_1.png
diff --git a/applications/plugins/tama_p1/icons/icon_2.png b/applications/external/tama_p1/icons/icon_2.png
similarity index 100%
rename from applications/plugins/tama_p1/icons/icon_2.png
rename to applications/external/tama_p1/icons/icon_2.png
diff --git a/applications/plugins/tama_p1/icons/icon_3.png b/applications/external/tama_p1/icons/icon_3.png
similarity index 100%
rename from applications/plugins/tama_p1/icons/icon_3.png
rename to applications/external/tama_p1/icons/icon_3.png
diff --git a/applications/plugins/tama_p1/icons/icon_4.png b/applications/external/tama_p1/icons/icon_4.png
similarity index 100%
rename from applications/plugins/tama_p1/icons/icon_4.png
rename to applications/external/tama_p1/icons/icon_4.png
diff --git a/applications/plugins/tama_p1/icons/icon_5.png b/applications/external/tama_p1/icons/icon_5.png
similarity index 100%
rename from applications/plugins/tama_p1/icons/icon_5.png
rename to applications/external/tama_p1/icons/icon_5.png
diff --git a/applications/plugins/tama_p1/icons/icon_6.png b/applications/external/tama_p1/icons/icon_6.png
similarity index 100%
rename from applications/plugins/tama_p1/icons/icon_6.png
rename to applications/external/tama_p1/icons/icon_6.png
diff --git a/applications/plugins/tama_p1/icons/icon_7.png b/applications/external/tama_p1/icons/icon_7.png
similarity index 100%
rename from applications/plugins/tama_p1/icons/icon_7.png
rename to applications/external/tama_p1/icons/icon_7.png
diff --git a/applications/plugins/tama_p1/tama.h b/applications/external/tama_p1/tama.h
similarity index 100%
rename from applications/plugins/tama_p1/tama.h
rename to applications/external/tama_p1/tama.h
diff --git a/applications/external/tama_p1/tamaIcon.png b/applications/external/tama_p1/tamaIcon.png
new file mode 100644
index 000000000..ccdf23972
Binary files /dev/null and b/applications/external/tama_p1/tamaIcon.png differ
diff --git a/applications/plugins/tama_p1/tama_p1.c b/applications/external/tama_p1/tama_p1.c
similarity index 100%
rename from applications/plugins/tama_p1/tama_p1.c
rename to applications/external/tama_p1/tama_p1.c
diff --git a/applications/plugins/tama_p1/tamalib/LICENSE b/applications/external/tama_p1/tamalib/LICENSE
similarity index 100%
rename from applications/plugins/tama_p1/tamalib/LICENSE
rename to applications/external/tama_p1/tamalib/LICENSE
diff --git a/applications/plugins/tama_p1/tamalib/cpu.c b/applications/external/tama_p1/tamalib/cpu.c
similarity index 100%
rename from applications/plugins/tama_p1/tamalib/cpu.c
rename to applications/external/tama_p1/tamalib/cpu.c
diff --git a/applications/plugins/tama_p1/tamalib/cpu.h b/applications/external/tama_p1/tamalib/cpu.h
similarity index 100%
rename from applications/plugins/tama_p1/tamalib/cpu.h
rename to applications/external/tama_p1/tamalib/cpu.h
diff --git a/applications/plugins/tama_p1/tamalib/hal.h b/applications/external/tama_p1/tamalib/hal.h
similarity index 100%
rename from applications/plugins/tama_p1/tamalib/hal.h
rename to applications/external/tama_p1/tamalib/hal.h
diff --git a/applications/plugins/tama_p1/tamalib/hal_types.h.template b/applications/external/tama_p1/tamalib/hal_types.h.template
similarity index 100%
rename from applications/plugins/tama_p1/tamalib/hal_types.h.template
rename to applications/external/tama_p1/tamalib/hal_types.h.template
diff --git a/applications/plugins/tama_p1/tamalib/hw.c b/applications/external/tama_p1/tamalib/hw.c
similarity index 100%
rename from applications/plugins/tama_p1/tamalib/hw.c
rename to applications/external/tama_p1/tamalib/hw.c
diff --git a/applications/plugins/tama_p1/tamalib/hw.h b/applications/external/tama_p1/tamalib/hw.h
similarity index 100%
rename from applications/plugins/tama_p1/tamalib/hw.h
rename to applications/external/tama_p1/tamalib/hw.h
diff --git a/applications/plugins/tama_p1/tamalib/tamalib.c b/applications/external/tama_p1/tamalib/tamalib.c
similarity index 100%
rename from applications/plugins/tama_p1/tamalib/tamalib.c
rename to applications/external/tama_p1/tamalib/tamalib.c
diff --git a/applications/plugins/tama_p1/tamalib/tamalib.h b/applications/external/tama_p1/tamalib/tamalib.h
similarity index 100%
rename from applications/plugins/tama_p1/tamalib/tamalib.h
rename to applications/external/tama_p1/tamalib/tamalib.h
diff --git a/applications/plugins/tanksgame/application.fam b/applications/external/tanksgame/application.fam
similarity index 100%
rename from applications/plugins/tanksgame/application.fam
rename to applications/external/tanksgame/application.fam
diff --git a/applications/plugins/tanksgame/constants.h b/applications/external/tanksgame/constants.h
similarity index 100%
rename from applications/plugins/tanksgame/constants.h
rename to applications/external/tanksgame/constants.h
diff --git a/applications/plugins/tanksgame/images/HappyFlipper_128x64.png b/applications/external/tanksgame/images/HappyFlipper_128x64.png
similarity index 100%
rename from applications/plugins/tanksgame/images/HappyFlipper_128x64.png
rename to applications/external/tanksgame/images/HappyFlipper_128x64.png
diff --git a/applications/plugins/tanksgame/images/TanksSplashScreen_128x64.png b/applications/external/tanksgame/images/TanksSplashScreen_128x64.png
similarity index 100%
rename from applications/plugins/tanksgame/images/TanksSplashScreen_128x64.png
rename to applications/external/tanksgame/images/TanksSplashScreen_128x64.png
diff --git a/applications/plugins/tanksgame/images/enemy_down.png b/applications/external/tanksgame/images/enemy_down.png
similarity index 100%
rename from applications/plugins/tanksgame/images/enemy_down.png
rename to applications/external/tanksgame/images/enemy_down.png
diff --git a/applications/plugins/tanksgame/images/enemy_left.png b/applications/external/tanksgame/images/enemy_left.png
similarity index 100%
rename from applications/plugins/tanksgame/images/enemy_left.png
rename to applications/external/tanksgame/images/enemy_left.png
diff --git a/applications/plugins/tanksgame/images/enemy_right.png b/applications/external/tanksgame/images/enemy_right.png
similarity index 100%
rename from applications/plugins/tanksgame/images/enemy_right.png
rename to applications/external/tanksgame/images/enemy_right.png
diff --git a/applications/plugins/tanksgame/images/enemy_up.png b/applications/external/tanksgame/images/enemy_up.png
similarity index 100%
rename from applications/plugins/tanksgame/images/enemy_up.png
rename to applications/external/tanksgame/images/enemy_up.png
diff --git a/applications/plugins/tanksgame/images/projectile_down.png b/applications/external/tanksgame/images/projectile_down.png
similarity index 100%
rename from applications/plugins/tanksgame/images/projectile_down.png
rename to applications/external/tanksgame/images/projectile_down.png
diff --git a/applications/plugins/tanksgame/images/projectile_left.png b/applications/external/tanksgame/images/projectile_left.png
similarity index 100%
rename from applications/plugins/tanksgame/images/projectile_left.png
rename to applications/external/tanksgame/images/projectile_left.png
diff --git a/applications/plugins/tanksgame/images/projectile_right.png b/applications/external/tanksgame/images/projectile_right.png
similarity index 100%
rename from applications/plugins/tanksgame/images/projectile_right.png
rename to applications/external/tanksgame/images/projectile_right.png
diff --git a/applications/plugins/tanksgame/images/projectile_up.png b/applications/external/tanksgame/images/projectile_up.png
similarity index 100%
rename from applications/plugins/tanksgame/images/projectile_up.png
rename to applications/external/tanksgame/images/projectile_up.png
diff --git a/applications/plugins/tanksgame/images/tank_base.png b/applications/external/tanksgame/images/tank_base.png
similarity index 100%
rename from applications/plugins/tanksgame/images/tank_base.png
rename to applications/external/tanksgame/images/tank_base.png
diff --git a/applications/plugins/tanksgame/images/tank_down.png b/applications/external/tanksgame/images/tank_down.png
similarity index 100%
rename from applications/plugins/tanksgame/images/tank_down.png
rename to applications/external/tanksgame/images/tank_down.png
diff --git a/applications/plugins/tanksgame/images/tank_explosion.png b/applications/external/tanksgame/images/tank_explosion.png
similarity index 100%
rename from applications/plugins/tanksgame/images/tank_explosion.png
rename to applications/external/tanksgame/images/tank_explosion.png
diff --git a/applications/plugins/tanksgame/images/tank_hedgehog.png b/applications/external/tanksgame/images/tank_hedgehog.png
similarity index 100%
rename from applications/plugins/tanksgame/images/tank_hedgehog.png
rename to applications/external/tanksgame/images/tank_hedgehog.png
diff --git a/applications/plugins/tanksgame/images/tank_left.png b/applications/external/tanksgame/images/tank_left.png
similarity index 100%
rename from applications/plugins/tanksgame/images/tank_left.png
rename to applications/external/tanksgame/images/tank_left.png
diff --git a/applications/plugins/tanksgame/images/tank_right.png b/applications/external/tanksgame/images/tank_right.png
similarity index 100%
rename from applications/plugins/tanksgame/images/tank_right.png
rename to applications/external/tanksgame/images/tank_right.png
diff --git a/applications/plugins/tanksgame/images/tank_stone.png b/applications/external/tanksgame/images/tank_stone.png
similarity index 100%
rename from applications/plugins/tanksgame/images/tank_stone.png
rename to applications/external/tanksgame/images/tank_stone.png
diff --git a/applications/plugins/tanksgame/images/tank_up.png b/applications/external/tanksgame/images/tank_up.png
similarity index 100%
rename from applications/plugins/tanksgame/images/tank_up.png
rename to applications/external/tanksgame/images/tank_up.png
diff --git a/applications/plugins/tanksgame/images/tank_wall.png b/applications/external/tanksgame/images/tank_wall.png
similarity index 100%
rename from applications/plugins/tanksgame/images/tank_wall.png
rename to applications/external/tanksgame/images/tank_wall.png
diff --git a/applications/plugins/tanksgame/tanksIcon.png b/applications/external/tanksgame/tanksIcon.png
similarity index 100%
rename from applications/plugins/tanksgame/tanksIcon.png
rename to applications/external/tanksgame/tanksIcon.png
diff --git a/applications/plugins/tanksgame/tanks_game.c b/applications/external/tanksgame/tanks_game.c
similarity index 100%
rename from applications/plugins/tanksgame/tanks_game.c
rename to applications/external/tanksgame/tanks_game.c
diff --git a/applications/plugins/tetris_game/application.fam b/applications/external/tetris_game/application.fam
similarity index 87%
rename from applications/plugins/tetris_game/application.fam
rename to applications/external/tetris_game/application.fam
index 52f5588cb..21c829e99 100644
--- a/applications/plugins/tetris_game/application.fam
+++ b/applications/external/tetris_game/application.fam
@@ -3,7 +3,6 @@ App(
name="Tetris",
apptype=FlipperAppType.EXTERNAL,
entry_point="tetris_game_app",
- cdefines=["APP_TETRIS_GAME"],
requires=["gui"],
stack_size=2 * 1024,
order=240,
diff --git a/applications/plugins/tetris_game/tetris_10px.png b/applications/external/tetris_game/tetris_10px.png
similarity index 100%
rename from applications/plugins/tetris_game/tetris_10px.png
rename to applications/external/tetris_game/tetris_10px.png
diff --git a/applications/plugins/tetris_game/tetris_game.c b/applications/external/tetris_game/tetris_game.c
similarity index 100%
rename from applications/plugins/tetris_game/tetris_game.c
rename to applications/external/tetris_game/tetris_game.c
diff --git a/applications/plugins/text_viewer/LICENSE b/applications/external/text_viewer/LICENSE
similarity index 100%
rename from applications/plugins/text_viewer/LICENSE
rename to applications/external/text_viewer/LICENSE
diff --git a/applications/plugins/text_viewer/application.fam b/applications/external/text_viewer/application.fam
similarity index 90%
rename from applications/plugins/text_viewer/application.fam
rename to applications/external/text_viewer/application.fam
index dcd573c9d..518626f41 100644
--- a/applications/plugins/text_viewer/application.fam
+++ b/applications/external/text_viewer/application.fam
@@ -3,7 +3,6 @@ App(
name="Text Viewer",
apptype=FlipperAppType.EXTERNAL,
entry_point="text_viewer_app",
- cdefines=["APP_TEXT_VIEWER"],
requires=[
"gui",
"dialogs",
diff --git a/applications/plugins/text_viewer/icons/text_10px.png b/applications/external/text_viewer/icons/text_10px.png
similarity index 100%
rename from applications/plugins/text_viewer/icons/text_10px.png
rename to applications/external/text_viewer/icons/text_10px.png
diff --git a/applications/plugins/text_viewer/text_viewer.c b/applications/external/text_viewer/text_viewer.c
similarity index 100%
rename from applications/plugins/text_viewer/text_viewer.c
rename to applications/external/text_viewer/text_viewer.c
diff --git a/applications/plugins/tictactoe_game/application.fam b/applications/external/tictactoe_game/application.fam
similarity index 87%
rename from applications/plugins/tictactoe_game/application.fam
rename to applications/external/tictactoe_game/application.fam
index 2ad09f11b..09ee2eba5 100644
--- a/applications/plugins/tictactoe_game/application.fam
+++ b/applications/external/tictactoe_game/application.fam
@@ -3,7 +3,6 @@ App(
name="Tic Tac Toe",
apptype=FlipperAppType.EXTERNAL,
entry_point="tictactoe_game_app",
- cdefines=["APP_TICTACTOE_GAME"],
requires=["gui"],
stack_size=1 * 1024,
order=250,
diff --git a/applications/plugins/tictactoe_game/tictactoe_10px.png b/applications/external/tictactoe_game/tictactoe_10px.png
similarity index 100%
rename from applications/plugins/tictactoe_game/tictactoe_10px.png
rename to applications/external/tictactoe_game/tictactoe_10px.png
diff --git a/applications/plugins/tictactoe_game/tictactoe_game.c b/applications/external/tictactoe_game/tictactoe_game.c
similarity index 100%
rename from applications/plugins/tictactoe_game/tictactoe_game.c
rename to applications/external/tictactoe_game/tictactoe_game.c
diff --git a/applications/plugins/timelapse/application.fam b/applications/external/timelapse/application.fam
similarity index 100%
rename from applications/plugins/timelapse/application.fam
rename to applications/external/timelapse/application.fam
diff --git a/applications/plugins/timelapse/gpio_item.c b/applications/external/timelapse/gpio_item.c
similarity index 100%
rename from applications/plugins/timelapse/gpio_item.c
rename to applications/external/timelapse/gpio_item.c
diff --git a/applications/plugins/timelapse/gpio_item.h b/applications/external/timelapse/gpio_item.h
similarity index 100%
rename from applications/plugins/timelapse/gpio_item.h
rename to applications/external/timelapse/gpio_item.h
diff --git a/applications/external/timelapse/icons/ButtonDown_7x4.png b/applications/external/timelapse/icons/ButtonDown_7x4.png
new file mode 100644
index 000000000..2954bb6a6
Binary files /dev/null and b/applications/external/timelapse/icons/ButtonDown_7x4.png differ
diff --git a/applications/external/timelapse/icons/ButtonLeft_4x7.png b/applications/external/timelapse/icons/ButtonLeft_4x7.png
new file mode 100644
index 000000000..0b4655d43
Binary files /dev/null and b/applications/external/timelapse/icons/ButtonLeft_4x7.png differ
diff --git a/applications/external/timelapse/icons/ButtonRight_4x7.png b/applications/external/timelapse/icons/ButtonRight_4x7.png
new file mode 100644
index 000000000..8e1c74c1c
Binary files /dev/null and b/applications/external/timelapse/icons/ButtonRight_4x7.png differ
diff --git a/applications/external/timelapse/icons/ButtonUp_7x4.png b/applications/external/timelapse/icons/ButtonUp_7x4.png
new file mode 100644
index 000000000..1be79328b
Binary files /dev/null and b/applications/external/timelapse/icons/ButtonUp_7x4.png differ
diff --git a/applications/external/timelapse/icons/Pin_star_7x7.png b/applications/external/timelapse/icons/Pin_star_7x7.png
new file mode 100644
index 000000000..42fdea86e
Binary files /dev/null and b/applications/external/timelapse/icons/Pin_star_7x7.png differ
diff --git a/applications/plugins/timelapse/icons/loading_10px.png b/applications/external/timelapse/icons/loading_10px.png
similarity index 100%
rename from applications/plugins/timelapse/icons/loading_10px.png
rename to applications/external/timelapse/icons/loading_10px.png
diff --git a/applications/plugins/timelapse/zeitraffer.c b/applications/external/timelapse/zeitraffer.c
similarity index 100%
rename from applications/plugins/timelapse/zeitraffer.c
rename to applications/external/timelapse/zeitraffer.c
diff --git a/applications/plugins/timelapse/zeitraffer.png b/applications/external/timelapse/zeitraffer.png
similarity index 100%
rename from applications/plugins/timelapse/zeitraffer.png
rename to applications/external/timelapse/zeitraffer.png
diff --git a/applications/plugins/totp/LICENSE b/applications/external/totp/LICENSE
similarity index 100%
rename from applications/plugins/totp/LICENSE
rename to applications/external/totp/LICENSE
diff --git a/applications/plugins/totp/application.fam b/applications/external/totp/application.fam
similarity index 72%
rename from applications/plugins/totp/application.fam
rename to applications/external/totp/application.fam
index f6a61f119..096716e74 100644
--- a/applications/plugins/totp/application.fam
+++ b/applications/external/totp/application.fam
@@ -3,10 +3,12 @@ App(
name="Authenticator",
apptype=FlipperAppType.EXTERNAL,
entry_point="totp_app",
- cdefines=["APP_TOTP"],
- requires=["gui", "cli", "dialogs", "storage", "input", "notification"],
+ requires=["gui", "cli", "dialogs", "storage", "input", "notification", "bt"],
stack_size=2 * 1024,
order=20,
+ fap_author="Alexander Kopachov (@akopachov)",
+ fap_description="Software-based TOTP authenticator for Flipper Zero device",
+ fap_weburl="https://github.com/akopachov/flipper-zero_authenticator",
fap_category="Misc",
fap_icon_assets="images",
fap_icon="totp_10px.png",
diff --git a/applications/plugins/totp/cli/cli.c b/applications/external/totp/cli/cli.c
similarity index 95%
rename from applications/plugins/totp/cli/cli.c
rename to applications/external/totp/cli/cli.c
index 28d173766..ce2530804 100644
--- a/applications/plugins/totp/cli/cli.c
+++ b/applications/external/totp/cli/cli.c
@@ -12,6 +12,7 @@
#include "commands/pin/pin.h"
#include "commands/notification/notification.h"
#include "commands/reset/reset.h"
+#include "commands/automation/automation.h"
static void totp_cli_print_unknown_command(const FuriString* unknown_command) {
TOTP_CLI_PRINTF_ERROR(
@@ -57,6 +58,8 @@ static void totp_cli_handler(Cli* cli, FuriString* args, void* context) {
totp_cli_command_pin_handle(plugin_state, args, cli);
} else if(furi_string_cmp_str(cmd, TOTP_CLI_COMMAND_NOTIFICATION) == 0) {
totp_cli_command_notification_handle(plugin_state, args, cli);
+ } else if(furi_string_cmp_str(cmd, TOTP_CLI_COMMAND_AUTOMATION) == 0) {
+ totp_cli_command_automation_handle(plugin_state, args, cli);
} else if(furi_string_cmp_str(cmd, TOTP_CLI_COMMAND_RESET) == 0) {
totp_cli_command_reset_handle(cli, cli_context->event_queue);
} else {
diff --git a/applications/plugins/totp/cli/cli.h b/applications/external/totp/cli/cli.h
similarity index 100%
rename from applications/plugins/totp/cli/cli.h
rename to applications/external/totp/cli/cli.h
diff --git a/applications/plugins/totp/cli/cli_helpers.c b/applications/external/totp/cli/cli_helpers.c
similarity index 100%
rename from applications/plugins/totp/cli/cli_helpers.c
rename to applications/external/totp/cli/cli_helpers.c
diff --git a/applications/plugins/totp/cli/cli_helpers.h b/applications/external/totp/cli/cli_helpers.h
similarity index 100%
rename from applications/plugins/totp/cli/cli_helpers.h
rename to applications/external/totp/cli/cli_helpers.h
diff --git a/applications/plugins/totp/cli/commands/add/add.c b/applications/external/totp/cli/commands/add/add.c
similarity index 99%
rename from applications/plugins/totp/cli/commands/add/add.c
rename to applications/external/totp/cli/commands/add/add.c
index fbcd58530..91f9256b2 100644
--- a/applications/plugins/totp/cli/commands/add/add.c
+++ b/applications/external/totp/cli/commands/add/add.c
@@ -62,7 +62,7 @@ void totp_cli_command_add_docopt_usage() {
}
void totp_cli_command_add_docopt_arguments() {
- TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_ADD_ARG_NAME " Token name\r\n");
+ TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_ADD_ARG_NAME " Token name\r\n");
}
void totp_cli_command_add_docopt_options() {
diff --git a/applications/plugins/totp/cli/commands/add/add.h b/applications/external/totp/cli/commands/add/add.h
similarity index 100%
rename from applications/plugins/totp/cli/commands/add/add.h
rename to applications/external/totp/cli/commands/add/add.h
diff --git a/applications/external/totp/cli/commands/automation/automation.c b/applications/external/totp/cli/commands/automation/automation.c
new file mode 100644
index 000000000..1fd87f456
--- /dev/null
+++ b/applications/external/totp/cli/commands/automation/automation.c
@@ -0,0 +1,133 @@
+#include "automation.h"
+#include
+#include "../../../services/config/config.h"
+#include "../../../ui/scene_director.h"
+#include "../../cli_helpers.h"
+
+#define TOTP_CLI_COMMAND_AUTOMATION_ARG_METHOD "automation"
+#define TOTP_CLI_COMMAND_AUTOMATION_METHOD_NONE "none"
+#define TOTP_CLI_COMMAND_AUTOMATION_METHOD_USB "usb"
+#ifdef TOTP_BADBT_TYPE_ENABLED
+#define TOTP_CLI_COMMAND_AUTOMATION_METHOD_BT "bt"
+#endif
+
+void totp_cli_command_automation_docopt_commands() {
+ TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_AUTOMATION " Get or set automation method\r\n");
+}
+
+void totp_cli_command_automation_docopt_usage() {
+ TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_NAME " " TOTP_CLI_COMMAND_AUTOMATION " " DOCOPT_OPTIONAL(
+ DOCOPT_MULTIPLE(DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_AUTOMATION_ARG_METHOD))) "\r\n");
+}
+
+void totp_cli_command_automation_docopt_arguments() {
+ TOTP_CLI_PRINTF(
+ " " TOTP_CLI_COMMAND_AUTOMATION_ARG_METHOD
+ " Automation method to be set. Must be one of [" TOTP_CLI_COMMAND_AUTOMATION_METHOD_NONE
+ ", " TOTP_CLI_COMMAND_AUTOMATION_METHOD_USB
+#ifdef TOTP_BADBT_TYPE_ENABLED
+ ", " TOTP_CLI_COMMAND_AUTOMATION_METHOD_BT
+#endif
+ "]\r\n");
+}
+
+static void totp_cli_command_automation_print_method(AutomationMethod method, char* color) {
+#ifdef TOTP_BADBT_TYPE_ENABLED
+ bool has_previous_method = false;
+#endif
+ if(method & AutomationMethodBadUsb) {
+ TOTP_CLI_PRINTF_COLORFUL(color, "\"" TOTP_CLI_COMMAND_AUTOMATION_METHOD_USB "\"");
+#ifdef TOTP_BADBT_TYPE_ENABLED
+ has_previous_method = true;
+#endif
+ }
+
+#ifdef TOTP_BADBT_TYPE_ENABLED
+ if(method & AutomationMethodBadBt) {
+ if(has_previous_method) {
+ TOTP_CLI_PRINTF_COLORFUL(color, " and ");
+ }
+
+ TOTP_CLI_PRINTF_COLORFUL(color, "\"" TOTP_CLI_COMMAND_AUTOMATION_METHOD_BT "\"");
+ }
+#endif
+
+ if(method == AutomationMethodNone) {
+ TOTP_CLI_PRINTF_COLORFUL(color, "\"" TOTP_CLI_COMMAND_AUTOMATION_METHOD_NONE "\"");
+ }
+}
+
+void totp_cli_command_automation_handle(PluginState* plugin_state, FuriString* args, Cli* cli) {
+ if(!totp_cli_ensure_authenticated(plugin_state, cli)) {
+ return;
+ }
+
+ FuriString* temp_str = furi_string_alloc();
+ bool new_method_provided = false;
+ AutomationMethod new_method = AutomationMethodNone;
+ bool args_valid = true;
+ while(args_read_string_and_trim(args, temp_str)) {
+ if(furi_string_cmpi_str(temp_str, TOTP_CLI_COMMAND_AUTOMATION_METHOD_NONE) == 0) {
+ new_method_provided = true;
+ new_method = AutomationMethodNone;
+ } else if(furi_string_cmpi_str(temp_str, TOTP_CLI_COMMAND_AUTOMATION_METHOD_USB) == 0) {
+ new_method_provided = true;
+ new_method |= AutomationMethodBadUsb;
+ }
+#ifdef TOTP_BADBT_TYPE_ENABLED
+ else if(furi_string_cmpi_str(temp_str, TOTP_CLI_COMMAND_AUTOMATION_METHOD_BT) == 0) {
+ new_method_provided = true;
+ new_method |= AutomationMethodBadBt;
+ }
+#endif
+ else {
+ args_valid = false;
+ break;
+ }
+ }
+
+ do {
+ if(!args_valid) {
+ TOTP_CLI_PRINT_INVALID_ARGUMENTS();
+ break;
+ }
+
+ if(new_method_provided) {
+ Scene previous_scene = TotpSceneNone;
+ if(plugin_state->current_scene == TotpSceneGenerateToken ||
+ plugin_state->current_scene == TotpSceneAppSettings) {
+ previous_scene = plugin_state->current_scene;
+ totp_scene_director_activate_scene(plugin_state, TotpSceneNone, NULL);
+ }
+
+ plugin_state->automation_method = new_method;
+ if(totp_config_file_update_automation_method(new_method) ==
+ TotpConfigFileUpdateSuccess) {
+ TOTP_CLI_PRINTF_SUCCESS("Automation method is set to ");
+ totp_cli_command_automation_print_method(new_method, TOTP_CLI_COLOR_SUCCESS);
+ cli_nl();
+ } else {
+ TOTP_CLI_PRINT_ERROR_UPDATING_CONFIG_FILE();
+ }
+
+#ifdef TOTP_BADBT_TYPE_ENABLED
+ if(!(new_method & AutomationMethodBadBt) &&
+ plugin_state->bt_type_code_worker_context != NULL) {
+ totp_bt_type_code_worker_free(plugin_state->bt_type_code_worker_context);
+ plugin_state->bt_type_code_worker_context = NULL;
+ }
+#endif
+
+ if(previous_scene != TotpSceneNone) {
+ totp_scene_director_activate_scene(plugin_state, previous_scene, NULL);
+ }
+ } else {
+ TOTP_CLI_PRINTF_INFO("Current automation method is ");
+ totp_cli_command_automation_print_method(
+ plugin_state->automation_method, TOTP_CLI_COLOR_INFO);
+ cli_nl();
+ }
+ } while(false);
+
+ furi_string_free(temp_str);
+}
\ No newline at end of file
diff --git a/applications/external/totp/cli/commands/automation/automation.h b/applications/external/totp/cli/commands/automation/automation.h
new file mode 100644
index 000000000..fb62e638e
--- /dev/null
+++ b/applications/external/totp/cli/commands/automation/automation.h
@@ -0,0 +1,11 @@
+#pragma once
+
+#include
+#include "../../../types/plugin_state.h"
+
+#define TOTP_CLI_COMMAND_AUTOMATION "automation"
+
+void totp_cli_command_automation_handle(PluginState* plugin_state, FuriString* args, Cli* cli);
+void totp_cli_command_automation_docopt_commands();
+void totp_cli_command_automation_docopt_usage();
+void totp_cli_command_automation_docopt_arguments();
\ No newline at end of file
diff --git a/applications/plugins/totp/cli/commands/delete/delete.c b/applications/external/totp/cli/commands/delete/delete.c
similarity index 99%
rename from applications/plugins/totp/cli/commands/delete/delete.c
rename to applications/external/totp/cli/commands/delete/delete.c
index 8fe72e220..04cc815a4 100644
--- a/applications/plugins/totp/cli/commands/delete/delete.c
+++ b/applications/external/totp/cli/commands/delete/delete.c
@@ -24,7 +24,7 @@ void totp_cli_command_delete_docopt_usage() {
}
void totp_cli_command_delete_docopt_arguments() {
- TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_DELETE_ARG_INDEX " Token index in the list\r\n");
+ TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_DELETE_ARG_INDEX " Token index in the list\r\n");
}
void totp_cli_command_delete_docopt_options() {
diff --git a/applications/plugins/totp/cli/commands/delete/delete.h b/applications/external/totp/cli/commands/delete/delete.h
similarity index 100%
rename from applications/plugins/totp/cli/commands/delete/delete.h
rename to applications/external/totp/cli/commands/delete/delete.h
diff --git a/applications/plugins/totp/cli/commands/help/help.c b/applications/external/totp/cli/commands/help/help.c
similarity index 91%
rename from applications/plugins/totp/cli/commands/help/help.c
rename to applications/external/totp/cli/commands/help/help.c
index 104b39e47..34b44debd 100644
--- a/applications/plugins/totp/cli/commands/help/help.c
+++ b/applications/external/totp/cli/commands/help/help.c
@@ -8,6 +8,7 @@
#include "../pin/pin.h"
#include "../notification/notification.h"
#include "../reset/reset.h"
+#include "../automation/automation.h"
void totp_cli_command_help_docopt_commands() {
TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_HELP ", " TOTP_CLI_COMMAND_HELP_ALT
@@ -31,6 +32,7 @@ void totp_cli_command_help_handle() {
totp_cli_command_pin_docopt_usage();
totp_cli_command_notification_docopt_usage();
totp_cli_command_reset_docopt_usage();
+ totp_cli_command_automation_docopt_usage();
cli_nl();
TOTP_CLI_PRINTF("Commands:\r\n");
totp_cli_command_help_docopt_commands();
@@ -42,12 +44,14 @@ void totp_cli_command_help_handle() {
totp_cli_command_pin_docopt_commands();
totp_cli_command_notification_docopt_commands();
totp_cli_command_reset_docopt_commands();
+ totp_cli_command_automation_docopt_commands();
cli_nl();
TOTP_CLI_PRINTF("Arguments:\r\n");
totp_cli_command_add_docopt_arguments();
totp_cli_command_delete_docopt_arguments();
totp_cli_command_timezone_docopt_arguments();
totp_cli_command_notification_docopt_arguments();
+ totp_cli_command_automation_docopt_arguments();
cli_nl();
TOTP_CLI_PRINTF("Options:\r\n");
totp_cli_command_add_docopt_options();
diff --git a/applications/plugins/totp/cli/commands/help/help.h b/applications/external/totp/cli/commands/help/help.h
similarity index 100%
rename from applications/plugins/totp/cli/commands/help/help.h
rename to applications/external/totp/cli/commands/help/help.h
diff --git a/applications/plugins/totp/cli/commands/list/list.c b/applications/external/totp/cli/commands/list/list.c
similarity index 100%
rename from applications/plugins/totp/cli/commands/list/list.c
rename to applications/external/totp/cli/commands/list/list.c
diff --git a/applications/plugins/totp/cli/commands/list/list.h b/applications/external/totp/cli/commands/list/list.h
similarity index 100%
rename from applications/plugins/totp/cli/commands/list/list.h
rename to applications/external/totp/cli/commands/list/list.h
diff --git a/applications/plugins/totp/cli/commands/move/move.c b/applications/external/totp/cli/commands/move/move.c
similarity index 100%
rename from applications/plugins/totp/cli/commands/move/move.c
rename to applications/external/totp/cli/commands/move/move.c
diff --git a/applications/plugins/totp/cli/commands/move/move.h b/applications/external/totp/cli/commands/move/move.h
similarity index 100%
rename from applications/plugins/totp/cli/commands/move/move.h
rename to applications/external/totp/cli/commands/move/move.h
diff --git a/applications/plugins/totp/cli/commands/notification/notification.c b/applications/external/totp/cli/commands/notification/notification.c
similarity index 96%
rename from applications/plugins/totp/cli/commands/notification/notification.c
rename to applications/external/totp/cli/commands/notification/notification.c
index bbbd52703..016b83d0d 100644
--- a/applications/plugins/totp/cli/commands/notification/notification.c
+++ b/applications/external/totp/cli/commands/notification/notification.c
@@ -4,7 +4,7 @@
#include "../../../ui/scene_director.h"
#include "../../cli_helpers.h"
-#define TOTP_CLI_COMMAND_NOTIFICATION_ARG_METHOD "method"
+#define TOTP_CLI_COMMAND_NOTIFICATION_ARG_METHOD "notification"
#define TOTP_CLI_COMMAND_NOTIFICATION_METHOD_NONE "none"
#define TOTP_CLI_COMMAND_NOTIFICATION_METHOD_SOUND "sound"
#define TOTP_CLI_COMMAND_NOTIFICATION_METHOD_VIBRO "vibro"
@@ -23,7 +23,7 @@ void totp_cli_command_notification_docopt_usage() {
void totp_cli_command_notification_docopt_arguments() {
TOTP_CLI_PRINTF(
" " TOTP_CLI_COMMAND_NOTIFICATION_ARG_METHOD
- " Notification method to be set. Must be one of [" TOTP_CLI_COMMAND_NOTIFICATION_METHOD_NONE
+ " Notification method to be set. Must be one of [" TOTP_CLI_COMMAND_NOTIFICATION_METHOD_NONE
", " TOTP_CLI_COMMAND_NOTIFICATION_METHOD_SOUND
", " TOTP_CLI_COMMAND_NOTIFICATION_METHOD_VIBRO "]\r\n");
}
diff --git a/applications/plugins/totp/cli/commands/notification/notification.h b/applications/external/totp/cli/commands/notification/notification.h
similarity index 100%
rename from applications/plugins/totp/cli/commands/notification/notification.h
rename to applications/external/totp/cli/commands/notification/notification.h
diff --git a/applications/plugins/totp/cli/commands/pin/pin.c b/applications/external/totp/cli/commands/pin/pin.c
similarity index 100%
rename from applications/plugins/totp/cli/commands/pin/pin.c
rename to applications/external/totp/cli/commands/pin/pin.c
diff --git a/applications/plugins/totp/cli/commands/pin/pin.h b/applications/external/totp/cli/commands/pin/pin.h
similarity index 100%
rename from applications/plugins/totp/cli/commands/pin/pin.h
rename to applications/external/totp/cli/commands/pin/pin.h
diff --git a/applications/plugins/totp/cli/commands/reset/reset.c b/applications/external/totp/cli/commands/reset/reset.c
similarity index 100%
rename from applications/plugins/totp/cli/commands/reset/reset.c
rename to applications/external/totp/cli/commands/reset/reset.c
diff --git a/applications/plugins/totp/cli/commands/reset/reset.h b/applications/external/totp/cli/commands/reset/reset.h
similarity index 100%
rename from applications/plugins/totp/cli/commands/reset/reset.h
rename to applications/external/totp/cli/commands/reset/reset.h
diff --git a/applications/plugins/totp/cli/commands/timezone/timezone.c b/applications/external/totp/cli/commands/timezone/timezone.c
similarity index 97%
rename from applications/plugins/totp/cli/commands/timezone/timezone.c
rename to applications/external/totp/cli/commands/timezone/timezone.c
index 9eb0cb5f6..265d80e53 100644
--- a/applications/plugins/totp/cli/commands/timezone/timezone.c
+++ b/applications/external/totp/cli/commands/timezone/timezone.c
@@ -20,7 +20,7 @@ void totp_cli_command_timezone_docopt_usage() {
void totp_cli_command_timezone_docopt_arguments() {
TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_TIMEZONE_ARG_TIMEZONE
- " Timezone offset in hours to be set\r\n");
+ " Timezone offset in hours to be set\r\n");
}
void totp_cli_command_timezone_handle(PluginState* plugin_state, FuriString* args, Cli* cli) {
diff --git a/applications/plugins/totp/cli/commands/timezone/timezone.h b/applications/external/totp/cli/commands/timezone/timezone.h
similarity index 100%
rename from applications/plugins/totp/cli/commands/timezone/timezone.h
rename to applications/external/totp/cli/commands/timezone/timezone.h
diff --git a/applications/external/totp/features_config.h b/applications/external/totp/features_config.h
new file mode 100644
index 000000000..d3b30aee0
--- /dev/null
+++ b/applications/external/totp/features_config.h
@@ -0,0 +1,2 @@
+#define TOTP_BADBT_TYPE_ENABLED
+#define TOTP_BADBT_TYPE_ICON_ENABLED
\ No newline at end of file
diff --git a/applications/plugins/nfc_magic/assets/DolphinCommon_56x48.png b/applications/external/totp/images/DolphinCommon_56x48.png
similarity index 100%
rename from applications/plugins/nfc_magic/assets/DolphinCommon_56x48.png
rename to applications/external/totp/images/DolphinCommon_56x48.png
diff --git a/applications/external/totp/images/hid_ble_10x7.png b/applications/external/totp/images/hid_ble_10x7.png
new file mode 100644
index 000000000..3cd1ff95c
Binary files /dev/null and b/applications/external/totp/images/hid_ble_10x7.png differ
diff --git a/applications/plugins/totp/images/totp_arrow_bottom_10x5.png b/applications/external/totp/images/totp_arrow_bottom_10x5.png
similarity index 100%
rename from applications/plugins/totp/images/totp_arrow_bottom_10x5.png
rename to applications/external/totp/images/totp_arrow_bottom_10x5.png
diff --git a/applications/plugins/totp/images/totp_arrow_left_8x9.png b/applications/external/totp/images/totp_arrow_left_8x9.png
similarity index 100%
rename from applications/plugins/totp/images/totp_arrow_left_8x9.png
rename to applications/external/totp/images/totp_arrow_left_8x9.png
diff --git a/applications/plugins/totp/images/totp_arrow_right_8x9.png b/applications/external/totp/images/totp_arrow_right_8x9.png
similarity index 100%
rename from applications/plugins/totp/images/totp_arrow_right_8x9.png
rename to applications/external/totp/images/totp_arrow_right_8x9.png
diff --git a/applications/plugins/totp/lib/base32/base32.c b/applications/external/totp/lib/base32/base32.c
similarity index 100%
rename from applications/plugins/totp/lib/base32/base32.c
rename to applications/external/totp/lib/base32/base32.c
diff --git a/applications/plugins/totp/lib/base32/base32.h b/applications/external/totp/lib/base32/base32.h
similarity index 100%
rename from applications/plugins/totp/lib/base32/base32.h
rename to applications/external/totp/lib/base32/base32.h
diff --git a/applications/plugins/totp/lib/list/list.c b/applications/external/totp/lib/list/list.c
similarity index 100%
rename from applications/plugins/totp/lib/list/list.c
rename to applications/external/totp/lib/list/list.c
diff --git a/applications/plugins/totp/lib/list/list.h b/applications/external/totp/lib/list/list.h
similarity index 100%
rename from applications/plugins/totp/lib/list/list.h
rename to applications/external/totp/lib/list/list.h
diff --git a/applications/plugins/totp/lib/polyfills/memset_s.c b/applications/external/totp/lib/polyfills/memset_s.c
similarity index 100%
rename from applications/plugins/totp/lib/polyfills/memset_s.c
rename to applications/external/totp/lib/polyfills/memset_s.c
diff --git a/applications/plugins/totp/lib/polyfills/memset_s.h b/applications/external/totp/lib/polyfills/memset_s.h
similarity index 100%
rename from applications/plugins/totp/lib/polyfills/memset_s.h
rename to applications/external/totp/lib/polyfills/memset_s.h
diff --git a/applications/plugins/totp/lib/polyfills/strnlen.c b/applications/external/totp/lib/polyfills/strnlen.c
similarity index 100%
rename from applications/plugins/totp/lib/polyfills/strnlen.c
rename to applications/external/totp/lib/polyfills/strnlen.c
diff --git a/applications/plugins/totp/lib/polyfills/strnlen.h b/applications/external/totp/lib/polyfills/strnlen.h
similarity index 100%
rename from applications/plugins/totp/lib/polyfills/strnlen.h
rename to applications/external/totp/lib/polyfills/strnlen.h
diff --git a/applications/plugins/totp/lib/roll_value/roll_value.c b/applications/external/totp/lib/roll_value/roll_value.c
similarity index 100%
rename from applications/plugins/totp/lib/roll_value/roll_value.c
rename to applications/external/totp/lib/roll_value/roll_value.c
diff --git a/applications/plugins/totp/lib/roll_value/roll_value.h b/applications/external/totp/lib/roll_value/roll_value.h
similarity index 100%
rename from applications/plugins/totp/lib/roll_value/roll_value.h
rename to applications/external/totp/lib/roll_value/roll_value.h
diff --git a/applications/plugins/totp/lib/timezone_utils/timezone_utils.c b/applications/external/totp/lib/timezone_utils/timezone_utils.c
similarity index 100%
rename from applications/plugins/totp/lib/timezone_utils/timezone_utils.c
rename to applications/external/totp/lib/timezone_utils/timezone_utils.c
diff --git a/applications/plugins/totp/lib/timezone_utils/timezone_utils.h b/applications/external/totp/lib/timezone_utils/timezone_utils.h
similarity index 100%
rename from applications/plugins/totp/lib/timezone_utils/timezone_utils.h
rename to applications/external/totp/lib/timezone_utils/timezone_utils.h
diff --git a/applications/plugins/totp/services/config/config.c b/applications/external/totp/services/config/config.c
similarity index 93%
rename from applications/plugins/totp/services/config/config.c
rename to applications/external/totp/services/config/config.c
index 76c72707c..ac7093dde 100644
--- a/applications/plugins/totp/services/config/config.c
+++ b/applications/external/totp/services/config/config.c
@@ -4,6 +4,7 @@
#include "../list/list.h"
#include "../../types/common.h"
#include "../../types/token_info.h"
+#include "../../features_config.h"
#include "migrations/config_migration_v1_to_v2.h"
#include "migrations/config_migration_v2_to_v3.h"
@@ -136,6 +137,14 @@ static TotpConfigFileOpenResult totp_open_config_file(Storage* storage, FlipperF
flipper_format_write_uint32(
fff_data_file, TOTP_CONFIG_KEY_NOTIFICATION_METHOD, &tmp_uint32, 1);
+ tmp_uint32 = AutomationMethodBadUsb;
+ flipper_format_write_comment_cstr(fff_data_file, " ");
+ flipper_format_write_comment_cstr(
+ fff_data_file,
+ "Automation method (0 - None, 1 - BadUSB, 2 - BadBT, 3 - BadUSB and BadBT)");
+ flipper_format_write_uint32(
+ fff_data_file, TOTP_CONFIG_KEY_AUTOMATION_METHOD, &tmp_uint32, 1);
+
FuriString* temp_str = furi_string_alloc();
flipper_format_write_comment_cstr(fff_data_file, " ");
@@ -329,6 +338,33 @@ TotpConfigFileUpdateResult
return update_result;
}
+TotpConfigFileUpdateResult
+ totp_config_file_update_automation_method(AutomationMethod new_automation_method) {
+ Storage* cfg_storage = totp_open_storage();
+ FlipperFormat* file;
+ TotpConfigFileUpdateResult update_result;
+
+ if(totp_open_config_file(cfg_storage, &file) == TotpConfigFileOpenSuccess) {
+ do {
+ uint32_t tmp_uint32 = new_automation_method;
+ if(!flipper_format_insert_or_update_uint32(
+ file, TOTP_CONFIG_KEY_AUTOMATION_METHOD, &tmp_uint32, 1)) {
+ update_result = TotpConfigFileUpdateError;
+ break;
+ }
+
+ update_result = TotpConfigFileUpdateSuccess;
+ } while(false);
+
+ totp_close_config_file(file);
+ } else {
+ update_result = TotpConfigFileUpdateError;
+ }
+
+ totp_close_storage();
+ return update_result;
+}
+
TotpConfigFileUpdateResult totp_config_file_update_user_settings(const PluginState* plugin_state) {
Storage* cfg_storage = totp_open_storage();
FlipperFormat* file;
@@ -347,6 +383,13 @@ TotpConfigFileUpdateResult totp_config_file_update_user_settings(const PluginSta
break;
}
+ tmp_uint32 = plugin_state->automation_method;
+ if(!flipper_format_insert_or_update_uint32(
+ file, TOTP_CONFIG_KEY_AUTOMATION_METHOD, &tmp_uint32, 1)) {
+ update_result = TotpConfigFileUpdateError;
+ break;
+ }
+
update_result = TotpConfigFileUpdateSuccess;
} while(false);
@@ -409,6 +452,13 @@ TotpConfigFileUpdateResult totp_full_save_config_file(const PluginState* const p
break;
}
+ tmp_uint32 = plugin_state->automation_method;
+ if(!flipper_format_write_uint32(
+ fff_data_file, TOTP_CONFIG_KEY_AUTOMATION_METHOD, &tmp_uint32, 1)) {
+ result = TotpConfigFileUpdateError;
+ break;
+ }
+
bool tokens_written = true;
TOTP_LIST_FOREACH(plugin_state->tokens_list, node, {
const TokenInfo* token_info = node->data;
@@ -594,6 +644,15 @@ TotpConfigFileOpenResult totp_config_file_load_base(PluginState* const plugin_st
}
plugin_state->notification_method = tmp_uint32;
+
+ flipper_format_rewind(fff_data_file);
+
+ if(!flipper_format_read_uint32(
+ fff_data_file, TOTP_CONFIG_KEY_AUTOMATION_METHOD, &tmp_uint32, 1)) {
+ tmp_uint32 = AutomationMethodBadUsb;
+ }
+
+ plugin_state->automation_method = tmp_uint32;
} while(false);
furi_string_free(temp_str);
diff --git a/applications/plugins/totp/services/config/config.h b/applications/external/totp/services/config/config.h
similarity index 92%
rename from applications/plugins/totp/services/config/config.h
rename to applications/external/totp/services/config/config.h
index c630810a6..3d325368d 100644
--- a/applications/plugins/totp/services/config/config.h
+++ b/applications/external/totp/services/config/config.h
@@ -103,6 +103,14 @@ TotpConfigFileUpdateResult totp_config_file_update_timezone_offset(float new_tim
TotpConfigFileUpdateResult
totp_config_file_update_notification_method(NotificationMethod new_notification_method);
+/**
+ * @brief Updates automation method in an application config file
+ * @param new_automation_method new automation method to be set
+ * @return Config file update result
+ */
+TotpConfigFileUpdateResult
+ totp_config_file_update_automation_method(AutomationMethod new_automation_method);
+
/**
* @brief Updates application user settings
* @param plugin_state application state
diff --git a/applications/plugins/totp/services/config/constants.h b/applications/external/totp/services/config/constants.h
similarity index 92%
rename from applications/plugins/totp/services/config/constants.h
rename to applications/external/totp/services/config/constants.h
index 487fb159e..526179f41 100644
--- a/applications/plugins/totp/services/config/constants.h
+++ b/applications/external/totp/services/config/constants.h
@@ -13,6 +13,7 @@
#define TOTP_CONFIG_KEY_BASE_IV "BaseIV"
#define TOTP_CONFIG_KEY_PINSET "PinIsSet"
#define TOTP_CONFIG_KEY_NOTIFICATION_METHOD "NotificationMethod"
+#define TOTP_CONFIG_KEY_AUTOMATION_METHOD "AutomationMethod"
#define TOTP_CONFIG_TOKEN_ALGO_SHA1_NAME "sha1"
#define TOTP_CONFIG_TOKEN_ALGO_SHA256_NAME "sha256"
diff --git a/applications/plugins/totp/services/config/migrations/config_migration_v1_to_v2.c b/applications/external/totp/services/config/migrations/config_migration_v1_to_v2.c
similarity index 100%
rename from applications/plugins/totp/services/config/migrations/config_migration_v1_to_v2.c
rename to applications/external/totp/services/config/migrations/config_migration_v1_to_v2.c
diff --git a/applications/plugins/totp/services/config/migrations/config_migration_v1_to_v2.h b/applications/external/totp/services/config/migrations/config_migration_v1_to_v2.h
similarity index 100%
rename from applications/plugins/totp/services/config/migrations/config_migration_v1_to_v2.h
rename to applications/external/totp/services/config/migrations/config_migration_v1_to_v2.h
diff --git a/applications/plugins/totp/services/config/migrations/config_migration_v2_to_v3.c b/applications/external/totp/services/config/migrations/config_migration_v2_to_v3.c
similarity index 100%
rename from applications/plugins/totp/services/config/migrations/config_migration_v2_to_v3.c
rename to applications/external/totp/services/config/migrations/config_migration_v2_to_v3.c
diff --git a/applications/plugins/totp/services/config/migrations/config_migration_v2_to_v3.h b/applications/external/totp/services/config/migrations/config_migration_v2_to_v3.h
similarity index 100%
rename from applications/plugins/totp/services/config/migrations/config_migration_v2_to_v3.h
rename to applications/external/totp/services/config/migrations/config_migration_v2_to_v3.h
diff --git a/applications/plugins/totp/services/convert/convert.h b/applications/external/totp/services/convert/convert.h
similarity index 100%
rename from applications/plugins/totp/services/convert/convert.h
rename to applications/external/totp/services/convert/convert.h
diff --git a/applications/plugins/totp/services/crypto/crypto.c b/applications/external/totp/services/crypto/crypto.c
similarity index 100%
rename from applications/plugins/totp/services/crypto/crypto.c
rename to applications/external/totp/services/crypto/crypto.c
diff --git a/applications/plugins/totp/services/crypto/crypto.h b/applications/external/totp/services/crypto/crypto.h
similarity index 100%
rename from applications/plugins/totp/services/crypto/crypto.h
rename to applications/external/totp/services/crypto/crypto.h
diff --git a/applications/plugins/totp/services/hmac/byteswap.c b/applications/external/totp/services/hmac/byteswap.c
similarity index 100%
rename from applications/plugins/totp/services/hmac/byteswap.c
rename to applications/external/totp/services/hmac/byteswap.c
diff --git a/applications/plugins/totp/services/hmac/byteswap.h b/applications/external/totp/services/hmac/byteswap.h
similarity index 100%
rename from applications/plugins/totp/services/hmac/byteswap.h
rename to applications/external/totp/services/hmac/byteswap.h
diff --git a/applications/plugins/totp/services/hmac/hmac_common.h b/applications/external/totp/services/hmac/hmac_common.h
similarity index 100%
rename from applications/plugins/totp/services/hmac/hmac_common.h
rename to applications/external/totp/services/hmac/hmac_common.h
diff --git a/applications/plugins/totp/services/hmac/hmac_sha1.c b/applications/external/totp/services/hmac/hmac_sha1.c
similarity index 100%
rename from applications/plugins/totp/services/hmac/hmac_sha1.c
rename to applications/external/totp/services/hmac/hmac_sha1.c
diff --git a/applications/plugins/totp/services/hmac/hmac_sha1.h b/applications/external/totp/services/hmac/hmac_sha1.h
similarity index 100%
rename from applications/plugins/totp/services/hmac/hmac_sha1.h
rename to applications/external/totp/services/hmac/hmac_sha1.h
diff --git a/applications/plugins/totp/services/hmac/hmac_sha256.c b/applications/external/totp/services/hmac/hmac_sha256.c
similarity index 100%
rename from applications/plugins/totp/services/hmac/hmac_sha256.c
rename to applications/external/totp/services/hmac/hmac_sha256.c
diff --git a/applications/plugins/totp/services/hmac/hmac_sha256.h b/applications/external/totp/services/hmac/hmac_sha256.h
similarity index 100%
rename from applications/plugins/totp/services/hmac/hmac_sha256.h
rename to applications/external/totp/services/hmac/hmac_sha256.h
diff --git a/applications/plugins/totp/services/hmac/hmac_sha512.c b/applications/external/totp/services/hmac/hmac_sha512.c
similarity index 100%
rename from applications/plugins/totp/services/hmac/hmac_sha512.c
rename to applications/external/totp/services/hmac/hmac_sha512.c
diff --git a/applications/plugins/totp/services/hmac/hmac_sha512.h b/applications/external/totp/services/hmac/hmac_sha512.h
similarity index 100%
rename from applications/plugins/totp/services/hmac/hmac_sha512.h
rename to applications/external/totp/services/hmac/hmac_sha512.h
diff --git a/applications/plugins/totp/services/hmac/memxor.c b/applications/external/totp/services/hmac/memxor.c
similarity index 100%
rename from applications/plugins/totp/services/hmac/memxor.c
rename to applications/external/totp/services/hmac/memxor.c
diff --git a/applications/plugins/totp/services/hmac/memxor.h b/applications/external/totp/services/hmac/memxor.h
similarity index 100%
rename from applications/plugins/totp/services/hmac/memxor.h
rename to applications/external/totp/services/hmac/memxor.h
diff --git a/applications/plugins/totp/services/hmac/sha1.c b/applications/external/totp/services/hmac/sha1.c
similarity index 100%
rename from applications/plugins/totp/services/hmac/sha1.c
rename to applications/external/totp/services/hmac/sha1.c
diff --git a/applications/plugins/totp/services/hmac/sha1.h b/applications/external/totp/services/hmac/sha1.h
similarity index 100%
rename from applications/plugins/totp/services/hmac/sha1.h
rename to applications/external/totp/services/hmac/sha1.h
diff --git a/applications/plugins/totp/services/hmac/sha256.c b/applications/external/totp/services/hmac/sha256.c
similarity index 100%
rename from applications/plugins/totp/services/hmac/sha256.c
rename to applications/external/totp/services/hmac/sha256.c
diff --git a/applications/plugins/totp/services/hmac/sha256.h b/applications/external/totp/services/hmac/sha256.h
similarity index 100%
rename from applications/plugins/totp/services/hmac/sha256.h
rename to applications/external/totp/services/hmac/sha256.h
diff --git a/applications/plugins/totp/services/hmac/sha512.c b/applications/external/totp/services/hmac/sha512.c
similarity index 100%
rename from applications/plugins/totp/services/hmac/sha512.c
rename to applications/external/totp/services/hmac/sha512.c
diff --git a/applications/plugins/totp/services/hmac/sha512.h b/applications/external/totp/services/hmac/sha512.h
similarity index 100%
rename from applications/plugins/totp/services/hmac/sha512.h
rename to applications/external/totp/services/hmac/sha512.h
diff --git a/applications/plugins/totp/services/hmac/u64.h b/applications/external/totp/services/hmac/u64.h
similarity index 100%
rename from applications/plugins/totp/services/hmac/u64.h
rename to applications/external/totp/services/hmac/u64.h
diff --git a/applications/plugins/totp/services/totp/totp.c b/applications/external/totp/services/totp/totp.c
similarity index 100%
rename from applications/plugins/totp/services/totp/totp.c
rename to applications/external/totp/services/totp/totp.c
diff --git a/applications/plugins/totp/services/totp/totp.h b/applications/external/totp/services/totp/totp.h
similarity index 100%
rename from applications/plugins/totp/services/totp/totp.h
rename to applications/external/totp/services/totp/totp.h
diff --git a/applications/plugins/totp/totp_10px.png b/applications/external/totp/totp_10px.png
similarity index 100%
rename from applications/plugins/totp/totp_10px.png
rename to applications/external/totp/totp_10px.png
diff --git a/applications/plugins/totp/totp_app.c b/applications/external/totp/totp_app.c
similarity index 93%
rename from applications/plugins/totp/totp_app.c
rename to applications/external/totp/totp_app.c
index 966c9fb34..e5c101e5f 100644
--- a/applications/plugins/totp/totp_app.c
+++ b/applications/external/totp/totp_app.c
@@ -7,7 +7,7 @@
#include
#include
#include
-#include
+#include "features_config.h"
#include "services/config/config.h"
#include "types/plugin_state.h"
#include "types/token_info.h"
@@ -108,6 +108,14 @@ static bool totp_plugin_state_init(PluginState* const plugin_state) {
return false;
}
+#ifdef TOTP_BADBT_TYPE_ENABLED
+ if(plugin_state->automation_method & AutomationMethodBadBt) {
+ plugin_state->bt_type_code_worker_context = totp_bt_type_code_worker_init();
+ } else {
+ plugin_state->bt_type_code_worker_context = NULL;
+ }
+#endif
+
return true;
}
@@ -130,6 +138,13 @@ static void totp_plugin_state_free(PluginState* plugin_state) {
free(plugin_state->crypto_verify_data);
}
+#ifdef TOTP_BADBT_TYPE_ENABLED
+ if(plugin_state->bt_type_code_worker_context != NULL) {
+ totp_bt_type_code_worker_free(plugin_state->bt_type_code_worker_context);
+ plugin_state->bt_type_code_worker_context = NULL;
+ }
+#endif
+
furi_mutex_free(plugin_state->mutex);
free(plugin_state);
}
@@ -153,9 +168,6 @@ int32_t totp_app() {
return 253;
}
- // Affecting dolphin level
- DOLPHIN_DEED(DolphinDeedPluginStart);
-
// Set system callbacks
ViewPort* view_port = view_port_alloc();
view_port_draw_callback_set(view_port, render_callback, plugin_state);
diff --git a/applications/external/totp/types/automation_method.h b/applications/external/totp/types/automation_method.h
new file mode 100644
index 000000000..b51e59e03
--- /dev/null
+++ b/applications/external/totp/types/automation_method.h
@@ -0,0 +1,13 @@
+#pragma once
+
+#include "../features_config.h"
+
+typedef uint8_t AutomationMethod;
+
+enum AutomationMethods {
+ AutomationMethodNone = 0b00,
+ AutomationMethodBadUsb = 0b01,
+#ifdef TOTP_BADBT_TYPE_ENABLED
+ AutomationMethodBadBt = 0b10,
+#endif
+};
diff --git a/applications/plugins/totp/types/common.h b/applications/external/totp/types/common.h
similarity index 100%
rename from applications/plugins/totp/types/common.h
rename to applications/external/totp/types/common.h
diff --git a/applications/plugins/totp/types/event_type.h b/applications/external/totp/types/event_type.h
similarity index 100%
rename from applications/plugins/totp/types/event_type.h
rename to applications/external/totp/types/event_type.h
diff --git a/applications/plugins/totp/types/notification_method.h b/applications/external/totp/types/notification_method.h
similarity index 100%
rename from applications/plugins/totp/types/notification_method.h
rename to applications/external/totp/types/notification_method.h
diff --git a/applications/plugins/totp/types/nullable.h b/applications/external/totp/types/nullable.h
similarity index 100%
rename from applications/plugins/totp/types/nullable.h
rename to applications/external/totp/types/nullable.h
diff --git a/applications/plugins/totp/types/plugin_event.h b/applications/external/totp/types/plugin_event.h
similarity index 100%
rename from applications/plugins/totp/types/plugin_event.h
rename to applications/external/totp/types/plugin_event.h
diff --git a/applications/plugins/totp/types/plugin_state.h b/applications/external/totp/types/plugin_state.h
similarity index 82%
rename from applications/plugins/totp/types/plugin_state.h
rename to applications/external/totp/types/plugin_state.h
index dee500305..59a218ce3 100644
--- a/applications/plugins/totp/types/plugin_state.h
+++ b/applications/external/totp/types/plugin_state.h
@@ -3,9 +3,14 @@
#include
#include
#include
+#include "../features_config.h"
#include "../lib/list/list.h"
#include "../ui/totp_scenes_enum.h"
#include "notification_method.h"
+#include "automation_method.h"
+#ifdef TOTP_BADBT_TYPE_ENABLED
+#include "../workers/bt_type_code/bt_type_code.h"
+#endif
#define TOTP_IV_SIZE 16
@@ -92,4 +97,16 @@ typedef struct {
* @brief Main rendering loop mutex
*/
FuriMutex* mutex;
+
+ /**
+ * @brief Automation method
+ */
+ AutomationMethod automation_method;
+
+#ifdef TOTP_BADBT_TYPE_ENABLED
+ /**
+ * @brief Bad-Bluetooth worker context
+ */
+ TotpBtTypeCodeWorkerContext* bt_type_code_worker_context;
+#endif
} PluginState;
diff --git a/applications/plugins/totp/types/token_info.c b/applications/external/totp/types/token_info.c
similarity index 100%
rename from applications/plugins/totp/types/token_info.c
rename to applications/external/totp/types/token_info.c
diff --git a/applications/plugins/totp/types/token_info.h b/applications/external/totp/types/token_info.h
similarity index 100%
rename from applications/plugins/totp/types/token_info.h
rename to applications/external/totp/types/token_info.h
diff --git a/applications/plugins/totp/types/user_pin_codes.h b/applications/external/totp/types/user_pin_codes.h
similarity index 100%
rename from applications/plugins/totp/types/user_pin_codes.h
rename to applications/external/totp/types/user_pin_codes.h
diff --git a/applications/plugins/totp/ui/common_dialogs.c b/applications/external/totp/ui/common_dialogs.c
similarity index 100%
rename from applications/plugins/totp/ui/common_dialogs.c
rename to applications/external/totp/ui/common_dialogs.c
diff --git a/applications/plugins/totp/ui/common_dialogs.h b/applications/external/totp/ui/common_dialogs.h
similarity index 100%
rename from applications/plugins/totp/ui/common_dialogs.h
rename to applications/external/totp/ui/common_dialogs.h
diff --git a/applications/plugins/totp/ui/constants.h b/applications/external/totp/ui/constants.h
similarity index 100%
rename from applications/plugins/totp/ui/constants.h
rename to applications/external/totp/ui/constants.h
diff --git a/applications/plugins/totp/ui/scene_director.c b/applications/external/totp/ui/scene_director.c
similarity index 96%
rename from applications/plugins/totp/ui/scene_director.c
rename to applications/external/totp/ui/scene_director.c
index fcc9f37d8..c77e88ab4 100644
--- a/applications/plugins/totp/ui/scene_director.c
+++ b/applications/external/totp/ui/scene_director.c
@@ -37,7 +37,9 @@ void totp_scene_director_activate_scene(
}
void totp_scene_director_deactivate_active_scene(PluginState* const plugin_state) {
- switch(plugin_state->current_scene) {
+ Scene current_scene = plugin_state->current_scene;
+ plugin_state->current_scene = TotpSceneNone;
+ switch(current_scene) {
case TotpSceneGenerateToken:
totp_scene_generate_token_deactivate(plugin_state);
break;
diff --git a/applications/plugins/totp/ui/scene_director.h b/applications/external/totp/ui/scene_director.h
similarity index 100%
rename from applications/plugins/totp/ui/scene_director.h
rename to applications/external/totp/ui/scene_director.h
diff --git a/applications/plugins/totp/ui/scenes/add_new_token/totp_input_text.c b/applications/external/totp/ui/scenes/add_new_token/totp_input_text.c
similarity index 100%
rename from applications/plugins/totp/ui/scenes/add_new_token/totp_input_text.c
rename to applications/external/totp/ui/scenes/add_new_token/totp_input_text.c
diff --git a/applications/plugins/totp/ui/scenes/add_new_token/totp_input_text.h b/applications/external/totp/ui/scenes/add_new_token/totp_input_text.h
similarity index 100%
rename from applications/plugins/totp/ui/scenes/add_new_token/totp_input_text.h
rename to applications/external/totp/ui/scenes/add_new_token/totp_input_text.h
diff --git a/applications/plugins/totp/ui/scenes/add_new_token/totp_scene_add_new_token.c b/applications/external/totp/ui/scenes/add_new_token/totp_scene_add_new_token.c
similarity index 100%
rename from applications/plugins/totp/ui/scenes/add_new_token/totp_scene_add_new_token.c
rename to applications/external/totp/ui/scenes/add_new_token/totp_scene_add_new_token.c
diff --git a/applications/plugins/totp/ui/scenes/add_new_token/totp_scene_add_new_token.h b/applications/external/totp/ui/scenes/add_new_token/totp_scene_add_new_token.h
similarity index 100%
rename from applications/plugins/totp/ui/scenes/add_new_token/totp_scene_add_new_token.h
rename to applications/external/totp/ui/scenes/add_new_token/totp_scene_add_new_token.h
diff --git a/applications/plugins/totp/ui/scenes/app_settings/totp_app_settings.c b/applications/external/totp/ui/scenes/app_settings/totp_app_settings.c
similarity index 70%
rename from applications/plugins/totp/ui/scenes/app_settings/totp_app_settings.c
rename to applications/external/totp/ui/scenes/app_settings/totp_app_settings.c
index b8936f395..1671542b8 100644
--- a/applications/plugins/totp/ui/scenes/app_settings/totp_app_settings.c
+++ b/applications/external/totp/ui/scenes/app_settings/totp_app_settings.c
@@ -10,16 +10,35 @@
#include "../../../services/convert/convert.h"
#include "../../../lib/roll_value/roll_value.h"
#include "../../../types/nullable.h"
+#include "../../../features_config.h"
+#ifdef TOTP_BADBT_TYPE_ENABLED
+#include "../../../workers/bt_type_code/bt_type_code.h"
+#endif
char* YES_NO_LIST[] = {"NO", "YES"};
+char* ON_OFF_LIST[] = {"OFF", "ON"};
-typedef enum { HoursInput, MinutesInput, Sound, Vibro, ConfirmButton } Control;
+typedef enum {
+ HoursInput,
+ MinutesInput,
+ Sound,
+ Vibro,
+ BadUsb,
+#ifdef TOTP_BADBT_TYPE_ENABLED
+ BadBt,
+#endif
+ ConfirmButton
+} Control;
typedef struct {
int8_t tz_offset_hours;
uint8_t tz_offset_minutes;
bool notification_sound;
bool notification_vibro;
+ bool badusb_enabled;
+#ifdef TOTP_BADBT_TYPE_ENABLED
+ bool badbt_enabled;
+#endif
uint8_t y_offset;
TotpNullable_uint16_t current_token_index;
Control selected_control;
@@ -47,6 +66,10 @@ void totp_scene_app_settings_activate(
scene_state->tz_offset_minutes = 60.0f * off_dec;
scene_state->notification_sound = plugin_state->notification_method & NotificationMethodSound;
scene_state->notification_vibro = plugin_state->notification_method & NotificationMethodVibro;
+ scene_state->badusb_enabled = plugin_state->automation_method & AutomationMethodBadUsb;
+#ifdef TOTP_BADBT_TYPE_ENABLED
+ scene_state->badbt_enabled = plugin_state->automation_method & AutomationMethodBadBt;
+#endif
}
static void two_digit_to_str(int8_t num, char* str) {
@@ -73,7 +96,7 @@ void totp_scene_app_settings_render(Canvas* const canvas, const PluginState* plu
char tmp_str[4];
two_digit_to_str(scene_state->tz_offset_hours, &tmp_str[0]);
- canvas_draw_str_aligned(canvas, 0, 16 - scene_state->y_offset, AlignLeft, AlignTop, "Hours:");
+ canvas_draw_str_aligned(canvas, 0, 17 - scene_state->y_offset, AlignLeft, AlignTop, "Hours:");
ui_control_select_render(
canvas,
36,
@@ -84,7 +107,7 @@ void totp_scene_app_settings_render(Canvas* const canvas, const PluginState* plu
two_digit_to_str(scene_state->tz_offset_minutes, &tmp_str[0]);
canvas_draw_str_aligned(
- canvas, 0, 34 - scene_state->y_offset, AlignLeft, AlignTop, "Minutes:");
+ canvas, 0, 35 - scene_state->y_offset, AlignLeft, AlignTop, "Minutes:");
ui_control_select_render(
canvas,
36,
@@ -104,7 +127,7 @@ void totp_scene_app_settings_render(Canvas* const canvas, const PluginState* plu
canvas, 0, 64 - scene_state->y_offset, AlignLeft, AlignTop, "Notifications");
canvas_set_font(canvas, FontSecondary);
- canvas_draw_str_aligned(canvas, 0, 80 - scene_state->y_offset, AlignLeft, AlignTop, "Sound:");
+ canvas_draw_str_aligned(canvas, 0, 81 - scene_state->y_offset, AlignLeft, AlignTop, "Sound:");
ui_control_select_render(
canvas,
36,
@@ -113,7 +136,7 @@ void totp_scene_app_settings_render(Canvas* const canvas, const PluginState* plu
YES_NO_LIST[scene_state->notification_sound],
scene_state->selected_control == Sound);
- canvas_draw_str_aligned(canvas, 0, 98 - scene_state->y_offset, AlignLeft, AlignTop, "Vibro:");
+ canvas_draw_str_aligned(canvas, 0, 99 - scene_state->y_offset, AlignLeft, AlignTop, "Vibro:");
ui_control_select_render(
canvas,
36,
@@ -122,10 +145,43 @@ void totp_scene_app_settings_render(Canvas* const canvas, const PluginState* plu
YES_NO_LIST[scene_state->notification_vibro],
scene_state->selected_control == Vibro);
+ canvas_draw_icon(
+ canvas, SCREEN_WIDTH_CENTER - 5, 123 - scene_state->y_offset, &I_totp_arrow_bottom_10x5);
+
+ canvas_set_font(canvas, FontPrimary);
+ canvas_draw_str_aligned(
+ canvas, 0, 128 - scene_state->y_offset, AlignLeft, AlignTop, "Automation");
+ canvas_set_font(canvas, FontSecondary);
+
+ canvas_draw_str_aligned(
+ canvas, 0, 145 - scene_state->y_offset, AlignLeft, AlignTop, "BadUSB:");
+ ui_control_select_render(
+ canvas,
+ 36,
+ 138 - scene_state->y_offset,
+ SCREEN_WIDTH - 36,
+ ON_OFF_LIST[scene_state->badusb_enabled],
+ scene_state->selected_control == BadUsb);
+
+#ifdef TOTP_BADBT_TYPE_ENABLED
+ canvas_draw_str_aligned(canvas, 0, 163 - scene_state->y_offset, AlignLeft, AlignTop, "BadBT:");
+ ui_control_select_render(
+ canvas,
+ 36,
+ 156 - scene_state->y_offset,
+ SCREEN_WIDTH - 36,
+ ON_OFF_LIST[scene_state->badbt_enabled],
+ scene_state->selected_control == BadBt);
+#endif
+
ui_control_button_render(
canvas,
SCREEN_WIDTH_CENTER - 24,
- 115 - scene_state->y_offset,
+#ifdef TOTP_BADBT_TYPE_ENABLED
+ 178 - scene_state->y_offset,
+#else
+ 165 - scene_state->y_offset,
+#endif
48,
13,
"Confirm",
@@ -152,7 +208,9 @@ bool totp_scene_app_settings_handle_event(
HoursInput,
ConfirmButton,
RollOverflowBehaviorStop);
- if(scene_state->selected_control > MinutesInput) {
+ if(scene_state->selected_control > Vibro) {
+ scene_state->y_offset = 128;
+ } else if(scene_state->selected_control > MinutesInput) {
scene_state->y_offset = 64;
} else {
scene_state->y_offset = 0;
@@ -161,7 +219,9 @@ bool totp_scene_app_settings_handle_event(
case InputKeyDown:
totp_roll_value_uint8_t(
&scene_state->selected_control, 1, HoursInput, ConfirmButton, RollOverflowBehaviorStop);
- if(scene_state->selected_control > MinutesInput) {
+ if(scene_state->selected_control > Vibro) {
+ scene_state->y_offset = 128;
+ } else if(scene_state->selected_control > MinutesInput) {
scene_state->y_offset = 64;
} else {
scene_state->y_offset = 0;
@@ -178,7 +238,14 @@ bool totp_scene_app_settings_handle_event(
scene_state->notification_sound = !scene_state->notification_sound;
} else if(scene_state->selected_control == Vibro) {
scene_state->notification_vibro = !scene_state->notification_vibro;
+ } else if(scene_state->selected_control == BadUsb) {
+ scene_state->badusb_enabled = !scene_state->badusb_enabled;
}
+#ifdef TOTP_BADBT_TYPE_ENABLED
+ else if(scene_state->selected_control == BadBt) {
+ scene_state->badbt_enabled = !scene_state->badbt_enabled;
+ }
+#endif
break;
case InputKeyLeft:
if(scene_state->selected_control == HoursInput) {
@@ -191,7 +258,14 @@ bool totp_scene_app_settings_handle_event(
scene_state->notification_sound = !scene_state->notification_sound;
} else if(scene_state->selected_control == Vibro) {
scene_state->notification_vibro = !scene_state->notification_vibro;
+ } else if(scene_state->selected_control == BadUsb) {
+ scene_state->badusb_enabled = !scene_state->badusb_enabled;
}
+#ifdef TOTP_BADBT_TYPE_ENABLED
+ else if(scene_state->selected_control == BadBt) {
+ scene_state->badbt_enabled = !scene_state->badbt_enabled;
+ }
+#endif
break;
case InputKeyOk:
if(scene_state->selected_control == ConfirmButton) {
@@ -204,12 +278,26 @@ bool totp_scene_app_settings_handle_event(
(scene_state->notification_vibro ? NotificationMethodVibro :
NotificationMethodNone);
+ plugin_state->automation_method =
+ scene_state->badusb_enabled ? AutomationMethodBadUsb : AutomationMethodNone;
+#ifdef TOTP_BADBT_TYPE_ENABLED
+ plugin_state->automation_method |= scene_state->badbt_enabled ? AutomationMethodBadBt :
+ AutomationMethodNone;
+#endif
+
if(totp_config_file_update_user_settings(plugin_state) !=
TotpConfigFileUpdateSuccess) {
totp_dialogs_config_updating_error(plugin_state);
return false;
}
+#ifdef TOTP_BADBT_TYPE_ENABLED
+ if(!scene_state->badbt_enabled && plugin_state->bt_type_code_worker_context != NULL) {
+ totp_bt_type_code_worker_free(plugin_state->bt_type_code_worker_context);
+ plugin_state->bt_type_code_worker_context = NULL;
+ }
+#endif
+
if(!scene_state->current_token_index.is_null) {
TokenMenuSceneContext generate_scene_context = {
.current_token_index = scene_state->current_token_index.value};
diff --git a/applications/plugins/totp/ui/scenes/app_settings/totp_app_settings.h b/applications/external/totp/ui/scenes/app_settings/totp_app_settings.h
similarity index 100%
rename from applications/plugins/totp/ui/scenes/app_settings/totp_app_settings.h
rename to applications/external/totp/ui/scenes/app_settings/totp_app_settings.h
diff --git a/applications/plugins/totp/ui/scenes/authenticate/totp_scene_authenticate.c b/applications/external/totp/ui/scenes/authenticate/totp_scene_authenticate.c
similarity index 100%
rename from applications/plugins/totp/ui/scenes/authenticate/totp_scene_authenticate.c
rename to applications/external/totp/ui/scenes/authenticate/totp_scene_authenticate.c
diff --git a/applications/plugins/totp/ui/scenes/authenticate/totp_scene_authenticate.h b/applications/external/totp/ui/scenes/authenticate/totp_scene_authenticate.h
similarity index 100%
rename from applications/plugins/totp/ui/scenes/authenticate/totp_scene_authenticate.h
rename to applications/external/totp/ui/scenes/authenticate/totp_scene_authenticate.h
diff --git a/applications/plugins/totp/ui/scenes/generate_token/totp_scene_generate_token.c b/applications/external/totp/ui/scenes/generate_token/totp_scene_generate_token.c
similarity index 80%
rename from applications/plugins/totp/ui/scenes/generate_token/totp_scene_generate_token.c
rename to applications/external/totp/ui/scenes/generate_token/totp_scene_generate_token.c
index a882ae78c..a8e93bbff 100644
--- a/applications/plugins/totp/ui/scenes/generate_token/totp_scene_generate_token.c
+++ b/applications/external/totp/ui/scenes/generate_token/totp_scene_generate_token.c
@@ -14,7 +14,11 @@
#include "../../../lib/roll_value/roll_value.h"
#include "../../scene_director.h"
#include "../token_menu/totp_scene_token_menu.h"
-#include "../../../workers/type_code/type_code.h"
+#include "../../../features_config.h"
+#include "../../../workers/usb_type_code/usb_type_code.h"
+#ifdef TOTP_BADBT_TYPE_ENABLED
+#include "../../../workers/bt_type_code/bt_type_code.h"
+#endif
static const uint8_t PROGRESS_BAR_MARGIN = 3;
static const uint8_t PROGRESS_BAR_HEIGHT = 4;
@@ -25,9 +29,10 @@ typedef struct {
bool need_token_update;
TokenInfo* current_token;
uint32_t last_token_gen_time;
- TotpTypeCodeWorkerContext* type_code_worker_context;
+ TotpUsbTypeCodeWorkerContext* usb_type_code_worker_context;
NotificationMessage const** notification_sequence_new_token;
NotificationMessage const** notification_sequence_badusb;
+ FuriMutex* last_code_update_sync;
} SceneState;
static const NotificationSequence*
@@ -72,7 +77,7 @@ static const NotificationSequence*
}
static const NotificationSequence*
- get_notification_sequence_badusb(const PluginState* plugin_state, SceneState* scene_state) {
+ get_notification_sequence_automation(const PluginState* plugin_state, SceneState* scene_state) {
if(scene_state->notification_sequence_badusb == NULL) {
uint8_t i = 0;
uint8_t length = 3;
@@ -201,9 +206,28 @@ void totp_scene_generate_token_activate(
plugin_state->current_scene_state = scene_state;
FURI_LOG_D(LOGGING_TAG, "Timezone set to: %f", (double)plugin_state->timezone_offset);
update_totp_params(plugin_state);
- scene_state->type_code_worker_context = totp_type_code_worker_start();
- scene_state->type_code_worker_context->string = &scene_state->last_code[0];
- scene_state->type_code_worker_context->string_length = TOTP_TOKEN_DIGITS_MAX_COUNT + 1;
+
+ scene_state->last_code_update_sync = furi_mutex_alloc(FuriMutexTypeNormal);
+ if(plugin_state->automation_method & AutomationMethodBadUsb) {
+ scene_state->usb_type_code_worker_context = totp_usb_type_code_worker_start(
+ &scene_state->last_code[0],
+ TOTP_TOKEN_DIGITS_MAX_COUNT + 1,
+ scene_state->last_code_update_sync);
+ }
+
+#ifdef TOTP_BADBT_TYPE_ENABLED
+
+ if(plugin_state->automation_method & AutomationMethodBadBt) {
+ if(plugin_state->bt_type_code_worker_context == NULL) {
+ plugin_state->bt_type_code_worker_context = totp_bt_type_code_worker_init();
+ }
+ totp_bt_type_code_worker_start(
+ plugin_state->bt_type_code_worker_context,
+ &scene_state->last_code[0],
+ TOTP_TOKEN_DIGITS_MAX_COUNT + 1,
+ scene_state->last_code_update_sync);
+ }
+#endif
}
void totp_scene_generate_token_render(Canvas* const canvas, PluginState* plugin_state) {
@@ -242,8 +266,7 @@ void totp_scene_generate_token_render(Canvas* const canvas, PluginState* plugin_
const TokenInfo* tokenInfo = scene_state->current_token;
if(tokenInfo->token != NULL && tokenInfo->token_length > 0) {
- furi_mutex_acquire(
- scene_state->type_code_worker_context->string_sync, FuriWaitForever);
+ furi_mutex_acquire(scene_state->last_code_update_sync, FuriWaitForever);
size_t key_length;
uint8_t* key = totp_crypto_decrypt(
tokenInfo->token, tokenInfo->token_length, &plugin_state->iv[0], &key_length);
@@ -262,12 +285,11 @@ void totp_scene_generate_token_render(Canvas* const canvas, PluginState* plugin_
memset_s(key, key_length, 0, key_length);
free(key);
} else {
- furi_mutex_acquire(
- scene_state->type_code_worker_context->string_sync, FuriWaitForever);
+ furi_mutex_acquire(scene_state->last_code_update_sync, FuriWaitForever);
int_token_to_str(0, scene_state->last_code, tokenInfo->digits);
}
- furi_mutex_release(scene_state->type_code_worker_context->string_sync);
+ furi_mutex_release(scene_state->last_code_update_sync);
if(is_new_token_time) {
notification_message(
@@ -327,6 +349,15 @@ void totp_scene_generate_token_render(Canvas* const canvas, PluginState* plugin_
canvas_draw_icon(
canvas, SCREEN_WIDTH - 9, SCREEN_HEIGHT_CENTER - 24, &I_totp_arrow_right_8x9);
}
+
+#if defined(TOTP_BADBT_TYPE_ENABLED) && defined(TOTP_BADBT_TYPE_ICON_ENABLED)
+ if(plugin_state->automation_method & AutomationMethodBadBt &&
+ plugin_state->bt_type_code_worker_context != NULL &&
+ plugin_state->bt_type_code_worker_context->is_advertising) {
+ canvas_draw_icon(
+ canvas, SCREEN_WIDTH_CENTER - 5, SCREEN_HEIGHT_CENTER + 13, &I_hid_ble_10x7);
+ }
+#endif
}
bool totp_scene_generate_token_handle_event(
@@ -341,14 +372,30 @@ bool totp_scene_generate_token_handle_event(
}
SceneState* scene_state;
- if(event->input.type == InputTypeLong && event->input.key == InputKeyDown) {
- scene_state = (SceneState*)plugin_state->current_scene_state;
- totp_type_code_worker_notify(
- scene_state->type_code_worker_context, TotpTypeCodeWorkerEventType);
- notification_message(
- plugin_state->notification_app,
- get_notification_sequence_badusb(plugin_state, scene_state));
- return true;
+ if(event->input.type == InputTypeLong) {
+ if(event->input.key == InputKeyDown &&
+ plugin_state->automation_method & AutomationMethodBadUsb) {
+ scene_state = (SceneState*)plugin_state->current_scene_state;
+ totp_usb_type_code_worker_notify(
+ scene_state->usb_type_code_worker_context, TotpUsbTypeCodeWorkerEventType);
+ notification_message(
+ plugin_state->notification_app,
+ get_notification_sequence_automation(plugin_state, scene_state));
+ return true;
+ }
+#ifdef TOTP_BADBT_TYPE_ENABLED
+ else if(
+ event->input.key == InputKeyUp &&
+ plugin_state->automation_method & AutomationMethodBadBt) {
+ scene_state = (SceneState*)plugin_state->current_scene_state;
+ totp_bt_type_code_worker_notify(
+ plugin_state->bt_type_code_worker_context, TotpBtTypeCodeWorkerEventType);
+ notification_message(
+ plugin_state->notification_app,
+ get_notification_sequence_automation(plugin_state, scene_state));
+ return true;
+ }
+#endif
}
if(event->input.type != InputTypePress && event->input.type != InputTypeRepeat) {
@@ -400,7 +447,14 @@ void totp_scene_generate_token_deactivate(PluginState* plugin_state) {
if(plugin_state->current_scene_state == NULL) return;
SceneState* scene_state = (SceneState*)plugin_state->current_scene_state;
- totp_type_code_worker_stop(scene_state->type_code_worker_context);
+ if(plugin_state->automation_method & AutomationMethodBadUsb) {
+ totp_usb_type_code_worker_stop(scene_state->usb_type_code_worker_context);
+ }
+#ifdef TOTP_BADBT_TYPE_ENABLED
+ if(plugin_state->automation_method & AutomationMethodBadBt) {
+ totp_bt_type_code_worker_stop(plugin_state->bt_type_code_worker_context);
+ }
+#endif
if(scene_state->notification_sequence_new_token != NULL) {
free(scene_state->notification_sequence_new_token);
@@ -410,6 +464,8 @@ void totp_scene_generate_token_deactivate(PluginState* plugin_state) {
free(scene_state->notification_sequence_badusb);
}
+ furi_mutex_free(scene_state->last_code_update_sync);
+
free(scene_state);
plugin_state->current_scene_state = NULL;
}
diff --git a/applications/plugins/totp/ui/scenes/generate_token/totp_scene_generate_token.h b/applications/external/totp/ui/scenes/generate_token/totp_scene_generate_token.h
similarity index 100%
rename from applications/plugins/totp/ui/scenes/generate_token/totp_scene_generate_token.h
rename to applications/external/totp/ui/scenes/generate_token/totp_scene_generate_token.h
diff --git a/applications/plugins/totp/ui/scenes/token_menu/totp_scene_token_menu.c b/applications/external/totp/ui/scenes/token_menu/totp_scene_token_menu.c
similarity index 100%
rename from applications/plugins/totp/ui/scenes/token_menu/totp_scene_token_menu.c
rename to applications/external/totp/ui/scenes/token_menu/totp_scene_token_menu.c
diff --git a/applications/plugins/totp/ui/scenes/token_menu/totp_scene_token_menu.h b/applications/external/totp/ui/scenes/token_menu/totp_scene_token_menu.h
similarity index 100%
rename from applications/plugins/totp/ui/scenes/token_menu/totp_scene_token_menu.h
rename to applications/external/totp/ui/scenes/token_menu/totp_scene_token_menu.h
diff --git a/applications/plugins/totp/ui/totp_scenes_enum.h b/applications/external/totp/ui/totp_scenes_enum.h
similarity index 100%
rename from applications/plugins/totp/ui/totp_scenes_enum.h
rename to applications/external/totp/ui/totp_scenes_enum.h
diff --git a/applications/plugins/totp/ui/ui_controls.c b/applications/external/totp/ui/ui_controls.c
similarity index 100%
rename from applications/plugins/totp/ui/ui_controls.c
rename to applications/external/totp/ui/ui_controls.c
diff --git a/applications/plugins/totp/ui/ui_controls.h b/applications/external/totp/ui/ui_controls.h
similarity index 100%
rename from applications/plugins/totp/ui/ui_controls.h
rename to applications/external/totp/ui/ui_controls.h
diff --git a/applications/external/totp/workers/bt_type_code/bt_type_code.c b/applications/external/totp/workers/bt_type_code/bt_type_code.c
new file mode 100644
index 000000000..8b9cf6548
--- /dev/null
+++ b/applications/external/totp/workers/bt_type_code/bt_type_code.c
@@ -0,0 +1,141 @@
+#include "bt_type_code.h"
+#include
+#include
+#include "../../types/common.h"
+#include "../../services/convert/convert.h"
+#include "../constants.h"
+
+#define HID_BT_KEYS_STORAGE_PATH EXT_PATH("authenticator/.bt_hid.keys")
+
+static inline bool totp_type_code_worker_stop_requested() {
+ return furi_thread_flags_get() & TotpBtTypeCodeWorkerEventStop;
+}
+
+static void totp_type_code_worker_type_code(TotpBtTypeCodeWorkerContext* context) {
+ uint8_t i = 0;
+ do {
+ furi_delay_ms(500);
+ i++;
+ } while(!furi_hal_bt_is_active() && i < 100 && !totp_type_code_worker_stop_requested());
+
+ if(furi_hal_bt_is_active() && furi_mutex_acquire(context->string_sync, 500) == FuriStatusOk) {
+ furi_delay_ms(500);
+ i = 0;
+ while(i < context->string_length && context->string[i] != 0) {
+ uint8_t digit = CONVERT_CHAR_TO_DIGIT(context->string[i]);
+ if(digit > 9) break;
+ uint8_t hid_kb_key = hid_number_keys[digit];
+ furi_hal_bt_hid_kb_press(hid_kb_key);
+ furi_delay_ms(30);
+ furi_hal_bt_hid_kb_release(hid_kb_key);
+ i++;
+ }
+
+ furi_mutex_release(context->string_sync);
+ }
+}
+
+static int32_t totp_type_code_worker_callback(void* context) {
+ furi_assert(context);
+ FuriMutex* context_mutex = furi_mutex_alloc(FuriMutexTypeNormal);
+ if(context_mutex == NULL) {
+ return 251;
+ }
+
+ TotpBtTypeCodeWorkerContext* bt_context = context;
+
+ furi_hal_bt_start_advertising();
+ bt_context->is_advertising = true;
+
+ while(true) {
+ uint32_t flags = furi_thread_flags_wait(
+ TotpBtTypeCodeWorkerEventStop | TotpBtTypeCodeWorkerEventType,
+ FuriFlagWaitAny,
+ FuriWaitForever);
+ furi_check((flags & FuriFlagError) == 0); //-V562
+ if(flags & TotpBtTypeCodeWorkerEventStop) break;
+
+ if(furi_mutex_acquire(context_mutex, FuriWaitForever) == FuriStatusOk) {
+ if(flags & TotpBtTypeCodeWorkerEventType) {
+ totp_type_code_worker_type_code(bt_context);
+ }
+
+ furi_mutex_release(context_mutex);
+ }
+ }
+
+ furi_hal_bt_stop_advertising();
+
+ bt_context->is_advertising = false;
+
+ furi_mutex_free(context_mutex);
+
+ return 0;
+}
+
+void totp_bt_type_code_worker_start(
+ TotpBtTypeCodeWorkerContext* context,
+ char* code_buf,
+ uint8_t code_buf_length,
+ FuriMutex* code_buf_update_sync) {
+ furi_assert(context != NULL);
+ context->string = code_buf;
+ context->string_length = code_buf_length;
+ context->string_sync = code_buf_update_sync;
+ context->thread = furi_thread_alloc();
+ furi_thread_set_name(context->thread, "TOTPBtHidWorker");
+ furi_thread_set_stack_size(context->thread, 1024);
+ furi_thread_set_context(context->thread, context);
+ furi_thread_set_callback(context->thread, totp_type_code_worker_callback);
+ furi_thread_start(context->thread);
+}
+
+void totp_bt_type_code_worker_stop(TotpBtTypeCodeWorkerContext* context) {
+ furi_assert(context != NULL);
+ furi_thread_flags_set(furi_thread_get_id(context->thread), TotpBtTypeCodeWorkerEventStop);
+ furi_thread_join(context->thread);
+ furi_thread_free(context->thread);
+ context->thread = NULL;
+}
+
+void totp_bt_type_code_worker_notify(
+ TotpBtTypeCodeWorkerContext* context,
+ TotpBtTypeCodeWorkerEvent event) {
+ furi_assert(context != NULL);
+ furi_thread_flags_set(furi_thread_get_id(context->thread), event);
+}
+
+TotpBtTypeCodeWorkerContext* totp_bt_type_code_worker_init() {
+ TotpBtTypeCodeWorkerContext* context = malloc(sizeof(TotpBtTypeCodeWorkerContext));
+ furi_check(context != NULL);
+
+ context->bt = furi_record_open(RECORD_BT);
+ context->is_advertising = false;
+ bt_disconnect(context->bt);
+ furi_delay_ms(200);
+ bt_keys_storage_set_storage_path(context->bt, HID_BT_KEYS_STORAGE_PATH);
+ if(!bt_set_profile(context->bt, BtProfileHidKeyboard)) {
+ FURI_LOG_E(LOGGING_TAG, "Failed to switch BT to keyboard HID profile");
+ }
+
+ return context;
+}
+
+void totp_bt_type_code_worker_free(TotpBtTypeCodeWorkerContext* context) {
+ furi_assert(context != NULL);
+
+ if(context->thread != NULL) {
+ totp_bt_type_code_worker_stop(context);
+ }
+
+ bt_disconnect(context->bt);
+ bt_keys_storage_set_default_path(context->bt);
+
+ if(!bt_set_profile(context->bt, BtProfileSerial)) {
+ FURI_LOG_E(LOGGING_TAG, "Failed to switch BT to Serial profile");
+ }
+ furi_record_close(RECORD_BT);
+ context->bt = NULL;
+
+ free(context);
+}
\ No newline at end of file
diff --git a/applications/external/totp/workers/bt_type_code/bt_type_code.h b/applications/external/totp/workers/bt_type_code/bt_type_code.h
new file mode 100644
index 000000000..475b66db4
--- /dev/null
+++ b/applications/external/totp/workers/bt_type_code/bt_type_code.h
@@ -0,0 +1,35 @@
+#pragma once
+
+#include
+#include
+#include
+#include
+
+typedef uint8_t TotpBtTypeCodeWorkerEvent;
+
+typedef struct {
+ char* string;
+ uint8_t string_length;
+ FuriThread* thread;
+ FuriMutex* string_sync;
+ Bt* bt;
+ bool is_advertising;
+} TotpBtTypeCodeWorkerContext;
+
+enum TotpBtTypeCodeWorkerEvents {
+ TotpBtTypeCodeWorkerEventReserved = (1 << 0),
+ TotpBtTypeCodeWorkerEventStop = (1 << 1),
+ TotpBtTypeCodeWorkerEventType = (1 << 2)
+};
+
+TotpBtTypeCodeWorkerContext* totp_bt_type_code_worker_init();
+void totp_bt_type_code_worker_free(TotpBtTypeCodeWorkerContext* context);
+void totp_bt_type_code_worker_start(
+ TotpBtTypeCodeWorkerContext* context,
+ char* code_buf,
+ uint8_t code_buf_length,
+ FuriMutex* code_buf_update_sync);
+void totp_bt_type_code_worker_stop(TotpBtTypeCodeWorkerContext* context);
+void totp_bt_type_code_worker_notify(
+ TotpBtTypeCodeWorkerContext* context,
+ TotpBtTypeCodeWorkerEvent event);
\ No newline at end of file
diff --git a/applications/external/totp/workers/constants.c b/applications/external/totp/workers/constants.c
new file mode 100644
index 000000000..f3c103578
--- /dev/null
+++ b/applications/external/totp/workers/constants.c
@@ -0,0 +1,14 @@
+#include "constants.h"
+#include
+
+const uint8_t hid_number_keys[10] = {
+ HID_KEYBOARD_0,
+ HID_KEYBOARD_1,
+ HID_KEYBOARD_2,
+ HID_KEYBOARD_3,
+ HID_KEYBOARD_4,
+ HID_KEYBOARD_5,
+ HID_KEYBOARD_6,
+ HID_KEYBOARD_7,
+ HID_KEYBOARD_8,
+ HID_KEYBOARD_9};
\ No newline at end of file
diff --git a/applications/external/totp/workers/constants.h b/applications/external/totp/workers/constants.h
new file mode 100644
index 000000000..c314b6c16
--- /dev/null
+++ b/applications/external/totp/workers/constants.h
@@ -0,0 +1,4 @@
+#pragma once
+#include
+
+extern const uint8_t hid_number_keys[10];
\ No newline at end of file
diff --git a/applications/plugins/totp/workers/type_code/type_code.c b/applications/external/totp/workers/usb_type_code/usb_type_code.c
similarity index 68%
rename from applications/plugins/totp/workers/type_code/type_code.c
rename to applications/external/totp/workers/usb_type_code/usb_type_code.c
index f2b4c9b9e..3132e2317 100644
--- a/applications/plugins/totp/workers/type_code/type_code.c
+++ b/applications/external/totp/workers/usb_type_code/usb_type_code.c
@@ -1,19 +1,8 @@
-#include "type_code.h"
+#include "usb_type_code.h"
#include "../../services/convert/convert.h"
+#include "../constants.h"
-static const uint8_t hid_number_keys[10] = {
- HID_KEYBOARD_0,
- HID_KEYBOARD_1,
- HID_KEYBOARD_2,
- HID_KEYBOARD_3,
- HID_KEYBOARD_4,
- HID_KEYBOARD_5,
- HID_KEYBOARD_6,
- HID_KEYBOARD_7,
- HID_KEYBOARD_8,
- HID_KEYBOARD_9};
-
-static void totp_type_code_worker_restore_usb_mode(TotpTypeCodeWorkerContext* context) {
+static void totp_type_code_worker_restore_usb_mode(TotpUsbTypeCodeWorkerContext* context) {
if(context->usb_mode_prev != NULL) {
furi_hal_usb_set_config(context->usb_mode_prev, NULL);
context->usb_mode_prev = NULL;
@@ -21,10 +10,10 @@ static void totp_type_code_worker_restore_usb_mode(TotpTypeCodeWorkerContext* co
}
static inline bool totp_type_code_worker_stop_requested() {
- return furi_thread_flags_get() & TotpTypeCodeWorkerEventStop;
+ return furi_thread_flags_get() & TotpUsbTypeCodeWorkerEventStop;
}
-static void totp_type_code_worker_type_code(TotpTypeCodeWorkerContext* context) {
+static void totp_type_code_worker_type_code(TotpUsbTypeCodeWorkerContext* context) {
context->usb_mode_prev = furi_hal_usb_get_config();
furi_hal_usb_unlock();
furi_check(furi_hal_usb_set_config(&usb_hid, NULL) == true);
@@ -57,6 +46,7 @@ static void totp_type_code_worker_type_code(TotpTypeCodeWorkerContext* context)
}
static int32_t totp_type_code_worker_callback(void* context) {
+ furi_assert(context);
FuriMutex* context_mutex = furi_mutex_alloc(FuriMutexTypeNormal);
if(context_mutex == NULL) {
return 251;
@@ -64,14 +54,14 @@ static int32_t totp_type_code_worker_callback(void* context) {
while(true) {
uint32_t flags = furi_thread_flags_wait(
- TotpTypeCodeWorkerEventStop | TotpTypeCodeWorkerEventType,
+ TotpUsbTypeCodeWorkerEventStop | TotpUsbTypeCodeWorkerEventType,
FuriFlagWaitAny,
FuriWaitForever);
furi_check((flags & FuriFlagError) == 0); //-V562
- if(flags & TotpTypeCodeWorkerEventStop) break;
+ if(flags & TotpUsbTypeCodeWorkerEventStop) break;
if(furi_mutex_acquire(context_mutex, FuriWaitForever) == FuriStatusOk) {
- if(flags & TotpTypeCodeWorkerEventType) {
+ if(flags & TotpUsbTypeCodeWorkerEventType) {
totp_type_code_worker_type_code(context);
}
@@ -84,13 +74,18 @@ static int32_t totp_type_code_worker_callback(void* context) {
return 0;
}
-TotpTypeCodeWorkerContext* totp_type_code_worker_start() {
- TotpTypeCodeWorkerContext* context = malloc(sizeof(TotpTypeCodeWorkerContext));
+TotpUsbTypeCodeWorkerContext* totp_usb_type_code_worker_start(
+ char* code_buf,
+ uint8_t code_buf_length,
+ FuriMutex* code_buf_update_sync) {
+ TotpUsbTypeCodeWorkerContext* context = malloc(sizeof(TotpUsbTypeCodeWorkerContext));
furi_check(context != NULL);
- context->string_sync = furi_mutex_alloc(FuriMutexTypeNormal);
+ context->string = code_buf;
+ context->string_length = code_buf_length;
+ context->string_sync = code_buf_update_sync;
context->thread = furi_thread_alloc();
context->usb_mode_prev = NULL;
- furi_thread_set_name(context->thread, "TOTPHidWorker");
+ furi_thread_set_name(context->thread, "TOTPUsbHidWorker");
furi_thread_set_stack_size(context->thread, 1024);
furi_thread_set_context(context->thread, context);
furi_thread_set_callback(context->thread, totp_type_code_worker_callback);
@@ -98,19 +93,18 @@ TotpTypeCodeWorkerContext* totp_type_code_worker_start() {
return context;
}
-void totp_type_code_worker_stop(TotpTypeCodeWorkerContext* context) {
+void totp_usb_type_code_worker_stop(TotpUsbTypeCodeWorkerContext* context) {
furi_assert(context != NULL);
- furi_thread_flags_set(furi_thread_get_id(context->thread), TotpTypeCodeWorkerEventStop);
+ furi_thread_flags_set(furi_thread_get_id(context->thread), TotpUsbTypeCodeWorkerEventStop);
furi_thread_join(context->thread);
furi_thread_free(context->thread);
- furi_mutex_free(context->string_sync);
totp_type_code_worker_restore_usb_mode(context);
free(context);
}
-void totp_type_code_worker_notify(
- TotpTypeCodeWorkerContext* context,
- TotpTypeCodeWorkerEvent event) {
+void totp_usb_type_code_worker_notify(
+ TotpUsbTypeCodeWorkerContext* context,
+ TotpUsbTypeCodeWorkerEvent event) {
furi_assert(context != NULL);
furi_thread_flags_set(furi_thread_get_id(context->thread), event);
}
\ No newline at end of file
diff --git a/applications/external/totp/workers/usb_type_code/usb_type_code.h b/applications/external/totp/workers/usb_type_code/usb_type_code.h
new file mode 100644
index 000000000..94fddcc59
--- /dev/null
+++ b/applications/external/totp/workers/usb_type_code/usb_type_code.h
@@ -0,0 +1,30 @@
+#pragma once
+
+#include
+#include
+#include
+
+typedef uint8_t TotpUsbTypeCodeWorkerEvent;
+
+typedef struct {
+ char* string;
+ uint8_t string_length;
+ FuriThread* thread;
+ FuriMutex* string_sync;
+ FuriHalUsbInterface* usb_mode_prev;
+} TotpUsbTypeCodeWorkerContext;
+
+enum TotpUsbTypeCodeWorkerEvents {
+ TotpUsbTypeCodeWorkerEventReserved = (1 << 0),
+ TotpUsbTypeCodeWorkerEventStop = (1 << 1),
+ TotpUsbTypeCodeWorkerEventType = (1 << 2)
+};
+
+TotpUsbTypeCodeWorkerContext* totp_usb_type_code_worker_start(
+ char* code_buf,
+ uint8_t code_buf_length,
+ FuriMutex* code_buf_update_sync);
+void totp_usb_type_code_worker_stop(TotpUsbTypeCodeWorkerContext* context);
+void totp_usb_type_code_worker_notify(
+ TotpUsbTypeCodeWorkerContext* context,
+ TotpUsbTypeCodeWorkerEvent event);
\ No newline at end of file
diff --git a/applications/plugins/usb_hid_autofire/LICENSE b/applications/external/tuning_fork/LICENSE
similarity index 100%
rename from applications/plugins/usb_hid_autofire/LICENSE
rename to applications/external/tuning_fork/LICENSE
diff --git a/applications/plugins/tuning_fork/application.fam b/applications/external/tuning_fork/application.fam
similarity index 100%
rename from applications/plugins/tuning_fork/application.fam
rename to applications/external/tuning_fork/application.fam
diff --git a/applications/plugins/tuning_fork/notes.h b/applications/external/tuning_fork/notes.h
similarity index 100%
rename from applications/plugins/tuning_fork/notes.h
rename to applications/external/tuning_fork/notes.h
diff --git a/applications/plugins/tuning_fork/tuning_fork.c b/applications/external/tuning_fork/tuning_fork.c
similarity index 100%
rename from applications/plugins/tuning_fork/tuning_fork.c
rename to applications/external/tuning_fork/tuning_fork.c
diff --git a/applications/plugins/tuning_fork/tuning_fork_icon.png b/applications/external/tuning_fork/tuning_fork_icon.png
similarity index 100%
rename from applications/plugins/tuning_fork/tuning_fork_icon.png
rename to applications/external/tuning_fork/tuning_fork_icon.png
diff --git a/applications/plugins/tuning_fork/tunings.h b/applications/external/tuning_fork/tunings.h
similarity index 100%
rename from applications/plugins/tuning_fork/tunings.h
rename to applications/external/tuning_fork/tunings.h
diff --git a/applications/plugins/uart_terminal/LICENSE b/applications/external/uart_terminal/LICENSE
similarity index 100%
rename from applications/plugins/uart_terminal/LICENSE
rename to applications/external/uart_terminal/LICENSE
diff --git a/applications/plugins/uart_terminal/application.fam b/applications/external/uart_terminal/application.fam
similarity index 88%
rename from applications/plugins/uart_terminal/application.fam
rename to applications/external/uart_terminal/application.fam
index a36960ea4..530baf2fc 100644
--- a/applications/plugins/uart_terminal/application.fam
+++ b/applications/external/uart_terminal/application.fam
@@ -3,7 +3,6 @@ App(
name="[GPIO] UART Terminal",
apptype=FlipperAppType.EXTERNAL,
entry_point="uart_terminal_app",
- cdefines=["APP_UART_TERMINAL"],
requires=["gui"],
stack_size=1 * 1024,
order=90,
diff --git a/applications/plugins/uart_terminal/assets/KeyBackspaceSelected_16x9.png b/applications/external/uart_terminal/assets/KeyBackspaceSelected_16x9.png
similarity index 100%
rename from applications/plugins/uart_terminal/assets/KeyBackspaceSelected_16x9.png
rename to applications/external/uart_terminal/assets/KeyBackspaceSelected_16x9.png
diff --git a/applications/plugins/uart_terminal/assets/KeyBackspace_16x9.png b/applications/external/uart_terminal/assets/KeyBackspace_16x9.png
similarity index 100%
rename from applications/plugins/uart_terminal/assets/KeyBackspace_16x9.png
rename to applications/external/uart_terminal/assets/KeyBackspace_16x9.png
diff --git a/applications/plugins/uart_terminal/assets/KeySaveSelected_24x11.png b/applications/external/uart_terminal/assets/KeySaveSelected_24x11.png
similarity index 100%
rename from applications/plugins/uart_terminal/assets/KeySaveSelected_24x11.png
rename to applications/external/uart_terminal/assets/KeySaveSelected_24x11.png
diff --git a/applications/plugins/uart_terminal/assets/KeySave_24x11.png b/applications/external/uart_terminal/assets/KeySave_24x11.png
similarity index 100%
rename from applications/plugins/uart_terminal/assets/KeySave_24x11.png
rename to applications/external/uart_terminal/assets/KeySave_24x11.png
diff --git a/applications/plugins/uart_terminal/assets/WarningDolphin_45x42.png b/applications/external/uart_terminal/assets/WarningDolphin_45x42.png
similarity index 100%
rename from applications/plugins/uart_terminal/assets/WarningDolphin_45x42.png
rename to applications/external/uart_terminal/assets/WarningDolphin_45x42.png
diff --git a/applications/plugins/uart_terminal/scenes/uart_terminal_scene.c b/applications/external/uart_terminal/scenes/uart_terminal_scene.c
similarity index 100%
rename from applications/plugins/uart_terminal/scenes/uart_terminal_scene.c
rename to applications/external/uart_terminal/scenes/uart_terminal_scene.c
diff --git a/applications/plugins/uart_terminal/scenes/uart_terminal_scene.h b/applications/external/uart_terminal/scenes/uart_terminal_scene.h
similarity index 100%
rename from applications/plugins/uart_terminal/scenes/uart_terminal_scene.h
rename to applications/external/uart_terminal/scenes/uart_terminal_scene.h
diff --git a/applications/plugins/uart_terminal/scenes/uart_terminal_scene_config.h b/applications/external/uart_terminal/scenes/uart_terminal_scene_config.h
similarity index 100%
rename from applications/plugins/uart_terminal/scenes/uart_terminal_scene_config.h
rename to applications/external/uart_terminal/scenes/uart_terminal_scene_config.h
diff --git a/applications/plugins/uart_terminal/scenes/uart_terminal_scene_console_output.c b/applications/external/uart_terminal/scenes/uart_terminal_scene_console_output.c
similarity index 100%
rename from applications/plugins/uart_terminal/scenes/uart_terminal_scene_console_output.c
rename to applications/external/uart_terminal/scenes/uart_terminal_scene_console_output.c
diff --git a/applications/plugins/uart_terminal/scenes/uart_terminal_scene_start.c b/applications/external/uart_terminal/scenes/uart_terminal_scene_start.c
similarity index 100%
rename from applications/plugins/uart_terminal/scenes/uart_terminal_scene_start.c
rename to applications/external/uart_terminal/scenes/uart_terminal_scene_start.c
diff --git a/applications/plugins/uart_terminal/scenes/uart_terminal_scene_text_input.c b/applications/external/uart_terminal/scenes/uart_terminal_scene_text_input.c
similarity index 100%
rename from applications/plugins/uart_terminal/scenes/uart_terminal_scene_text_input.c
rename to applications/external/uart_terminal/scenes/uart_terminal_scene_text_input.c
diff --git a/applications/plugins/uart_terminal/uart_terminal.png b/applications/external/uart_terminal/uart_terminal.png
similarity index 100%
rename from applications/plugins/uart_terminal/uart_terminal.png
rename to applications/external/uart_terminal/uart_terminal.png
diff --git a/applications/plugins/uart_terminal/uart_terminal_app.c b/applications/external/uart_terminal/uart_terminal_app.c
similarity index 100%
rename from applications/plugins/uart_terminal/uart_terminal_app.c
rename to applications/external/uart_terminal/uart_terminal_app.c
diff --git a/applications/plugins/uart_terminal/uart_terminal_app.h b/applications/external/uart_terminal/uart_terminal_app.h
similarity index 100%
rename from applications/plugins/uart_terminal/uart_terminal_app.h
rename to applications/external/uart_terminal/uart_terminal_app.h
diff --git a/applications/plugins/uart_terminal/uart_terminal_app_i.h b/applications/external/uart_terminal/uart_terminal_app_i.h
similarity index 100%
rename from applications/plugins/uart_terminal/uart_terminal_app_i.h
rename to applications/external/uart_terminal/uart_terminal_app_i.h
diff --git a/applications/plugins/uart_terminal/uart_terminal_custom_event.h b/applications/external/uart_terminal/uart_terminal_custom_event.h
similarity index 100%
rename from applications/plugins/uart_terminal/uart_terminal_custom_event.h
rename to applications/external/uart_terminal/uart_terminal_custom_event.h
diff --git a/applications/plugins/uart_terminal/uart_terminal_uart.c b/applications/external/uart_terminal/uart_terminal_uart.c
similarity index 100%
rename from applications/plugins/uart_terminal/uart_terminal_uart.c
rename to applications/external/uart_terminal/uart_terminal_uart.c
diff --git a/applications/plugins/uart_terminal/uart_terminal_uart.h b/applications/external/uart_terminal/uart_terminal_uart.h
similarity index 100%
rename from applications/plugins/uart_terminal/uart_terminal_uart.h
rename to applications/external/uart_terminal/uart_terminal_uart.h
diff --git a/applications/plugins/uart_terminal/uart_text_input.c b/applications/external/uart_terminal/uart_text_input.c
similarity index 100%
rename from applications/plugins/uart_terminal/uart_text_input.c
rename to applications/external/uart_terminal/uart_text_input.c
diff --git a/applications/plugins/uart_terminal/uart_text_input.h b/applications/external/uart_terminal/uart_text_input.h
similarity index 100%
rename from applications/plugins/uart_terminal/uart_text_input.h
rename to applications/external/uart_terminal/uart_text_input.h
diff --git a/applications/plugins/uart_terminal/uart_validators.c b/applications/external/uart_terminal/uart_validators.c
similarity index 100%
rename from applications/plugins/uart_terminal/uart_validators.c
rename to applications/external/uart_terminal/uart_validators.c
diff --git a/applications/plugins/uart_terminal/uart_validators.h b/applications/external/uart_terminal/uart_validators.h
similarity index 100%
rename from applications/plugins/uart_terminal/uart_validators.h
rename to applications/external/uart_terminal/uart_validators.h
diff --git a/applications/plugins/unitemp/LICENSE.md b/applications/external/unitemp/LICENSE.md
similarity index 100%
rename from applications/plugins/unitemp/LICENSE.md
rename to applications/external/unitemp/LICENSE.md
diff --git a/applications/plugins/unitemp/Sensors.c b/applications/external/unitemp/Sensors.c
similarity index 100%
rename from applications/plugins/unitemp/Sensors.c
rename to applications/external/unitemp/Sensors.c
diff --git a/applications/plugins/unitemp/Sensors.h b/applications/external/unitemp/Sensors.h
similarity index 100%
rename from applications/plugins/unitemp/Sensors.h
rename to applications/external/unitemp/Sensors.h
diff --git a/applications/plugins/unitemp/application.fam b/applications/external/unitemp/application.fam
similarity index 93%
rename from applications/plugins/unitemp/application.fam
rename to applications/external/unitemp/application.fam
index d975da5ff..b0e0aa6ee 100644
--- a/applications/plugins/unitemp/application.fam
+++ b/applications/external/unitemp/application.fam
@@ -3,7 +3,6 @@ App(
name="[GPIO] Unitemp",
apptype=FlipperAppType.EXTERNAL,
entry_point="unitemp_app",
- cdefines=["UNITEMP_APP"],
requires=[
"gui",
],
diff --git a/applications/plugins/unitemp/assets/flipper_happy_2_60x38.png b/applications/external/unitemp/assets/flipper_happy_2_60x38.png
similarity index 100%
rename from applications/plugins/unitemp/assets/flipper_happy_2_60x38.png
rename to applications/external/unitemp/assets/flipper_happy_2_60x38.png
diff --git a/applications/plugins/unitemp/assets/flipper_happy_60x38.png b/applications/external/unitemp/assets/flipper_happy_60x38.png
similarity index 100%
rename from applications/plugins/unitemp/assets/flipper_happy_60x38.png
rename to applications/external/unitemp/assets/flipper_happy_60x38.png
diff --git a/applications/plugins/unitemp/assets/flipper_sad_60x38.png b/applications/external/unitemp/assets/flipper_sad_60x38.png
similarity index 100%
rename from applications/plugins/unitemp/assets/flipper_sad_60x38.png
rename to applications/external/unitemp/assets/flipper_sad_60x38.png
diff --git a/applications/plugins/unitemp/assets/hum_9x15.png b/applications/external/unitemp/assets/hum_9x15.png
similarity index 100%
rename from applications/plugins/unitemp/assets/hum_9x15.png
rename to applications/external/unitemp/assets/hum_9x15.png
diff --git a/applications/plugins/unitemp/assets/in_hg_15x15.png b/applications/external/unitemp/assets/in_hg_15x15.png
similarity index 100%
rename from applications/plugins/unitemp/assets/in_hg_15x15.png
rename to applications/external/unitemp/assets/in_hg_15x15.png
diff --git a/applications/plugins/unitemp/assets/mm_hg_15x15.png b/applications/external/unitemp/assets/mm_hg_15x15.png
similarity index 100%
rename from applications/plugins/unitemp/assets/mm_hg_15x15.png
rename to applications/external/unitemp/assets/mm_hg_15x15.png
diff --git a/applications/plugins/unitemp/assets/pressure_7x13.png b/applications/external/unitemp/assets/pressure_7x13.png
similarity index 100%
rename from applications/plugins/unitemp/assets/pressure_7x13.png
rename to applications/external/unitemp/assets/pressure_7x13.png
diff --git a/applications/plugins/unitemp/assets/repo_qr_50x50.png b/applications/external/unitemp/assets/repo_qr_50x50.png
similarity index 100%
rename from applications/plugins/unitemp/assets/repo_qr_50x50.png
rename to applications/external/unitemp/assets/repo_qr_50x50.png
diff --git a/applications/plugins/unitemp/assets/sherlok_53x45.png b/applications/external/unitemp/assets/sherlok_53x45.png
similarity index 100%
rename from applications/plugins/unitemp/assets/sherlok_53x45.png
rename to applications/external/unitemp/assets/sherlok_53x45.png
diff --git a/applications/plugins/unitemp/assets/temp_C_11x14.png b/applications/external/unitemp/assets/temp_C_11x14.png
similarity index 100%
rename from applications/plugins/unitemp/assets/temp_C_11x14.png
rename to applications/external/unitemp/assets/temp_C_11x14.png
diff --git a/applications/plugins/unitemp/assets/temp_F_11x14.png b/applications/external/unitemp/assets/temp_F_11x14.png
similarity index 100%
rename from applications/plugins/unitemp/assets/temp_F_11x14.png
rename to applications/external/unitemp/assets/temp_F_11x14.png
diff --git a/applications/plugins/unitemp/icon.png b/applications/external/unitemp/icon.png
similarity index 100%
rename from applications/plugins/unitemp/icon.png
rename to applications/external/unitemp/icon.png
diff --git a/applications/plugins/unitemp/interfaces/I2CSensor.c b/applications/external/unitemp/interfaces/I2CSensor.c
similarity index 100%
rename from applications/plugins/unitemp/interfaces/I2CSensor.c
rename to applications/external/unitemp/interfaces/I2CSensor.c
diff --git a/applications/plugins/unitemp/interfaces/I2CSensor.h b/applications/external/unitemp/interfaces/I2CSensor.h
similarity index 100%
rename from applications/plugins/unitemp/interfaces/I2CSensor.h
rename to applications/external/unitemp/interfaces/I2CSensor.h
diff --git a/applications/plugins/unitemp/interfaces/OneWireSensor.c b/applications/external/unitemp/interfaces/OneWireSensor.c
similarity index 100%
rename from applications/plugins/unitemp/interfaces/OneWireSensor.c
rename to applications/external/unitemp/interfaces/OneWireSensor.c
diff --git a/applications/plugins/unitemp/interfaces/OneWireSensor.h b/applications/external/unitemp/interfaces/OneWireSensor.h
similarity index 100%
rename from applications/plugins/unitemp/interfaces/OneWireSensor.h
rename to applications/external/unitemp/interfaces/OneWireSensor.h
diff --git a/applications/plugins/unitemp/interfaces/SPISensor.c b/applications/external/unitemp/interfaces/SPISensor.c
similarity index 100%
rename from applications/plugins/unitemp/interfaces/SPISensor.c
rename to applications/external/unitemp/interfaces/SPISensor.c
diff --git a/applications/plugins/unitemp/interfaces/SPISensor.h b/applications/external/unitemp/interfaces/SPISensor.h
similarity index 100%
rename from applications/plugins/unitemp/interfaces/SPISensor.h
rename to applications/external/unitemp/interfaces/SPISensor.h
diff --git a/applications/plugins/unitemp/interfaces/SingleWireSensor.c b/applications/external/unitemp/interfaces/SingleWireSensor.c
similarity index 100%
rename from applications/plugins/unitemp/interfaces/SingleWireSensor.c
rename to applications/external/unitemp/interfaces/SingleWireSensor.c
diff --git a/applications/plugins/unitemp/interfaces/SingleWireSensor.h b/applications/external/unitemp/interfaces/SingleWireSensor.h
similarity index 100%
rename from applications/plugins/unitemp/interfaces/SingleWireSensor.h
rename to applications/external/unitemp/interfaces/SingleWireSensor.h
diff --git a/applications/plugins/unitemp/sensors/AM2320.c b/applications/external/unitemp/sensors/AM2320.c
similarity index 100%
rename from applications/plugins/unitemp/sensors/AM2320.c
rename to applications/external/unitemp/sensors/AM2320.c
diff --git a/applications/plugins/unitemp/sensors/AM2320.h b/applications/external/unitemp/sensors/AM2320.h
similarity index 100%
rename from applications/plugins/unitemp/sensors/AM2320.h
rename to applications/external/unitemp/sensors/AM2320.h
diff --git a/applications/plugins/unitemp/sensors/BME680.c b/applications/external/unitemp/sensors/BME680.c
similarity index 100%
rename from applications/plugins/unitemp/sensors/BME680.c
rename to applications/external/unitemp/sensors/BME680.c
diff --git a/applications/plugins/unitemp/sensors/BME680.h b/applications/external/unitemp/sensors/BME680.h
similarity index 100%
rename from applications/plugins/unitemp/sensors/BME680.h
rename to applications/external/unitemp/sensors/BME680.h
diff --git a/applications/plugins/unitemp/sensors/BMP180.c b/applications/external/unitemp/sensors/BMP180.c
similarity index 100%
rename from applications/plugins/unitemp/sensors/BMP180.c
rename to applications/external/unitemp/sensors/BMP180.c
diff --git a/applications/plugins/unitemp/sensors/BMP180.h b/applications/external/unitemp/sensors/BMP180.h
similarity index 100%
rename from applications/plugins/unitemp/sensors/BMP180.h
rename to applications/external/unitemp/sensors/BMP180.h
diff --git a/applications/plugins/unitemp/sensors/BMx280.c b/applications/external/unitemp/sensors/BMx280.c
similarity index 100%
rename from applications/plugins/unitemp/sensors/BMx280.c
rename to applications/external/unitemp/sensors/BMx280.c
diff --git a/applications/plugins/unitemp/sensors/BMx280.h b/applications/external/unitemp/sensors/BMx280.h
similarity index 100%
rename from applications/plugins/unitemp/sensors/BMx280.h
rename to applications/external/unitemp/sensors/BMx280.h
diff --git a/applications/plugins/unitemp/sensors/DHT20.c b/applications/external/unitemp/sensors/DHT20.c
similarity index 100%
rename from applications/plugins/unitemp/sensors/DHT20.c
rename to applications/external/unitemp/sensors/DHT20.c
diff --git a/applications/plugins/unitemp/sensors/DHT20.h b/applications/external/unitemp/sensors/DHT20.h
similarity index 100%
rename from applications/plugins/unitemp/sensors/DHT20.h
rename to applications/external/unitemp/sensors/DHT20.h
diff --git a/applications/plugins/unitemp/sensors/HDC1080.c b/applications/external/unitemp/sensors/HDC1080.c
similarity index 100%
rename from applications/plugins/unitemp/sensors/HDC1080.c
rename to applications/external/unitemp/sensors/HDC1080.c
diff --git a/applications/plugins/unitemp/sensors/HDC1080.h b/applications/external/unitemp/sensors/HDC1080.h
similarity index 100%
rename from applications/plugins/unitemp/sensors/HDC1080.h
rename to applications/external/unitemp/sensors/HDC1080.h
diff --git a/applications/plugins/unitemp/sensors/HTU21x.c b/applications/external/unitemp/sensors/HTU21x.c
similarity index 100%
rename from applications/plugins/unitemp/sensors/HTU21x.c
rename to applications/external/unitemp/sensors/HTU21x.c
diff --git a/applications/plugins/unitemp/sensors/HTU21x.h b/applications/external/unitemp/sensors/HTU21x.h
similarity index 100%
rename from applications/plugins/unitemp/sensors/HTU21x.h
rename to applications/external/unitemp/sensors/HTU21x.h
diff --git a/applications/plugins/unitemp/sensors/LM75.c b/applications/external/unitemp/sensors/LM75.c
similarity index 100%
rename from applications/plugins/unitemp/sensors/LM75.c
rename to applications/external/unitemp/sensors/LM75.c
diff --git a/applications/plugins/unitemp/sensors/LM75.h b/applications/external/unitemp/sensors/LM75.h
similarity index 100%
rename from applications/plugins/unitemp/sensors/LM75.h
rename to applications/external/unitemp/sensors/LM75.h
diff --git a/applications/plugins/unitemp/sensors/MAX31855.c b/applications/external/unitemp/sensors/MAX31855.c
similarity index 100%
rename from applications/plugins/unitemp/sensors/MAX31855.c
rename to applications/external/unitemp/sensors/MAX31855.c
diff --git a/applications/plugins/unitemp/sensors/MAX31855.h b/applications/external/unitemp/sensors/MAX31855.h
similarity index 100%
rename from applications/plugins/unitemp/sensors/MAX31855.h
rename to applications/external/unitemp/sensors/MAX31855.h
diff --git a/applications/plugins/unitemp/sensors/MAX6675.c b/applications/external/unitemp/sensors/MAX6675.c
similarity index 100%
rename from applications/plugins/unitemp/sensors/MAX6675.c
rename to applications/external/unitemp/sensors/MAX6675.c
diff --git a/applications/plugins/unitemp/sensors/MAX6675.h b/applications/external/unitemp/sensors/MAX6675.h
similarity index 100%
rename from applications/plugins/unitemp/sensors/MAX6675.h
rename to applications/external/unitemp/sensors/MAX6675.h
diff --git a/applications/plugins/unitemp/sensors/SHT30.c b/applications/external/unitemp/sensors/SHT30.c
similarity index 100%
rename from applications/plugins/unitemp/sensors/SHT30.c
rename to applications/external/unitemp/sensors/SHT30.c
diff --git a/applications/plugins/unitemp/sensors/SHT30.h b/applications/external/unitemp/sensors/SHT30.h
similarity index 100%
rename from applications/plugins/unitemp/sensors/SHT30.h
rename to applications/external/unitemp/sensors/SHT30.h
diff --git a/applications/plugins/unitemp/sensors/Sensors.xlsx b/applications/external/unitemp/sensors/Sensors.xlsx
similarity index 100%
rename from applications/plugins/unitemp/sensors/Sensors.xlsx
rename to applications/external/unitemp/sensors/Sensors.xlsx
diff --git a/applications/plugins/unitemp/unitemp.c b/applications/external/unitemp/unitemp.c
similarity index 100%
rename from applications/plugins/unitemp/unitemp.c
rename to applications/external/unitemp/unitemp.c
diff --git a/applications/plugins/unitemp/unitemp.h b/applications/external/unitemp/unitemp.h
similarity index 100%
rename from applications/plugins/unitemp/unitemp.h
rename to applications/external/unitemp/unitemp.h
diff --git a/applications/plugins/unitemp/views/General_view.c b/applications/external/unitemp/views/General_view.c
similarity index 100%
rename from applications/plugins/unitemp/views/General_view.c
rename to applications/external/unitemp/views/General_view.c
diff --git a/applications/plugins/unitemp/views/MainMenu_view.c b/applications/external/unitemp/views/MainMenu_view.c
similarity index 100%
rename from applications/plugins/unitemp/views/MainMenu_view.c
rename to applications/external/unitemp/views/MainMenu_view.c
diff --git a/applications/plugins/unitemp/views/Popup_view.c b/applications/external/unitemp/views/Popup_view.c
similarity index 100%
rename from applications/plugins/unitemp/views/Popup_view.c
rename to applications/external/unitemp/views/Popup_view.c
diff --git a/applications/plugins/unitemp/views/SensorActions_view.c b/applications/external/unitemp/views/SensorActions_view.c
similarity index 100%
rename from applications/plugins/unitemp/views/SensorActions_view.c
rename to applications/external/unitemp/views/SensorActions_view.c
diff --git a/applications/plugins/unitemp/views/SensorEdit_view.c b/applications/external/unitemp/views/SensorEdit_view.c
similarity index 100%
rename from applications/plugins/unitemp/views/SensorEdit_view.c
rename to applications/external/unitemp/views/SensorEdit_view.c
diff --git a/applications/plugins/unitemp/views/SensorNameEdit_view.c b/applications/external/unitemp/views/SensorNameEdit_view.c
similarity index 100%
rename from applications/plugins/unitemp/views/SensorNameEdit_view.c
rename to applications/external/unitemp/views/SensorNameEdit_view.c
diff --git a/applications/plugins/unitemp/views/SensorsList_view.c b/applications/external/unitemp/views/SensorsList_view.c
similarity index 100%
rename from applications/plugins/unitemp/views/SensorsList_view.c
rename to applications/external/unitemp/views/SensorsList_view.c
diff --git a/applications/plugins/unitemp/views/Settings_view.c b/applications/external/unitemp/views/Settings_view.c
similarity index 100%
rename from applications/plugins/unitemp/views/Settings_view.c
rename to applications/external/unitemp/views/Settings_view.c
diff --git a/applications/plugins/unitemp/views/UnitempViews.h b/applications/external/unitemp/views/UnitempViews.h
similarity index 100%
rename from applications/plugins/unitemp/views/UnitempViews.h
rename to applications/external/unitemp/views/UnitempViews.h
diff --git a/applications/plugins/unitemp/views/Widgets_view.c b/applications/external/unitemp/views/Widgets_view.c
similarity index 100%
rename from applications/plugins/unitemp/views/Widgets_view.c
rename to applications/external/unitemp/views/Widgets_view.c
diff --git a/applications/plugins/usb_hid_autofire/CHANGELOG.md b/applications/external/usb_hid_autofire/CHANGELOG.md
similarity index 100%
rename from applications/plugins/usb_hid_autofire/CHANGELOG.md
rename to applications/external/usb_hid_autofire/CHANGELOG.md
diff --git a/applications/plugins/wifi_deauther/LICENSE b/applications/external/usb_hid_autofire/LICENSE
similarity index 100%
rename from applications/plugins/wifi_deauther/LICENSE
rename to applications/external/usb_hid_autofire/LICENSE
diff --git a/applications/plugins/usb_hid_autofire/application.fam b/applications/external/usb_hid_autofire/application.fam
similarity index 75%
rename from applications/plugins/usb_hid_autofire/application.fam
rename to applications/external/usb_hid_autofire/application.fam
index 8a184d674..9e7b9378c 100644
--- a/applications/plugins/usb_hid_autofire/application.fam
+++ b/applications/external/usb_hid_autofire/application.fam
@@ -1,5 +1,3 @@
-# qv. https://github.com/flipperdevices/flipperzero-firmware/blob/dev/documentation/AppManifests.md
-
App(
appid="usb_hid_autofire",
name="USB HID Autofire",
diff --git a/applications/plugins/usb_hid_autofire/tools.c b/applications/external/usb_hid_autofire/tools.c
similarity index 100%
rename from applications/plugins/usb_hid_autofire/tools.c
rename to applications/external/usb_hid_autofire/tools.c
diff --git a/applications/plugins/usb_hid_autofire/tools.h b/applications/external/usb_hid_autofire/tools.h
similarity index 100%
rename from applications/plugins/usb_hid_autofire/tools.h
rename to applications/external/usb_hid_autofire/tools.h
diff --git a/applications/plugins/usb_hid_autofire/usb_hid_autofire.c b/applications/external/usb_hid_autofire/usb_hid_autofire.c
similarity index 100%
rename from applications/plugins/usb_hid_autofire/usb_hid_autofire.c
rename to applications/external/usb_hid_autofire/usb_hid_autofire.c
diff --git a/applications/plugins/usb_hid_autofire/usb_hid_autofire.kra b/applications/external/usb_hid_autofire/usb_hid_autofire.kra
similarity index 100%
rename from applications/plugins/usb_hid_autofire/usb_hid_autofire.kra
rename to applications/external/usb_hid_autofire/usb_hid_autofire.kra
diff --git a/applications/plugins/usb_hid_autofire/usb_hid_autofire.png b/applications/external/usb_hid_autofire/usb_hid_autofire.png
similarity index 100%
rename from applications/plugins/usb_hid_autofire/usb_hid_autofire.png
rename to applications/external/usb_hid_autofire/usb_hid_autofire.png
diff --git a/applications/plugins/usb_hid_autofire/usb_hid_autofire.svg b/applications/external/usb_hid_autofire/usb_hid_autofire.svg
similarity index 100%
rename from applications/plugins/usb_hid_autofire/usb_hid_autofire.svg
rename to applications/external/usb_hid_autofire/usb_hid_autofire.svg
diff --git a/applications/plugins/usb_hid_autofire/version.h b/applications/external/usb_hid_autofire/version.h
similarity index 100%
rename from applications/plugins/usb_hid_autofire/version.h
rename to applications/external/usb_hid_autofire/version.h
diff --git a/applications/plugins/videopoker/application.fam b/applications/external/videopoker/application.fam
similarity index 100%
rename from applications/plugins/videopoker/application.fam
rename to applications/external/videopoker/application.fam
diff --git a/applications/plugins/videopoker/poker.c b/applications/external/videopoker/poker.c
similarity index 100%
rename from applications/plugins/videopoker/poker.c
rename to applications/external/videopoker/poker.c
diff --git a/applications/plugins/videopoker/pokerIcon.png b/applications/external/videopoker/pokerIcon.png
similarity index 100%
rename from applications/plugins/videopoker/pokerIcon.png
rename to applications/external/videopoker/pokerIcon.png
diff --git a/applications/plugins/wav_player/application.fam b/applications/external/wav_player/application.fam
similarity index 88%
rename from applications/plugins/wav_player/application.fam
rename to applications/external/wav_player/application.fam
index 4040ed159..007a47694 100644
--- a/applications/plugins/wav_player/application.fam
+++ b/applications/external/wav_player/application.fam
@@ -3,7 +3,6 @@ App(
name="WAV Player",
apptype=FlipperAppType.EXTERNAL,
entry_point="wav_player_app",
- cdefines=["APP_WAV_PLAYER"],
stack_size=4 * 1024,
order=46,
fap_icon="wav_10px.png",
diff --git a/applications/plugins/wav_player/images/music_10px.png b/applications/external/wav_player/images/music_10px.png
similarity index 100%
rename from applications/plugins/wav_player/images/music_10px.png
rename to applications/external/wav_player/images/music_10px.png
diff --git a/applications/plugins/wav_player/wav_10px.png b/applications/external/wav_player/wav_10px.png
similarity index 100%
rename from applications/plugins/wav_player/wav_10px.png
rename to applications/external/wav_player/wav_10px.png
diff --git a/applications/plugins/wav_player/wav_parser.c b/applications/external/wav_player/wav_parser.c
similarity index 100%
rename from applications/plugins/wav_player/wav_parser.c
rename to applications/external/wav_player/wav_parser.c
diff --git a/applications/plugins/wav_player/wav_parser.h b/applications/external/wav_player/wav_parser.h
similarity index 100%
rename from applications/plugins/wav_player/wav_parser.h
rename to applications/external/wav_player/wav_parser.h
diff --git a/applications/plugins/wav_player/wav_player.c b/applications/external/wav_player/wav_player.c
similarity index 71%
rename from applications/plugins/wav_player/wav_player.c
rename to applications/external/wav_player/wav_player.c
index 3fb8b1ea5..9c1b4e5b6 100644
--- a/applications/plugins/wav_player/wav_player.c
+++ b/applications/external/wav_player/wav_player.c
@@ -127,7 +127,7 @@ static void app_free(WavPlayerApp* app) {
// TODO: that works only with 8-bit 2ch audio
static bool fill_data(WavPlayerApp* app, size_t index) {
- if(app->num_channels == 1) {
+ if(app->num_channels == 1 && app->bits_per_sample == 8) {
uint16_t* sample_buffer_start = &app->sample_buffer[index];
size_t count = stream_read(app->stream, app->tmp_buffer, app->samples_count_half);
@@ -166,7 +166,108 @@ static bool fill_data(WavPlayerApp* app, size_t index) {
return count != app->samples_count_half;
}
- if(app->num_channels == 2) {
+ if(app->num_channels == 1 && app->bits_per_sample == 16) {
+ uint16_t* sample_buffer_start = &app->sample_buffer[index];
+ size_t count = stream_read(app->stream, app->tmp_buffer, app->samples_count);
+
+ for(size_t i = count; i < app->samples_count; i++) {
+ //app->tmp_buffer[i] = 0;
+ }
+
+ for(size_t i = 0; i < app->samples_count; i += 2) {
+ int16_t int_16 =
+ (((int16_t)app->tmp_buffer[i + 1] << 8) + (int16_t)app->tmp_buffer[i]);
+
+ float data = ((float)int_16 / 256.0 + 127.0);
+ data -= UINT8_MAX / 2; // to signed
+ data /= UINT8_MAX / 2; // scale -1..1
+
+ data *= app->volume; // volume
+ data = tanhf(data); // hyperbolic tangent limiter
+
+ data *= UINT8_MAX / 2; // scale -128..127
+ data += UINT8_MAX / 2; // to unsigned
+
+ if(data < 0) {
+ data = 0;
+ }
+
+ if(data > 255) {
+ data = 255;
+ }
+
+ sample_buffer_start[i / 2] = data;
+ }
+
+ wav_player_view_set_data(app->view, sample_buffer_start, app->samples_count_half);
+
+ return count != app->samples_count;
+ }
+
+ if(app->num_channels == 2 && app->bits_per_sample == 16) {
+ uint16_t* sample_buffer_start = &app->sample_buffer[index];
+ size_t count = stream_read(app->stream, app->tmp_buffer, app->samples_count);
+
+ for(size_t i = 0; i < app->samples_count; i += 4) {
+ int16_t L = (((int16_t)app->tmp_buffer[i + 1] << 8) + (int16_t)app->tmp_buffer[i]);
+ int16_t R = (((int16_t)app->tmp_buffer[i + 3] << 8) + (int16_t)app->tmp_buffer[i + 2]);
+ int32_t int_16 = L / 2 + R / 2; // (L + R) / 2
+
+ float data = ((float)int_16 / 256.0 + 127.0);
+ data -= UINT8_MAX / 2; // to signed
+ data /= UINT8_MAX / 2; // scale -1..1
+
+ data *= app->volume; // volume
+ data = tanhf(data); // hyperbolic tangent limiter
+
+ data *= UINT8_MAX / 2; // scale -128..127
+ data += UINT8_MAX / 2; // to unsigned
+
+ if(data < 0) {
+ data = 0;
+ }
+
+ if(data > 255) {
+ data = 255;
+ }
+
+ sample_buffer_start[i / 4] = data;
+ }
+
+ count = stream_read(app->stream, app->tmp_buffer, app->samples_count);
+
+ for(size_t i = 0; i < app->samples_count; i += 4) {
+ int16_t L = (((int16_t)app->tmp_buffer[i + 1] << 8) + (int16_t)app->tmp_buffer[i]);
+ int16_t R = (((int16_t)app->tmp_buffer[i + 3] << 8) + (int16_t)app->tmp_buffer[i + 2]);
+ int32_t int_16 = L / 2 + R / 2; // (L + R) / 2
+
+ float data = ((float)int_16 / 256.0 + 127.0);
+ data -= UINT8_MAX / 2; // to signed
+ data /= UINT8_MAX / 2; // scale -1..1
+
+ data *= app->volume; // volume
+ data = tanhf(data); // hyperbolic tangent limiter
+
+ data *= UINT8_MAX / 2; // scale -128..127
+ data += UINT8_MAX / 2; // to unsigned
+
+ if(data < 0) {
+ data = 0;
+ }
+
+ if(data > 255) {
+ data = 255;
+ }
+
+ sample_buffer_start[i / 4 + app->samples_count / 4] = data;
+ }
+
+ wav_player_view_set_data(app->view, sample_buffer_start, app->samples_count_half);
+
+ return count != app->samples_count;
+ }
+
+ if(app->num_channels == 2 && app->bits_per_sample == 8) {
uint16_t* sample_buffer_start = &app->sample_buffer[index];
size_t count = stream_read(app->stream, app->tmp_buffer, app->samples_count);
@@ -270,6 +371,9 @@ static void app_run(WavPlayerApp* app) {
while(1) {
if(furi_message_queue_get(app->queue, &event, FuriWaitForever) == FuriStatusOk) {
if(event.type == WavPlayerEventHalfTransfer) {
+ wav_player_view_set_chans(app->view, app->num_channels);
+ wav_player_view_set_bits(app->view, app->bits_per_sample);
+
eof = fill_data(app, 0);
wav_player_view_set_current(app->view, stream_tell(app->stream));
if(eof) {
@@ -280,6 +384,9 @@ static void app_run(WavPlayerApp* app) {
}
} else if(event.type == WavPlayerEventFullTransfer) {
+ wav_player_view_set_chans(app->view, app->num_channels);
+ wav_player_view_set_bits(app->view, app->bits_per_sample);
+
eof = fill_data(app, app->samples_count_half);
wav_player_view_set_current(app->view, stream_tell(app->stream));
if(eof) {
@@ -297,14 +404,20 @@ static void app_run(WavPlayerApp* app) {
} else if(event.type == WavPlayerEventCtrlMoveL) {
int32_t seek =
stream_tell(app->stream) - wav_parser_get_data_start(app->parser);
- seek =
- MIN(seek, (int32_t)(wav_parser_get_data_len(app->parser) / (size_t)100));
+ seek = MIN(
+ seek,
+ (int32_t)(wav_parser_get_data_len(app->parser) / (size_t)100) % 2 ?
+ ((int32_t)(wav_parser_get_data_len(app->parser) / (size_t)100) - 1) :
+ (int32_t)(wav_parser_get_data_len(app->parser) / (size_t)100));
stream_seek(app->stream, -seek, StreamOffsetFromCurrent);
wav_player_view_set_current(app->view, stream_tell(app->stream));
} else if(event.type == WavPlayerEventCtrlMoveR) {
int32_t seek = wav_parser_get_data_end(app->parser) - stream_tell(app->stream);
- seek =
- MIN(seek, (int32_t)(wav_parser_get_data_len(app->parser) / (size_t)100));
+ seek = MIN(
+ seek,
+ (int32_t)(wav_parser_get_data_len(app->parser) / (size_t)100) % 2 ?
+ ((int32_t)(wav_parser_get_data_len(app->parser) / (size_t)100) - 1) :
+ (int32_t)(wav_parser_get_data_len(app->parser) / (size_t)100));
stream_seek(app->stream, seek, StreamOffsetFromCurrent);
wav_player_view_set_current(app->view, stream_tell(app->stream));
} else if(event.type == WavPlayerEventCtrlOk) {
diff --git a/applications/plugins/wav_player/wav_player_hal.c b/applications/external/wav_player/wav_player_hal.c
similarity index 87%
rename from applications/plugins/wav_player/wav_player_hal.c
rename to applications/external/wav_player/wav_player_hal.c
index e711eccb4..98375d012 100644
--- a/applications/plugins/wav_player/wav_player_hal.c
+++ b/applications/external/wav_player/wav_player_hal.c
@@ -35,7 +35,7 @@ void wav_player_speaker_init(uint32_t sample_rate) {
TIM_InitStruct.Prescaler = 0;
//TIM_InitStruct.Autoreload = 1451; //64 000 000 / 1451 ~= 44100 Hz
- TIM_InitStruct.Autoreload = 64000000 / sample_rate; //to support various sample rates
+ TIM_InitStruct.Autoreload = 64000000 / sample_rate - 1; //to support various sample rates
LL_TIM_Init(SAMPLE_RATE_TIMER, &TIM_InitStruct);
@@ -48,16 +48,12 @@ void wav_player_speaker_init(uint32_t sample_rate) {
//=========================================================
//configuring PA6 pin as TIM16 output
- //furi_hal_gpio_init_ex(&gpio_ext_pa6, (GpioMode)GpioPullNo, (GpioPull)GpioModeAltFunctionPushPull, GpioSpeedVeryHigh, GpioAltFn14TIM16);
- //furi_hal_gpio_init_ex(&gpio_ext_pa6, (GpioMode)GpioPullNo, (GpioPull)GpioModeAltFunctionPushPull, GpioSpeedLow, GpioAltFn14TIM16);
furi_hal_gpio_init_ex(
&gpio_ext_pa6,
GpioModeAltFunctionPushPull,
GpioPullNo,
GpioSpeedVeryHigh,
GpioAltFn14TIM16);
- //furi_hal_gpio_init_simple(&gpio_ext_pa6, GpioModeOutputPushPull);
- //furi_hal_gpio_write(&gpio_ext_pa6, false);
}
void wav_player_speaker_start() {
diff --git a/applications/plugins/wav_player/wav_player_hal.h b/applications/external/wav_player/wav_player_hal.h
similarity index 100%
rename from applications/plugins/wav_player/wav_player_hal.h
rename to applications/external/wav_player/wav_player_hal.h
diff --git a/applications/plugins/wav_player/wav_player_view.c b/applications/external/wav_player/wav_player_view.c
similarity index 90%
rename from applications/plugins/wav_player/wav_player_view.c
rename to applications/external/wav_player/wav_player_view.c
index eee39c902..0cb04d23f 100644
--- a/applications/plugins/wav_player/wav_player_view.c
+++ b/applications/external/wav_player/wav_player_view.c
@@ -12,6 +12,12 @@ static void wav_player_view_draw_callback(Canvas* canvas, void* _model) {
uint8_t x_pos = 0;
uint8_t y_pos = 0;
+ /*char buffer[20];
+ snprintf(buffer, sizeof(buffer), "%d", model->num_channels);
+ canvas_draw_str(canvas, 0, 10, buffer);
+ snprintf(buffer, sizeof(buffer), "%d", model->bits_per_sample);
+ canvas_draw_str(canvas, 0, 20, buffer);*/
+
// volume
x_pos = 123;
y_pos = 0;
@@ -156,6 +162,18 @@ void wav_player_view_set_play(WavPlayerView* wav_view, bool play) {
wav_view->view, WavPlayerViewModel * model, { model->play = play; }, true);
}
+void wav_player_view_set_chans(WavPlayerView* wav_view, uint16_t chn) {
+ furi_assert(wav_view);
+ with_view_model(
+ wav_view->view, WavPlayerViewModel * model, { model->num_channels = chn; }, true);
+}
+
+void wav_player_view_set_bits(WavPlayerView* wav_view, uint16_t bit) {
+ furi_assert(wav_view);
+ with_view_model(
+ wav_view->view, WavPlayerViewModel * model, { model->bits_per_sample = bit; }, true);
+}
+
void wav_player_view_set_data(WavPlayerView* wav_view, uint16_t* data, size_t data_count) {
furi_assert(wav_view);
with_view_model(
diff --git a/applications/plugins/wav_player/wav_player_view.h b/applications/external/wav_player/wav_player_view.h
similarity index 89%
rename from applications/plugins/wav_player/wav_player_view.h
rename to applications/external/wav_player/wav_player_view.h
index d841b04c0..02ed4479c 100644
--- a/applications/plugins/wav_player/wav_player_view.h
+++ b/applications/external/wav_player/wav_player_view.h
@@ -43,6 +43,9 @@ typedef struct {
size_t end;
size_t current;
uint8_t data[DATA_COUNT];
+
+ uint16_t bits_per_sample;
+ uint16_t num_channels;
} WavPlayerViewModel;
WavPlayerView* wav_player_view_alloc();
@@ -63,6 +66,9 @@ void wav_player_view_set_play(WavPlayerView* wav_view, bool play);
void wav_player_view_set_data(WavPlayerView* wav_view, uint16_t* data, size_t data_count);
+void wav_player_view_set_bits(WavPlayerView* wav_view, uint16_t bit);
+void wav_player_view_set_chans(WavPlayerView* wav_view, uint16_t chn);
+
void wav_player_view_set_ctrl_callback(WavPlayerView* wav_view, WavPlayerCtrlCallback callback);
void wav_player_view_set_context(WavPlayerView* wav_view, void* context);
diff --git a/applications/plugins/weather_station/application.fam b/applications/external/weather_station/application.fam
similarity index 79%
rename from applications/plugins/weather_station/application.fam
rename to applications/external/weather_station/application.fam
index 935f92573..8dcaa1259 100644
--- a/applications/plugins/weather_station/application.fam
+++ b/applications/external/weather_station/application.fam
@@ -1,10 +1,9 @@
App(
appid="weather_station",
name="Weather Station",
- apptype=FlipperAppType.PLUGIN,
+ apptype=FlipperAppType.EXTERNAL,
targets=["f7"],
entry_point="weather_station_app",
- cdefines=["APP_WEATHER_STATION"],
requires=["gui"],
stack_size=4 * 1024,
order=50,
diff --git a/applications/plugins/weather_station/helpers/weather_station_event.h b/applications/external/weather_station/helpers/weather_station_event.h
similarity index 100%
rename from applications/plugins/weather_station/helpers/weather_station_event.h
rename to applications/external/weather_station/helpers/weather_station_event.h
diff --git a/applications/plugins/weather_station/helpers/weather_station_types.h b/applications/external/weather_station/helpers/weather_station_types.h
similarity index 100%
rename from applications/plugins/weather_station/helpers/weather_station_types.h
rename to applications/external/weather_station/helpers/weather_station_types.h
diff --git a/applications/plugins/weather_station/images/Humid_10x15.png b/applications/external/weather_station/images/Humid_10x15.png
similarity index 100%
rename from applications/plugins/weather_station/images/Humid_10x15.png
rename to applications/external/weather_station/images/Humid_10x15.png
diff --git a/applications/plugins/weather_station/images/Humid_8x13.png b/applications/external/weather_station/images/Humid_8x13.png
similarity index 100%
rename from applications/plugins/weather_station/images/Humid_8x13.png
rename to applications/external/weather_station/images/Humid_8x13.png
diff --git a/applications/plugins/weather_station/images/Lock_7x8.png b/applications/external/weather_station/images/Lock_7x8.png
similarity index 100%
rename from applications/plugins/weather_station/images/Lock_7x8.png
rename to applications/external/weather_station/images/Lock_7x8.png
diff --git a/applications/plugins/weather_station/images/Pin_back_arrow_10x8.png b/applications/external/weather_station/images/Pin_back_arrow_10x8.png
similarity index 100%
rename from applications/plugins/weather_station/images/Pin_back_arrow_10x8.png
rename to applications/external/weather_station/images/Pin_back_arrow_10x8.png
diff --git a/applications/plugins/weather_station/images/Quest_7x8.png b/applications/external/weather_station/images/Quest_7x8.png
similarity index 100%
rename from applications/plugins/weather_station/images/Quest_7x8.png
rename to applications/external/weather_station/images/Quest_7x8.png
diff --git a/applications/plugins/weather_station/images/Scanning_123x52.png b/applications/external/weather_station/images/Scanning_123x52.png
similarity index 100%
rename from applications/plugins/weather_station/images/Scanning_123x52.png
rename to applications/external/weather_station/images/Scanning_123x52.png
diff --git a/applications/plugins/weather_station/images/Therm_7x16.png b/applications/external/weather_station/images/Therm_7x16.png
similarity index 100%
rename from applications/plugins/weather_station/images/Therm_7x16.png
rename to applications/external/weather_station/images/Therm_7x16.png
diff --git a/applications/plugins/weather_station/images/Timer_11x11.png b/applications/external/weather_station/images/Timer_11x11.png
similarity index 100%
rename from applications/plugins/weather_station/images/Timer_11x11.png
rename to applications/external/weather_station/images/Timer_11x11.png
diff --git a/applications/plugins/weather_station/images/Unlock_7x8.png b/applications/external/weather_station/images/Unlock_7x8.png
similarity index 100%
rename from applications/plugins/weather_station/images/Unlock_7x8.png
rename to applications/external/weather_station/images/Unlock_7x8.png
diff --git a/applications/plugins/weather_station/images/WarningDolphin_45x42.png b/applications/external/weather_station/images/WarningDolphin_45x42.png
similarity index 100%
rename from applications/plugins/weather_station/images/WarningDolphin_45x42.png
rename to applications/external/weather_station/images/WarningDolphin_45x42.png
diff --git a/applications/plugins/weather_station/images/station_icon.png b/applications/external/weather_station/images/station_icon.png
similarity index 100%
rename from applications/plugins/weather_station/images/station_icon.png
rename to applications/external/weather_station/images/station_icon.png
diff --git a/applications/plugins/weather_station/protocols/acurite_592txr.c b/applications/external/weather_station/protocols/acurite_592txr.c
similarity index 100%
rename from applications/plugins/weather_station/protocols/acurite_592txr.c
rename to applications/external/weather_station/protocols/acurite_592txr.c
diff --git a/applications/plugins/weather_station/protocols/acurite_592txr.h b/applications/external/weather_station/protocols/acurite_592txr.h
similarity index 100%
rename from applications/plugins/weather_station/protocols/acurite_592txr.h
rename to applications/external/weather_station/protocols/acurite_592txr.h
diff --git a/applications/plugins/weather_station/protocols/acurite_606tx.c b/applications/external/weather_station/protocols/acurite_606tx.c
similarity index 100%
rename from applications/plugins/weather_station/protocols/acurite_606tx.c
rename to applications/external/weather_station/protocols/acurite_606tx.c
diff --git a/applications/plugins/weather_station/protocols/acurite_606tx.h b/applications/external/weather_station/protocols/acurite_606tx.h
similarity index 100%
rename from applications/plugins/weather_station/protocols/acurite_606tx.h
rename to applications/external/weather_station/protocols/acurite_606tx.h
diff --git a/applications/plugins/weather_station/protocols/acurite_609txc.c b/applications/external/weather_station/protocols/acurite_609txc.c
similarity index 100%
rename from applications/plugins/weather_station/protocols/acurite_609txc.c
rename to applications/external/weather_station/protocols/acurite_609txc.c
diff --git a/applications/plugins/weather_station/protocols/acurite_609txc.h b/applications/external/weather_station/protocols/acurite_609txc.h
similarity index 100%
rename from applications/plugins/weather_station/protocols/acurite_609txc.h
rename to applications/external/weather_station/protocols/acurite_609txc.h
diff --git a/applications/plugins/weather_station/protocols/ambient_weather.c b/applications/external/weather_station/protocols/ambient_weather.c
similarity index 100%
rename from applications/plugins/weather_station/protocols/ambient_weather.c
rename to applications/external/weather_station/protocols/ambient_weather.c
diff --git a/applications/plugins/weather_station/protocols/ambient_weather.h b/applications/external/weather_station/protocols/ambient_weather.h
similarity index 100%
rename from applications/plugins/weather_station/protocols/ambient_weather.h
rename to applications/external/weather_station/protocols/ambient_weather.h
diff --git a/applications/plugins/weather_station/protocols/auriol_hg0601a.c b/applications/external/weather_station/protocols/auriol_hg0601a.c
similarity index 100%
rename from applications/plugins/weather_station/protocols/auriol_hg0601a.c
rename to applications/external/weather_station/protocols/auriol_hg0601a.c
diff --git a/applications/plugins/weather_station/protocols/auriol_hg0601a.h b/applications/external/weather_station/protocols/auriol_hg0601a.h
similarity index 100%
rename from applications/plugins/weather_station/protocols/auriol_hg0601a.h
rename to applications/external/weather_station/protocols/auriol_hg0601a.h
diff --git a/applications/plugins/weather_station/protocols/gt_wt_02.c b/applications/external/weather_station/protocols/gt_wt_02.c
similarity index 100%
rename from applications/plugins/weather_station/protocols/gt_wt_02.c
rename to applications/external/weather_station/protocols/gt_wt_02.c
diff --git a/applications/plugins/weather_station/protocols/gt_wt_02.h b/applications/external/weather_station/protocols/gt_wt_02.h
similarity index 100%
rename from applications/plugins/weather_station/protocols/gt_wt_02.h
rename to applications/external/weather_station/protocols/gt_wt_02.h
diff --git a/applications/plugins/weather_station/protocols/gt_wt_03.c b/applications/external/weather_station/protocols/gt_wt_03.c
similarity index 100%
rename from applications/plugins/weather_station/protocols/gt_wt_03.c
rename to applications/external/weather_station/protocols/gt_wt_03.c
diff --git a/applications/plugins/weather_station/protocols/gt_wt_03.h b/applications/external/weather_station/protocols/gt_wt_03.h
similarity index 100%
rename from applications/plugins/weather_station/protocols/gt_wt_03.h
rename to applications/external/weather_station/protocols/gt_wt_03.h
diff --git a/applications/plugins/weather_station/protocols/infactory.c b/applications/external/weather_station/protocols/infactory.c
similarity index 100%
rename from applications/plugins/weather_station/protocols/infactory.c
rename to applications/external/weather_station/protocols/infactory.c
diff --git a/applications/plugins/weather_station/protocols/infactory.h b/applications/external/weather_station/protocols/infactory.h
similarity index 100%
rename from applications/plugins/weather_station/protocols/infactory.h
rename to applications/external/weather_station/protocols/infactory.h
diff --git a/applications/plugins/weather_station/protocols/lacrosse_tx.c b/applications/external/weather_station/protocols/lacrosse_tx.c
similarity index 100%
rename from applications/plugins/weather_station/protocols/lacrosse_tx.c
rename to applications/external/weather_station/protocols/lacrosse_tx.c
diff --git a/applications/plugins/weather_station/protocols/lacrosse_tx.h b/applications/external/weather_station/protocols/lacrosse_tx.h
similarity index 100%
rename from applications/plugins/weather_station/protocols/lacrosse_tx.h
rename to applications/external/weather_station/protocols/lacrosse_tx.h
diff --git a/applications/plugins/weather_station/protocols/lacrosse_tx141thbv2.c b/applications/external/weather_station/protocols/lacrosse_tx141thbv2.c
similarity index 100%
rename from applications/plugins/weather_station/protocols/lacrosse_tx141thbv2.c
rename to applications/external/weather_station/protocols/lacrosse_tx141thbv2.c
diff --git a/applications/plugins/weather_station/protocols/lacrosse_tx141thbv2.h b/applications/external/weather_station/protocols/lacrosse_tx141thbv2.h
similarity index 100%
rename from applications/plugins/weather_station/protocols/lacrosse_tx141thbv2.h
rename to applications/external/weather_station/protocols/lacrosse_tx141thbv2.h
diff --git a/applications/plugins/weather_station/protocols/nexus_th.c b/applications/external/weather_station/protocols/nexus_th.c
similarity index 100%
rename from applications/plugins/weather_station/protocols/nexus_th.c
rename to applications/external/weather_station/protocols/nexus_th.c
diff --git a/applications/plugins/weather_station/protocols/nexus_th.h b/applications/external/weather_station/protocols/nexus_th.h
similarity index 100%
rename from applications/plugins/weather_station/protocols/nexus_th.h
rename to applications/external/weather_station/protocols/nexus_th.h
diff --git a/applications/plugins/weather_station/protocols/oregon2.c b/applications/external/weather_station/protocols/oregon2.c
similarity index 100%
rename from applications/plugins/weather_station/protocols/oregon2.c
rename to applications/external/weather_station/protocols/oregon2.c
diff --git a/applications/plugins/weather_station/protocols/oregon2.h b/applications/external/weather_station/protocols/oregon2.h
similarity index 100%
rename from applications/plugins/weather_station/protocols/oregon2.h
rename to applications/external/weather_station/protocols/oregon2.h
diff --git a/applications/plugins/weather_station/protocols/oregon_v1.c b/applications/external/weather_station/protocols/oregon_v1.c
similarity index 100%
rename from applications/plugins/weather_station/protocols/oregon_v1.c
rename to applications/external/weather_station/protocols/oregon_v1.c
diff --git a/applications/plugins/weather_station/protocols/oregon_v1.h b/applications/external/weather_station/protocols/oregon_v1.h
similarity index 100%
rename from applications/plugins/weather_station/protocols/oregon_v1.h
rename to applications/external/weather_station/protocols/oregon_v1.h
diff --git a/applications/plugins/weather_station/protocols/protocol_items.c b/applications/external/weather_station/protocols/protocol_items.c
similarity index 100%
rename from applications/plugins/weather_station/protocols/protocol_items.c
rename to applications/external/weather_station/protocols/protocol_items.c
diff --git a/applications/plugins/weather_station/protocols/protocol_items.h b/applications/external/weather_station/protocols/protocol_items.h
similarity index 100%
rename from applications/plugins/weather_station/protocols/protocol_items.h
rename to applications/external/weather_station/protocols/protocol_items.h
diff --git a/applications/plugins/weather_station/protocols/thermopro_tx4.c b/applications/external/weather_station/protocols/thermopro_tx4.c
similarity index 100%
rename from applications/plugins/weather_station/protocols/thermopro_tx4.c
rename to applications/external/weather_station/protocols/thermopro_tx4.c
diff --git a/applications/plugins/weather_station/protocols/thermopro_tx4.h b/applications/external/weather_station/protocols/thermopro_tx4.h
similarity index 100%
rename from applications/plugins/weather_station/protocols/thermopro_tx4.h
rename to applications/external/weather_station/protocols/thermopro_tx4.h
diff --git a/applications/plugins/weather_station/protocols/tx_8300.c b/applications/external/weather_station/protocols/tx_8300.c
similarity index 100%
rename from applications/plugins/weather_station/protocols/tx_8300.c
rename to applications/external/weather_station/protocols/tx_8300.c
diff --git a/applications/plugins/weather_station/protocols/tx_8300.h b/applications/external/weather_station/protocols/tx_8300.h
similarity index 100%
rename from applications/plugins/weather_station/protocols/tx_8300.h
rename to applications/external/weather_station/protocols/tx_8300.h
diff --git a/applications/plugins/weather_station/protocols/ws_generic.c b/applications/external/weather_station/protocols/ws_generic.c
similarity index 100%
rename from applications/plugins/weather_station/protocols/ws_generic.c
rename to applications/external/weather_station/protocols/ws_generic.c
diff --git a/applications/plugins/weather_station/protocols/ws_generic.h b/applications/external/weather_station/protocols/ws_generic.h
similarity index 100%
rename from applications/plugins/weather_station/protocols/ws_generic.h
rename to applications/external/weather_station/protocols/ws_generic.h
diff --git a/applications/plugins/weather_station/scenes/weather_station_receiver.c b/applications/external/weather_station/scenes/weather_station_receiver.c
similarity index 100%
rename from applications/plugins/weather_station/scenes/weather_station_receiver.c
rename to applications/external/weather_station/scenes/weather_station_receiver.c
diff --git a/applications/plugins/weather_station/scenes/weather_station_scene.c b/applications/external/weather_station/scenes/weather_station_scene.c
similarity index 100%
rename from applications/plugins/weather_station/scenes/weather_station_scene.c
rename to applications/external/weather_station/scenes/weather_station_scene.c
diff --git a/applications/plugins/weather_station/scenes/weather_station_scene.h b/applications/external/weather_station/scenes/weather_station_scene.h
similarity index 100%
rename from applications/plugins/weather_station/scenes/weather_station_scene.h
rename to applications/external/weather_station/scenes/weather_station_scene.h
diff --git a/applications/plugins/weather_station/scenes/weather_station_scene_about.c b/applications/external/weather_station/scenes/weather_station_scene_about.c
similarity index 100%
rename from applications/plugins/weather_station/scenes/weather_station_scene_about.c
rename to applications/external/weather_station/scenes/weather_station_scene_about.c
diff --git a/applications/plugins/weather_station/scenes/weather_station_scene_config.h b/applications/external/weather_station/scenes/weather_station_scene_config.h
similarity index 100%
rename from applications/plugins/weather_station/scenes/weather_station_scene_config.h
rename to applications/external/weather_station/scenes/weather_station_scene_config.h
diff --git a/applications/plugins/weather_station/scenes/weather_station_scene_receiver_config.c b/applications/external/weather_station/scenes/weather_station_scene_receiver_config.c
similarity index 100%
rename from applications/plugins/weather_station/scenes/weather_station_scene_receiver_config.c
rename to applications/external/weather_station/scenes/weather_station_scene_receiver_config.c
diff --git a/applications/plugins/weather_station/scenes/weather_station_scene_receiver_info.c b/applications/external/weather_station/scenes/weather_station_scene_receiver_info.c
similarity index 100%
rename from applications/plugins/weather_station/scenes/weather_station_scene_receiver_info.c
rename to applications/external/weather_station/scenes/weather_station_scene_receiver_info.c
diff --git a/applications/plugins/weather_station/scenes/weather_station_scene_start.c b/applications/external/weather_station/scenes/weather_station_scene_start.c
similarity index 100%
rename from applications/plugins/weather_station/scenes/weather_station_scene_start.c
rename to applications/external/weather_station/scenes/weather_station_scene_start.c
diff --git a/applications/plugins/weather_station/views/weather_station_receiver.c b/applications/external/weather_station/views/weather_station_receiver.c
similarity index 100%
rename from applications/plugins/weather_station/views/weather_station_receiver.c
rename to applications/external/weather_station/views/weather_station_receiver.c
diff --git a/applications/plugins/weather_station/views/weather_station_receiver.h b/applications/external/weather_station/views/weather_station_receiver.h
similarity index 100%
rename from applications/plugins/weather_station/views/weather_station_receiver.h
rename to applications/external/weather_station/views/weather_station_receiver.h
diff --git a/applications/plugins/weather_station/views/weather_station_receiver_info.c b/applications/external/weather_station/views/weather_station_receiver_info.c
similarity index 100%
rename from applications/plugins/weather_station/views/weather_station_receiver_info.c
rename to applications/external/weather_station/views/weather_station_receiver_info.c
diff --git a/applications/plugins/weather_station/views/weather_station_receiver_info.h b/applications/external/weather_station/views/weather_station_receiver_info.h
similarity index 100%
rename from applications/plugins/weather_station/views/weather_station_receiver_info.h
rename to applications/external/weather_station/views/weather_station_receiver_info.h
diff --git a/applications/plugins/weather_station/weather_station_10px.png b/applications/external/weather_station/weather_station_10px.png
similarity index 100%
rename from applications/plugins/weather_station/weather_station_10px.png
rename to applications/external/weather_station/weather_station_10px.png
diff --git a/applications/plugins/weather_station/weather_station_app.c b/applications/external/weather_station/weather_station_app.c
similarity index 96%
rename from applications/plugins/weather_station/weather_station_app.c
rename to applications/external/weather_station/weather_station_app.c
index ffa569f20..a3135a6b0 100644
--- a/applications/plugins/weather_station/weather_station_app.c
+++ b/applications/external/weather_station/weather_station_app.c
@@ -107,6 +107,11 @@ WeatherStationApp* weather_station_app_alloc() {
// Enable power for External CC1101 if it is connected
furi_hal_subghz_enable_ext_power();
+ // Auto switch to internal radio if external radio is not available
+ furi_delay_ms(15);
+ if(!furi_hal_subghz_check_radio()) {
+ furi_hal_subghz_set_radio_type(SubGhzRadioInternal);
+ }
furi_hal_power_suppress_charge_enter();
diff --git a/applications/plugins/weather_station/weather_station_app_i.c b/applications/external/weather_station/weather_station_app_i.c
similarity index 100%
rename from applications/plugins/weather_station/weather_station_app_i.c
rename to applications/external/weather_station/weather_station_app_i.c
diff --git a/applications/plugins/weather_station/weather_station_app_i.h b/applications/external/weather_station/weather_station_app_i.h
similarity index 100%
rename from applications/plugins/weather_station/weather_station_app_i.h
rename to applications/external/weather_station/weather_station_app_i.h
diff --git a/applications/plugins/weather_station/weather_station_history.c b/applications/external/weather_station/weather_station_history.c
similarity index 100%
rename from applications/plugins/weather_station/weather_station_history.c
rename to applications/external/weather_station/weather_station_history.c
diff --git a/applications/plugins/weather_station/weather_station_history.h b/applications/external/weather_station/weather_station_history.h
similarity index 100%
rename from applications/plugins/weather_station/weather_station_history.h
rename to applications/external/weather_station/weather_station_history.h
diff --git a/applications/plugins/yatzee/LICENSE b/applications/external/wifi_deauther/LICENSE
similarity index 100%
rename from applications/plugins/yatzee/LICENSE
rename to applications/external/wifi_deauther/LICENSE
diff --git a/applications/plugins/wifi_deauther/application.fam b/applications/external/wifi_deauther/application.fam
similarity index 100%
rename from applications/plugins/wifi_deauther/application.fam
rename to applications/external/wifi_deauther/application.fam
diff --git a/applications/plugins/wifi_deauther/scenes/wifi_deauther_scene.c b/applications/external/wifi_deauther/scenes/wifi_deauther_scene.c
similarity index 100%
rename from applications/plugins/wifi_deauther/scenes/wifi_deauther_scene.c
rename to applications/external/wifi_deauther/scenes/wifi_deauther_scene.c
diff --git a/applications/plugins/wifi_deauther/scenes/wifi_deauther_scene.h b/applications/external/wifi_deauther/scenes/wifi_deauther_scene.h
similarity index 100%
rename from applications/plugins/wifi_deauther/scenes/wifi_deauther_scene.h
rename to applications/external/wifi_deauther/scenes/wifi_deauther_scene.h
diff --git a/applications/plugins/wifi_deauther/scenes/wifi_deauther_scene_config.h b/applications/external/wifi_deauther/scenes/wifi_deauther_scene_config.h
similarity index 100%
rename from applications/plugins/wifi_deauther/scenes/wifi_deauther_scene_config.h
rename to applications/external/wifi_deauther/scenes/wifi_deauther_scene_config.h
diff --git a/applications/plugins/wifi_deauther/scenes/wifi_deauther_scene_console_output.c b/applications/external/wifi_deauther/scenes/wifi_deauther_scene_console_output.c
similarity index 100%
rename from applications/plugins/wifi_deauther/scenes/wifi_deauther_scene_console_output.c
rename to applications/external/wifi_deauther/scenes/wifi_deauther_scene_console_output.c
diff --git a/applications/plugins/wifi_deauther/scenes/wifi_deauther_scene_start.c b/applications/external/wifi_deauther/scenes/wifi_deauther_scene_start.c
similarity index 100%
rename from applications/plugins/wifi_deauther/scenes/wifi_deauther_scene_start.c
rename to applications/external/wifi_deauther/scenes/wifi_deauther_scene_start.c
diff --git a/applications/plugins/wifi_deauther/scenes/wifi_deauther_scene_text_input.c b/applications/external/wifi_deauther/scenes/wifi_deauther_scene_text_input.c
similarity index 100%
rename from applications/plugins/wifi_deauther/scenes/wifi_deauther_scene_text_input.c
rename to applications/external/wifi_deauther/scenes/wifi_deauther_scene_text_input.c
diff --git a/applications/plugins/wifi_deauther/wifi_10px.png b/applications/external/wifi_deauther/wifi_10px.png
similarity index 100%
rename from applications/plugins/wifi_deauther/wifi_10px.png
rename to applications/external/wifi_deauther/wifi_10px.png
diff --git a/applications/plugins/wifi_deauther/wifi_deauther_app.c b/applications/external/wifi_deauther/wifi_deauther_app.c
similarity index 98%
rename from applications/plugins/wifi_deauther/wifi_deauther_app.c
rename to applications/external/wifi_deauther/wifi_deauther_app.c
index 28fb28d88..2c9ea17fc 100644
--- a/applications/plugins/wifi_deauther/wifi_deauther_app.c
+++ b/applications/external/wifi_deauther/wifi_deauther_app.c
@@ -3,6 +3,7 @@
#include
#include
#include
+#include
static bool wifi_deauther_app_custom_event_callback(void* context, uint32_t event) {
furi_assert(context);
@@ -24,6 +25,7 @@ static void wifi_deauther_app_tick_event_callback(void* context) {
WifideautherApp* wifi_deauther_app_alloc() {
WifideautherApp* app = malloc(sizeof(WifideautherApp));
+ DOLPHIN_DEED(DolphinDeedPluginStart);
app->gui = furi_record_open(RECORD_GUI);
diff --git a/applications/plugins/wifi_deauther/wifi_deauther_app.h b/applications/external/wifi_deauther/wifi_deauther_app.h
similarity index 100%
rename from applications/plugins/wifi_deauther/wifi_deauther_app.h
rename to applications/external/wifi_deauther/wifi_deauther_app.h
diff --git a/applications/plugins/wifi_deauther/wifi_deauther_app_i.h b/applications/external/wifi_deauther/wifi_deauther_app_i.h
similarity index 100%
rename from applications/plugins/wifi_deauther/wifi_deauther_app_i.h
rename to applications/external/wifi_deauther/wifi_deauther_app_i.h
diff --git a/applications/plugins/wifi_deauther/wifi_deauther_custom_event.h b/applications/external/wifi_deauther/wifi_deauther_custom_event.h
similarity index 100%
rename from applications/plugins/wifi_deauther/wifi_deauther_custom_event.h
rename to applications/external/wifi_deauther/wifi_deauther_custom_event.h
diff --git a/applications/plugins/wifi_deauther/wifi_deauther_uart.c b/applications/external/wifi_deauther/wifi_deauther_uart.c
similarity index 100%
rename from applications/plugins/wifi_deauther/wifi_deauther_uart.c
rename to applications/external/wifi_deauther/wifi_deauther_uart.c
diff --git a/applications/plugins/wifi_deauther/wifi_deauther_uart.h b/applications/external/wifi_deauther/wifi_deauther_uart.h
similarity index 100%
rename from applications/plugins/wifi_deauther/wifi_deauther_uart.h
rename to applications/external/wifi_deauther/wifi_deauther_uart.h
diff --git a/applications/plugins/wifi_marauder_companion/application.fam b/applications/external/wifi_marauder_companion/application.fam
similarity index 79%
rename from applications/plugins/wifi_marauder_companion/application.fam
rename to applications/external/wifi_marauder_companion/application.fam
index 049d66045..5fe303b00 100644
--- a/applications/plugins/wifi_marauder_companion/application.fam
+++ b/applications/external/wifi_marauder_companion/application.fam
@@ -3,9 +3,8 @@ App(
name="[ESP32] WiFi Marauder",
apptype=FlipperAppType.EXTERNAL,
entry_point="wifi_marauder_app",
- cdefines=["APP_WIFI_MARAUDER"],
requires=["gui"],
- stack_size=1 * 1024,
+ stack_size=4 * 1024,
order=90,
fap_icon="wifi_10px.png",
fap_category="WiFi",
diff --git a/applications/plugins/wifi_marauder_companion/scenes/wifi_marauder_scene.c b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene.c
similarity index 100%
rename from applications/plugins/wifi_marauder_companion/scenes/wifi_marauder_scene.c
rename to applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene.c
diff --git a/applications/plugins/wifi_marauder_companion/scenes/wifi_marauder_scene.h b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene.h
similarity index 100%
rename from applications/plugins/wifi_marauder_companion/scenes/wifi_marauder_scene.h
rename to applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene.h
diff --git a/applications/plugins/wifi_marauder_companion/scenes/wifi_marauder_scene_config.h b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_config.h
similarity index 58%
rename from applications/plugins/wifi_marauder_companion/scenes/wifi_marauder_scene_config.h
rename to applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_config.h
index 75e95c9bc..715897d17 100644
--- a/applications/plugins/wifi_marauder_companion/scenes/wifi_marauder_scene_config.h
+++ b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_config.h
@@ -1,3 +1,5 @@
ADD_SCENE(wifi_marauder, start, Start)
ADD_SCENE(wifi_marauder, console_output, ConsoleOutput)
ADD_SCENE(wifi_marauder, text_input, TextInput)
+ADD_SCENE(wifi_marauder, settings_init, SettingsInit)
+ADD_SCENE(wifi_marauder, log_viewer, LogViewer)
diff --git a/applications/plugins/wifi_marauder_companion/scenes/wifi_marauder_scene_console_output.c b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_console_output.c
similarity index 62%
rename from applications/plugins/wifi_marauder_companion/scenes/wifi_marauder_scene_console_output.c
rename to applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_console_output.c
index 8b9984dbf..0729500eb 100644
--- a/applications/plugins/wifi_marauder_companion/scenes/wifi_marauder_scene_console_output.c
+++ b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_console_output.c
@@ -4,6 +4,11 @@ void wifi_marauder_console_output_handle_rx_data_cb(uint8_t* buf, size_t len, vo
furi_assert(context);
WifiMarauderApp* app = context;
+ if(app->is_writing_log) {
+ app->has_saved_logs_this_session = true;
+ storage_file_write(app->log_file, buf, len);
+ }
+
// If text box store gets too big, then truncate it
app->text_box_store_strlen += len;
if(app->text_box_store_strlen >= WIFI_MARAUDER_TEXT_BOX_STORE_SIZE - 1) {
@@ -14,10 +19,18 @@ void wifi_marauder_console_output_handle_rx_data_cb(uint8_t* buf, size_t len, vo
// Null-terminate buf and append to text box store
buf[len] = '\0';
furi_string_cat_printf(app->text_box_store, "%s", buf);
-
view_dispatcher_send_custom_event(app->view_dispatcher, WifiMarauderEventRefreshConsoleOutput);
}
+void wifi_marauder_console_output_handle_rx_packets_cb(uint8_t* buf, size_t len, void* context) {
+ furi_assert(context);
+ WifiMarauderApp* app = context;
+
+ if(app->is_writing_pcap) {
+ storage_file_write(app->capture_file, buf, len);
+ }
+}
+
void wifi_marauder_scene_console_output_on_enter(void* context) {
WifiMarauderApp* app = context;
@@ -33,8 +46,7 @@ void wifi_marauder_scene_console_output_on_enter(void* context) {
furi_string_reset(app->text_box_store);
app->text_box_store_strlen = 0;
if(0 == strncmp("help", app->selected_tx_string, strlen("help"))) {
- const char* help_msg =
- "Marauder companion v0.3.0\nFor app support/feedback,\nreach out to me:\n@cococode#6011 (discord)\n0xchocolate (github)\n";
+ const char* help_msg = "Marauder companion " WIFI_MARAUDER_APP_VERSION "\n";
furi_string_cat_str(app->text_box_store, help_msg);
app->text_box_store_strlen += strlen(help_msg);
}
@@ -46,7 +58,7 @@ void wifi_marauder_scene_console_output_on_enter(void* context) {
}
}
- // Set starting text - for "View Log", this will just be what was already in the text box store
+ // Set starting text - for "View Log from end", this will just be what was already in the text box store
text_box_set_text(app->text_box, furi_string_get_cstr(app->text_box_store));
scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneConsoleOutput, 0);
@@ -54,10 +66,29 @@ void wifi_marauder_scene_console_output_on_enter(void* context) {
// Register callback to receive data
wifi_marauder_uart_set_handle_rx_data_cb(
- app->uart, wifi_marauder_console_output_handle_rx_data_cb); // setup callback for rx thread
+ app->uart,
+ wifi_marauder_console_output_handle_rx_data_cb); // setup callback for general log rx thread
+ wifi_marauder_uart_set_handle_rx_data_cb(
+ app->lp_uart,
+ wifi_marauder_console_output_handle_rx_packets_cb); // setup callback for packets rx thread
- // Send command with newline '\n'
+ // Get ready to send command
if(app->is_command && app->selected_tx_string) {
+ // Create files *before* sending command
+ // (it takes time to iterate through the directory)
+ if(app->ok_to_save_logs) {
+ app->is_writing_log = true;
+ wifi_marauder_create_log_file(app);
+ }
+
+ // If it is a sniff function, open the pcap file for recording
+ if(app->ok_to_save_pcaps &&
+ strncmp("sniff", app->selected_tx_string, strlen("sniff")) == 0) {
+ app->is_writing_pcap = true;
+ wifi_marauder_create_pcap_file(app);
+ }
+
+ // Send command with newline '\n'
wifi_marauder_uart_tx(
(uint8_t*)(app->selected_tx_string), strlen(app->selected_tx_string));
wifi_marauder_uart_tx((uint8_t*)("\n"), 1);
@@ -84,9 +115,20 @@ void wifi_marauder_scene_console_output_on_exit(void* context) {
// Unregister rx callback
wifi_marauder_uart_set_handle_rx_data_cb(app->uart, NULL);
+ wifi_marauder_uart_set_handle_rx_data_cb(app->lp_uart, NULL);
// Automatically stop the scan when exiting view
if(app->is_command) {
wifi_marauder_uart_tx((uint8_t*)("stopscan\n"), strlen("stopscan\n"));
}
-}
\ No newline at end of file
+
+ app->is_writing_pcap = false;
+ if(app->capture_file && storage_file_is_open(app->capture_file)) {
+ storage_file_close(app->capture_file);
+ }
+
+ app->is_writing_log = false;
+ if(app->log_file && storage_file_is_open(app->log_file)) {
+ storage_file_close(app->log_file);
+ }
+}
diff --git a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_log_viewer.c b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_log_viewer.c
new file mode 100644
index 000000000..f4e84ccc8
--- /dev/null
+++ b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_log_viewer.c
@@ -0,0 +1,185 @@
+#include "../wifi_marauder_app_i.h"
+
+void wifi_marauder_scene_log_viewer_widget_callback(
+ GuiButtonType result,
+ InputType type,
+ void* context) {
+ WifiMarauderApp* app = context;
+ if(type == InputTypeShort) {
+ view_dispatcher_send_custom_event(app->view_dispatcher, result);
+ }
+}
+
+static void _read_log_page_into_text_store(WifiMarauderApp* app) {
+ char temp[64 + 1];
+ storage_file_seek(
+ app->log_file, WIFI_MARAUDER_TEXT_BOX_STORE_SIZE * (app->open_log_file_page - 1), true);
+ furi_string_reset(app->text_box_store);
+ for(uint16_t i = 0; i < (WIFI_MARAUDER_TEXT_BOX_STORE_SIZE / (sizeof(temp) - 1)); i++) {
+ uint16_t num_bytes = storage_file_read(app->log_file, temp, sizeof(temp) - 1);
+ if(num_bytes == 0) {
+ break;
+ }
+ temp[num_bytes] = '\0';
+ furi_string_cat_str(app->text_box_store, temp);
+ }
+}
+
+void wifi_marauder_scene_log_viewer_setup_widget(WifiMarauderApp* app, bool called_from_browse) {
+ Widget* widget = app->widget;
+ bool is_open = storage_file_is_open(app->log_file);
+ bool should_open_log = (app->has_saved_logs_this_session || called_from_browse);
+ if(is_open) {
+ _read_log_page_into_text_store(app);
+ } else if(
+ should_open_log &&
+ storage_file_open(app->log_file, app->log_file_path, FSAM_READ, FSOM_OPEN_EXISTING)) {
+ uint64_t filesize = storage_file_size(app->log_file);
+ app->open_log_file_num_pages = filesize / WIFI_MARAUDER_TEXT_BOX_STORE_SIZE;
+ int extra_page = (filesize % WIFI_MARAUDER_TEXT_BOX_STORE_SIZE != 0) ? 1 : 0;
+ app->open_log_file_num_pages = (filesize / WIFI_MARAUDER_TEXT_BOX_STORE_SIZE) + extra_page;
+ app->open_log_file_page = 1;
+ _read_log_page_into_text_store(app);
+ } else {
+ app->open_log_file_page = 0;
+ app->open_log_file_num_pages = 0;
+ }
+
+ widget_reset(widget);
+
+ if(furi_string_empty(app->text_box_store)) {
+ char help_msg[256];
+ snprintf(
+ help_msg,
+ sizeof(help_msg),
+ "The log is empty! :(\nTry sending a command?\n\nSaving pcaps to flipper sdcard: %s\nSaving logs to flipper sdcard: %s",
+ app->ok_to_save_pcaps ? "ON" : "OFF",
+ app->ok_to_save_logs ? "ON" : "OFF");
+ furi_string_set_str(app->text_box_store, help_msg);
+ }
+
+ widget_add_text_scroll_element(
+ widget, 0, 0, 128, 53, furi_string_get_cstr(app->text_box_store));
+
+ if(1 < app->open_log_file_page && app->open_log_file_page < app->open_log_file_num_pages) {
+ // hide "Browse" text for middle pages
+ widget_add_button_element(
+ widget, GuiButtonTypeCenter, "", wifi_marauder_scene_log_viewer_widget_callback, app);
+ } else {
+ // only show "Browse" text on first and last page
+ widget_add_button_element(
+ widget,
+ GuiButtonTypeCenter,
+ "Browse",
+ wifi_marauder_scene_log_viewer_widget_callback,
+ app);
+ }
+
+ char pagecounter[100];
+ snprintf(
+ pagecounter,
+ sizeof(pagecounter),
+ "%d/%d",
+ app->open_log_file_page,
+ app->open_log_file_num_pages);
+ if(app->open_log_file_page > 1) {
+ if(app->open_log_file_page == app->open_log_file_num_pages) {
+ // only show left side page-count on last page
+ widget_add_button_element(
+ widget,
+ GuiButtonTypeLeft,
+ pagecounter,
+ wifi_marauder_scene_log_viewer_widget_callback,
+ app);
+ } else {
+ widget_add_button_element(
+ widget, GuiButtonTypeLeft, "", wifi_marauder_scene_log_viewer_widget_callback, app);
+ }
+ }
+ if(app->open_log_file_page < app->open_log_file_num_pages) {
+ widget_add_button_element(
+ widget,
+ GuiButtonTypeRight,
+ pagecounter,
+ wifi_marauder_scene_log_viewer_widget_callback,
+ app);
+ }
+}
+
+void wifi_marauder_scene_log_viewer_on_enter(void* context) {
+ WifiMarauderApp* app = context;
+
+ app->open_log_file_page = 0;
+ app->open_log_file_num_pages = 0;
+ bool saved_logs_exist = false;
+ if(!app->has_saved_logs_this_session && furi_string_empty(app->text_box_store)) {
+ // no commands sent yet this session, find last saved log
+ if(storage_dir_open(app->log_file, MARAUDER_APP_FOLDER_LOGS)) {
+ char name[70];
+ char lastname[70];
+ while(storage_dir_read(app->log_file, NULL, name, sizeof(name))) {
+ // keep reading directory until last file is reached
+ strlcpy(lastname, name, sizeof(lastname));
+ saved_logs_exist = true;
+ }
+ if(saved_logs_exist) {
+ snprintf(
+ app->log_file_path,
+ sizeof(app->log_file_path),
+ "%s/%s",
+ MARAUDER_APP_FOLDER_LOGS,
+ lastname);
+ }
+ }
+ storage_dir_close(app->log_file);
+ }
+
+ wifi_marauder_scene_log_viewer_setup_widget(app, saved_logs_exist);
+
+ view_dispatcher_switch_to_view(app->view_dispatcher, WifiMarauderAppViewWidget);
+}
+
+bool wifi_marauder_scene_log_viewer_on_event(void* context, SceneManagerEvent event) {
+ WifiMarauderApp* app = context;
+ bool consumed = false;
+
+ if(event.type == SceneManagerEventTypeCustom) {
+ if(event.event == GuiButtonTypeCenter) {
+ // Browse
+ FuriString* predefined_filepath = furi_string_alloc_set_str(MARAUDER_APP_FOLDER_LOGS);
+ FuriString* selected_filepath = furi_string_alloc();
+ if(dialog_file_browser_show(
+ app->dialogs, selected_filepath, predefined_filepath, NULL)) {
+ strncpy(
+ app->log_file_path,
+ furi_string_get_cstr(selected_filepath),
+ sizeof(app->log_file_path));
+ if(storage_file_is_open(app->log_file)) {
+ storage_file_close(app->log_file);
+ }
+ wifi_marauder_scene_log_viewer_setup_widget(app, true);
+ }
+ furi_string_free(selected_filepath);
+ furi_string_free(predefined_filepath);
+ consumed = true;
+ } else if(event.event == GuiButtonTypeRight) {
+ // Advance page
+ ++app->open_log_file_page;
+ wifi_marauder_scene_log_viewer_setup_widget(app, false);
+ } else if(event.event == GuiButtonTypeLeft) {
+ // Previous page
+ --app->open_log_file_page;
+ wifi_marauder_scene_log_viewer_setup_widget(app, false);
+ }
+ }
+
+ return consumed;
+}
+
+void wifi_marauder_scene_log_viewer_on_exit(void* context) {
+ WifiMarauderApp* app = context;
+ widget_reset(app->widget);
+ if(storage_file_is_open(app->log_file)) {
+ storage_file_close(app->log_file);
+ }
+}
diff --git a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_settings_init.c b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_settings_init.c
new file mode 100644
index 000000000..04d099d12
--- /dev/null
+++ b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_settings_init.c
@@ -0,0 +1,130 @@
+#include "../wifi_marauder_app_i.h"
+
+const char* Y = "Y";
+const char* N = "N";
+
+#define PROMPT_PCAPS 0
+#define PROMPT_LOGS 1
+
+void wifi_marauder_scene_settings_init_widget_callback(
+ GuiButtonType result,
+ InputType type,
+ void* context) {
+ WifiMarauderApp* app = context;
+ if(type == InputTypeShort) {
+ view_dispatcher_send_custom_event(app->view_dispatcher, result);
+ }
+}
+
+void wifi_marauder_scene_settings_init_setup_widget(WifiMarauderApp* app) {
+ Widget* widget = app->widget;
+
+ widget_reset(widget);
+
+ widget_add_button_element(
+ widget, GuiButtonTypeLeft, "No", wifi_marauder_scene_settings_init_widget_callback, app);
+ widget_add_button_element(
+ widget, GuiButtonTypeRight, "Yes", wifi_marauder_scene_settings_init_widget_callback, app);
+
+ if(app->which_prompt == PROMPT_PCAPS) {
+ widget_add_string_element(widget, 0, 0, AlignLeft, AlignTop, FontPrimary, "Save pcaps?");
+ widget_add_text_scroll_element(
+ widget,
+ 0,
+ 12,
+ 128,
+ 38,
+ "With compatible marauder\nfirmware, you can choose to\nsave captures (pcaps) to the\nflipper sd card here:\n" MARAUDER_APP_FOLDER_USER_PCAPS
+ "\n\nYou can change this setting in the app at any time. Would\nyou like to enable this feature now?");
+ } else {
+ widget_add_string_element(widget, 0, 0, AlignLeft, AlignTop, FontPrimary, "Save logs?");
+ widget_add_text_scroll_element(
+ widget,
+ 0,
+ 12,
+ 128,
+ 38,
+ "This app supports saving text\nlogs of console output to the\nflipper sd card here:\n" MARAUDER_APP_FOLDER_USER_LOGS
+ "\n\nYou can change this setting in the app at any time. Would\nyou like to enable this feature now?");
+ }
+}
+
+void wifi_marauder_scene_settings_init_on_enter(void* context) {
+ WifiMarauderApp* app = context;
+
+ app->which_prompt = PROMPT_PCAPS;
+ wifi_marauder_scene_settings_init_setup_widget(app);
+
+ view_dispatcher_switch_to_view(app->view_dispatcher, WifiMarauderAppViewWidget);
+}
+
+bool wifi_marauder_scene_settings_init_on_event(void* context, SceneManagerEvent event) {
+ WifiMarauderApp* app = context;
+ bool consumed = false;
+
+ if(event.type == SceneManagerEventTypeCustom) {
+ // get which button press: "Yes" or "No"
+ if(event.event == GuiButtonTypeRight) {
+ // Yes
+ if(app->which_prompt == PROMPT_PCAPS) {
+ app->ok_to_save_pcaps = true;
+ } else {
+ app->ok_to_save_logs = true;
+ }
+ } else if(event.event == GuiButtonTypeLeft) {
+ // No
+ if(app->which_prompt == PROMPT_PCAPS) {
+ app->ok_to_save_pcaps = false;
+ } else {
+ app->ok_to_save_logs = false;
+ }
+ }
+
+ // save setting to file, load next widget or scene
+ if(app->which_prompt == PROMPT_PCAPS) {
+ if(storage_file_open(
+ app->save_pcap_setting_file,
+ SAVE_PCAP_SETTING_FILEPATH,
+ FSAM_WRITE,
+ FSOM_CREATE_ALWAYS)) {
+ const char* ok = app->ok_to_save_pcaps ? Y : N;
+ storage_file_write(app->save_pcap_setting_file, ok, sizeof(ok));
+ } else {
+ dialog_message_show_storage_error(app->dialogs, "Cannot save settings");
+ }
+ storage_file_close(app->save_pcap_setting_file);
+ // same scene, different-looking widget
+ app->which_prompt = PROMPT_LOGS;
+ wifi_marauder_scene_settings_init_setup_widget(app);
+ } else {
+ if(storage_file_open(
+ app->save_logs_setting_file,
+ SAVE_LOGS_SETTING_FILEPATH,
+ FSAM_WRITE,
+ FSOM_CREATE_ALWAYS)) {
+ const char* ok = app->ok_to_save_logs ? Y : N;
+ storage_file_write(app->save_logs_setting_file, ok, sizeof(ok));
+ } else {
+ dialog_message_show_storage_error(app->dialogs, "Cannot save settings");
+ }
+ storage_file_close(app->save_logs_setting_file);
+ // go back to start scene (main menu)
+ app->need_to_prompt_settings_init = false;
+ scene_manager_previous_scene(app->scene_manager);
+ }
+ consumed = true;
+ }
+
+ return consumed;
+}
+
+void wifi_marauder_scene_settings_init_on_exit(void* context) {
+ WifiMarauderApp* app = context;
+ widget_reset(app->widget);
+ if(storage_file_is_open(app->save_pcap_setting_file)) {
+ storage_file_close(app->save_pcap_setting_file);
+ }
+ if(storage_file_is_open(app->save_logs_setting_file)) {
+ storage_file_close(app->save_logs_setting_file);
+ }
+}
diff --git a/applications/plugins/wifi_marauder_companion/scenes/wifi_marauder_scene_start.c b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_start.c
similarity index 82%
rename from applications/plugins/wifi_marauder_companion/scenes/wifi_marauder_scene_start.c
rename to applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_start.c
index df759bd15..2b2ee3a8a 100644
--- a/applications/plugins/wifi_marauder_companion/scenes/wifi_marauder_scene_start.c
+++ b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_start.c
@@ -127,6 +127,13 @@ const WifiMarauderItem items[NUM_MENU_ITEMS] = {
{"Update", {"ota", "sd"}, 2, {"update -w", "update -s"}, NO_ARGS, FOCUS_CONSOLE_END, NO_TIP},
{"Reboot", {""}, 1, {"reboot"}, NO_ARGS, FOCUS_CONSOLE_END, NO_TIP},
{"Help", {""}, 1, {"help"}, NO_ARGS, FOCUS_CONSOLE_START, SHOW_STOPSCAN_TIP},
+ {"Save to flipper sdcard", // keep as last entry or change logic in callback below
+ {""},
+ 1,
+ {""},
+ NO_ARGS,
+ FOCUS_CONSOLE_START,
+ NO_TIP},
};
static void wifi_marauder_scene_start_var_list_enter_callback(void* context, uint32_t index) {
@@ -136,6 +143,13 @@ static void wifi_marauder_scene_start_var_list_enter_callback(void* context, uin
furi_assert(index < NUM_MENU_ITEMS);
const WifiMarauderItem* item = &items[index];
+ if(index == NUM_MENU_ITEMS - 1) {
+ // "Save to flipper sdcard" special case - start SettingsInit widget
+ view_dispatcher_send_custom_event(
+ app->view_dispatcher, WifiMarauderEventStartSettingsInit);
+ return;
+ }
+
const int selected_option_index = app->selected_option_index[index];
furi_assert(selected_option_index < item->num_options_menu);
app->selected_tx_string = item->actual_commands[selected_option_index];
@@ -147,6 +161,12 @@ static void wifi_marauder_scene_start_var_list_enter_callback(void* context, uin
item->focus_console;
app->show_stopscan_tip = item->show_stopscan_tip;
+ if(!app->is_command && selected_option_index == 0) {
+ // View Log from start
+ view_dispatcher_send_custom_event(app->view_dispatcher, WifiMarauderEventStartLogViewer);
+ return;
+ }
+
bool needs_keyboard = (item->needs_keyboard == TOGGLE_ARGS) ? (selected_option_index != 0) :
item->needs_keyboard;
if(needs_keyboard) {
@@ -193,6 +213,11 @@ void wifi_marauder_scene_start_on_enter(void* context) {
var_item_list, scene_manager_get_scene_state(app->scene_manager, WifiMarauderSceneStart));
view_dispatcher_switch_to_view(app->view_dispatcher, WifiMarauderAppViewVarItemList);
+
+ // Wait, if the user hasn't initialized sdcard settings, let's prompt them once (then come back here)
+ if(app->need_to_prompt_settings_init) {
+ scene_manager_next_scene(app->scene_manager, WifiMarauderSceneSettingsInit);
+ }
}
bool wifi_marauder_scene_start_on_event(void* context, SceneManagerEvent event) {
@@ -204,16 +229,28 @@ bool wifi_marauder_scene_start_on_event(void* context, SceneManagerEvent event)
if(event.event == WifiMarauderEventStartKeyboard) {
scene_manager_set_scene_state(
app->scene_manager, WifiMarauderSceneStart, app->selected_menu_index);
- scene_manager_next_scene(app->scene_manager, WifiMarauderAppViewTextInput);
+ scene_manager_next_scene(app->scene_manager, WifiMarauderSceneTextInput);
} else if(event.event == WifiMarauderEventStartConsole) {
scene_manager_set_scene_state(
app->scene_manager, WifiMarauderSceneStart, app->selected_menu_index);
- scene_manager_next_scene(app->scene_manager, WifiMarauderAppViewConsoleOutput);
+ scene_manager_next_scene(app->scene_manager, WifiMarauderSceneConsoleOutput);
+ } else if(event.event == WifiMarauderEventStartSettingsInit) {
+ scene_manager_set_scene_state(
+ app->scene_manager, WifiMarauderSceneStart, app->selected_menu_index);
+ scene_manager_next_scene(app->scene_manager, WifiMarauderSceneSettingsInit);
+ } else if(event.event == WifiMarauderEventStartLogViewer) {
+ scene_manager_set_scene_state(
+ app->scene_manager, WifiMarauderSceneStart, app->selected_menu_index);
+ scene_manager_next_scene(app->scene_manager, WifiMarauderSceneLogViewer);
}
consumed = true;
} else if(event.type == SceneManagerEventTypeTick) {
app->selected_menu_index = variable_item_list_get_selected_item_index(app->var_item_list);
consumed = true;
+ } else if(event.type == SceneManagerEventTypeBack) {
+ scene_manager_stop(app->scene_manager);
+ view_dispatcher_stop(app->view_dispatcher);
+ consumed = true;
}
return consumed;
diff --git a/applications/plugins/wifi_marauder_companion/scenes/wifi_marauder_scene_text_input.c b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_text_input.c
similarity index 99%
rename from applications/plugins/wifi_marauder_companion/scenes/wifi_marauder_scene_text_input.c
rename to applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_text_input.c
index ac8b15a2d..b721e868d 100644
--- a/applications/plugins/wifi_marauder_companion/scenes/wifi_marauder_scene_text_input.c
+++ b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_text_input.c
@@ -80,7 +80,7 @@ bool wifi_marauder_scene_text_input_on_event(void* context, SceneManagerEvent ev
if(event.event == WifiMarauderEventStartConsole) {
// Point to custom string to send
app->selected_tx_string = app->text_input_store;
- scene_manager_next_scene(app->scene_manager, WifiMarauderAppViewConsoleOutput);
+ scene_manager_next_scene(app->scene_manager, WifiMarauderSceneConsoleOutput);
consumed = true;
} else if(event.event == WifiMarauderEventSaveSourceMac) {
if(12 != strlen(app->text_input_store)) {
@@ -138,7 +138,7 @@ bool wifi_marauder_scene_text_input_on_event(void* context, SceneManagerEvent ev
app->special_case_input_src_addr,
app->special_case_input_dst_addr);
app->selected_tx_string = app->text_input_store;
- scene_manager_next_scene(app->scene_manager, WifiMarauderAppViewConsoleOutput);
+ scene_manager_next_scene(app->scene_manager, WifiMarauderSceneConsoleOutput);
}
consumed = true;
}
diff --git a/applications/plugins/wifi_marauder_companion/wifi_10px.png b/applications/external/wifi_marauder_companion/wifi_10px.png
similarity index 100%
rename from applications/plugins/wifi_marauder_companion/wifi_10px.png
rename to applications/external/wifi_marauder_companion/wifi_10px.png
diff --git a/applications/plugins/wifi_marauder_companion/wifi_marauder_app.c b/applications/external/wifi_marauder_companion/wifi_marauder_app.c
similarity index 51%
rename from applications/plugins/wifi_marauder_companion/wifi_marauder_app.c
rename to applications/external/wifi_marauder_companion/wifi_marauder_app.c
index 1deb4e6f2..42e94a8b8 100644
--- a/applications/plugins/wifi_marauder_companion/wifi_marauder_app.c
+++ b/applications/external/wifi_marauder_companion/wifi_marauder_app.c
@@ -2,6 +2,7 @@
#include
#include
+#include
static bool wifi_marauder_app_custom_event_callback(void* context, uint32_t event) {
furi_assert(context);
@@ -23,8 +24,15 @@ static void wifi_marauder_app_tick_event_callback(void* context) {
WifiMarauderApp* wifi_marauder_app_alloc() {
WifiMarauderApp* app = malloc(sizeof(WifiMarauderApp));
+ DOLPHIN_DEED(DolphinDeedPluginStart);
app->gui = furi_record_open(RECORD_GUI);
+ app->dialogs = furi_record_open(RECORD_DIALOGS);
+ app->storage = furi_record_open(RECORD_STORAGE);
+ app->capture_file = storage_file_alloc(app->storage);
+ app->log_file = storage_file_alloc(app->storage);
+ app->save_pcap_setting_file = storage_file_alloc(app->storage);
+ app->save_logs_setting_file = storage_file_alloc(app->storage);
app->view_dispatcher = view_dispatcher_alloc();
app->scene_manager = scene_manager_alloc(&wifi_marauder_scene_handlers, app);
@@ -62,11 +70,62 @@ WifiMarauderApp* wifi_marauder_app_alloc() {
view_dispatcher_add_view(
app->view_dispatcher, WifiMarauderAppViewTextInput, text_input_get_view(app->text_input));
+ app->widget = widget_alloc();
+ view_dispatcher_add_view(
+ app->view_dispatcher, WifiMarauderAppViewWidget, widget_get_view(app->widget));
+
+ app->has_saved_logs_this_session = false;
+
+ // if user hasn't confirmed whether to save pcaps and logs to sdcard, then prompt when scene starts
+ app->need_to_prompt_settings_init =
+ (!storage_file_exists(app->storage, SAVE_PCAP_SETTING_FILEPATH) ||
+ !storage_file_exists(app->storage, SAVE_LOGS_SETTING_FILEPATH));
+
scene_manager_next_scene(app->scene_manager, WifiMarauderSceneStart);
return app;
}
+void wifi_marauder_make_app_folder(WifiMarauderApp* app) {
+ furi_assert(app);
+
+ if(!storage_simply_mkdir(app->storage, MARAUDER_APP_FOLDER)) {
+ dialog_message_show_storage_error(app->dialogs, "Cannot create\napp folder");
+ }
+
+ if(!storage_simply_mkdir(app->storage, MARAUDER_APP_FOLDER_PCAPS)) {
+ dialog_message_show_storage_error(app->dialogs, "Cannot create\npcaps folder");
+ }
+
+ if(!storage_simply_mkdir(app->storage, MARAUDER_APP_FOLDER_LOGS)) {
+ dialog_message_show_storage_error(app->dialogs, "Cannot create\npcaps folder");
+ }
+}
+
+void wifi_marauder_load_settings(WifiMarauderApp* app) {
+ if(storage_file_open(
+ app->save_pcap_setting_file,
+ SAVE_PCAP_SETTING_FILEPATH,
+ FSAM_READ,
+ FSOM_OPEN_EXISTING)) {
+ char ok[1];
+ storage_file_read(app->save_pcap_setting_file, ok, sizeof(ok));
+ app->ok_to_save_pcaps = ok[0] == 'Y';
+ }
+ storage_file_close(app->save_pcap_setting_file);
+
+ if(storage_file_open(
+ app->save_logs_setting_file,
+ SAVE_LOGS_SETTING_FILEPATH,
+ FSAM_READ,
+ FSOM_OPEN_EXISTING)) {
+ char ok[1];
+ storage_file_read(app->save_logs_setting_file, ok, sizeof(ok));
+ app->ok_to_save_logs = ok[0] == 'Y';
+ }
+ storage_file_close(app->save_logs_setting_file);
+}
+
void wifi_marauder_app_free(WifiMarauderApp* app) {
furi_assert(app);
@@ -74,36 +133,56 @@ void wifi_marauder_app_free(WifiMarauderApp* app) {
view_dispatcher_remove_view(app->view_dispatcher, WifiMarauderAppViewVarItemList);
view_dispatcher_remove_view(app->view_dispatcher, WifiMarauderAppViewConsoleOutput);
view_dispatcher_remove_view(app->view_dispatcher, WifiMarauderAppViewTextInput);
+ view_dispatcher_remove_view(app->view_dispatcher, WifiMarauderAppViewWidget);
+ widget_free(app->widget);
text_box_free(app->text_box);
furi_string_free(app->text_box_store);
text_input_free(app->text_input);
+ storage_file_free(app->capture_file);
+ storage_file_free(app->log_file);
+ storage_file_free(app->save_pcap_setting_file);
+ storage_file_free(app->save_logs_setting_file);
// View dispatcher
view_dispatcher_free(app->view_dispatcher);
scene_manager_free(app->scene_manager);
wifi_marauder_uart_free(app->uart);
+ wifi_marauder_uart_free(app->lp_uart);
// Close records
furi_record_close(RECORD_GUI);
+ furi_record_close(RECORD_STORAGE);
+ furi_record_close(RECORD_DIALOGS);
free(app);
}
int32_t wifi_marauder_app(void* p) {
UNUSED(p);
- furi_hal_power_enable_otg();
- furi_delay_ms(300);
+
+ uint8_t attempts = 0;
+ while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) {
+ furi_hal_power_enable_otg();
+ furi_delay_ms(10);
+ }
+ furi_delay_ms(200);
WifiMarauderApp* wifi_marauder_app = wifi_marauder_app_alloc();
- wifi_marauder_app->uart = wifi_marauder_uart_init(wifi_marauder_app);
+ wifi_marauder_make_app_folder(wifi_marauder_app);
+ wifi_marauder_load_settings(wifi_marauder_app);
+
+ wifi_marauder_app->uart = wifi_marauder_usart_init(wifi_marauder_app);
+ wifi_marauder_app->lp_uart = wifi_marauder_lp_uart_init(wifi_marauder_app);
view_dispatcher_run(wifi_marauder_app->view_dispatcher);
wifi_marauder_app_free(wifi_marauder_app);
- furi_hal_power_disable_otg();
+ if(furi_hal_power_is_otg_enabled()) {
+ furi_hal_power_disable_otg();
+ }
return 0;
}
diff --git a/applications/plugins/wifi_marauder_companion/wifi_marauder_app.h b/applications/external/wifi_marauder_companion/wifi_marauder_app.h
similarity index 74%
rename from applications/plugins/wifi_marauder_companion/wifi_marauder_app.h
rename to applications/external/wifi_marauder_companion/wifi_marauder_app.h
index 2d41daa55..89a06f904 100644
--- a/applications/plugins/wifi_marauder_companion/wifi_marauder_app.h
+++ b/applications/external/wifi_marauder_companion/wifi_marauder_app.h
@@ -4,6 +4,8 @@
extern "C" {
#endif
+#define WIFI_MARAUDER_APP_VERSION "v0.3.3"
+
typedef struct WifiMarauderApp WifiMarauderApp;
#ifdef __cplusplus
diff --git a/applications/plugins/wifi_marauder_companion/wifi_marauder_app_i.h b/applications/external/wifi_marauder_companion/wifi_marauder_app_i.h
similarity index 61%
rename from applications/plugins/wifi_marauder_companion/wifi_marauder_app_i.h
rename to applications/external/wifi_marauder_companion/wifi_marauder_app_i.h
index 4df9e326b..39b48fae3 100644
--- a/applications/plugins/wifi_marauder_companion/wifi_marauder_app_i.h
+++ b/applications/external/wifi_marauder_companion/wifi_marauder_app_i.h
@@ -6,6 +6,7 @@
#include "scenes/wifi_marauder_scene.h"
#include "wifi_marauder_custom_event.h"
#include "wifi_marauder_uart.h"
+#include "wifi_marauder_pcap.h"
#include
#include
@@ -13,12 +14,25 @@
#include
#include
#include
+#include
-#define NUM_MENU_ITEMS (16)
+#include
+#include
+
+#define NUM_MENU_ITEMS (17)
#define WIFI_MARAUDER_TEXT_BOX_STORE_SIZE (4096)
#define WIFI_MARAUDER_TEXT_INPUT_STORE_SIZE (512)
+#define MARAUDER_APP_FOLDER_USER "apps_data/marauder"
+#define MARAUDER_APP_FOLDER EXT_PATH(MARAUDER_APP_FOLDER_USER)
+#define MARAUDER_APP_FOLDER_PCAPS MARAUDER_APP_FOLDER "/pcaps"
+#define MARAUDER_APP_FOLDER_LOGS MARAUDER_APP_FOLDER "/logs"
+#define MARAUDER_APP_FOLDER_USER_PCAPS MARAUDER_APP_FOLDER_USER "/pcaps"
+#define MARAUDER_APP_FOLDER_USER_LOGS MARAUDER_APP_FOLDER_USER "/logs"
+#define SAVE_PCAP_SETTING_FILEPATH MARAUDER_APP_FOLDER "/save_pcaps_here.setting"
+#define SAVE_LOGS_SETTING_FILEPATH MARAUDER_APP_FOLDER "/save_logs_here.setting"
+
struct WifiMarauderApp {
Gui* gui;
ViewDispatcher* view_dispatcher;
@@ -29,11 +43,26 @@ struct WifiMarauderApp {
size_t text_box_store_strlen;
TextBox* text_box;
TextInput* text_input;
- //Widget* widget;
+ Storage* storage;
+ File* capture_file;
+ File* log_file;
+ char log_file_path[100];
+ File* save_pcap_setting_file;
+ File* save_logs_setting_file;
+ bool need_to_prompt_settings_init;
+ int which_prompt;
+ bool ok_to_save_pcaps;
+ bool ok_to_save_logs;
+ bool has_saved_logs_this_session;
+ DialogsApp* dialogs;
VariableItemList* var_item_list;
+ Widget* widget;
+ int open_log_file_page;
+ int open_log_file_num_pages;
WifiMarauderUart* uart;
+ WifiMarauderUart* lp_uart;
int selected_menu_index;
int selected_option_index[NUM_MENU_ITEMS];
const char* selected_tx_string;
@@ -41,6 +70,8 @@ struct WifiMarauderApp {
bool is_custom_tx_string;
bool focus_console_start;
bool show_stopscan_tip;
+ bool is_writing_pcap;
+ bool is_writing_log;
// For input source and destination MAC in targeted deauth attack
int special_case_input_step;
@@ -73,4 +104,5 @@ typedef enum {
WifiMarauderAppViewVarItemList,
WifiMarauderAppViewConsoleOutput,
WifiMarauderAppViewTextInput,
+ WifiMarauderAppViewWidget,
} WifiMarauderAppView;
diff --git a/applications/plugins/wifi_marauder_companion/wifi_marauder_custom_event.h b/applications/external/wifi_marauder_companion/wifi_marauder_custom_event.h
similarity index 64%
rename from applications/plugins/wifi_marauder_companion/wifi_marauder_custom_event.h
rename to applications/external/wifi_marauder_companion/wifi_marauder_custom_event.h
index 990b457f5..79f96b107 100644
--- a/applications/plugins/wifi_marauder_companion/wifi_marauder_custom_event.h
+++ b/applications/external/wifi_marauder_companion/wifi_marauder_custom_event.h
@@ -5,5 +5,7 @@ typedef enum {
WifiMarauderEventStartConsole,
WifiMarauderEventStartKeyboard,
WifiMarauderEventSaveSourceMac,
- WifiMarauderEventSaveDestinationMac
+ WifiMarauderEventSaveDestinationMac,
+ WifiMarauderEventStartSettingsInit,
+ WifiMarauderEventStartLogViewer
} WifiMarauderCustomEvent;
diff --git a/applications/external/wifi_marauder_companion/wifi_marauder_pcap.c b/applications/external/wifi_marauder_companion/wifi_marauder_pcap.c
new file mode 100644
index 000000000..73e3d98ea
--- /dev/null
+++ b/applications/external/wifi_marauder_companion/wifi_marauder_pcap.c
@@ -0,0 +1,64 @@
+#include "wifi_marauder_app_i.h"
+#include "wifi_marauder_pcap.h"
+
+void wifi_marauder_get_prefix_from_sniff_cmd(char* dest, const char* command) {
+ int start, end, delta;
+ start = strlen("sniff");
+ end = strcspn(command, " ");
+ delta = end - start;
+ strncpy(dest, command + start, end - start);
+ dest[delta] = '\0';
+}
+
+void wifi_marauder_get_prefix_from_cmd(char* dest, const char* command) {
+ int end;
+ end = strcspn(command, " ");
+ strncpy(dest, command, end);
+ dest[end] = '\0';
+}
+
+void wifi_marauder_create_pcap_file(WifiMarauderApp* app) {
+ char prefix[10];
+ char capture_file_path[100];
+ wifi_marauder_get_prefix_from_sniff_cmd(prefix, app->selected_tx_string);
+
+ int i = 0;
+ do {
+ snprintf(
+ capture_file_path,
+ sizeof(capture_file_path),
+ "%s/%s_%d.pcap",
+ MARAUDER_APP_FOLDER_PCAPS,
+ prefix,
+ i);
+ i++;
+ } while(storage_file_exists(app->storage, capture_file_path));
+
+ if(!storage_file_open(app->capture_file, capture_file_path, FSAM_WRITE, FSOM_CREATE_ALWAYS)) {
+ dialog_message_show_storage_error(app->dialogs, "Cannot open pcap file");
+ }
+}
+
+void wifi_marauder_create_log_file(WifiMarauderApp* app) {
+ char prefix[10];
+ char log_file_path[100];
+ wifi_marauder_get_prefix_from_cmd(prefix, app->selected_tx_string);
+
+ int i = 0;
+ do {
+ snprintf(
+ log_file_path,
+ sizeof(log_file_path),
+ "%s/%s_%d.log",
+ MARAUDER_APP_FOLDER_LOGS,
+ prefix,
+ i);
+ i++;
+ } while(storage_file_exists(app->storage, log_file_path));
+
+ if(!storage_file_open(app->log_file, log_file_path, FSAM_WRITE, FSOM_CREATE_ALWAYS)) {
+ dialog_message_show_storage_error(app->dialogs, "Cannot open log file");
+ } else {
+ strcpy(app->log_file_path, log_file_path);
+ }
+}
\ No newline at end of file
diff --git a/applications/external/wifi_marauder_companion/wifi_marauder_pcap.h b/applications/external/wifi_marauder_companion/wifi_marauder_pcap.h
new file mode 100644
index 000000000..94f6282f3
--- /dev/null
+++ b/applications/external/wifi_marauder_companion/wifi_marauder_pcap.h
@@ -0,0 +1,20 @@
+#pragma once
+
+#include "furi_hal.h"
+
+/**
+ * Creates a PCAP file to store incoming packets.
+ * The file name will have a prefix according to the type of scan being performed by the application (Eg: raw_0.pcap)
+ *
+ * @param app Application context
+ */
+void wifi_marauder_create_pcap_file(WifiMarauderApp* app);
+
+/**
+ * Creates a log file to store text from console output.
+ * The file name will have a prefix according to the command being performed by the application (Eg: scanap_0.log)
+ *
+ * @param app Application context
+ */
+// same as wifi_marauder_create_pcap_file, but for log files (to save console text output)
+void wifi_marauder_create_log_file(WifiMarauderApp* app);
diff --git a/applications/plugins/wifi_marauder_companion/wifi_marauder_uart.c b/applications/external/wifi_marauder_companion/wifi_marauder_uart.c
similarity index 71%
rename from applications/plugins/wifi_marauder_companion/wifi_marauder_uart.c
rename to applications/external/wifi_marauder_companion/wifi_marauder_uart.c
index 228b0f83d..5ce6480f2 100644
--- a/applications/plugins/wifi_marauder_companion/wifi_marauder_uart.c
+++ b/applications/external/wifi_marauder_companion/wifi_marauder_uart.c
@@ -2,10 +2,12 @@
#include "wifi_marauder_uart.h"
#define UART_CH (FuriHalUartIdUSART1)
+#define LP_UART_CH (FuriHalUartIdLPUART1)
#define BAUDRATE (115200)
struct WifiMarauderUart {
WifiMarauderApp* app;
+ FuriHalUartId channel;
FuriThread* rx_thread;
FuriStreamBuffer* rx_stream;
uint8_t rx_buf[RX_BUF_SIZE + 1];
@@ -60,25 +62,42 @@ void wifi_marauder_uart_tx(uint8_t* data, size_t len) {
furi_hal_uart_tx(UART_CH, data, len);
}
-WifiMarauderUart* wifi_marauder_uart_init(WifiMarauderApp* app) {
+void wifi_marauder_lp_uart_tx(uint8_t* data, size_t len) {
+ furi_hal_uart_tx(LP_UART_CH, data, len);
+}
+
+WifiMarauderUart*
+ wifi_marauder_uart_init(WifiMarauderApp* app, FuriHalUartId channel, const char* thread_name) {
WifiMarauderUart* uart = malloc(sizeof(WifiMarauderUart));
uart->app = app;
+ uart->channel = channel;
uart->rx_stream = furi_stream_buffer_alloc(RX_BUF_SIZE, 1);
uart->rx_thread = furi_thread_alloc();
- furi_thread_set_name(uart->rx_thread, "WifiMarauderUartRxThread");
+ furi_thread_set_name(uart->rx_thread, thread_name);
furi_thread_set_stack_size(uart->rx_thread, 1024);
furi_thread_set_context(uart->rx_thread, uart);
furi_thread_set_callback(uart->rx_thread, uart_worker);
furi_thread_start(uart->rx_thread);
-
- furi_hal_console_disable();
- furi_hal_uart_set_br(UART_CH, BAUDRATE);
- furi_hal_uart_set_irq_cb(UART_CH, wifi_marauder_uart_on_irq_cb, uart);
+ if(channel == FuriHalUartIdUSART1) {
+ furi_hal_console_disable();
+ } else if(channel == FuriHalUartIdLPUART1) {
+ furi_hal_uart_init(channel, BAUDRATE);
+ }
+ furi_hal_uart_set_br(channel, BAUDRATE);
+ furi_hal_uart_set_irq_cb(channel, wifi_marauder_uart_on_irq_cb, uart);
return uart;
}
+WifiMarauderUart* wifi_marauder_usart_init(WifiMarauderApp* app) {
+ return wifi_marauder_uart_init(app, UART_CH, "WifiMarauderUartRxThread");
+}
+
+WifiMarauderUart* wifi_marauder_lp_uart_init(WifiMarauderApp* app) {
+ return wifi_marauder_uart_init(app, LP_UART_CH, "WifiMarauderLPUartRxThread");
+}
+
void wifi_marauder_uart_free(WifiMarauderUart* uart) {
furi_assert(uart);
@@ -86,7 +105,7 @@ void wifi_marauder_uart_free(WifiMarauderUart* uart) {
furi_thread_join(uart->rx_thread);
furi_thread_free(uart->rx_thread);
- furi_hal_uart_set_irq_cb(UART_CH, NULL, NULL);
+ furi_hal_uart_set_irq_cb(uart->channel, NULL, NULL);
furi_hal_console_enable();
free(uart);
diff --git a/applications/plugins/wifi_marauder_companion/wifi_marauder_uart.h b/applications/external/wifi_marauder_companion/wifi_marauder_uart.h
similarity index 61%
rename from applications/plugins/wifi_marauder_companion/wifi_marauder_uart.h
rename to applications/external/wifi_marauder_companion/wifi_marauder_uart.h
index e38c18dd2..e352cfec5 100644
--- a/applications/plugins/wifi_marauder_companion/wifi_marauder_uart.h
+++ b/applications/external/wifi_marauder_companion/wifi_marauder_uart.h
@@ -2,7 +2,7 @@
#include "furi_hal.h"
-#define RX_BUF_SIZE (320)
+#define RX_BUF_SIZE (2048)
typedef struct WifiMarauderUart WifiMarauderUart;
@@ -10,5 +10,7 @@ void wifi_marauder_uart_set_handle_rx_data_cb(
WifiMarauderUart* uart,
void (*handle_rx_data_cb)(uint8_t* buf, size_t len, void* context));
void wifi_marauder_uart_tx(uint8_t* data, size_t len);
-WifiMarauderUart* wifi_marauder_uart_init(WifiMarauderApp* app);
+void wifi_marauder_lp_uart_tx(uint8_t* data, size_t len);
+WifiMarauderUart* wifi_marauder_usart_init(WifiMarauderApp* app);
+WifiMarauderUart* wifi_marauder_lp_uart_init(WifiMarauderApp* app);
void wifi_marauder_uart_free(WifiMarauderUart* uart);
diff --git a/applications/plugins/wifi_scanner/FlipperZeroWiFiModuleDefines.h b/applications/external/wifi_scanner/FlipperZeroWiFiModuleDefines.h
similarity index 100%
rename from applications/plugins/wifi_scanner/FlipperZeroWiFiModuleDefines.h
rename to applications/external/wifi_scanner/FlipperZeroWiFiModuleDefines.h
diff --git a/applications/plugins/wifi_scanner/application.fam b/applications/external/wifi_scanner/application.fam
similarity index 87%
rename from applications/plugins/wifi_scanner/application.fam
rename to applications/external/wifi_scanner/application.fam
index a46ecf3fb..a8ce99794 100644
--- a/applications/plugins/wifi_scanner/application.fam
+++ b/applications/external/wifi_scanner/application.fam
@@ -3,7 +3,6 @@ App(
name="[WiFi] Scanner",
apptype=FlipperAppType.EXTERNAL,
entry_point="wifi_scanner_app",
- cdefines=["APP_WIFI_SCANNER"],
requires=["gui"],
stack_size=2 * 1024,
order=70,
diff --git a/applications/plugins/wifi_scanner/wifi_10px.png b/applications/external/wifi_scanner/wifi_10px.png
similarity index 100%
rename from applications/plugins/wifi_scanner/wifi_10px.png
rename to applications/external/wifi_scanner/wifi_10px.png
diff --git a/applications/plugins/wifi_scanner/wifi_scanner.c b/applications/external/wifi_scanner/wifi_scanner.c
similarity index 98%
rename from applications/plugins/wifi_scanner/wifi_scanner.c
rename to applications/external/wifi_scanner/wifi_scanner.c
index 3a79ce16e..341287b54 100644
--- a/applications/plugins/wifi_scanner/wifi_scanner.c
+++ b/applications/external/wifi_scanner/wifi_scanner.c
@@ -663,7 +663,12 @@ int32_t wifi_scanner_app(void* p) {
#else
app->m_context = Initializing;
#if ENABLE_MODULE_POWER
- furi_hal_power_enable_otg();
+ uint8_t attempts = 0;
+ while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) {
+ furi_hal_power_enable_otg();
+ furi_delay_ms(10);
+ }
+ furi_delay_ms(200);
#endif // ENABLE_MODULE_POWER
#endif // ENABLE_MODULE_DETECTION
@@ -722,7 +727,12 @@ int32_t wifi_scanner_app(void* p) {
app->m_wifiModuleAttached = true;
app->m_context = Initializing;
#if ENABLE_MODULE_POWER
- furi_hal_power_enable_otg();
+ uint8_t attempts2 = 0;
+ while(!furi_hal_power_is_otg_enabled() && attempts2++ < 3) {
+ furi_hal_power_enable_otg();
+ furi_delay_ms(10);
+ }
+
#endif
}
}
@@ -851,7 +861,9 @@ int32_t wifi_scanner_app(void* p) {
WIFI_APP_LOG_I("App freed");
#if ENABLE_MODULE_POWER
- furi_hal_power_disable_otg();
+ if(furi_hal_power_is_otg_enabled()) {
+ furi_hal_power_disable_otg();
+ }
#endif
return 0;
diff --git a/applications/plugins/wii_ec_anal/LICENSE b/applications/external/wii_ec_anal/LICENSE
similarity index 100%
rename from applications/plugins/wii_ec_anal/LICENSE
rename to applications/external/wii_ec_anal/LICENSE
diff --git a/applications/plugins/wii_ec_anal/WiiEC.png b/applications/external/wii_ec_anal/WiiEC.png
similarity index 100%
rename from applications/plugins/wii_ec_anal/WiiEC.png
rename to applications/external/wii_ec_anal/WiiEC.png
diff --git a/applications/plugins/wii_ec_anal/_image_tool/LICENSE b/applications/external/wii_ec_anal/_image_tool/LICENSE
similarity index 100%
rename from applications/plugins/wii_ec_anal/_image_tool/LICENSE
rename to applications/external/wii_ec_anal/_image_tool/LICENSE
diff --git a/applications/plugins/wii_ec_anal/_image_tool/README b/applications/external/wii_ec_anal/_image_tool/README
similarity index 100%
rename from applications/plugins/wii_ec_anal/_image_tool/README
rename to applications/external/wii_ec_anal/_image_tool/README
diff --git a/applications/plugins/wii_ec_anal/_image_tool/_convert.c b/applications/external/wii_ec_anal/_image_tool/_convert.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/_image_tool/_convert.c
rename to applications/external/wii_ec_anal/_image_tool/_convert.c
diff --git a/applications/plugins/wii_ec_anal/_image_tool/_convert.sh b/applications/external/wii_ec_anal/_image_tool/_convert.sh
similarity index 100%
rename from applications/plugins/wii_ec_anal/_image_tool/_convert.sh
rename to applications/external/wii_ec_anal/_image_tool/_convert.sh
diff --git a/applications/plugins/wii_ec_anal/_image_tool/_convert_images.c b/applications/external/wii_ec_anal/_image_tool/_convert_images.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/_image_tool/_convert_images.c
rename to applications/external/wii_ec_anal/_image_tool/_convert_images.c
diff --git a/applications/plugins/wii_ec_anal/_image_tool/_convert_images.h b/applications/external/wii_ec_anal/_image_tool/_convert_images.h
similarity index 100%
rename from applications/plugins/wii_ec_anal/_image_tool/_convert_images.h
rename to applications/external/wii_ec_anal/_image_tool/_convert_images.h
diff --git a/applications/plugins/wii_ec_anal/_image_tool/_convert_test.c b/applications/external/wii_ec_anal/_image_tool/_convert_test.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/_image_tool/_convert_test.c
rename to applications/external/wii_ec_anal/_image_tool/_convert_test.c
diff --git a/applications/plugins/wii_ec_anal/application.fam b/applications/external/wii_ec_anal/application.fam
similarity index 100%
rename from applications/plugins/wii_ec_anal/application.fam
rename to applications/external/wii_ec_anal/application.fam
diff --git a/applications/plugins/wii_ec_anal/bc_logging.h b/applications/external/wii_ec_anal/bc_logging.h
similarity index 100%
rename from applications/plugins/wii_ec_anal/bc_logging.h
rename to applications/external/wii_ec_anal/bc_logging.h
diff --git a/applications/plugins/wii_ec_anal/err.h b/applications/external/wii_ec_anal/err.h
similarity index 100%
rename from applications/plugins/wii_ec_anal/err.h
rename to applications/external/wii_ec_anal/err.h
diff --git a/applications/plugins/wii_ec_anal/gfx/images.c b/applications/external/wii_ec_anal/gfx/images.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/images.c
rename to applications/external/wii_ec_anal/gfx/images.c
diff --git a/applications/plugins/wii_ec_anal/gfx/images.h b/applications/external/wii_ec_anal/gfx/images.h
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/images.h
rename to applications/external/wii_ec_anal/gfx/images.h
diff --git a/applications/plugins/wii_ec_anal/gfx/img_3x5_0.c b/applications/external/wii_ec_anal/gfx/img_3x5_0.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_3x5_0.c
rename to applications/external/wii_ec_anal/gfx/img_3x5_0.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_3x5_1.c b/applications/external/wii_ec_anal/gfx/img_3x5_1.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_3x5_1.c
rename to applications/external/wii_ec_anal/gfx/img_3x5_1.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_3x5_2.c b/applications/external/wii_ec_anal/gfx/img_3x5_2.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_3x5_2.c
rename to applications/external/wii_ec_anal/gfx/img_3x5_2.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_3x5_3.c b/applications/external/wii_ec_anal/gfx/img_3x5_3.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_3x5_3.c
rename to applications/external/wii_ec_anal/gfx/img_3x5_3.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_3x5_4.c b/applications/external/wii_ec_anal/gfx/img_3x5_4.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_3x5_4.c
rename to applications/external/wii_ec_anal/gfx/img_3x5_4.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_3x5_5.c b/applications/external/wii_ec_anal/gfx/img_3x5_5.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_3x5_5.c
rename to applications/external/wii_ec_anal/gfx/img_3x5_5.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_3x5_6.c b/applications/external/wii_ec_anal/gfx/img_3x5_6.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_3x5_6.c
rename to applications/external/wii_ec_anal/gfx/img_3x5_6.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_3x5_7.c b/applications/external/wii_ec_anal/gfx/img_3x5_7.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_3x5_7.c
rename to applications/external/wii_ec_anal/gfx/img_3x5_7.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_3x5_8.c b/applications/external/wii_ec_anal/gfx/img_3x5_8.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_3x5_8.c
rename to applications/external/wii_ec_anal/gfx/img_3x5_8.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_3x5_9.c b/applications/external/wii_ec_anal/gfx/img_3x5_9.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_3x5_9.c
rename to applications/external/wii_ec_anal/gfx/img_3x5_9.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_3x5_v.c b/applications/external/wii_ec_anal/gfx/img_3x5_v.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_3x5_v.c
rename to applications/external/wii_ec_anal/gfx/img_3x5_v.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_5x7_0.c b/applications/external/wii_ec_anal/gfx/img_5x7_0.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_5x7_0.c
rename to applications/external/wii_ec_anal/gfx/img_5x7_0.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_5x7_1.c b/applications/external/wii_ec_anal/gfx/img_5x7_1.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_5x7_1.c
rename to applications/external/wii_ec_anal/gfx/img_5x7_1.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_5x7_2.c b/applications/external/wii_ec_anal/gfx/img_5x7_2.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_5x7_2.c
rename to applications/external/wii_ec_anal/gfx/img_5x7_2.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_5x7_3.c b/applications/external/wii_ec_anal/gfx/img_5x7_3.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_5x7_3.c
rename to applications/external/wii_ec_anal/gfx/img_5x7_3.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_5x7_4.c b/applications/external/wii_ec_anal/gfx/img_5x7_4.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_5x7_4.c
rename to applications/external/wii_ec_anal/gfx/img_5x7_4.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_5x7_5.c b/applications/external/wii_ec_anal/gfx/img_5x7_5.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_5x7_5.c
rename to applications/external/wii_ec_anal/gfx/img_5x7_5.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_5x7_6.c b/applications/external/wii_ec_anal/gfx/img_5x7_6.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_5x7_6.c
rename to applications/external/wii_ec_anal/gfx/img_5x7_6.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_5x7_7.c b/applications/external/wii_ec_anal/gfx/img_5x7_7.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_5x7_7.c
rename to applications/external/wii_ec_anal/gfx/img_5x7_7.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_5x7_8.c b/applications/external/wii_ec_anal/gfx/img_5x7_8.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_5x7_8.c
rename to applications/external/wii_ec_anal/gfx/img_5x7_8.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_5x7_9.c b/applications/external/wii_ec_anal/gfx/img_5x7_9.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_5x7_9.c
rename to applications/external/wii_ec_anal/gfx/img_5x7_9.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_5x7_A.c b/applications/external/wii_ec_anal/gfx/img_5x7_A.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_5x7_A.c
rename to applications/external/wii_ec_anal/gfx/img_5x7_A.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_5x7_B.c b/applications/external/wii_ec_anal/gfx/img_5x7_B.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_5x7_B.c
rename to applications/external/wii_ec_anal/gfx/img_5x7_B.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_5x7_C.c b/applications/external/wii_ec_anal/gfx/img_5x7_C.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_5x7_C.c
rename to applications/external/wii_ec_anal/gfx/img_5x7_C.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_5x7_D.c b/applications/external/wii_ec_anal/gfx/img_5x7_D.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_5x7_D.c
rename to applications/external/wii_ec_anal/gfx/img_5x7_D.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_5x7_E.c b/applications/external/wii_ec_anal/gfx/img_5x7_E.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_5x7_E.c
rename to applications/external/wii_ec_anal/gfx/img_5x7_E.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_5x7_F.c b/applications/external/wii_ec_anal/gfx/img_5x7_F.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_5x7_F.c
rename to applications/external/wii_ec_anal/gfx/img_5x7_F.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_6x8_0.c b/applications/external/wii_ec_anal/gfx/img_6x8_0.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_6x8_0.c
rename to applications/external/wii_ec_anal/gfx/img_6x8_0.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_6x8_1.c b/applications/external/wii_ec_anal/gfx/img_6x8_1.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_6x8_1.c
rename to applications/external/wii_ec_anal/gfx/img_6x8_1.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_6x8_2.c b/applications/external/wii_ec_anal/gfx/img_6x8_2.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_6x8_2.c
rename to applications/external/wii_ec_anal/gfx/img_6x8_2.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_6x8_3.c b/applications/external/wii_ec_anal/gfx/img_6x8_3.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_6x8_3.c
rename to applications/external/wii_ec_anal/gfx/img_6x8_3.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_6x8_4.c b/applications/external/wii_ec_anal/gfx/img_6x8_4.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_6x8_4.c
rename to applications/external/wii_ec_anal/gfx/img_6x8_4.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_6x8_5.c b/applications/external/wii_ec_anal/gfx/img_6x8_5.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_6x8_5.c
rename to applications/external/wii_ec_anal/gfx/img_6x8_5.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_6x8_6.c b/applications/external/wii_ec_anal/gfx/img_6x8_6.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_6x8_6.c
rename to applications/external/wii_ec_anal/gfx/img_6x8_6.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_6x8_7.c b/applications/external/wii_ec_anal/gfx/img_6x8_7.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_6x8_7.c
rename to applications/external/wii_ec_anal/gfx/img_6x8_7.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_6x8_8.c b/applications/external/wii_ec_anal/gfx/img_6x8_8.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_6x8_8.c
rename to applications/external/wii_ec_anal/gfx/img_6x8_8.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_6x8_9.c b/applications/external/wii_ec_anal/gfx/img_6x8_9.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_6x8_9.c
rename to applications/external/wii_ec_anal/gfx/img_6x8_9.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_6x8_A.c b/applications/external/wii_ec_anal/gfx/img_6x8_A.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_6x8_A.c
rename to applications/external/wii_ec_anal/gfx/img_6x8_A.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_6x8_B.c b/applications/external/wii_ec_anal/gfx/img_6x8_B.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_6x8_B.c
rename to applications/external/wii_ec_anal/gfx/img_6x8_B.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_6x8_C.c b/applications/external/wii_ec_anal/gfx/img_6x8_C.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_6x8_C.c
rename to applications/external/wii_ec_anal/gfx/img_6x8_C.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_6x8_D.c b/applications/external/wii_ec_anal/gfx/img_6x8_D.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_6x8_D.c
rename to applications/external/wii_ec_anal/gfx/img_6x8_D.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_6x8_E.c b/applications/external/wii_ec_anal/gfx/img_6x8_E.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_6x8_E.c
rename to applications/external/wii_ec_anal/gfx/img_6x8_E.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_6x8_F.c b/applications/external/wii_ec_anal/gfx/img_6x8_F.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_6x8_F.c
rename to applications/external/wii_ec_anal/gfx/img_6x8_F.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_6x8_G.c b/applications/external/wii_ec_anal/gfx/img_6x8_G.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_6x8_G.c
rename to applications/external/wii_ec_anal/gfx/img_6x8_G.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_6x8_X.c b/applications/external/wii_ec_anal/gfx/img_6x8_X.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_6x8_X.c
rename to applications/external/wii_ec_anal/gfx/img_6x8_X.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_6x8_Y.c b/applications/external/wii_ec_anal/gfx/img_6x8_Y.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_6x8_Y.c
rename to applications/external/wii_ec_anal/gfx/img_6x8_Y.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_6x8_Z.c b/applications/external/wii_ec_anal/gfx/img_6x8_Z.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_6x8_Z.c
rename to applications/external/wii_ec_anal/gfx/img_6x8_Z.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_6x8_d_.c b/applications/external/wii_ec_anal/gfx/img_6x8_d_.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_6x8_d_.c
rename to applications/external/wii_ec_anal/gfx/img_6x8_d_.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_6x8_n_.c b/applications/external/wii_ec_anal/gfx/img_6x8_n_.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_6x8_n_.c
rename to applications/external/wii_ec_anal/gfx/img_6x8_n_.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_6x8_v_.c b/applications/external/wii_ec_anal/gfx/img_6x8_v_.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_6x8_v_.c
rename to applications/external/wii_ec_anal/gfx/img_6x8_v_.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_RIP.c b/applications/external/wii_ec_anal/gfx/img_RIP.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_RIP.c
rename to applications/external/wii_ec_anal/gfx/img_RIP.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_cc_Cable.c b/applications/external/wii_ec_anal/gfx/img_cc_Cable.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_cc_Cable.c
rename to applications/external/wii_ec_anal/gfx/img_cc_Cable.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_cc_Joy.c b/applications/external/wii_ec_anal/gfx/img_cc_Joy.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_cc_Joy.c
rename to applications/external/wii_ec_anal/gfx/img_cc_Joy.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_cc_Main.c b/applications/external/wii_ec_anal/gfx/img_cc_Main.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_cc_Main.c
rename to applications/external/wii_ec_anal/gfx/img_cc_Main.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_cc_btn_A1.c b/applications/external/wii_ec_anal/gfx/img_cc_btn_A1.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_cc_btn_A1.c
rename to applications/external/wii_ec_anal/gfx/img_cc_btn_A1.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_cc_btn_B1.c b/applications/external/wii_ec_anal/gfx/img_cc_btn_B1.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_cc_btn_B1.c
rename to applications/external/wii_ec_anal/gfx/img_cc_btn_B1.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_cc_btn_X1.c b/applications/external/wii_ec_anal/gfx/img_cc_btn_X1.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_cc_btn_X1.c
rename to applications/external/wii_ec_anal/gfx/img_cc_btn_X1.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_cc_btn_Y1.c b/applications/external/wii_ec_anal/gfx/img_cc_btn_Y1.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_cc_btn_Y1.c
rename to applications/external/wii_ec_anal/gfx/img_cc_btn_Y1.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_cc_pad_LR1.c b/applications/external/wii_ec_anal/gfx/img_cc_pad_LR1.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_cc_pad_LR1.c
rename to applications/external/wii_ec_anal/gfx/img_cc_pad_LR1.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_cc_pad_UD1.c b/applications/external/wii_ec_anal/gfx/img_cc_pad_UD1.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_cc_pad_UD1.c
rename to applications/external/wii_ec_anal/gfx/img_cc_pad_UD1.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_cc_trg_L1.c b/applications/external/wii_ec_anal/gfx/img_cc_trg_L1.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_cc_trg_L1.c
rename to applications/external/wii_ec_anal/gfx/img_cc_trg_L1.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_cc_trg_L2.c b/applications/external/wii_ec_anal/gfx/img_cc_trg_L2.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_cc_trg_L2.c
rename to applications/external/wii_ec_anal/gfx/img_cc_trg_L2.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_cc_trg_L3.c b/applications/external/wii_ec_anal/gfx/img_cc_trg_L3.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_cc_trg_L3.c
rename to applications/external/wii_ec_anal/gfx/img_cc_trg_L3.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_cc_trg_L4.c b/applications/external/wii_ec_anal/gfx/img_cc_trg_L4.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_cc_trg_L4.c
rename to applications/external/wii_ec_anal/gfx/img_cc_trg_L4.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_cc_trg_R1.c b/applications/external/wii_ec_anal/gfx/img_cc_trg_R1.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_cc_trg_R1.c
rename to applications/external/wii_ec_anal/gfx/img_cc_trg_R1.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_cc_trg_R2.c b/applications/external/wii_ec_anal/gfx/img_cc_trg_R2.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_cc_trg_R2.c
rename to applications/external/wii_ec_anal/gfx/img_cc_trg_R2.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_cc_trg_R3.c b/applications/external/wii_ec_anal/gfx/img_cc_trg_R3.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_cc_trg_R3.c
rename to applications/external/wii_ec_anal/gfx/img_cc_trg_R3.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_cc_trg_R4.c b/applications/external/wii_ec_anal/gfx/img_cc_trg_R4.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_cc_trg_R4.c
rename to applications/external/wii_ec_anal/gfx/img_cc_trg_R4.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_csLogo_FULL.c b/applications/external/wii_ec_anal/gfx/img_csLogo_FULL.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_csLogo_FULL.c
rename to applications/external/wii_ec_anal/gfx/img_csLogo_FULL.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_csLogo_Small.c b/applications/external/wii_ec_anal/gfx/img_csLogo_Small.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_csLogo_Small.c
rename to applications/external/wii_ec_anal/gfx/img_csLogo_Small.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_ecp_SCL.c b/applications/external/wii_ec_anal/gfx/img_ecp_SCL.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_ecp_SCL.c
rename to applications/external/wii_ec_anal/gfx/img_ecp_SCL.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_ecp_SDA.c b/applications/external/wii_ec_anal/gfx/img_ecp_SDA.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_ecp_SDA.c
rename to applications/external/wii_ec_anal/gfx/img_ecp_SDA.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_ecp_port.c b/applications/external/wii_ec_anal/gfx/img_ecp_port.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_ecp_port.c
rename to applications/external/wii_ec_anal/gfx/img_ecp_port.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_key_Back.c b/applications/external/wii_ec_anal/gfx/img_key_Back.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_key_Back.c
rename to applications/external/wii_ec_anal/gfx/img_key_Back.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_key_D.c b/applications/external/wii_ec_anal/gfx/img_key_D.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_key_D.c
rename to applications/external/wii_ec_anal/gfx/img_key_D.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_key_L.c b/applications/external/wii_ec_anal/gfx/img_key_L.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_key_L.c
rename to applications/external/wii_ec_anal/gfx/img_key_L.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_key_OK.c b/applications/external/wii_ec_anal/gfx/img_key_OK.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_key_OK.c
rename to applications/external/wii_ec_anal/gfx/img_key_OK.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_key_OKi.c b/applications/external/wii_ec_anal/gfx/img_key_OKi.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_key_OKi.c
rename to applications/external/wii_ec_anal/gfx/img_key_OKi.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_key_R.c b/applications/external/wii_ec_anal/gfx/img_key_R.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_key_R.c
rename to applications/external/wii_ec_anal/gfx/img_key_R.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_key_U.c b/applications/external/wii_ec_anal/gfx/img_key_U.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_key_U.c
rename to applications/external/wii_ec_anal/gfx/img_key_U.c
diff --git a/applications/plugins/wii_ec_anal/gfx/img_key_Ui.c b/applications/external/wii_ec_anal/gfx/img_key_Ui.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/gfx/img_key_Ui.c
rename to applications/external/wii_ec_anal/gfx/img_key_Ui.c
diff --git a/applications/plugins/wii_ec_anal/i2c_workaround.h b/applications/external/wii_ec_anal/i2c_workaround.h
similarity index 100%
rename from applications/plugins/wii_ec_anal/i2c_workaround.h
rename to applications/external/wii_ec_anal/i2c_workaround.h
diff --git a/applications/plugins/wii_ec_anal/info.sh b/applications/external/wii_ec_anal/info.sh
similarity index 100%
rename from applications/plugins/wii_ec_anal/info.sh
rename to applications/external/wii_ec_anal/info.sh
diff --git a/applications/plugins/wii_ec_anal/wii_anal.c b/applications/external/wii_ec_anal/wii_anal.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/wii_anal.c
rename to applications/external/wii_ec_anal/wii_anal.c
diff --git a/applications/plugins/wii_ec_anal/wii_anal.h b/applications/external/wii_ec_anal/wii_anal.h
similarity index 100%
rename from applications/plugins/wii_ec_anal/wii_anal.h
rename to applications/external/wii_ec_anal/wii_anal.h
diff --git a/applications/plugins/wii_ec_anal/wii_anal_ec.c b/applications/external/wii_ec_anal/wii_anal_ec.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/wii_anal_ec.c
rename to applications/external/wii_ec_anal/wii_anal_ec.c
diff --git a/applications/plugins/wii_ec_anal/wii_anal_ec.h b/applications/external/wii_ec_anal/wii_anal_ec.h
similarity index 100%
rename from applications/plugins/wii_ec_anal/wii_anal_ec.h
rename to applications/external/wii_ec_anal/wii_anal_ec.h
diff --git a/applications/plugins/wii_ec_anal/wii_anal_keys.c b/applications/external/wii_ec_anal/wii_anal_keys.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/wii_anal_keys.c
rename to applications/external/wii_ec_anal/wii_anal_keys.c
diff --git a/applications/plugins/wii_ec_anal/wii_anal_keys.h b/applications/external/wii_ec_anal/wii_anal_keys.h
similarity index 100%
rename from applications/plugins/wii_ec_anal/wii_anal_keys.h
rename to applications/external/wii_ec_anal/wii_anal_keys.h
diff --git a/applications/plugins/wii_ec_anal/wii_anal_lcd.c b/applications/external/wii_ec_anal/wii_anal_lcd.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/wii_anal_lcd.c
rename to applications/external/wii_ec_anal/wii_anal_lcd.c
diff --git a/applications/plugins/wii_ec_anal/wii_anal_lcd.h b/applications/external/wii_ec_anal/wii_anal_lcd.h
similarity index 100%
rename from applications/plugins/wii_ec_anal/wii_anal_lcd.h
rename to applications/external/wii_ec_anal/wii_anal_lcd.h
diff --git a/applications/plugins/wii_ec_anal/wii_anal_ver.h b/applications/external/wii_ec_anal/wii_anal_ver.h
similarity index 100%
rename from applications/plugins/wii_ec_anal/wii_anal_ver.h
rename to applications/external/wii_ec_anal/wii_anal_ver.h
diff --git a/applications/plugins/wii_ec_anal/wii_ec.c b/applications/external/wii_ec_anal/wii_ec.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/wii_ec.c
rename to applications/external/wii_ec_anal/wii_ec.c
diff --git a/applications/plugins/wii_ec_anal/wii_ec.h b/applications/external/wii_ec_anal/wii_ec.h
similarity index 100%
rename from applications/plugins/wii_ec_anal/wii_ec.h
rename to applications/external/wii_ec_anal/wii_ec.h
diff --git a/applications/plugins/wii_ec_anal/wii_ec_classic.c b/applications/external/wii_ec_anal/wii_ec_classic.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/wii_ec_classic.c
rename to applications/external/wii_ec_anal/wii_ec_classic.c
diff --git a/applications/plugins/wii_ec_anal/wii_ec_classic.h b/applications/external/wii_ec_anal/wii_ec_classic.h
similarity index 100%
rename from applications/plugins/wii_ec_anal/wii_ec_classic.h
rename to applications/external/wii_ec_anal/wii_ec_classic.h
diff --git a/applications/plugins/wii_ec_anal/wii_ec_macros.h b/applications/external/wii_ec_anal/wii_ec_macros.h
similarity index 100%
rename from applications/plugins/wii_ec_anal/wii_ec_macros.h
rename to applications/external/wii_ec_anal/wii_ec_macros.h
diff --git a/applications/plugins/wii_ec_anal/wii_ec_nunchuck.c b/applications/external/wii_ec_anal/wii_ec_nunchuck.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/wii_ec_nunchuck.c
rename to applications/external/wii_ec_anal/wii_ec_nunchuck.c
diff --git a/applications/plugins/wii_ec_anal/wii_ec_nunchuck.h b/applications/external/wii_ec_anal/wii_ec_nunchuck.h
similarity index 100%
rename from applications/plugins/wii_ec_anal/wii_ec_nunchuck.h
rename to applications/external/wii_ec_anal/wii_ec_nunchuck.h
diff --git a/applications/plugins/wii_ec_anal/wii_ec_udraw.c b/applications/external/wii_ec_anal/wii_ec_udraw.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/wii_ec_udraw.c
rename to applications/external/wii_ec_anal/wii_ec_udraw.c
diff --git a/applications/plugins/wii_ec_anal/wii_ec_udraw.h b/applications/external/wii_ec_anal/wii_ec_udraw.h
similarity index 100%
rename from applications/plugins/wii_ec_anal/wii_ec_udraw.h
rename to applications/external/wii_ec_anal/wii_ec_udraw.h
diff --git a/applications/plugins/wii_ec_anal/wii_i2c.c b/applications/external/wii_ec_anal/wii_i2c.c
similarity index 100%
rename from applications/plugins/wii_ec_anal/wii_i2c.c
rename to applications/external/wii_ec_anal/wii_i2c.c
diff --git a/applications/plugins/wii_ec_anal/wii_i2c.h b/applications/external/wii_ec_anal/wii_i2c.h
similarity index 100%
rename from applications/plugins/wii_ec_anal/wii_i2c.h
rename to applications/external/wii_ec_anal/wii_i2c.h
diff --git a/applications/external/yatzee/LICENSE b/applications/external/yatzee/LICENSE
new file mode 100644
index 000000000..f288702d2
--- /dev/null
+++ b/applications/external/yatzee/LICENSE
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ Copyright (C)
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+.
diff --git a/applications/plugins/yatzee/application.fam b/applications/external/yatzee/application.fam
similarity index 100%
rename from applications/plugins/yatzee/application.fam
rename to applications/external/yatzee/application.fam
diff --git a/applications/plugins/yatzee/images/die_1.png b/applications/external/yatzee/images/die_1.png
similarity index 100%
rename from applications/plugins/yatzee/images/die_1.png
rename to applications/external/yatzee/images/die_1.png
diff --git a/applications/plugins/yatzee/images/die_2.png b/applications/external/yatzee/images/die_2.png
similarity index 100%
rename from applications/plugins/yatzee/images/die_2.png
rename to applications/external/yatzee/images/die_2.png
diff --git a/applications/plugins/yatzee/images/die_3.png b/applications/external/yatzee/images/die_3.png
similarity index 100%
rename from applications/plugins/yatzee/images/die_3.png
rename to applications/external/yatzee/images/die_3.png
diff --git a/applications/plugins/yatzee/images/die_4.png b/applications/external/yatzee/images/die_4.png
similarity index 100%
rename from applications/plugins/yatzee/images/die_4.png
rename to applications/external/yatzee/images/die_4.png
diff --git a/applications/plugins/yatzee/images/die_5.png b/applications/external/yatzee/images/die_5.png
similarity index 100%
rename from applications/plugins/yatzee/images/die_5.png
rename to applications/external/yatzee/images/die_5.png
diff --git a/applications/plugins/yatzee/images/die_6.png b/applications/external/yatzee/images/die_6.png
similarity index 100%
rename from applications/plugins/yatzee/images/die_6.png
rename to applications/external/yatzee/images/die_6.png
diff --git a/applications/plugins/yatzee/images/yatzee_icon_10px.png b/applications/external/yatzee/images/yatzee_icon_10px.png
similarity index 100%
rename from applications/plugins/yatzee/images/yatzee_icon_10px.png
rename to applications/external/yatzee/images/yatzee_icon_10px.png
diff --git a/applications/plugins/yatzee/yatzee.c b/applications/external/yatzee/yatzee.c
similarity index 100%
rename from applications/plugins/yatzee/yatzee.c
rename to applications/external/yatzee/yatzee.c
diff --git a/applications/plugins/zombiez/application.fam b/applications/external/zombiez/application.fam
similarity index 87%
rename from applications/plugins/zombiez/application.fam
rename to applications/external/zombiez/application.fam
index 3245187d2..069e591bc 100644
--- a/applications/plugins/zombiez/application.fam
+++ b/applications/external/zombiez/application.fam
@@ -3,7 +3,6 @@ App(
name="Zombiez",
apptype=FlipperAppType.EXTERNAL,
entry_point="zombiez_game_app",
- cdefines=["APP_ZOMBIEZ_GAME"],
requires=["gui"],
stack_size=2 * 1024,
order=280,
diff --git a/applications/plugins/zombiez/zombie_10px.png b/applications/external/zombiez/zombie_10px.png
similarity index 100%
rename from applications/plugins/zombiez/zombie_10px.png
rename to applications/external/zombiez/zombie_10px.png
diff --git a/applications/plugins/zombiez/zombiez.c b/applications/external/zombiez/zombiez.c
similarity index 100%
rename from applications/plugins/zombiez/zombiez.c
rename to applications/external/zombiez/zombiez.c
diff --git a/applications/plugins/zombiez/zombiez.h b/applications/external/zombiez/zombiez.h
similarity index 100%
rename from applications/plugins/zombiez/zombiez.h
rename to applications/external/zombiez/zombiez.h
diff --git a/applications/main/application.fam b/applications/main/application.fam
index 459a878ed..006670947 100644
--- a/applications/main/application.fam
+++ b/applications/main/application.fam
@@ -5,14 +5,15 @@ App(
provides=[
"fap_loader",
"subghz",
- "subghz_remote",
"lfrfid",
"nfc",
"infrared",
"gpio",
+ "onewire",
"ibutton",
"bad_kb",
"u2f",
+ "xtreme_app",
"archive",
],
)
diff --git a/applications/main/archive/application.fam b/applications/main/archive/application.fam
index 309cee8d5..c7e701653 100644
--- a/applications/main/archive/application.fam
+++ b/applications/main/archive/application.fam
@@ -6,6 +6,5 @@ App(
cdefines=["APP_ARCHIVE"],
requires=["gui"],
stack_size=6 * 1024,
- icon="A_FileManager_14",
order=0,
)
diff --git a/applications/main/archive/helpers/archive_browser.c b/applications/main/archive/helpers/archive_browser.c
index f93d9694d..8555dca42 100644
--- a/applications/main/archive/helpers/archive_browser.c
+++ b/applications/main/archive/helpers/archive_browser.c
@@ -498,8 +498,10 @@ void archive_switch_tab(ArchiveBrowserView* browser, InputKey key) {
tab = archive_get_tab(browser);
if(archive_is_dir_exists(browser->path)) {
bool skip_assets = (strcmp(archive_get_tab_ext(tab), "*") == 0) ? false : true;
- // Hide dot files everywhere except Browser
- bool hide_dot_files = (strcmp(archive_get_tab_ext(tab), "*") == 0) ? false : true;
+ // Hide dot files everywhere except Browser if in debug mode
+ bool hide_dot_files = (strcmp(archive_get_tab_ext(tab), "*") == 0) ?
+ !furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug) :
+ true;
archive_file_browser_set_path(
browser, browser->path, archive_get_tab_ext(tab), skip_assets, hide_dot_files);
tab_empty = false; // Empty check will be performed later
diff --git a/applications/main/archive/helpers/archive_favorites.c b/applications/main/archive/helpers/archive_favorites.c
index f395ee5a1..7304be6b4 100644
--- a/applications/main/archive/helpers/archive_favorites.c
+++ b/applications/main/archive/helpers/archive_favorites.c
@@ -140,6 +140,13 @@ bool archive_favorites_read(void* context) {
bool result = storage_file_open(file, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING);
+ if(!result) {
+ storage_file_close(file);
+ storage_common_copy(storage, ARCHIVE_FAV_OLD_PATH, ARCHIVE_FAV_PATH);
+ storage_common_remove(storage, ARCHIVE_FAV_OLD_PATH);
+ result = storage_file_open(file, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING);
+ }
+
if(result) {
while(1) {
if(!archive_favorites_read_line(file, buffer)) {
diff --git a/applications/main/archive/helpers/archive_favorites.h b/applications/main/archive/helpers/archive_favorites.h
index db8943378..e45af92e7 100644
--- a/applications/main/archive/helpers/archive_favorites.h
+++ b/applications/main/archive/helpers/archive_favorites.h
@@ -2,8 +2,9 @@
#include
-#define ARCHIVE_FAV_PATH ANY_PATH("favorites.txt")
-#define ARCHIVE_FAV_TEMP_PATH ANY_PATH("favorites.tmp")
+#define ARCHIVE_FAV_OLD_PATH EXT_PATH("favorites.txt")
+#define ARCHIVE_FAV_PATH CFG_PATH("favorites.txt")
+#define ARCHIVE_FAV_TEMP_PATH CFG_PATH("favorites.tmp")
uint16_t archive_favorites_count(void* context);
bool archive_favorites_read(void* context);
diff --git a/applications/main/archive/helpers/archive_files.h b/applications/main/archive/helpers/archive_files.h
index 431c701b3..e4bed3a63 100644
--- a/applications/main/archive/helpers/archive_files.h
+++ b/applications/main/archive/helpers/archive_files.h
@@ -93,7 +93,9 @@ static int ArchiveFile_t_cmp(const ArchiveFile_t* a, const ArchiveFile_t* b) {
}
}
- return furi_string_cmpi(a->path, b->path);
+ return furi_string_cmpi(
+ furi_string_empty(a->custom_name) ? a->path : a->custom_name,
+ furi_string_empty(b->custom_name) ? b->path : b->custom_name);
}
#define M_OPL_ArchiveFile_t() \
diff --git a/applications/main/archive/scenes/archive_scene_browser.c b/applications/main/archive/scenes/archive_scene_browser.c
index 0696647ea..8d606cf09 100644
--- a/applications/main/archive/scenes/archive_scene_browser.c
+++ b/applications/main/archive/scenes/archive_scene_browser.c
@@ -14,12 +14,12 @@
static const char* flipper_app_name[] = {
[ArchiveFileTypeIButton] = "iButton",
[ArchiveFileTypeNFC] = "NFC",
- [ArchiveFileTypeSubGhz] = "Sub-GHz",
- [ArchiveFileTypeLFRFID] = "125 kHz RFID",
+ [ArchiveFileTypeSubGhz] = "SubGHz",
+ [ArchiveFileTypeLFRFID] = "RFID",
[ArchiveFileTypeInfrared] = "Infrared",
[ArchiveFileTypeBadKb] = "Bad KB",
[ArchiveFileTypeU2f] = "U2F",
- [ArchiveFileTypeApplication] = "Applications",
+ [ArchiveFileTypeApplication] = "Apps",
[ArchiveFileTypeUpdateManifest] = "UpdaterApp",
};
diff --git a/applications/main/archive/views/archive_browser_view.c b/applications/main/archive/views/archive_browser_view.c
index 59c20ce6b..994290a3c 100644
--- a/applications/main/archive/views/archive_browser_view.c
+++ b/applications/main/archive/views/archive_browser_view.c
@@ -300,7 +300,8 @@ static void draw_list(Canvas* canvas, ArchiveBrowserViewModel* model) {
((scrollbar ? MAX_LEN_PX - 6 : MAX_LEN_PX) - x_offset),
str_buf,
scroll_counter,
- (model->item_idx != idx));
+ (model->item_idx != idx),
+ false);
furi_string_free(str_buf);
}
diff --git a/applications/main/bad_kb/bad_kb_app.c b/applications/main/bad_kb/bad_kb_app.c
index 5c095c2f0..251181554 100644
--- a/applications/main/bad_kb/bad_kb_app.c
+++ b/applications/main/bad_kb/bad_kb_app.c
@@ -1,4 +1,4 @@
-#include "bad_kb_app_i.h"
+#include "bad_kb_app.h"
#include "bad_kb_settings_filename.h"
#include
#include
@@ -72,6 +72,94 @@ static void bad_kb_save_settings(BadKbApp* app) {
storage_file_free(settings_file);
}
+void bad_kb_reload_worker(BadKbApp* app) {
+ bad_kb_script_close(app->bad_kb_script);
+ app->bad_kb_script = bad_kb_script_open(app->file_path, app->is_bt ? app->bt : NULL);
+ bad_kb_script_set_keyboard_layout(app->bad_kb_script, app->keyboard_layout);
+}
+
+void bad_kb_config_switch_mode(BadKbApp* app) {
+ scene_manager_previous_scene(app->scene_manager);
+ if(app->is_bt) {
+ furi_hal_bt_start_advertising();
+ } else {
+ furi_hal_bt_stop_advertising();
+ }
+ scene_manager_next_scene(app->scene_manager, BadKbSceneConfig);
+ bad_kb_reload_worker(app);
+}
+
+void bad_kb_config_switch_remember_mode(BadKbApp* app) {
+ if(app->bt_remember) {
+ // set bouding mac
+ uint8_t mac[6] = BAD_KB_BOUND_MAC_ADDRESS;
+ furi_hal_bt_set_profile_pairing_method(
+ FuriHalBtProfileHidKeyboard, GapPairingPinCodeVerifyYesNo);
+ bt_set_profile_mac_address(app->bt, mac); // this also restart bt
+ // enable keys storage
+ bt_enable_peer_key_update(app->bt);
+ } else {
+ // set back user defined mac address
+ furi_hal_bt_set_profile_pairing_method(FuriHalBtProfileHidKeyboard, GapPairingNone);
+ bt_set_profile_mac_address(app->bt, app->mac);
+ // disable key storage
+ bt_disable_peer_key_update(app->bt);
+ }
+ bad_kb_reload_worker(app);
+}
+
+int32_t bad_kb_connection_init(BadKbApp* app) {
+ app->usb_prev_mode = furi_hal_usb_get_config();
+ furi_hal_usb_set_config(NULL, NULL);
+
+ bt_timeout = bt_hid_delays[LevelRssi39_0];
+ bt_disconnect(app->bt);
+ // furi_delay_ms(200);
+ bt_keys_storage_set_storage_path(app->bt, BAD_KB_APP_PATH_BOUND_KEYS_FILE);
+ app->bt_prev_mode = furi_hal_bt_get_profile_pairing_method(FuriHalBtProfileHidKeyboard);
+ if(app->bt_remember) {
+ uint8_t mac[6] = BAD_KB_BOUND_MAC_ADDRESS;
+ furi_hal_bt_set_profile_mac_addr(FuriHalBtProfileHidKeyboard, mac);
+ // using GapPairingNone breaks bounding between devices
+ furi_hal_bt_set_profile_pairing_method(
+ FuriHalBtProfileHidKeyboard, GapPairingPinCodeVerifyYesNo);
+ } else {
+ furi_hal_bt_set_profile_pairing_method(FuriHalBtProfileHidKeyboard, GapPairingNone);
+ }
+
+ bt_set_profile(app->bt, BtProfileHidKeyboard);
+ if(app->is_bt) {
+ furi_hal_bt_start_advertising();
+ if(app->bt_remember) {
+ bt_enable_peer_key_update(app->bt);
+ } else {
+ bt_disable_peer_key_update(app->bt); // disable peer key adding to bt SRAM storage
+ }
+ } else {
+ furi_hal_bt_stop_advertising();
+ }
+
+ return 0;
+}
+
+void bad_kb_connection_deinit(BadKbApp* app) {
+ furi_hal_usb_set_config(app->usb_prev_mode, NULL);
+
+ // bt_hid_hold_while_keyboard_buffer_full(6, 3000); // release all keys
+ bt_disconnect(app->bt); // stop ble
+ // furi_delay_ms(200); // Wait 2nd core to update nvm storage
+ bt_keys_storage_set_default_path(app->bt);
+ if(app->bt_remember) {
+ // hal primitives doesn't restarts ble, that's what we want cuz we are shutting down
+ furi_hal_bt_set_profile_mac_addr(FuriHalBtProfileHidKeyboard, app->mac);
+ }
+ bt_enable_peer_key_update(app->bt); // starts saving peer keys (bounded devices)
+ // fails if ble radio stack isn't ready when switching profile
+ // if it happens, maybe we should increase the delay after bt_disconnect
+ bt_set_profile(app->bt, BtProfileSerial);
+ furi_hal_bt_set_profile_pairing_method(FuriHalBtProfileHidKeyboard, app->bt_prev_mode);
+}
+
BadKbApp* bad_kb_app_alloc(char* arg) {
BadKbApp* app = malloc(sizeof(BadKbApp));
@@ -125,16 +213,9 @@ BadKbApp* bad_kb_app_alloc(char* arg) {
view_dispatcher_add_view(
app->view_dispatcher, BadKbAppViewError, widget_get_view(app->widget));
- app->var_item_list_bt = variable_item_list_alloc();
+ app->var_item_list = variable_item_list_alloc();
view_dispatcher_add_view(
- app->view_dispatcher,
- BadKbAppViewConfigBt,
- variable_item_list_get_view(app->var_item_list_bt));
- app->var_item_list_usb = variable_item_list_alloc();
- view_dispatcher_add_view(
- app->view_dispatcher,
- BadKbAppViewConfigUsb,
- variable_item_list_get_view(app->var_item_list_usb));
+ app->view_dispatcher, BadKbAppViewConfig, variable_item_list_get_view(app->var_item_list));
app->bad_kb_view = bad_kb_alloc();
view_dispatcher_add_view(
@@ -188,10 +269,8 @@ void bad_kb_app_free(BadKbApp* app) {
widget_free(app->widget);
// Variable item list
- view_dispatcher_remove_view(app->view_dispatcher, BadKbAppViewConfigBt);
- variable_item_list_free(app->var_item_list_bt);
- view_dispatcher_remove_view(app->view_dispatcher, BadKbAppViewConfigUsb);
- variable_item_list_free(app->var_item_list_usb);
+ view_dispatcher_remove_view(app->view_dispatcher, BadKbAppViewConfig);
+ variable_item_list_free(app->var_item_list);
// Text Input
view_dispatcher_remove_view(app->view_dispatcher, BadKbAppViewConfigName);
diff --git a/applications/main/bad_kb/bad_kb_app.h b/applications/main/bad_kb/bad_kb_app.h
index af64be253..6aa133963 100644
--- a/applications/main/bad_kb/bad_kb_app.h
+++ b/applications/main/bad_kb/bad_kb_app.h
@@ -1,11 +1,92 @@
#pragma once
-#ifdef __cplusplus
-extern "C" {
-#endif
+#include "bad_kb_app.h"
+#include "scenes/bad_kb_scene.h"
+#include "helpers/ducky_script.h"
-typedef struct BadKbApp BadKbApp;
+#include
+#include