diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 69f8289f..0bc13024 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -22,8 +22,8 @@ /applications/main/subghz/ @skotopes @DrZlo13 @hedger @Skorpionm /applications/main/u2f/ @skotopes @DrZlo13 @hedger @nminaylov -/applications/plugins/bt_hid_app/ @skotopes @DrZlo13 @hedger @gornekich -/applications/plugins/picopass/ @skotopes @DrZlo13 @hedger @gornekich +/applications/external/bt_hid_app/ @skotopes @DrZlo13 @hedger @gornekich +/applications/external/picopass/ @skotopes @DrZlo13 @hedger @gornekich /applications/services/bt/ @skotopes @DrZlo13 @hedger @gornekich /applications/services/cli/ @skotopes @DrZlo13 @hedger @nminaylov diff --git a/.gitmodules b/.gitmodules index a97e0933..56368cd5 100644 --- a/.gitmodules +++ b/.gitmodules @@ -28,6 +28,6 @@ [submodule "lib/cxxheaderparser"] path = lib/cxxheaderparser url = https://github.com/robotpy/cxxheaderparser.git -[submodule "applications/plugins/dap_link/lib/free-dap"] - path = applications/plugins/dap_link/lib/free-dap +[submodule "applications/external/dap_link/lib/free-dap"] + path = applications/external/dap_link/lib/free-dap url = https://github.com/ataradov/free-dap.git diff --git a/.pvsoptions b/.pvsoptions index ca1b2b57..6b22aed7 100644 --- a/.pvsoptions +++ b/.pvsoptions @@ -1 +1 @@ ---ignore-ccache -C gccarm --rules-config .pvsconfig -e lib/fatfs -e lib/fnv1a-hash -e lib/FreeRTOS-Kernel -e lib/heatshrink -e lib/libusb_stm32 -e lib/littlefs -e lib/mbedtls -e lib/micro-ecc -e lib/microtar -e lib/mlib -e lib/qrcode -e lib/ST25RFAL002 -e lib/STM32CubeWB -e lib/u8g2 -e lib/nanopb -e */arm-none-eabi/* -e applications/plugins/dap_link/lib/free-dap +--ignore-ccache -C gccarm --rules-config .pvsconfig -e lib/fatfs -e lib/fnv1a-hash -e lib/FreeRTOS-Kernel -e lib/heatshrink -e lib/libusb_stm32 -e lib/littlefs -e lib/mbedtls -e lib/micro-ecc -e lib/microtar -e lib/mlib -e lib/qrcode -e lib/ST25RFAL002 -e lib/STM32CubeWB -e lib/u8g2 -e lib/nanopb -e */arm-none-eabi/* -e applications/external/dap_link/lib/free-dap diff --git a/SConstruct b/SConstruct index 62e37dfd..090a9259 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.GetProjetDirName() +dist_dir_name = distenv.GetProjetDirName() +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/examples/application.fam b/applications/examples/application.fam index 8556714c..347411fa 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_plugins/application.fam b/applications/examples/example_plugins/application.fam new file mode 100644 index 00000000..a6e3c207 --- /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 00000000..acc5903a --- /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 00000000..12eba01c --- /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 00000000..15621935 --- /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 00000000..0b774dad --- /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 00000000..e24bc47b --- /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 00000000..42b3a186 --- /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 00000000..7035b79f --- /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 00000000..d0db44c4 --- /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 00000000..aacfb8c1 --- /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 00000000..17cc8be5 --- /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 00000000..d40c0dde --- /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 00000000..f27b0a08 --- /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 00000000..bf0ab50b --- /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 00000000..f0b2f726 --- /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 00000000..e8b5a22d --- /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/external/application.fam b/applications/external/application.fam new file mode 100644 index 00000000..12dc1cc1 --- /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/clock/application.fam b/applications/external/clock/application.fam similarity index 82% rename from applications/plugins/clock/application.fam rename to applications/external/clock/application.fam index 590f5dfe..a6a2eff3 100644 --- a/applications/plugins/clock/application.fam +++ b/applications/external/clock/application.fam @@ -1,7 +1,7 @@ App( appid="clock", name="Clock", - apptype=FlipperAppType.PLUGIN, + apptype=FlipperAppType.EXTERNAL, entry_point="clock_app", requires=["gui"], stack_size=2 * 1024, diff --git a/applications/plugins/clock/clock.png b/applications/external/clock/clock.png similarity index 100% rename from applications/plugins/clock/clock.png rename to applications/external/clock/clock.png diff --git a/applications/plugins/clock/clock_app.c b/applications/external/clock/clock_app.c similarity index 100% rename from applications/plugins/clock/clock_app.c rename to applications/external/clock/clock_app.c diff --git a/applications/plugins/dap_link/README.md b/applications/external/dap_link/README.md similarity index 100% rename from applications/plugins/dap_link/README.md rename to applications/external/dap_link/README.md 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 711e4833..01714380 100644 --- a/applications/plugins/dap_link/application.fam +++ b/applications/external/dap_link/application.fam @@ -1,7 +1,7 @@ App( appid="dap_link", name="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/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 b6e4e3bf..a9d8305d 100644 --- a/applications/plugins/hid_app/application.fam +++ b/applications/external/hid_app/application.fam @@ -1,7 +1,7 @@ App( appid="hid_usb", name="Remote", - apptype=FlipperAppType.PLUGIN, + apptype=FlipperAppType.EXTERNAL, entry_point="hid_usb_app", stack_size=1 * 1024, fap_category="USB", @@ -14,7 +14,7 @@ App( App( appid="hid_ble", name="Remote", - apptype=FlipperAppType.PLUGIN, + apptype=FlipperAppType.EXTERNAL, entry_point="hid_ble_app", stack_size=1 * 1024, fap_category="Bluetooth", 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/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 c51abf19..3414c0a4 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", 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/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/nfc_magic/assets/DolphinCommon_56x48.png b/applications/external/nfc_magic/assets/DolphinCommon_56x48.png similarity index 100% rename from applications/plugins/nfc_magic/assets/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 100% rename from applications/plugins/nfc_magic/lib/magic/magic.c rename to applications/external/nfc_magic/lib/magic/magic.c 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 100% rename from applications/plugins/nfc_magic/nfc_magic.c rename to applications/external/nfc_magic/nfc_magic.c 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 100% rename from applications/plugins/nfc_magic/nfc_magic_worker.c rename to applications/external/nfc_magic/nfc_magic_worker.c 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/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 100% rename from applications/plugins/picopass/helpers/iclass_elite_dict.c rename to applications/external/picopass/helpers/iclass_elite_dict.c diff --git a/applications/plugins/picopass/helpers/iclass_elite_dict.h b/applications/external/picopass/helpers/iclass_elite_dict.h similarity index 100% rename from applications/plugins/picopass/helpers/iclass_elite_dict.h rename to applications/external/picopass/helpers/iclass_elite_dict.h 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 100% rename from applications/plugins/picopass/picopass.c rename to applications/external/picopass/picopass.c 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 100% rename from applications/plugins/picopass/picopass_device.h rename to applications/external/picopass/picopass_device.h diff --git a/applications/plugins/picopass/picopass_i.h b/applications/external/picopass/picopass_i.h similarity index 100% rename from applications/plugins/picopass/picopass_i.h rename to applications/external/picopass/picopass_i.h 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 100% rename from applications/plugins/picopass/picopass_worker.c rename to applications/external/picopass/picopass_worker.c diff --git a/applications/plugins/picopass/picopass_worker.h b/applications/external/picopass/picopass_worker.h similarity index 100% rename from applications/plugins/picopass/picopass_worker.h rename to applications/external/picopass/picopass_worker.h 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 100% rename from applications/plugins/picopass/scenes/picopass_scene_config.h rename to applications/external/picopass/scenes/picopass_scene_config.h 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/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 100% rename from applications/plugins/picopass/scenes/picopass_scene_read_card_success.c rename to applications/external/picopass/scenes/picopass_scene_read_card_success.c 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 100% rename from applications/plugins/picopass/scenes/picopass_scene_start.c rename to applications/external/picopass/scenes/picopass_scene_start.c 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/plugins/signal_generator/application.fam b/applications/external/signal_generator/application.fam similarity index 78% rename from applications/plugins/signal_generator/application.fam rename to applications/external/signal_generator/application.fam index 60f8deff..094e784c 100644 --- a/applications/plugins/signal_generator/application.fam +++ b/applications/external/signal_generator/application.fam @@ -1,9 +1,8 @@ App( appid="signal_generator", name="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 75% rename from applications/plugins/snake_game/application.fam rename to applications/external/snake_game/application.fam index d55f53bb..c736a4dd 100644 --- a/applications/plugins/snake_game/application.fam +++ b/applications/external/snake_game/application.fam @@ -1,9 +1,8 @@ App( appid="snake_game", 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=30, 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/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/README.md b/applications/external/spi_mem_manager/tools/README.md similarity index 100% rename from applications/plugins/spi_mem_manager/tools/README.md rename to applications/external/spi_mem_manager/tools/README.md 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/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 935f9257..8dcaa125 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 100% rename from applications/plugins/weather_station/weather_station_app.c rename to applications/external/weather_station/weather_station_app.c 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/main/fap_loader/application.fam b/applications/main/fap_loader/application.fam index 784ee950..b0e67cd4 100644 --- a/applications/main/fap_loader/application.fam +++ b/applications/main/fap_loader/application.fam @@ -7,6 +7,7 @@ App( requires=[ "gui", "storage", + "loader", ], stack_size=int(1.5 * 1024), icon="A_Plugins_14", diff --git a/applications/main/fap_loader/elf_cpp/elf_hashtable.cpp b/applications/main/fap_loader/elf_cpp/elf_hashtable.cpp deleted file mode 100644 index e845ed00..00000000 --- a/applications/main/fap_loader/elf_cpp/elf_hashtable.cpp +++ /dev/null @@ -1,48 +0,0 @@ -#include "compilesort.hpp" -#include "elf_hashtable.h" -#include "elf_hashtable_entry.h" -#include "elf_hashtable_checks.hpp" - -#include -#include - -/* Generated table */ -#include - -#define TAG "elf_hashtable" - -static_assert(!has_hash_collisions(elf_api_table), "Detected API method hash collision!"); - -/** - * Get function address by function name - * @param name function name - * @param address output for function address - * @return true if the table contains a function - */ - -bool elf_resolve_from_hashtable(const char* name, Elf32_Addr* address) { - bool result = false; - uint32_t gnu_sym_hash = elf_gnu_hash(name); - - sym_entry key = { - .hash = gnu_sym_hash, - .address = 0, - }; - - auto find_res = std::lower_bound(elf_api_table.cbegin(), elf_api_table.cend(), key); - if((find_res == elf_api_table.cend() || (find_res->hash != gnu_sym_hash))) { - FURI_LOG_W(TAG, "Can't find symbol '%s' (hash %lx)!", name, gnu_sym_hash); - result = false; - } else { - result = true; - *address = find_res->address; - } - - return result; -} - -const ElfApiInterface hashtable_api_interface = { - .api_version_major = (elf_api_version >> 16), - .api_version_minor = (elf_api_version & 0xFFFF), - .resolver_callback = &elf_resolve_from_hashtable, -}; diff --git a/applications/main/fap_loader/elf_cpp/elf_hashtable.h b/applications/main/fap_loader/elf_cpp/elf_hashtable.h deleted file mode 100644 index e574f116..00000000 --- a/applications/main/fap_loader/elf_cpp/elf_hashtable.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once -#include - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -extern const ElfApiInterface hashtable_api_interface; - -#ifdef __cplusplus -} -#endif diff --git a/applications/main/fap_loader/elf_cpp/elf_hashtable_checks.hpp b/applications/main/fap_loader/elf_cpp/elf_hashtable_checks.hpp deleted file mode 100644 index 61ee80e9..00000000 --- a/applications/main/fap_loader/elf_cpp/elf_hashtable_checks.hpp +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Check for multiple entries with the same hash value at compilation time. - */ - -#pragma once -#include -#include "elf_hashtable_entry.h" - -template -constexpr bool has_hash_collisions(const std::array api_methods) { - for(std::size_t i = 0; i < (N - 1); ++i) { - if(api_methods[i].hash == api_methods[i + 1].hash) { - return true; - } - } - - return false; -} diff --git a/applications/main/fap_loader/elf_cpp/elf_hashtable_entry.h b/applications/main/fap_loader/elf_cpp/elf_hashtable_entry.h deleted file mode 100644 index 7b540fba..00000000 --- a/applications/main/fap_loader/elf_cpp/elf_hashtable_entry.h +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once -#include - -#ifdef __cplusplus -extern "C" { -#endif - -struct sym_entry { - uint32_t hash; - uint32_t address; -}; - -#ifdef __cplusplus -} - -#include -#include - -#define API_METHOD(x, ret_type, args_type) \ - sym_entry { \ - .hash = elf_gnu_hash(#x), .address = (uint32_t)(static_cast(x)) \ - } - -#define API_VARIABLE(x, var_type) \ - sym_entry { \ - .hash = elf_gnu_hash(#x), .address = (uint32_t)(&(x)), \ - } - -constexpr bool operator<(const sym_entry& k1, const sym_entry& k2) { - return k1.hash < k2.hash; -} - -constexpr uint32_t elf_gnu_hash(const char* s) { - uint32_t h = 0x1505; - for(unsigned char c = *s; c != '\0'; c = *++s) { - h = (h << 5) + h + c; - } - return h; -} - -#endif diff --git a/applications/main/fap_loader/fap_loader_app.c b/applications/main/fap_loader/fap_loader_app.c index e81a3ce4..dcbad8e1 100644 --- a/applications/main/fap_loader/fap_loader_app.c +++ b/applications/main/fap_loader/fap_loader_app.c @@ -7,7 +7,7 @@ #include #include #include -#include "elf_cpp/elf_hashtable.h" +#include #include "fap_loader_app.h" #define TAG "fap_loader_app" @@ -27,7 +27,7 @@ bool fap_loader_load_name_and_icon( Storage* storage, uint8_t** icon_ptr, FuriString* item_name) { - FlipperApplication* app = flipper_application_alloc(storage, &hashtable_api_interface); + FlipperApplication* app = flipper_application_alloc(storage, firmware_api_interface); FlipperApplicationPreloadStatus preload_res = flipper_application_preload_manifest(app, furi_string_get_cstr(path)); @@ -71,7 +71,7 @@ static bool fap_loader_run_selected_app(FapLoader* loader) { bool show_error = true; do { file_selected = true; - loader->app = flipper_application_alloc(loader->storage, &hashtable_api_interface); + loader->app = flipper_application_alloc(loader->storage, firmware_api_interface); size_t start = furi_get_tick(); FURI_LOG_I(TAG, "FAP Loader is loading %s", furi_string_get_cstr(loader->fap_path)); diff --git a/applications/plugins/application.fam b/applications/plugins/application.fam deleted file mode 100644 index 6d25e45a..00000000 --- a/applications/plugins/application.fam +++ /dev/null @@ -1,9 +0,0 @@ -App( - appid="basic_plugins", - name="Basic applications for plug-in menu", - apptype=FlipperAppType.METAPACKAGE, - provides=[ - "music_player", - "snake_game", - ], -) diff --git a/applications/services/applications.h b/applications/services/applications.h index 871e9af5..85f73674 100644 --- a/applications/services/applications.h +++ b/applications/services/applications.h @@ -39,18 +39,6 @@ extern const size_t FLIPPER_APPS_COUNT; extern const FlipperOnStartHook FLIPPER_ON_SYSTEM_START[]; extern const size_t FLIPPER_ON_SYSTEM_START_COUNT; -/* Plugins list - * Spawned by loader - */ -extern const FlipperApplication FLIPPER_PLUGINS[]; -extern const size_t FLIPPER_PLUGINS_COUNT; - -/* Debug menu apps - * Spawned by loader - */ -extern const FlipperApplication FLIPPER_DEBUG_APPS[]; -extern const size_t FLIPPER_DEBUG_APPS_COUNT; - /* System apps * Can only be spawned by loader by name */ diff --git a/applications/services/loader/application.fam b/applications/services/loader/application.fam index 91103e46..49f3c414 100644 --- a/applications/services/loader/application.fam +++ b/applications/services/loader/application.fam @@ -7,5 +7,8 @@ App( requires=["gui"], stack_size=2 * 1024, order=90, - sdk_headers=["loader.h"], + sdk_headers=[ + "loader.h", + "firmware_api/firmware_api.h", + ], ) diff --git a/applications/services/loader/firmware_api/firmware_api.cpp b/applications/services/loader/firmware_api/firmware_api.cpp new file mode 100644 index 00000000..814dd82c --- /dev/null +++ b/applications/services/loader/firmware_api/firmware_api.cpp @@ -0,0 +1,21 @@ +#include "firmware_api.h" + +#include +#include + +/* Generated table */ +#include + +static_assert(!has_hash_collisions(elf_api_table), "Detected API method hash collision!"); + +constexpr HashtableApiInterface elf_api_interface{ + { + .api_version_major = (elf_api_version >> 16), + .api_version_minor = (elf_api_version & 0xFFFF), + .resolver_callback = &elf_resolve_from_hashtable, + }, + .table_cbegin = elf_api_table.cbegin(), + .table_cend = elf_api_table.cend(), +}; + +const ElfApiInterface* const firmware_api_interface = &elf_api_interface; diff --git a/applications/services/loader/firmware_api/firmware_api.h b/applications/services/loader/firmware_api/firmware_api.h new file mode 100644 index 00000000..c73ae896 --- /dev/null +++ b/applications/services/loader/firmware_api/firmware_api.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +extern const ElfApiInterface* const firmware_api_interface; diff --git a/applications/services/loader/loader.c b/applications/services/loader/loader.c index 5f2d8a2e..f83d47d6 100644 --- a/applications/services/loader/loader.c +++ b/applications/services/loader/loader.c @@ -88,10 +88,6 @@ static FlipperApplication const* loader_find_application_by_name_in_list( const FlipperApplication* loader_find_application_by_name(const char* name) { const FlipperApplication* application = NULL; application = loader_find_application_by_name_in_list(name, FLIPPER_APPS, FLIPPER_APPS_COUNT); - if(!application) { - application = - loader_find_application_by_name_in_list(name, FLIPPER_PLUGINS, FLIPPER_PLUGINS_COUNT); - } if(!application) { application = loader_find_application_by_name_in_list( name, FLIPPER_SETTINGS_APPS, FLIPPER_SETTINGS_APPS_COUNT); @@ -100,10 +96,6 @@ const FlipperApplication* loader_find_application_by_name(const char* name) { application = loader_find_application_by_name_in_list( name, FLIPPER_SYSTEM_APPS, FLIPPER_SYSTEM_APPS_COUNT); } - if(!application) { - application = loader_find_application_by_name_in_list( - name, FLIPPER_DEBUG_APPS, FLIPPER_DEBUG_APPS_COUNT); - } return application; } @@ -160,18 +152,6 @@ static void loader_cli_list(Cli* cli, FuriString* args, Loader* instance) { for(size_t i = 0; i < FLIPPER_APPS_COUNT; i++) { printf("\t%s\r\n", FLIPPER_APPS[i].name); } - - printf("Plugins:\r\n"); - for(size_t i = 0; i < FLIPPER_PLUGINS_COUNT; i++) { - printf("\t%s\r\n", FLIPPER_PLUGINS[i].name); - } - - if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { - printf("Debug:\r\n"); - for(size_t i = 0; i < FLIPPER_DEBUG_APPS_COUNT; i++) { - printf("\t%s\r\n", FLIPPER_DEBUG_APPS[i].name); - } - } } static void loader_cli_info(Cli* cli, FuriString* args, Loader* instance) { @@ -341,22 +321,6 @@ static Loader* loader_alloc() { view_set_previous_callback(menu_get_view(instance->primary_menu), loader_hide_menu); view_dispatcher_add_view( instance->view_dispatcher, LoaderMenuViewPrimary, menu_get_view(instance->primary_menu)); - // Plugins menu - instance->plugins_menu = submenu_alloc(); - view_set_context(submenu_get_view(instance->plugins_menu), instance->plugins_menu); - view_set_previous_callback( - submenu_get_view(instance->plugins_menu), loader_back_to_primary_menu); - view_dispatcher_add_view( - instance->view_dispatcher, - LoaderMenuViewPlugins, - submenu_get_view(instance->plugins_menu)); - // Debug menu - instance->debug_menu = submenu_alloc(); - view_set_context(submenu_get_view(instance->debug_menu), instance->debug_menu); - view_set_previous_callback( - submenu_get_view(instance->debug_menu), loader_back_to_primary_menu); - view_dispatcher_add_view( - instance->view_dispatcher, LoaderMenuViewDebug, submenu_get_view(instance->debug_menu)); // Settings menu instance->settings_menu = submenu_alloc(); view_set_context(submenu_get_view(instance->settings_menu), instance->settings_menu); @@ -385,10 +349,6 @@ static void loader_free(Loader* instance) { menu_free(loader_instance->primary_menu); view_dispatcher_remove_view(loader_instance->view_dispatcher, LoaderMenuViewPrimary); - submenu_free(loader_instance->plugins_menu); - view_dispatcher_remove_view(loader_instance->view_dispatcher, LoaderMenuViewPlugins); - submenu_free(loader_instance->debug_menu); - view_dispatcher_remove_view(loader_instance->view_dispatcher, LoaderMenuViewDebug); submenu_free(loader_instance->settings_menu); view_dispatcher_remove_view(loader_instance->view_dispatcher, LoaderMenuViewSettings); view_dispatcher_free(loader_instance->view_dispatcher); @@ -411,24 +371,6 @@ static void loader_build_menu() { loader_menu_callback, (void*)&FLIPPER_APPS[i]); } - if(FLIPPER_PLUGINS_COUNT != 0) { - menu_add_item( - loader_instance->primary_menu, - "Plugins", - &A_Plugins_14, - i++, - loader_submenu_callback, - (void*)LoaderMenuViewPlugins); - } - if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug) && (FLIPPER_DEBUG_APPS_COUNT > 0)) { - menu_add_item( - loader_instance->primary_menu, - "Debug Tools", - &A_Debug_14, - i++, - loader_submenu_callback, - (void*)LoaderMenuViewDebug); - } menu_add_item( loader_instance->primary_menu, "Settings", @@ -439,29 +381,8 @@ static void loader_build_menu() { } static void loader_build_submenu() { - FURI_LOG_I(TAG, "Building plugins menu"); - size_t i; - for(i = 0; i < FLIPPER_PLUGINS_COUNT; i++) { - submenu_add_item( - loader_instance->plugins_menu, - FLIPPER_PLUGINS[i].name, - i, - loader_menu_callback, - (void*)&FLIPPER_PLUGINS[i]); - } - - FURI_LOG_I(TAG, "Building debug menu"); - for(i = 0; i < FLIPPER_DEBUG_APPS_COUNT; i++) { - submenu_add_item( - loader_instance->debug_menu, - FLIPPER_DEBUG_APPS[i].name, - i, - loader_menu_callback, - (void*)&FLIPPER_DEBUG_APPS[i]); - } - FURI_LOG_I(TAG, "Building settings menu"); - for(i = 0; i < FLIPPER_SETTINGS_APPS_COUNT; i++) { + for(size_t i = 0; i < FLIPPER_SETTINGS_APPS_COUNT; i++) { submenu_add_item( loader_instance->settings_menu, FLIPPER_SETTINGS_APPS[i].name, diff --git a/applications/services/loader/loader_i.h b/applications/services/loader/loader_i.h index db91f806..00028cd6 100644 --- a/applications/services/loader/loader_i.h +++ b/applications/services/loader/loader_i.h @@ -26,8 +26,6 @@ struct Loader { ViewDispatcher* view_dispatcher; Menu* primary_menu; - Submenu* plugins_menu; - Submenu* debug_menu; Submenu* settings_menu; volatile uint8_t lock_count; @@ -37,7 +35,5 @@ struct Loader { typedef enum { LoaderMenuViewPrimary, - LoaderMenuViewPlugins, - LoaderMenuViewDebug, LoaderMenuViewSettings, } LoaderMenuView; diff --git a/assets/.gitignore b/assets/.gitignore index 26957704..a66a6eed 100644 --- a/assets/.gitignore +++ b/assets/.gitignore @@ -2,3 +2,4 @@ /resources/Manifest /resources/apps/* /resources/dolphin/* +/resources/apps_data/**/*.fal diff --git a/documentation/Doxyfile b/documentation/Doxyfile index 1824e5a5..9611e7f1 100644 --- a/documentation/Doxyfile +++ b/documentation/Doxyfile @@ -938,7 +938,7 @@ EXCLUDE = \ lib/microtar \ lib/mbedtls \ lib/cxxheaderparser \ - applications/plugins/dap_link/lib/free-dap + applications/external/dap_link/lib/free-dap # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded diff --git a/fbt_options.py b/fbt_options.py index 4850389a..a10c64b9 100644 --- a/fbt_options.py +++ b/fbt_options.py @@ -71,11 +71,6 @@ FIRMWARE_APPS = { "system_apps", # Settings "settings_apps", - # Stock plugins - no longer built into fw, now they're .faps - # Yet you can still build them as a part of fw - # "basic_plugins", - # Debug - # "debug_apps", ], "unit_tests": [ "basic_services", diff --git a/firmware/targets/f18/api_symbols.csv b/firmware/targets/f18/api_symbols.csv index 07c323a1..61195aba 100644 --- a/firmware/targets/f18/api_symbols.csv +++ b/firmware/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,18.1,, +Version,+,18.2,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -28,6 +28,7 @@ Header,+,applications/services/gui/modules/widget_elements/widget_element.h,, Header,+,applications/services/gui/view_dispatcher.h,, Header,+,applications/services/gui/view_stack.h,, Header,+,applications/services/input/input.h,, +Header,+,applications/services/loader/firmware_api/firmware_api.h,, Header,+,applications/services/loader/loader.h,, Header,+,applications/services/locale/locale.h,, Header,+,applications/services/notification/notification.h,, @@ -104,7 +105,11 @@ Header,+,lib/STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc/stm32wbxx_ll_tim.h,, Header,+,lib/STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc/stm32wbxx_ll_usart.h,, Header,+,lib/STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc/stm32wbxx_ll_utils.h,, Header,+,lib/STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc/stm32wbxx_ll_wwdg.h,, +Header,+,lib/flipper_application/api_hashtable/api_hashtable.h,, +Header,+,lib/flipper_application/api_hashtable/compilesort.hpp,, Header,+,lib/flipper_application/flipper_application.h,, +Header,+,lib/flipper_application/plugins/composite_resolver.h,, +Header,+,lib/flipper_application/plugins/plugin_manager.h,, Header,+,lib/flipper_format/flipper_format.h,, Header,+,lib/flipper_format/flipper_format_i.h,, Header,+,lib/libusb_stm32/inc/hid_usage_button.h,, @@ -567,6 +572,10 @@ Function,+,cli_session_close,void,Cli* Function,+,cli_session_open,void,"Cli*, void*" Function,+,cli_write,void,"Cli*, const uint8_t*, size_t" Function,-,clock,clock_t, +Function,+,composite_api_resolver_add,void,"CompositeApiResolver*, const ElfApiInterface*" +Function,+,composite_api_resolver_alloc,CompositeApiResolver*, +Function,+,composite_api_resolver_free,void,CompositeApiResolver* +Function,+,composite_api_resolver_get,const ElfApiInterface*,CompositeApiResolver* Function,+,crc32_calc_buffer,uint32_t,"uint32_t, const void*, size_t" Function,+,crc32_calc_file,uint32_t,"File*, const FileCrcProgressCb, void*" Function,-,ctermid,char*,char* @@ -639,6 +648,7 @@ Function,+,elements_slightly_rounded_box,void,"Canvas*, uint8_t, uint8_t, uint8_ Function,+,elements_slightly_rounded_frame,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t" Function,+,elements_string_fit_width,void,"Canvas*, FuriString*, uint8_t" Function,+,elements_text_box,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, Align, Align, const char*, _Bool" +Function,+,elf_resolve_from_hashtable,_Bool,"const ElfApiInterface*, const char*, Elf32_Addr*" Function,+,empty_screen_alloc,EmptyScreen*, Function,+,empty_screen_free,void,EmptyScreen* Function,+,empty_screen_get_view,View*,EmptyScreen* @@ -696,14 +706,16 @@ Function,-,fiscanf,int,"FILE*, const char*, ..." Function,+,flipper_application_alloc,FlipperApplication*,"Storage*, const ElfApiInterface*" Function,+,flipper_application_free,void,FlipperApplication* Function,+,flipper_application_get_manifest,const FlipperApplicationManifest*,FlipperApplication* +Function,+,flipper_application_is_plugin,_Bool,FlipperApplication* Function,+,flipper_application_load_status_to_string,const char*,FlipperApplicationLoadStatus Function,+,flipper_application_manifest_is_compatible,_Bool,"const FlipperApplicationManifest*, const ElfApiInterface*" Function,+,flipper_application_manifest_is_target_compatible,_Bool,const FlipperApplicationManifest* Function,+,flipper_application_manifest_is_valid,_Bool,const FlipperApplicationManifest* Function,+,flipper_application_map_to_memory,FlipperApplicationLoadStatus,FlipperApplication* +Function,+,flipper_application_plugin_get_descriptor,const FlipperAppPluginDescriptor*,FlipperApplication* Function,+,flipper_application_preload,FlipperApplicationPreloadStatus,"FlipperApplication*, const char*" Function,+,flipper_application_preload_manifest,FlipperApplicationPreloadStatus,"FlipperApplication*, const char*" -Function,-,flipper_application_preload_status_to_string,const char*,FlipperApplicationPreloadStatus +Function,+,flipper_application_preload_status_to_string,const char*,FlipperApplicationPreloadStatus Function,+,flipper_application_spawn,FuriThread*,"FlipperApplication*, void*" Function,+,flipper_format_buffered_file_alloc,FlipperFormat*,Storage* Function,+,flipper_format_buffered_file_close,_Bool,FlipperFormat* @@ -1473,6 +1485,13 @@ Function,-,pcTaskGetName,char*,TaskHandle_t Function,-,pcTimerGetName,const char*,TimerHandle_t Function,-,pclose,int,FILE* Function,-,perror,void,const char* +Function,+,plugin_manager_alloc,PluginManager*,"const char*, uint32_t, const ElfApiInterface*" +Function,+,plugin_manager_free,void,PluginManager* +Function,+,plugin_manager_get,const FlipperAppPluginDescriptor*,"PluginManager*, uint32_t" +Function,+,plugin_manager_get_count,uint32_t,PluginManager* +Function,+,plugin_manager_get_ep,const void*,"PluginManager*, uint32_t" +Function,+,plugin_manager_load_all,PluginManagerError,"PluginManager*, const char*" +Function,+,plugin_manager_load_single,PluginManagerError,"PluginManager*, const char*" Function,-,popen,FILE*,"const char*, const char*" Function,+,popup_alloc,Popup*, Function,+,popup_disable_timeout,void,Popup* @@ -2053,6 +2072,7 @@ Variable,-,_sys_nerr,int, Variable,-,_timezone,long, Variable,-,_tzname,char*[2], Variable,+,cli_vcp,CliSession, +Variable,+,firmware_api_interface,const ElfApiInterface*, Variable,+,furi_hal_i2c_bus_external,FuriHalI2cBus, Variable,+,furi_hal_i2c_bus_power,FuriHalI2cBus, Variable,+,furi_hal_i2c_handle_external,FuriHalI2cBusHandle, diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index fcacaeee..e46322f4 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,18.1,, +Version,+,18.2,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -28,6 +28,7 @@ Header,+,applications/services/gui/modules/widget_elements/widget_element.h,, Header,+,applications/services/gui/view_dispatcher.h,, Header,+,applications/services/gui/view_stack.h,, Header,+,applications/services/input/input.h,, +Header,+,applications/services/loader/firmware_api/firmware_api.h,, Header,+,applications/services/loader/loader.h,, Header,+,applications/services/locale/locale.h,, Header,+,applications/services/notification/notification.h,, @@ -110,7 +111,11 @@ Header,+,lib/STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc/stm32wbxx_ll_tim.h,, Header,+,lib/STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc/stm32wbxx_ll_usart.h,, Header,+,lib/STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc/stm32wbxx_ll_utils.h,, Header,+,lib/STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc/stm32wbxx_ll_wwdg.h,, +Header,+,lib/flipper_application/api_hashtable/api_hashtable.h,, +Header,+,lib/flipper_application/api_hashtable/compilesort.hpp,, Header,+,lib/flipper_application/flipper_application.h,, +Header,+,lib/flipper_application/plugins/composite_resolver.h,, +Header,+,lib/flipper_application/plugins/plugin_manager.h,, Header,+,lib/flipper_format/flipper_format.h,, Header,+,lib/flipper_format/flipper_format_i.h,, Header,+,lib/infrared/encoder_decoder/infrared.h,, @@ -679,6 +684,10 @@ Function,+,cli_session_close,void,Cli* Function,+,cli_session_open,void,"Cli*, void*" Function,+,cli_write,void,"Cli*, const uint8_t*, size_t" Function,-,clock,clock_t, +Function,+,composite_api_resolver_add,void,"CompositeApiResolver*, const ElfApiInterface*" +Function,+,composite_api_resolver_alloc,CompositeApiResolver*, +Function,+,composite_api_resolver_free,void,CompositeApiResolver* +Function,+,composite_api_resolver_get,const ElfApiInterface*,CompositeApiResolver* Function,-,copysign,double,"double, double" Function,-,copysignf,float,"float, float" Function,-,copysignl,long double,"long double, long double" @@ -778,6 +787,7 @@ Function,+,elements_slightly_rounded_box,void,"Canvas*, uint8_t, uint8_t, uint8_ Function,+,elements_slightly_rounded_frame,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t" Function,+,elements_string_fit_width,void,"Canvas*, FuriString*, uint8_t" Function,+,elements_text_box,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, Align, Align, const char*, _Bool" +Function,+,elf_resolve_from_hashtable,_Bool,"const ElfApiInterface*, const char*, Elf32_Addr*" Function,+,empty_screen_alloc,EmptyScreen*, Function,+,empty_screen_free,void,EmptyScreen* Function,+,empty_screen_get_view,View*,EmptyScreen* @@ -863,14 +873,16 @@ Function,-,fiscanf,int,"FILE*, const char*, ..." Function,+,flipper_application_alloc,FlipperApplication*,"Storage*, const ElfApiInterface*" Function,+,flipper_application_free,void,FlipperApplication* Function,+,flipper_application_get_manifest,const FlipperApplicationManifest*,FlipperApplication* +Function,+,flipper_application_is_plugin,_Bool,FlipperApplication* Function,+,flipper_application_load_status_to_string,const char*,FlipperApplicationLoadStatus Function,+,flipper_application_manifest_is_compatible,_Bool,"const FlipperApplicationManifest*, const ElfApiInterface*" Function,+,flipper_application_manifest_is_target_compatible,_Bool,const FlipperApplicationManifest* Function,+,flipper_application_manifest_is_valid,_Bool,const FlipperApplicationManifest* Function,+,flipper_application_map_to_memory,FlipperApplicationLoadStatus,FlipperApplication* +Function,+,flipper_application_plugin_get_descriptor,const FlipperAppPluginDescriptor*,FlipperApplication* Function,+,flipper_application_preload,FlipperApplicationPreloadStatus,"FlipperApplication*, const char*" Function,+,flipper_application_preload_manifest,FlipperApplicationPreloadStatus,"FlipperApplication*, const char*" -Function,-,flipper_application_preload_status_to_string,const char*,FlipperApplicationPreloadStatus +Function,+,flipper_application_preload_status_to_string,const char*,FlipperApplicationPreloadStatus Function,+,flipper_application_spawn,FuriThread*,"FlipperApplication*, void*" Function,+,flipper_format_buffered_file_alloc,FlipperFormat*,Storage* Function,+,flipper_format_buffered_file_close,_Bool,FlipperFormat* @@ -2091,6 +2103,13 @@ Function,-,platformProtectST25RComm,void, Function,-,platformSetIrqCallback,void,PlatformIrqCallback Function,-,platformSpiTxRx,_Bool,"const uint8_t*, uint8_t*, uint16_t" Function,-,platformUnprotectST25RComm,void, +Function,+,plugin_manager_alloc,PluginManager*,"const char*, uint32_t, const ElfApiInterface*" +Function,+,plugin_manager_free,void,PluginManager* +Function,+,plugin_manager_get,const FlipperAppPluginDescriptor*,"PluginManager*, uint32_t" +Function,+,plugin_manager_get_count,uint32_t,PluginManager* +Function,+,plugin_manager_get_ep,const void*,"PluginManager*, uint32_t" +Function,+,plugin_manager_load_all,PluginManagerError,"PluginManager*, const char*" +Function,+,plugin_manager_load_single,PluginManagerError,"PluginManager*, const char*" Function,-,popen,FILE*,"const char*, const char*" Function,+,popup_alloc,Popup*, Function,+,popup_disable_timeout,void,Popup* @@ -3021,6 +3040,7 @@ Variable,-,_sys_nerr,int, Variable,-,_timezone,long, Variable,-,_tzname,char*[2], Variable,+,cli_vcp,CliSession, +Variable,+,firmware_api_interface,const ElfApiInterface*, Variable,+,furi_hal_i2c_bus_external,FuriHalI2cBus, Variable,+,furi_hal_i2c_bus_power,FuriHalI2cBus, Variable,+,furi_hal_i2c_handle_external,FuriHalI2cBusHandle, diff --git a/furi/flipper.c b/furi/flipper.c index f0147c06..5c2ad813 100644 --- a/furi/flipper.c +++ b/furi/flipper.c @@ -33,7 +33,7 @@ void flipper_init() { FURI_LOG_I(TAG, "Boot mode %d, starting services", furi_hal_rtc_get_boot_mode()); for(size_t i = 0; i < FLIPPER_SERVICES_COUNT; i++) { - FURI_LOG_I(TAG, "Starting service %s", FLIPPER_SERVICES[i].name); + FURI_LOG_D(TAG, "Starting service %s", FLIPPER_SERVICES[i].name); FuriThread* thread = furi_thread_alloc_ex( FLIPPER_SERVICES[i].name, diff --git a/lib/flipper_application/SConscript b/lib/flipper_application/SConscript index 9fbbf95d..d253cc82 100644 --- a/lib/flipper_application/SConscript +++ b/lib/flipper_application/SConscript @@ -6,6 +6,10 @@ env.Append( ], SDK_HEADERS=[ File("flipper_application.h"), + File("plugins/plugin_manager.h"), + File("plugins/composite_resolver.h"), + File("api_hashtable/api_hashtable.h"), + File("api_hashtable/compilesort.hpp"), ], ) @@ -14,6 +18,7 @@ libenv = env.Clone(FW_LIB_NAME="flipper_application") libenv.ApplyLibFlags() sources = libenv.GlobRecursive("*.c") +sources.append(File("api_hashtable/api_hashtable.cpp")) lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources) libenv.Install("${LIB_DIST_DIR}", lib) diff --git a/lib/flipper_application/api_hashtable/api_hashtable.cpp b/lib/flipper_application/api_hashtable/api_hashtable.cpp new file mode 100644 index 00000000..022792dc --- /dev/null +++ b/lib/flipper_application/api_hashtable/api_hashtable.cpp @@ -0,0 +1,38 @@ +#include "api_hashtable.h" + +#include +#include + +#define TAG "hashtable_api" + +bool elf_resolve_from_hashtable( + const ElfApiInterface* interface, + const char* name, + Elf32_Addr* address) { + const HashtableApiInterface* hashtable_interface = + static_cast(interface); + bool result = false; + uint32_t gnu_sym_hash = elf_gnu_hash(name); + + sym_entry key = { + .hash = gnu_sym_hash, + .address = 0, + }; + + auto find_res = + std::lower_bound(hashtable_interface->table_cbegin, hashtable_interface->table_cend, key); + if((find_res == hashtable_interface->table_cend || (find_res->hash != gnu_sym_hash))) { + FURI_LOG_W( + TAG, + "Can't find symbol '%s' (hash %lx) @ %p!", + name, + gnu_sym_hash, + hashtable_interface->table_cbegin); + result = false; + } else { + result = true; + *address = find_res->address; + } + + return result; +} diff --git a/lib/flipper_application/api_hashtable/api_hashtable.h b/lib/flipper_application/api_hashtable/api_hashtable.h new file mode 100644 index 00000000..7e4b4aba --- /dev/null +++ b/lib/flipper_application/api_hashtable/api_hashtable.h @@ -0,0 +1,85 @@ +#pragma once + +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Symbol table entry + */ +struct sym_entry { + uint32_t hash; + uint32_t address; +}; + +/** + * @brief Resolver for API entries using a pre-sorted table with hashes + * @param interface pointer to HashtableApiInterface + * @param name function name + * @param address output for function address + * @return true if the table contains a function + */ +bool elf_resolve_from_hashtable( + const ElfApiInterface* interface, + const char* name, + Elf32_Addr* address); + +#ifdef __cplusplus +} + +#include +#include + +/** + * @brief HashtableApiInterface is an implementation of ElfApiInterface + * that uses a hash table to resolve function addresses. + * table_cbegin and table_cend must point to a sorted array of sym_entry + */ +struct HashtableApiInterface : public ElfApiInterface { + const sym_entry *table_cbegin, *table_cend; +}; + +#define API_METHOD(x, ret_type, args_type) \ + sym_entry { \ + .hash = elf_gnu_hash(#x), .address = (uint32_t)(static_cast(x)) \ + } + +#define API_VARIABLE(x, var_type) \ + sym_entry { .hash = elf_gnu_hash(#x), .address = (uint32_t)(&(x)), } + +constexpr bool operator<(const sym_entry& k1, const sym_entry& k2) { + return k1.hash < k2.hash; +} + +/** + * @brief Calculate hash for a string using the ELF GNU hash algorithm + * @param s string to calculate hash for + * @return hash value + */ +constexpr uint32_t elf_gnu_hash(const char* s) { + uint32_t h = 0x1505; + for(unsigned char c = *s; c != '\0'; c = *++s) { + h = (h << 5) + h + c; + } + return h; +} + +/* Compile-time check for hash collisions in API table. + * Usage: static_assert(!has_hash_collisions(api_methods), "Hash collision detected"); + */ +template +constexpr bool has_hash_collisions(const std::array& api_methods) { + for(std::size_t i = 0; i < (N - 1); ++i) { + if(api_methods[i].hash == api_methods[i + 1].hash) { + return true; + } + } + + return false; +} + +#endif \ No newline at end of file diff --git a/applications/main/fap_loader/elf_cpp/compilesort.hpp b/lib/flipper_application/api_hashtable/compilesort.hpp similarity index 99% rename from applications/main/fap_loader/elf_cpp/compilesort.hpp rename to lib/flipper_application/api_hashtable/compilesort.hpp index 74661169..9737fd02 100644 --- a/applications/main/fap_loader/elf_cpp/compilesort.hpp +++ b/lib/flipper_application/api_hashtable/compilesort.hpp @@ -4,6 +4,8 @@ #pragma once +#ifdef __cplusplus + #include #include @@ -109,3 +111,5 @@ constexpr auto create_array_t(const Ts&&... values) { static_assert(traits::are_same_type(), "all elements must have same type"); return std::array{static_cast(values)...}; } + +#endif diff --git a/lib/flipper_application/elf/elf_api_interface.h b/lib/flipper_application/elf/elf_api_interface.h index ca31fc48..f07df4ed 100644 --- a/lib/flipper_application/elf/elf_api_interface.h +++ b/lib/flipper_application/elf/elf_api_interface.h @@ -3,10 +3,14 @@ #include #include -#define ELF_INVALID_ADDRESS 0xFFFFFFFF - -typedef struct { +/** + * @brief Interface for ELF loader to resolve symbols + */ +typedef struct ElfApiInterface { uint16_t api_version_major; uint16_t api_version_minor; - bool (*resolver_callback)(const char* name, Elf32_Addr* address); + bool (*resolver_callback)( + const struct ElfApiInterface* interface, + const char* name, + Elf32_Addr* address); } ElfApiInterface; diff --git a/lib/flipper_application/elf/elf_file.c b/lib/flipper_application/elf/elf_file.c index 58e31533..146afccb 100644 --- a/lib/flipper_application/elf/elf_file.c +++ b/lib/flipper_application/elf/elf_file.c @@ -17,6 +17,8 @@ #define FURI_LOG_D(...) #endif +#define ELF_INVALID_ADDRESS 0xFFFFFFFF + #define TRAMPOLINE_CODE_SIZE 6 /** @@ -166,7 +168,7 @@ static ELFSection* elf_section_of(ELFFile* elf, int index) { static Elf32_Addr elf_address_of(ELFFile* elf, Elf32_Sym* sym, const char* sName) { if(sym->st_shndx == SHN_UNDEF) { Elf32_Addr addr = 0; - if(elf->api_interface->resolver_callback(sName, &addr)) { + if(elf->api_interface->resolver_callback(elf->api_interface, sName, &addr)) { return addr; } } else { @@ -514,10 +516,13 @@ static SectionType elf_preload_section( section_p->sec_idx = section_idx; if(section_header->sh_type == SHT_PREINIT_ARRAY) { + furi_assert(elf->preinit_array == NULL); elf->preinit_array = section_p; } else if(section_header->sh_type == SHT_INIT_ARRAY) { + furi_assert(elf->init_array == NULL); elf->init_array = section_p; } else if(section_header->sh_type == SHT_FINI_ARRAY) { + furi_assert(elf->fini_array == NULL); elf->fini_array = section_p; } @@ -605,10 +610,17 @@ ELFFile* elf_file_alloc(Storage* storage, const ElfApiInterface* api_interface) elf->api_interface = api_interface; ELFSectionDict_init(elf->sections); AddressCache_init(elf->trampoline_cache); + elf->init_array_called = false; return elf; } void elf_file_free(ELFFile* elf) { + // furi_check(!elf->init_array_called); + if(elf->init_array_called) { + FURI_LOG_W(TAG, "Init array was called, but fini array wasn't"); + elf_file_call_section_list(elf->fini_array, true); + } + // free sections data { ELFSectionDict_it_t it; @@ -774,19 +786,26 @@ ELFFileLoadStatus elf_file_load_sections(ELFFile* elf) { return status; } -void elf_file_pre_run(ELFFile* elf) { +void elf_file_call_init(ELFFile* elf) { + furi_check(!elf->init_array_called); elf_file_call_section_list(elf->preinit_array, false); elf_file_call_section_list(elf->init_array, false); + elf->init_array_called = true; } -int32_t elf_file_run(ELFFile* elf, void* args) { - int32_t result; - result = ((int32_t(*)(void*))elf->entry)(args); - return result; +bool elf_file_is_init_complete(ELFFile* elf) { + return elf->init_array_called; } -void elf_file_post_run(ELFFile* elf) { +void* elf_file_get_entry_point(ELFFile* elf) { + furi_check(elf->init_array_called); + return (void*)elf->entry; +} + +void elf_file_call_fini(ELFFile* elf) { + furi_check(elf->init_array_called); elf_file_call_section_list(elf->fini_array, true); + elf->init_array_called = false; } const ElfApiInterface* elf_file_get_api_interface(ELFFile* elf_file) { diff --git a/lib/flipper_application/elf/elf_file.h b/lib/flipper_application/elf/elf_file.h index f371cdb2..631fe122 100644 --- a/lib/flipper_application/elf/elf_file.h +++ b/lib/flipper_application/elf/elf_file.h @@ -82,24 +82,34 @@ bool elf_file_load_section_table(ELFFile* elf_file); ELFFileLoadStatus elf_file_load_sections(ELFFile* elf_file); /** - * @brief Execute ELF file pre-run stage, call static constructors for example (load stage #3) + * @brief Execute ELF file pre-run stage, + * call static constructors for example (load stage #3) + * Must be done before invoking any code from the ELF file * @param elf */ -void elf_file_pre_run(ELFFile* elf); +void elf_file_call_init(ELFFile* elf); /** - * @brief Run ELF file (load stage #4) + * @brief Check if ELF file pre-run stage was executed and its code is runnable + * @param elf + */ +bool elf_file_is_init_complete(ELFFile* elf); + +/** + * @brief Get actual entry point for ELF file * @param elf_file * @param args * @return int32_t */ -int32_t elf_file_run(ELFFile* elf_file, void* args); +void* elf_file_get_entry_point(ELFFile* elf_file); /** - * @brief Execute ELF file post-run stage, call static destructors for example (load stage #5) + * @brief Execute ELF file post-run stage, + * call static destructors for example (load stage #5) + * Must be done if any code from the ELF file was executed * @param elf */ -void elf_file_post_run(ELFFile* elf); +void elf_file_call_fini(ELFFile* elf); /** * @brief Get ELF file API interface diff --git a/lib/flipper_application/elf/elf_file_i.h b/lib/flipper_application/elf/elf_file_i.h index 9b38540b..af9a1d9b 100644 --- a/lib/flipper_application/elf/elf_file_i.h +++ b/lib/flipper_application/elf/elf_file_i.h @@ -45,6 +45,8 @@ struct ELFFile { ELFSection* preinit_array; ELFSection* init_array; ELFSection* fini_array; + + bool init_array_called; }; #ifdef __cplusplus diff --git a/lib/flipper_application/flipper_application.c b/lib/flipper_application/flipper_application.c index 6e20c080..ca917cf1 100644 --- a/lib/flipper_application/flipper_application.c +++ b/lib/flipper_application/flipper_application.c @@ -10,6 +10,7 @@ struct FlipperApplication { FlipperApplicationManifest manifest; ELFFile* elf; FuriThread* thread; + void* ep_thread_args; }; /* For debugger access to app state */ @@ -20,9 +21,14 @@ FlipperApplication* FlipperApplication* app = malloc(sizeof(FlipperApplication)); app->elf = elf_file_alloc(storage, api_interface); app->thread = NULL; + app->ep_thread_args = NULL; return app; } +bool flipper_application_is_plugin(FlipperApplication* app) { + return app->manifest.stack_size == 0; +} + void flipper_application_free(FlipperApplication* app) { furi_assert(app); @@ -31,9 +37,16 @@ void flipper_application_free(FlipperApplication* app) { furi_thread_free(app->thread); } - last_loaded_app = NULL; + if(!flipper_application_is_plugin(app)) { + last_loaded_app = NULL; + } elf_file_clear_debug_info(&app->state); + + if(elf_file_is_init_complete(app->elf)) { + elf_file_call_fini(app->elf); + } + elf_file_free(app->elf); free(app); } @@ -140,7 +153,9 @@ const FlipperApplicationManifest* flipper_application_get_manifest(FlipperApplic } FlipperApplicationLoadStatus flipper_application_map_to_memory(FlipperApplication* app) { - last_loaded_app = app; + if(!flipper_application_is_plugin(app)) { + last_loaded_app = app; + } ELFFileLoadStatus status = elf_file_load_sections(app->elf); switch(status) { @@ -157,9 +172,15 @@ FlipperApplicationLoadStatus flipper_application_map_to_memory(FlipperApplicatio } static int32_t flipper_application_thread(void* context) { - elf_file_pre_run(last_loaded_app->elf); - int32_t result = elf_file_run(last_loaded_app->elf, context); - elf_file_post_run(last_loaded_app->elf); + furi_assert(context); + FlipperApplication* app = (FlipperApplication*)context; + + elf_file_call_init(app->elf); + + FlipperApplicationEntryPoint entry_point = elf_file_get_entry_point(app->elf); + int32_t ret_code = entry_point(app->ep_thread_args); + + elf_file_call_fini(app->elf); // wait until all notifications from RAM are completed NotificationApp* notifications = furi_record_open(RECORD_NOTIFICATION); @@ -169,17 +190,17 @@ static int32_t flipper_application_thread(void* context) { notification_message_block(notifications, &sequence_empty); furi_record_close(RECORD_NOTIFICATION); - return result; + return ret_code; } FuriThread* flipper_application_spawn(FlipperApplication* app, void* args) { furi_check(app->thread == NULL); + furi_check(!flipper_application_is_plugin(app)); + app->ep_thread_args = args; const FlipperApplicationManifest* manifest = flipper_application_get_manifest(app); - furi_check(manifest->stack_size > 0); - app->thread = furi_thread_alloc_ex( - manifest->name, manifest->stack_size, flipper_application_thread, args); + manifest->name, manifest->stack_size, flipper_application_thread, app); return app->thread; } @@ -213,3 +234,28 @@ const char* flipper_application_load_status_to_string(FlipperApplicationLoadStat } return load_status_strings[status]; } + +const FlipperAppPluginDescriptor* + flipper_application_plugin_get_descriptor(FlipperApplication* app) { + if(!flipper_application_is_plugin(app)) { + return NULL; + } + + if(!elf_file_is_init_complete(app->elf)) { + elf_file_call_init(app->elf); + } + + typedef const FlipperAppPluginDescriptor* (*get_lib_descriptor_t)(void); + get_lib_descriptor_t lib_ep = elf_file_get_entry_point(app->elf); + furi_check(lib_ep); + + const FlipperAppPluginDescriptor* lib_descriptor = lib_ep(); + + FURI_LOG_D( + TAG, + "Library for %s, API v. %lu loaded", + lib_descriptor->appid, + lib_descriptor->ep_api_version); + + return lib_descriptor; +} \ No newline at end of file diff --git a/lib/flipper_application/flipper_application.h b/lib/flipper_application/flipper_application.h index b3e5996b..519cc397 100644 --- a/lib/flipper_application/flipper_application.h +++ b/lib/flipper_application/flipper_application.h @@ -115,6 +115,40 @@ FlipperApplicationLoadStatus flipper_application_map_to_memory(FlipperApplicatio */ FuriThread* flipper_application_spawn(FlipperApplication* app, void* args); +/** + * @brief Check if application is a plugin (not a runnable standalone app) + * @param app Application pointer + * @return true if application is a plugin, false otherwise + */ +bool flipper_application_is_plugin(FlipperApplication* app); + +/** + * @brief Entry point prototype for standalone applications + */ +typedef int32_t (*FlipperApplicationEntryPoint)(void*); + +/** + * @brief An object that describes a plugin - must be returned by plugin's entry point + */ +typedef struct { + const char* appid; + const uint32_t ep_api_version; + const void* entry_point; +} FlipperAppPluginDescriptor; + +/** + * @brief Entry point prototype for plugins + */ +typedef const FlipperAppPluginDescriptor* (*FlipperApplicationPluginEntryPoint)(void); + +/** + * @brief Get plugin descriptor for preloaded plugin + * @param app Application pointer + * @return Pointer to plugin descriptor + */ +const FlipperAppPluginDescriptor* + flipper_application_plugin_get_descriptor(FlipperApplication* app); + #ifdef __cplusplus } -#endif \ No newline at end of file +#endif diff --git a/lib/flipper_application/plugins/composite_resolver.c b/lib/flipper_application/plugins/composite_resolver.c new file mode 100644 index 00000000..1402c3ad --- /dev/null +++ b/lib/flipper_application/plugins/composite_resolver.c @@ -0,0 +1,52 @@ +#include "composite_resolver.h" + +#include +#include + +LIST_DEF(ElfApiInterfaceList, const ElfApiInterface*, M_POD_OPLIST) +#define M_OPL_ElfApiInterfaceList_t() LIST_OPLIST(ElfApiInterfaceList, M_POD_OPLIST) + +struct CompositeApiResolver { + ElfApiInterface api_interface; + ElfApiInterfaceList_t interfaces; +}; + +static bool composite_api_resolver_callback( + const ElfApiInterface* interface, + const char* name, + Elf32_Addr* address) { + CompositeApiResolver* resolver = (CompositeApiResolver*)interface; + for + M_EACH(interface, resolver->interfaces, ElfApiInterfaceList_t) { + if((*interface)->resolver_callback(*interface, name, address)) { + return true; + } + } + return false; +} + +CompositeApiResolver* composite_api_resolver_alloc() { + CompositeApiResolver* resolver = malloc(sizeof(CompositeApiResolver)); + resolver->api_interface.api_version_major = 0; + resolver->api_interface.api_version_minor = 0; + resolver->api_interface.resolver_callback = &composite_api_resolver_callback; + ElfApiInterfaceList_init(resolver->interfaces); + return resolver; +} + +void composite_api_resolver_free(CompositeApiResolver* resolver) { + ElfApiInterfaceList_clear(resolver->interfaces); + free(resolver); +} + +void composite_api_resolver_add(CompositeApiResolver* resolver, const ElfApiInterface* interface) { + if(ElfApiInterfaceList_empty_p(resolver->interfaces)) { + resolver->api_interface.api_version_major = interface->api_version_major; + resolver->api_interface.api_version_minor = interface->api_version_minor; + } + ElfApiInterfaceList_push_back(resolver->interfaces, interface); +} + +const ElfApiInterface* composite_api_resolver_get(CompositeApiResolver* resolver) { + return &resolver->api_interface; +} diff --git a/lib/flipper_application/plugins/composite_resolver.h b/lib/flipper_application/plugins/composite_resolver.h new file mode 100644 index 00000000..a2d4bab2 --- /dev/null +++ b/lib/flipper_application/plugins/composite_resolver.h @@ -0,0 +1,46 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Composite API resolver + * Resolves API interface by calling all resolvers in order + * Uses API version from first resolver + * Note: when using hashtable resolvers, collisions between tables are not detected + * Can be cast to ElfApiInterface* + */ +typedef struct CompositeApiResolver CompositeApiResolver; + +/** + * @brief Allocate composite API resolver + * @return CompositeApiResolver* instance + */ +CompositeApiResolver* composite_api_resolver_alloc(); + +/** + * @brief Free composite API resolver + * @param resolver Instance + */ +void composite_api_resolver_free(CompositeApiResolver* resolver); + +/** + * @brief Add API resolver to composite resolver + * @param resolver Instance + * @param interface API resolver + */ +void composite_api_resolver_add(CompositeApiResolver* resolver, const ElfApiInterface* interface); + +/** + * @brief Get API interface from composite resolver + * @param resolver Instance + * @return API interface + */ +const ElfApiInterface* composite_api_resolver_get(CompositeApiResolver* resolver); + +#ifdef __cplusplus +} +#endif diff --git a/lib/flipper_application/plugins/plugin_manager.c b/lib/flipper_application/plugins/plugin_manager.c new file mode 100644 index 00000000..101471dc --- /dev/null +++ b/lib/flipper_application/plugins/plugin_manager.c @@ -0,0 +1,153 @@ +#include "plugin_manager.h" + +#include +#include +#include + +#include +#include + +#include + +#define TAG "libmgr" + +ARRAY_DEF(FlipperApplicationList, FlipperApplication*, M_PTR_OPLIST) +#define M_OPL_FlipperApplicationList_t() ARRAY_OPLIST(FlipperApplicationList, M_PTR_OPLIST) + +struct PluginManager { + const char* application_id; + uint32_t api_version; + Storage* storage; + FlipperApplicationList_t libs; + const ElfApiInterface* api_interface; +}; + +PluginManager* plugin_manager_alloc( + const char* application_id, + uint32_t api_version, + const ElfApiInterface* api_interface) { + PluginManager* manager = malloc(sizeof(PluginManager)); + manager->application_id = application_id; + manager->api_version = api_version; + manager->api_interface = api_interface ? api_interface : firmware_api_interface; + manager->storage = furi_record_open(RECORD_STORAGE); + FlipperApplicationList_init(manager->libs); + return manager; +} + +void plugin_manager_free(PluginManager* manager) { + for + M_EACH(loaded_lib, manager->libs, FlipperApplicationList_t) { + flipper_application_free(*loaded_lib); + } + FlipperApplicationList_clear(manager->libs); + furi_record_close(RECORD_STORAGE); + free(manager); +} + +PluginManagerError plugin_manager_load_single(PluginManager* manager, const char* path) { + FlipperApplication* lib = flipper_application_alloc(manager->storage, manager->api_interface); + + PluginManagerError error = PluginManagerErrorNone; + do { + FlipperApplicationPreloadStatus preload_res = flipper_application_preload(lib, path); + + if(preload_res != FlipperApplicationPreloadStatusSuccess) { + FURI_LOG_E(TAG, "Failed to preload %s", path); + error = PluginManagerErrorLoaderError; + break; + } + + if(!flipper_application_is_plugin(lib)) { + FURI_LOG_E(TAG, "Not a plugin %s", path); + error = PluginManagerErrorLoaderError; + break; + } + + FlipperApplicationLoadStatus load_status = flipper_application_map_to_memory(lib); + if(load_status != FlipperApplicationLoadStatusSuccess) { + FURI_LOG_E(TAG, "Failed to load module_demo_plugin1.fal"); + break; + } + + const FlipperAppPluginDescriptor* app_descriptor = + flipper_application_plugin_get_descriptor(lib); + + if(!app_descriptor) { + FURI_LOG_E(TAG, "Failed to get descriptor %s", path); + error = PluginManagerErrorLoaderError; + break; + } + + if(strcmp(app_descriptor->appid, manager->application_id) != 0) { + FURI_LOG_E(TAG, "Application id mismatch %s", path); + error = PluginManagerErrorApplicationIdMismatch; + break; + } + + if(app_descriptor->ep_api_version != manager->api_version) { + FURI_LOG_E(TAG, "API version mismatch %s", path); + error = PluginManagerErrorAPIVersionMismatch; + break; + } + + FlipperApplicationList_push_back(manager->libs, lib); + } while(false); + + if(error != PluginManagerErrorNone) { + flipper_application_free(lib); + } + + return error; +} + +PluginManagerError plugin_manager_load_all(PluginManager* manager, const char* path) { + File* directory = storage_file_alloc(manager->storage); + char file_name_buffer[256]; + FuriString* file_name = furi_string_alloc(); + do { + if(!storage_dir_open(directory, path)) { + FURI_LOG_E(TAG, "Failed to open directory %s", path); + break; + } + while(true) { + if(!storage_dir_read(directory, NULL, file_name_buffer, sizeof(file_name_buffer))) { + break; + } + + furi_string_set(file_name, file_name_buffer); + if(!furi_string_end_with_str(file_name, ".fal")) { + continue; + } + + path_concat(path, file_name_buffer, file_name); + FURI_LOG_D(TAG, "Loading %s", furi_string_get_cstr(file_name)); + PluginManagerError error = + plugin_manager_load_single(manager, furi_string_get_cstr(file_name)); + + if(error != PluginManagerErrorNone) { + FURI_LOG_E(TAG, "Failed to load %s", furi_string_get_cstr(file_name)); + break; + } + } + } while(false); + storage_dir_close(directory); + storage_file_free(directory); + furi_string_free(file_name); + return PluginManagerErrorNone; +} + +uint32_t plugin_manager_get_count(PluginManager* manager) { + return FlipperApplicationList_size(manager->libs); +} + +const FlipperAppPluginDescriptor* plugin_manager_get(PluginManager* manager, uint32_t index) { + FlipperApplication* app = *FlipperApplicationList_get(manager->libs, index); + return flipper_application_plugin_get_descriptor(app); +} + +const void* plugin_manager_get_ep(PluginManager* manager, uint32_t index) { + const FlipperAppPluginDescriptor* lib_descr = plugin_manager_get(manager, index); + furi_check(lib_descr); + return lib_descr->entry_point; +} diff --git a/lib/flipper_application/plugins/plugin_manager.h b/lib/flipper_application/plugins/plugin_manager.h new file mode 100644 index 00000000..d94c25db --- /dev/null +++ b/lib/flipper_application/plugins/plugin_manager.h @@ -0,0 +1,82 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Object that manages plugins for an application + * Implements mass loading of plugins and provides access to their descriptors + */ +typedef struct PluginManager PluginManager; + +typedef enum { + PluginManagerErrorNone = 0, + PluginManagerErrorLoaderError, + PluginManagerErrorApplicationIdMismatch, + PluginManagerErrorAPIVersionMismatch, +} PluginManagerError; + +/** + * @brief Allocates new PluginManager + * @param application_id Application ID filter - only plugins with matching ID will be loaded + * @param api_version Application API version filter - only plugins with matching API version + * @param api_interface Application API interface - used to resolve plugins' API imports + * If plugin uses private application's API, use CompoundApiInterface + * @return new PluginManager instance + */ +PluginManager* plugin_manager_alloc( + const char* application_id, + uint32_t api_version, + const ElfApiInterface* api_interface); + +/** + * @brief Frees PluginManager + * @param manager PluginManager instance + */ +void plugin_manager_free(PluginManager* manager); + +/** + * @brief Loads single plugin by full path + * @param manager PluginManager instance + * @param path Path to plugin + * @return Error code + */ +PluginManagerError plugin_manager_load_single(PluginManager* manager, const char* path); + +/** + * @brief Loads all plugins from specified directory + * @param manager PluginManager instance + * @param path Path to directory + * @return Error code + */ +PluginManagerError plugin_manager_load_all(PluginManager* manager, const char* path); + +/** + * @brief Returns number of loaded plugins + * @param manager PluginManager instance + * @return Number of loaded plugins + */ +uint32_t plugin_manager_get_count(PluginManager* manager); + +/** + * @brief Returns plugin descriptor by index + * @param manager PluginManager instance + * @param index Plugin index + * @return Plugin descriptor + */ +const FlipperAppPluginDescriptor* plugin_manager_get(PluginManager* manager, uint32_t index); + +/** + * @brief Returns plugin entry point by index + * @param manager PluginManager instance + * @param index Plugin index + * @return Plugin entry point + */ +const void* plugin_manager_get_ep(PluginManager* manager, uint32_t index); + +#ifdef __cplusplus +} +#endif diff --git a/scripts/distfap.py b/scripts/distfap.py new file mode 100644 index 00000000..060fe26f --- /dev/null +++ b/scripts/distfap.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python3 + +from flipper.app import App +from flipper.storage import FlipperStorage, FlipperStorageOperations +from flipper.utils.cdc import resolve_port + +import os +import posixpath + + +class Main(App): + def init(self): + self.parser.add_argument("-p", "--port", help="CDC Port", default="auto") + self.parser.add_argument( + "-n", + "--no-launch", + dest="launch_app", + action="store_false", + help="Don't launch app", + ) + + self.parser.add_argument("fap_src_path", help="App file to upload") + self.parser.add_argument( + "--fap_dst_dir", help="Upload path", default="/ext/apps", required=False + ) + self.parser.set_defaults(func=self.install) + + def install(self): + if not (port := resolve_port(self.logger, self.args.port)): + return 1 + + try: + with FlipperStorage(port) as storage: + storage_ops = FlipperStorageOperations(storage) + fap_local_path = self.args.fap_src_path + self.args.fap_dst_dir = self.args.fap_dst_dir.rstrip("/\\") + + if not os.path.isfile(fap_local_path): + self.logger.error( + f"Error: source .fap ({fap_local_path}) not found" + ) + return 2 + + fap_dst_path = posixpath.join( + self.args.fap_dst_dir, os.path.basename(fap_local_path) + ) + + self.logger.info(f'Installing "{fap_local_path}" to {fap_dst_path}') + + storage_ops.recursive_send(fap_dst_path, fap_local_path, False) + + if not self.args.launch_app: + return 0 + + storage.send_and_wait_eol( + f'loader open "Applications" {fap_dst_path}\r' + ) + + if len(result := storage.read.until(storage.CLI_EOL)): + self.logger.error(f"Unexpected response: {result.decode('ascii')}") + return 3 + return 0 + + except Exception as e: + self.logger.error(f"Error: {e}") + # raise + return 4 + + +if __name__ == "__main__": + Main()() diff --git a/scripts/fbt/appmanifest.py b/scripts/fbt/appmanifest.py index eb1652b7..37ddc434 100644 --- a/scripts/fbt/appmanifest.py +++ b/scripts/fbt/appmanifest.py @@ -12,13 +12,13 @@ class FlipperAppType(Enum): SERVICE = "Service" SYSTEM = "System" APP = "App" - PLUGIN = "Plugin" DEBUG = "Debug" ARCHIVE = "Archive" SETTINGS = "Settings" STARTUP = "StartupHook" EXTERNAL = "External" METAPACKAGE = "Package" + PLUGIN = "Plugin" @dataclass @@ -69,12 +69,22 @@ class FlipperApplication: fap_private_libs: List[Library] = field(default_factory=list) fap_file_assets: Optional[str] = None # Internally used by fbt + _appmanager: Optional["AppManager"] = None _appdir: Optional[object] = None _apppath: Optional[str] = None + _plugins: List["FlipperApplication"] = field(default_factory=list) def supports_hardware_target(self, target: str): return target in self.targets or "all" in self.targets + @property + def is_default_deployable(self): + return self.apptype != FlipperAppType.DEBUG and self.fap_category != "Examples" + + def __post_init__(self): + if self.apptype == FlipperAppType.PLUGIN: + self.stack_size = 0 + class AppManager: def __init__(self): @@ -94,6 +104,23 @@ class AppManager: return app return None + def _validate_app_params(self, *args, **kw): + apptype = kw.get("apptype") + if apptype == FlipperAppType.PLUGIN: + if kw.get("stack_size"): + raise FlipperManifestException( + f"Plugin {kw.get('appid')} cannot have stack (did you mean FlipperAppType.EXTERNAL?)" + ) + if not kw.get("requires"): + raise FlipperManifestException( + f"Plugin {kw.get('appid')} must have 'requires' in manifest" + ) + # Harmless - cdefines for external apps are meaningless + # if apptype == FlipperAppType.EXTERNAL and kw.get("cdefines"): + # raise FlipperManifestException( + # f"External app {kw.get('appid')} must not have 'cdefines' in manifest" + # ) + def load_manifest(self, app_manifest_path: str, app_dir_node: object): if not os.path.exists(app_manifest_path): raise FlipperManifestException( @@ -105,12 +132,14 @@ class AppManager: def App(*args, **kw): nonlocal app_manifests + self._validate_app_params(*args, **kw) app_manifests.append( FlipperApplication( *args, **kw, _appdir=app_dir_node, _apppath=os.path.dirname(app_manifest_path), + _appmanager=self, ), ) @@ -155,7 +184,6 @@ class AppBuildset: FlipperAppType.SERVICE, FlipperAppType.SYSTEM, FlipperAppType.APP, - FlipperAppType.PLUGIN, FlipperAppType.DEBUG, FlipperAppType.ARCHIVE, FlipperAppType.SETTINGS, @@ -182,6 +210,7 @@ class AppBuildset: self._check_conflicts() self._check_unsatisfied() # unneeded? self._check_target_match() + self._group_plugins() self.apps = sorted( list(map(self.appmgr.get, self.appnames)), key=lambda app: app.appid, @@ -260,6 +289,18 @@ class AppBuildset: f"Apps incompatible with target {self.hw_target}: {', '.join(incompatible)}" ) + def _group_plugins(self): + known_extensions = self.get_apps_of_type(FlipperAppType.PLUGIN, all_known=True) + for extension_app in known_extensions: + for parent_app_id in extension_app.requires: + try: + parent_app = self.appmgr.get(parent_app_id) + parent_app._plugins.append(extension_app) + except FlipperManifestException as e: + self._writer( + f"Module {extension_app.appid} has unknown parent {parent_app_id}" + ) + def get_apps_cdefs(self): cdefs = set() for app in self.apps: @@ -301,7 +342,6 @@ class ApplicationsCGenerator: FlipperAppType.SERVICE: ("FlipperApplication", "FLIPPER_SERVICES"), FlipperAppType.SYSTEM: ("FlipperApplication", "FLIPPER_SYSTEM_APPS"), FlipperAppType.APP: ("FlipperApplication", "FLIPPER_APPS"), - FlipperAppType.PLUGIN: ("FlipperApplication", "FLIPPER_PLUGINS"), FlipperAppType.DEBUG: ("FlipperApplication", "FLIPPER_DEBUG_APPS"), FlipperAppType.SETTINGS: ("FlipperApplication", "FLIPPER_SETTINGS_APPS"), FlipperAppType.STARTUP: ("FlipperOnStartHook", "FLIPPER_ON_SYSTEM_START"), diff --git a/scripts/fbt/fapassets.py b/scripts/fbt/fapassets.py new file mode 100644 index 00000000..0649f03e --- /dev/null +++ b/scripts/fbt/fapassets.py @@ -0,0 +1,108 @@ +import os +import hashlib +import struct +from typing import TypedDict + + +class File(TypedDict): + path: str + size: int + content_path: str + + +class Dir(TypedDict): + path: str + + +class FileBundler: + """ + u32 magic + u32 version + u32 dirs_count + u32 files_count + u32 signature_size + u8[] signature + Dirs: + u32 dir_name length + u8[] dir_name + Files: + u32 file_name length + u8[] file_name + u32 file_content_size + u8[] file_content + """ + + def __init__(self, directory_path: str): + self.directory_path = directory_path + self.file_list: list[File] = [] + self.directory_list: list[Dir] = [] + self._gather() + + def _gather(self): + for root, dirs, files in os.walk(self.directory_path): + for file_info in files: + file_path = os.path.join(root, file_info) + file_size = os.path.getsize(file_path) + self.file_list.append( + { + "path": os.path.relpath(file_path, self.directory_path), + "size": file_size, + "content_path": file_path, + } + ) + + for dir_info in dirs: + dir_path = os.path.join(root, dir_info) + # dir_size = sum( + # os.path.getsize(os.path.join(dir_path, f)) for f in os.listdir(dir_path) + # ) + self.directory_list.append( + { + "path": os.path.relpath(dir_path, self.directory_path), + } + ) + + self.file_list.sort(key=lambda f: f["path"]) + self.directory_list.sort(key=lambda d: d["path"]) + + def export(self, target_path: str): + self._md5_hash = hashlib.md5() + with open(target_path, "wb") as f: + # Write header magic and version + f.write(struct.pack(" FlipperExternalAppInfo + EXT_LIBS={}, + _APP_ICONS=[], ) env.AddMethod(BuildAppElf) - env.AddMethod(GetExtAppFromPath) + env.AddMethod(GetExtAppByIdOrPath) env.Append( BUILDERS={ "FapDist": Builder( @@ -466,7 +427,7 @@ def generate(env, **kw): generator=generate_embed_app_metadata_actions, suffix=".fap", src_suffix=".elf", - # emitter=generate_embed_app_metadata_emitter, + emitter=embed_app_metadata_emitter, ), "ValidateAppImports": Builder( action=[ diff --git a/scripts/fbt_tools/fbt_sdk.py b/scripts/fbt_tools/fbt_sdk.py index 3a37eacc..32481981 100644 --- a/scripts/fbt_tools/fbt_sdk.py +++ b/scripts/fbt_tools/fbt_sdk.py @@ -220,7 +220,7 @@ def gen_sdk_data(sdk_cache: SdkCache): def _check_sdk_is_up2date(sdk_cache: SdkCache): if not sdk_cache.is_buildable(): raise UserError( - "SDK version is not finalized, please review changes and re-run operation" + "SDK version is not finalized, please review changes and re-run operation. See AppsOnSDCard.md for more details" ) diff --git a/scripts/flipper/storage.py b/scripts/flipper/storage.py index 9c9f5295..47e11236 100644 --- a/scripts/flipper/storage.py +++ b/scripts/flipper/storage.py @@ -4,6 +4,9 @@ import serial import time import hashlib import math +import logging +import posixpath +import enum def timing(func): @@ -25,12 +28,47 @@ def timing(func): return wrapper +class StorageErrorCode(enum.Enum): + OK = "OK" + NOT_READY = "filesystem not ready" + EXIST = "file/dir already exist" + NOT_EXIST = "file/dir not exist" + INVALID_PARAMETER = "invalid parameter" + DENIED = "access denied" + INVALID_NAME = "invalid name/path" + INTERNAL = "internal error" + NOT_IMPLEMENTED = "function not implemented" + ALREADY_OPEN = "file is already open" + UNKNOWN = "unknown error" + + @property + def is_error(self): + return self != self.OK + + @classmethod + def from_value(cls, s: str | bytes): + if isinstance(s, bytes): + s = s.decode("ascii") + for code in cls: + if code.value == s: + return code + return cls.UNKNOWN + + +class FlipperStorageException(Exception): + def __init__(self, message): + super().__init__(f"Storage error: {message}") + + def __init__(self, path: str, error_code: StorageErrorCode): + super().__init__(f"Storage error: path '{path}': {error_code.value}") + + class BufferedRead: def __init__(self, stream): self.buffer = bytearray() self.stream = stream - def until(self, eol="\n", cut_eol=True): + def until(self, eol: str = "\n", cut_eol: bool = True): eol = eol.encode("ascii") while True: # search in buffer @@ -59,9 +97,15 @@ class FlipperStorage: self.port.timeout = 2 self.port.baudrate = 115200 # Doesn't matter for VCP self.read = BufferedRead(self.port) - self.last_error = "" self.chunk_size = chunk_size + def __enter__(self): + self.start() + return self + + def __exit__(self, exc_type, exc_value, traceback): + self.stop() + def start(self): self.port.open() self.port.reset_input_buffer() @@ -71,37 +115,34 @@ class FlipperStorage: # And read buffer until we get prompt self.read.until(self.CLI_PROMPT) - def stop(self): + def stop(self) -> None: self.port.close() - def send(self, line): + def send(self, line: str) -> None: self.port.write(line.encode("ascii")) - def send_and_wait_eol(self, line): + def send_and_wait_eol(self, line: str): self.send(line) return self.read.until(self.CLI_EOL) - def send_and_wait_prompt(self, line): + def send_and_wait_prompt(self, line: str): self.send(line) return self.read.until(self.CLI_PROMPT) - def has_error(self, data): - """Is data has error""" - if data.find(b"Storage error") != -1: - return True - else: - return False + def has_error(self, data: bytes | str) -> bool: + """Is data an error message""" + return data.find(b"Storage error:") != -1 - def get_error(self, data): + def get_error(self, data: bytes) -> StorageErrorCode: """Extract error text from data and print it""" - error, error_text = data.decode("ascii").split(": ") - return error_text.strip() + _, error_text = data.decode("ascii").split(": ") + return StorageErrorCode.from_value(error_text.strip()) - def list_tree(self, path="/", level=0): + def list_tree(self, path: str = "/", level: int = 0): """List files and dirs on Flipper""" path = path.replace("//", "/") - self.send_and_wait_eol('storage list "' + path + '"\r') + self.send_and_wait_eol(f'storage list "{path}"\r') data = self.read.until(self.CLI_PROMPT) lines = data.split(b"\r\n") @@ -139,7 +180,7 @@ class FlipperStorage: # Something wrong, pass pass - def walk(self, path="/"): + def walk(self, path: str = "/"): dirs = [] nondirs = [] walk_dirs = [] @@ -181,14 +222,15 @@ class FlipperStorage: # Something wrong, pass pass - # topdown walk, yield before recursy + # topdown walk, yield before recursing yield path, dirs, nondirs for new_path in walk_dirs: yield from self.walk(new_path) - def send_file(self, filename_from, filename_to): + def send_file(self, filename_from: str, filename_to: str): """Send file from local device to Flipper""" - self.remove(filename_to) + if self.exist_file(filename_to): + self.remove(filename_to) with open(filename_from, "rb") as file: filesize = os.fstat(file.fileno()).st_size @@ -203,9 +245,9 @@ class FlipperStorage: self.send_and_wait_eol(f'storage write_chunk "{filename_to}" {size}\r') answer = self.read.until(self.CLI_EOL) if self.has_error(answer): - self.last_error = self.get_error(answer) + last_error = self.get_error(answer) self.read.until(self.CLI_PROMPT) - return False + raise FlipperStorageException(filename_to, last_error) self.port.write(filedata) self.read.until(self.CLI_PROMPT) @@ -218,9 +260,8 @@ class FlipperStorage: ) sys.stdout.flush() print() - return True - def read_file(self, filename): + def read_file(self, filename: str): """Receive file from Flipper, and get filedata (bytes)""" buffer_size = self.chunk_size self.send_and_wait_eol( @@ -229,9 +270,10 @@ class FlipperStorage: answer = self.read.until(self.CLI_EOL) filedata = bytearray() if self.has_error(answer): - self.last_error = self.get_error(answer) + last_error = self.get_error(answer) self.read.until(self.CLI_PROMPT) - return filedata + raise FlipperStorageException(filename, last_error) + # return filedata size = int(answer.split(b": ")[1]) read_size = 0 @@ -251,121 +293,89 @@ class FlipperStorage: self.read.until(self.CLI_PROMPT) return filedata - def receive_file(self, filename_from, filename_to): + def receive_file(self, filename_from: str, filename_to: str): """Receive file from Flipper to local storage""" with open(filename_to, "wb") as file: data = self.read_file(filename_from) - if not data: - return False - else: - file.write(data) - return True + file.write(data) - def exist(self, path): - """Is file or dir exist on Flipper""" - self.send_and_wait_eol('storage stat "' + path + '"\r') - answer = self.read.until(self.CLI_EOL) + def exist(self, path: str): + """Does file or dir exist on Flipper""" + self.send_and_wait_eol(f'storage stat "{path}"\r') + response = self.read.until(self.CLI_EOL) self.read.until(self.CLI_PROMPT) - if self.has_error(answer): - self.last_error = self.get_error(answer) - return False - else: - return True + return not self.has_error(response) - def exist_dir(self, path): - """Is dir exist on Flipper""" - self.send_and_wait_eol('storage stat "' + path + '"\r') - answer = self.read.until(self.CLI_EOL) + def exist_dir(self, path: str): + """Does dir exist on Flipper""" + self.send_and_wait_eol(f'storage stat "{path}"\r') + response = self.read.until(self.CLI_EOL) + self.read.until(self.CLI_PROMPT) + if self.has_error(response): + error_code = self.get_error(response) + if error_code in ( + StorageErrorCode.NOT_EXIST, + StorageErrorCode.INVALID_NAME, + ): + return False + raise FlipperStorageException(path, error_code) + + return True + + def exist_file(self, path: str): + """Does file exist on Flipper""" + self.send_and_wait_eol(f'storage stat "{path}"\r') + response = self.read.until(self.CLI_EOL) self.read.until(self.CLI_PROMPT) - if self.has_error(answer): - self.last_error = self.get_error(answer) - return False - else: - if answer.find(b"Directory") != -1: - return True - elif answer.find(b"Storage") != -1: - return True - else: - return False + return response.find(b"File, size:") != -1 - def exist_file(self, path): - """Is file exist on Flipper""" - self.send_and_wait_eol('storage stat "' + path + '"\r') - answer = self.read.until(self.CLI_EOL) - self.read.until(self.CLI_PROMPT) + def _check_no_error(self, response, path=None): + if self.has_error(response): + raise FlipperStorageException(self.get_error(response)) - if self.has_error(answer): - self.last_error = self.get_error(answer) - return False - else: - if answer.find(b"File, size:") != -1: - return True - else: - return False - - def size(self, path): + def size(self, path: str): """file size on Flipper""" - self.send_and_wait_eol('storage stat "' + path + '"\r') - answer = self.read.until(self.CLI_EOL) + self.send_and_wait_eol(f'storage stat "{path}"\r') + response = self.read.until(self.CLI_EOL) self.read.until(self.CLI_PROMPT) - if self.has_error(answer): - self.last_error = self.get_error(answer) - return False - else: - if answer.find(b"File, size:") != -1: - size = int( - "".join( - ch - for ch in answer.split(b": ")[1].decode("ascii") - if ch.isdigit() - ) + self._check_no_error(response, path) + if response.find(b"File, size:") != -1: + size = int( + "".join( + ch + for ch in response.split(b": ")[1].decode("ascii") + if ch.isdigit() ) - return size - else: - self.last_error = "access denied" - return -1 + ) + return size + raise FlipperStorageException("Not a file") - def mkdir(self, path): + def mkdir(self, path: str): """Create a directory on Flipper""" - self.send_and_wait_eol('storage mkdir "' + path + '"\r') - answer = self.read.until(self.CLI_EOL) + self.send_and_wait_eol(f'storage mkdir "{path}"\r') + response = self.read.until(self.CLI_EOL) self.read.until(self.CLI_PROMPT) - - if self.has_error(answer): - self.last_error = self.get_error(answer) - return False - else: - return True + self._check_no_error(response, path) def format_ext(self): - """Create a directory on Flipper""" + """Format external storage on Flipper""" self.send_and_wait_eol("storage format /ext\r") self.send_and_wait_eol("y\r") - answer = self.read.until(self.CLI_EOL) + response = self.read.until(self.CLI_EOL) self.read.until(self.CLI_PROMPT) + self._check_no_error(response, "/ext") - if self.has_error(answer): - self.last_error = self.get_error(answer) - return False - else: - return True - - def remove(self, path): + def remove(self, path: str): """Remove file or directory on Flipper""" - self.send_and_wait_eol('storage remove "' + path + '"\r') - answer = self.read.until(self.CLI_EOL) + self.send_and_wait_eol(f'storage remove "{path}"\r') + response = self.read.until(self.CLI_EOL) self.read.until(self.CLI_PROMPT) + self._check_no_error(response, path) - if self.has_error(answer): - self.last_error = self.get_error(answer) - return False - else: - return True - - def hash_local(self, filename): + def hash_local(self, filename: str): """Hash of local file""" hash_md5 = hashlib.md5() with open(filename, "rb") as f: @@ -373,14 +383,112 @@ class FlipperStorage: hash_md5.update(chunk) return hash_md5.hexdigest() - def hash_flipper(self, filename): + def hash_flipper(self, filename: str): """Get hash of file on Flipper""" self.send_and_wait_eol('storage md5 "' + filename + '"\r') hash = self.read.until(self.CLI_EOL) self.read.until(self.CLI_PROMPT) + self._check_no_error(hash, filename) + return hash.decode("ascii") - if self.has_error(hash): - self.last_error = self.get_error(hash) - return "" + +class FlipperStorageOperations: + def __init__(self, storage): + self.storage: FlipperStorage = storage + self.logger = logging.getLogger("FStorageOps") + + def send_file_to_storage( + self, flipper_file_path: str, local_file_path: str, force: bool = False + ): + self.logger.debug( + f"* send_file_to_storage: {local_file_path}->{flipper_file_path}, {force=}" + ) + exists = self.storage.exist_file(flipper_file_path) + do_upload = not exists + if exists: + hash_local = self.storage.hash_local(local_file_path) + hash_flipper = self.storage.hash_flipper(flipper_file_path) + self.logger.debug(f"hash check: local {hash_local}, flipper {hash_flipper}") + do_upload = force or (hash_local != hash_flipper) + + if do_upload: + self.logger.info(f'Sending "{local_file_path}" to "{flipper_file_path}"') + self.storage.send_file(local_file_path, flipper_file_path) + + # make directory with exist check + def mkpath(self, flipper_dir_path: str): + path_components, dirs_to_create = flipper_dir_path.split("/"), [] + while not self.storage.exist_dir(dir_path := "/".join(path_components)): + self.logger.debug(f'"{dir_path}" does not exist, will create') + dirs_to_create.append(path_components.pop()) + for dir_to_create in reversed(dirs_to_create): + path_components.append(dir_to_create) + self.storage.mkdir("/".join(path_components)) + + # send file or folder recursively + def recursive_send(self, flipper_path: str, local_path: str, force: bool = False): + if not os.path.exists(local_path): + raise FlipperStorageException(f'"{local_path}" does not exist') + + if os.path.isdir(local_path): + # create parent dir + self.mkpath(flipper_path) + + for dirpath, dirnames, filenames in os.walk(local_path): + self.logger.debug(f'Processing directory "{os.path.normpath(dirpath)}"') + dirnames.sort() + filenames.sort() + rel_path = os.path.relpath(dirpath, local_path) + + # create subdirs + for dirname in dirnames: + flipper_dir_path = os.path.join(flipper_path, rel_path, dirname) + flipper_dir_path = os.path.normpath(flipper_dir_path).replace( + os.sep, "/" + ) + self.mkpath(flipper_dir_path) + + # send files + for filename in filenames: + flipper_file_path = os.path.join(flipper_path, rel_path, filename) + flipper_file_path = os.path.normpath(flipper_file_path).replace( + os.sep, "/" + ) + local_file_path = os.path.normpath(os.path.join(dirpath, filename)) + self.send_file_to_storage(flipper_file_path, local_file_path, force) else: - return hash.decode("ascii") + self.mkpath(posixpath.dirname(flipper_path)) + self.send_file_to_storage(flipper_path, local_path, force) + + def recursive_receive(self, flipper_path: str, local_path: str): + if self.storage.exist_dir(flipper_path): + for dirpath, dirnames, filenames in self.storage.walk(flipper_path): + self.logger.debug( + f'Processing directory "{os.path.normpath(dirpath)}"'.replace( + os.sep, "/" + ) + ) + dirnames.sort() + filenames.sort() + + rel_path = os.path.relpath(dirpath, flipper_path) + + for dirname in dirnames: + local_dir_path = os.path.join(local_path, rel_path, dirname) + local_dir_path = os.path.normpath(local_dir_path) + os.makedirs(local_dir_path, exist_ok=True) + + for filename in filenames: + local_file_path = os.path.join(local_path, rel_path, filename) + local_file_path = os.path.normpath(local_file_path) + flipper_file_path = os.path.normpath( + os.path.join(dirpath, filename) + ).replace(os.sep, "/") + self.logger.info( + f'Receiving "{flipper_file_path}" to "{local_file_path}"' + ) + self.storage.receive_file(flipper_file_path, local_file_path) + + else: + self.logger.info(f'Receiving "{flipper_path}" to "{local_path}"') + self.storage.receive_file(flipper_path, local_path) diff --git a/scripts/requirements.txt b/scripts/requirements.txt deleted file mode 100644 index 5b6fac5f..00000000 --- a/scripts/requirements.txt +++ /dev/null @@ -1,9 +0,0 @@ -ansi==0.3.6 -black==22.6.0 -colorlog==6.7.0 -heatshrink2==0.11.0 -Pillow==9.1.1 -protobuf==3.20.1 -pyserial==3.5 -python3-protobuf==2.5.0 -SCons==4.4.0 diff --git a/scripts/runfap.py b/scripts/runfap.py index 410b3e7d..f8ff607c 100644 --- a/scripts/runfap.py +++ b/scripts/runfap.py @@ -1,108 +1,86 @@ #!/usr/bin/env python3 -import posixpath -from typing import final from flipper.app import App -from flipper.storage import FlipperStorage +from flipper.storage import FlipperStorage, FlipperStorageOperations from flipper.utils.cdc import resolve_port -import logging import os -import pathlib -import serial.tools.list_ports as list_ports +import posixpath +from functools import reduce +import operator class Main(App): def init(self): self.parser.add_argument("-p", "--port", help="CDC Port", default="auto") self.parser.add_argument( - "-n", - "--no-launch", - dest="launch_app", - action="store_false", - help="Don't launch app", + "--sources", + "-s", + nargs="+", + action="append", + default=[], + help="Files to send", + ) + self.parser.add_argument( + "--targets", + "-t", + nargs="+", + action="append", + default=[], + help="File destinations (must be same length as -s)", + ) + self.parser.add_argument( + "--host-app", + "-a", + help="Host app to launch", ) - self.parser.add_argument("fap_src_path", help="App file to upload") - self.parser.add_argument( - "--fap_dst_dir", help="Upload path", default="/ext/apps", required=False - ) self.parser.set_defaults(func=self.install) - # logging - self.logger = logging.getLogger() - - # make directory with exist check - def mkdir_on_storage(self, storage, flipper_dir_path): - if not storage.exist_dir(flipper_dir_path): - self.logger.debug(f'"{flipper_dir_path}" does not exist, creating') - if not storage.mkdir(flipper_dir_path): - self.logger.error(f"Error: {storage.last_error}") - return False - else: - self.logger.debug(f'"{flipper_dir_path}" already exists') - return True - - # send file with exist check and hash check - def send_file_to_storage(self, storage, flipper_file_path, local_file_path, force): - exists = storage.exist_file(flipper_file_path) - do_upload = not exists - if exists: - hash_local = storage.hash_local(local_file_path) - hash_flipper = storage.hash_flipper(flipper_file_path) - self.logger.debug(f"hash check: local {hash_local}, flipper {hash_flipper}") - do_upload = force or (hash_local != hash_flipper) - - if do_upload: - self.logger.info(f'Sending "{local_file_path}" to "{flipper_file_path}"') - if not storage.send_file(local_file_path, flipper_file_path): - self.logger.error(f"Error: {storage.last_error}") - return False - return True + @staticmethod + def flatten(l): + return reduce(operator.concat, l, []) def install(self): - if not (port := resolve_port(self.logger, self.args.port)): + self.args.sources = self.flatten(self.args.sources) + self.args.targets = self.flatten(self.args.targets) + + if len(self.args.sources) != len(self.args.targets): + self.logger.error( + f"Error: sources ({self.args.sources}) and targets ({self.args.targets}) must be same length" + ) return 1 - storage = FlipperStorage(port) - storage.start() + if not (port := resolve_port(self.logger, self.args.port)): + return 2 try: - fap_local_path = self.args.fap_src_path - self.args.fap_dst_dir = self.args.fap_dst_dir.rstrip("/\\") + with FlipperStorage(port) as storage: + storage_ops = FlipperStorageOperations(storage) + for fap_local_path, fap_dst_path in zip( + self.args.sources, self.args.targets + ): + self.logger.info(f'Installing "{fap_local_path}" to {fap_dst_path}') - if not os.path.isfile(fap_local_path): - self.logger.error(f"Error: source .fap ({fap_local_path}) not found") - return -1 + storage_ops.recursive_send(fap_dst_path, fap_local_path, False) - fap_dst_path = posixpath.join( - self.args.fap_dst_dir, os.path.basename(fap_local_path) - ) + fap_host_app = self.args.targets[0] + startup_command = f'"Applications" {fap_host_app}' + if self.args.host_app: + startup_command = self.args.host_app - self.logger.info(f'Installing "{fap_local_path}" to {fap_dst_path}') + self.logger.info(f"Launching app: {startup_command}") + storage.send_and_wait_eol(f"loader open {startup_command}\r") - if not self.mkdir_on_storage(storage, self.args.fap_dst_dir): - self.logger.error(f"Error: cannot create dir: {storage.last_error}") - return -2 - - if not self.send_file_to_storage( - storage, fap_dst_path, fap_local_path, False - ): - self.logger.error(f"Error: upload failed: {storage.last_error}") - return -3 - - if self.args.launch_app: - storage.send_and_wait_eol( - f'loader open "Applications" {fap_dst_path}\r' - ) - result = storage.read.until(storage.CLI_EOL) - if len(result): + if len(result := storage.read.until(storage.CLI_EOL)): self.logger.error(f"Unexpected response: {result.decode('ascii')}") - return -4 + return 3 + return 0 - return 0 - finally: - storage.stop() + except Exception as e: + self.logger.error(f"Error: {e}") + # raise + return 4 if __name__ == "__main__": diff --git a/scripts/selfupdate.py b/scripts/selfupdate.py index 1c16c5ca..9bfbfefa 100644 --- a/scripts/selfupdate.py +++ b/scripts/selfupdate.py @@ -2,7 +2,7 @@ from typing import final from flipper.app import App -from flipper.storage import FlipperStorage +from flipper.storage import FlipperStorage, FlipperStorageOperations from flipper.utils.cdc import resolve_port import logging @@ -24,89 +24,47 @@ class Main(App): # logging self.logger = logging.getLogger() - # make directory with exist check - def mkdir_on_storage(self, storage, flipper_dir_path): - if not storage.exist_dir(flipper_dir_path): - self.logger.debug(f'"{flipper_dir_path}" does not exist, creating') - if not storage.mkdir(flipper_dir_path): - self.logger.error(f"Error: {storage.last_error}") - return False - else: - self.logger.debug(f'"{flipper_dir_path}" already exists') - return True - - # send file with exist check and hash check - def send_file_to_storage(self, storage, flipper_file_path, local_file_path, force): - exists = storage.exist_file(flipper_file_path) - do_upload = not exists - if exists: - hash_local = storage.hash_local(local_file_path) - hash_flipper = storage.hash_flipper(flipper_file_path) - self.logger.debug(f"hash check: local {hash_local}, flipper {hash_flipper}") - do_upload = force or (hash_local != hash_flipper) - - if do_upload: - self.logger.info(f'Sending "{local_file_path}" to "{flipper_file_path}"') - if not storage.send_file(local_file_path, flipper_file_path): - self.logger.error(f"Error: {storage.last_error}") - return False - return True - def install(self): if not (port := resolve_port(self.logger, self.args.port)): return 1 - storage = FlipperStorage(port) - storage.start() + if not os.path.isfile(self.args.manifest_path): + self.logger.error("Error: manifest not found") + return 2 + + manifest_path = pathlib.Path(os.path.abspath(self.args.manifest_path)) + manifest_name, pkg_name = manifest_path.parts[-1], manifest_path.parts[-2] + + pkg_dir_name = self.args.pkg_dir_name or pkg_name + update_root = "/ext/update" + flipper_update_path = f"{update_root}/{pkg_dir_name}" + + self.logger.info(f'Installing "{pkg_name}" from {flipper_update_path}') try: - if not os.path.isfile(self.args.manifest_path): - self.logger.error("Error: manifest not found") - return 2 + with FlipperStorage(port) as storage: + storage_ops = FlipperStorageOperations(storage) + storage_ops.mkpath(update_root) + storage_ops.mkpath(flipper_update_path) + storage_ops.recursive_send( + flipper_update_path, manifest_path.parents[0] + ) - manifest_path = pathlib.Path(os.path.abspath(self.args.manifest_path)) - manifest_name, pkg_name = manifest_path.parts[-1], manifest_path.parts[-2] - - pkg_dir_name = self.args.pkg_dir_name or pkg_name - update_root = "/ext/update" - flipper_update_path = f"{update_root}/{pkg_dir_name}" - - self.logger.info(f'Installing "{pkg_name}" from {flipper_update_path}') - # if not os.path.exists(self.args.manifest_path): - # self.logger.error("Error: package not found") - if not self.mkdir_on_storage( - storage, update_root - ) or not self.mkdir_on_storage(storage, flipper_update_path): - self.logger.error(f"Error: cannot create {storage.last_error}") - return -2 - - for dirpath, dirnames, filenames in os.walk(manifest_path.parents[0]): - for fname in filenames: - self.logger.debug(f"Uploading {fname}") - local_file_path = os.path.join(dirpath, fname) - flipper_file_path = f"{flipper_update_path}/{fname}" - if not self.send_file_to_storage( - storage, flipper_file_path, local_file_path, False - ): - self.logger.error(f"Error: {storage.last_error}") - return -3 - - # return -11 storage.send_and_wait_eol( f"update install {flipper_update_path}/{manifest_name}\r" ) result = storage.read.until(storage.CLI_EOL) if not b"Verifying" in result: self.logger.error(f"Unexpected response: {result.decode('ascii')}") - return -4 + return 3 result = storage.read.until(storage.CLI_EOL) if not result.startswith(b"OK"): self.logger.error(result.decode("ascii")) - return -5 - break - return 0 - finally: - storage.stop() + return 4 + return 0 + except Exception as e: + self.logger.error(e) + return 5 if __name__ == "__main__": diff --git a/scripts/storage.py b/scripts/storage.py index ee5dabd4..84c01021 100755 --- a/scripts/storage.py +++ b/scripts/storage.py @@ -1,16 +1,28 @@ #!/usr/bin/env python3 from flipper.app import App -from flipper.storage import FlipperStorage +from flipper.storage import FlipperStorage, FlipperStorageOperations from flipper.utils.cdc import resolve_port -import logging import os import binascii import filecmp import tempfile +def WrapStorageOp(func): + def wrapper(*args, **kwargs): + try: + func(*args, **kwargs) + return 0 + except Exception as e: + print(f"Error: {e}") + # raise # uncomment to debug + return 1 + + return wrapper + + class Main(App): def init(self): self.parser.add_argument("-p", "--port", help="CDC Port", default="auto") @@ -71,229 +83,71 @@ class Main(App): ) self.parser_stress.set_defaults(func=self.stress) - def _get_storage(self): + def _get_port(self): if not (port := resolve_port(self.logger, self.args.port)): - return None - - storage = FlipperStorage(port) - storage.start() - return storage + raise Exception("Failed to resolve port") + return port + @WrapStorageOp def mkdir(self): - if not (storage := self._get_storage()): - return 1 - self.logger.debug(f'Creating "{self.args.flipper_path}"') - if not storage.mkdir(self.args.flipper_path): - self.logger.error(f"Error: {storage.last_error}") - storage.stop() - return 0 + with FlipperStorage(self._get_port()) as storage: + storage.mkdir(self.args.flipper_path) + @WrapStorageOp def remove(self): - if not (storage := self._get_storage()): - return 1 - self.logger.debug(f'Removing "{self.args.flipper_path}"') - if not storage.remove(self.args.flipper_path): - self.logger.error(f"Error: {storage.last_error}") - storage.stop() - return 0 + with FlipperStorage(self._get_port()) as storage: + storage.remove(self.args.flipper_path) + @WrapStorageOp def receive(self): - if not (storage := self._get_storage()): - return 1 - - if storage.exist_dir(self.args.flipper_path): - for dirpath, dirnames, filenames in storage.walk(self.args.flipper_path): - self.logger.debug( - f'Processing directory "{os.path.normpath(dirpath)}"'.replace( - os.sep, "/" - ) - ) - dirnames.sort() - filenames.sort() - - rel_path = os.path.relpath(dirpath, self.args.flipper_path) - - for dirname in dirnames: - local_dir_path = os.path.join( - self.args.local_path, rel_path, dirname - ) - local_dir_path = os.path.normpath(local_dir_path) - os.makedirs(local_dir_path, exist_ok=True) - - for filename in filenames: - local_file_path = os.path.join( - self.args.local_path, rel_path, filename - ) - local_file_path = os.path.normpath(local_file_path) - flipper_file_path = os.path.normpath( - os.path.join(dirpath, filename) - ).replace(os.sep, "/") - self.logger.info( - f'Receiving "{flipper_file_path}" to "{local_file_path}"' - ) - if not storage.receive_file(flipper_file_path, local_file_path): - self.logger.error(f"Error: {storage.last_error}") - - else: - self.logger.info( - f'Receiving "{self.args.flipper_path}" to "{self.args.local_path}"' + with FlipperStorage(self._get_port()) as storage: + FlipperStorageOperations(storage).recursive_receive( + self.args.flipper_path, self.args.local_path ) - if not storage.receive_file(self.args.flipper_path, self.args.local_path): - self.logger.error(f"Error: {storage.last_error}") - storage.stop() - return 0 + @WrapStorageOp def send(self): - if not (storage := self._get_storage()): - return 1 - - self.send_to_storage( - storage, self.args.flipper_path, self.args.local_path, self.args.force - ) - storage.stop() - return 0 - - # send file or folder recursively - def send_to_storage(self, storage, flipper_path, local_path, force): - if not os.path.exists(local_path): - self.logger.error(f'Error: "{local_path}" is not exist') - - if os.path.isdir(local_path): - # create parent dir - self.mkdir_on_storage(storage, flipper_path) - - for dirpath, dirnames, filenames in os.walk(local_path): - self.logger.debug(f'Processing directory "{os.path.normpath(dirpath)}"') - dirnames.sort() - filenames.sort() - rel_path = os.path.relpath(dirpath, local_path) - - # create subdirs - for dirname in dirnames: - flipper_dir_path = os.path.join(flipper_path, rel_path, dirname) - flipper_dir_path = os.path.normpath(flipper_dir_path).replace( - os.sep, "/" - ) - self.mkdir_on_storage(storage, flipper_dir_path) - - # send files - for filename in filenames: - flipper_file_path = os.path.join(flipper_path, rel_path, filename) - flipper_file_path = os.path.normpath(flipper_file_path).replace( - os.sep, "/" - ) - local_file_path = os.path.normpath(os.path.join(dirpath, filename)) - self.send_file_to_storage( - storage, flipper_file_path, local_file_path, force - ) - else: - self.send_file_to_storage(storage, flipper_path, local_path, force) - - # make directory with exist check - def mkdir_on_storage(self, storage, flipper_dir_path): - if not storage.exist_dir(flipper_dir_path): - self.logger.debug(f'"{flipper_dir_path}" does not exist, creating') - if not storage.mkdir(flipper_dir_path): - self.logger.error(f"Error: {storage.last_error}") - else: - self.logger.debug(f'"{flipper_dir_path}" already exists') - - # send file with exist check and hash check - def send_file_to_storage(self, storage, flipper_file_path, local_file_path, force): - if not storage.exist_file(flipper_file_path): - self.logger.debug( - f'"{flipper_file_path}" does not exist, sending "{local_file_path}"' + with FlipperStorage(self._get_port()) as storage: + FlipperStorageOperations(storage).recursive_send( + self.args.flipper_path, self.args.local_path, self.args.force ) - self.logger.info(f'Sending "{local_file_path}" to "{flipper_file_path}"') - if not storage.send_file(local_file_path, flipper_file_path): - self.logger.error(f"Error: {storage.last_error}") - elif force: - self.logger.debug( - f'"{flipper_file_path}" exists, but will be overwritten by "{local_file_path}"' - ) - self.logger.info(f'Sending "{local_file_path}" to "{flipper_file_path}"') - if not storage.send_file(local_file_path, flipper_file_path): - self.logger.error(f"Error: {storage.last_error}") - else: - self.logger.debug( - f'"{flipper_file_path}" exists, compare hash with "{local_file_path}"' - ) - hash_local = storage.hash_local(local_file_path) - hash_flipper = storage.hash_flipper(flipper_file_path) - - if not hash_flipper: - self.logger.error(f"Error: {storage.last_error}") - - if hash_local == hash_flipper: - self.logger.debug( - f'"{flipper_file_path}" is equal to "{local_file_path}"' - ) - else: - self.logger.debug( - f'"{flipper_file_path}" is NOT equal to "{local_file_path}"' - ) - self.logger.info( - f'Sending "{local_file_path}" to "{flipper_file_path}"' - ) - if not storage.send_file(local_file_path, flipper_file_path): - self.logger.error(f"Error: {storage.last_error}") + @WrapStorageOp def read(self): - if not (storage := self._get_storage()): - return 1 - self.logger.debug(f'Reading "{self.args.flipper_path}"') - data = storage.read_file(self.args.flipper_path) - if not data: - self.logger.error(f"Error: {storage.last_error}") - else: + with FlipperStorage(self._get_port()) as storage: + data = storage.read_file(self.args.flipper_path) try: print("Text data:") print(data.decode()) except: print("Binary hexadecimal data:") print(binascii.hexlify(data).decode()) - storage.stop() - return 0 + @WrapStorageOp def size(self): - if not (storage := self._get_storage()): - return 1 - self.logger.debug(f'Getting size of "{self.args.flipper_path}"') - size = storage.size(self.args.flipper_path) - if size < 0: - self.logger.error(f"Error: {storage.last_error}") - else: - print(size) - storage.stop() - return 0 + with FlipperStorage(self._get_port()) as storage: + print(storage.size(self.args.flipper_path)) + @WrapStorageOp def list(self): - if not (storage := self._get_storage()): - return 1 - self.logger.debug(f'Listing "{self.args.flipper_path}"') - storage.list_tree(self.args.flipper_path) - storage.stop() - return 0 + with FlipperStorage(self._get_port()) as storage: + storage.list_tree(self.args.flipper_path) + @WrapStorageOp def format_ext(self): - if not (storage := self._get_storage()): - return 1 - self.logger.debug("Formatting /ext SD card") + with FlipperStorage(self._get_port()) as storage: + storage.format_ext() - if not storage.format_ext(): - self.logger.error(f"Error: {storage.last_error}") - storage.stop() - return 0 - + @WrapStorageOp def stress(self): self.logger.error("This test is wearing out flash memory.") - self.logger.error("Never use it with internal storage(/int)") + self.logger.error("Never use it with internal storage (/int)") if self.args.flipper_path.startswith( "/int" @@ -312,24 +166,19 @@ class Main(App): with open(send_file_name, "w") as fout: fout.write("A" * self.args.file_size) - storage = self._get_storage() - if not storage: - return 1 - - if storage.exist_file(self.args.flipper_path): - self.logger.error("File exists, remove it first") - return - while self.args.count > 0: - storage.send_file(send_file_name, self.args.flipper_path) - storage.receive_file(self.args.flipper_path, receive_file_name) - if not filecmp.cmp(receive_file_name, send_file_name): - self.logger.error("Files mismatch") - break - storage.remove(self.args.flipper_path) - os.unlink(receive_file_name) - self.args.count -= 1 - storage.stop() - return 0 + with FlipperStorage(self._get_port()) as storage: + if storage.exist_file(self.args.flipper_path): + self.logger.error("File exists, remove it first") + return + while self.args.count > 0: + storage.send_file(send_file_name, self.args.flipper_path) + storage.receive_file(self.args.flipper_path, receive_file_name) + if not filecmp.cmp(receive_file_name, send_file_name): + self.logger.error("Files mismatch") + break + storage.remove(self.args.flipper_path) + os.unlink(receive_file_name) + self.args.count -= 1 if __name__ == "__main__": diff --git a/site_scons/commandline.scons b/site_scons/commandline.scons index e3ddc59a..d832a466 100644 --- a/site_scons/commandline.scons +++ b/site_scons/commandline.scons @@ -194,10 +194,6 @@ vars.AddVariables( "system_apps", # Settings "settings_apps", - # Plugins - # "basic_plugins", - # Debug - # "debug_apps", ), }, ), @@ -222,7 +218,7 @@ vars.AddVariables( ("applications/settings", False), ("applications/system", False), ("applications/debug", False), - ("applications/plugins", False), + ("applications/external", False), ("applications/examples", False), ("applications_user", False), ], diff --git a/site_scons/extapps.scons b/site_scons/extapps.scons index abe1a453..208b7577 100644 --- a/site_scons/extapps.scons +++ b/site_scons/extapps.scons @@ -1,7 +1,9 @@ from dataclasses import dataclass, field +from os.path import dirname + from SCons.Node import NodeList from SCons.Warnings import warn, WarningOnByDefault - +from SCons.Errors import UserError Import("ENV") @@ -12,7 +14,8 @@ appenv = ENV["APPENV"] = ENV.Clone( "fbt_extapps", "fbt_assets", "fbt_sdk", - ] + ], + RESOURCES_ROOT=ENV.Dir("#/assets/resources"), ) appenv.Replace( @@ -57,7 +60,7 @@ appenv.AppendUnique( @dataclass class FlipperExtAppBuildArtifacts: - applications: dict = field(default_factory=dict) + application_map: dict = field(default_factory=dict) resources_dist: NodeList = field(default_factory=NodeList) sdk_tree: NodeList = field(default_factory=NodeList) @@ -86,6 +89,9 @@ for app in known_extapps: appenv.BuildAppElf(app) +extapps = FlipperExtAppBuildArtifacts() +extapps.application_map = appenv["EXT_APPS"] + if incompatible_apps: warn( WarningOnByDefault, @@ -95,27 +101,60 @@ if incompatible_apps: if appenv["FORCE"]: appenv.AlwaysBuild( - list(app_artifact.compact for app_artifact in appenv["EXT_APPS"].values()) + list(app_artifact.compact for app_artifact in extapps.application_map.values()) ) Alias( - "faps", list(app_artifact.validator for app_artifact in appenv["EXT_APPS"].values()) + "faps", + list(app_artifact.validator for app_artifact in extapps.application_map.values()), ) -extapps = FlipperExtAppBuildArtifacts() -extapps.applications = appenv["EXT_APPS"] -extapps.resources_dist = appenv.FapDist(appenv.Dir("#/assets/resources/apps"), []) +extapps.resources_dist = appenv.FapDist(appenv["RESOURCES_ROOT"], []) if appsrc := appenv.subst("$APPSRC"): - app_artifacts = appenv.GetExtAppFromPath(appsrc) + deploy_sources, flipp_dist_paths, validators = [], [], [] + run_script_extra_ars = "" + + def _add_dist_targets(app_artifacts): + validators.append(app_artifacts.validator) + for _, ext_path in app_artifacts.dist_entries: + deploy_sources.append(app_artifacts.compact) + flipp_dist_paths.append(f"/ext/{ext_path}") + return app_artifacts + + def _add_host_app_to_targets(host_app): + artifacts_app_to_run = appenv["EXT_APPS"].get(host_app.appid, None) + _add_dist_targets(artifacts_app_to_run) + for plugin in host_app._plugins: + _add_dist_targets(appenv["EXT_APPS"].get(plugin.appid, None)) + + artifacts_app_to_run = appenv.GetExtAppByIdOrPath(appsrc) + if artifacts_app_to_run.app.apptype == FlipperAppType.PLUGIN: + # We deploy host app instead + host_app = appenv["APPMGR"].get(artifacts_app_to_run.app.requires[0]) + + if host_app: + if host_app.apptype == FlipperAppType.EXTERNAL: + _add_host_app_to_targets(host_app) + else: + # host app is a built-in app + run_script_extra_ars = f"-a {host_app.name}" + _add_dist_targets(artifacts_app_to_run) + else: + raise UserError("Host app is unknown") + else: + _add_host_app_to_targets(artifacts_app_to_run.app) + + # print(deploy_sources, flipp_dist_paths) appenv.PhonyTarget( "launch_app", - '${PYTHON3} "${APP_RUN_SCRIPT}" "${SOURCE}" --fap_dst_dir "/ext/apps/${FAP_CATEGORY}"', - source=app_artifacts.compact, - FAP_CATEGORY=app_artifacts.app.fap_category, + '${PYTHON3} "${APP_RUN_SCRIPT}" ${EXTRA_ARGS} -s ${SOURCES} -t ${FLIPPER_FILE_TARGETS}', + source=deploy_sources, + FLIPPER_FILE_TARGETS=flipp_dist_paths, + EXTRA_ARGS=run_script_extra_ars, ) - appenv.Alias("launch_app", app_artifacts.validator) + appenv.Alias("launch_app", validators) # SDK management