204 lines
5.1 KiB
Rust
204 lines
5.1 KiB
Rust
//! Binary data conversion utilities.
|
|
|
|
use crate::durandal::err::*;
|
|
use serde::Serialize;
|
|
use std::{fmt, num::NonZeroU16};
|
|
|
|
#[doc(hidden)]
|
|
macro_rules! rd_1 {
|
|
// big endian
|
|
(BE $b:ident $nam:ident u16 $n:expr) => {
|
|
rd_1!($b u16::from_be_bytes, $nam 2 $n);
|
|
};
|
|
(BE $b:ident $nam:ident i16 $n:expr) => {
|
|
rd_1!($b i16::from_be_bytes, $nam 2 $n);
|
|
};
|
|
(BE $b:ident $nam:ident u32 $n:expr) => {
|
|
rd_1!($b u32::from_be_bytes, $nam 4 $n);
|
|
};
|
|
(BE $b:ident $nam:ident i32 $n:expr) => {
|
|
rd_1!($b i32::from_be_bytes, $nam 4 $n);
|
|
};
|
|
(BE $b:ident $nam:ident as usize u16 $n:expr) => {
|
|
rd_1!($b u16::from_be_bytes, $nam 2 $n);
|
|
let $nam = $nam as usize;
|
|
};
|
|
(BE $b:ident $nam:ident as usize u32 $n:expr) => {
|
|
rd_1!($b u32::from_be_bytes, $nam 4 $n);
|
|
let $nam = $nam as usize;
|
|
};
|
|
(BE $b:ident $nam:ident Angle $n:expr) => {
|
|
rd_1!($b u16::from_be_bytes, $nam 2 $n);
|
|
let $nam = Angle::from_bits($nam);
|
|
};
|
|
(BE $b:ident $nam:ident Fixed $n:expr) => {
|
|
rd_1!($b u32::from_be_bytes, $nam 4 $n);
|
|
let $nam = Fixed::from_bits($nam);
|
|
};
|
|
(BE $b:ident $nam:ident Unit $n:expr) => {
|
|
rd_1!($b u16::from_be_bytes, $nam 2 $n);
|
|
let $nam = Unit::from_bits($nam);
|
|
};
|
|
|
|
// little endian
|
|
(LE $b:ident $nam:ident u16 $n:expr) => {
|
|
rd_1!($b u16::from_le_bytes $nam 2 $n);
|
|
};
|
|
(LE $b:ident $nam:ident i16 $n:expr) => {
|
|
rd_1!($b i16::from_le_bytes $nam 2 $n);
|
|
};
|
|
(LE $b:ident $nam:ident u32 $n:expr) => {
|
|
rd_1!($b u32::from_le_bytes $nam 4 $n);
|
|
};
|
|
(LE $b:ident $nam:ident i32 $n:expr) => {
|
|
rd_1!($b i32::from_le_bytes $nam 4 $n);
|
|
};
|
|
(LE $b:ident $nam:ident as usize u16 $n:expr) => {
|
|
rd_1!($b u16::from_le_bytes $nam 2 $n);
|
|
let $nam = $nam as usize;
|
|
};
|
|
(LE $b:ident $nam:ident as usize u32 $n:expr) => {
|
|
rd_1!($b u32::from_le_bytes $nam 4 $n);
|
|
let $nam = $nam as usize;
|
|
};
|
|
|
|
// generic endianness
|
|
($_:ident $b:ident $nam:ident u8 $n:expr) => {
|
|
let $nam = $b[$n];
|
|
};
|
|
($_:ident $b:ident $nam:ident array u8 $n:expr) => {
|
|
let $nam = &$b[$n];
|
|
};
|
|
($_:ident $b:ident $nam:ident i8 $n:expr) => {
|
|
let $nam = $b[$n] as i8;
|
|
};
|
|
($_:ident $b:ident $nam:ident iden $n:expr) => {
|
|
let $nam = [$b[$n], $b[$n + 1], $b[$n + 2], $b[$n + 3]];
|
|
};
|
|
($_:ident $b:ident $nam:ident $f:ident $n:expr) => {
|
|
let $nam = $f(&$b[$n])?;
|
|
};
|
|
($_:ident $b:ident $nam:ident nt $f:ident $n:expr) => {
|
|
let $nam = $f(&$b[$n]);
|
|
};
|
|
|
|
// worker - creates let statement
|
|
($b:ident $pth:path , $nam:ident 2 $n:expr) => {
|
|
let $nam = $pth([$b[$n], $b[$n + 1]]);
|
|
};
|
|
($b:ident $pth:path , $nam:ident 4 $n:expr) => {
|
|
let $nam = $pth([$b[$n], $b[$n + 1], $b[$n + 2], $b[$n + 3]]);
|
|
};
|
|
}
|
|
|
|
macro_rules! read_data {
|
|
(
|
|
$sz:expr , $ty:ident in $b:ident =>
|
|
$( $nam:ident = $t:ident [ $n:expr ] $( $ex:ident )* ; )*
|
|
) => {
|
|
if $b.len() < $sz {
|
|
bail!("not enough data");
|
|
}
|
|
|
|
$(rd_1!($ty $b $nam $($ex)* $t $n);)*
|
|
};
|
|
}
|
|
|
|
pub fn ident(b: &[u8]) -> Ident {[b[0], b[1], b[2], b[3]]}
|
|
pub fn u32b(b: &[u8]) -> u32 {u32::from_be_bytes([b[0], b[1], b[2], b[3]])}
|
|
pub fn u16b(b: &[u8]) -> u16 {u16::from_be_bytes([b[0], b[1]])}
|
|
pub fn i32b(b: &[u8]) -> i32 {i32::from_be_bytes([b[0], b[1], b[2], b[3]])}
|
|
pub fn i16b(b: &[u8]) -> i16 {i16::from_be_bytes([b[0], b[1]])}
|
|
|
|
pub fn rd_array<T, F>(b: &[u8], read: F) -> ResultS<Vec<T>>
|
|
where T: Sized,
|
|
F: Fn(&[u8]) -> ResultS<(T, usize)>
|
|
{
|
|
let mut v = Vec::new();
|
|
let mut p = 0;
|
|
|
|
while p < b.len() {
|
|
let (r, s) = read(&b[p..])?;
|
|
v.push(r);
|
|
p += s;
|
|
}
|
|
|
|
Ok(v)
|
|
}
|
|
|
|
pub fn rd_ofstable<T, F>(b: &[u8],
|
|
mut p: usize,
|
|
num: usize,
|
|
read: F)
|
|
-> ResultS<Vec<T>>
|
|
where T: Sized,
|
|
F: Fn(&[u8]) -> ResultS<T>
|
|
{
|
|
let mut v = Vec::with_capacity(num);
|
|
|
|
for _ in 0..num {
|
|
let ofs = u32b(&b[p..]) as usize;
|
|
|
|
if ofs >= b.len() {
|
|
bail!("not enough data");
|
|
}
|
|
|
|
v.push(read(&b[ofs..])?);
|
|
p += 4;
|
|
}
|
|
|
|
Ok(v)
|
|
}
|
|
|
|
impl ObjID
|
|
{
|
|
/// Creates an `ObjID` from a `u16`.
|
|
pub fn from_repr(n: u16) -> ObjID
|
|
{
|
|
if n == u16::max_value() {
|
|
ObjID(None)
|
|
} else {
|
|
ObjID(NonZeroU16::new(n + 1))
|
|
}
|
|
}
|
|
|
|
/// Returns the `u16` representation of an `ObjID`.
|
|
pub fn get_repr(&self) -> u16
|
|
{
|
|
match self.0 {
|
|
None => u16::max_value(),
|
|
Some(n) => n.get() - 1,
|
|
}
|
|
}
|
|
|
|
/// Returns the `Option` representation of an `ObjID`.
|
|
pub fn get(&self) -> Option<u16>
|
|
{
|
|
match self.0 {
|
|
None => None,
|
|
Some(n) => Some(n.get() - 1),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl fmt::Debug for ObjID
|
|
{
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result
|
|
{
|
|
match self.get() {
|
|
None => write!(f, "ObjID(None)"),
|
|
Some(n) => write!(f, "ObjID({})", n),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// A four-character-code identifier.
|
|
pub type Ident = [u8; 4];
|
|
|
|
/// An object identified by a `u16` which may be `u16::max_value()` to
|
|
/// represent None.
|
|
#[derive(Serialize)]
|
|
pub struct ObjID(Option<NonZeroU16>);
|
|
|
|
// EOF
|