flipperzero_rt/
manifest.rs

1//! Flipper Zero Manifest.
2
3use core::ffi::c_char;
4
5use flipperzero_sys as sys;
6
7const MANIFEST_MAGIC: u32 = 0x52474448;
8const HARDWARE_TARGET: u16 = 7;
9const DEFAULT_STACK_SIZE: u16 = 2048; // 2 KiB
10
11/// Define application manifest.
12///
13/// # Examples
14///
15/// ```
16/// # use flipperzero_rt::manifest;
17/// manifest!(
18///     name = "MyApp",
19///     stack_size = 1024,
20///     app_version = 1,
21///     has_icon = true,
22///     icon: "app.icon",
23/// );
24/// ```
25#[macro_export]
26macro_rules! manifest {
27    ($($field:ident = $value:expr),* $(,)?) => {
28        #[unsafe(no_mangle)]
29        #[unsafe(link_section = ".fapmeta")]
30        static FAP_MANIFEST: $crate::manifest::ApplicationManifestV1 = $crate::manifest::ApplicationManifestV1 {
31            $( $field: $crate::_manifest_field!($field = $value), )*
32            .. $crate::manifest::ApplicationManifestV1::default()
33        };
34    };
35}
36
37#[macro_export]
38#[doc(hidden)]
39macro_rules! _manifest_field {
40    (stack_size = $value:expr) => {
41        $value
42    };
43    (app_version = $value:expr) => {
44        $value
45    };
46    (name = $value:expr) => {
47        $crate::manifest::_padded($value.as_bytes())
48    };
49    (has_icon = $value:expr) => {
50        $value as core::ffi::c_char
51    };
52    (icon = $value:expr) => {
53        $crate::manifest::_padded(include_bytes!($value))
54    };
55}
56
57#[repr(C, packed)]
58pub struct ManifestBase {
59    pub manifest_magic: u32,
60    pub manifest_version: u32,
61    pub api_version: u32,
62    pub hardware_target_id: u16,
63}
64
65/// Application Manifest (version 1).
66#[repr(C, packed)]
67pub struct ApplicationManifestV1 {
68    pub base: ManifestBase,
69    pub stack_size: u16,
70    pub app_version: u32,
71    pub name: [u8; 32],
72    pub has_icon: c_char,
73    pub icon: [u8; 32],
74}
75
76impl ApplicationManifestV1 {
77    /// Default manifest.
78    pub const fn default() -> Self {
79        Self {
80            base: ManifestBase {
81                manifest_magic: MANIFEST_MAGIC,
82                manifest_version: 1,
83                api_version: sys::API_VERSION,
84                hardware_target_id: HARDWARE_TARGET,
85            },
86            stack_size: DEFAULT_STACK_SIZE,
87            app_version: 1,
88            name: _padded(b"Application"),
89            has_icon: 0,
90            icon: [0; 32],
91        }
92    }
93}
94
95/// Pads an array with NULL bytes.
96/// Ensures that there's always a NULL byte at the end.
97#[doc(hidden)]
98pub const fn _padded<const SIZE: usize>(value: &[u8]) -> [u8; SIZE] {
99    let mut array: [u8; SIZE] = [0; SIZE];
100
101    let mut i = 0;
102    while i < array.len() - 1 && i < value.len() {
103        array[i] = value[i];
104        i += 1;
105    }
106    array[i] = 0;
107
108    array
109}