Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,9 @@ rust-toolchain.toml

# Pica200 output files
*.shbin

# Various dev tools
.idea
.bacon-locations
bacon.toml
justfile
10 changes: 5 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
[workspace]
members = [
"citro3d",
"citro3d-sys",
"citro3d-macros",
]
members = ["citro3d", "citro3d-sys", "citro3d-macros", "citro2d-sys", "citro2d"]
default-members = [
"citro3d",
"citro3d-sys",
"citro3d-macros",
"citro2d-sys",
"citro2d",
]
resolver = "2"

[patch."https://github.com/rust3ds/citro3d-rs.git"]
citro3d = { path = "citro3d" }
citro3d-sys = { path = "citro3d-sys" }
citro3d-macros = { path = "citro3d-macros" }
citro2d-sys = { path = "citro2d-sys" }
citro2d = { path = "citro2d" }
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
⚠️ WIP ⚠️

Rust bindings and safe wrapper to the [citro3d](https://github.com/devkitPro/citro3d)
library, to write homebrew graphical programs for the Nintendo 3DS.
and [citro2d](https://github.com/devkitPro/citro2d) library, to write homebrew graphical programs for the Nintendo 3DS.

## Crates

Expand All @@ -15,5 +15,5 @@ library, to write homebrew graphical programs for the Nintendo 3DS.

## License

* `citro3d-sys` is licensed under Zlib
* `citro3d-sys` and `citro2d-sys` is licensed under Zlib
* `citro3d` and `citro3d-macros` are dual-licensed under MIT or Apache-2.0
20 changes: 20 additions & 0 deletions citro2d-sys/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
[package]
name = "citro2d-sys"
version = "0.1.0"
authors = ["Rust3DS Org", "Bailey Townsend"]
edition = "2021"
license = "Zlib"
links = "citro2d"

[dependencies]
libc = "0.2.116"
ctru-sys = { git = "https://github.com/rust3ds/ctru-rs.git" }
citro3d-sys = { path = "../citro3d-sys" }

[build-dependencies]
bindgen = { version = "0.68.1", features = ["experimental"] }
cc = "1.0.83"
doxygen-rs = "0.4.2"

[dev-dependencies]
shim-3ds = { git = "https://github.com/rust3ds/shim-3ds.git" }
18 changes: 18 additions & 0 deletions citro2d-sys/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
As with the original citro3d, this library is licensed under zlib.

This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any
damages arising from the use of this software.

Permission is granted to anyone to use this software for any
purpose, including commercial applications, and to alter it and
redistribute it freely, subject to the following restrictions:

1. The origin of this software must not be misrepresented; you
must not claim that you wrote the original software. If you use
this software in a product, an acknowledgment in the product
documentation would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and
must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
10 changes: 10 additions & 0 deletions citro2d-sys/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# citro2d-sys

Rust bindings to [`citro2d`](https://github.com/devkitPro/citro2d).
Bindings are generated at build time using the locally-installed devkitPro.

[Documentation](https://rust3ds.github.io/citro3d-rs/crates/citro2d_sys) is generated from the
`main` branch, and should generally be up to date with the latest devkitPro.
This will be more useful than [docs.rs](https://docs.rs/crates/citro2d), since
the bindings are generated at build time and `docs.rs`' build environment does not
have a copy of devkitPro to generate bindings from.
167 changes: 167 additions & 0 deletions citro2d-sys/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
//! This build script generates bindings from `citro2d` on the fly at compilation
//! time into `OUT_DIR`, from which they can be included into `lib.rs`.

use std::env;
use std::iter::FromIterator;
use std::path::{Path, PathBuf};

use bindgen::callbacks::{DeriveTrait, ImplementsTrait, ParseCallbacks};
use bindgen::{Builder, RustTarget};

fn main() {
let devkitpro = env::var("DEVKITPRO").expect("DEVKITPRO not set in environment");
println!("cargo:rerun-if-env-changed=DEVKITPRO");

let devkitarm = std::env::var("DEVKITARM").expect("DEVKITARM not set in environment");
println!("cargo:rerun-if-env-changed=DEVKITARM");

let debug_symbols = env::var("DEBUG").unwrap();
println!("cargo:rerun-if-env-changed=DEBUG");

let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
println!("cargo:rerun-if-env-changed=OUT_DIR");

println!("cargo:rerun-if-changed=build.rs");
println!("cargo:rustc-link-search=native={devkitpro}/libctru/lib");
println!(
"cargo:rustc-link-lib=static={}",
match debug_symbols.as_str() {
// Based on valid values described in
// https://doc.rust-lang.org/cargo/reference/profiles.html#debug
"0" | "false" | "none" => "citro2d",
_ => "citro2dd",
}
);

println!(
"cargo:rustc-link-lib=static={}",
match debug_symbols.as_str() {
// Based on valid values described in
// https://doc.rust-lang.org/cargo/reference/profiles.html#debug
"0" | "false" | "none" => "citro3d",
_ => "citro3dd",
}
);

let include_path = PathBuf::from_iter([devkitpro.as_str(), "libctru", "include"]);
let citro2d_h = include_path.join("citro2d.h");
let three_ds_h = include_path.join("3ds.h");

let sysroot = Path::new(devkitarm.as_str()).join("arm-none-eabi");
let system_include = sysroot.join("include");
let static_fns_path = Path::new("citro2d_statics_wrapper");

let gcc_dir = PathBuf::from_iter([devkitarm.as_str(), "lib", "gcc", "arm-none-eabi"]);

let gcc_include = gcc_dir
.read_dir()
.unwrap()
// Assuming that there is only one gcc version of libs under the devkitARM dir
.next()
.unwrap()
.unwrap()
.path()
.join("include");

let bindings = Builder::default()
.header(three_ds_h.to_str().unwrap())
.header(citro2d_h.to_str().unwrap())
.rust_target(RustTarget::Nightly)
.use_core()
.trust_clang_mangling(false)
.layout_tests(false)
.ctypes_prefix("::libc")
.prepend_enum_name(false)
.fit_macro_constants(true)
.raw_line("use ctru_sys::*;")
.raw_line("use libc::FILE;")
.must_use_type("Result")
.blocklist_type("u(8|16|32|64)")
.blocklist_type("FILE")
.opaque_type("(GPU|GFX)_.*")
.opaque_type("float24Uniform_s")
.allowlist_file(".*/c2d/.*[.]h")
.blocklist_file(".*/3ds/.*[.]h")
.blocklist_file(".*/sys/.*[.]h")
.wrap_static_fns(true)
.wrap_static_fns_path(out_dir.join(static_fns_path))
.clang_args([
"--target=arm-none-eabi",
"--sysroot",
sysroot.to_str().unwrap(),
"-isystem",
system_include.to_str().unwrap(),
"-isystem",
gcc_include.to_str().unwrap(),
"-I",
include_path.to_str().unwrap(),
"-mfloat-abi=hard",
"-march=armv6k",
"-mtune=mpcore",
"-mfpu=vfp",
"-DARM11 ",
"-D_3DS ",
"-D__3DS__ ",
"-fshort-enums",
])
.parse_callbacks(Box::new(CustomCallbacks))
.generate()
.expect("Unable to generate bindings");

bindings
.write_to_file(out_dir.join("bindings.rs"))
.expect("failed to write bindings");

// Compile static inline fns wrapper
let cc = Path::new(devkitarm.as_str()).join("bin/arm-none-eabi-gcc");
let ar = Path::new(devkitarm.as_str()).join("bin/arm-none-eabi-ar");

cc::Build::new()
.compiler(cc)
.archiver(ar)
.include(&include_path)
.file(out_dir.join(static_fns_path.with_extension("c")))
.flag("-march=armv6k")
.flag("-mtune=mpcore")
.flag("-mfloat-abi=hard")
.flag("-mfpu=vfp")
.flag("-mtp=soft")
.flag("-Wno-deprecated-declarations")
.compile("citro2d_statics_wrapper");
}

/// Custom callback struct to allow us to mark some "known good types" as
/// [`Copy`], which in turn allows using Rust `union` instead of bindgen union types. See
/// <https://rust-lang.github.io/rust-bindgen/using-unions.html#which-union-type-will-bindgen-generate>
/// for more info.
///
/// We do the same for [`Debug`] just for the convenience of derived Debug impls
/// on some `citro2d` types.
///
/// Finally, we use [`doxygen_rs`] to transform the doc comments into something
/// easier to read in the generated documentation / hover documentation.
#[derive(Debug)]
struct CustomCallbacks;

impl ParseCallbacks for CustomCallbacks {
fn process_comment(&self, comment: &str) -> Option<String> {
Some(doxygen_rs::transform(comment))
}

fn blocklisted_type_implements_trait(
&self,
name: &str,
derive_trait: DeriveTrait,
) -> Option<ImplementsTrait> {
if let DeriveTrait::Copy | DeriveTrait::Debug = derive_trait {
match name {
"u64_" | "u32_" | "u16_" | "u8_" | "u64" | "u32" | "u16" | "u8" | "gfxScreen_t"
| "gfx3dSide_t" => Some(ImplementsTrait::Yes),
_ if name.starts_with("GPU_") => Some(ImplementsTrait::Yes),
_ => None,
}
} else {
None
}
}
}
19 changes: 19 additions & 0 deletions citro2d-sys/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#![no_std]
#![allow(non_snake_case)]
#![allow(warnings)]
#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(clippy::all)]
#![doc(html_root_url = "https://rust3ds.github.io/citro3d-rs/crates")]
#![doc(
html_favicon_url = "https://user-images.githubusercontent.com/11131775/225929072-2fa1741c-93ae-4b47-9bdf-af70f3d59910.png"
)]
#![doc(
html_logo_url = "https://user-images.githubusercontent.com/11131775/225929072-2fa1741c-93ae-4b47-9bdf-af70f3d59910.png"
)]

include!(concat!(env!("OUT_DIR"), "/bindings.rs"));

// Prevent linking errors from the standard `test` library when running `cargo 3ds test --lib`.
#[cfg(test)]
extern crate shim_3ds;
22 changes: 22 additions & 0 deletions citro2d/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[package]
name = "citro2d"
version = "0.1.0"
edition = "2024"
license = "MIT OR Apache-2.0"

[dependencies]
document-features = "0.2.7"
ctru-rs = { git = "https://github.com/rust3ds/ctru-rs.git" }
ctru-sys = { git = "https://github.com/rust3ds/ctru-rs.git" }
citro2d-sys = { path = "../citro2d-sys" }
citro3d = { version = "0.1.0", path = "../citro3d" }
citro3d-sys = { version = "0.1.0", path = "../citro3d-sys" }

[dev-dependencies]
test-runner = { git = "https://github.com/rust3ds/ctru-rs.git" }

[package.metadata.docs.rs]
all-features = true
default-target = "armv6k-nintendo-3ds"
targs = []
cargo-args = ["-Z", "build-std"]
Loading