diff --git a/ctru-rs/examples/title-info.rs b/ctru-rs/examples/title-info.rs index 0faaadd..5bb6e76 100644 --- a/ctru-rs/examples/title-info.rs +++ b/ctru-rs/examples/title-info.rs @@ -9,19 +9,26 @@ fn main() { let hid = Hid::init().expect("Couldn't obtain HID controller"); let apt = Apt::init().expect("Couldn't obtain APT controller"); let am = Am::init().expect("Couldn't obtain AM controller"); - let _console = Console::init(gfx.top_screen.borrow_mut()); + let top_screen = Console::init(gfx.top_screen.borrow_mut()); + let bottom_screen = Console::init(gfx.bottom_screen.borrow_mut()); - let title_count = am + let sd_count = am .get_title_count(FsMediaType::Sd) - .expect("Failed to get title count"); - println!("This 3DS has {title_count} titles on its SD Card:"); - - let title_list = am + .expect("Failed to get sd title count"); + let sd_list = am .get_title_list(FsMediaType::Sd) - .expect("Failed to get title list"); - for id in title_list { - println!("{id:x}"); - } + .expect("Failed to get sd title list"); + + let nand_count = am + .get_title_count(FsMediaType::Nand) + .expect("Failed to get nand title count"); + let nand_list = am + .get_title_list(FsMediaType::Nand) + .expect("Failed to get nand title list"); + + let mut offset = 0; + let mut refresh = true; + let mut use_nand = false; // Main loop while apt.main_loop() { @@ -31,6 +38,77 @@ fn main() { if hid.keys_down().contains(KeyPad::KEY_START) { break; } + if hid.keys_down().contains(KeyPad::KEY_SELECT) { + refresh = true; + offset = 0; + use_nand = !use_nand; + } + + let cur_list = if use_nand { &nand_list } else { &sd_list }; + + if hid.keys_down().intersects(KeyPad::KEY_DOWN) { + if offset + 1 < cur_list.len() { + offset = offset + 1; + refresh = true; + } + } else if hid.keys_down().intersects(KeyPad::KEY_UP) { + if offset > 0 { + offset = offset - 1; + refresh = true; + } + } + + if refresh { + let mut selected_title = 0; + // 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() { + if i == 0 { + selected_title = *id; + println!("=> {id:x}"); + } else { + println!(" {id:x}"); + } + } + + // Clear bottom screen and write properties of selected title to it + bottom_screen.select(); + 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]) { + 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), + } + + println!("\x1b[26;0HPress START to exit"); + if use_nand { + println!("Press SELECT to choose SD Card"); + println!("Current medium: NAND"); + println!("Title count: {}", nand_count); + } else { + println!("Press SELECT to choose NAND"); + println!("Current medium: SD Card"); + println!("Title count: {}", sd_count); + } + + refresh = false; + } + // Flush and swap framebuffers gfx.flush_buffers(); gfx.swap_buffers(); diff --git a/ctru-rs/src/services/am.rs b/ctru-rs/src/services/am.rs index cde4724..89cc19a 100644 --- a/ctru-rs/src/services/am.rs +++ b/ctru-rs/src/services/am.rs @@ -1,6 +1,34 @@ use crate::error::ResultCode; use crate::services::fs::FsMediaType; +#[derive(Copy, Clone, Debug)] +#[repr(C)] +pub struct TitleInfo { + id: u64, + size: u64, + version: u16, + pad: u16, + type_: u32, +} + +// Make sure TitleInfo is correct size +const _TITLEINFO_SIZE_CHECK: [u8; 0x18] = [0; std::mem::size_of::()]; + +impl TitleInfo { + pub fn id(&self) -> u64 { + self.id + } + pub fn size_bytes(&self) -> u64 { + self.size + } + pub fn version(&self) -> u16 { + self.version + } + pub fn type_(&self) -> u32 { + self.type_ + } +} + pub struct Am(()); impl Am { @@ -20,11 +48,10 @@ impl Am { } 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; unsafe { - let count = self.get_title_count(mediatype)?; - let mut buf = Vec::with_capacity(count as usize); - let mut read_amount = 0; - ResultCode(ctru_sys::AM_GetTitleList( &mut read_amount, mediatype as u32, @@ -33,8 +60,27 @@ impl Am { ))?; buf.set_len(read_amount as usize); - Ok(buf) } + 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) } }