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}