Browse Source

Replace custom types with wrappers

pull/156/head
Jhynjhiruu 10 months ago
parent
commit
009bc7b4f1
  1. 8
      ctru-rs/examples/local-networking.rs
  2. 283
      ctru-rs/src/services/uds.rs

8
ctru-rs/examples/local-networking.rs

@ -9,8 +9,8 @@ fn handle_status_event(uds: &Uds, prev_node_mask: u16) -> ctru::Result<u16> { @@ -9,8 +9,8 @@ fn handle_status_event(uds: &Uds, prev_node_mask: u16) -> ctru::Result<u16> {
println!("Connection status event signalled");
let status = uds.get_connection_status()?;
println!("Status: {status:#02X?}");
let left = prev_node_mask & (status.node_bitmask ^ prev_node_mask);
let joined = status.node_bitmask & (status.node_bitmask ^ prev_node_mask);
let left = prev_node_mask & (status.node_bitmask() ^ prev_node_mask);
let joined = status.node_bitmask() & (status.node_bitmask() ^ prev_node_mask);
for i in 0..16 {
if left & (1 << i) != 0 {
println!("Node {} disconnected", i + 1);
@ -25,7 +25,7 @@ fn handle_status_event(uds: &Uds, prev_node_mask: u16) -> ctru::Result<u16> { @@ -25,7 +25,7 @@ fn handle_status_event(uds: &Uds, prev_node_mask: u16) -> ctru::Result<u16> {
);
}
}
Ok(status.node_bitmask)
Ok(status.node_bitmask())
}
fn main() -> Result<(), Error> {
@ -126,7 +126,7 @@ fn main() -> Result<(), Error> { @@ -126,7 +126,7 @@ fn main() -> Result<(), Error> {
println!(
"{} Username: {}",
if index == selected_network { ">" } else { " " },
n.nodes[0].as_ref().unwrap().username
n.nodes()[0].unwrap().username()
);
}

283
ctru-rs/src/services/uds.rs

@ -37,7 +37,7 @@ pub enum Error { @@ -37,7 +37,7 @@ pub enum Error {
/// The provided username was too long.
UsernameTooLong,
/// The provided username contained a NULL byte.
UsernameContainsNull,
UsernameContainsNull(usize),
/// Not connected to a network.
NotConnected,
/// No context bound.
@ -66,6 +66,12 @@ impl<T> FromResidual<crate::Error> for Result<T, Error> { @@ -66,6 +66,12 @@ impl<T> FromResidual<crate::Error> for Result<T, Error> {
}
}
impl From<std::ffi::NulError> for Error {
fn from(value: std::ffi::NulError) -> Self {
Error::UsernameContainsNull(value.nul_position())
}
}
impl Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
@ -74,7 +80,8 @@ impl Display for Error { @@ -74,7 +80,8 @@ impl Display for Error {
match self {
Self::UsernameTooLong =>
"provided username was too long (max 10 bytes, not code points)".into(),
Self::UsernameContainsNull => "provided username contained a NULL byte".into(),
Self::UsernameContainsNull(pos) =>
format!("provided username contained a NULL byte at position {pos}"),
Self::NotConnected => "not connected to a network".into(),
Self::NoContext => "no context bound".into(),
Self::Spectator => "cannot send data on a network as a spectator".into(),
@ -89,8 +96,7 @@ impl Display for Error { @@ -89,8 +96,7 @@ impl Display for Error {
impl StdError for Error {}
/// Possible types of connection to a network
///
/// Possible types of connection to a network.
#[doc(alias = "udsConnectionType")]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u32)]
@ -158,110 +164,219 @@ impl TryFrom<u16> for NodeID { @@ -158,110 +164,219 @@ impl TryFrom<u16> for NodeID {
}
/// Information about a network node.
#[allow(missing_docs)]
#[doc(alias = "udsNodeInfo")]
#[derive(Debug)]
// Ported to Rust so that Debug can be derived on it.
pub struct NodeInfo {
pub uds_friendcodeseed: u64,
pub username: String,
unk_x1c: u16,
pub flag: u8,
pad_x1f: u8,
pub node_id: NodeID,
pad_x22: u16,
word_x24: u32,
#[derive(Copy, Clone)]
pub struct NodeInfo(ctru_sys::udsNodeInfo);
impl Debug for NodeInfo {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "NodeInfo(")?;
f.debug_struct("udsNodeInfo")
.field("uds_friendcodeseed", &self.uds_friendcodeseed())
.field("username", &self.username())
.field("flag", &self.flag())
.field("NetworkNodeID", &self.node_id())
.finish()?;
write!(f, ")")
}
}
impl From<ctru_sys::udsNodeInfo> for NodeInfo {
fn from(value: ctru_sys::udsNodeInfo) -> Self {
unsafe {
Self {
uds_friendcodeseed: value.uds_friendcodeseed,
username: String::from_utf16_lossy(
&value.__bindgen_anon_1.__bindgen_anon_1.username,
),
unk_x1c: value.__bindgen_anon_1.__bindgen_anon_1.unk_x1c,
flag: value.__bindgen_anon_1.__bindgen_anon_1.flag,
pad_x1f: value.__bindgen_anon_1.__bindgen_anon_1.pad_x1f,
node_id: value
.NetworkNodeID
.try_into()
.expect("UDS service should always provide a valid NetworkNodeID"),
pad_x22: value.pad_x22,
word_x24: value.word_x24,
Self(value)
}
}
impl NodeInfo {
/// Friend code seed associated with this network node.
pub fn uds_friendcodeseed(&self) -> u64 {
self.0.uds_friendcodeseed
}
/// Username associated with this network node.
pub fn username(&self) -> String {
String::from_utf16_lossy(unsafe { &self.0.__bindgen_anon_1.__bindgen_anon_1.username })
}
/// Flag associated with this network node.
pub fn flag(&self) -> u8 {
unsafe { self.0.__bindgen_anon_1.__bindgen_anon_1.flag }
}
/// Node ID associated with this network node.
pub fn node_id(&self) -> NodeID {
self.0
.NetworkNodeID
.try_into()
.expect("UDS service should always provide a valid NetworkNodeID")
}
}
/// Information returned from scanning for networks.
#[doc(alias = "udsNetworkScanInfo")]
#[derive(Debug)]
// Ported to Rust so that Debug can be derived on it.
pub struct NetworkScanInfo {
/// NWM output structure.
pub datareply_entry: ctru_sys::nwmBeaconDataReplyEntry,
/// Information about the network.
pub network: ctru_sys::udsNetworkStruct,
/// All nodes on the network (first node is the server,
/// max 16, `None` means no node connected).
pub nodes: [Option<NodeInfo>; 16],
#[derive(Copy, Clone)]
pub struct NetworkScanInfo(ctru_sys::udsNetworkScanInfo);
impl Debug for NetworkScanInfo {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "NetworkScanInfo(")?;
f.debug_struct("udsNetworkScanInfo")
.field("datareply_entry", &self.datareply_entry())
.field("network", &self.network())
.field("nodes", &self.nodes())
.finish()?;
write!(f, ")")
}
}
impl From<ctru_sys::udsNetworkScanInfo> for NetworkScanInfo {
fn from(value: ctru_sys::udsNetworkScanInfo) -> Self {
Self {
datareply_entry: value.datareply_entry,
network: value.network,
nodes: value.nodes.map(|n| {
Self(value)
}
}
impl NetworkScanInfo {
/// NWM output structure.
pub fn datareply_entry(&self) -> ctru_sys::nwmBeaconDataReplyEntry {
self.0.datareply_entry
}
/// Get a reference to the NWM output structure.
pub fn datareply_entry_ref(&self) -> &ctru_sys::nwmBeaconDataReplyEntry {
&self.0.datareply_entry
}
/// Get a mutable reference to the NWM output structure.
pub fn datareply_entry_mut(&mut self) -> &mut ctru_sys::nwmBeaconDataReplyEntry {
&mut self.0.datareply_entry
}
/// Information about the network.
pub fn network(&self) -> ctru_sys::udsNetworkStruct {
self.0.network
}
/// Get a reference to the information about the network.
pub fn network_ref(&self) -> &ctru_sys::udsNetworkStruct {
&self.0.network
}
/// Get a mutable reference to the information about the network.
pub fn network_mut(&mut self) -> &mut ctru_sys::udsNetworkStruct {
&mut self.0.network
}
/// All nodes on the network (first node is the server,
/// max 16, `None` means no node connected).
pub fn nodes(&self) -> [Option<NodeInfo>; 16] {
self.0.nodes.map(|n| {
if n.uds_friendcodeseed != 0 {
Some(n.into())
} else {
None
}
}),
})
}
}
/// Possible raw connection status values.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u32)]
#[non_exhaustive]
pub enum ConnectionStatusInfo {
/// Not connected to any network.
Disconnected = 3,
/// Connected as a host.
Host = 6,
/// Connected as a client.
Client = 9,
/// Connected as a spectator.
Spectator = 10,
/// Unknown
Unknown = 11,
}
impl From<ConnectionStatusInfo> for u32 {
fn from(value: ConnectionStatusInfo) -> Self {
value as Self
}
}
impl TryFrom<u32> for ConnectionStatusInfo {
type Error = ();
fn try_from(value: u32) -> std::result::Result<Self, Self::Error> {
match value {
3 => Ok(Self::Disconnected),
6 => Ok(Self::Host),
9 => Ok(Self::Client),
10 => Ok(Self::Spectator),
11 => Ok(Self::Unknown),
_ => Err(()),
}
}
}
/// Status of the connection.
#[doc(alias = "udsConnectionStatus")]
#[derive(Debug)]
pub struct ConnectionStatus {
/// Raw status information
// TODO: is this in some kind of readable format?
pub status: u32,
unk_x4: u32,
/// Network node ID for the current device.
pub cur_node_id: NodeID,
unk_xa: u16,
unk_xc: [u32; 8],
/// Number of nodes connected to the network.
pub total_nodes: u8,
/// Maximum nodes allowed on this network.
pub max_nodes: u8,
/// Bitmask for which of the 16 possible nodes are connected
/// to this network; bit 0 is the server, bit 1 is the first
/// original client, etc.
pub node_bitmask: u16,
#[derive(Clone, Copy)]
pub struct ConnectionStatus(ctru_sys::udsConnectionStatus);
impl Debug for ConnectionStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "ConnectionStatus(")?;
f.debug_struct("udsConnectionStatus")
.field("status", &self.status())
.field("cur_node_id", &self.cur_node_id())
.field("total_nodes", &self.total_nodes())
.field("max_nodes", &self.max_nodes())
.field("node_bitmask", &self.node_bitmask())
.finish()?;
write!(f, ")")
}
}
impl From<ctru_sys::udsConnectionStatus> for ConnectionStatus {
fn from(value: ctru_sys::udsConnectionStatus) -> Self {
Self {
status: value.status,
unk_x4: value.unk_x4,
cur_node_id: value
Self(value)
}
}
impl ConnectionStatus {
/// Raw status information.
pub fn status(&self) -> Option<ConnectionStatusInfo> {
self.0.status.try_into().ok()
}
/// Network node ID for the current device.
pub fn cur_node_id(&self) -> NodeID {
self.0
.cur_NetworkNodeID
.try_into()
.expect("UDS service should always provide a valid NetworkNodeID"),
unk_xa: value.unk_xa,
unk_xc: value.unk_xc,
total_nodes: value.total_nodes,
max_nodes: value.max_nodes,
node_bitmask: value.node_bitmask,
.expect("UDS service should always provide a valid NetworkNodeID")
}
/// Number of nodes connected to the network.
pub fn total_nodes(&self) -> u8 {
self.0.total_nodes
}
/// Maximum nodes allowed on this network.
pub fn max_nodes(&self) -> u8 {
self.0.max_nodes
}
/// Bitmask for which of the 16 possible nodes are connected
/// to this network; bit 0 is the server, bit 1 is the first
/// original client, etc.
pub fn node_bitmask(&self) -> u16 {
self.0.node_bitmask
}
}
@ -307,7 +422,11 @@ impl Uds { @@ -307,7 +422,11 @@ impl Uds {
/// The maximum amount of app data any server can provide.
/// Limited by the size of a struct in libctru.
const MAX_APPDATA_SIZE: usize = 200;
const MAX_APPDATA_SIZE: usize = Self::size_of_call(|s: ctru_sys::udsNetworkStruct| s.appdata);
const fn size_of_call<T, U>(_: fn(T) -> U) -> usize {
std::mem::size_of::<U>()
}
/// Retrieve the current status of the service.
pub fn service_status(&self) -> ServiceStatus {
@ -352,15 +471,7 @@ impl Uds { @@ -352,15 +471,7 @@ impl Uds {
return Err(Error::UsernameTooLong);
}
}
let cstr = username.map(CString::new);
let cstr = if let Some(conv) = cstr {
match conv {
Ok(c) => Some(c),
Err(_) => return Err(Error::UsernameContainsNull),
}
} else {
None
};
let cstr = username.map(CString::new).transpose()?;
let handler = ServiceReference::new(
&UDS_ACTIVE,
|| {
@ -480,7 +591,7 @@ impl Uds { @@ -480,7 +591,7 @@ impl Uds {
ResultCode(unsafe {
ctru_sys::udsGetNetworkStructApplicationData(
&network.network as *const _,
network.network_ref() as *const _,
appdata_buffer.as_mut_ptr().cast(),
appdata_buffer.len(),
actual_size.as_mut_ptr(),
@ -580,7 +691,7 @@ impl Uds { @@ -580,7 +691,7 @@ impl Uds {
ResultCode(unsafe {
ctru_sys::udsConnectNetwork(
&network.network as *const _,
network.network_ref() as *const _,
passphrase.as_ptr().cast(),
passphrase.len(),
context.as_mut_ptr(),

Loading…
Cancel
Save