mirror of
https://github.com/Zenithsiz/ndsz.git
synced 2026-02-03 09:50:35 +00:00
Replaced zutil's split_array interface with ndsz-bytes.
This commit is contained in:
parent
f515cbed6f
commit
95332b131e
13
ndsz-bytes/Cargo.toml
Normal file
13
ndsz-bytes/Cargo.toml
Normal file
@ -0,0 +1,13 @@
|
||||
[package]
|
||||
edition = "2018"
|
||||
name = "ndsz-bytes"
|
||||
version = "0.1.0"
|
||||
|
||||
[dependencies]
|
||||
|
||||
# Bytes
|
||||
arrayref = "0.3.6"
|
||||
byteorder = "1.4.2"
|
||||
|
||||
# Error handling
|
||||
thiserror = "1.0.23"
|
||||
111
ndsz-bytes/src/array_split.rs
Normal file
111
ndsz-bytes/src/array_split.rs
Normal file
@ -0,0 +1,111 @@
|
||||
//! Array splitting
|
||||
|
||||
// TODO: Add back a `PhantomData` field for when splitting nothing once
|
||||
// rust-analyzer doesn't crap itself with it
|
||||
|
||||
/// Splits a byte array reference into several smaller byte arrays references,
|
||||
/// or even single byte references.
|
||||
pub macro array_split
|
||||
(
|
||||
$arr:expr,
|
||||
$(
|
||||
$name:ident :
|
||||
|
||||
$( [$arr_size:expr] )?
|
||||
$( $val_size:literal )?
|
||||
|
||||
),* $(,)?
|
||||
) {{
|
||||
// Struct holding all fields
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
struct Fields<'a, T> {
|
||||
$(
|
||||
pub $name:
|
||||
|
||||
$( &'a [T; $arr_size], )?
|
||||
$( &'a T, #[cfg(invalid)] __field: [u8; $val_size], )?
|
||||
)*
|
||||
}
|
||||
|
||||
// Get everything from `array_refs`
|
||||
#[allow(
|
||||
clippy::used_underscore_binding,
|
||||
clippy::ptr_offset_with_cast,
|
||||
clippy::indexing_slicing,
|
||||
)]
|
||||
let (
|
||||
$(
|
||||
$name
|
||||
),*
|
||||
) = $crate::arrayref::array_refs!(
|
||||
$arr,
|
||||
$(
|
||||
$( $arr_size )?
|
||||
$( $val_size )?
|
||||
),*
|
||||
);
|
||||
|
||||
// And return the fields
|
||||
Fields {
|
||||
$(
|
||||
$name
|
||||
$( : &( $name[$val_size - $val_size] ) )?
|
||||
,
|
||||
)*
|
||||
}
|
||||
}}
|
||||
|
||||
/// Splits a byte array mutable reference into several smaller byte arrays references,
|
||||
/// or even single byte references.
|
||||
#[allow(clippy::module_name_repetitions)] // `_mut` version should be in the same module
|
||||
pub macro array_split_mut(
|
||||
$arr:expr,
|
||||
$(
|
||||
$name:ident :
|
||||
|
||||
$( [$arr_size:expr] )?
|
||||
$( $val_size:literal )?
|
||||
|
||||
),* $(,)?
|
||||
) {{
|
||||
// Struct holding all fields
|
||||
#[derive(Debug)]
|
||||
struct Fields<'a, T> {
|
||||
$(
|
||||
pub $name:
|
||||
|
||||
$( &'a mut [T; $arr_size], )?
|
||||
// Note: This `cfg` is simply done so that `__field` never appears.
|
||||
// The `__field` serves to identify when this part should be written.
|
||||
$( &'a mut T, #[cfg(invalid)] __field: [u8; $val_size], )?
|
||||
)*
|
||||
}
|
||||
|
||||
// Get everything from `mut_array_refs`
|
||||
#[allow(
|
||||
clippy::used_underscore_binding,
|
||||
clippy::ptr_offset_with_cast,
|
||||
clippy::indexing_slicing,
|
||||
)]
|
||||
let (
|
||||
$(
|
||||
$name
|
||||
),*
|
||||
) = $crate::arrayref::mut_array_refs!(
|
||||
$arr,
|
||||
$(
|
||||
$( $arr_size )?
|
||||
$( $val_size )?
|
||||
),*
|
||||
);
|
||||
|
||||
// And return the fields
|
||||
Fields {
|
||||
$(
|
||||
$name
|
||||
// Note: This serves to turn a `&mut [u8; 1]` into a `&mut u8`.
|
||||
$( : &mut ( $name[$val_size - $val_size] ) )?
|
||||
,
|
||||
)*
|
||||
}
|
||||
}}
|
||||
82
ndsz-bytes/src/byteorder_ext.rs
Normal file
82
ndsz-bytes/src/byteorder_ext.rs
Normal file
@ -0,0 +1,82 @@
|
||||
//! [`ByteOrder`] extension trait
|
||||
|
||||
// Imports
|
||||
use {crate::ByteArray, byteorder::ByteOrder};
|
||||
|
||||
/// Helper trait for [`ByteOrder`] to use a generic type
|
||||
pub trait ByteOrderExt<B: ByteOrder>: Sized {
|
||||
/// Array type
|
||||
type ByteArray: ByteArray;
|
||||
|
||||
/// Reads this type
|
||||
fn read(bytes: &Self::ByteArray) -> Self;
|
||||
|
||||
/// Writes this type
|
||||
fn write(&self, bytes: &mut Self::ByteArray);
|
||||
}
|
||||
|
||||
impl<B: ByteOrder, const N: usize> ByteOrderExt<B> for [u8; N] {
|
||||
type ByteArray = [u8; N];
|
||||
|
||||
fn read(bytes: &Self::ByteArray) -> Self {
|
||||
*bytes
|
||||
}
|
||||
|
||||
fn write(&self, bytes: &mut Self::ByteArray) {
|
||||
*bytes = *self;
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::use_self)] // We want the byte buffer to be `[u8; _]`
|
||||
impl<B: ByteOrder> ByteOrderExt<B> for u8 {
|
||||
type ByteArray = [u8; 1];
|
||||
|
||||
fn read(bytes: &Self::ByteArray) -> Self {
|
||||
bytes[0]
|
||||
}
|
||||
|
||||
fn write(&self, bytes: &mut Self::ByteArray) {
|
||||
bytes[0] = *self;
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::as_conversions, clippy::cast_possible_wrap, clippy::cast_sign_loss)] // We want to explicitly convert it from bytes
|
||||
impl<B: ByteOrder> ByteOrderExt<B> for i8 {
|
||||
type ByteArray = [u8; 1];
|
||||
|
||||
fn read(bytes: &Self::ByteArray) -> Self {
|
||||
bytes[0] as Self
|
||||
}
|
||||
|
||||
fn write(&self, bytes: &mut Self::ByteArray) {
|
||||
bytes[0] = *self as u8;
|
||||
}
|
||||
}
|
||||
|
||||
/// Implements [`ByteOrderExt`] for `$T` with size `$SIZE` and methods to read/write `$read_func`/`$write_func`
|
||||
macro_rules! impl_read_bytes {
|
||||
($($T:ty, $SIZE:literal, $read_func:ident, $write_func:ident),* $(,)?) => {
|
||||
$(
|
||||
impl<B: ByteOrder> ByteOrderExt<B> for $T {
|
||||
type ByteArray = [u8; $SIZE];
|
||||
|
||||
fn read(bytes: &Self::ByteArray) -> Self {
|
||||
B::$read_func(bytes)
|
||||
}
|
||||
|
||||
fn write(&self, bytes: &mut Self::ByteArray) {
|
||||
B::$write_func(bytes, *self);
|
||||
}
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
impl_read_bytes! {
|
||||
u16, 2, read_u16, write_u16,
|
||||
u32, 4, read_u32, write_u32,
|
||||
u64, 8, read_u64, write_u64,
|
||||
i16, 2, read_i16, write_i16,
|
||||
i32, 4, read_i32, write_i32,
|
||||
i64, 8, read_i64, write_i64,
|
||||
}
|
||||
79
ndsz-bytes/src/bytes.rs
Normal file
79
ndsz-bytes/src/bytes.rs
Normal file
@ -0,0 +1,79 @@
|
||||
//! `Bytes` trait.
|
||||
|
||||
// Imports
|
||||
use std::error::Error;
|
||||
|
||||
/// Conversion from and to bytes
|
||||
pub trait Bytes
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
/// The type of array required by this structure
|
||||
type ByteArray: ByteArray;
|
||||
|
||||
/// The error type used for the operation
|
||||
type DeserializeError: Error;
|
||||
|
||||
/// The error type used for the operation
|
||||
type SerializeError: Error;
|
||||
|
||||
/// Deserializes this from `bytes`
|
||||
fn deserialize_bytes(bytes: &Self::ByteArray) -> Result<Self, Self::DeserializeError>;
|
||||
|
||||
/// Serializes this to `bytes`
|
||||
fn serialize_bytes(&self, bytes: &mut Self::ByteArray) -> Result<(), Self::SerializeError>;
|
||||
|
||||
/// Serializes this to bytes and returns them.
|
||||
fn to_bytes(&self) -> Result<Self::ByteArray, Self::SerializeError> {
|
||||
let mut bytes = Self::ByteArray::zeros();
|
||||
self.serialize_bytes(&mut bytes)?;
|
||||
Ok(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
/// A trait for restricting `Bytes::ByteArray`
|
||||
pub trait ByteArray {
|
||||
/// Array size
|
||||
const SIZE: usize;
|
||||
|
||||
/// Returns this array as a slice
|
||||
fn as_slice(&self) -> &[u8];
|
||||
|
||||
/// Returns this array as a slice mutably
|
||||
fn as_slice_mut(&mut self) -> &mut [u8];
|
||||
|
||||
/// Creates a new array filled with `0`s
|
||||
fn zeros() -> Self;
|
||||
}
|
||||
|
||||
impl<const N: usize> ByteArray for [u8; N] {
|
||||
const SIZE: usize = N;
|
||||
|
||||
fn as_slice(&self) -> &[u8] {
|
||||
self
|
||||
}
|
||||
|
||||
fn as_slice_mut(&mut self) -> &mut [u8] {
|
||||
self
|
||||
}
|
||||
|
||||
fn zeros() -> Self {
|
||||
[0; N]
|
||||
}
|
||||
}
|
||||
|
||||
impl ByteArray for u8 {
|
||||
const SIZE: usize = 1;
|
||||
|
||||
fn as_slice(&self) -> &[u8] {
|
||||
std::slice::from_ref(self)
|
||||
}
|
||||
|
||||
fn as_slice_mut(&mut self) -> &mut [u8] {
|
||||
std::slice::from_mut(self)
|
||||
}
|
||||
|
||||
fn zeros() -> Self {
|
||||
0
|
||||
}
|
||||
}
|
||||
55
ndsz-bytes/src/bytes_io_ext.rs
Normal file
55
ndsz-bytes/src/bytes_io_ext.rs
Normal file
@ -0,0 +1,55 @@
|
||||
//! Bytes io extensions
|
||||
|
||||
// Imports
|
||||
use {
|
||||
crate::{ByteArray, Bytes},
|
||||
std::{error, fmt, io},
|
||||
};
|
||||
|
||||
/// Bytes read extension trait
|
||||
pub trait BytesReadExt: io::Read {
|
||||
/// Deserializes `B` from this stream
|
||||
fn read_deserialize<B: Bytes>(&mut self) -> Result<B, ReadDeserializeError<B::DeserializeError>> {
|
||||
let mut bytes = B::ByteArray::zeros();
|
||||
self.read_exact(bytes.as_slice_mut())
|
||||
.map_err(ReadDeserializeError::Read)?;
|
||||
B::deserialize_bytes(&bytes).map_err(ReadDeserializeError::Parse)
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: io::Read> BytesReadExt for R {}
|
||||
|
||||
/// Bytes write extension trait
|
||||
pub trait BytesWriteExt: io::Write {
|
||||
/// Serializes `B` to this stream
|
||||
fn write_serialize<B: Bytes>(&mut self, value: &B) -> Result<(), WriteSerializeError<B::SerializeError>> {
|
||||
let bytes = value.to_bytes().map_err(WriteSerializeError::Serialize)?;
|
||||
self.write_all(bytes.as_slice()).map_err(WriteSerializeError::Write)
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: io::Write> BytesWriteExt for W {}
|
||||
|
||||
/// Read bytes error
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum ReadDeserializeError<E: fmt::Debug + error::Error + 'static> {
|
||||
/// Unable to read bytes
|
||||
#[error("Unable to read bytes")]
|
||||
Read(#[source] io::Error),
|
||||
|
||||
/// Unable to parse bytes
|
||||
#[error("Unable to parse bytes")]
|
||||
Parse(#[source] E),
|
||||
}
|
||||
|
||||
/// Write bytes error
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum WriteSerializeError<E: fmt::Debug + error::Error + 'static> {
|
||||
/// Unable to serialize value
|
||||
#[error("Unable to serialize value")]
|
||||
Serialize(#[source] E),
|
||||
|
||||
/// Unable to write bytes
|
||||
#[error("Unable to write bytes")]
|
||||
Write(#[source] io::Error),
|
||||
}
|
||||
22
ndsz-bytes/src/lib.rs
Normal file
22
ndsz-bytes/src/lib.rs
Normal file
@ -0,0 +1,22 @@
|
||||
//! Helper crate for working with raws bytes.
|
||||
|
||||
// Features
|
||||
#![feature(decl_macro)]
|
||||
|
||||
// Modules
|
||||
mod array_split;
|
||||
mod byteorder_ext;
|
||||
mod bytes;
|
||||
pub mod bytes_io_ext;
|
||||
pub mod validate;
|
||||
|
||||
// Exports
|
||||
pub use self::{
|
||||
array_split::{array_split, array_split_mut},
|
||||
byteorder_ext::ByteOrderExt,
|
||||
bytes::{ByteArray, Bytes},
|
||||
bytes_io_ext::{BytesReadExt, BytesWriteExt},
|
||||
validate::{Validate, ValidateVisitor},
|
||||
};
|
||||
#[doc(hidden)]
|
||||
pub use ::arrayref; // Export `arrayref` to use in macros
|
||||
33
ndsz-bytes/src/validate.rs
Normal file
33
ndsz-bytes/src/validate.rs
Normal file
@ -0,0 +1,33 @@
|
||||
//! Validation interface
|
||||
|
||||
/// Structures that are validatable before being written to bytes.
|
||||
///
|
||||
/// This works in tandem with the [`Bytes`](crate::Bytes) interface to allow
|
||||
/// applications which take user input to validate input before serializing it.
|
||||
///
|
||||
/// Although this information exists by calling [`Bytes::serialize_bytes`](crate::Bytes::serialize_bytes),
|
||||
/// this interface provides two main advantages:
|
||||
///
|
||||
/// 1. It is faster than serializing the data, as it doesn't need to write the raw bytes and
|
||||
/// can focus on simply parsing possible errors.
|
||||
/// 2. It provides warnings alongside the errors. These are also provided via `log::warn`, but
|
||||
/// these cannot be sent to the user easily.
|
||||
pub trait Validate<'a> {
|
||||
/// Error type for this validation
|
||||
type Error: 'a;
|
||||
|
||||
/// Warning type for this validation
|
||||
type Warning: 'a;
|
||||
|
||||
/// Validates this type with the visitor `visitor`
|
||||
fn validate<V: ValidateVisitor<'a, Self>>(&'a self, visitor: V);
|
||||
}
|
||||
|
||||
/// A validate visitor, used to collect errors and warnings
|
||||
pub trait ValidateVisitor<'a, T: ?Sized + Validate<'a>> {
|
||||
/// Visits a warning
|
||||
fn visit_warning(&mut self, warning: T::Warning);
|
||||
|
||||
/// Visits an error
|
||||
fn visit_error(&mut self, error: T::Error);
|
||||
}
|
||||
@ -7,6 +7,9 @@ version = "0.0.0"
|
||||
|
||||
[dependencies]
|
||||
|
||||
# Ndsz
|
||||
ndsz-bytes = {path = "../ndsz-bytes"}
|
||||
|
||||
# Bytes
|
||||
byteorder = "1.4.3"
|
||||
|
||||
|
||||
@ -16,7 +16,7 @@ pub struct FilePtr {
|
||||
impl FilePtr {
|
||||
/// Parses a header data from bytes
|
||||
pub fn from_bytes(bytes: &[u8; 0x8]) -> Self {
|
||||
let bytes = zutil::array_split!(bytes,
|
||||
let bytes = ndsz_bytes::array_split!(bytes,
|
||||
start_address: [0x4],
|
||||
end_address: [0x4],
|
||||
);
|
||||
|
||||
@ -69,7 +69,7 @@ pub struct MainTableEntry {
|
||||
impl MainTableEntry {
|
||||
/// Parses an entry from bytes
|
||||
pub fn from_bytes(bytes: &[u8; 0x8]) -> Self {
|
||||
let bytes = zutil::array_split!(bytes,
|
||||
let bytes = ndsz_bytes::array_split!(bytes,
|
||||
sub_table_offset: [0x4],
|
||||
first_file_id : [0x2],
|
||||
parent_id : [0x2],
|
||||
|
||||
@ -7,6 +7,9 @@ version = "0.0.0"
|
||||
|
||||
[dependencies]
|
||||
|
||||
# Ndsz
|
||||
ndsz-bytes = {path = "../ndsz-bytes"}
|
||||
|
||||
# Bytes
|
||||
byteorder = "1.4.3"
|
||||
|
||||
|
||||
@ -28,7 +28,7 @@ pub struct Header {
|
||||
impl Header {
|
||||
/// Parses a header from bytes
|
||||
pub fn from_bytes(bytes: &[u8; 0x10]) -> Result<Self, FromBytesError> {
|
||||
let bytes = zutil::array_split!(bytes,
|
||||
let bytes = ndsz_bytes::array_split!(bytes,
|
||||
magic : [0x4],
|
||||
constant : [0x4],
|
||||
section_size : [0x4],
|
||||
|
||||
@ -8,6 +8,7 @@ version = "0.0.0"
|
||||
[dependencies]
|
||||
|
||||
# Ndsz
|
||||
ndsz-bytes = {path = "../ndsz-bytes"}
|
||||
ndsz-fat = {path = "../ndsz-fat"}
|
||||
|
||||
# Bytes
|
||||
|
||||
@ -62,7 +62,7 @@ impl Header {
|
||||
|
||||
/// Parses a header data from bytes
|
||||
pub fn from_bytes(bytes: &[u8; Self::SIZE]) -> Option<Self> {
|
||||
let bytes = zutil::array_split!(bytes,
|
||||
let bytes = ndsz_bytes::array_split!(bytes,
|
||||
chunk_name: [0x4],
|
||||
chunk_size: [0x4],
|
||||
);
|
||||
|
||||
@ -22,7 +22,7 @@ impl FatHeader {
|
||||
|
||||
/// Parses a header data from bytes
|
||||
pub fn from_bytes(bytes: &[u8; Self::SIZE]) -> Option<Self> {
|
||||
let bytes = zutil::array_split!(bytes,
|
||||
let bytes = ndsz_bytes::array_split!(bytes,
|
||||
chunk_name: [0x4],
|
||||
chunk_size: [0x4],
|
||||
files_len : [0x2],
|
||||
|
||||
@ -16,7 +16,7 @@ impl FntHeader {
|
||||
|
||||
/// Parses a header data from bytes
|
||||
pub fn from_bytes(bytes: &[u8; Self::SIZE]) -> Option<Self> {
|
||||
let bytes = zutil::array_split!(bytes,
|
||||
let bytes = ndsz_bytes::array_split!(bytes,
|
||||
chunk_name: [0x4],
|
||||
chunk_size: [0x4],
|
||||
);
|
||||
|
||||
@ -19,7 +19,7 @@ pub struct Header {
|
||||
impl Header {
|
||||
/// Parses a header data from bytes
|
||||
pub fn from_bytes(bytes: &[u8; 0x10]) -> Result<Self, FromBytesError> {
|
||||
let bytes = zutil::array_split!(bytes,
|
||||
let bytes = ndsz_bytes::array_split!(bytes,
|
||||
chunk_name: [0x4],
|
||||
byte_order: [0x2],
|
||||
version : [0x2],
|
||||
|
||||
@ -7,6 +7,9 @@ version = "0.0.0"
|
||||
|
||||
[dependencies]
|
||||
|
||||
# Ndsz
|
||||
ndsz-bytes = {path = "../ndsz-bytes"}
|
||||
|
||||
# Bytes
|
||||
byteorder = "1.4.3"
|
||||
|
||||
|
||||
@ -114,7 +114,7 @@ pub struct Header {
|
||||
impl Header {
|
||||
/// Parses a header data from bytes
|
||||
pub fn from_bytes(bytes: &[u8; 0x180]) -> Result<Self, FromBytesError> {
|
||||
let bytes = zutil::array_split!(bytes,
|
||||
let bytes = ndsz_bytes::array_split!(bytes,
|
||||
game_title : [0xc], // 0x0
|
||||
game_code : [0x4], // 0xc
|
||||
maker_code : [0x2], // 0x10
|
||||
@ -199,7 +199,7 @@ pub struct TableLoadData {
|
||||
impl TableLoadData {
|
||||
/// Parses a table load data from bytes
|
||||
pub fn from_bytes(bytes: &[u8; 8]) -> Self {
|
||||
let bytes = zutil::array_split!(bytes,
|
||||
let bytes = ndsz_bytes::array_split!(bytes,
|
||||
offset: [0x4],
|
||||
length: [0x4],
|
||||
);
|
||||
@ -230,7 +230,7 @@ pub struct ArmLoadData {
|
||||
impl ArmLoadData {
|
||||
/// Parses load data from bytes
|
||||
pub fn from_bytes(bytes: &[u8; 16]) -> Self {
|
||||
let bytes = zutil::array_split!(bytes,
|
||||
let bytes = ndsz_bytes::array_split!(bytes,
|
||||
offset : [0x4],
|
||||
entry_address: [0x4],
|
||||
load_address : [0x4],
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user