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}