ctru/services/romfs.rs
1//! Read-Only Memory FileSystem service.
2//!
3//! This service lets the application access a virtual mounted device created using a folder included within the application bundle.
4//! After mounting the RomFS file system, the included files and folders will be accessible exactly like any other file, just by using the drive prefix `romfs:/<file-path>`.
5//!
6//! # Usage
7//!
8//! This module only gets compiled if the configured RomFS directory is found and the `romfs`
9//! feature is enabled.
10//!
11//! Configure the path in your project's `Cargo.toml` manifest (the default path is "romfs"). Paths are relative to the
12//! `CARGO_MANIFEST_DIR` environment variable, which is the directory containing the manifest of
13//! your package.
14//!
15//! ```toml
16//! [package.metadata.cargo-3ds]
17//! romfs_dir = "romfs"
18//! ```
19//!
20//! Alternatively, you can include the RomFS archive manually when building with `3dsxtool`.
21//!
22//! # Notes
23//!
24//! `std::path` has problems when parsing file paths that include the `romfs:` prefix.
25//! As such, it's suggested to use the paths directly or to do simple append operations to avoid unexpected behaviour.
26//! Related [issue](https://github.com/rust-lang/rust/issues/52331).
27#![doc(alias = "embed")]
28#![doc(alias = "filesystem")]
29
30use crate::error::ResultCode;
31use std::sync::Mutex;
32
33use crate::services::ServiceReference;
34
35/// Handle to the RomFS service.
36pub struct RomFS {
37 _service_handler: ServiceReference,
38}
39
40static ROMFS_ACTIVE: Mutex<()> = Mutex::new(());
41
42impl RomFS {
43 /// Mount the bundled RomFS archive as a virtual drive.
44 ///
45 /// # Example
46 ///
47 /// ```
48 /// # let _runner = test_runner::GdbRunner::default();
49 /// # use std::error::Error;
50 /// # fn main() -> Result<(), Box<dyn Error>> {
51 /// #
52 /// use ctru::services::romfs::RomFS;
53 ///
54 /// let romfs = RomFS::new()?;
55 ///
56 /// // Remember to include the RomFS archive and to use your actual files!
57 /// let contents = std::fs::read_to_string("romfs:/test-file.txt");
58 /// #
59 /// # Ok(())
60 /// # }
61 /// ```
62 #[doc(alias = "romfsMountSelf")]
63 pub fn new() -> crate::Result<Self> {
64 let _service_handler = ServiceReference::new(
65 &ROMFS_ACTIVE,
66 || {
67 ResultCode(unsafe { ctru_sys::romfsMountSelf(c"romfs".as_ptr()) })?;
68 Ok(())
69 },
70 || {
71 let _ = unsafe { ctru_sys::romfsUnmount(c"romfs".as_ptr()) };
72 },
73 )?;
74
75 Ok(Self { _service_handler })
76 }
77}
78
79#[cfg(test)]
80mod tests {
81 use super::*;
82
83 // NOTE: this test only passes when run with a .3dsx, which for now requires separate build
84 // and run steps so the 3dsx is built before the runner looks for the executable
85 #[test]
86 #[should_panic]
87 fn romfs_lock() {
88 let romfs = RomFS::new().unwrap();
89
90 drop(ROMFS_ACTIVE.try_lock().unwrap());
91
92 drop(romfs);
93 }
94}