From 4dd2196971772a910849fdb9423c51549dae2a23 Mon Sep 17 00:00:00 2001 From: AzureMarker Date: Mon, 31 Jan 2022 20:01:25 -0800 Subject: [PATCH 01/15] Add a basic futures example --- ctru-rs/Cargo.toml | 1 + ctru-rs/examples/futures-basic.rs | 66 +++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 ctru-rs/examples/futures-basic.rs diff --git a/ctru-rs/Cargo.toml b/ctru-rs/Cargo.toml index 1b303f3..4635fa6 100644 --- a/ctru-rs/Cargo.toml +++ b/ctru-rs/Cargo.toml @@ -25,6 +25,7 @@ toml = "0.5" [dev-dependencies] ferris-says = "0.2.1" +futures = "0.3" time = "0.3.7" [features] diff --git a/ctru-rs/examples/futures-basic.rs b/ctru-rs/examples/futures-basic.rs new file mode 100644 index 0000000..5cac1fb --- /dev/null +++ b/ctru-rs/examples/futures-basic.rs @@ -0,0 +1,66 @@ +//! This example runs a basic future executor from the `futures` library. +//! Every 60 frames (about 1 second) it prints "Tick" to the console. +//! The executor runs on a separate thread. Internally it yields when it has no more work to do, +//! allowing other threads to run. +//! The example also implements clean shutdown by using a oneshot channel to end the future, thus +//! ending the executor and the thread it runs on. + +use ctru::console::Console; +use ctru::services::hid::KeyPad; +use ctru::services::{Apt, Hid}; +use ctru::Gfx; +use futures::StreamExt; + +fn main() { + ctru::init(); + let gfx = Gfx::default(); + let hid = Hid::init().expect("Couldn't obtain HID controller"); + let apt = Apt::init().expect("Couldn't obtain APT controller"); + let _console = Console::init(gfx.top_screen.borrow_mut()); + + println!("Starting executor..."); + + let (exit_sender, mut exit_receiver) = futures::channel::oneshot::channel(); + let (mut timer_sender, mut timer_receiver) = futures::channel::mpsc::channel(0); + let executor_thread = ctru::thread::spawn(move || { + let mut executor = futures::executor::LocalPool::new(); + + executor.run_until(async move { + loop { + futures::select! { + _ = exit_receiver => break, + _ = timer_receiver.next() => { + println!("Tick"); + } + } + } + }); + }); + + println!("Executor started!"); + + let mut frame_count = 0; + while apt.main_loop() { + hid.scan_input(); + + if hid.keys_down().contains(KeyPad::KEY_START) { + println!("Shutting down..."); + let _ = exit_sender.send(()); + let _ = executor_thread.join(); + break; + } + + frame_count += 1; + + if frame_count == 60 { + if let Err(e) = timer_sender.try_send(()) { + println!("Error sending timer message: {e}"); + } + frame_count = 0; + } + + gfx.flush_buffers(); + gfx.swap_buffers(); + gfx.wait_for_vblank(); + } +} From 5c5ac7ef6686b5fecd9b8471a30292aabe9801ee Mon Sep 17 00:00:00 2001 From: AzureMarker Date: Mon, 31 Jan 2022 20:01:41 -0800 Subject: [PATCH 02/15] Ignore IDE files --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index c34b8d3..54f1838 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ target Cargo.lock .cargo + +# IDE files +.idea From c13c1c8c6855513cecdf08be677cd99c9b55cdbd Mon Sep 17 00:00:00 2001 From: AzureMarker Date: Tue, 1 Feb 2022 17:07:43 -0800 Subject: [PATCH 03/15] Run executor thread on system core --- ctru-rs/examples/futures-basic.rs | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/ctru-rs/examples/futures-basic.rs b/ctru-rs/examples/futures-basic.rs index 5cac1fb..77d20ca 100644 --- a/ctru-rs/examples/futures-basic.rs +++ b/ctru-rs/examples/futures-basic.rs @@ -22,20 +22,23 @@ fn main() { let (exit_sender, mut exit_receiver) = futures::channel::oneshot::channel(); let (mut timer_sender, mut timer_receiver) = futures::channel::mpsc::channel(0); - let executor_thread = ctru::thread::spawn(move || { - let mut executor = futures::executor::LocalPool::new(); + let executor_thread = ctru::thread::Builder::new() + .affinity(1) + .spawn(move || { + let mut executor = futures::executor::LocalPool::new(); - executor.run_until(async move { - loop { - futures::select! { - _ = exit_receiver => break, - _ = timer_receiver.next() => { - println!("Tick"); + executor.run_until(async move { + loop { + futures::select! { + _ = exit_receiver => break, + _ = timer_receiver.next() => { + println!("Tick"); + } } } - } - }); - }); + }); + }) + .expect("Failed to create executor thread"); println!("Executor started!"); From 39adc8590e0806767b1498d3e025409688b301f0 Mon Sep 17 00:00:00 2001 From: AzureMarker Date: Tue, 1 Feb 2022 18:30:49 -0800 Subject: [PATCH 04/15] Enable the system core by setting our time limit percentage --- ctru-rs/examples/futures-basic.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ctru-rs/examples/futures-basic.rs b/ctru-rs/examples/futures-basic.rs index 77d20ca..c48964b 100644 --- a/ctru-rs/examples/futures-basic.rs +++ b/ctru-rs/examples/futures-basic.rs @@ -18,6 +18,10 @@ fn main() { let apt = Apt::init().expect("Couldn't obtain APT controller"); let _console = Console::init(gfx.top_screen.borrow_mut()); + // Give ourselves up to 30% of the system core's time + apt.set_app_cpu_time_limit(30) + .expect("Failed to enable system core"); + println!("Starting executor..."); let (exit_sender, mut exit_receiver) = futures::channel::oneshot::channel(); From 82eded005e849cc2f373f44cd34a36fc028bbb91 Mon Sep 17 00:00:00 2001 From: AzureMarker Date: Wed, 2 Feb 2022 20:14:53 -0800 Subject: [PATCH 05/15] Remove duplicate dependencies by patching, and add back links field We don't want duplicate ctru-sys crates in the dependency graph. Same for libc. We can patch them here to avoid any issues. Now that we don't have duplicate ctru-sys crates, we can add back the links field. This field is important because it warns us when there are two crates linking to the same library (ex. duplicate ctru-sys crates). --- Cargo.toml | 8 ++++++++ ctru-sys/Cargo.toml | 1 + 2 files changed, 9 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 65c4a2f..80b876e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,2 +1,10 @@ [workspace] members = ["ctru-rs", "ctru-sys"] + +[patch.crates-io] +# We have some changes not in the upstream +libc = { git = "https://github.com/Meziu/libc.git" } + +[patch.'https://github.com/Meziu/ctru-rs'] +# Make sure all dependencies use the local ctru-sys package +ctru-sys = { path = "ctru-sys" } \ No newline at end of file diff --git a/ctru-sys/Cargo.toml b/ctru-sys/Cargo.toml index 46bed0b..8c059bb 100644 --- a/ctru-sys/Cargo.toml +++ b/ctru-sys/Cargo.toml @@ -3,6 +3,7 @@ name = "ctru-sys" version = "0.4.0" authors = ["Ronald Kinard "] license = "https://en.wikipedia.org/wiki/Zlib_License" +links = "ctru" edition = "2021" [dependencies] From c061240dfcf87bc5d3a1b588977bc4c270746b82 Mon Sep 17 00:00:00 2001 From: AzureMarker Date: Wed, 2 Feb 2022 21:41:33 -0800 Subject: [PATCH 06/15] Add futures-tokio-basic example (not working yet) There's some issues with the example right now. See https://github.com/Meziu/ctru-rs/pull/36#issuecomment-1028617954 --- ctru-rs/Cargo.toml | 1 + ctru-rs/examples/futures-tokio-basic.rs | 65 +++++++++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 ctru-rs/examples/futures-tokio-basic.rs diff --git a/ctru-rs/Cargo.toml b/ctru-rs/Cargo.toml index 1b303f3..ff327f5 100644 --- a/ctru-rs/Cargo.toml +++ b/ctru-rs/Cargo.toml @@ -26,6 +26,7 @@ toml = "0.5" [dev-dependencies] ferris-says = "0.2.1" time = "0.3.7" +tokio = { version = "1.16", features = ["rt", "time", "sync", "macros"] } [features] default = ["romfs"] diff --git a/ctru-rs/examples/futures-tokio-basic.rs b/ctru-rs/examples/futures-tokio-basic.rs new file mode 100644 index 0000000..e8e8775 --- /dev/null +++ b/ctru-rs/examples/futures-tokio-basic.rs @@ -0,0 +1,65 @@ +use ctru::console::Console; +use ctru::services::hid::KeyPad; +use ctru::services::{Apt, Hid}; +use ctru::Gfx; +use std::time::Duration; + +fn main() { + ctru::init(); + let gfx = Gfx::default(); + let hid = Hid::init().expect("Couldn't obtain HID controller"); + let apt = Apt::init().expect("Couldn't obtain APT controller"); + let _console = Console::init(gfx.top_screen.borrow_mut()); + + // FIXME: replace this with `Ps` when #39 merges + assert!(unsafe { ctru_sys::psInit() } >= 0); + + // Give ourselves up to 30% of the system core's time + apt.set_app_cpu_time_limit(30) + .expect("Failed to enable system core"); + + println!("Starting runtime..."); + + let (exit_sender, mut exit_receiver) = tokio::sync::oneshot::channel(); + let runtime = tokio::runtime::Builder::new_current_thread() + .enable_time() + .build() + .expect("Couldn't build runtime"); + + let runtime_thread = ctru::thread::Builder::new() + .affinity(1) + .spawn(move || { + runtime.block_on(async move { + let mut wake_time = tokio::time::Instant::now() + Duration::from_secs(1); + loop { + let sleep_future = tokio::time::sleep_until(wake_time); + + tokio::select! { + _ = &mut exit_receiver => break, + _ = sleep_future => { + println!("Tick"); + wake_time += Duration::from_secs(1); + } + } + } + }); + }) + .expect("Failed to create runtime thread"); + + println!("Runtime started!"); + + while apt.main_loop() { + hid.scan_input(); + + if hid.keys_down().contains(KeyPad::KEY_START) { + println!("Shutting down..."); + let _ = exit_sender.send(()); + let _ = runtime_thread.join(); + break; + } + + gfx.flush_buffers(); + gfx.swap_buffers(); + gfx.wait_for_vblank(); + } +} From 54085101f33d56099afe3f45341f712a758418fe Mon Sep 17 00:00:00 2001 From: Andrea Ciliberti Date: Fri, 4 Feb 2022 16:53:03 +0100 Subject: [PATCH 07/15] Bumped ctru-sys to latest libctru version --- ctru-sys/Cargo.toml | 2 +- ctru-sys/bindgen.sh | 2 +- ctru-sys/src/bindings.rs | 114 ++++++++++++++++++++++++++++++++++++--- 3 files changed, 109 insertions(+), 9 deletions(-) diff --git a/ctru-sys/Cargo.toml b/ctru-sys/Cargo.toml index 8c059bb..3cde33c 100644 --- a/ctru-sys/Cargo.toml +++ b/ctru-sys/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ctru-sys" -version = "0.4.0" +version = "0.4.1" authors = ["Ronald Kinard "] license = "https://en.wikipedia.org/wiki/Zlib_License" links = "ctru" diff --git a/ctru-sys/bindgen.sh b/ctru-sys/bindgen.sh index 247f6b3..3a25578 100755 --- a/ctru-sys/bindgen.sh +++ b/ctru-sys/bindgen.sh @@ -26,5 +26,5 @@ bindgen "$DEVKITPRO/libctru/include/3ds.h" \ -mtune=mpcore \ -mfpu=vfp \ -DARM11 \ - -D_3DS \ + -D__3DS__ \ > src/bindings.rs diff --git a/ctru-sys/src/bindings.rs b/ctru-sys/src/bindings.rs index 612cf41..5e6aafa 100644 --- a/ctru-sys/src/bindings.rs +++ b/ctru-sys/src/bindings.rs @@ -215,8 +215,12 @@ pub const CUR_PROCESS_HANDLE: u32 = 4294934529; pub const ARBITRATION_SIGNAL_ALL: i32 = -1; pub const CUR_THREAD_HANDLE: u32 = 4294934528; pub const SYSCLOCK_SOC: u32 = 16756991; +pub const SYSCLOCK_SYS: u32 = 33513982; +pub const SYSCLOCK_SDMMC: u32 = 67027964; pub const SYSCLOCK_ARM9: u32 = 134055928; pub const SYSCLOCK_ARM11: u32 = 268111856; +pub const SYSCLOCK_ARM11_LGR1: u32 = 536223712; +pub const SYSCLOCK_ARM11_LGR2: u32 = 804335568; pub const SYSCLOCK_ARM11_NEW: u32 = 804335568; pub const CPU_TICKS_PER_MSEC: f64 = 268111.856; pub const CPU_TICKS_PER_USEC: f64 = 268.111856; @@ -277,6 +281,8 @@ pub const CONSOLE_BLINK_FAST: u32 = 32; pub const CONSOLE_COLOR_REVERSE: u32 = 64; pub const CONSOLE_CONCEAL: u32 = 128; pub const CONSOLE_CROSSED_OUT: u32 = 256; +pub const CONSOLE_FG_CUSTOM: u32 = 512; +pub const CONSOLE_BG_CUSTOM: u32 = 1024; pub const __GNUCLIKE_ASM: u32 = 3; pub const __GNUCLIKE___TYPEOF: u32 = 1; pub const __GNUCLIKE___OFFSETOF: u32 = 1; @@ -1216,6 +1222,7 @@ pub const MIISELECTOR_GUESTMII_SLOTS: u32 = 6; pub const MIISELECTOR_USERMII_SLOTS: u32 = 100; pub const MIISELECTOR_GUESTMII_NAME_LEN: u32 = 12; pub const ARCHIVE_DIRITER_MAGIC: u32 = 1751347809; +pub const LINK3DS_COMM_PORT: u32 = 17491; pub type __int8_t = ::libc::c_schar; pub type __uint8_t = ::libc::c_uchar; pub type __int16_t = ::libc::c_short; @@ -3446,7 +3453,10 @@ extern "C" { fb_b: *const ::libc::c_void, stride: u32_, mode: u32_, - ); + ) -> bool; +} +extern "C" { + pub fn gspIsPresentPending(screen: ::libc::c_uint) -> bool; } extern "C" { pub fn gspSetEventCallback( @@ -3628,8 +3638,8 @@ pub struct PrintConsole { pub windowWidth: ::libc::c_int, pub windowHeight: ::libc::c_int, pub tabSize: ::libc::c_int, - pub fg: ::libc::c_int, - pub bg: ::libc::c_int, + pub fg: u16_, + pub bg: u16_, pub flags: ::libc::c_int, pub PrintChar: ConsolePrint, pub consoleInitialised: bool, @@ -4040,12 +4050,26 @@ extern "C" { extern "C" { pub fn mappableFree(mem: *mut ::libc::c_void); } +pub const VRAM_ALLOC_A: vramAllocPos = 1; +pub const VRAM_ALLOC_B: vramAllocPos = 2; +pub const VRAM_ALLOC_ANY: vramAllocPos = 3; +pub type vramAllocPos = ::libc::c_uint; extern "C" { pub fn vramAlloc(size: size_t) -> *mut ::libc::c_void; } +extern "C" { + pub fn vramAllocAt(size: size_t, pos: vramAllocPos) -> *mut ::libc::c_void; +} extern "C" { pub fn vramMemAlign(size: size_t, alignment: size_t) -> *mut ::libc::c_void; } +extern "C" { + pub fn vramMemAlignAt( + size: size_t, + alignment: size_t, + pos: vramAllocPos, + ) -> *mut ::libc::c_void; +} extern "C" { pub fn vramRealloc(mem: *mut ::libc::c_void, size: size_t) -> *mut ::libc::c_void; } @@ -4414,6 +4438,13 @@ impl Default for FS_Path { } } } +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FS_SdMmcSpeedInfo { + pub highSpeedModeEnabled: bool, + pub usesHighestClockRate: bool, + pub sdClkCtrl: u16_, +} pub type FS_Archive = u64_; extern "C" { pub fn fsInit() -> Result; @@ -4553,10 +4584,10 @@ extern "C" { pub fn FSUSER_GetNandCid(out: *mut u8_, length: u32_) -> Result; } extern "C" { - pub fn FSUSER_GetSdmcSpeedInfo(speedInfo: *mut u32_) -> Result; + pub fn FSUSER_GetSdmcSpeedInfo(speedInfo: *mut FS_SdMmcSpeedInfo) -> Result; } extern "C" { - pub fn FSUSER_GetNandSpeedInfo(speedInfo: *mut u32_) -> Result; + pub fn FSUSER_GetNandSpeedInfo(speedInfo: *mut FS_SdMmcSpeedInfo) -> Result; } extern "C" { pub fn FSUSER_GetSdmcLog(out: *mut u8_, length: u32_) -> Result; @@ -5027,6 +5058,19 @@ pub struct AM_TWLPartitionInfo { pub titlesCapacity: u64_, pub titlesFreeSpace: u64_, } +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct AM_ContentInfo { + pub index: u16_, + pub type_: u16_, + pub contentId: u32_, + pub size: u64_, + pub flags: u8_, + pub padding: [u8_; 7usize], +} +pub const AM_CONTENT_DOWNLOADED: AM_ContentInfoFlags = 1; +pub const AM_CONTENT_OWNED: AM_ContentInfoFlags = 2; +pub type AM_ContentInfoFlags = ::libc::c_uint; extern "C" { pub fn amInit() -> Result; } @@ -5325,6 +5369,23 @@ extern "C" { extern "C" { pub fn AM_DeleteAllTwlTitles() -> Result; } +extern "C" { + pub fn AMAPP_GetDLCContentInfoCount( + count: *mut u32_, + mediatype: FS_MediaType, + titleID: u64_, + ) -> Result; +} +extern "C" { + pub fn AMAPP_ListDLCContentInfos( + contentInfoRead: *mut u32_, + mediatype: FS_MediaType, + titleID: u64_, + contentInfoCount: u32_, + offset: u32_, + contentInfos: *mut AM_ContentInfo, + ) -> Result; +} extern "C" { pub fn ampxiInit(servhandle: Handle) -> Result; } @@ -7305,10 +7366,10 @@ extern "C" { pub fn FSPXI_GetNandCid(serviceHandle: Handle, out: *mut ::libc::c_void, size: u32_) -> Result; } extern "C" { - pub fn FSPXI_GetSdmcSpeedInfo(serviceHandle: Handle, out: *mut u32_) -> Result; + pub fn FSPXI_GetSdmcSpeedInfo(serviceHandle: Handle, out: *mut FS_SdMmcSpeedInfo) -> Result; } extern "C" { - pub fn FSPXI_GetNandSpeedInfo(serviceHandle: Handle, out: *mut u32_) -> Result; + pub fn FSPXI_GetNandSpeedInfo(serviceHandle: Handle, out: *mut FS_SdMmcSpeedInfo) -> Result; } extern "C" { pub fn FSPXI_GetSdmcLog(serviceHandle: Handle, out: *mut ::libc::c_void, size: u32_) -> Result; @@ -10314,6 +10375,9 @@ extern "C" { extern "C" { pub fn ptmuExit(); } +extern "C" { + pub fn ptmuGetSessionHandle() -> *mut Handle; +} extern "C" { pub fn PTMU_GetShellState(out: *mut u8_) -> Result; } @@ -10361,6 +10425,9 @@ extern "C" { extern "C" { pub fn ptmSysmExit(); } +extern "C" { + pub fn ptmSysmGetSessionHandle() -> *mut Handle; +} extern "C" { pub fn PTMSYSM_RequestSleep() -> Result; } @@ -10379,6 +10446,9 @@ extern "C" { extern "C" { pub fn PTMSYSM_Awaken() -> Result; } +extern "C" { + pub fn PTMSYSM_SetUserTime(msY2k: s64) -> Result; +} extern "C" { pub fn PTMSYSM_InvalidateSystemTime() -> Result; } @@ -10400,6 +10470,30 @@ extern "C" { extern "C" { pub fn PTMSYSM_RebootAsync(timeout: u64_) -> Result; } +extern "C" { + pub fn ptmGetsInit() -> Result; +} +extern "C" { + pub fn ptmGetsExit(); +} +extern "C" { + pub fn ptmGetsGetSessionHandle() -> *mut Handle; +} +extern "C" { + pub fn PTMGETS_GetSystemTime(outMsY2k: *mut s64) -> Result; +} +extern "C" { + pub fn ptmSetsInit() -> Result; +} +extern "C" { + pub fn ptmSetsExit(); +} +extern "C" { + pub fn ptmSetsGetSessionHandle() -> *mut Handle; +} +extern "C" { + pub fn PTMSETS_SetSystemTime(msY2k: s64) -> Result; +} pub const WAIT_NONE: PXIDEV_WaitType = 0; pub const WAIT_SLEEP: PXIDEV_WaitType = 1; pub const WAIT_IREQ_RETURN: PXIDEV_WaitType = 2; @@ -13543,3 +13637,9 @@ extern "C" { extern "C" { pub fn gdbHioDevSystem(command: *const ::libc::c_char) -> ::libc::c_int; } +extern "C" { + pub static mut __3dslink_host: in_addr; +} +extern "C" { + pub fn link3dsConnectToHost(redirStdout: bool, redirStderr: bool) -> ::libc::c_int; +} From 88315aec61a21f5e37d35aaf109b9e259d9cc3f9 Mon Sep 17 00:00:00 2001 From: AzureMarker Date: Fri, 4 Feb 2022 22:27:13 -0800 Subject: [PATCH 08/15] Group together thread info calls --- ctru-rs/src/thread.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/ctru-rs/src/thread.rs b/ctru-rs/src/thread.rs index 225b1f8..14d7a56 100644 --- a/ctru-rs/src/thread.rs +++ b/ctru-rs/src/thread.rs @@ -1040,7 +1040,10 @@ mod thread_info { } pub fn set(thread: Thread) { - CTRU_THREAD_INFO.with(|c| assert!(c.borrow().is_none())); - CTRU_THREAD_INFO.with(move |c| *c.borrow_mut() = Some(ThreadInfo { thread })); + CTRU_THREAD_INFO.with(move |c| { + let mut thread_info = c.borrow_mut(); + assert!(thread_info.is_none()); + *thread_info = Some(ThreadInfo { thread }); + }); } } From 8ed7c97a5dc374b16c92bc1231c154f4bf64365e Mon Sep 17 00:00:00 2001 From: Andrea Ciliberti Date: Sat, 5 Feb 2022 15:51:28 +0100 Subject: [PATCH 09/15] Simple functions added to Ps and HashMap example --- ctru-rs/examples/hashmaps.rs | 39 +++++++++++++++++++++++++ ctru-rs/src/services/ps.rs | 56 ++++++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+) create mode 100644 ctru-rs/examples/hashmaps.rs diff --git a/ctru-rs/examples/hashmaps.rs b/ctru-rs/examples/hashmaps.rs new file mode 100644 index 0000000..d0aab20 --- /dev/null +++ b/ctru-rs/examples/hashmaps.rs @@ -0,0 +1,39 @@ +use ctru::console::Console; +use ctru::gfx::Gfx; +use ctru::services::apt::Apt; +use ctru::services::hid::{Hid, KeyPad}; +use ctru::services::ps::Ps; + +fn main() { + // Initialize services + ctru::init(); + let apt = Apt::init().unwrap(); + let hid = Hid::init().unwrap(); + let gfx = Gfx::default(); + let _console = Console::init(gfx.top_screen.borrow_mut()); + + // HashMaps generate hashes thanks to the 3DS' criptografically secure generator. + // Sadly, this generator is only active when activating the `Ps` service. + // To do this, we have to make sure the `Ps` service handle is alive for the whole + // run time (or at least, when `HashMaps` are used). + // Not having a living `Ps` instance when using `HashMap`s results in a panic + let _ps = Ps::init().unwrap(); + + let mut map = std::collections::HashMap::new(); + map.insert("A Key!", 102); + map.insert("Another key?", 543); + map.remove("A Key!"); + + println!("{:#?}", map); + + while apt.main_loop() { + gfx.flush_buffers(); + gfx.swap_buffers(); + gfx.wait_for_vblank(); + + hid.scan_input(); + if hid.keys_down().contains(KeyPad::KEY_START) { + break; + } + } +} diff --git a/ctru-rs/src/services/ps.rs b/ctru-rs/src/services/ps.rs index 5e9adfa..5b1ca27 100644 --- a/ctru-rs/src/services/ps.rs +++ b/ctru-rs/src/services/ps.rs @@ -8,6 +8,30 @@ #[non_exhaustive] pub struct Ps; +#[repr(u32)] +pub enum AESAlgorithm { + CbcEnc, + CbcDec, + CtrEnc, + CtrDec, + CcmEnc, + CcmDec, +} + +#[repr(u32)] +pub enum AESKeyType { + Keyslot0D, + Keyslot2D, + Keyslot31, + Keyslot38, + Keyslot32, + Keyslot39Dlp, + Keyslot2E, + KeyslotInvalid, + Keyslot36, + Keyslot39Nfc, +} + impl Ps { /// Initialize the PS module. pub fn init() -> crate::Result { @@ -18,6 +42,38 @@ impl Ps { Ok(Self) } } + + pub fn local_friend_code_seed(&self) -> crate::Result { + let mut seed: u64 = 0; + + let r = unsafe { ctru_sys::PS_GetLocalFriendCodeSeed(&mut seed) }; + if r < 0 { + Err(r.into()) + } else { + Ok(seed) + } + } + + pub fn device_id(&self) -> crate::Result { + let mut id: u32 = 0; + + let r = unsafe { ctru_sys::PS_GetDeviceId(&mut id) }; + if r < 0 { + Err(r.into()) + } else { + Ok(id) + } + } + + pub fn generate_random_bytes(&self, out: &mut [u8]) -> crate::Result<()> { + let r = + unsafe { ctru_sys::PS_GenerateRandomBytes(out as *mut _ as *mut _, out.len() as u32) }; + if r < 0 { + Err(r.into()) + } else { + Ok(()) + } + } } impl Drop for Ps { From 133df927c0602ef5ca622dc07463812f060d219d Mon Sep 17 00:00:00 2001 From: AzureMarker Date: Sat, 5 Feb 2022 12:02:00 -0800 Subject: [PATCH 10/15] Add thread locals example to make sure they work --- ctru-rs/examples/thread_locals.rs | 66 +++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 ctru-rs/examples/thread_locals.rs diff --git a/ctru-rs/examples/thread_locals.rs b/ctru-rs/examples/thread_locals.rs new file mode 100644 index 0000000..937855a --- /dev/null +++ b/ctru-rs/examples/thread_locals.rs @@ -0,0 +1,66 @@ +use ctru::console::Console; +use ctru::services::hid::KeyPad; +use ctru::services::{Apt, Hid}; +use ctru::Gfx; +use std::cell::RefCell; + +std::thread_local! { + static MY_LOCAL: RefCell<&'static str> = RefCell::new("initial value"); +} + +fn main() { + ctru::init(); + let gfx = Gfx::default(); + gfx.top_screen.borrow_mut().set_wide_mode(true); + let hid = Hid::init().expect("Couldn't obtain HID controller"); + let apt = Apt::init().expect("Couldn't obtain APT controller"); + let _console = Console::init(gfx.top_screen.borrow_mut()); + + // Give ourselves up to 30% of the system core's time + apt.set_app_cpu_time_limit(30) + .expect("Failed to enable system core"); + + MY_LOCAL.with(|local| { + println!("Initial value on main thread: {}", local.borrow()); + *local.borrow_mut() = "second value"; + }); + MY_LOCAL.with(|local| { + println!("Value on main thread after mutation: {}", local.borrow()); + }); + + ctru::thread::Builder::new() + .affinity(1) + .spawn(move || { + MY_LOCAL.with(|local| { + println!("Initial value on second thread: {}", local.borrow()); + *local.borrow_mut() = "third value"; + }); + MY_LOCAL.with(|local| { + println!("Value on second thread after mutation: {}", local.borrow()); + }); + }) + .expect("Failed to create thread") + .join() + .expect("Failed to join on thread"); + + MY_LOCAL.with(|local| { + println!( + "Value on main thread after second thread exits: {}", + local.borrow() + ); + }); + + println!("Press Start to exit"); + + while apt.main_loop() { + hid.scan_input(); + + if hid.keys_down().contains(KeyPad::KEY_START) { + break; + } + + gfx.flush_buffers(); + gfx.swap_buffers(); + gfx.wait_for_vblank(); + } +} From 368cf8bc0c4e79ef13ce73796f82ac84cddb33be Mon Sep 17 00:00:00 2001 From: AzureMarker Date: Sat, 5 Feb 2022 17:29:19 -0800 Subject: [PATCH 11/15] Increase stack size and add more detailed logs The default stack size is super small: 4096 bytes. This was causing the stack to overflow during normal processing. This was also not detected, so it corrupted the heap and led to odd behavior or aborts. See this long comment for more details and explanation: https://github.com/Meziu/ctru-rs/pull/42#issuecomment-1030724973 I bumped the stack size to 2MiB, which is the default for Rust's std threads. I have a few ideas to fix the underlying issues here (add a guard struct like std does for stack overflows, move 3ds-specific stuff like affinity into std but keep the impl in ctru-rs via linker hacks). Also added some more logging. --- ctru-rs/examples/futures-tokio-basic.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ctru-rs/examples/futures-tokio-basic.rs b/ctru-rs/examples/futures-tokio-basic.rs index e8e8775..cf70a85 100644 --- a/ctru-rs/examples/futures-tokio-basic.rs +++ b/ctru-rs/examples/futures-tokio-basic.rs @@ -28,16 +28,21 @@ fn main() { let runtime_thread = ctru::thread::Builder::new() .affinity(1) + .stack_size(0x200000) .spawn(move || { runtime.block_on(async move { + println!("Start of future"); let mut wake_time = tokio::time::Instant::now() + Duration::from_secs(1); + let mut iteration = 0; loop { + println!("Start of loop"); let sleep_future = tokio::time::sleep_until(wake_time); tokio::select! { _ = &mut exit_receiver => break, _ = sleep_future => { - println!("Tick"); + println!("Tick {}", iteration); + iteration += 1; wake_time += Duration::from_secs(1); } } From 282044068b38a771f32cda7839ff1aef435a9798 Mon Sep 17 00:00:00 2001 From: AzureMarker Date: Sat, 5 Feb 2022 17:38:33 -0800 Subject: [PATCH 12/15] Use the new Ps struct --- ctru-rs/examples/futures-tokio-basic.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/ctru-rs/examples/futures-tokio-basic.rs b/ctru-rs/examples/futures-tokio-basic.rs index cf70a85..5260e7d 100644 --- a/ctru-rs/examples/futures-tokio-basic.rs +++ b/ctru-rs/examples/futures-tokio-basic.rs @@ -1,5 +1,6 @@ use ctru::console::Console; use ctru::services::hid::KeyPad; +use ctru::services::ps::Ps; use ctru::services::{Apt, Hid}; use ctru::Gfx; use std::time::Duration; @@ -9,11 +10,9 @@ fn main() { let gfx = Gfx::default(); let hid = Hid::init().expect("Couldn't obtain HID controller"); let apt = Apt::init().expect("Couldn't obtain APT controller"); + let _ps = Ps::init().expect("Couldn't initialize PS service"); let _console = Console::init(gfx.top_screen.borrow_mut()); - // FIXME: replace this with `Ps` when #39 merges - assert!(unsafe { ctru_sys::psInit() } >= 0); - // Give ourselves up to 30% of the system core's time apt.set_app_cpu_time_limit(30) .expect("Failed to enable system core"); From 8bf2b276cf15dad83b5ae280b83a373392088a26 Mon Sep 17 00:00:00 2001 From: AzureMarker Date: Sat, 5 Feb 2022 18:07:45 -0800 Subject: [PATCH 13/15] Turn off randomness in tokio::select when more than one future is ready --- ctru-rs/examples/futures-tokio-basic.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ctru-rs/examples/futures-tokio-basic.rs b/ctru-rs/examples/futures-tokio-basic.rs index 5260e7d..423a45f 100644 --- a/ctru-rs/examples/futures-tokio-basic.rs +++ b/ctru-rs/examples/futures-tokio-basic.rs @@ -38,12 +38,15 @@ fn main() { let sleep_future = tokio::time::sleep_until(wake_time); tokio::select! { - _ = &mut exit_receiver => break, + // Use the first available future instead of randomizing + biased; + _ = sleep_future => { println!("Tick {}", iteration); iteration += 1; wake_time += Duration::from_secs(1); } + _ = &mut exit_receiver => break, } } }); From fd9b58498d2a61ea7959b5747fdae9557b12a189 Mon Sep 17 00:00:00 2001 From: AzureMarker Date: Sat, 5 Feb 2022 22:23:00 -0800 Subject: [PATCH 14/15] IT WORKS Needs some fixes in pthread-3ds. Made the log less noisy and added some comments. --- ctru-rs/examples/futures-tokio-basic.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ctru-rs/examples/futures-tokio-basic.rs b/ctru-rs/examples/futures-tokio-basic.rs index 423a45f..60753a2 100644 --- a/ctru-rs/examples/futures-tokio-basic.rs +++ b/ctru-rs/examples/futures-tokio-basic.rs @@ -26,15 +26,15 @@ fn main() { .expect("Couldn't build runtime"); let runtime_thread = ctru::thread::Builder::new() + // Run on the system core .affinity(1) + // Use a bigger stack size. Default is 0x1000 but we'd easily overflow that. .stack_size(0x200000) .spawn(move || { runtime.block_on(async move { - println!("Start of future"); let mut wake_time = tokio::time::Instant::now() + Duration::from_secs(1); let mut iteration = 0; loop { - println!("Start of loop"); let sleep_future = tokio::time::sleep_until(wake_time); tokio::select! { From b835695b0b3357b8b5adee3f6a6cf75e10b01cc5 Mon Sep 17 00:00:00 2001 From: Andrea Ciliberti Date: Sun, 6 Feb 2022 09:18:50 +0100 Subject: [PATCH 15/15] Changed example names to keep the standard --- ctru-rs/examples/{futures-tokio-basic.rs => futures-tokio.rs} | 0 ctru-rs/examples/{thread_locals.rs => thread-locals.rs} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename ctru-rs/examples/{futures-tokio-basic.rs => futures-tokio.rs} (100%) rename ctru-rs/examples/{thread_locals.rs => thread-locals.rs} (100%) diff --git a/ctru-rs/examples/futures-tokio-basic.rs b/ctru-rs/examples/futures-tokio.rs similarity index 100% rename from ctru-rs/examples/futures-tokio-basic.rs rename to ctru-rs/examples/futures-tokio.rs diff --git a/ctru-rs/examples/thread_locals.rs b/ctru-rs/examples/thread-locals.rs similarity index 100% rename from ctru-rs/examples/thread_locals.rs rename to ctru-rs/examples/thread-locals.rs