ctru/
lib.rs

1//! Safe and idiomatic Rust wrapper around [`libctru`](https://github.com/devkitPro/libctru).
2//!
3//! # About
4//!
5//! This crate behaves as the main tool to access system-specific functionality on the Nintendo 3DS when developing homebrew software in Rust.
6//! Thanks to it, developers can develop userland applications by accessing access the underlying system services and the console's hardware
7//! (such as [HID devices](crate::services::hid), [network capabilities](crate::services::soc), [graphics](crate::services::gfx), [built-in cameras](crate::services::cam), etc.).
8//!
9//! Among these features, [`ctru-rs`](crate) also automatically includes functionality to properly integrate the Rust `std` with the console's operating system,
10//! which the developer would otherwise need to implement manually.
11//!
12//! # Usage
13//!
14//! Thoroughly read the official [`ctru-rs` wiki](https://github.com/rust3ds/ctru-rs/wiki) which guides you through the setup needed to install the required toolchain and helpful tools.
15//! After following the guide and understanding the many quirks of the Nintendo 3DS homebrew development environment, you can create a new project by including this crate as a dependency
16//! in your `Cargo.toml` manifest and build your binaries either manually (for the `armv6k-nintendo-3ds` target) or via [`cargo-3ds`](https://github.com/rust3ds/cargo-3ds).
17
18#![crate_type = "rlib"]
19#![crate_name = "ctru"]
20#![warn(missing_docs)]
21#![deny(unsafe_op_in_unsafe_fn)]
22#![feature(custom_test_frameworks)]
23#![feature(try_trait_v2)]
24#![feature(allocator_api)]
25#![test_runner(test_runner::run_gdb)] // TODO: does this make sense to have configurable?
26#![doc(
27    html_favicon_url = "https://user-images.githubusercontent.com/11131775/225929072-2fa1741c-93ae-4b47-9bdf-af70f3d59910.png"
28)]
29#![doc(
30    html_logo_url = "https://user-images.githubusercontent.com/11131775/225929072-2fa1741c-93ae-4b47-9bdf-af70f3d59910.png"
31)]
32#![doc(html_root_url = "https://rust3ds.github.io/ctru-rs/crates")]
33
34// Nothing is imported from these crates but their inclusion here assures correct linking of the missing implementations.
35extern crate pthread_3ds;
36extern crate shim_3ds;
37
38/// Expanded stack size used to spawn the main thread by `libctru`.
39///
40/// It takes effect only if the `big-stack` feature is active. Otherwise, the default stack size should be ~32kB.
41///
42/// This value was chosen to support crate dependencies which expected more stack than provided. It's suggested to use less stack if possible.
43#[unsafe(no_mangle)]
44// When building lib tests, we don't want to redefine the same symbol twice,
45// since ctru-rs is both the crate under test and a dev-dependency (non-test).
46// We might also be able to use #[linkage] for similar effect, but this way
47// works without depending on another unstable feature.
48#[cfg(all(feature = "big-stack", not(test)))]
49static __stacksize__: usize = 2 * 1024 * 1024; // 2MB
50
51macro_rules! from_impl {
52    ($from_type:ty, $into_type:ty) => {
53        impl From<$from_type> for $into_type {
54            fn from(v: $from_type) -> Self {
55                v as $into_type
56            }
57        }
58    };
59}
60
61pub mod applets;
62pub mod console;
63pub mod error;
64pub mod linear;
65pub mod mii;
66pub mod os;
67pub mod prelude;
68mod sealed;
69pub mod services;
70
71pub use crate::error::{Error, Result};
72
73/// Sets a custom [panic hook](https://doc.rust-lang.org/std/panic/fn.set_hook.html) that uses the error applet to display panic messages.
74///
75/// You can also choose to have the previously registered panic hook called along with the error applet popup, which can be useful
76/// if you want to use output redirection to display panic messages over `3dslink` or `GDB`.
77///
78/// You can use [`std::panic::take_hook`](https://doc.rust-lang.org/std/panic/fn.take_hook.html) to unregister the panic hook
79/// set by this function.
80///
81/// # Notes
82///
83/// * If the [`Gfx`] service is not initialized during a panic, the error applet will not be displayed and the old panic hook will be called.
84pub fn set_panic_hook(call_old_hook: bool) {
85    crate::applets::error::set_panic_hook(call_old_hook);
86}
87
88/// A helper type for writing string data into `[u16]` buffers.
89///
90/// This type is mainly useful for interop with `libctru` APIs that expect UTF-16 text as input. The writer implements the
91/// [`std::fmt::Write`](https://doc.rust-lang.org/std/fmt/trait.Write.html) trait and ensures that the text is written in-bounds and properly nul-terminated.
92///
93/// # Notes
94///
95/// Subsequent writes to the same `Utf16Writer` will append to the buffer instead of overwriting the existing contents. If you want to start over from the
96/// beginning of the buffer, simply create a new `Utf16Writer`.
97///
98/// If a write causes the buffer to reach the end of its capacity, `std::fmt::Error` will be returned, but all string data up until the end of the capacity will
99/// still be written.
100pub struct Utf16Writer<'a> {
101    buf: &'a mut [u16],
102    index: usize,
103}
104
105impl Utf16Writer<'_> {
106    /// Creates a new [Utf16Writer] that writes its output into the provided buffer.
107    pub fn new(buf: &mut [u16]) -> Utf16Writer<'_> {
108        Utf16Writer { buf, index: 0 }
109    }
110}
111
112impl std::fmt::Write for Utf16Writer<'_> {
113    fn write_str(&mut self, s: &str) -> std::fmt::Result {
114        let max = self.buf.len() - 1;
115
116        for code_unit in s.encode_utf16() {
117            if self.index == max {
118                self.buf[self.index] = 0;
119                return Err(std::fmt::Error);
120            } else {
121                self.buf[self.index] = code_unit;
122                self.index += 1;
123            }
124        }
125
126        self.buf[self.index] = 0;
127
128        Ok(())
129    }
130}