Safe and idiomatic Rust wrapper around [`libctru`](https://github.com/devkitPro/libctru).
## Getting Started
Thoroughly read the [`ctru-rs` wiki](https://github.com/rust3ds/ctru-rs/wiki/Getting-Started) to meet the requirements
and to understand what it takes to develop homebrew software on the Nintendo 3DS family of consoles.
After that, you can simply add the crate as a dependency to your project and build your final binary by using [`cargo-3ds`](https://github.com/rust3ds/cargo-3ds)
or by manually compiling for the `armv6k-nintendo-3ds` target.
## Examples
Many examples to demonstrate the `ctru-rs` functionality are available in the [`examples`](./examples/) folder. Simply run them via
```bash
cargo 3ds run --example <example-name>
```
## License
This project is distributed under the Zlib license.
//! This is similar to the `hello-world` example, with the main difference of using 2 virtual `Console`s that can be alternated to print on both screens.
/// Guest Mii's won't be available regardless of their allowlist/blocklist state if the [`MiiSelector`] is run without setting [`Options::ENABLE_GUESTS`].
/// Look into [`MiiSelector::set_options()`] to see how to work with options.
///
/// # Example
///
/// ```no_run
/// # fn main() {
/// #
/// use ctru::applets::mii_selector::{Index, MiiSelector};
/// Guest Mii's won't be available regardless of their allowlist/blocklist state if the [`MiiSelector`] is run without setting [`Options::ENABLE_GUESTS`].
/// Look into [`MiiSelector::set_options()`] to see how to work with options.
///
/// # Example
///
/// ```no_run
/// # fn main() {
/// #
/// use ctru::applets::mii_selector::{Index, MiiSelector};
/// let mut mii_selector = MiiSelector::new();
///
/// // Blocklist the guest Mii at index 1 so that it cannot be selected.
/// Depending on the configuration, the Mii Selector window will appear either on the bottom screen (default behaviour) or the top screen (see [`Options::USE_TOP_SCREEN`]).
///
/// TODO: UNSAFE OPERATION, LAUNCHING APPLETS REQUIRES GRAPHICS, WITHOUT AN ACTIVE GFX THIS WILL CAUSE A SEGMENTATION FAULT.
///
/// # Example
///
/// ```no_run
/// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> {
/// #
/// use ctru::applets::mii_selector::{MiiSelector, Options};
///
/// let mut mii_selector = MiiSelector::new();
/// mii_selector.set_title("Select a Mii!");
///
/// let opts = Options::ENABLE_CANCEL & Options::ENABLE_GUESTS;
//! Applets are small integrated programs that the OS makes available to the developer to streamline commonly needed functionality.
//! Thanks to these integrations the developer can avoid wasting time re-implementing common features and instead use a more reliable base for their application.
//!
//! Unlike [services](crate::services), applets aren't accessed via a system subprocess (which would require obtaining a special handle at runtime).
//! Instead, the application builds a configuration storing the various parameters which is then used to "launch" the applet.
//!
//! Applets block execution of the thread that launches them as long as the user doesn't close the applet.
//! This applet opens a virtual keyboard on the console's bottom screen which lets the user write UTF-16 valid text.
// TODO: Implement remaining functionality (password mode, filter callbacks, etc.). Also improve "max text length" API. Improve `number of buttons` API when creating a new SoftwareKeyboard.
// TODO: Split the Parental PIN lock operations into a different type.
//! The [`Console`] works as a virtual shell that renders on screen all output of `stdout`. As such, it is useful as a basic interface to show info to the user,
//! such as in simple "Hello World" applications or more complex software that does not need much user interaction.
//!
//! Have a look at [`Soc::redirect_to_3dslink()`](crate::services::soc::Soc::redirect_to_3dslink) for a better alternative when debugging applications.
usestd::cell::RefMut;
usestd::cell::RefMut;
usestd::default::Default;
usestd::default::Default;
@ -7,31 +14,129 @@ use crate::services::gfx::Screen;
/// Error enum for generic errors within [`Console`].
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pubenumError{
/// The coordinate specified on the given axis exceeds the limits imposed by the [`Console`] window.
CoordinateOutOfBounds(Axis),
/// The size specified for the given dimension exceeds the limits imposed by the [`Console`] window.
DimensionOutOfBounds(Dimension),
}
/// 2D coordinate axes.
#[allow(missing_docs)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pubenumAxis{
X,
Y,
}
/// 2D dimensions.
#[allow(missing_docs)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pubenumDimension{
Width,
Height,
}
/// Virtual text console.
///
/// [`Console`] lets the application redirect `stdout` and `stderr` to a simple text displayer on the 3DS screen.
/// This means that any text written to `stdout` and `stderr` (e.g. using `println!`, `eprintln!` or `dbg!`) will become visible in the area taken by the console.
///
/// # Notes
///
/// The [`Console`] will take full possession of the screen handed to it as long as it stays alive. It also supports some ANSI codes, such as text color and cursor positioning.
/// The [`Console`]'s window size will be:
/// - 40x30 on the [`BottomScreen`](crate::services::gfx::BottomScreen).
/// - 50x30 on the normal [`TopScreen`](crate::services::gfx::TopScreen).
/// - 100x30 on the [`TopScreen`](crate::services::gfx::TopScreen) when wide mode is enabled.
///
/// # Alternatives
///
/// If you'd like to see live standard output while running the application but cannot or do not want to show the text on the 3DS itself,
/// you can try using [`Soc::redirect_to_3dslink`](crate::services::soc::Soc::redirect_to_3dslink) while activating the `--server` flag for `3dslink` (also supported by `cargo-3ds`).
/// More info in the [`cargo-3ds` docs](https://github.com/rust3ds/cargo-3ds#running-executables).
#[doc(alias = "PrintConsole")]
pubstructConsole<'screen>{
pubstructConsole<'screen>{
context: Box<PrintConsole>,
context: Box<PrintConsole>,
_screen: RefMut<'screen,dynScreen>,
screen: RefMut<'screen,dynScreen>,
}
}
impl<'screen>Console<'screen>{
impl<'screen>Console<'screen>{
/// Initialize a console on the chosen screen, overwriting whatever was on the screen
/// Initialize a console on the chosen screen.
/// previously (including other consoles). The new console is automatically selected for
/// printing.
///
///
/// # Notes
/// # Notes
///
///
/// [Console] automatically takes care of flushing and swapping buffers for its screen when printing.
/// This operation overwrites whatever was on the screen before the initialization (including other [`Console`]s)
/// and changes the [`FramebufferFormat`](crate::services::gspgpu::FramebufferFormat) of the selected screen to better suit the [`Console`].
///
/// The new console is automatically selected for printing.
///
/// [`Console`] automatically takes care of flushing and swapping buffers for its screen when printing.
///
/// # Panics
///
/// If the [`Gfx`](crate::services::gfx::Gfx) service was initialised via [`Gfx::with_formats_vram()`](crate::services::gfx::Gfx::with_formats_vram)
/// this function will crash the program with an ARM exception.
///
/// # Example
///
/// ```
/// # let _runner = test_runner::GdbRunner::default();
/// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> {
/// #
/// use ctru::console::Console;
/// use ctru::services::gfx::Gfx;
///
/// // Initialize graphics (using framebuffers allocated on the HEAP).
/// let gfx = Gfx::new()?;
///
/// // Create a `Console` that takes control of the upper LCD screen.
/// let top_console = Console::new(gfx.top_screen.borrow_mut());
//! Among these features, [`ctru-rs`](crate) also automatically includes functionality to properly integrate the Rust `std` with the console's operating system,
//! which the developer would otherwise need to implement manually.
//!
//! # Usage
//!
//! 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.
//! 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
//! 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).
#![crate_type = "rlib"]
#![crate_type = "rlib"]
#![crate_name = "ctru"]
#![crate_name = "ctru"]
#![feature(test)]
#![warn(missing_docs)]
#![feature(custom_test_frameworks)]
#![feature(custom_test_frameworks)]
#![feature(try_trait_v2)]
#![feature(try_trait_v2)]
#![feature(allocator_api)]
#![feature(allocator_api)]
#![feature(nonnull_slice_from_raw_parts)]
#![test_runner(test_runner::run_gdb)]// TODO: does this make sense to have configurable?
//! This module contains the structs that represent all the data of a Mii.
//! This module contains the structs that represent all the data of a Mii.
//! This data is given by the [``MiiSelector``](crate::applets::mii_selector::MiiSelector)
//!
//! Have a look at the [`MiiSelector`](crate::applets::mii_selector::MiiSelector) applet to learn how to ask the user for a specific Mii.
/// Represents the region lock of the console
/// Region lock of the Mii.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pubenumRegionLock{
pubenumRegionLock{
/// No region-lock.
None,
None,
/// Japan region-lock.
Japan,
Japan,
/// USA region-lock.
USA,
USA,
/// Europe region-lock.
Europe,
Europe,
}
}
/// Represent the charset of the console
/// Charset of the Mii.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pubenumCharset{
pubenumCharset{
/// Japan-USA-Europe unified charset.
JapanUSAEurope,
JapanUSAEurope,
/// China charset.
China,
China,
/// Korea charset.
Korea,
Korea,
/// Taiwan charset.
Taiwan,
Taiwan,
}
}
/// Represents the options of the Mii
/// Generic options of the Mii.
#[derive(Copy, Clone, Debug)]
#[derive(Copy, Clone, Debug)]
pubstructMiiDataOptions{
pubstructOptions{
/// Whether it is allowed to copy the Mii.
pubis_copying_allowed: bool,
pubis_copying_allowed: bool,
/// Whether the profanity flag is active.
pubis_profanity_flag_enabled: bool,
pubis_profanity_flag_enabled: bool,
/// The Mii's active region-lock.
pubregion_lock: RegionLock,
pubregion_lock: RegionLock,
/// The Mii's used charset.
pubcharset: Charset,
pubcharset: Charset,
}
}
/// Represents the position that the Mii has on the selector
/// Positional Index that the Mii has on the [`MiiSelector`](crate::applets::mii_selector::MiiSelector) window.
#[derive(Copy, Clone, Debug)]
#[derive(Copy, Clone, Debug)]
pubstructSelectorPosition{
pubstructSelectorPosition{
/// Index of the page where the Mii is found.
pubpage_index: u8,
pubpage_index: u8,
/// Index of the slot (relative to the page) where the Mii is found.
pubslot_index: u8,
pubslot_index: u8,
}
}
/// Represents the kind of origin console
/// Console model from which the Mii originated.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pubenumOriginConsole{
pubenumOriginConsole{
/// Nintendo Wii.
Wii,
Wii,
/// Nintendo DSi.
DSi,
DSi,
/// Both New 3DS and Old 3DS
/// Nintendo 3DS.
///
/// This includes all consoles of the 3DS family (3DS, 2DS, and their respective "New" or "XL" variants).
N3DS,
N3DS,
/// Nintendo Wii U/Switch.
WiiUSwitch,
WiiUSwitch,
}
}
/// Represents the identity of the origin console
/// Identity of the origin console.
#[derive(Copy, Clone, Debug)]
#[derive(Copy, Clone, Debug)]
pubstructConsoleIdentity{
pubstructConsoleIdentity{
/// From which console the Mii originated from.
puborigin_console: OriginConsole,
puborigin_console: OriginConsole,
}
}
/// Represents the sex of the Mii
/// Sex of the Mii.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pubenumMiiSex{
pubenumSex{
/// Male sex.
Male,
Male,
/// Female sex.
Female,
Female,
}
}
/// Represents the details of the Mii
/// Generic details of the Mii.
#[derive(Copy, Clone, Debug)]
#[derive(Copy, Clone, Debug)]
pubstructDetails{
pubstructDetails{
pubsex: MiiSex,
/// Sex of the Mii.
pubsex: Sex,
/// Birthday month.
pubbirthday_month: u8,
pubbirthday_month: u8,
/// Birthday day.
pubbirthday_day: u8,
pubbirthday_day: u8,
/// Color of the Mii's shirt.
pubshirt_color: u8,
pubshirt_color: u8,
/// Whether the Mii is a favorite.
pubis_favorite: bool,
pubis_favorite: bool,
/// Whether the Mii can be shared.
pubis_sharing_enabled: bool,
}
}
/// Represents the face style of the Mii
/// Face style of the Mii.
#[derive(Copy, Clone, Debug)]
#[derive(Copy, Clone, Debug)]
pubstructFaceStyle{
pubstructFaceStyle{
pubis_sharing_enabled: bool,
/// Face shape.
pubshape: u8,
pubshape: u8,
/// Skin color.
pubskin_color: u8,
pubskin_color: u8,
}
}
/// Represents the face details of the Mii
/// Face details of the Mii.
#[derive(Copy, Clone, Debug)]
#[derive(Copy, Clone, Debug)]
pubstructFaceDetails{
pubstructFaceDetails{
/// Face style.
pubstyle: FaceStyle,
pubstyle: FaceStyle,
/// Wrinkles.
pubwrinkles: u8,
pubwrinkles: u8,
/// Makeup.
pubmakeup: u8,
pubmakeup: u8,
}
}
/// Represents the hair details of the Mii
/// Hair details of the Mii.
#[derive(Copy, Clone, Debug)]
#[derive(Copy, Clone, Debug)]
pubstructHairDetails{
pubstructHairDetails{
/// Hair style.
pubstyle: u8,
pubstyle: u8,
/// Hair color.
pubcolor: u8,
pubcolor: u8,
/// Whether the Mii's hair is flipped.
pubis_flipped: bool,
pubis_flipped: bool,
}
}
/// Represents the eye details of the Mii
/// Eye details of the Mii.
#[derive(Copy, Clone, Debug)]
#[derive(Copy, Clone, Debug)]
pubstructEyeDetails{
pubstructEyeDetails{
/// Eye style.
pubstyle: u8,
pubstyle: u8,
/// Eye color.
pubcolor: u8,
pubcolor: u8,
/// Eye scale.
pubscale: u8,
pubscale: u8,
/// Eye scale (y-axis).
puby_scale: u8,
puby_scale: u8,
/// Eye rotation.
pubrotation: u8,
pubrotation: u8,
/// Spacing between the eyes
/// Spacing between the eyes.
pubx_spacing: u8,
pubx_spacing: u8,
/// Eye height.
puby_position: u8,
puby_position: u8,
}
}
/// Represents the eyebrow details of the Mii
/// Eyebrow details of the Mii.
#[derive(Copy, Clone, Debug)]
#[derive(Copy, Clone, Debug)]
pubstructEyebrowDetails{
pubstructEyebrowDetails{
/// Eyebrow style.
pubstyle: u8,
pubstyle: u8,
/// Eyebrow color.
pubcolor: u8,
pubcolor: u8,
/// Eyebrow scale.
pubscale: u8,
pubscale: u8,
/// Eyebrow scale (y-axis).
puby_scale: u8,
puby_scale: u8,
/// Eyebrow rotation.
pubrotation: u8,
pubrotation: u8,
/// Spacing between the eyebrows
/// Spacing between the eyebrows
pubx_spacing: u8,
pubx_spacing: u8,
/// Eyebrow height.
puby_position: u8,
puby_position: u8,
}
}
/// Represents the details of the nose
/// Nose details of the Mii.
#[derive(Copy, Clone, Debug)]
#[derive(Copy, Clone, Debug)]
pubstructNoseDetails{
pubstructNoseDetails{
/// Nose style.
pubstyle: u8,
pubstyle: u8,
/// Nose scale.
pubscale: u8,
pubscale: u8,
/// Nose height.
puby_position: u8,
puby_position: u8,
}
}
/// Represents the details of the mouth
/// Mouth details of the Mii.
#[derive(Copy, Clone, Debug)]
#[derive(Copy, Clone, Debug)]
pubstructMouthDetails{
pubstructMouthDetails{
/// Mouth style.
pubstyle: u8,
pubstyle: u8,
/// Mouth color.
pubcolor: u8,
pubcolor: u8,
/// Mouth scale.
pubscale: u8,
pubscale: u8,
/// Mouth scale (y-axis).
puby_scale: u8,
puby_scale: u8,
/// Mouth height.
puby_position: u8,
}
}
/// Represents the details of the mustache
/// Mustache details of the Mii.
#[derive(Copy, Clone, Debug)]
#[derive(Copy, Clone, Debug)]
pubstructMustacheDetails{
pubstructMustacheDetails{
pubmouth_y_position: u8,
/// Mustache style.
pubmustache_style: u8,
pubmustache_style: u8,
}
}
/// Represents the details of the beard
/// Beard details of the Mii.
#[derive(Copy, Clone, Debug)]
#[derive(Copy, Clone, Debug)]
pubstructBeardDetails{
pubstructBeardDetails{
/// Beard style
pubstyle: u8,
pubstyle: u8,
/// Beard color.
pubcolor: u8,
pubcolor: u8,
/// Beard scale.
pubscale: u8,
pubscale: u8,
/// Beard height.
puby_position: u8,
puby_position: u8,
}
}
/// Represents the details of the glass
/// Glasses details of the Mii.
#[derive(Copy, Clone, Debug)]
#[derive(Copy, Clone, Debug)]
pubstructGlassDetails{
pubstructGlassesDetails{
/// Glasses style.
pubstyle: u8,
pubstyle: u8,
/// Glasses color.
pubcolor: u8,
pubcolor: u8,
/// Glasses scale.
pubscale: u8,
pubscale: u8,
/// Glasses height.
puby_position: u8,
puby_position: u8,
}
}
/// Represents the details of the mole
/// Mole details of the Mii.
#[derive(Copy, Clone, Debug)]
#[derive(Copy, Clone, Debug)]
pubstructMoleDetails{
pubstructMoleDetails{
/// Whether the Mii has a mole.
pubis_enabled: bool,
pubis_enabled: bool,
/// Mole scale.
pubscale: u8,
pubscale: u8,
/// Mole position (x-axis).
pubx_position: u8,
pubx_position: u8,
/// Mole position (y-axis).
puby_position: u8,
puby_position: u8,
}
}
/// Represents all the data of a Mii
/// Full Mii data representation.
///
///
/// Some values are not ordered _like_ the Mii Editor UI. The mapped values can be seen here:
/// Some values are not ordered *like* the Mii Editor UI. The mapped values can be seen [here](https://www.3dbrew.org/wiki/Mii#Mapped_Editor_.3C-.3E_Hex_values).
//! The APT service handles integration with other applications,
//! including high-level OS features such as Sleep mode, the Home Menu and application switching.
//!
//! It also handles running applets, small programs made available by the OS to streamline specific functionality.
//! Those are implemented in the [`applets`](crate::applets) module.
usecrate::error::ResultCode;
usecrate::error::ResultCode;
/// Handle to the Applet service.
pubstructApt(());
pubstructApt(());
implApt{
implApt{
/// Initialize a new service handle.
///
/// # Example
///
/// ```
/// # let _runner = test_runner::GdbRunner::default();
/// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> {
/// #
/// use ctru::services::apt::Apt;
///
/// let apt = Apt::new()?;
/// #
/// # Ok(())
/// # }
/// ```
#[doc(alias = "aptInit")]
pubfnnew()-> crate::Result<Apt>{
pubfnnew()-> crate::Result<Apt>{
unsafe{
unsafe{
ResultCode(ctru_sys::aptInit())?;
ResultCode(ctru_sys::aptInit())?;
@ -10,10 +36,46 @@ impl Apt {
}
}
}
}
/// Returns `true` if the application is running in the foreground as normal.
///
/// # Notes
///
/// This function is called as such since it automatically handles all checks for Home Menu switching, Sleep mode and other events that could take away control from the application.
/// For this reason, its main use is as the condition of a while loop that controls the main logic for your program.
///
/// # Example
///
/// ```
/// # let _runner = test_runner::GdbRunner::default();
/// use std::error::Error;
/// use ctru::services::apt::Apt;
///
/// // In a simple `main` function, the structure should be the following.
/// fn main() -> Result<(), Box<dyn Error>> {
///
/// let apt = Apt::new()?;
///
/// while apt.main_loop() {
/// // Main program logic should be written here.
/// }
///
/// // Optional clean-ups after running the application should be written after the main loop.
/// #
/// # Ok(())
/// # }
/// ```
#[doc(alias = "aptMainLoop")]
pubfnmain_loop(&self)-> bool{
pubfnmain_loop(&self)-> bool{
unsafe{ctru_sys::aptMainLoop()}
unsafe{ctru_sys::aptMainLoop()}
}
}
/// Set (in percentage) the amount of time to lend to the application thread spawned on the syscore (core #1).
///
/// # Notes
///
/// It is necessary to set a time limit before spawning threads on the syscore.
/// The percentage value must be withing 5% and 89%, though it is suggested to use lower values (around 30-45%) to avoid slowing down the OS processes.
/// The new `Gfx` instance will allocate the needed framebuffers in the CPU-GPU shared memory region (to ensure compatibiltiy with all possible uses of the `Gfx` service).
/// As such, it's the same as calling:
///
/// ```
/// # let _runner = test_runner::GdbRunner::default();
/// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> {
/// #
/// # use ctru::services::gfx::Gfx;
/// # use ctru::services::gspgpu::FramebufferFormat;
//! System services used to handle system-specific functionalities.
//! OS services used to handle system-specific functionality.
//!
//!
//! Most of the 3DS console's functionalities (when writing homebrew) are locked behind services,
//! Most of the 3DS console's functionalities (when writing user-land homebrew) are accessible via services,
//! which need to be initialized before accessing any particular feature.
//! which need to be initialized before accessing any particular feature.
//!
//!
//! Some include: button input, audio playback, graphics rendering, built-in cameras, etc.
//! To ensure safety while using the underlying services, [`ctru-rs`](crate) leverages Rust's lifetime model.
//! After initializing the handle for a specific service (e.g. [`Apt`](apt::Apt)) the service will be accessible as long as there is at least one handle "alive".
//! As such, handles should be dropped *after* the use of a specific service. This is particularly important for services which are necessary for functionality
//! "outside" their associated methods, such as [`RomFS`](romfs::RomFS), which creates an accessible virtual filesystem, or [`Soc`](soc::Soc),
//! which enables all network communications via sockets.
//!
//! In [`ctru-rs`](crate) some services only allow a single handle to be created at a time, to ensure a safe and controlled environment.
//! The NDSP service is used to handle communications to the DSP processor present on the console's motherboard.
//! Thanks to the DSP processor the program can play sound effects and music on the console's built-in speakers or to any audio device
//! connected via the audio jack.
//!
//! To use NDSP audio, you will need to dump DSP firmware from a real 3DS using
//! something like [DSP1](https://www.gamebrew.org/wiki/DSP1_3DS).
//!
//! `libctru` expects to find it at `sdmc:/3ds/dspfirm.cdc` when initializing the NDSP service.
#![doc(alias = "audio")]
// As a result of requiring DSP firmware to initialize, all of the doctests in
// this module are `no_run`, since Citra doesn't provide a stub for the DSP firmware:
// https://github.com/citra-emu/citra/issues/6111
pubmodwave;
pubmodwave;
usewave::{Wave,WaveStatus};
usewave::{Status,Wave};
usecrate::error::ResultCode;
usecrate::error::ResultCode;
usecrate::services::ServiceReference;
usecrate::services::ServiceReference;
@ -14,60 +28,101 @@ use std::sync::Mutex;
constNUMBER_OF_CHANNELS: u8=24;
constNUMBER_OF_CHANNELS: u8=24;
/// Audio output mode.
#[doc(alias = "ndspOutputMode")]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(u32)]
#[repr(u32)]
pubenumOutputMode{
pubenumOutputMode{
/// Single-Channel.
Mono=ctru_sys::NDSP_OUTPUT_MONO,
Mono=ctru_sys::NDSP_OUTPUT_MONO,
/// Dual-Channel.
Stereo=ctru_sys::NDSP_OUTPUT_STEREO,
Stereo=ctru_sys::NDSP_OUTPUT_STEREO,
/// Surround.
Surround=ctru_sys::NDSP_OUTPUT_SURROUND,
Surround=ctru_sys::NDSP_OUTPUT_SURROUND,
}
}
/// PCM formats supported by the audio engine.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(u32)]
#[repr(u32)]
pubenumAudioFormat{
pubenumAudioFormat{
/// PCM 8bit single-channel.
PCM8Mono=ctru_sys::NDSP_FORMAT_MONO_PCM8,
PCM8Mono=ctru_sys::NDSP_FORMAT_MONO_PCM8,
/// PCM 16bit single-channel.
PCM16Mono=ctru_sys::NDSP_FORMAT_MONO_PCM16,
PCM16Mono=ctru_sys::NDSP_FORMAT_MONO_PCM16,
/// PCM 8bit interleaved dual-channel.
PCM8Stereo=ctru_sys::NDSP_FORMAT_STEREO_PCM8,
PCM8Stereo=ctru_sys::NDSP_FORMAT_STEREO_PCM8,
/// PCM 16bit interleaved dual-channel.
PCM16Stereo=ctru_sys::NDSP_FORMAT_STEREO_PCM16,
PCM16Stereo=ctru_sys::NDSP_FORMAT_STEREO_PCM16,
}
}
/// Representation of volume mix for a channel.
/// Representation of the volume mix for a channel.
#[derive(Copy, Clone, Debug, PartialEq)]
#[derive(Copy, Clone, Debug, PartialEq)]
pubstructAudioMix{
pubstructAudioMix{
raw: [f32;12],
raw: [f32;12],
}
}
/// Auxiliary Device index.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(usize)]
pubenumAuxDevice{
/// Aux device with index 0.
Zero=0,
/// Aux device with index 1.
One=1,
}
/// Interpolation used between audio frames.
#[doc(alias = "ndspInterpType")]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(u32)]
#[repr(u32)]
pubenumInterpolationType{
pubenumInterpolationType{
/// Polyphase interpolation.
Polyphase=ctru_sys::NDSP_INTERP_POLYPHASE,
Polyphase=ctru_sys::NDSP_INTERP_POLYPHASE,
/// Linear interpolation.
Linear=ctru_sys::NDSP_INTERP_LINEAR,
Linear=ctru_sys::NDSP_INTERP_LINEAR,
/// No interpolation.
None=ctru_sys::NDSP_INTERP_NONE,
None=ctru_sys::NDSP_INTERP_NONE,
}
}
/// Errors returned by [`ndsp`](self) functions.
#[non_exhaustive]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pubenumNdspError{
pubenumError{
/// Channel ID
/// Channel with the specified ID does not exist.
InvalidChannel(u8),
InvalidChannel(u8),
/// Channel ID
/// Channel with the specified ID is already being used.
ChannelAlreadyInUse(u8),
ChannelAlreadyInUse(u8),
/// Channel ID
/// The wave is already busy playing in the channel with the specified ID.
WaveBusy(u8),
WaveBusy(u8),
/// Sample amount requested, Max sample amount
/// The sample amount requested was larger than the maximum.
SampleCountOutOfBounds(usize,usize),
SampleCountOutOfBounds(usize,usize),
}
}
/// NDSP Channel representation.
///
/// There are 24 individual channels in total and each can play a different audio [`Wave`] simultaneuosly.
///
/// # Default
///
/// NDSP initialises all channels with default values on initialization, but the developer is supposed to change these values to correctly work with the service.
///
/// In particular:
/// - Default audio format is set to [`AudioFormat::PCM16Mono`].
/// - Default sample rate is set to 1 Hz.
/// - Default interpolation type is set to [`InterpolationType::Polyphase`].
/// - Default mix is set to [`AudioMix::default()`]
///
/// The handle to a channel can be retrieved with [`Ndsp::channel()`]
pubstructChannel<'ndsp>{
pubstructChannel<'ndsp>{
id: u8,
id: u8,
_rf: RefMut<'ndsp,()>,// we don't need to hold any data
_rf: RefMut<'ndsp,()>,// we don't need to hold any data
}
}
staticNDSP_ACTIVE: Mutex<usize>=Mutex::new(0);
staticNDSP_ACTIVE: Mutex<()>=Mutex::new(());
/// Handler of the DSP service and DSP processor.
/// Handle to the DSP service.
///
///
/// This is the main struct to handle audio playback using the 3DS' speakers and headphone jack.
/// Only one handle for this service can exist at a time.
/// Only one "instance" of this struct can exist at a time.
//! This service lets the application access a virtual mounted device created using a folder included within the application bundle.
//! 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>`.
//!
//! # Usage
//!
//!
//! This module only gets compiled if the configured RomFS directory is found and the `romfs`
//! This module only gets compiled if the configured RomFS directory is found and the `romfs`
//! feature is enabled.
//! feature is enabled.
//!
//!
//! Configure the path in Cargo.toml (the default path is "romfs"). Paths are relative to the
//! Configure the path in your project's `Cargo.toml` manifest (the default path is "romfs"). Paths are relative to the
//! `CARGO_MANIFEST_DIR` environment variable, which is the directory containing the manifest of
//! `CARGO_MANIFEST_DIR` environment variable, which is the directory containing the manifest of
//! your package.
//! your package.
//!
//!
@ -11,6 +16,16 @@
//! [package.metadata.cargo-3ds]
//! [package.metadata.cargo-3ds]
//! romfs_dir = "romfs"
//! romfs_dir = "romfs"
//! ```
//! ```
//!
//! Alternatively, you can include the RomFS archive manually when building with `3dsxtool`.
//!
//! # Notes
//!
//! `std::path` has problems when parsing file paths that include the `romfs:` prefix.
//! As such, it's suggested to use the paths directly or to do simple append operations to avoid unexpected behaviour.
//! Related [issue](https://github.com/rust-lang/rust/issues/52331).
#![doc(alias = "embed")]
#![doc(alias = "filesystem")]
usecrate::error::ResultCode;
usecrate::error::ResultCode;
usestd::ffi::CStr;
usestd::ffi::CStr;
@ -18,17 +33,37 @@ use std::sync::Mutex;
usecrate::services::ServiceReference;
usecrate::services::ServiceReference;
/// Handle to the RomFS service.
pubstructRomFS{
pubstructRomFS{
_service_handler: ServiceReference,
_service_handler: ServiceReference,
}
}
staticROMFS_ACTIVE: Mutex<usize>=Mutex::new(0);
staticROMFS_ACTIVE: Mutex<()>=Mutex::new(());
implRomFS{
implRomFS{
/// Mount the bundled RomFS archive as a virtual drive.
///
/// # Example
///
/// ```
/// # let _runner = test_runner::GdbRunner::default();
/// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> {
/// #
/// use ctru::services::romfs::RomFS;
///
/// let romfs = RomFS::new()?;
///
/// // Remember to include the RomFS archive and to use your actual files!
/// let contents = std::fs::read_to_string("romfs:/test-file.txt");
//! By using this service the program enables the use of network sockets and utilities such as those found in `std::net`, which are completely inaccessible by default.
//! As such, remember to hold a handle to this service handle while using any network functionality, or else the `std::net` methods will return generic OS errors.
#![doc(alias = "socket")]
#![doc(alias = "network")]
uselibc::memalign;
uselibc::memalign;
usestd::net::Ipv4Addr;
usestd::net::Ipv4Addr;
@ -8,37 +13,66 @@ use crate::error::ResultCode;
usecrate::services::ServiceReference;
usecrate::services::ServiceReference;
usecrate::Error;
usecrate::Error;
/// Network socket service
/// Handle to the Network Socket service.
///
/// Initializing this service will enable the use of network sockets and utilities
/// such as those found in `std::net`. The service will close once this struct gets dropped.
pubstructSoc{
pubstructSoc{
_service_handler: ServiceReference,
_service_handler: ServiceReference,
sock_3dslink: libc::c_int,
sock_3dslink: libc::c_int,
}
}
staticSOC_ACTIVE: Mutex<usize>=Mutex::new(0);
staticSOC_ACTIVE: Mutex<()>=Mutex::new(());
implSoc{
implSoc{
/// Initialize the Soc service with a default buffer size of 0x100000 bytes
/// Initialize a new service handle using a socket buffer size of `0x100000` bytes.
///
///
/// # Errors
/// # Errors
///
///
/// This function will return an error if the `Soc` service is already initialized
/// This function will return an error if the [`Soc`] service is already being used.
///
/// # Example
///
/// ```
/// # let _runner = test_runner::GdbRunner::default();
/// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> {
/// #
/// use ctru::services::soc::Soc;
///
/// let soc = Soc::new()?;
/// #
/// # Ok(())
/// # }
/// ```
#[doc(alias = "socInit")]
pubfnnew()-> crate::Result<Self>{
pubfnnew()-> crate::Result<Self>{
Self::init_with_buffer_size(0x100000)
Self::init_with_buffer_size(0x100000)
}
}
/// Initialize the Soc service with a custom buffer size in bytes. The size should be
/// Initialize a new service handle using a custom socket buffer size.
/// 0x100000 bytes or greater.
///
/// The size should be `0x100000` bytes or greater.
///
///
/// # Errors
/// # Errors
///
///
/// This function will return an error if the `Soc` service is already initialized
/// This function will return an error if the [`Soc`] service is already being used.
///
/// # Example
///
/// ```
/// # let _runner = test_runner::GdbRunner::default();
/// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> {
/// #
/// use ctru::services::soc::Soc;
///
/// let soc = Soc::init_with_buffer_size(0x100000)?;