use std::fmt;
use bytemuck::bytes_of;
pub const SZ_DATA: usize = 512;
pub const SZ_ARGS: usize = size_of::<[u64; 3]>();
pub(super) const CRC: crc::Crc<u16> = crc::Crc::<u16>::new(&crc::CRC_16_ISO_IEC_14443_3_A);
#[derive(
Copy,
Clone,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
bytemuck::Pod,
bytemuck::Zeroable,
derive_more::From,
derive_more::Into,
)]
#[repr(C)]
pub struct Command(u16);
macro_rules! commands {
($($name_camel:ident, $name_snake:ident = $value:literal),+ $(,)?) => {
impl Command {
$(
#[doc = concat!("Command code for ", stringify!($name_snake))]
pub const $name_snake: Command = Command($value);
)+
pub fn name(&self) -> Option<&'static str> {
match self.0 {
$(
$value => Some(stringify!($name_snake)),
)+
_ => None
}
}
}
};
}
commands! {
Ack, ACK = 0x00FF,
DebugPrintStrings, DEBUG_PRINT_STRINGS = 0x0100,
DebugPrintIntegers, DEBUG_PRINT_INTEGERS = 0x0101,
Version, VERSION = 0x0107,
Status, STATUS = 0x0108,
Ping, PING = 0x0109,
Capabilities, CAPABILITIES = 0x0112,
QuitSession, QUIT_SESSION = 0x0113,
Wtx, WTX = 0x0116,
DownloadedBigBuf, DOWNLOADED_BIGBUF = 0x0208,
HfIso14443aReader, HF_ISO_14443A_READER = 0x0385,
HfIClassReader, HF_ICLASS_READER = 0x0394,
}
impl fmt::Debug for Command {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(name) = self.name() {
write!(f, "{name}")?;
} else {
write!(f, "????")?;
}
write!(f, "(0x{:04X})", self.0)?;
Ok(())
}
}
#[bitfield_struct::bitfield(u16)]
pub(super) struct NgLength {
#[bits(15)]
pub length: u16,
pub ng: bool,
}
pub(super) trait ByteFold {
fn byte_fold<F>(&self, mut f: F)
where
F: FnMut(&[u8]),
{
let _ = self.try_byte_fold(|bytes| {
f(bytes);
Ok::<(), ()>(())
});
}
fn try_byte_fold<F, E>(&self, f: F) -> Result<(), E>
where
F: FnMut(&[u8]) -> Result<(), E>;
fn byte_fold_to<'a>(&self, buf: &'a mut [u8]) -> &'a mut [u8] {
let mut head = 0;
self.byte_fold(|bytes| {
buf[head..][..bytes.len()].copy_from_slice(bytes);
head += bytes.len();
});
&mut buf[..head]
}
}
pub(super) trait NgPayloadInternal: ByteFold + fmt::Debug + Copy + Clone {
fn ng_length(&self) -> NgLength;
}
#[derive(Debug, Copy, Clone)]
pub struct FrameOld {
pub cmd: Command,
pub args: [u64; 3],
pub payload: [u8; SZ_DATA],
}
impl ByteFold for FrameOld {
fn try_byte_fold<F, E>(&self, mut f: F) -> Result<(), E>
where
F: FnMut(&[u8]) -> Result<(), E>,
{
f(bytes_of(&self.cmd))?;
f(bytes_of(&self.args))?;
f(&self.payload)?;
Ok(())
}
}
pub trait NgPayload {
#[must_use]
fn payload(&self) -> &[u8];
}
#[derive(Copy, Clone)]
pub struct MixShim {
pub args: [u64; 3],
pub(super) len: usize,
pub(super) buf: [u8; SZ_DATA - SZ_ARGS],
}
impl fmt::Debug for MixShim {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("MixShim")
.field("args", &self.args)
.field("payload", &self.payload())
.finish_non_exhaustive()
}
}
impl NgPayload for MixShim {
fn payload(&self) -> &[u8] {
&self.buf[..self.len]
}
}
impl ByteFold for MixShim {
fn try_byte_fold<F, E>(&self, mut f: F) -> Result<(), E>
where
F: FnMut(&[u8]) -> Result<(), E>,
{
f(bytes_of(&self.args))?;
f(self.payload())?;
Ok(())
}
}
impl NgPayloadInternal for MixShim {
fn ng_length(&self) -> NgLength {
NgLength::new().with_ng(false).with_length(
(SZ_ARGS + self.len)
.try_into()
.expect("payload length within u16"),
)
}
}
#[derive(Copy, Clone)]
pub struct NgShim {
pub(super) len: usize,
pub(super) buf: [u8; SZ_DATA],
}
impl fmt::Debug for NgShim {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("NgShim")
.field("payload", &self.payload())
.finish_non_exhaustive()
}
}
impl NgPayload for NgShim {
fn payload(&self) -> &[u8] {
&self.buf[..self.len]
}
}
impl ByteFold for NgShim {
fn try_byte_fold<F, E>(&self, mut f: F) -> Result<(), E>
where
F: FnMut(&[u8]) -> Result<(), E>,
{
f(self.payload())
}
}
impl NgPayloadInternal for NgShim {
fn ng_length(&self) -> NgLength {
NgLength::new()
.with_ng(true)
.with_length(self.len.try_into().expect("payload length within u16"))
}
}