Browse Source

Small changes and finalized AM service

pull/134/head
Andrea Ciliberti 1 year ago
parent
commit
127eafdc4b
  1. 15
      ctru-rs/examples/title-info.rs
  2. 5
      ctru-rs/src/lib.rs
  3. 136
      ctru-rs/src/services/am.rs
  4. 2
      ctru-rs/src/services/mod.rs

15
ctru-rs/examples/title-info.rs

@ -80,18 +80,9 @@ fn main() {
// Move cursor to top left // Move cursor to top left
println!("\x1b[1;1"); println!("\x1b[1;1");
match selected_title.size() { println!("Size: {} kB", selected_title.size() / 1024);
Ok(size) => println!("Size: {} kB", size / 1024), println!("Version: 0x{:x}", selected_title.version());
Err(e) => println!("Failed to get title size: {}", e), println!("Product code: \"{}\"", selected_title.product_code());
}
match selected_title.version() {
Ok(version) => println!("Version: 0x{:x}", version),
Err(e) => println!("Failed to get title version: {}", e),
}
match selected_title.product_code() {
Ok(code) => println!("Product code: \"{code}\""),
Err(e) => println!("Failed to get product code: {}", e),
}
println!("\x1b[26;0HPress START to exit"); println!("\x1b[26;0HPress START to exit");
if use_nand { if use_nand {

5
ctru-rs/src/lib.rs

@ -4,14 +4,14 @@
//! //!
//! This crate behaves as the main tool to access system-specific functionality on the Nintendo 3DS when developing homebrew software in Rust. //! This crate behaves as the main tool to access system-specific functionality on the Nintendo 3DS when developing homebrew software in Rust.
//! Thanks to it, developers can access the underlying system services and the console's hardware to develop userland applications //! Thanks to it, developers can access the underlying system services and the console's hardware to develop userland applications
//! (such as HID devices, network capabilities, graphics, built-in cameras, etc.). //! (such as [HID devices](crate::services::hid), [network capabilities](crate::services::soc), [graphics](crate::services::gfx), [built-in cameras](crate::services::cam), etc.).
//! //!
//! Among these features, `ctru-rs` also automatically includes functionality to properly integrate the Rust `std` with the console's operating system, //! Among these features, `ctru-rs` also automatically includes functionality to properly integrate the Rust `std` with the console's operating system,
//! which the developer would otherwise need to implement manually. //! which the developer would otherwise need to implement manually.
//! //!
//! # Usage //! # Usage
//! //!
//! Read thoroughly the official [`ctru-rs` wiki](https://github.com/rust3ds/ctru-rs/wiki) which guides you through the setup needed to install the required toolchain and helpful tools. //! Thoroughly read the official [`ctru-rs` wiki](https://github.com/rust3ds/ctru-rs/wiki) which guides you through the setup needed to install the required toolchain and helpful tools.
//! After following the guide and understanding the many quirks of the Nintendo 3DS homebrew development environment, you can create a new project by including this crate as a dependency //! After following the guide and understanding the many quirks of the Nintendo 3DS homebrew development environment, you can create a new project by including this crate as a dependency
//! of your project in your `Cargo.toml` manifest and build your binaries either manually (for the `armv6k-nintendo-3ds` target) or via [`cargo-3ds`](https://github.com/rust3ds/cargo-3ds). //! of your project in your `Cargo.toml` manifest and build your binaries either manually (for the `armv6k-nintendo-3ds` target) or via [`cargo-3ds`](https://github.com/rust3ds/cargo-3ds).
@ -21,7 +21,6 @@
#![feature(test)] #![feature(test)]
#![feature(custom_test_frameworks)] #![feature(custom_test_frameworks)]
#![feature(try_trait_v2)] #![feature(try_trait_v2)]
#![feature(once_cell_try)]
#![feature(allocator_api)] #![feature(allocator_api)]
#![test_runner(test_runner::run)] #![test_runner(test_runner::run)]
#![doc( #![doc(

136
ctru-rs/src/services/am.rs

@ -4,20 +4,19 @@
//! - Read the installed applications on the console and their information (depending on the install location). //! - Read the installed applications on the console and their information (depending on the install location).
//! - Install compatible applications to the console. //! - Install compatible applications to the console.
//! //!
//! `ctru-rs` doesn't support installing titles (yet). //! TODO: `ctru-rs` doesn't support installing or uninstalling titles yet.
use crate::error::ResultCode; use crate::error::ResultCode;
use crate::services::fs::FsMediaType; use crate::services::fs::FsMediaType;
use std::cell::OnceCell;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::mem::MaybeUninit;
/// Struct holding general information about a specific title. /// General information about a specific title entry.
#[doc(alias = "AM_TitleEntry")] #[doc(alias = "AM_TitleEntry")]
pub struct Title<'a> { pub struct Title<'a> {
id: u64, id: u64,
mediatype: FsMediaType, mediatype: FsMediaType,
entry: OnceCell<ctru_sys::AM_TitleEntry>, size: u64,
version: u16,
_am: PhantomData<&'a Am>, _am: PhantomData<&'a Am>,
} }
@ -29,54 +28,26 @@ impl<'a> Title<'a> {
/// Returns this title's unique product code. /// Returns this title's unique product code.
#[doc(alias = "AM_GetTitleProductCode")] #[doc(alias = "AM_GetTitleProductCode")]
pub fn product_code(&self) -> crate::Result<String> { pub fn product_code(&self) -> String {
let mut buf: [u8; 16] = [0; 16]; let mut buf: [u8; 16] = [0; 16];
// This operation is safe as long as the title was correctly obtained via [`Am::title_list()`].
unsafe { unsafe {
ResultCode(ctru_sys::AM_GetTitleProductCode( let _ =
self.mediatype.into(), ctru_sys::AM_GetTitleProductCode(self.mediatype.into(), self.id, buf.as_mut_ptr());
self.id,
buf.as_mut_ptr(),
))?;
} }
Ok(String::from_utf8_lossy(&buf).to_string())
}
/// Retrieves additional information on the title.
#[doc(alias = "AM_GetTitleInfo")]
fn title_info(&self) -> crate::Result<ctru_sys::AM_TitleEntry> {
let mut info = MaybeUninit::zeroed();
unsafe {
ResultCode(ctru_sys::AM_GetTitleInfo(
self.mediatype.into(),
1,
&mut self.id.clone(),
info.as_mut_ptr() as _,
))?;
Ok(info.assume_init()) String::from_utf8_lossy(&buf).to_string()
}
} }
/// Returns the size of this title in bytes. /// Returns the size of this title in bytes.
pub fn size(&self) -> crate::Result<u64> { pub fn size(&self) -> u64 {
// Get the internal entry, or fill it if empty. self.size
let entry = self
.entry
.get_or_try_init(|| -> crate::Result<ctru_sys::AM_TitleEntry> { self.title_info() })?;
Ok(entry.size)
} }
/// Returns the installed version of this title. /// Returns the installed version of this title.
pub fn version(&self) -> crate::Result<u16> { pub fn version(&self) -> u16 {
// Get the internal entry, or fill it if empty. self.version
let entry = self
.entry
.get_or_try_init(|| -> crate::Result<ctru_sys::AM_TitleEntry> { self.title_info() })?;
Ok(entry.version)
} }
} }
@ -84,7 +55,20 @@ impl<'a> Title<'a> {
pub struct Am(()); pub struct Am(());
impl Am { impl Am {
/// Initialize a new handle. /// Initialize a new service handle.
///
/// # Example
/// ```no_run
/// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> {
/// #
/// use ctru::services::am::Am;
///
/// let app_manager = Am::new()?;
/// #
/// # Ok(())
/// # }
/// ```
#[doc(alias = "amInit")] #[doc(alias = "amInit")]
pub fn new() -> crate::Result<Am> { pub fn new() -> crate::Result<Am> {
unsafe { unsafe {
@ -94,6 +78,24 @@ impl Am {
} }
/// Returns the amount of titles currently installed in a specific install location. /// Returns the amount of titles currently installed in a specific install location.
///
/// # Example
/// ```no_run
/// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> {
/// #
/// use ctru::services::{fs::FsMediaType, am::Am};
/// let app_manager = Am::new()?;
///
/// // Number of titles installed on the Nand storage.
/// let nand_count = app_manager.title_count(FsMediaType::Nand);
///
/// // Number of apps installed on the SD card storage
/// let sd_count = app_manager.title_count(FsMediaType::Sd);
/// #
/// # Ok(())
/// # }
/// ```
#[doc(alias = "AM_GetTitleCount")] #[doc(alias = "AM_GetTitleCount")]
pub fn title_count(&self, mediatype: FsMediaType) -> crate::Result<u32> { pub fn title_count(&self, mediatype: FsMediaType) -> crate::Result<u32> {
unsafe { unsafe {
@ -104,11 +106,30 @@ impl Am {
} }
/// Returns the list of titles installed in a specific install location. /// Returns the list of titles installed in a specific install location.
///
/// # Example
/// ```no_run
/// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> {
/// #
/// use ctru::services::{fs::FsMediaType, am::Am};
/// let app_manager = Am::new()?;
///
/// // Number of apps installed on the SD card storage
/// let sd_titles = app_manager.title_list(FsMediaType::Sd)?;
///
/// // Unique product code identifier of the 5th installed title.
/// let product_code = sd_titles[4].product_code();
/// #
/// # Ok(())
/// # }
/// ```
#[doc(alias = "AM_GetTitleList")] #[doc(alias = "AM_GetTitleList")]
pub fn title_list(&self, mediatype: FsMediaType) -> crate::Result<Vec<Title>> { pub fn title_list(&self, mediatype: FsMediaType) -> crate::Result<Vec<Title>> {
let count = self.title_count(mediatype)?; let count = self.title_count(mediatype)?;
let mut buf = vec![0; count as usize]; let mut buf = vec![0; count as usize];
let mut read_amount = 0; let mut read_amount = 0;
unsafe { unsafe {
ResultCode(ctru_sys::AM_GetTitleList( ResultCode(ctru_sys::AM_GetTitleList(
&mut read_amount, &mut read_amount,
@ -117,13 +138,30 @@ impl Am {
buf.as_mut_ptr(), buf.as_mut_ptr(),
))?; ))?;
} }
Ok(buf
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() .into_iter()
.map(|id| Title { .map(|title| {
id, Title {
mediatype, id: title.titleID,
entry: OnceCell::new(), mediatype,
_am: PhantomData, size: title.size,
version: title.version,
_am: PhantomData,
}
}) })
.collect()) .collect())
} }

2
ctru-rs/src/services/mod.rs

@ -3,7 +3,7 @@
//! Most of the 3DS console's functionalities (when writing user-land homebrew) are accessible via services, //! Most of the 3DS console's functionalities (when writing user-land homebrew) are accessible via services,
//! which need to be initialized before accessing any particular feature. //! which need to be initialized before accessing any particular feature.
//! //!
//! To ensure safety measures when using the underlying services, `ctru-rs` leverages Rust's lifetime model. //! To ensure safety while using the underlying services, `ctru-rs` leverages Rust's lifetime model.
//! After initializing the handle for a specific service (e.g. [`Apt`](apt::Apt)) the service will be accessible as long as there is at least one handle "alive". //! After initializing the handle for a specific service (e.g. [`Apt`](apt::Apt)) the service will be accessible as long as there is at least one handle "alive".
//! As such, handles should be dropped *after* the use of a specific service. This is particularly important for services which are necessary for functionality //! As such, handles should be dropped *after* the use of a specific service. This is particularly important for services which are necessary for functionality
//! "outside" their associated methods, such as [`RomFS`](romfs::RomFS), which creates an accessible virtual filesystem, or [`Soc`](soc::Soc), //! "outside" their associated methods, such as [`RomFS`](romfs::RomFS), which creates an accessible virtual filesystem, or [`Soc`](soc::Soc),

Loading…
Cancel
Save