ctru/services/
am.rs

1//! Application Manager service.
2//!
3//! As the name implies, the AM service manages installed applications. It can:
4//! - Read the installed applications on the console and their information (depending on the install location).
5//! - Install compatible applications to the console.
6//!
7//! TODO: [`ctru-rs`](crate) doesn't support installing or uninstalling titles yet.
8#![doc(alias = "app")]
9#![doc(alias = "manager")]
10
11use crate::error::ResultCode;
12use crate::services::fs::MediaType;
13use std::marker::PhantomData;
14
15/// General information about a specific title entry.
16#[doc(alias = "AM_TitleEntry")]
17pub struct Title<'a> {
18    id: u64,
19    mediatype: MediaType,
20    size: u64,
21    version: u16,
22    _am: PhantomData<&'a Am>,
23}
24
25impl<'a> Title<'a> {
26    /// Returns this title's ID.
27    pub fn id(&self) -> u64 {
28        self.id
29    }
30
31    /// Returns this title's unique product code.
32    #[doc(alias = "AM_GetTitleProductCode")]
33    pub fn product_code(&self) -> String {
34        let mut buf: [u8; 16] = [0; 16];
35
36        // This operation is safe as long as the title was correctly obtained via [`Am::title_list()`].
37        unsafe {
38            let _ =
39                ctru_sys::AM_GetTitleProductCode(self.mediatype.into(), self.id, buf.as_mut_ptr());
40        }
41
42        String::from_utf8_lossy(&buf).to_string()
43    }
44
45    /// Returns the size of this title in bytes.
46    pub fn size(&self) -> u64 {
47        self.size
48    }
49
50    /// Returns the installed version of this title.
51    pub fn version(&self) -> u16 {
52        self.version
53    }
54
55    /// Returns this title's media type
56    pub fn media_type(&self) -> MediaType {
57        self.mediatype
58    }
59}
60
61/// Handle to the Application Manager service.
62pub struct Am(());
63
64impl Am {
65    /// Initialize a new service handle.
66    ///
67    /// # Example
68    ///
69    /// ```
70    /// # let _runner = test_runner::GdbRunner::default();
71    /// # use std::error::Error;
72    /// # fn main() -> Result<(), Box<dyn Error>> {
73    /// #
74    /// use ctru::services::am::Am;
75    ///
76    /// let app_manager = Am::new()?;
77    /// #
78    /// # Ok(())
79    /// # }
80    /// ```
81    #[doc(alias = "amInit")]
82    pub fn new() -> crate::Result<Am> {
83        unsafe {
84            ResultCode(ctru_sys::amInit())?;
85            Ok(Am(()))
86        }
87    }
88
89    /// Returns the amount of titles currently installed in a specific install location.
90    ///
91    /// # Example
92    ///
93    /// ```
94    /// # let _runner = test_runner::GdbRunner::default();
95    /// # use std::error::Error;
96    /// # fn main() -> Result<(), Box<dyn Error>> {
97    /// #
98    /// use ctru::services::am::Am;
99    /// use ctru::services::fs::MediaType;
100    /// let app_manager = Am::new()?;
101    ///
102    /// // Number of titles installed on the Nand storage.
103    /// let nand_count = app_manager.title_count(MediaType::Nand);
104    ///
105    /// // Number of apps installed on the SD card storage
106    /// let sd_count = app_manager.title_count(MediaType::Sd);
107    /// #
108    /// # Ok(())
109    /// # }
110    /// ```
111    #[doc(alias = "AM_GetTitleCount")]
112    pub fn title_count(&self, mediatype: MediaType) -> crate::Result<u32> {
113        unsafe {
114            let mut count = 0;
115            ResultCode(ctru_sys::AM_GetTitleCount(mediatype.into(), &mut count))?;
116            Ok(count)
117        }
118    }
119
120    /// Returns the list of titles installed in a specific install location.
121    ///
122    /// # Example
123    ///
124    /// ```
125    /// # let _runner = test_runner::GdbRunner::default();
126    /// # use std::error::Error;
127    /// # fn main() -> Result<(), Box<dyn Error>> {
128    /// #
129    /// use ctru::services::am::Am;
130    /// use ctru::services::fs::MediaType;
131    /// let app_manager = Am::new()?;
132    ///
133    /// // Number of apps installed on the SD card storage
134    /// let sd_titles = app_manager.title_list(MediaType::Sd)?;
135    ///
136    /// // Unique product code identifier of the 5th installed title.
137    /// let product_code = sd_titles[4].product_code();
138    /// #
139    /// # Ok(())
140    /// # }
141    /// ```
142    #[doc(alias = "AM_GetTitleList")]
143    pub fn title_list(&self, mediatype: MediaType) -> crate::Result<Vec<Title<'_>>> {
144        let count = self.title_count(mediatype)?;
145        let mut buf = vec![0; count as usize];
146        let mut read_amount = 0;
147
148        unsafe {
149            ResultCode(ctru_sys::AM_GetTitleList(
150                &mut read_amount,
151                mediatype.into(),
152                count,
153                buf.as_mut_ptr(),
154            ))?;
155        }
156
157        let mut info: Vec<ctru_sys::AM_TitleEntry> = Vec::with_capacity(count as _);
158
159        unsafe {
160            ResultCode(ctru_sys::AM_GetTitleInfo(
161                mediatype.into(),
162                count,
163                buf.as_mut_ptr(),
164                info.as_mut_ptr() as _,
165            ))?;
166
167            info.set_len(count as _);
168        };
169
170        Ok(info
171            .into_iter()
172            .map(|title| Title {
173                id: title.titleID,
174                mediatype,
175                size: title.size,
176                version: title.version,
177                _am: PhantomData,
178            })
179            .collect())
180    }
181}
182
183impl Drop for Am {
184    #[doc(alias = "amExit")]
185    fn drop(&mut self) {
186        unsafe { ctru_sys::amExit() };
187    }
188}