Skip to content
55 changes: 30 additions & 25 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,17 +1,36 @@
[package]
name = "nx"
version = "0.4.1"
version = "0.5.0"
authors = ["XorTroll", "Pantsman0"]
edition = "2024"
include = [
"src",
"nx_derive"
]

[features]
default = []
services = []
smc = []
gpu = ["services"]
vty = ["canvas", "dep:embedded-term", "dep:embedded-graphics-core"]
console = ["fonts"]
canvas = ["gpu", "dep:line_drawing"]
fonts = ["canvas", "dep:ab_glyph", "dep:font8x8"]
truetype = ["fonts"]
fs = ["services", "dep:embedded-io"]
input = ["services", "applet"]
la = ["services"]
rand = ["services", "dep:rand"]
socket = ["services", "dep:embedded-io"]
applet = ["services"]
mii = ["services"]


[dependencies]
paste = "1.0"
logpacket = { git = "https://github.com/aarch64-switch-rs/logpacket", tag = "0.1.0"}
arrayvec = { version = "0.7.4", default-features = false }
arrayvec = { version = "0.7.6", default-features = false }
static_assertions = "1.1.0"
lock_api = { version = "0.4.12", features = ["nightly"] }
atomic_enum = "0.3.0"
Expand All @@ -20,10 +39,13 @@ nx-derive = { path = "nx-derive/" }
bitfield-struct = "0.11.0"
num-derive = "0.4.2"
enum-iterator = "2.1.0"
enum_dispatch = "0.3.13"

[dependencies.libc]
version = "0.2"
default-features = false

[dependencies.embedded-io]
version = "0.6.1"
optional = true
features = ["alloc"]

[dependencies.embedded-term]
version = "0.1.1"
Expand All @@ -40,7 +62,7 @@ version = "1.0.1"

[dependencies.ab_glyph]
optional = true
version = "0.2"
version = "0.2.31"
default-features = false
features = ["variable-fonts", "libm"]

Expand All @@ -56,7 +78,7 @@ default-features = false
features = ["libm"]

[dependencies.unwinding]
version = "0.2.6"
version = "0.2.8"
default-features = false
features = ["unwinder", "panic", "fde-custom", "personality"]

Expand All @@ -67,24 +89,7 @@ features = [ "const_mut_refs", "alloc_ref", "use_spin" ]

[dependencies.rand]
optional = true
version = "0.9"
version = "0.9.2"
default-features = false
features = ["alloc"]

[features]
default = []
services = []
smc = []
gpu = ["services"]
vty = ["canvas", "dep:embedded-term", "dep:embedded-graphics-core"]
console = ["fonts"]
canvas = ["gpu", "dep:line_drawing"]
fonts = ["canvas", "dep:ab_glyph", "dep:font8x8"]
truetype = ["fonts"]
fs = ["services"]
input = ["services", "applet"]
la = ["services"]
rand = ["services", "dep:rand"]
socket = ["services"]
applet = ["services"]
mii = ["services"]
93 changes: 69 additions & 24 deletions src/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ use alloc::sync::Arc;
use alloc::vec::Vec;
use core::mem as cmem;
use core::ops::DerefMut;
use embedded_io::ErrorType;
pub use embedded_io::SeekFrom;
pub use embedded_io::Write;

pub mod rc;

Expand Down Expand Up @@ -53,7 +56,7 @@ pub trait File: Sync {
fn write(&mut self, offset: usize, buf: &[u8], option: FileWriteOption) -> Result<()>;

/// Flushes the pending file writes.
fn flush(&mut self) -> Result<()>;
fn flush(&self) -> Result<()>;

/// Sets the file size.
///
Expand Down Expand Up @@ -283,7 +286,7 @@ impl File for ProxyFile {
.write(option, offset, buf.len(), ipc_sf::Buffer::from_array(buf))
}

fn flush(&mut self) -> Result<()> {
fn flush(&self) -> Result<()> {
self.file_obj.flush()
}

Expand Down Expand Up @@ -498,14 +501,6 @@ impl FileSystem for ProxyFileSystem {
}
}

/// Represents an offset kind/relativeness.
#[allow(missing_docs)]
pub enum SeekFrom {
Start(usize),
Current(isize),
End(isize),
}

/// Represents a wrapper type to simplify file access, tracking the currently seek-ed location in the file.
pub struct FileAccessor {
file: Box<dyn File>,
Expand Down Expand Up @@ -550,11 +545,13 @@ impl FileAccessor {
/// * `offset`: The offset to seek to.
pub fn seek(&mut self, pos: SeekFrom) -> Result<()> {
match pos {
SeekFrom::Start(offset) => self.offset = offset,
SeekFrom::Current(offset) => self.offset = self.offset.saturating_add_signed(offset),
SeekFrom::Start(offset) => self.offset = offset as _,
SeekFrom::Current(offset) => {
self.offset = self.offset.saturating_add_signed(offset as _)
}
SeekFrom::End(offset) => {
let size = self.get_size()?;
self.offset = size.saturating_add_signed(offset);
self.offset = size.saturating_add_signed(offset as _);
}
};
Ok(())
Expand Down Expand Up @@ -593,18 +590,23 @@ impl FileAccessor {
Ok(t)
}

// TODO (writes): some sort of "flush" flag to not always flush after writing?

/// Writes data from the given array
///
/// # Arguments
///
/// * `arr`: The input array
pub fn write_array<T: Copy>(&mut self, arr: &[T]) -> Result<()> {
pub fn write_array<T: Copy, const FLUSH: bool>(&mut self, arr: &[T]) -> Result<()> {
let transmuted: &[u8] =
unsafe { core::slice::from_raw_parts(arr.as_ptr() as _, core::mem::size_of_val(arr)) };
self.file
.write(self.offset, transmuted, FileWriteOption::Flush())?;
self.file.write(
self.offset,
transmuted,
if FLUSH {
FileWriteOption::Flush()
} else {
FileWriteOption::None()
},
)?;
self.offset += transmuted.len();
Ok(())
}
Expand All @@ -614,21 +616,64 @@ impl FileAccessor {
/// # Arguments
///
/// * `t`: The value to write
pub fn write_val<T: Copy>(&mut self, t: &T) -> Result<()> {
pub fn write_val<T: Copy, const FLUSH: bool>(&mut self, t: &T) -> Result<()> {
let transmuted = unsafe {
core::slice::from_raw_parts(t as *const T as *const u8, cmem::size_of::<T>())
};

self.file
.write(self.offset, transmuted, FileWriteOption::Flush())?;
self.file.write(
self.offset,
transmuted,
if FLUSH {
FileWriteOption::Flush()
} else {
FileWriteOption::None()
},
)?;
self.offset += transmuted.len();
Ok(())
}

/// Flushes reads/writes to the underlying file.
///
/// It is provided, but it is not currently required as all writes to [`FileAccessor`] send the `Flush` flag.
pub fn flush(&self) -> Result<()> {
self.file.flush()
}
}

impl ErrorType for FileAccessor {
type Error = ResultCode;
}

impl embedded_io::Read for FileAccessor {
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
if buf.len() == 0 {
return Ok(0);
}

self.read_array(buf)
}
}

impl embedded_io::Write for FileAccessor {
fn write(&mut self, buf: &[u8]) -> Result<usize> {
if buf.len() == 0 {
return Ok(0);
}

self.write_array::<u8, false>(buf).map(|_| buf.len())
}

fn flush(&mut self) -> Result<()> {
FileAccessor::flush(&*self)
}
}

impl core::fmt::Write for FileAccessor {
fn write_str(&mut self, s: &str) -> core::fmt::Result {
self.write_array(s.as_bytes()).map_err(|_| core::fmt::Error)
self.write_array::<u8, true>(s.as_bytes())
.map_err(|_| core::fmt::Error)
}
}
/// Represents a wrapper type to simplify directory access
Expand Down Expand Up @@ -1034,8 +1079,8 @@ pub fn open_file(path: &str, option: FileOpenOption) -> Result<FileAccessor> {
}
};

let offset: usize = match option.contains(FileOpenOption::Append()) {
true => file.get_size().unwrap_or(0),
let offset: u64 = match option.contains(FileOpenOption::Append()) {
true => file.get_size().unwrap_or(0) as _,
false => 0,
};

Expand Down
4 changes: 2 additions & 2 deletions src/ipc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,14 +80,14 @@ impl ObjectInfo {
ipc_client_send_control_command!([*self; cmif::ControlRequestId::ConvertCurrentObjectToDomain] () => (domain_object_id: cmif::DomainObjectId))
}

pub fn query_pointer_buffer_size(&mut self) -> Result<u16> {
pub fn query_pointer_buffer_size(&self) -> Result<u16> {
if self.uses_tipc_protocol() {
return super::rc::ResultNotSupported::make_err();
}
ipc_client_send_control_command!([*self; cmif::ControlRequestId::QueryPointerBufferSize] () => (pointer_buffer_size: u16))
}

pub fn clone_current_object(&mut self) -> Result<sf::MoveHandle> {
pub fn clone_current_object(&self) -> Result<sf::MoveHandle> {
if self.uses_tipc_protocol() {
return super::rc::ResultNotSupported::make_err();
}
Expand Down
21 changes: 17 additions & 4 deletions src/ipc/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,27 +163,40 @@ pub trait IClientObject {
self.get_session().object_info
}

fn set_info(&mut self, info: ObjectInfo) {
unsafe fn set_info(&mut self, info: ObjectInfo) {
self.get_session_mut().set_info(info);
}

fn convert_to_domain(&mut self) -> Result<()> {
self.get_session_mut().convert_to_domain()
}

fn query_own_pointer_buffer_size(&mut self) -> Result<u16> {
fn query_own_pointer_buffer_size(&self) -> Result<u16> {
self.get_info().query_pointer_buffer_size()
}

fn close_session(&mut self) {
self.get_session_mut().close()
}

fn is_valid(&mut self) -> bool {
fn is_valid(&self) -> bool {
self.get_info().is_valid()
}

fn is_domain(&mut self) -> bool {
fn is_domain(&self) -> bool {
self.get_info().is_domain()
}

fn clone(&self) -> Result<Self>
where
Self: Sized,
{
let handle = self.get_info().clone_current_object()?;
Ok(Self::new(sf::Session {
object_info: ObjectInfo {
handle: handle.handle,
..self.get_info()
},
}))
}
}
4 changes: 2 additions & 2 deletions src/ipc/sf/bsd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -655,7 +655,7 @@ impl From<Option<TimeSpec>> for Linger {

#[derive(Copy, Clone, Debug, Request, Response)]
#[repr(C)]
pub enum FnCtlCmd {
pub enum FcntlCmd {
/// Duplicate file descriptor
DupFd = 0,
/// Get file descriptor flags (close on exec)
Expand Down Expand Up @@ -918,7 +918,7 @@ pub trait Bsd {
fn listen(&self, sockfd: i32, backlog: i32) -> BsdResult<()>;

#[ipc_rid(20)]
fn fnctl(&self, sockfd: i32, cmd: FnCtlCmd, flags: i32) -> BsdResult<()>;
fn fcntl(&self, sockfd: i32, cmd: FcntlCmd, flags: i32) -> BsdResult<()>;

#[ipc_rid(21)]
fn set_sock_opt(
Expand Down
2 changes: 1 addition & 1 deletion src/ipc/sf/fsp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ pub trait File {
buf: sf::InNonSecureMapAliasBuffer<'_, u8>,
);
#[ipc_rid(2)]
fn flush(&mut self);
fn flush(&self);
#[ipc_rid(3)]
fn set_size(&mut self, size: usize);
#[ipc_rid(4)]
Expand Down
27 changes: 27 additions & 0 deletions src/result.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,3 +180,30 @@ pub trait ResultBase {
result_define! {
Success: 0, 0
}

#[cfg(any(feature = "fs", feature = "socket"))]
impl embedded_io::Error for ResultCode {
fn kind(&self) -> embedded_io::ErrorKind {
use embedded_io::ErrorKind;
match (self.get_module(), self.get_description()) {
#[cfg(feature = "socket")]
(crate::socket::rc::RESULT_MODULE, errno) => match errno {
1004 => ErrorKind::Interrupted,
1005 => ErrorKind::WriteZero,
1011 => ErrorKind::TimedOut,
1032 => ErrorKind::BrokenPipe,
_ => ErrorKind::Other,
},
#[cfg(feature = "fs")]
(crate::fs::rc::RESULT_SUBMODULE, errno) => match errno {
4000..=4999 => ErrorKind::InvalidData,
6003..=6199 => ErrorKind::InvalidInput,
6602 | 6603 => ErrorKind::NotFound,
6300..=6399 => ErrorKind::Unsupported,
6400..=6449 => ErrorKind::PermissionDenied,
_ => ErrorKind::Other,
},
_ => ErrorKind::Other,
}
}
}
Loading