diff --git a/ctru-rs/examples/local-networking.rs b/ctru-rs/examples/local-networking.rs index 03f072f..75f2d0e 100644 --- a/ctru-rs/examples/local-networking.rs +++ b/ctru-rs/examples/local-networking.rs @@ -165,7 +165,7 @@ fn main() { Ok(p) => { if let Some((pkt, node)) = p { println!( - "{:02X}{:02X}{:02X}{:02X} from {:04X}", + "{:02X}{:02X}{:02X}{:02X} from {:?}", pkt[0], pkt[1], pkt[2], pkt[3], node ); } @@ -186,7 +186,7 @@ fn main() { if mode != ConnectionType::Spectator { uds.send_packet( &transfer_data.to_le_bytes(), - ctru_sys::UDS_BROADCAST_NETWORKNODEID as _, + NodeID::Broadcast, data_channel, SendFlags::Default, ) @@ -241,7 +241,7 @@ fn main() { Ok(p) => { if let Some((pkt, node)) = p { println!( - "{:02X}{:02X}{:02X}{:02X} from {:04X}", + "{:02X}{:02X}{:02X}{:02X} from {:?}", pkt[0], pkt[1], pkt[2], pkt[3], node ); } @@ -261,7 +261,7 @@ fn main() { let transfer_data = hid.keys_held().bits(); uds.send_packet( &transfer_data.to_le_bytes(), - ctru_sys::UDS_BROADCAST_NETWORKNODEID as _, + NodeID::Broadcast, data_channel, SendFlags::Default, ) diff --git a/ctru-rs/src/services/uds.rs b/ctru-rs/src/services/uds.rs index ae4decc..33f6dc3 100644 --- a/ctru-rs/src/services/uds.rs +++ b/ctru-rs/src/services/uds.rs @@ -20,11 +20,11 @@ use macaddr::MacAddr6; bitflags! { /// Flags used for sending packets to a network. - #[allow(missing_docs)] - #[derive(Copy, Clone, Debug, PartialEq, Eq)] + #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct SendFlags: u8 { - /// According to libctru source, it's not really known what these do. + /// Unknown function according to `libctru`. const Default = ctru_sys::UDS_SENDFLAG_Default as u8; + /// Broadcast the data frame even when sending to a non-broadcast address. const Broadcast = ctru_sys::UDS_SENDFLAG_Broadcast as u8; } } @@ -94,6 +94,43 @@ impl TryFrom for ConnectionType { } } +/// ID for a node on the network. +#[doc(alias = "NetworkNodeID")] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum NodeID { + /// No node ID set (not connected to a network). + None, + /// A normal node on the network, counting from 1 (the host) to 16, inclusive. + Node(u8), + /// Broadcast to all nodes + Broadcast, +} + +impl From for u16 { + fn from(value: NodeID) -> Self { + match value { + NodeID::None => 0, + NodeID::Node(node) => node as u16, + NodeID::Broadcast => ctru_sys::UDS_BROADCAST_NETWORKNODEID as u16, + } + } +} + +impl TryFrom for NodeID { + type Error = (); + + fn try_from(value: u16) -> std::result::Result { + match value as u32 { + 0 => Ok(Self::None), + ctru_sys::UDS_HOST_NETWORKNODEID..=ctru_sys::UDS_MAXNODES => { + Ok(Self::Node(value as u8)) + } + ctru_sys::UDS_BROADCAST_NETWORKNODEID => Ok(Self::Broadcast), + _ => Err(()), + } + } +} + /// Information about a network node. #[allow(missing_docs)] #[doc(alias = "udsNodeInfo")] @@ -105,7 +142,7 @@ pub struct NodeInfo { unk_x1c: u16, pub flag: u8, pad_x1f: u8, - pub node_id: u16, + pub node_id: NodeID, pad_x22: u16, word_x24: u32, } @@ -121,7 +158,10 @@ impl From for NodeInfo { 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, + 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, } @@ -163,11 +203,12 @@ impl From for NetworkScanInfo { #[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: u16, + pub cur_node_id: NodeID, unk_xa: u16, unk_xc: [u32; 8], /// Number of nodes connected to the network. @@ -185,7 +226,10 @@ impl From for ConnectionStatus { Self { status: value.status, unk_x4: value.unk_x4, - cur_node_id: value.cur_NetworkNodeID, + cur_node_id: value + .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, @@ -196,7 +240,7 @@ impl From for ConnectionStatus { } /// Status of the service handle. -#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum ServiceStatus { /// Not connected to or hosting a network. Disconnected, @@ -252,7 +296,7 @@ impl Uds { /// Initialise a new service handle. /// No `new_with_buffer_size` function is provided, as there isn't really a /// reason to use any size other than the default. - /// + /// /// The `username` parameter should be a max 10-byte (not 10 code point!) UTF-8 string, converted to UTF-16 internally. /// Pass `None` to use the 3DS's configured username. /// @@ -754,7 +798,7 @@ impl Uds { pub fn send_packet( &self, packet: &[u8], - to_nodes: u16, + address: NodeID, channel: u8, flags: SendFlags, ) -> Result<(), Error> { @@ -768,7 +812,7 @@ impl Uds { let code = ResultCode(unsafe { ctru_sys::udsSendTo( - to_nodes, + address.into(), channel, flags.bits(), packet.as_ptr().cast(), @@ -815,7 +859,7 @@ impl Uds { /// # } /// ``` #[doc(alias = "udsPullPacket")] - pub fn pull_packet(&self) -> Result, u16)>, Error> { + pub fn pull_packet(&self) -> Result, NodeID)>, Error> { if self.service_status() == ServiceStatus::Disconnected { return Err(Error::NotConnected); } @@ -843,7 +887,12 @@ impl Uds { None } else { // TODO: to_vec() first, then truncate() and shrink_to_fit()? - Some((frame[..actual_size].to_vec(), src_node_id)) + Some(( + frame[..actual_size].to_vec(), + src_node_id + .try_into() + .expect("UDS service should always provide a valid NetworkNodeID"), + )) }) }