Browse Source

Merge pull request #95 from Maccraft123/feature/am

Add a basic AM service wrapper and an example that uses it
pull/105/head
Meziu 2 years ago committed by GitHub
parent
commit
0b54ffd0b8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 116
      ctru-rs/examples/title-info.rs
  2. 106
      ctru-rs/src/services/am.rs
  3. 8
      ctru-rs/src/services/fs.rs
  4. 1
      ctru-rs/src/services/mod.rs

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

@ -0,0 +1,116 @@
use ctru::prelude::*;
use ctru::services::am::Am;
use ctru::services::fs::FsMediaType;
fn main() {
ctru::use_panic_handler();
let gfx = Gfx::init().expect("Couldn't obtain GFX controller");
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 top_screen = Console::init(gfx.top_screen.borrow_mut());
let bottom_screen = Console::init(gfx.bottom_screen.borrow_mut());
let sd_count = am
.get_title_count(FsMediaType::Sd)
.expect("Failed to get sd title count");
let sd_list = am
.get_title_list(FsMediaType::Sd)
.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() {
//Scan all the inputs. This should be done once for each frame
hid.scan_input();
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 = 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, title) in cur_list.iter().skip(offset).take(29).enumerate() {
if i == 0 {
selected_title = title;
println!("=> {:x}", title.id());
} else {
println!(" {:x}", title.id());
}
}
// 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");
match selected_title.get_title_info() {
Ok(info) => {
println!("Size: {} KB", info.size_bytes() / 1024);
println!("Version: 0x{:x}", info.version());
}
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 {
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();
//Wait for VBlank
gfx.wait_for_vblank();
}
}

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

@ -0,0 +1,106 @@
use crate::error::ResultCode;
use crate::services::fs::FsMediaType;
use std::marker::PhantomData;
use std::mem::MaybeUninit;
#[derive(Copy, Clone, Debug)]
#[repr(transparent)]
pub struct TitleInfo(ctru_sys::AM_TitleEntry);
impl TitleInfo {
pub fn id(&self) -> u64 {
self.0.titleID
}
pub fn size_bytes(&self) -> u64 {
self.0.size
}
pub fn version(&self) -> u16 {
self.0.version
}
}
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<String> {
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<TitleInfo> {
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 {
pub fn init() -> crate::Result<Am> {
unsafe {
ResultCode(ctru_sys::amInit())?;
Ok(Am(()))
}
}
pub fn get_title_count(&self, mediatype: FsMediaType) -> crate::Result<u32> {
unsafe {
let mut count = 0;
ResultCode(ctru_sys::AM_GetTitleCount(mediatype as u32, &mut count))?;
Ok(count)
}
}
pub fn get_title_list(&self, mediatype: FsMediaType) -> crate::Result<Vec<Title>> {
let count = self.get_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 as u32,
count,
buf.as_mut_ptr(),
))?;
}
Ok(buf
.into_iter()
.map(|id| Title {
id,
mediatype,
_am: PhantomData,
})
.collect())
}
}
impl Drop for Am {
fn drop(&mut self) {
unsafe { ctru_sys::amExit() };
}
}

8
ctru-rs/src/services/fs.rs

@ -43,6 +43,14 @@ bitflags! {
} }
} }
#[derive(Copy, Clone, Debug)]
#[repr(u32)]
pub enum FsMediaType {
Nand = ctru_sys::MEDIATYPE_NAND,
Sd = ctru_sys::MEDIATYPE_SD,
GameCard = ctru_sys::MEDIATYPE_GAME_CARD,
}
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
pub enum PathType { pub enum PathType {
Invalid, Invalid,

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

@ -5,6 +5,7 @@
//! //!
//! Some include: button input, audio playback, graphics rendering, built-in cameras, etc. //! Some include: button input, audio playback, graphics rendering, built-in cameras, etc.
pub mod am;
pub mod apt; pub mod apt;
pub mod cam; pub mod cam;
pub mod cfgu; pub mod cfgu;

Loading…
Cancel
Save