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