Browse Source

Merge pull request #14 from AzureMarker/feature/file-system

pull/30/head
Meziu 3 years ago committed by GitHub
parent
commit
09bfe7aa61
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 8
      ctru-rs/Cargo.toml
  2. 32
      ctru-rs/build.rs
  3. 178
      ctru-rs/examples/file-explorer.rs
  4. 1
      ctru-rs/romfs/test-file.txt
  5. 21
      ctru-rs/src/lib.rs
  6. 36
      ctru-rs/src/romfs.rs
  7. 20
      ctru-rs/src/sdmc.rs

8
ctru-rs/Cargo.toml

@ -11,6 +11,7 @@ crate-type = ["rlib"] @@ -11,6 +11,7 @@ crate-type = ["rlib"]
name = "ctru"
[dependencies]
cfg-if = "1.0"
ctru-sys = { path = "../ctru-sys", version = "0.4" }
const-zero = "0.1.0"
linker-fix-3ds = { git = "https://github.com/Meziu/rust-linker-fix-3ds.git" }
@ -19,5 +20,12 @@ libc = "0.2" @@ -19,5 +20,12 @@ libc = "0.2"
bitflags = "1.0.0"
widestring = "0.2.2"
[build-dependencies]
toml = "0.5"
[dev-dependencies]
ferris-says = "0.2.1"
[features]
default = ["romfs"]
romfs = []

32
ctru-rs/build.rs

@ -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);
}

178
ctru-rs/examples/file-explorer.rs

@ -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();
}
}

1
ctru-rs/romfs/test-file.txt

@ -0,0 +1 @@ @@ -0,0 +1 @@
test

21
ctru-rs/src/lib.rs

@ -33,13 +33,30 @@ pub mod applets; @@ -33,13 +33,30 @@ pub mod applets;
pub mod console;
pub mod error;
pub mod gfx;
pub mod sdmc;
pub mod services;
pub mod srv;
pub mod thread;
cfg_if::cfg_if! {
if #[cfg(all(feature = "romfs", romfs_exists))] {
pub mod romfs;
} else {
pub mod romfs {
//! The RomFS folder has not been detected and/or the `romfs` feature has not been 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"
//! ```
}
}
}
pub use crate::error::{Error, Result};
pub use crate::gfx::Gfx;
pub use crate::sdmc::Sdmc;
pub use crate::srv::Srv;

36
ctru-rs/src/romfs.rs

@ -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()) };
}
}

20
ctru-rs/src/sdmc.rs

@ -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…
Cancel
Save