Ian Chamberlain
3 years ago
10 changed files with 322 additions and 24 deletions
@ -0,0 +1,32 @@
@@ -0,0 +1,32 @@
|
||||
use std::path::PathBuf; |
||||
|
||||
fn main() { |
||||
// Open Cargo.toml
|
||||
let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap(); |
||||
let manifest_path = format!("{manifest_dir}/Cargo.toml"); |
||||
let manifest_str = std::fs::read_to_string(&manifest_path) |
||||
.unwrap_or_else(|e| panic!("Could not open {manifest_path}: {e}")); |
||||
let manifest_data: toml::Value = |
||||
toml::de::from_str(&manifest_str).expect("Could not parse Cargo manifest as TOML"); |
||||
|
||||
// Find the romfs setting and compute the path
|
||||
let romfs_dir_setting = manifest_data |
||||
.as_table() |
||||
.and_then(|table| table.get("package")) |
||||
.and_then(toml::Value::as_table) |
||||
.and_then(|table| table.get("metadata")) |
||||
.and_then(toml::Value::as_table) |
||||
.and_then(|table| table.get("cargo-3ds")) |
||||
.and_then(toml::Value::as_table) |
||||
.and_then(|table| table.get("romfs_dir")) |
||||
.and_then(toml::Value::as_str) |
||||
.unwrap_or("romfs"); |
||||
let romfs_path = PathBuf::from(format!("{manifest_dir}/{romfs_dir_setting}")); |
||||
|
||||
// Check if the romfs path exists so we can compile the module
|
||||
if romfs_path.exists() { |
||||
println!("cargo:rustc-cfg=romfs_exists"); |
||||
} |
||||
|
||||
println!("cargo:rerun-if-changed={}", manifest_dir); |
||||
} |
@ -0,0 +1,178 @@
@@ -0,0 +1,178 @@
|
||||
//! A file explorer which shows off using standard library file system APIs to
|
||||
//! read the SD card.
|
||||
|
||||
use ctru::applets::swkbd::{Button, Swkbd}; |
||||
use ctru::console::Console; |
||||
use ctru::services::hid::KeyPad; |
||||
use ctru::services::{Apt, Hid}; |
||||
use ctru::Gfx; |
||||
use std::fs::DirEntry; |
||||
use std::path::{Path, PathBuf}; |
||||
|
||||
fn main() { |
||||
ctru::init(); |
||||
let apt = Apt::init().unwrap(); |
||||
let hid = Hid::init().unwrap(); |
||||
let gfx = Gfx::default(); |
||||
|
||||
#[cfg(all(feature = "romfs", romfs_exists))] |
||||
let _romfs = ctru::romfs::RomFS::new().unwrap(); |
||||
|
||||
FileExplorer::init(&apt, &hid, &gfx).run(); |
||||
} |
||||
|
||||
struct FileExplorer<'a> { |
||||
apt: &'a Apt, |
||||
hid: &'a Hid, |
||||
gfx: &'a Gfx, |
||||
console: Console<'a>, |
||||
path: PathBuf, |
||||
entries: Vec<DirEntry>, |
||||
running: bool, |
||||
} |
||||
|
||||
impl<'a> FileExplorer<'a> { |
||||
fn init(apt: &'a Apt, hid: &'a Hid, gfx: &'a Gfx) -> Self { |
||||
gfx.top_screen.borrow_mut().set_wide_mode(true); |
||||
let console = Console::init(gfx.top_screen.borrow_mut()); |
||||
|
||||
FileExplorer { |
||||
apt, |
||||
hid, |
||||
gfx, |
||||
console, |
||||
path: PathBuf::from("/"), |
||||
entries: Vec::new(), |
||||
running: false, |
||||
} |
||||
} |
||||
|
||||
fn run(&mut self) { |
||||
self.running = true; |
||||
self.print_menu(); |
||||
|
||||
while self.running && self.apt.main_loop() { |
||||
self.hid.scan_input(); |
||||
let input = self.hid.keys_down(); |
||||
|
||||
if input.contains(KeyPad::KEY_START) { |
||||
break; |
||||
} else if input.contains(KeyPad::KEY_B) { |
||||
self.path.pop(); |
||||
self.console.clear(); |
||||
self.print_menu(); |
||||
} else if input.contains(KeyPad::KEY_A) { |
||||
self.get_input_and_run(Self::set_next_path); |
||||
} else if input.contains(KeyPad::KEY_X) { |
||||
self.get_input_and_run(Self::set_exact_path); |
||||
} |
||||
|
||||
self.gfx.flush_buffers(); |
||||
self.gfx.swap_buffers(); |
||||
self.gfx.wait_for_vblank(); |
||||
} |
||||
} |
||||
|
||||
fn print_menu(&mut self) { |
||||
println!("Viewing {}", self.path.display()); |
||||
|
||||
let dir_listing = std::fs::read_dir(&self.path).expect("Failed to open path"); |
||||
self.entries = Vec::new(); |
||||
|
||||
for (i, entry) in dir_listing.enumerate() { |
||||
match entry { |
||||
Ok(entry) => { |
||||
println!("{:2} - {}", i, entry.file_name().to_string_lossy()); |
||||
self.entries.push(entry); |
||||
|
||||
// Paginate the output
|
||||
if (i + 1) % 20 == 0 { |
||||
println!("Press A to go to next page, or Start to exit"); |
||||
|
||||
while self.apt.main_loop() { |
||||
self.hid.scan_input(); |
||||
let input = self.hid.keys_down(); |
||||
|
||||
if input.contains(KeyPad::KEY_A) { |
||||
break; |
||||
} |
||||
|
||||
if input.contains(KeyPad::KEY_START) { |
||||
self.running = false; |
||||
return; |
||||
} |
||||
|
||||
self.gfx.wait_for_vblank(); |
||||
} |
||||
} |
||||
} |
||||
Err(e) => { |
||||
println!("{} - Error: {}", i, e); |
||||
} |
||||
} |
||||
} |
||||
|
||||
println!("Start to exit, A to select an entry by number, B to go up a directory, X to set the path."); |
||||
} |
||||
|
||||
fn get_input_and_run(&mut self, action: impl FnOnce(&mut Self, String)) { |
||||
let mut keyboard = Swkbd::default(); |
||||
let mut new_path_str = String::new(); |
||||
|
||||
match keyboard.get_utf8(&mut new_path_str) { |
||||
Ok(Button::Right) => { |
||||
// Clicked "OK"
|
||||
action(self, new_path_str); |
||||
} |
||||
Ok(Button::Left) => { |
||||
// Clicked "Cancel"
|
||||
} |
||||
Ok(Button::Middle) => { |
||||
// This button wasn't shown
|
||||
unreachable!() |
||||
} |
||||
Err(e) => { |
||||
panic!("Error: {:?}", e) |
||||
} |
||||
} |
||||
} |
||||
|
||||
fn set_next_path(&mut self, next_path_index: String) { |
||||
let next_path_index: usize = match next_path_index.parse() { |
||||
Ok(index) => index, |
||||
Err(e) => { |
||||
println!("Number parsing error: {}", e); |
||||
return; |
||||
} |
||||
}; |
||||
|
||||
let next_entry = match self.entries.get(next_path_index) { |
||||
Some(entry) => entry, |
||||
None => { |
||||
println!("Input number of bounds"); |
||||
return; |
||||
} |
||||
}; |
||||
|
||||
if !next_entry.file_type().unwrap().is_dir() { |
||||
println!("Not a directory: {}", next_path_index); |
||||
return; |
||||
} |
||||
|
||||
self.console.clear(); |
||||
self.path = next_entry.path(); |
||||
self.print_menu(); |
||||
} |
||||
|
||||
fn set_exact_path(&mut self, new_path_str: String) { |
||||
let new_path = Path::new(&new_path_str); |
||||
if !new_path.is_dir() { |
||||
println!("Not a directory: {}", new_path_str); |
||||
return; |
||||
} |
||||
|
||||
self.console.clear(); |
||||
self.path = new_path.to_path_buf(); |
||||
self.print_menu(); |
||||
} |
||||
} |
@ -0,0 +1,47 @@
@@ -0,0 +1,47 @@
|
||||
use ctru::console::Console; |
||||
use ctru::gfx::Gfx; |
||||
use ctru::services::apt::Apt; |
||||
use ctru::services::hid::{Hid, KeyPad}; |
||||
use ctru::thread; |
||||
|
||||
use std::time::Duration; |
||||
|
||||
fn main() { |
||||
// Initialize services
|
||||
ctru::init(); |
||||
let apt = Apt::init().unwrap(); |
||||
let hid = Hid::init().unwrap(); |
||||
let gfx = Gfx::default(); |
||||
let _console = Console::init(gfx.top_screen.borrow_mut()); |
||||
|
||||
let prio = thread::current().priority(); |
||||
println!("Main thread prio: {}\n", prio); |
||||
|
||||
for ix in 0..3 { |
||||
thread::Builder::new() |
||||
.priority(prio - 1) |
||||
.spawn(move || { |
||||
let sleep_duration: u64 = 1000 + ix * 250; |
||||
let mut i = 0; |
||||
loop { |
||||
println!("Thread{ix} says {i}"); |
||||
i += 1; |
||||
thread::sleep(Duration::from_millis(sleep_duration)); |
||||
} |
||||
}) |
||||
.unwrap(); |
||||
|
||||
println!("Created thread {ix}"); |
||||
} |
||||
|
||||
while apt.main_loop() { |
||||
gfx.flush_buffers(); |
||||
gfx.swap_buffers(); |
||||
gfx.wait_for_vblank(); |
||||
|
||||
hid.scan_input(); |
||||
if hid.keys_down().contains(KeyPad::KEY_START) { |
||||
break; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,36 @@
@@ -0,0 +1,36 @@
|
||||
//! This module only gets compiled if the configured RomFS directory is found and the `romfs`
|
||||
//! feature is enabled.
|
||||
//!
|
||||
//! Configure the path in Cargo.toml (the default path is "romfs"). Paths are relative to the
|
||||
//! `CARGO_MANIFEST_DIR` environment variable, which is the directory containing the manifest of
|
||||
//! your package.
|
||||
//!
|
||||
//! ```toml
|
||||
//! [package.metadata.cargo-3ds]
|
||||
//! romfs_dir = "romfs"
|
||||
//! ```
|
||||
|
||||
use std::ffi::CStr; |
||||
|
||||
#[non_exhaustive] |
||||
pub struct RomFS; |
||||
|
||||
impl RomFS { |
||||
pub fn new() -> crate::Result<Self> { |
||||
let mount_name = CStr::from_bytes_with_nul(b"romfs\0").unwrap(); |
||||
let result = unsafe { ctru_sys::romfsMountSelf(mount_name.as_ptr()) }; |
||||
|
||||
if result < 0 { |
||||
Err(result.into()) |
||||
} else { |
||||
Ok(Self) |
||||
} |
||||
} |
||||
} |
||||
|
||||
impl Drop for RomFS { |
||||
fn drop(&mut self) { |
||||
let mount_name = CStr::from_bytes_with_nul(b"romfs\0").unwrap(); |
||||
unsafe { ctru_sys::romfsUnmount(mount_name.as_ptr()) }; |
||||
} |
||||
} |
@ -1,20 +0,0 @@
@@ -1,20 +0,0 @@
|
||||
pub struct Sdmc(()); |
||||
|
||||
impl Sdmc { |
||||
pub fn init() -> crate::Result<Sdmc> { |
||||
unsafe { |
||||
let r = ctru_sys::archiveMountSdmc(); |
||||
if r < 0 { |
||||
Err(r.into()) |
||||
} else { |
||||
Ok(Sdmc(())) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
impl Drop for Sdmc { |
||||
fn drop(&mut self) { |
||||
unsafe { ctru_sys::archiveUnmountAll() }; |
||||
} |
||||
} |
Loading…
Reference in new issue