diff --git a/ctru-rs/examples/title-info.rs b/ctru-rs/examples/title-info.rs index 5bb6e76..f4231a0 100644 --- a/ctru-rs/examples/title-info.rs +++ b/ctru-rs/examples/title-info.rs @@ -59,18 +59,18 @@ fn main() { } if refresh { - let mut selected_title = 0; + let mut selected_title = cur_list.iter().skip(offset).next().unwrap(); // Clear top screen and write title ids to it top_screen.select(); print!("\x1b[2J"); // Top screen seems to have only 30 rows - for (i, id) in cur_list.iter().skip(offset).take(29).enumerate() { + for (i, title) in cur_list.iter().skip(offset).take(29).enumerate() { if i == 0 { - selected_title = *id; - println!("=> {id:x}"); + selected_title = title; + println!("=> {:x}", title.id()); } else { - println!(" {id:x}"); + println!(" {:x}", title.id()); } } @@ -79,21 +79,19 @@ fn main() { println!("\x1b[2J"); // Move cursor to top left println!("\x1b[1;1"); - let media = if use_nand { - FsMediaType::Nand - } else { - FsMediaType::Sd - }; - match am.get_title_info(media, &mut [selected_title]) { + + match selected_title.get_title_info() { Ok(info) => { - // Vec returned by Am::get_title_info always has same length as inputed slice - let info = info[0]; println!("Size: {} KB", info.size_bytes() / 1024); println!("Version: 0x{:x}", info.version()); println!("Type: 0x{:x}", info.type_()); } Err(e) => println!("Failed to get title info: {}", e), } + match selected_title.get_product_code() { + Ok(code) => println!("Product code: \"{code}\""), + Err(e) => println!("Failed to get product code: {}", e), + } println!("\x1b[26;0HPress START to exit"); if use_nand { diff --git a/ctru-rs/src/services/am.rs b/ctru-rs/src/services/am.rs index 89cc19a..c540a05 100644 --- a/ctru-rs/src/services/am.rs +++ b/ctru-rs/src/services/am.rs @@ -1,5 +1,7 @@ use crate::error::ResultCode; use crate::services::fs::FsMediaType; +use std::marker::PhantomData; +use std::mem::MaybeUninit; #[derive(Copy, Clone, Debug)] #[repr(C)] @@ -29,6 +31,46 @@ impl TitleInfo { } } +pub struct Title<'a> { + id: u64, + mediatype: FsMediaType, + _am: PhantomData<&'a Am>, +} + +impl<'a> Title<'a> { + pub fn id(&self) -> u64 { + self.id + } + + pub fn get_product_code(&self) -> crate::Result { + let mut buf: [u8; 16] = [0; 16]; + + unsafe { + ResultCode(ctru_sys::AM_GetTitleProductCode( + self.mediatype as u32, + self.id, + buf.as_mut_ptr(), + ))?; + } + Ok(String::from_utf8_lossy(&buf).to_string()) + } + + pub fn get_title_info(&self) -> crate::Result { + let mut info = MaybeUninit::zeroed(); + + unsafe { + ResultCode(ctru_sys::AM_GetTitleInfo( + self.mediatype as u32, + 1, + &mut self.id.clone(), + info.as_mut_ptr() as _, + ))?; + + Ok(info.assume_init()) + } + } +} + pub struct Am(()); impl Am { @@ -47,7 +89,7 @@ impl Am { } } - pub fn get_title_list(&self, mediatype: FsMediaType) -> crate::Result> { + pub fn get_title_list(&self, mediatype: FsMediaType) -> crate::Result> { let count = self.get_title_count(mediatype)?; let mut buf = Vec::with_capacity(count as usize); let mut read_amount = 0; @@ -61,26 +103,14 @@ impl Am { buf.set_len(read_amount as usize); } - Ok(buf) - } - - pub fn get_title_info( - &self, - mediatype: FsMediaType, - id_list: &mut [u64], - ) -> crate::Result> { - let mut info = Vec::with_capacity(id_list.len()); - unsafe { - ResultCode(ctru_sys::AM_GetTitleInfo( - mediatype as u32, - id_list.len() as u32, - id_list.as_mut_ptr(), - info.as_mut_ptr() as _, - ))?; - - info.set_len(id_list.len()); - } - Ok(info) + Ok(buf + .into_iter() + .map(|id| Title { + id, + mediatype, + _am: PhantomData, + }) + .collect()) } }